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

Commit 02d6425

Browse files
author
David Chung
authored
YAML support (#457)
Signed-off-by: David Chung <[email protected]>
1 parent 843edf3 commit 02d6425

File tree

30 files changed

+1430
-520
lines changed

30 files changed

+1430
-520
lines changed

cmd/cli/base/init.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package base
2+
3+
import (
4+
logutil "github.com/docker/infrakit/pkg/log"
5+
)
6+
7+
var log = logutil.New("module", "cli/base")

cmd/cli/base/template.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package base
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"path"
8+
"strings"
9+
10+
"github.com/docker/infrakit/pkg/discovery"
11+
metadata_template "github.com/docker/infrakit/pkg/plugin/metadata/template"
12+
"github.com/docker/infrakit/pkg/template"
13+
"github.com/ghodss/yaml"
14+
"github.com/spf13/pflag"
15+
)
16+
17+
// ProcessTemplateFunc is the function that processes the template at url and returns view or error.
18+
type ProcessTemplateFunc func(url string) (rendered string, err error)
19+
20+
// ToJSONFunc converts the input buffer to json format
21+
type ToJSONFunc func(in []byte) (json []byte, err error)
22+
23+
// FromJSONFunc converts json formatted input to output buffer
24+
type FromJSONFunc func(json []byte) (out []byte, err error)
25+
26+
// ReadFromStdinIfElse checks condition and reads from stdin if true; otherwise it executes other.
27+
func ReadFromStdinIfElse(
28+
condition func() bool,
29+
otherwise func() (string, error),
30+
toJSON ToJSONFunc) (rendered string, err error) {
31+
32+
if condition() {
33+
buff, err := ioutil.ReadAll(os.Stdin)
34+
if err != nil {
35+
return "", err
36+
}
37+
json, err := toJSON(buff)
38+
log.Debug("stdin", "buffer", string(json))
39+
return string(json), err
40+
}
41+
rendered, err = otherwise()
42+
if err != nil {
43+
return
44+
}
45+
var buff []byte
46+
buff, err = toJSON([]byte(rendered))
47+
if err != nil {
48+
return
49+
}
50+
return string(buff), nil
51+
}
52+
53+
// TemplateProcessor returns a flagset and a function for processing template input.
54+
func TemplateProcessor(plugins func() discovery.Plugins) (*pflag.FlagSet, ToJSONFunc, FromJSONFunc, ProcessTemplateFunc) {
55+
56+
fs := pflag.NewFlagSet("template", pflag.ExitOnError)
57+
58+
globals := fs.StringSliceP("global", "g", []string{}, "key=value pairs of 'global' values in template")
59+
yamlDoc := fs.BoolP("yaml", "y", false, "True if input is in yaml format; json is the default")
60+
dump := fs.BoolP("dump", "x", false, "True to dump to output instead of executing")
61+
62+
return fs,
63+
// ToJSONFunc
64+
func(in []byte) (json []byte, err error) {
65+
66+
defer func() {
67+
68+
if *dump {
69+
fmt.Println("Raw:")
70+
fmt.Println(string(in))
71+
fmt.Println("Converted")
72+
fmt.Println(string(json))
73+
os.Exit(0) // special for debugging
74+
}
75+
}()
76+
77+
if *yamlDoc {
78+
json, err = yaml.YAMLToJSON(in)
79+
return
80+
}
81+
json = in
82+
return
83+
84+
},
85+
// FromJSONFunc
86+
func(json []byte) (out []byte, err error) {
87+
88+
defer func() {
89+
90+
if *dump {
91+
fmt.Println("Raw:")
92+
fmt.Println(string(json))
93+
fmt.Println("Converted")
94+
fmt.Println(string(out))
95+
os.Exit(0) // special for debugging
96+
}
97+
}()
98+
99+
if *yamlDoc {
100+
out, err = yaml.JSONToYAML(json)
101+
return
102+
}
103+
out = json
104+
return
105+
106+
},
107+
// ProcessTemplateFunc
108+
func(url string) (view string, err error) {
109+
110+
if !strings.Contains(url, "://") {
111+
p := url
112+
if dir, err := os.Getwd(); err == nil {
113+
p = path.Join(dir, url)
114+
}
115+
url = "file://" + p
116+
}
117+
118+
log.Debug("reading template", "url", url)
119+
engine, err := template.NewTemplate(url, template.Options{})
120+
if err != nil {
121+
return
122+
}
123+
124+
for _, global := range *globals {
125+
kv := strings.SplitN(global, "=", 2)
126+
if len(kv) != 2 {
127+
log.Warn("bad format kv", "input", global)
128+
continue
129+
}
130+
key := strings.TrimSpace(kv[0])
131+
val := strings.TrimSpace(kv[1])
132+
if key != "" && val != "" {
133+
engine.Global(key, val)
134+
}
135+
}
136+
137+
engine.WithFunctions(func() []template.Function {
138+
return []template.Function{
139+
{
140+
Name: "metadata",
141+
Description: []string{
142+
"Metadata function takes a path of the form \"plugin_name/path/to/data\"",
143+
"and calls GET on the plugin with the path \"path/to/data\".",
144+
"It's identical to the CLI command infrakit metadata cat ...",
145+
},
146+
Func: metadata_template.MetadataFunc(plugins),
147+
},
148+
{
149+
Name: "resource",
150+
Func: func(s string) string {
151+
return fmt.Sprintf("{{ resource `%s` }}", s)
152+
},
153+
},
154+
}
155+
})
156+
157+
view, err = engine.Render(nil)
158+
if err != nil {
159+
return
160+
}
161+
162+
// if *yamlDoc {
163+
164+
// // Convert this to json if it's not in JSON -- the default is for the template to be in YAML
165+
// converted, e := yaml.YAMLToJSON([]byte(view))
166+
// log.Debug("converting yaml to json", "before", view, "after", string(converted))
167+
168+
// if e != nil {
169+
// err = e
170+
// return
171+
// }
172+
173+
// view = string(converted)
174+
// log.Debug("view", "rendered", view)
175+
// }
176+
177+
log.Debug("rendered", "view", view)
178+
if *dump {
179+
fmt.Println("Final:")
180+
fmt.Println(string(view))
181+
os.Exit(0)
182+
}
183+
return
184+
}
185+
}

cmd/cli/flavor/flavor.go

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package flavor
33
import (
44
"encoding/json"
55
"fmt"
6-
"io/ioutil"
76
"os"
87
"strings"
98

@@ -37,6 +36,7 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
3736
Short: "Access flavor plugin",
3837
}
3938
name := cmd.PersistentFlags().String("name", "", "Name of plugin")
39+
flavorPropertiesURL := cmd.PersistentFlags().String("properties", "", "Properties of the flavor plugin, a url")
4040

4141
cmd.PersistentPreRunE = func(c *cobra.Command, args []string) error {
4242
if err := cli.EnsurePersistentPreRunE(c); err != nil {
@@ -104,92 +104,108 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
104104
return group_types.Index{Group: group.ID(groupID), Sequence: groupSequence}
105105
}
106106

107+
///////////////////////////////////////////////////////////////////////////////////
108+
// validate
109+
validateTemplateFlags, toJSON, _, validateProcessTemplate := base.TemplateProcessor(plugins)
107110
validate := &cobra.Command{
108-
Use: "validate <flavor configuration file>",
109-
Short: "validate a flavor configuration",
111+
Use: "validate",
112+
Short: "Validate a flavor configuration",
110113
RunE: func(cmd *cobra.Command, args []string) error {
111114

112-
if len(args) != 1 {
115+
if len(args) != 0 {
113116
cmd.Usage()
114117
os.Exit(1)
115118
}
116119

117-
buff, err := ioutil.ReadFile(args[0])
120+
view, err := validateProcessTemplate(*flavorPropertiesURL)
118121
if err != nil {
119-
log.Warn("error", "err", err)
120-
os.Exit(1)
122+
return err
123+
}
124+
125+
buff, err := toJSON([]byte(view))
126+
if err != nil {
127+
return err
121128
}
122129

123130
return flavorPlugin.Validate(types.AnyBytes(buff), allocationMethodFromFlags())
124131
},
125132
}
133+
validate.Flags().AddFlagSet(validateTemplateFlags)
126134
addAllocationMethodFlags(validate)
127-
cmd.AddCommand(validate)
128135

136+
///////////////////////////////////////////////////////////////////////////////////
137+
// prepare
138+
prepareTemplateFlags, toJSON, _, prepareProcessTemplate := base.TemplateProcessor(plugins)
129139
prepare := &cobra.Command{
130-
Use: "prepare <flavor configuration file> <instance Spec JSON file>",
131-
Short: "prepare provisioning inputs for an instance",
140+
Use: "prepare <instance Spec template url>",
141+
Short: "Prepare provisioning inputs for an instance. Read from stdin if url is '-'",
132142
RunE: func(cmd *cobra.Command, args []string) error {
133143

134-
if len(args) != 2 {
144+
if len(args) != 1 {
135145
cmd.Usage()
136146
os.Exit(1)
137147
}
138148

139-
flavorProperties, err := ioutil.ReadFile(args[0])
149+
flavorProperties, err := prepareProcessTemplate(*flavorPropertiesURL)
140150
if err != nil {
141-
log.Warn("error", "err", err)
142-
os.Exit(1)
151+
return err
143152
}
144153

145-
buff, err := ioutil.ReadFile(args[1])
154+
view, err := base.ReadFromStdinIfElse(
155+
func() bool { return args[0] == "-" },
156+
func() (string, error) { return prepareProcessTemplate(args[0]) },
157+
toJSON,
158+
)
146159
if err != nil {
147-
log.Warn("error", "err", err)
148-
os.Exit(1)
160+
return err
149161
}
150162

151163
spec := instance.Spec{}
152-
if err := json.Unmarshal(buff, &spec); err != nil {
164+
if err := types.AnyString(view).Decode(&spec); err != nil {
153165
return err
154166
}
155167

156168
spec, err = flavorPlugin.Prepare(
157-
types.AnyBytes(flavorProperties),
169+
types.AnyString(flavorProperties),
158170
spec,
159171
allocationMethodFromFlags(),
160172
indexFromFlags(),
161173
)
162-
if err == nil {
163-
buff, err = json.MarshalIndent(spec, " ", " ")
164-
if err == nil {
165-
fmt.Println(string(buff))
166-
}
174+
if err != nil {
175+
return err
176+
}
177+
178+
if buff, err := json.MarshalIndent(spec, " ", " "); err == nil {
179+
fmt.Println(string(buff))
167180
}
168181
return err
169182
},
170183
}
184+
prepare.Flags().AddFlagSet(prepareTemplateFlags)
171185
addAllocationMethodFlags(prepare)
172186
indexFlags(prepare)
173-
cmd.AddCommand(prepare)
174187

188+
///////////////////////////////////////////////////////////////////////////////////
189+
// healthy
190+
healthyTemplateFlags, toJSON, _, healthyProcessTemplate := base.TemplateProcessor(plugins)
175191
healthy := &cobra.Command{
176-
Use: "healthy <flavor configuration file>",
192+
Use: "healthy",
177193
Short: "checks if an instance is considered healthy",
178194
}
179195
tags := healthy.Flags().StringSlice("tags", []string{}, "Tags to filter")
180196
id := healthy.Flags().String("id", "", "ID of resource")
181197
logicalID := healthy.Flags().String("logical-id", "", "Logical ID of resource")
198+
healthy.Flags().AddFlagSet(healthyTemplateFlags)
182199
healthy.RunE = func(cmd *cobra.Command, args []string) error {
183200

184-
if len(args) != 1 {
201+
if len(args) != 0 {
185202
cmd.Usage()
186203
os.Exit(1)
187204
}
188205

189-
flavorProperties, err := ioutil.ReadFile(args[0])
206+
flavorProperties, err := healthyProcessTemplate(*flavorPropertiesURL)
190207
if err != nil {
191-
log.Warn("error", "err", err)
192-
os.Exit(1)
208+
return err
193209
}
194210

195211
filter := map[string]string{}
@@ -214,13 +230,18 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
214230
desc.LogicalID = &logical
215231
}
216232

217-
healthy, err := flavorPlugin.Healthy(types.AnyBytes(flavorProperties), desc)
233+
healthy, err := flavorPlugin.Healthy(types.AnyString(flavorProperties), desc)
218234
if err == nil {
219235
fmt.Printf("%v\n", healthy)
220236
}
221237
return err
222238
}
223-
cmd.AddCommand(healthy)
239+
240+
cmd.AddCommand(
241+
validate,
242+
prepare,
243+
healthy,
244+
)
224245

225246
return cmd
226247
}

0 commit comments

Comments
 (0)