Skip to content

Instantly share code, notes, and snippets.

@c0deWranglr
Last active October 5, 2017 16:49
Show Gist options
  • Save c0deWranglr/728cd826d26e71c00b3e61722f773f8b to your computer and use it in GitHub Desktop.
Save c0deWranglr/728cd826d26e71c00b3e61722f773f8b to your computer and use it in GitHub Desktop.
Registers an sbt project with consul at a specified port
/*
To install, simple put this file in the global sbt plugins directory.
For sbt 0.13 that would be ~/.sbt/0.13/plugins/
To use, just call sbt "consul -p <port>" to register any project with consul at <port>
or sbt "consul -d" to deregister this project from consul
or sbt "consul -x" to deregister all sbt projects from consul
To save the port for automatic loading, run sbt "consul -p <port> -s".
*/
import sbt._
import Keys._
import complete.DefaultParsers._
import scala.io.Source
import java.io._
import scala.collection.mutable.ListBuffer
object Consul extends Plugin {
/*
Declares the difference registration types. There are 3 possible actions.
RegisterProject, DeregisterProject and DeregisterNode.
- RegisterProject: Registers the current sbt project with consul
- DeregisterProject: Deregisters the current sbt project from consul
- DeregisterNode: Deregisters the sbt node from consul
*/
object RegistrationType extends Enumeration {
val RegisterProject, DeregisterProject, DeregisterNode = Value
}
/*
Implicit class to parse a Sequence of strings in order to
construct the arguments for this plugin.
*/
implicit class ArgParse(val seq: Seq[String]) {
var port: Option[Int] = None // The port to register the project on sbt with consul
var registrationType = RegistrationType.RegisterProject // The type of registration to run
var save = false // Whether or not to save the CLI arguments to a file for convenience
/*
Parse the [seq] variable and set this classes variable accordingly.
* -d: Signifies an attempt to deregister the project from consul
* -x: Signifies an attempt to deregister the sbt node from consul
* -p: Assigns the following port number to this project for use with consul
* -s: Triggers the save action to save the port for convenience
*/
def parse(): ArgParse = {
if (seq != null) {
var idx = 0
while (idx < seq.length) {
seq(idx) match {
case o if o == "-d" => registrationType = RegistrationType.DeregisterProject
case o if o == "-x" => registrationType = RegistrationType.DeregisterNode
case o if o == "-p" => {
port = Option(seq(idx + 1).toInt)
idx += 1
}
case o if o == "-s" => save = true
}
idx += 1
}
}
return this
}
/*
Updates the current args (if loaded from the saved file) with args
loaded from the command line
*/
def updateFrom(other: ArgParse): Unit = {
other.port match {
case Some(value) => port = other.port
case None => {}
}
registrationType = other.registrationType
save = other.save
}
override def toString: String = {
if (!port.isEmpty) {
return "-p " + port.get
}
return ""
}
}
/*
Prints an information message
*/
def info(msg: String): Unit = {
println("[\033[34minfo\033[1;37m] " + msg)
}
/*
Prints a warning messasge
*/
def warn(msg: String): Unit = {
println("[\033[33mwarn\033[1;37m] " + msg)
}
/*
Prints an error message
*/
def error(msg: String): Unit = {
println("[\033[31merror\033[1;37m] " + msg)
}
lazy val consul = inputKey[Unit]("Configures the project with consul.")
override def settings = Seq(
consul := {
info("Loading arguments")
val node = "sbt"
val proj = name.value
val args = (try { Source.fromFile("consul-sbt.args").getLines.mkString(" ").split(" ").toSeq } catch { case e: Exception => null }).parse
args.updateFrom(spaceDelimited("").parsed.parse)
// Split execution path here depending on registrationType
val data = args.registrationType match {
/*
For the registration process, we will do the following in order:
- Get the currently bound docker-forward port for the
project (if no port was specified on start up)
- Stop the currently running docker container with the same name
as this project
- Get the mac's ip address to register with consul
- Return the consul args
*/
case RegistrationType.RegisterProject => {
if (args.port.isEmpty) {
info("Getting currently bound " + proj + " port")
try {
val ports = "docker-forward ls".!!.split("\n").map(_.split(" "))
val idx = ports.indexWhere(_(0) == proj)
args.port = Option(ports(idx).last.toInt)
}
catch {
case e: Exception => {
throw new Exception("Failed to find a port to bind to. Please specify a port number!")
}
}
}
info("Stopping the " + proj + " docker container if it exists")
("docker stop " + proj).!
info("Retrieving virtual box ip")
val net = Seq("ifconfig", "vboxnet0").!!.split(" ")
var index = -1
for (i <- 0 until net.length) {
if (net(i).contains("inet")) {
index = i + 1
}
}
val ip = net(index)
info("Registering " + proj + " with consul")
("register", s"""{\"Node\":\"$node\", \"Address\":\"$ip\", \"Service\":{\"Service\":\"$proj\", \"Port\":${args.port.get}, \"ID\":\"$proj\"}}""")
}
/*
For the project deregistration process, we will only need to return
the proper deregistration args for consul
*/
case RegistrationType.DeregisterProject => {
info("Deregistering " + proj + " from consul")
("deregister", s"""{\"Node\": \"$node\", \"ServiceID\":\"$proj\"}""")
}
/*
For the node deregistration process, we will only need to return
the proper deregistration args for consul
*/
case RegistrationType.DeregisterNode => {
info("Deregistering sbt node from consul")
("deregister", s"""{\"Node\": \"$node\"}""")
}
}
// Submit the args to consul
val ip = "docker-machine ip sofi".!!.trim()
val res = Seq("curl", "http://"+ ip +":8500/v1/catalog/"+ data._1, "-s", "-S", "-4", "-X", "PUT", "-H", "'Content-Type: application/json'", "-d", data._2).!!.trim()
res match {
case r if r == "true" => {}
case r => error("Failed to communicate with consul!")
}
// Save the args if requested.
if (args.save) {
val writer = new PrintWriter(new File("consul-sbt.args"))
writer.print(args.toString)
writer.close()
}
}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment