From 9d2078bc19fe3afb5727c769a60090e1e6523fe3 Mon Sep 17 00:00:00 2001 From: Michele Sciabarra Date: Sun, 8 Oct 2017 10:34:41 +0200 Subject: [PATCH] ammonite scripts to manage the cloud --- README.md | 7 ++-- ammonite/.gitignore | 4 ++ ammonite/README.md | 7 ++++ ammonite/aws.sc | 55 +++++++++++++++++++++++++++ ammonite/build.sbt | 17 +++++++++ ammonite/cmd.sc | 13 +++++++ ammonite/lib/Cmd.sc | 12 ++++++ ammonite/lib/EC2.sc | 63 +++++++++++++++++++++++++++++++ ammonite/lib/SSH.sc | 32 ++++++++++++++++ ammonite/predef.sc | 1 + ammonite/project/build.properties | 1 + ammonite/project/plugins.sbt | 1 + config.properties | 5 +++ terraform/terraform-stack | 1 - 14 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 ammonite/.gitignore create mode 100644 ammonite/README.md create mode 100644 ammonite/aws.sc create mode 100644 ammonite/build.sbt create mode 100644 ammonite/cmd.sc create mode 100644 ammonite/lib/Cmd.sc create mode 100644 ammonite/lib/EC2.sc create mode 100644 ammonite/lib/SSH.sc create mode 100644 ammonite/predef.sc create mode 100644 ammonite/project/build.properties create mode 100644 ammonite/project/plugins.sbt create mode 100644 config.properties delete mode 160000 terraform/terraform-stack diff --git a/README.md b/README.md index c528752..b0fed7a 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ # Mosaico -by [Michele Sciabarra](http://michele.sciabarra.com) and [Sciabarra.com](http://sciabarra.com) +by [Michele Sciabarra](http://michele.sciabarra.com) and [Sciabarra.com](http://sciabarra.com) A starter kit for building a Cloud in AWS - Terraform for creating the cloud - Ansible for privisioning Docker and Jenkins -- Docker-Compose and Docker-Swarm for management -- Ammonite Scripts for configuration and management - SBT for building a collection of Docker images - +- Ammonite Scripts for configuration and management +- Docker-Compose and Docker-Swarm for management ## Beginning diff --git a/ammonite/.gitignore b/ammonite/.gitignore new file mode 100644 index 0000000..4383c65 --- /dev/null +++ b/ammonite/.gitignore @@ -0,0 +1,4 @@ +session +history +cache/ +saved/ diff --git a/ammonite/README.md b/ammonite/README.md new file mode 100644 index 0000000..2b9feee --- /dev/null +++ b/ammonite/README.md @@ -0,0 +1,7 @@ +# Scripts + +- `aws.sc`: manage aws + +- `cmd.sc`: execute externa commads + +at some point in time the scripts will be autodocumenting diff --git a/ammonite/aws.sc b/ammonite/aws.sc new file mode 100644 index 0000000..44e61d2 --- /dev/null +++ b/ammonite/aws.sc @@ -0,0 +1,55 @@ +import ammonite.ops._ +import collection.JavaConversions._ + +import $file.lib.SSH +import $file.lib.EC2 + +val defaultUser = Option(sys.props("aws.ssh.user")).getOrElse("centos") + +val defaultTag = (Option(sys.props("aws.tag.name")).getOrElse("Application"), + Option(sys.props("aws.tag.value")).getOrElse("Mosaico")) + +def doSsh(tag: (String,String), args: Seq[String]) { + val instances = EC2.runningTaggedInstances(tag) + val exec = SSH.Ssh(defaultUser, EC2.ipAddresses(instances)) + try { + exec(args: _*) + } catch { + case err: Throwable => println(err.toString) + } +} + +@main def ssh(args: String*) = { + if(args.head(0) == '@') { + args.head.tail.split(",").foreach { + host => + print(s"[${host}] ") + val tag = "Name" -> host + doSsh(tag, args.tail) + } + } else { + //println(args) + doSsh(defaultTag, args) + } +} + +@main def list() { + val instances = EC2.withTag(EC2.instances(), defaultTag) + val out = instances map { x => + val tags = x.getTags.toList.map { + y => s"${y.getKey}=${y.getValue}" + }.mkString("[", ",", "]") + s"${x.getState.getName}\t${x.getPublicIpAddress}\t${x.getPublicDnsName}\t${x.getPrivateIpAddress}\t${x.getPrivateDnsName}\t${tags}" + } + println(out.mkString("\n")) +} + +@main def stop() { + val instances = EC2.runningTaggedInstances(defaultTag) + EC2.stopInstances(instances) +} + +@main def start() { + val instances = EC2.withTag(EC2.instances(), defaultTag) + EC2.startInstances(instances) +} diff --git a/ammonite/build.sbt b/ammonite/build.sbt new file mode 100644 index 0000000..bc7e488 --- /dev/null +++ b/ammonite/build.sbt @@ -0,0 +1,17 @@ +watchTransitiveSources := Seq() + +enablePlugins(MosaicoAmmonitePlugin,MosaicoConfigPlugin) + +ammPredef := Some("predef.sc") + +prpLookup += baseDirectory.value.getParentFile -> "config" + +addCommandAlias("aws", "amm aws.sc") + +addCommandAlias("awssh", "amm aws.sc ssh") + +addCommandAlias("cmd", "amm cmd.sc") + +addCommandAlias("terraform", "amm cmd.sc terraform --args") + +addCommandAlias("ansible", "amm cmd.sc ansible") diff --git a/ammonite/cmd.sc b/ammonite/cmd.sc new file mode 100644 index 0000000..80ee99e --- /dev/null +++ b/ammonite/cmd.sc @@ -0,0 +1,13 @@ +import ammonite.ops._ +import $file.lib.Cmd + + +@main def ansible() { + val inventory = sys.props("ansible.inventory") + val script = sys.props("ansible.script") + Cmd.ansible(inventory, script) +} + +@main def terraform(args: String*) { + Cmd.terraform(args) +} diff --git a/ammonite/lib/Cmd.sc b/ammonite/lib/Cmd.sc new file mode 100644 index 0000000..a65cd91 --- /dev/null +++ b/ammonite/lib/Cmd.sc @@ -0,0 +1,12 @@ +import ammonite.ops._ + + +def ansible(inventory: String, script: String) = { + val ansibleDir = pwd/up/'ansible + %("ansible-playbook", "-i", inventory, script)(ansibleDir) +} + +def terraform(args: Seq[String]) = { + implicit val wd = pwd/up/'terraform + %("terraform", args) +} diff --git a/ammonite/lib/EC2.sc b/ammonite/lib/EC2.sc new file mode 100644 index 0000000..e62ec3d --- /dev/null +++ b/ammonite/lib/EC2.sc @@ -0,0 +1,63 @@ +import $ivy.`com.amazonaws:aws-java-sdk:1.11.22` + +import ammonite.ops._ +import collection.JavaConverters._ + +import com.amazonaws.services.ec2._ +import com.amazonaws.services.ec2.model._ +import com.amazonaws.regions.{Region=>Ec2Region,Regions=>Ec2Regions} + +// instances + +val region = Option(sys.props("mosaico.region")).getOrElse("us-east-1") + +// client +val ec2 = new AmazonEC2Client() +val regions = Ec2Regions.fromName(region) +ec2.setRegion(Ec2Region.getRegion(regions)) + +// instances +def instances() = (for { + reservation <- ec2.describeInstances.getReservations.asScala + instance <- reservation.getInstances.asScala +} yield { + instance +}).toSeq + +def running(insts: Seq[Instance]) = insts.filter { + _.getState.getName == "running" +} + +def hasTag(tags: Seq[Tag], kv: (String,String)) = { + tags + .map(t => t.getKey == kv._1 && t.getValue == kv._2) + .reduce(_ || _) +} + +def withTag(insts: Seq[Instance], kv: (String,String)) = + insts.filter { x => + hasTag(x.getTags.asScala.toSeq, kv) + } + +def runningTaggedInstances(tag: (String,String)) = + withTag(running(instances), tag) + +def ipAddresses(instances: Seq[Instance]) = instances map { + x=> x.getPrivateIpAddress -> x.getPublicIpAddress +} + +def dnsNames(instances: Seq[Instance]) = instances map { + x=> x.getPrivateDnsName -> x.getPublicDnsName +} + +def startInstances(instances: Seq[Instance]) = { + val ids = instances.map { _.getInstanceId } + val req = new StartInstancesRequest().withInstanceIds(ids: _*) + ec2.startInstances(req) +} + +def stopInstances(instances: Seq[Instance]) = { + val ids = instances.map { _.getInstanceId } + val req = new StopInstancesRequest().withInstanceIds(ids: _*) + ec2.stopInstances(req) +} diff --git a/ammonite/lib/SSH.sc b/ammonite/lib/SSH.sc new file mode 100644 index 0000000..19200a5 --- /dev/null +++ b/ammonite/lib/SSH.sc @@ -0,0 +1,32 @@ +import ammonite.ops._ + +class Ssh(user: String, ips: Seq[(String,String)]) +{ + def apply(cmd: Any*): Unit = { + + val args = cmd map { obj => + obj match { + case s: Symbol => s.name + case x: Any => x.toString + } + } + + implicit val wd = pwd + + ips map { ip => + val tgt = s"${user}@${ip._2}" + + val sshArgs: Seq[String] = Seq( + "-o", "StrictHostKeyChecking=no", + tgt + ) ++ args + + println(s"[${ip._1}] ${args.mkString(" ")}") + %ssh(sshArgs) + } + } +} + +object Ssh { + def apply(user: String, ips: Seq[(String,String)]) = new Ssh(user, ips) +} diff --git a/ammonite/predef.sc b/ammonite/predef.sc new file mode 100644 index 0000000..d62da10 --- /dev/null +++ b/ammonite/predef.sc @@ -0,0 +1 @@ +interp.colors() = ammonite.util.Colors.Default diff --git a/ammonite/project/build.properties b/ammonite/project/build.properties new file mode 100644 index 0000000..c091b86 --- /dev/null +++ b/ammonite/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.16 diff --git a/ammonite/project/plugins.sbt b/ammonite/project/plugins.sbt new file mode 100644 index 0000000..dc0cb9d --- /dev/null +++ b/ammonite/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.sciabarra" %% "mosaico-sbt" % "0.4-SNAPSHOT") diff --git a/config.properties b/config.properties new file mode 100644 index 0000000..bb34285 --- /dev/null +++ b/config.properties @@ -0,0 +1,5 @@ +aws.tag.name=Application +aws.tag.value=Mosaico +aws.ssh.user=centos +ansible.inventory=ec2.py +ansible.script=ec2.yml diff --git a/terraform/terraform-stack b/terraform/terraform-stack deleted file mode 160000 index a758020..0000000 --- a/terraform/terraform-stack +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a758020af49d73f2d97db7ae733e00c82607868c