Skip to content

Commit

Permalink
Docker Compose V2 support
Browse files Browse the repository at this point in the history
for imports to architecture. Handles volumes and links, shows networks
but doesn’t route through them.
  • Loading branch information
adrianco committed Apr 10, 2016
1 parent d6e5c22 commit 5df525b
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 45 deletions.
10 changes: 10 additions & 0 deletions architecture/architecture.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,13 @@ func Write(a *archV0r1) {
os.Stdout.Write(b)
}
}

func WriteFile(a *archV0r1, fn string) {
dfile, err := os.Create(fn + ".json")
if err != nil {
log.Fatal(err)
}
sj, _ := json.Marshal(a)
dfile.WriteString(fmt.Sprintf("%v", string(sj)))
dfile.Close()
}
171 changes: 159 additions & 12 deletions compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ package compose
import (
//"fmt"
"github.com/adrianco/spigo/architecture"
"github.com/cloudfoundry-incubator/candiedyaml"
"gopkg.in/yaml.v2"
"io/ioutil"
"log"
"os"
"strings"
)

Expand Down Expand Up @@ -50,23 +52,133 @@ func ReadCompose(fn string) ComposeServices {

// ReadCompose for V2
func ReadComposeV2(fn string) *ComposeV2Yaml {
data, err := ioutil.ReadFile(fn)
file, err := os.Open(fn)
if err != nil {
log.Fatal(err)
log.Println("File does not exist:", err)
return nil
}
var c ComposeV2Yaml
e := yaml.Unmarshal(data, &c)
if e == nil {
return &c
} else {
log.Println(e)
defer file.Close()
document := new(interface{})
decoder := candiedyaml.NewDecoder(file)
err = decoder.Decode(document)
if err != nil {
log.Println("Couldn't decode yaml:", err)
return nil
}
c2 := new(ComposeV2Yaml)
cs := make(ComposeServices)
switch comp := (*document).(type) {
case map[interface{}]interface{}:
for label, section := range comp {
switch label {
case "version":
switch version := section.(type) {
case string:
c2.Version = version
default:
log.Println("Version not a string: %v", section)
return nil
}
case "services":
//fmt.Printf("Got services %v\n", section)
switch services := section.(type) {
case map[interface{}]interface{}:
for name, options := range services {
switch optionmap := options.(type) {
case map[interface{}]interface{}:
//fmt.Printf("Service %v:\n", name)
ca := new(ComposeAttributes)
for option, values := range optionmap {
ok := true
switch value := values.(type) {
case []interface{}:
//fmt.Printf(" slice %v:%v\n", option, value)
sv := make([]string, len(value))
for i, v := range value {
sv[i], ok = v.(string)
if !ok {
log.Printf("Can't convert %v:%v to string\n", option, value)
}
}
switch option {
case "volumes":
ca.Volumes = sv
case "ports":
ca.Ports = sv
case "links":
ca.Links = sv
case "networks":
ca.Networks = sv
default:
log.Printf("option ignored %v:%v\n", option, value)
}
case string:
//fmt.Printf(" string %v:%v\n", option, value)
switch option {
case "build":
ca.Build = value
case "image":
ca.Image = value
default:
log.Printf("option ignored %v:%v\n", option, value)
}
default:
log.Printf(" not a string or slice %v:%v\n", option, value)
}

}
cs[name.(string)] = *ca
default:
log.Printf("Couldn't find options in %v", optionmap)
}
}
default:
log.Println("Couldn't find services in %v", services)
}
c2.Services = cs
case "networks":
switch networks := section.(type) {
case map[interface{}]interface{}:
c2.Networks = make(map[string]interface{})
for name, options := range networks {
//fmt.Printf("network %v:%v\n", name, options)
s, ok := name.(string)
if ok {
c2.Networks[s] = options
} else {
log.Printf("Can't convert %v:%v to string\n", name, options)
}
}
}
case "volumes":
switch volumes := section.(type) {
case map[interface{}]interface{}:
c2.Volumes = make(map[string]interface{})
for name, options := range volumes {
//fmt.Printf("volume %v:%v\n", name, options)
s, ok := name.(string)
if ok {
c2.Volumes[s] = options
} else {
log.Printf("Can't convert %v:%v to string\n", name, options)
}
}
}
default:
log.Printf("No matching section: %v\n", label)
}
}
default:
log.Println("Couldn't find sections in compose v2 file")
}
c2.Services = cs
return c2
}

func ComposeArch(name string, c ComposeServices) {
func ComposeArch(name string, c *ComposeV2Yaml) {
a := architecture.MakeArch(name, "compose yaml")
for n, v := range c {
nets := make(map[string][]string)
for n, v := range c.Services {
//fmt.Println("Compose: ", n, v.Image, v.Build, v.Links)
co := v.Image
if co == "" {
Expand All @@ -76,7 +188,42 @@ func ComposeArch(name string, c ComposeServices) {
for _, l := range v.Links {
links = append(links, strings.Split(l, ":")[0])
}
architecture.AddContainer(a, n, "machine", "instance", co, "process", "monolith", 1, 3, links)
var networks []string
for _, name := range v.Networks {
nets[name] = append(nets[name], n) // map of which services refer to a network
networks = append(networks, name) // list of networks this service refers to
}
var volumes []string
for _, nv := range v.Volumes {
name := strings.Split(nv, ":")[0] // get root volume name
volumes = append(volumes, name) // list of volumes this service refers to
}
if n == "db" {
architecture.AddContainer(a, n, "machine", "instance", co, "process", "staash", 1, 1, volumes)
} else {
if n == "redis" {
architecture.AddContainer(a, n, "machine", "instance", co, "process", "store", 1, 1, links)
} else {
architecture.AddContainer(a, n, "machine", "instance", co, "process", "monolith", 1, 3, links)
}
}
external := false
for _, port := range v.Ports {
if len(strings.Split(port, ":")) >= 2 {
external = true
}
}
if external {
var extlink []string
extlink = append(extlink, n)
architecture.AddContainer(a, "www-"+n, "external", "", "", "", "denominator", 0, 0, extlink)
}
}
for n, _ := range c.Networks {
architecture.AddContainer(a, n, "network", "", "", "", "elb", 1, 0, nets[n])
}
for n, _ := range c.Volumes {
architecture.AddContainer(a, n, "volume", "", "", "", "store", 1, 0, nil)
}
architecture.Write(a)
architecture.WriteFile(a, name)
}
1 change: 1 addition & 0 deletions compose/composeTestResult.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"arch":"composeTestResult","version":"arch-0.1","description":"compose yaml","args":"[/var/folders/vy/jc5bpnm10xb3cdwmf9skr2rjgt5hnf/T/go-build233586086/github.com/adrianco/spigo/compose/_test/compose.test]","date":"2016-04-09T12:50:42.316647737-07:00","services":[{"name":"voting-app","machine":"machine","instance":"instance","container":"./voting-app/.","process":"process","package":"monolith","regions":1,"count":3,"dependencies":["redis"]},{"name":"www-voting-app","machine":"external","package":"denominator","count":0,"dependencies":["voting-app"]},{"name":"result-app","machine":"machine","instance":"instance","container":"./result-app/.","process":"process","package":"monolith","regions":1,"count":3,"dependencies":["db"]},{"name":"www-result-app","machine":"external","package":"denominator","count":0,"dependencies":["result-app"]},{"name":"worker","machine":"machine","instance":"instance","container":"./worker","process":"process","package":"monolith","regions":1,"count":3,"dependencies":["db","redis"]},{"name":"redis","machine":"machine","instance":"instance","container":"redis","process":"process","package":"store","regions":1,"count":1,"dependencies":null},{"name":"db","machine":"machine","instance":"instance","container":"postgres:9.4","process":"process","package":"staash","regions":1,"count":1,"dependencies":["db-data"]},{"name":"front-tier","machine":"network","package":"elb","regions":1,"count":0,"dependencies":["voting-app","result-app"]},{"name":"back-tier","machine":"network","package":"elb","regions":1,"count":0,"dependencies":["voting-app","result-app","worker","redis","db"]},{"name":"db-data","machine":"volume","package":"store","regions":1,"count":0,"dependencies":null}]}
126 changes: 126 additions & 0 deletions compose/composeV2_arch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"arch": "composeTestResult",
"args": "[/var/folders/vy/jc5bpnm10xb3cdwmf9skr2rjgt5hnf/T/go-build980515732/github.com/adrianco/spigo/compose/_test/compose.test]",
"date": "2016-04-09T12:43:57.950415642-07:00",
"description": "compose yaml",
"services": [
{
"container": "./result-app/.",
"count": 3,
"dependencies": [
"db"
],
"instance": "instance",
"machine": "machine",
"name": "result-app",
"package": "monolith",
"process": "process",
"regions": 1
},
{
"count": 0,
"dependencies": [
"result-app"
],
"machine": "external",
"name": "www-result-app",
"package": "denominator"
},
{
"container": "./worker",
"count": 3,
"dependencies": [
"db",
"redis"
],
"instance": "instance",
"machine": "machine",
"name": "worker",
"package": "monolith",
"process": "process",
"regions": 1
},
{
"container": "redis",
"count": 1,
"dependencies": null,
"instance": "instance",
"machine": "machine",
"name": "redis",
"package": "store",
"process": "process",
"regions": 1
},
{
"container": "postgres:9.4",
"count": 1,
"dependencies": [
"db-data"
],
"instance": "instance",
"machine": "machine",
"name": "db",
"package": "staash",
"process": "process",
"regions": 1
},
{
"container": "./voting-app/.",
"count": 3,
"dependencies": [
"redis"
],
"instance": "instance",
"machine": "machine",
"name": "voting-app",
"package": "monolith",
"process": "process",
"regions": 1
},
{
"count": 0,
"dependencies": [
"voting-app"
],
"machine": "external",
"name": "www-voting-app",
"package": "denominator"
},
{
"count": 0,
"dependencies": [
"result-app",
"voting-app"
],
"machine": "network",
"name": "front-tier",
"package": "elb",
"regions": 1
},
{
"count": 0,
"dependencies": [
"result-app",
"worker",
"redis",
"db",
"voting-app"
],
"machine": "network",
"name": "back-tier",
"package": "elb",
"regions": 1
},
{
"count": 0,
"dependencies": [
"db"
],
"machine": "volume",
"name": "db-data",
"package": "store",
"regions": 1
}
],
"version": "arch-0.1"
}
12 changes: 9 additions & 3 deletions compose/compose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,22 @@ db:
//archaius.Conf.Collect = false
//archaius.Conf.StopStep = 0
archaius.Conf.EurekaPoll = "1s"
fmt.Println("\nTesting Parser from Docker Compose V1 string")
try(testyaml)

fmt.Println("\nTesting file conversion from Docker Compose V1 compose_yaml/test.yml")
c := ReadCompose("compose_yaml/test.yml")
fmt.Println(c)

fmt.Println("\nTesting Docker Compose V2 format input from compose_yaml/testV2.yml")
file, err := os.Open("compose_yaml/testV2.yml")
if err != nil {
println("File does not exist:", err.Error())
os.Exit(1)
}
defer file.Close()
document := new(interface{})
// too hard to parse V2 yaml with decoder, so use candiedyaml to walk the structure
decoder := candiedyaml.NewDecoder(file)
err = decoder.Decode(document)
if err != nil {
Expand Down Expand Up @@ -187,7 +192,8 @@ db:
fmt.Println("Couldn't find sections in compose v2 file")
}

//c2 := ReadCompose("compose_yaml/testV2.yml")
//fmt.Println(c2)
//ComposeArch("test", c)
fmt.Println("\nTesting ReadComposeV2(compose_yaml/testV2.yml)")
c2 := ReadComposeV2("compose_yaml/testV2.yml")
fmt.Println(*c2)
ComposeArch("composeTestResult", c2)
}
Loading

0 comments on commit 5df525b

Please sign in to comment.