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

Commit e4e9d7c

Browse files
author
David Chung
authored
Example using GCP plugin with scriptable CLI (#465)
Signed-off-by: David Chung <[email protected]>
1 parent c3ed98a commit e4e9d7c

File tree

7 files changed

+359
-12
lines changed

7 files changed

+359
-12
lines changed

cmd/cli/base/output.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,54 @@ func RawOutput() (*pflag.FlagSet, RawOutputFunc) {
3939
return true, nil
4040
}
4141
}
42+
43+
// OutputFunc is a function that writes some data to the output writer
44+
type OutputFunc func(w io.Writer, v interface{}) (rendered bool, err error)
45+
46+
// Output returns the flagset and the func for printing output
47+
func Output() (*pflag.FlagSet, OutputFunc) {
48+
49+
fs := pflag.NewFlagSet("output", pflag.ExitOnError)
50+
51+
yamlDoc := fs.BoolP("yaml", "y", false, "True if input is in yaml format; json is the default")
52+
return fs, func(w io.Writer, v interface{}) (rendered bool, err error) {
53+
54+
var out string
55+
56+
switch v := v.(type) {
57+
case string:
58+
if *yamlDoc {
59+
if y, err := yaml.JSONToYAML([]byte(v)); err == nil {
60+
out = string(y)
61+
}
62+
} else {
63+
out = v
64+
}
65+
case []byte:
66+
if *yamlDoc {
67+
if y, err := yaml.JSONToYAML(v); err == nil {
68+
out = string(y)
69+
}
70+
} else {
71+
out = string(v)
72+
}
73+
default:
74+
any, err := types.AnyValue(v)
75+
if err != nil {
76+
return false, err
77+
}
78+
79+
buff := any.Bytes()
80+
if *yamlDoc {
81+
if y, err := yaml.JSONToYAML(buff); err == nil {
82+
out = string(y)
83+
}
84+
} else {
85+
out = any.String()
86+
}
87+
}
88+
89+
fmt.Fprintln(w, out)
90+
return true, nil
91+
}
92+
}

cmd/cli/instance/instance.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,19 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
153153
}
154154
tags := describe.Flags().StringSlice("tags", []string{}, "Tags to filter")
155155
properties := describe.Flags().BoolP("properties", "p", false, "Also returns current status/ properties")
156-
propertiesTemplate := describe.Flags().StringP("view", "v", "{{.}}", "Template to render properties")
156+
tagsTemplate := describe.Flags().StringP("tags-view", "t", "*", "Template to render tags")
157+
propertiesTemplate := describe.Flags().StringP("properties-view", "v", "{{.}}", "Template to render properties")
157158

158159
rawOutputFlags, rawOutput := base.RawOutput()
159160
describe.Flags().AddFlagSet(rawOutputFlags)
160161

161162
describe.RunE = func(cmd *cobra.Command, args []string) error {
162163

163-
view, err := template.New("describe-instances").Parse(*propertiesTemplate)
164+
tagsView, err := template.New("describe-instances-tags").Parse(*tagsTemplate)
165+
if err != nil {
166+
return err
167+
}
168+
propertiesView, err := template.New("describe-instances-properties").Parse(*propertiesTemplate)
164169
if err != nil {
165170
return err
166171
}
@@ -202,17 +207,24 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
202207
logical = string(*d.LogicalID)
203208
}
204209

205-
printTags := []string{}
206-
for k, v := range d.Tags {
207-
printTags = append(printTags, fmt.Sprintf("%s=%s", k, v))
210+
tagViewBuff := ""
211+
if *tagsTemplate == "*" {
212+
// default -- this is a hack
213+
printTags := []string{}
214+
for k, v := range d.Tags {
215+
printTags = append(printTags, fmt.Sprintf("%s=%s", k, v))
216+
}
217+
sort.Strings(printTags)
218+
tagViewBuff = strings.Join(printTags, ",")
219+
} else {
220+
tagViewBuff = renderTags(d.Tags, tagsView)
208221
}
209-
sort.Strings(printTags)
210222

211223
if *properties {
212-
fmt.Printf("%-30s\t%-30s\t%-30s\t%-s\n", d.ID, logical, strings.Join(printTags, ","),
213-
renderProperties(d.Properties, view))
224+
fmt.Printf("%-30s\t%-30s\t%-30s\t%-s\n", d.ID, logical, tagViewBuff,
225+
renderProperties(d.Properties, propertiesView))
214226
} else {
215-
fmt.Printf("%-30s\t%-30s\t%-s\n", d.ID, logical, strings.Join(printTags, ","))
227+
fmt.Printf("%-30s\t%-30s\t%-s\n", d.ID, logical, tagViewBuff)
216228
}
217229
}
218230
}
@@ -230,6 +242,15 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
230242
return cmd
231243
}
232244

245+
func renderTags(m map[string]string, view *template.Template) string {
246+
buff := new(bytes.Buffer)
247+
err := view.Execute(buff, m)
248+
if err != nil {
249+
return err.Error()
250+
}
251+
return buff.String()
252+
}
253+
233254
func renderProperties(properties *types.Any, view *template.Template) string {
234255
var v interface{}
235256
err := properties.Decode(&v)

cmd/cli/metadata/metadata.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package metadata
22

33
import (
44
"fmt"
5+
"os"
56
"strconv"
67

78
"github.com/docker/infrakit/cmd/cli/base"
@@ -206,6 +207,7 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
206207
return nil
207208
}
208209

210+
catFlags, catOutput := base.Output()
209211
cat := &cobra.Command{
210212
Use: "cat",
211213
Short: "Get metadata entry by path",
@@ -221,6 +223,10 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
221223
return err
222224
}
223225

226+
if match == nil {
227+
return fmt.Errorf("plugin not found:%v", *first)
228+
}
229+
224230
if path.Len() == 1 {
225231
fmt.Printf("%v\n", match != nil)
226232
} else {
@@ -231,7 +237,8 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
231237
if s, err := strconv.Unquote(value.String()); err == nil {
232238
str = s
233239
}
234-
fmt.Println(str)
240+
241+
catOutput(os.Stdout, str)
235242
}
236243
} else {
237244
log.Warn("Cannot metadata cat on plugin", "target", *first, "err", err)
@@ -242,6 +249,7 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
242249
return nil
243250
},
244251
}
252+
cat.Flags().AddFlagSet(catFlags)
245253

246254
cmd.AddCommand(ls, cat)
247255

examples/cli/aws/start-plugin.ikc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
{{ $stackName := flag "stack" "string" "stack name" | prompt "What's the name of the stack?" "string" }}
99

1010
{{ $instanceImage := "infrakit/aws:dev" }}
11-
{{ $instanceCmd := (cat "instance --log 5 --namespace-tags" (cat "infrakit.scope=" $stackName | nospace)) }}
1211

1312
{{ $infrakit := (cat (env "HOME") "/.infrakit/" | nospace) }}
1413
{{ $dockerMounts := (cat "-v /var/run/docker.sock:/var/run/docker.sock -v" (cat $infrakit ":/infrakit/" | nospace)) }}
@@ -22,7 +21,8 @@
2221
{{ if $userDocker }}
2322
# Starting docker container for aws instance plugin
2423
docker run -d --rm --name instance-plugin \
25-
{{$dockerMounts}} {{$dockerEnvs}} {{$instanceImage}} {{$instanceCmd}} \
24+
{{$dockerMounts}} {{$dockerEnvs}} {{$instanceImage}} infrakit-instance-aws \
25+
--namespace-tags {{cat "infrakit.scope=" $stackName | nospace}} \
2626
--access-key-id {{ $creds.aws_access_key_id }} \
2727
--secret-access-key {{ $creds.aws_secret_access_key }} \
2828
--region {{ $region }} --log 5

examples/cli/gcp/README.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
Creating Instance on GCP
2+
========================
3+
4+
You can use flags on the command line or start the plugin interactively:
5+
6+
```shell
7+
$ infrakit gcp start-plugin
8+
Run as Docker container? n
9+
What's the zone? us-central1-f
10+
What's the name of the project? docker4x
11+
Starting daemon
12+
Tailing log
13+
time="2017-04-05T00:34:40-07:00" level=debug msg="Using namespacemap[infrakit.scope:docker4x]"
14+
time="2017-04-05T00:34:40-07:00" level=debug msg="Project: docker4x"
15+
time="2017-04-05T00:34:40-07:00" level=debug msg="Zone: us-central1-f"
16+
time="2017-04-05T00:34:40-07:00" level=info msg="Listening at: /Users/davidchung/.infrakit/plugins/instance-gcp"
17+
time="2017-04-05T00:34:40-07:00" level=info msg="PID file at /Users/davidchung/.infrakit/plugins/instance-gcp.pid"
18+
```
19+
20+
Now in another shell, create an instance, using the `provision-instance` command which is basically a 'script'
21+
that is based on a YAML input and piped to `infrakit instance provision` itself:
22+
23+
```shell
24+
$ infrakit gcp -h
25+
26+
27+
___ ________ ________ ________ ________ ___ __ ___ _________
28+
|\ \|\ ___ \|\ _____\\ __ \|\ __ \|\ \|\ \ |\ \|\___ ___\
29+
\ \ \ \ \\ \ \ \ \__/\ \ \|\ \ \ \|\ \ \ \/ /|\ \ \|___ \ \_|
30+
\ \ \ \ \\ \ \ \ __\\ \ _ _\ \ __ \ \ ___ \ \ \ \ \ \
31+
\ \ \ \ \\ \ \ \ \_| \ \ \\ \\ \ \ \ \ \ \\ \ \ \ \ \ \ \
32+
\ \__\ \__\\ \__\ \__\ \ \__\\ _\\ \__\ \__\ \__\\ \__\ \__\ \ \__\
33+
\|__|\|__| \|__|\|__| \|__|\|__|\|__|\|__|\|__| \|__|\|__| \|__|
34+
35+
36+
gcp
37+
38+
Usage:
39+
infrakit gcp [command]
40+
41+
Available Commands:
42+
provision-instance provision-instance
43+
start-plugin start-plugin
44+
45+
Global Flags:
46+
-H, --host stringSlice host list. Default is local sockets
47+
--httptest.serve string if non-empty, httptest.NewServer serves on this address and blocks
48+
--log int log level (default 4)
49+
--log-caller include caller function (default true)
50+
--log-format string log format: logfmt|term|json (default "term")
51+
--log-stack include caller stack
52+
--log-stdout log to stdout
53+
54+
Use "infrakit gcp [command] --help" for more information about a command.
55+
```
56+
57+
```shell
58+
$ infrakit gcp provision-instance -h
59+
60+
61+
___ ________ ________ ________ ________ ___ __ ___ _________
62+
|\ \|\ ___ \|\ _____\\ __ \|\ __ \|\ \|\ \ |\ \|\___ ___\
63+
\ \ \ \ \\ \ \ \ \__/\ \ \|\ \ \ \|\ \ \ \/ /|\ \ \|___ \ \_|
64+
\ \ \ \ \\ \ \ \ __\\ \ _ _\ \ __ \ \ ___ \ \ \ \ \ \
65+
\ \ \ \ \\ \ \ \ \_| \ \ \\ \\ \ \ \ \ \ \\ \ \ \ \ \ \ \
66+
\ \__\ \__\\ \__\ \__\ \ \__\\ _\\ \__\ \__\ \__\\ \__\ \__\ \ \__\
67+
\|__|\|__| \|__|\|__| \|__|\|__|\|__|\|__|\|__| \|__|\|__| \|__|
68+
69+
70+
provision-instance
71+
72+
Usage:
73+
infrakit gcp provision-instance [flags]
74+
75+
Flags:
76+
--disk-size int Disk size in mb
77+
--machine-type string Machine type
78+
--prefix string Prefix to use
79+
--user string owner
80+
81+
Global Flags:
82+
-H, --host stringSlice host list. Default is local sockets
83+
--httptest.serve string if non-empty, httptest.NewServer serves on this address and blocks
84+
--log int log level (default 4)
85+
--log-caller include caller function (default true)
86+
--log-format string log format: logfmt|term|json (default "term")
87+
--log-stack include caller stack
88+
--log-stdout log to stdout
89+
```
90+
91+
Note that there are flags defined. You can set the in the commandline, or set the interactively:
92+
93+
```shell
94+
$ infrakit gcp provision-instance
95+
Owner? dchung
96+
Prefix for hostname: dchung
97+
Disk size in MB [60]? 100
98+
Machine type [n1-standard-1]? n1-standard-1
99+
dchung-2rg5i2
100+
```
101+
102+
Now you a list:
103+
104+
```shell
105+
$ infrakit instance --name instance-gcp describe
106+
ID LOGICAL TAGS
107+
dchung-kw5i37 - infrakit-created=2017-04-05,infrakit-user=dchung,infrakit.scope=docker4x,startup-script=#!/bin/bash
108+
sudo apt-get update -y
109+
sudo apt-get install wget curl
110+
wget -q0- https://get.docker.com | sh
111+
112+
```
113+
114+
Note the tags.. we can apply template to define the view on the tags.
115+
116+
```shell
117+
$ infrakit instance --name instance-gcp describe --tags-view="{{ len . }}"
118+
ID LOGICAL TAGS
119+
dchung-kw5i37 - 4
120+
```
121+
122+
Also, we can query for details and print out the raw data, as YAML:
123+
124+
```shell
125+
$ infrakit instance --name instance-gcp describe -pry
126+
- ID: dchung-kw5i37
127+
LogicalID: null
128+
Properties:
129+
cpuPlatform: Intel Ivy Bridge
130+
creationTimestamp: 2017-04-05T00:30:08.433-07:00
131+
description: Some description
132+
disks:
133+
- autoDelete: true
134+
boot: true
135+
deviceName: persistent-disk-0
136+
interface: SCSI
137+
kind: compute#attachedDisk
138+
licenses:
139+
- https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/licenses/ubuntu-1404-trusty
140+
mode: READ_WRITE
141+
source: https://www.googleapis.com/compute/v1/projects/docker4x/zones/us-central1-f/disks/dchung-kw5i37
142+
type: PERSISTENT
143+
id: "5569308151087965167"
144+
kind: compute#instance
145+
machineType: https://www.googleapis.com/compute/v1/projects/docker4x/zones/us-central1-f/machineTypes/n1-standard-1
146+
metadata:
147+
fingerprint: zMKHzh-yLow=
148+
items:
149+
- key: infrakit--scope
150+
value: docker4x
151+
- key: infrakit-created
152+
value: 2017-04-05
153+
- key: infrakit-user
154+
value: dchung
155+
- key: startup-script
156+
value: |
157+
#!/bin/bash
158+
sudo apt-get update -y
159+
sudo apt-get install wget curl
160+
wget -q0- https://get.docker.com | sh
161+
kind: compute#metadata
162+
name: dchung-kw5i37
163+
networkInterfaces:
164+
- accessConfigs:
165+
- kind: compute#accessConfig
166+
name: external-nat
167+
natIP: 104.154.99.160
168+
type: ONE_TO_ONE_NAT
169+
kind: compute#networkInterface
170+
name: nic0
171+
network: https://www.googleapis.com/compute/v1/projects/docker4x/global/networks/default
172+
networkIP: 10.128.0.3
173+
subnetwork: https://www.googleapis.com/compute/v1/projects/docker4x/regions/us-central1/subnetworks/default
174+
scheduling:
175+
automaticRestart: true
176+
onHostMaintenance: MIGRATE
177+
selfLink: https://www.googleapis.com/compute/v1/projects/docker4x/zones/us-central1-f/instances/dchung-kw5i37
178+
serviceAccounts:
179+
180+
scopes:
181+
- https://www.googleapis.com/auth/cloudruntimeconfig
182+
- https://www.googleapis.com/auth/logging.write
183+
status: RUNNING
184+
tags:
185+
fingerprint: pDfT_HVxXHI=
186+
items:
187+
- dchung
188+
zone: https://www.googleapis.com/compute/v1/projects/docker4x/zones/us-central1-f
189+
Tags:
190+
infrakit-created: 2017-04-05
191+
infrakit-user: dchung
192+
infrakit.scope: docker4x
193+
startup-script: |
194+
#!/bin/bash
195+
sudo apt-get update -y
196+
sudo apt-get install wget curl
197+
wget -q0- https://get.docker.com | sh
198+
```

0 commit comments

Comments
 (0)