Skip to content
This repository was archived by the owner on Jan 21, 2020. It is now read-only.

Commit 4725f4b

Browse files
author
David Chung
authored
Common Spec schema and dependency ordering across all resource / group /plugin types (#473)
Signed-off-by: David Chung <[email protected]>
1 parent 2b888b7 commit 4725f4b

31 files changed

+2597
-20
lines changed

docs/playbooks/intro/gcp/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Running InfraKit on GCP
2+
=======================
3+

docs/playbooks/intro/gcp/index.ikb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
# Create a single instance
3+
provision-instance : provision-instance.ikt
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Input to create instance using the GCP instance plugin
2+
{{/* =% sh %= */}}
3+
4+
{{ $user := flag "user" "string" "owner" | prompt "Owner?" "string" (env "USER") nil }}
5+
{{ $prefix := flag "prefix" "string" "Prefix to use" | prompt "Prefix for hostname:" "string" (env "USER") }}
6+
{{ $diskSize := flag "disk-size" "int" "Disk size in mb" | prompt "Disk size in MB?" "int" 100 }}
7+
{{ $machineType := flag "machine-type" "string" "Machine type" | prompt "Machine type?" "string" "n1-standard-1"}}
8+
{{ $privateIP := flag "private-ip" "string" "Private IP" | prompt "Private IP address (IPv4)?" "string" "10.128.0.10" nil }}
9+
{{ $image := flag "image" "string" "Image" | prompt "Image to boot?" "string" "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20161205" }}
10+
11+
echo "===> Running with {{$user}}, {{$image}}, {{$privateIP}}:"
12+
13+
infrakit --log 3 --log-stack --name instance-gcp instance provision -y - <<EOF
14+
15+
LogicalID : {{ $privateIP }}
16+
Tags:
17+
infrakit-user: {{ $user }}
18+
infrakit-created: {{ now | htmlDate }}
19+
20+
Init: |
21+
#!/bin/bash
22+
sudo apt-get update -y
23+
sudo apt-get install wget curl
24+
wget -q0- https://get.docker.com | sh
25+
26+
Properties:
27+
NamePrefix: {{ $prefix }}
28+
PrivateIP: {{ $privateIP }}
29+
Description: Some description
30+
Network: default
31+
MachineType: {{ $machineType }}
32+
DiskSizeMb: {{ $diskSize }}
33+
DiskImage: {{ $image }}
34+
Scopes:
35+
- https://www.googleapis.com/auth/cloudruntimeconfig
36+
- https://www.googleapis.com/auth/logging.write
37+
Tags:
38+
- {{ $user }}
39+
40+
EOF
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
{{/* =% sh %= */}}
4+
5+
{{ $gcp := flag "start-gcp" "bool" "Start GCP plugin" | prompt "Start GCP plugin?" "bool" "no" }}
6+
{{ $defaultCred := cat (env "HOME") "/.config/gcloud/application_default_credentials.json" | nospace }}
7+
{{ $credentials := flag "credential-path" "string" "Path to credentials.json" | cond $gcp | prompt "Credentials JSON path?" "string" $defaultCred }}
8+
{{ $zone := flag "zone" "string" "GCP zone" | cond $gcp | prompt "What's the zone?" "string" "us-centra1-a"}}
9+
10+
{{ $gcpImage := flag "gcp-plugin" "string" "Image of the plugin" | cond $gcp | prompt "What's the GCP plugin image?" "string" "infrakit/gcp:dev" }}
11+
12+
13+
{{ if $gcp }}
14+
15+
{{ $project := flag "project" "string" "Project name" | cond $gcp | prompt "What's the name of the project?" "string" }}
16+
17+
{{ $gcpCredentials := (cat $credentials ":/infrakit/platforms/gcp/credentials.json" | nospace) }}
18+
19+
20+
# Starting docker container for instance plugin
21+
docker run -d --volumes-from infrakit --name instance-plugin \
22+
-v {{$gcpCredentials}} {{$gcpImage}} infrakit-instance-gcp \
23+
--namespace-tags {{cat "infrakit.scope=" $project | nospace}} \
24+
--zone {{ $zone }} --log 5 --project {{ $project }}
25+
26+
{{ end }}

docs/playbooks/intro/start-infrakit.ikt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ docker run -d --volumes-from infrakit --name manager \
3636

3737
{{/* Here we just source a file in the same folder for more reuse and modularity */}}
3838
{{ source "aws/start-plugin.ikt" }}
39+
40+
{{ source "gcp/start-plugin.ikt" }}

pkg/cli/remote/remote.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,18 @@ loop:
109109
}
110110
}
111111

112+
// Documentation -- look for a README.md at the given dir
113+
readme := path.Join(path.Dir(string(url)), "README.md")
114+
if t, err := template.NewTemplate(readme, template.Options{}); err == nil {
115+
if view, err := t.Render(nil); err == nil {
116+
cmd.SetHelpTemplate(fmt.Sprintf(helpTemplate, view))
117+
}
118+
}
119+
112120
// if we can parse it as a map, then we have a 'directory'
113121
mods, err := dir(url)
114122
if err == nil {
115123

116-
// Documentation -- look for a README.md at the given dir
117-
readme := path.Join(path.Dir(string(url)), "README.md")
118-
if t, err := template.NewTemplate(readme, template.Options{}); err == nil {
119-
if view, err := t.Render(nil); err == nil {
120-
cmd.SetHelpTemplate(fmt.Sprintf(helpTemplate, view))
121-
}
122-
}
123-
124124
copy := url
125125
subs, err := list(plugins, mods, input, cmd, &copy)
126126
if err != nil {

pkg/core/depend.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package core
2+
3+
import (
4+
"encoding/json"
5+
"reflect"
6+
7+
"github.com/docker/infrakit/pkg/types"
8+
"github.com/twmb/algoimpl/go/graph"
9+
)
10+
11+
// converts a map to a Spec, nil if it cannot be done
12+
func mapToSpec(m map[string]interface{}) *types.Spec {
13+
// This is hacky -- generate a string representation
14+
// and try to parse it as struct
15+
buff, err := json.Marshal(m)
16+
if err != nil {
17+
return nil
18+
}
19+
s := types.Spec{}
20+
err = json.Unmarshal(buff, &s)
21+
if err != nil {
22+
return nil
23+
}
24+
if s.Validate() == nil {
25+
return &s
26+
}
27+
return nil
28+
}
29+
30+
// findSpecs parses the bytes and returns a Spec, if the Spec can be parsed
31+
// from the buffer. Some fields are verified and must be present for the
32+
// buffer to be considered a representation of a Spec.
33+
func findSpecs(v interface{}) []*types.Spec {
34+
35+
result := []*types.Spec{}
36+
37+
switch v := v.(type) {
38+
39+
case []*types.Spec:
40+
for _, x := range v {
41+
c := *x
42+
result = append(result, findSpecs(&c)...)
43+
}
44+
45+
case []types.Spec:
46+
for _, x := range v {
47+
c := x
48+
result = append(result, findSpecs(&c)...)
49+
}
50+
51+
case []interface{}:
52+
for _, x := range v {
53+
c := x
54+
result = append(result, findSpecs(c)...)
55+
}
56+
57+
case map[string]interface{}:
58+
// convert to Spec?
59+
result = append(result, findSpecs(mapToSpec(v))...)
60+
61+
case *types.Any:
62+
63+
if v == nil {
64+
return result
65+
}
66+
67+
spec := types.Spec{}
68+
if err := v.Decode(&spec); err == nil {
69+
70+
if spec.Validate() == nil {
71+
result = append(result, findSpecs(&spec)...)
72+
return result
73+
}
74+
75+
}
76+
77+
// now decode as regular struct - map or []interface{}
78+
var vv interface{}
79+
if err := v.Decode(&vv); err != nil {
80+
return nil
81+
}
82+
83+
switch vv := vv.(type) {
84+
case []interface{}:
85+
for _, x := range vv {
86+
result = append(result, findSpecs(x)...)
87+
}
88+
case map[string]interface{}:
89+
for _, x := range vv {
90+
result = append(result, findSpecs(x)...)
91+
}
92+
}
93+
94+
case types.Spec:
95+
c := v
96+
result = append(result, &c)
97+
result = append(result, findSpecs(c.Properties)...)
98+
99+
case *types.Spec:
100+
101+
if v == nil {
102+
return result
103+
}
104+
105+
c := *v
106+
result = append(result, &c)
107+
result = append(result, findSpecs(c.Properties)...)
108+
109+
default:
110+
value := reflect.Indirect(reflect.ValueOf(v))
111+
if value.Type().Kind() == reflect.Struct {
112+
for i := 0; i < value.NumField(); i++ {
113+
fv := value.Field(i)
114+
if fv.IsValid() {
115+
result = append(result, findSpecs(fv.Interface())...)
116+
}
117+
}
118+
}
119+
}
120+
return result
121+
}
122+
123+
// nested recurses through the Properties of the spec and returns any nested specs.
124+
func nested(s *types.Spec) []*types.Spec {
125+
if s.Properties == nil {
126+
return nil
127+
}
128+
return findSpecs(s.Properties)
129+
}
130+
131+
type key struct {
132+
class string
133+
name string
134+
}
135+
136+
func indexSpecs(specs []*types.Spec, g *graph.Graph) map[key]*graph.Node {
137+
index := map[key]*graph.Node{}
138+
for _, spec := range specs {
139+
140+
node := g.MakeNode()
141+
*(node.Value) = spec
142+
143+
index[key{class: spec.Class, name: spec.Metadata.Name}] = &node
144+
}
145+
return index
146+
}
147+
148+
func indexGet(index map[key]*graph.Node, class, name string) *graph.Node {
149+
if v, has := index[key{class: class, name: name}]; has {
150+
return v
151+
}
152+
return nil
153+
}
154+
155+
// AllSpecs returns a list of all the specs given plus any nested specs
156+
func AllSpecs(specs []*types.Spec) []*types.Spec {
157+
all := []*types.Spec{}
158+
for _, s := range specs {
159+
all = append(all, s)
160+
all = append(all, nested(s)...)
161+
}
162+
return all
163+
}
164+
165+
// OrderByDependency returns the given specs in dependency order. The input is assume to be exhaustive in that
166+
// all nested specs are part of the list.
167+
func OrderByDependency(specs []*types.Spec) ([]*types.Spec, error) {
168+
169+
g := graph.New(graph.Directed)
170+
if g == nil {
171+
return nil, nil
172+
}
173+
174+
index := indexSpecs(specs, g)
175+
176+
for _, spec := range specs {
177+
178+
from := indexGet(index, spec.Class, spec.Metadata.Name)
179+
if from == nil {
180+
return nil, errNotFound{class: spec.Class, name: spec.Metadata.Name}
181+
}
182+
183+
for _, depend := range spec.Depends {
184+
185+
to := indexGet(index, depend.Class, depend.Name)
186+
if to == nil {
187+
return nil, errBadDependency(depend)
188+
}
189+
190+
if from == to {
191+
192+
a := (*from.Value).(*types.Spec)
193+
b := (*to.Value).(*types.Spec)
194+
return nil, errCircularDependency([]*types.Spec{a, b})
195+
}
196+
197+
if err := g.MakeEdge(*to, *from); err != nil {
198+
return nil, err
199+
}
200+
}
201+
}
202+
203+
// cycle detection
204+
for _, connected := range g.StronglyConnectedComponents() {
205+
if len(connected) > 1 {
206+
207+
cycle := []*types.Spec{}
208+
for _, n := range connected {
209+
cycle = append(cycle, (*n.Value).(*types.Spec))
210+
}
211+
return nil, errCircularDependency(cycle)
212+
}
213+
}
214+
215+
ordered := []*types.Spec{}
216+
for _, n := range g.TopologicalSort() {
217+
ordered = append(ordered, (*n.Value).(*types.Spec))
218+
}
219+
220+
return ordered, nil
221+
}

0 commit comments

Comments
 (0)