From 0ba96142c99c56fadddf89cb5e940cd310d68ee2 Mon Sep 17 00:00:00 2001 From: David Chung Date: Sun, 7 Jan 2018 13:08:43 -0800 Subject: [PATCH 1/4] playbook flag for accepting defaults; fix playbook plugin flag and allows setting plugin from flag; clean up backends Signed-off-by: David Chung --- cmd/infrakit/main.go | 2 +- pkg/cli/backend/instance/provision.go | 54 ++++++++--------- pkg/cli/backend/manager/commit.go | 85 --------------------------- pkg/cli/backend/stack/enforce.go | 54 +++++++++++++++++ pkg/cli/context.go | 50 ++++++++++------ 5 files changed, 111 insertions(+), 134 deletions(-) delete mode 100644 pkg/cli/backend/manager/commit.go create mode 100644 pkg/cli/backend/stack/enforce.go diff --git a/cmd/infrakit/main.go b/cmd/infrakit/main.go index 7c77957c3..353c1d1de 100644 --- a/cmd/infrakit/main.go +++ b/cmd/infrakit/main.go @@ -34,10 +34,10 @@ import ( // CLI backends _ "github.com/docker/infrakit/pkg/cli/backend/http" _ "github.com/docker/infrakit/pkg/cli/backend/instance" - _ "github.com/docker/infrakit/pkg/cli/backend/manager" _ "github.com/docker/infrakit/pkg/cli/backend/print" _ "github.com/docker/infrakit/pkg/cli/backend/sh" _ "github.com/docker/infrakit/pkg/cli/backend/ssh" + _ "github.com/docker/infrakit/pkg/cli/backend/stack" _ "github.com/docker/infrakit/pkg/cli/backend/vmwscript" // Supported "kinds" diff --git a/pkg/cli/backend/instance/provision.go b/pkg/cli/backend/instance/provision.go index 141e454e8..31ec3d758 100644 --- a/pkg/cli/backend/instance/provision.go +++ b/pkg/cli/backend/instance/provision.go @@ -8,32 +8,38 @@ import ( "github.com/docker/infrakit/pkg/spi/instance" "github.com/docker/infrakit/pkg/types" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func init() { - backend.Register("instanceProvision", Provision, nil) + backend.Register("instanceProvision", Provision, + func(flags *pflag.FlagSet) { + flags.String("plugin", "", "plugin") + }) } -// Provision backend requires the name of the plugin and a boolean to indicate if the content is yaml. -// It then returns an executable function based on that specification to call the named instance plugin's provision -// method. +// Provision returns an executable function based on that specification to call the named instance plugin's provision +// method. The optional parameter in the playbook script can be overridden by the value of the `--plugin` flag +// in the command line. func Provision(scope scope.Scope, test bool, opt ...interface{}) (backend.ExecFunc, error) { - if len(opt) != 2 { - return nil, fmt.Errorf("require params: pluginName (string) and isYAML (bool)") - } - - name, is := opt[0].(string) - if !is { - return nil, fmt.Errorf("first param (pluginName) must be string") - } + return func(script string, cmd *cobra.Command, args []string) error { - isYAML, is := opt[1].(bool) - if !is { - return nil, fmt.Errorf("second param (isYAML) must be a bool") - } + var name string - return func(script string, cmd *cobra.Command, args []string) error { + // Optional parameter for plugin name can be overridden by the value of the flag (--plugin): + if len(opt) > 0 { + s, is := opt[0].(string) + if !is { + return fmt.Errorf("first param (pluginName) must be string") + } + name = s + } + if n, err := cmd.Flags().GetString("plugin"); err != nil { + return err + } else if n != "" { + name = n + } plugin, err := scope.Instance(name) if err != nil { @@ -41,18 +47,8 @@ func Provision(scope scope.Scope, test bool, opt ...interface{}) (backend.ExecFu } spec := instance.Spec{} - if isYAML { - y, err := types.AnyYAML([]byte(script)) - if err != nil { - return err - } - if err := y.Decode(&spec); err != nil { - return err - } - } else { - if err := types.AnyString(script).Decode(&spec); err != nil { - return err - } + if err := types.Decode([]byte(script), &spec); err != nil { + return err } id, err := plugin.Provision(spec) diff --git a/pkg/cli/backend/manager/commit.go b/pkg/cli/backend/manager/commit.go deleted file mode 100644 index 8ccf67249..000000000 --- a/pkg/cli/backend/manager/commit.go +++ /dev/null @@ -1,85 +0,0 @@ -package manager - -import ( - "fmt" - - "github.com/docker/infrakit/pkg/cli/backend" - logutil "github.com/docker/infrakit/pkg/log" - "github.com/docker/infrakit/pkg/plugin" - "github.com/docker/infrakit/pkg/run/scope" - "github.com/docker/infrakit/pkg/spi/group" - "github.com/docker/infrakit/pkg/types" - "github.com/spf13/cobra" -) - -var log = logutil.New("module", "cli/backend/manager") - -func init() { - backend.Register("managerCommit", Commit, nil) -} - -// Commit requires two parameters, first is isYAML (bool) and second is pretend (bool) -// It then returns an executable function based on that specification to call the manager's commit -// method with the content -func Commit(scope scope.Scope, test bool, opt ...interface{}) (backend.ExecFunc, error) { - - if len(opt) != 2 { - return nil, fmt.Errorf("require params: isYAML (bool), pretend (bool)") - } - - isYAML, is := opt[0].(bool) - if !is { - return nil, fmt.Errorf("first param (isYAML) must be a bool") - } - - pretend, is := opt[1].(bool) - if !is { - return nil, fmt.Errorf("second param (pretend) must be a bool") - } - - return func(script string, cmd *cobra.Command, args []string) error { - groups := []plugin.Spec{} - if isYAML { - y, err := types.AnyYAML([]byte(script)) - if err != nil { - return err - } - if err := y.Decode(&groups); err != nil { - return err - } - } else { - if err := types.AnyString(script).Decode(&groups); err != nil { - return err - } - } - - // Check the list of plugins - for _, gp := range groups { - - // unmarshal the group spec - spec := group.Spec{} - if gp.Properties != nil { - err := gp.Properties.Decode(&spec) - if err != nil { - return err - } - } - - target, err := scope.Group(gp.Plugin.String()) - log.Debug("commit", "plugin", gp.Plugin, "err", err, "spec", spec) - - if err != nil { - return err - } - - plan, err := target.CommitGroup(spec, pretend) - if err != nil { - return err - } - - fmt.Println("Group", spec.ID, "with plugin", gp.Plugin, "plan:", plan) - } - return nil - - }, nil -} diff --git a/pkg/cli/backend/stack/enforce.go b/pkg/cli/backend/stack/enforce.go new file mode 100644 index 000000000..19fca2555 --- /dev/null +++ b/pkg/cli/backend/stack/enforce.go @@ -0,0 +1,54 @@ +package stack + +import ( + "fmt" + + "github.com/docker/infrakit/pkg/cli/backend" + "github.com/docker/infrakit/pkg/run/scope" + "github.com/docker/infrakit/pkg/types" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func init() { + backend.Register("stackEnforce", Enforce, + func(flags *pflag.FlagSet) { + flags.String("stack", "", "Name of stack") + }) +} + +// Enforce backend requires the name of the plugin and a boolean to indicate if the content is yaml. +// It then returns an executable function based on that specification to call the named instance plugin's provision +// method. +func Enforce(scope scope.Scope, test bool, opt ...interface{}) (backend.ExecFunc, error) { + + return func(script string, cmd *cobra.Command, args []string) error { + + var name string + + // Optional parameter for plugin name can be overridden by the value of the flag (--stack): + if len(opt) > 0 { + n, is := opt[0].(string) + if !is { + return fmt.Errorf("first param (stackName) must be string") + } + name = n + } + name, err := cmd.Flags().GetString("stack") + if err != nil { + return err + } + + stack, err := scope.Stack(name) + if err != nil { + return err + } + + specs := types.Specs{} + if err := types.Decode([]byte(script), &specs); err != nil { + return err + } + + return stack.Enforce(specs) + }, nil +} diff --git a/pkg/cli/context.go b/pkg/cli/context.go index a2f79bec1..cc77c1ca6 100644 --- a/pkg/cli/context.go +++ b/pkg/cli/context.go @@ -23,17 +23,18 @@ const ( // Context is the context for the running module type Context struct { - test bool - printOnly bool - cmd *cobra.Command - src string - input io.Reader - exec bool - template *template.Template - options template.Options - run func(string, *cobra.Command, []string) error - script string - scope scope.Scope + test bool + printOnly bool + acceptDefaults bool + cmd *cobra.Command + src string + input io.Reader + exec bool + template *template.Template + options template.Options + run func(string, *cobra.Command, []string) error + script string + scope scope.Scope } // NewContext creates a context @@ -185,7 +186,7 @@ func parseBool(text string) (bool, error) { } // Prompt handles prompting the user using the given prompt message, type string and optional values. -func Prompt(in io.Reader, prompt, ftype string, optional ...interface{}) (interface{}, error) { +func Prompt(in io.Reader, prompt, ftype string, acceptDefaults bool, optional ...interface{}) (interface{}, error) { def, label := "", "" if len(optional) > 0 { def = fmt.Sprintf("%v", optional[0]) @@ -194,12 +195,19 @@ func Prompt(in io.Reader, prompt, ftype string, optional ...interface{}) (interf } } - input := bufio.NewReader(in) - fmt.Fprintf(os.Stderr, "%s %s: ", prompt, label) - text, _ := input.ReadString('\n') - text = strings.Trim(text, " \t\n") - if len(text) == 0 { + var text string // user input + + if def != "" && acceptDefaults { text = def + } else { + // TODO(chungers) - something fancier so we can support reading of passwords without echoing to screen + input := bufio.NewReader(in) + fmt.Fprintf(os.Stderr, "%s %s: ", prompt, label) + text, _ := input.ReadString('\n') + text = strings.Trim(text, " \t\n") + if len(text) == 0 { + text = def + } } switch ftype { @@ -378,6 +386,8 @@ func (c *Context) Funcs() []template.Function { end := optional[len(optional)-1] + // if the last argument is actually a function that is generated + // by the 'cond' function if cond, is := end.(func() (bool, interface{})); is { ok, last := cond() if !ok { @@ -397,7 +407,8 @@ func (c *Context) Funcs() []template.Function { } } - return Prompt(c.input, prompt, ftype, optional...) + + return Prompt(c.input, prompt, ftype, c.acceptDefaults, optional...) }, }, { @@ -436,7 +447,7 @@ func (c *Context) Funcs() []template.Function { } } - p, err := Prompt(c.input, prompt, ftype, optional...) + p, err := Prompt(c.input, prompt, ftype, c.acceptDefaults, optional...) pl = strings.Split(p.(string), ",") return pl, err }, @@ -459,6 +470,7 @@ func (c *Context) getTemplate() (*template.Template, error) { func (c *Context) BuildFlags() (err error) { c.cmd.Flags().BoolVar(&c.test, "test", false, "True to do a trial run") c.cmd.Flags().BoolVar(&c.printOnly, "print-only", false, "True to print the rendered input") + c.cmd.Flags().BoolVar(&c.acceptDefaults, "accept-defaults", false, "True to accept defaults of prompts and flags") var t *template.Template t, err = c.getTemplate() From 96b6fc9b572ac6690f883360ae009ae18bb1c946 Mon Sep 17 00:00:00 2001 From: David Chung Date: Sun, 7 Jan 2018 13:09:23 -0800 Subject: [PATCH 2/4] clean up aws plugin; fix up environment variables Signed-off-by: David Chung --- pkg/provider/aws/plugin/instance/monitor.go | 10 ++- pkg/rpc/client/client.go | 20 ++---- pkg/run/local/local.go | 11 ++++ pkg/run/v0/aws/aws.go | 69 ++++++++++++++------- 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/pkg/provider/aws/plugin/instance/monitor.go b/pkg/provider/aws/plugin/instance/monitor.go index 844bfd5a2..8e5c72ed2 100644 --- a/pkg/provider/aws/plugin/instance/monitor.go +++ b/pkg/provider/aws/plugin/instance/monitor.go @@ -18,8 +18,9 @@ const ( // list of known instances, and report anything it hasn't seen before, or // if anything that disappeared. type Monitor struct { - stop chan struct{} - topics map[string]interface{} + PollInterval time.Duration + stop chan struct{} + topics map[string]interface{} // Plugin is the instance plugin to use Plugin instance.Plugin @@ -64,7 +65,10 @@ func (m *Monitor) PublishOn(c chan<- *event.Event) { log.Infoln("Start monitoring instances", c) - ticker := time.Tick(2 * time.Second) + ticker := time.Tick(5 * time.Second) + if m.PollInterval > 0 { + ticker = time.Tick(m.PollInterval) + } instances := map[instance.ID]instance.Description{} last := mapset.NewSet() diff --git a/pkg/rpc/client/client.go b/pkg/rpc/client/client.go index 069172980..c3fcd39f4 100644 --- a/pkg/rpc/client/client.go +++ b/pkg/rpc/client/client.go @@ -8,13 +8,14 @@ import ( "net/http" "net/http/httputil" "net/url" - "os" + // "os" "path" "sync" "time" logutil "github.com/docker/infrakit/pkg/log" "github.com/docker/infrakit/pkg/rpc" + "github.com/docker/infrakit/pkg/run/local" "github.com/docker/infrakit/pkg/spi" "github.com/gorilla/rpc/v2/json2" ) @@ -74,17 +75,6 @@ func New(address string, api spi.InterfaceSpec) (Client, error) { return cl, nil } -// ClientTimeoutEnv environment variable for the client timeout (in time.Duration) -const ClientTimeoutEnv = "INFRAKIT_CLIENT_TIMEOUT" - -// timeout returns the client rpc http timeout -func timeout() time.Duration { - if parsed, err := time.ParseDuration(os.Getenv(ClientTimeoutEnv)); err == nil { - return parsed - } - return time.Duration(1 * time.Second) -} - func parseAddress(address string) (*url.URL, *http.Client, error) { if path.Ext(address) == ".listen" { buff, err := ioutil.ReadFile(address) @@ -105,7 +95,7 @@ func parseAddress(address string) (*url.URL, *http.Client, error) { u.Host = "h" u.Path = "" // clear it since it's a file path and we are using it to connect. return u, &http.Client{ - Timeout: timeout(), + Timeout: local.ClientTimeout(), Transport: &http.Transport{ // TODO(chungers) - fix this deprecation Dial: func(proto, addr string) (conn net.Conn, err error) { @@ -122,9 +112,9 @@ func parseAddress(address string) (*url.URL, *http.Client, error) { case "http", "https": transport := &http.Transport{ Dial: (&net.Dialer{ - Timeout: timeout(), + Timeout: local.ClientTimeout(), }).Dial, - TLSHandshakeTimeout: timeout(), + TLSHandshakeTimeout: local.ClientTimeout(), } return u, &http.Client{Transport: transport}, nil diff --git a/pkg/run/local/local.go b/pkg/run/local/local.go index 0fb36feab..db4c02a84 100644 --- a/pkg/run/local/local.go +++ b/pkg/run/local/local.go @@ -5,6 +5,9 @@ import ( "os" "os/user" "path/filepath" + "time" + + "github.com/docker/infrakit/pkg/types" ) const ( @@ -14,8 +17,16 @@ const ( // EnvPlaybooks is the environment variable for storing the playbooks file EnvPlaybooks = "INFRAKIT_PLAYBOOKS_FILE" + + // EnvClientTimeout is the timeout used by the rpc client + EnvClientTimeout = "INFRAKIT_CLIENT_TIMEOUT" ) +// ClientTimeout returns the client timeout +func ClientTimeout() time.Duration { + return types.MustParseDuration(Getenv(EnvClientTimeout, "15s")).Duration() +} + // InfrakitHome returns the directory of INFRAKIT_HOME if specified. Otherwise, it will return // the user's home directory. If that cannot be determined, then it returns the current working // directory. If that still cannot be determined, a temporary directory is returned. diff --git a/pkg/run/v0/aws/aws.go b/pkg/run/v0/aws/aws.go index 606b649ad..6bec59063 100644 --- a/pkg/run/v0/aws/aws.go +++ b/pkg/run/v0/aws/aws.go @@ -2,6 +2,7 @@ package aws import ( "strings" + "time" "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" @@ -34,7 +35,7 @@ const ( EnvRegion = "INFRAKIT_AWS_REGION" // EnvStackName is the env for stack name - EnvStackName = "INFRAKIT_AWS_STACKNAME" + EnvStackName = "INFRAKIT_AWS_STACK_NAME" // EnvMetadataTemplateURL is the location of the template for Metadata plugin EnvMetadataTemplateURL = "INFRAKIT_AWS_METADATA_TEMPLATE_URL" @@ -42,6 +43,9 @@ const ( // EnvMetadataPollInterval is the env to set fo polling for metadata updates EnvMetadataPollInterval = "INFRAKIT_AWS_METADATA_POLL_INTERVAL" + // EnvMonitorPollInterval is the env to set fo polling for instance changes + EnvMonitorPollInterval = "INFRAKIT_AWS_MONITOR_POLL_INTERVAL" + // EnvNamespaceTags is the env to set for namespace tags. It's k=v,... EnvNamespaceTags = "INFRAKIT_AWS_NAMESPACE_TAGS" @@ -59,12 +63,16 @@ func init() { // Options capture the options for starting up the plugin. type Options struct { + // Namespace is a set of kv pairs for tags that namespaces the resource instances Namespace map[string]string // ELBNames is a list of names for ELB instances to start the L4 plugins ELBNames []string + // MonitorPollInterval is the interval for the polling to observe instance new/delete events + MonitorPollInterval time.Duration + aws_metadata.Options `json:",inline" yaml:",inline"` } @@ -82,13 +90,16 @@ func defaultNamespace() map[string]string { // DefaultOptions return an Options with default values filled in. var DefaultOptions = Options{ - Namespace: defaultNamespace(), - ELBNames: strings.Split(local.Getenv(EnvELBNames, ""), ","), + Namespace: defaultNamespace(), + ELBNames: strings.Split(local.Getenv(EnvELBNames, ""), ","), + MonitorPollInterval: types.MustParseDuration(local.Getenv(EnvMonitorPollInterval, "0s")).Duration(), Options: aws_metadata.Options{ Template: local.Getenv(EnvMetadataTemplateURL, ""), StackName: local.Getenv(EnvStackName, ""), Options: aws_instance.Options{ - Region: local.Getenv(EnvRegion, ""), // empty string trigger auto-detect + Region: local.Getenv(EnvRegion, ""), // empty string trigger auto-detect + AccessKeyID: local.Getenv("AWS_ACCESS_KEY_ID", ""), + SecretAccessKey: local.Getenv("AWS_SECRET_ACCESS_KEY", ""), }, PollInterval: types.MustParseDuration(local.Getenv(EnvMetadataPollInterval, "60s")), }, @@ -105,13 +116,6 @@ func Run(scope scope.Scope, name plugin.Name, return } - var metadataPlugin metadata.Plugin - stopMetadataPlugin := make(chan struct{}) - metadataPlugin, err = aws_metadata.NewPlugin(options.Options, stopMetadataPlugin) - if err != nil { - return - } - var instancePlugin instance.Plugin builder := aws_instance.Builder{Options: options.Options.Options} instancePlugin, err = builder.BuildInstancePlugin(options.Namespace) @@ -119,13 +123,6 @@ func Run(scope scope.Scope, name plugin.Name, return } - monitor := &aws_instance.Monitor{Plugin: instancePlugin} - - onStop = func() { - close(stopMetadataPlugin) - monitor.Stop() - } - autoscalingClient := autoscaling.New(builder.Config) cloudWatchLogsClient := cloudwatchlogs.New(builder.Config) dynamodbClient := dynamodb.New(builder.Config) @@ -136,10 +133,6 @@ func Run(scope scope.Scope, name plugin.Name, transport.Name = name impls = map[run.PluginCode]interface{}{ - run.Event: map[string]event.Plugin{ - "ec2-instance": monitor.Init(), - }, - run.Metadata: metadataPlugin, run.Instance: map[string]instance.Plugin{ "autoscaling-autoscalinggroup": aws_instance.NewAutoScalingGroupPlugin(autoscalingClient, options.Namespace), "autoscaling-launchconfiguration": aws_instance.NewLaunchConfigurationPlugin(autoscalingClient, options.Namespace), @@ -175,5 +168,37 @@ func Run(scope scope.Scope, name plugin.Name, impls[run.L4] = func() (map[string]loadbalancer.L4, error) { return l4Map, nil } } + if options.MonitorPollInterval > 0 { + log.Info("run the event source for watching instance add/removes", "poll", options.MonitorPollInterval) + monitor := &aws_instance.Monitor{Plugin: instancePlugin} + impls[run.Event] = map[string]event.Plugin{ + "ec2-instance": monitor.Init(), + } + onStop = func() { + monitor.Stop() + } + } + + if u := local.Getenv(EnvMetadataTemplateURL, ""); u != "" { + + log.Info("Include metadata plugin", "url", u) + + var metadataPlugin metadata.Plugin + stopMetadataPlugin := make(chan struct{}) + metadataPlugin, err = aws_metadata.NewPlugin(options.Options, stopMetadataPlugin) + if err != nil { + return + } + impls[run.Metadata] = metadataPlugin + + cleanup := onStop + onStop = func() { + close(stopMetadataPlugin) + if cleanup != nil { + cleanup() + } + } + } + return } From b6902ca778fd4fd19ff037cbbd0b0d64c3f9881c Mon Sep 17 00:00:00 2001 From: David Chung Date: Sun, 7 Jan 2018 13:11:29 -0800 Subject: [PATCH 3/4] example aws playbook Signed-off-by: David Chung --- examples/playbooks/aws/README.md | 21 +++++++ examples/playbooks/aws/index.yml | 9 +++ examples/playbooks/aws/provision-instance.yml | 56 +++++++++++++++++++ .../playbooks/aws/provision-spot-instance.yml | 49 ++++++++++++++++ examples/playbooks/aws/start.sh | 26 +++++++++ 5 files changed, 161 insertions(+) create mode 100644 examples/playbooks/aws/README.md create mode 100644 examples/playbooks/aws/index.yml create mode 100644 examples/playbooks/aws/provision-instance.yml create mode 100644 examples/playbooks/aws/provision-spot-instance.yml create mode 100644 examples/playbooks/aws/start.sh diff --git a/examples/playbooks/aws/README.md b/examples/playbooks/aws/README.md new file mode 100644 index 000000000..bd658e94f --- /dev/null +++ b/examples/playbooks/aws/README.md @@ -0,0 +1,21 @@ +Example Playbook for AWS +======================== + +This playbook contains example of working with AWS. There are commands for +starting up infrakit (which assumes you have used AWS CLI on your local computer and +the `.aws/credentials` file exists) and commands for spinning up an on-demand or +spot instance configured with Docker, Git and Go compiler. + +## Adding this playbook + +Adding files locally: + +``` +infrakit playbook add aws file://$(pwd)/index.yml +``` + +Adding the playbook from Github: + +``` +infrakit playbook add aws https://raw.githubusercontent.com/docker/infrakit/master/examples/playbooks/aws/index.yml +``` diff --git a/examples/playbooks/aws/index.yml b/examples/playbooks/aws/index.yml new file mode 100644 index 000000000..55375b304 --- /dev/null +++ b/examples/playbooks/aws/index.yml @@ -0,0 +1,9 @@ + +# Start infrakit +start : start.sh + +# Create a single instance +ondemand : provision-instance.yml + +# Create a spot instance +spot : provision-spot-instance.yml \ No newline at end of file diff --git a/examples/playbooks/aws/provision-instance.yml b/examples/playbooks/aws/provision-instance.yml new file mode 100644 index 000000000..fda0b47e5 --- /dev/null +++ b/examples/playbooks/aws/provision-instance.yml @@ -0,0 +1,56 @@ +{{/* Input to create instance using the AWS instance plugin */}} +{{/* =% instanceProvision `aws/ec2-instance` %= */}} + +{{ $user := flag "user" "string" "username" | prompt "Please enter your user name:" "string" (env "USER")}} +{{ $project := flag "project" "string" "project" | prompt "Project?" "string" "myproject" }} +{{ $imageId := flag "image-id" "string" "Image ID" | prompt "AMI?" "string" "ami-df8406b0" }} +{{ $instanceType := flag "instance-type" "string" "instance type" | prompt "Instance type?" "string" "t2.micro" }} +{{ $privateIp := flag "private-ip" "string" "Private IP" | prompt "Private IP address (IPv4)?" "string" "172.31.20.100" }} + + +{{ $keyName := flag "key" "string" "ssh key name" | prompt "SSH key?" "string" "infrakit"}} +{{ $az := flag "az" "string" "availability zone" | prompt "Availability zone?" "string" "eu-central-1b"}} +{{ $subnetId := flag "subnet" "string" "subnet id" | prompt "Subnet ID?" "string" "subnet-11b8376a" }} +{{ $securityGroupId := flag "security-group-id" "string" "security group id" | prompt "Security group ID?" "string" "sg-975500fe" }} + +Tags: + infrakit.scope: {{ $project }} + infrakit.created: {{ now | htmlDate }} + infrakit.user: {{ $user }} + +Init: | + #!/bin/bash + sudo add-apt-repository ppa:gophers/archive + sudo apt-get update -y + sudo apt-get install -y wget curl git golang-1.9-go + wget -qO- https://get.docker.com | sh + ln -s /usr/lib/go-1.9/bin/go /usr/local/bin/go + +Properties: + RunInstancesInput: + BlockDeviceMappings: null + DisableApiTermination: null + EbsOptimized: null + IamInstanceProfile: null + ImageId: {{ $imageId }} + InstanceInitiatedShutdownBehavior: null + InstanceType: {{ $instanceType }} + KeyName: {{ $keyName }} + NetworkInterfaces: + - AssociatePublicIpAddress: true + PrivateIpAddress: {{ $privateIp }} + DeleteOnTermination: true + DeviceIndex: 0 + Groups: + - {{ $securityGroupId }} + NetworkInterfaceId: null + SubnetId: {{ $subnetId }} + Placement: + Affinity: null + AvailabilityZone: {{ $az }} + Tenancy: null + RamdiskId: null + SubnetId: null + UserData: null + Tags: + infrakit.user: {{ $user }} diff --git a/examples/playbooks/aws/provision-spot-instance.yml b/examples/playbooks/aws/provision-spot-instance.yml new file mode 100644 index 000000000..c910bb8fc --- /dev/null +++ b/examples/playbooks/aws/provision-spot-instance.yml @@ -0,0 +1,49 @@ +{{/* Input to create a spot instance using the AWS instance plugin */}} +{{/* =% instanceProvision `aws/ec2-spot-instance` %= */}} + +{{ $user := flag "user" "string" "username" | prompt "Please enter your user name:" "string" (env "USER")}} +{{ $project := flag "project" "string" "project" | prompt "Project?" "string" "myproject" }} +{{ $imageId := flag "image-id" "string" "Image ID" | prompt "AMI?" "string" "ami-df8406b0" }} +{{ $instanceType := flag "instance-type" "string" "instance type" | prompt "Instance type?" "string" "t2.micro" }} +{{ $privateIp := flag "private-ip" "string" "Private IP" | prompt "Private IP address (IPv4)?" "string" "172.31.20.100" }} +{{ $spotPrice := flag "spot-price" "string" "Spot price" | prompt "Spot price?" "string" "0.03" }} + +{{ $keyName := flag "key" "string" "ssh key name" | prompt "SSH key?" "string" "infrakit"}} +{{ $az := flag "az" "string" "availability zone" | prompt "Availability zone?" "string" "eu-central-1b"}} +{{ $subnetId := flag "subnet" "string" "subnet id" | prompt "Subnet ID?" "string" "subnet-11b8376a" }} +{{ $securityGroupId := flag "security-group-id" "string" "security group id" | prompt "Security group ID?" "string" "sg-975500fe" }} + +Tags: + infrakit.scope: {{ $project }} + infrakit.created: {{ now | htmlDate }} + infrakit.user: {{ $user }} + +Init: | + #!/bin/bash + sudo add-apt-repository ppa:gophers/archive + sudo apt-get update -y + sudo apt-get install -y wget curl git golang-1.9-go + wget -qO- https://get.docker.com | sh + ln -s /usr/lib/go-1.9/bin/go /usr/local/bin/go + +Properties: + RequestSpotInstancesInput: + LaunchSpecification: + ImageId: {{ $imageId }} + InstanceType: {{ $instanceType }} + KeyName: infrakit + NetworkInterfaces: + - AssociatePublicIpAddress: true + DeleteOnTermination: true + DeviceIndex: 0 + Groups: + - {{ $securityGroupId }} + NetworkInterfaceId: null + PrivateIpAddress: {{ $privateIp }} + PrivateIpAddresses: null + SecondaryPrivateIpAddressCount: null + SubnetId: {{ $subnetId }} + Placement: + AvailabilityZone: {{ $az }} + SpotPrice: "{{ $spotPrice }}" + Type: one-time diff --git a/examples/playbooks/aws/start.sh b/examples/playbooks/aws/start.sh new file mode 100644 index 000000000..81efc31a8 --- /dev/null +++ b/examples/playbooks/aws/start.sh @@ -0,0 +1,26 @@ +{{/* =% sh %= */}} + +{{ $profile := flag "aws-cred-profile" "string" "Profile name" | prompt "Profile for your .aws/credentials?" "string" "default" }} +{{ $region := flag "region" "string" "aws region" | prompt "Region?" "string" "eu-central-1"}} +{{ $project := flag "project" "string" "project name" | prompt "Project?" "string" "myproject" }} + +echo "Starting infrakit with aws plugin..." + +{{/* Pick a credential from the local user's ~/.aws folder. You should have this if you use awscli. */}} +{{ $creds := (source (cat "file://" (env "HOME") "/.aws/credentials" | nospace) | iniDecode | k $profile ) }} +FOUND="{{ not (empty $creds) }}" + +if [ $FOUND = "false" ]; then + echo "no credentials found. bye" + exit 1 +fi + +{{ echo "Found your credential for profile" $profile }} + +AWS_ACCESS_KEY_ID={{ $creds.aws_access_key_id }} \ +AWS_SECRET_ACCESS_KEY={{ $creds.aws_secret_access_key }} \ +INFRAKIT_AWS_REGION={{ $region }} \ +INFRAKIT_AWS_STACK_NAME={{ $project }} \ +INFRAKIT_AWS_NAMESPACE_TAGS="infrakit.scope={{ $project }}" \ +INFRAKIT_AWS_MONITOR_POLL_INTERVAL=5s \ +infrakit plugin start aws From 5bf8bf22db796aabc0a9f1c5bfedfec92d14532d Mon Sep 17 00:00:00 2001 From: David Chung Date: Sun, 7 Jan 2018 15:38:10 -0800 Subject: [PATCH 4/4] fix broken test from shadowing of variables Signed-off-by: David Chung --- pkg/cli/context.go | 11 +++++------ pkg/cli/context_test.go | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pkg/cli/context.go b/pkg/cli/context.go index cc77c1ca6..37305bf1e 100644 --- a/pkg/cli/context.go +++ b/pkg/cli/context.go @@ -195,16 +195,15 @@ func Prompt(in io.Reader, prompt, ftype string, acceptDefaults bool, optional .. } } - var text string // user input + text := def + if !acceptDefaults { - if def != "" && acceptDefaults { - text = def - } else { // TODO(chungers) - something fancier so we can support reading of passwords without echoing to screen input := bufio.NewReader(in) fmt.Fprintf(os.Stderr, "%s %s: ", prompt, label) - text, _ := input.ReadString('\n') - text = strings.Trim(text, " \t\n") + str, _ := input.ReadString('\n') + text = strings.Trim(str, " \t\n") + if len(text) == 0 { text = def } diff --git a/pkg/cli/context_test.go b/pkg/cli/context_test.go index c05b9a986..de41562b5 100644 --- a/pkg/cli/context_test.go +++ b/pkg/cli/context_test.go @@ -14,7 +14,6 @@ import ( _ "github.com/docker/infrakit/pkg/cli/backend/http" _ "github.com/docker/infrakit/pkg/cli/backend/instance" - _ "github.com/docker/infrakit/pkg/cli/backend/manager" _ "github.com/docker/infrakit/pkg/cli/backend/print" _ "github.com/docker/infrakit/pkg/cli/backend/sh" )