func (r *Run) runCreate(client *cf.CloudFormation, d tacks.Document, stack string) error { e := d.Environment tacks.Logger().Infof("Creating stack %s", e.StackName) var ( capabilities []*string onFailure = "DO_NOTHING" tags []*cf.Tag timeoutInMinutes uint8 = 15 ) if d.IsIamCapabilitiesRequired() { capabilities = append(capabilities, aws.String("CAPABILITY_IAM")) } if e.DeleteOnFailure { onFailure = "DELETE" } if e.Timeout > 0 { timeoutInMinutes = e.Timeout } for key, value := range e.Tags { tags = append(tags, &cf.Tag{ Key: aws.String(key), Value: aws.String(value), }) } _, err := client.CreateStack(&cf.CreateStackInput{ Capabilities: capabilities, OnFailure: aws.String(onFailure), StackName: aws.String(e.StackName), Tags: tags, TemplateBody: aws.String(stack), TimeoutInMinutes: aws.Long(int64(timeoutInMinutes)), }) return err }
func (r *Run) runUpdate(client *cf.CloudFormation, d tacks.Document, stack string) error { e := d.Environment tacks.Logger().Infof("Updating stack %s", e.StackName) var ( capabilities []*string ) if d.IsIamCapabilitiesRequired() { capabilities = append(capabilities, aws.String("CAPABILITY_IAM")) } _, err := client.UpdateStack(&cf.UpdateStackInput{ Capabilities: capabilities, StackName: aws.String(e.StackName), TemplateBody: aws.String(stack), }) return err }
func (w *Watch) Run() error { const null = "" if w.Stackname == null { return errors.New("no stack name given") } tacks.Logger().Infof("Watching stack %s", w.Stackname) if w.Refresh == 0 { w.Refresh = DefaultRefresh } name := aws.String(w.Stackname) client := cf.New(&aws.Config{ Region: w.Region, }) for { width, height, err := term.Size() if err != nil { return err } resp, err := client.DescribeStackEvents(&cf.DescribeStackEventsInput{ StackName: name, }) if err != nil { return err } tw := table.NewWriter(os.Stdout) tw.SetColWidth(width / 4) tw.SetColumnSeparator(" ") tw.SetHeader([]string{ "Timestamp", "ID Logical", "ID Physical", "Status", "Reason", }) var max = height / 2 if l := len(resp.StackEvents); max > l { max = l } for _, e := range resp.StackEvents[0:max] { var reason string if r := e.ResourceStatusReason; r != nil { reason = strings.Replace(*r, "\n", " ", -1) } timestamp := humanize.Time(*e.Timestamp) data := []string{ timestamp, *e.LogicalResourceID, *e.ResourceType, term.CloudFormation.Colorize(*e.ResourceStatus), reason, } tw.Append(data) fmt.Print("\033[H\033[2J") tw.Render() } time.Sleep(w.Refresh) } }
func main() { var logger = tacks.Logger() exitF = logger.Fatal var ( dryRun bool environment string region string refresh time.Duration verbose bool ) root := &cobra.Command{ Use: "tacks", Short: "Tacks provides executable CloudFormation stacks", } root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output") root.PersistentPreRun = func(_ *cobra.Command, _ []string) { if verbose { logger.Level = logrus.DebugLevel } } run := &cobra.Command{ Use: "run [tacks stack filename]", Short: "Run a tacks stack", Run: func(_ *cobra.Command, args []string) { var filename string if len(args) > 0 { filename = args[0] } run := &command.Run{ DryRun: dryRun, Environment: environment, Filename: filename, Region: region, } command.Foreground(run, exitF) if !dryRun { watch := &command.Watch{ Stackname: run.Document().Environment.StackName, Region: run.Region, } command.Background(watch, exitF) term.Wait() } }, } runFlags := run.Flags() runFlags.BoolVarP(&dryRun, "dry-run", "d", false, "output stack to stderr instead of sending it to CloudFormation") runFlags.StringVarP(&environment, "environment", "e", "", "specify the environment") watch := &cobra.Command{ Use: "watch [stack-name]", Short: "Watch stack events", Run: func(_ *cobra.Command, args []string) { var stackname string if len(args) > 0 { stackname = args[0] } watch := &command.Watch{ Stackname: stackname, Region: region, Refresh: refresh, } command.Background(watch, exitF) term.Wait() }, } for _, value := range []*cobra.Command{run, watch} { value.Flags().StringVarP(®ion, "region", "r", DefaultRegion, "AWS region") value.Flags().DurationVarP(&refresh, "refresh-interval", "i", command.DefaultRefresh, "refresh interval for watching stack events") } version := &cobra.Command{ Use: "version", Short: "Print the version information of tacks", Run: func(cmd *cobra.Command, args []string) { fmt.Printf("Tacks %s (%s)\n", version, build) }, } root.AddCommand(run) root.AddCommand(watch) root.AddCommand(version) if err := root.Execute(); err != nil { logger.Fatal(err) } }