Beispiel #1
1
func waitForCompletion(stack string, CloudFormation *cloudformation.CloudFormation, isDeleting bool) (string, error) {
	for {
		dres, err := CloudFormation.DescribeStacks(&cloudformation.DescribeStacksInput{
			StackName: aws.String(stack),
		})

		if err != nil {
			stdcli.Error(err)
		}

		err = displayProgress(stack, CloudFormation, isDeleting)

		if err != nil {
			stdcli.Error(err)
		}

		if len(dres.Stacks) != 1 {
			stdcli.Error(fmt.Errorf("could not read stack status"))
		}

		switch *dres.Stacks[0].StackStatus {
		case "CREATE_COMPLETE":
			// Dump .env if DEVELOPMENT
			if isDevelopment {
				fmt.Printf("Development .env:\n")

				// convert Port5432TcpAddr to PORT_5432_TCP_ADDR
				re := regexp.MustCompile("([a-z])([A-Z0-9])") // lower case letter followed by upper case or number, i.e. Port5432
				re2 := regexp.MustCompile("([0-9])([A-Z])")   // number followed by upper case letter, i.e. 5432Tcp

				for _, o := range dres.Stacks[0].Outputs {
					k := re.ReplaceAllString(*o.OutputKey, "${1}_${2}")
					k = re2.ReplaceAllString(k, "${1}_${2}")
					k = strings.ToUpper(k)

					fmt.Printf("%v=%v\n", k, *o.OutputValue)
				}
			}

			for _, o := range dres.Stacks[0].Outputs {
				if *o.OutputKey == "Dashboard" {
					return *o.OutputValue, nil
				}
			}

			return "", fmt.Errorf("could not install stack, contact [email protected] for assistance")
		case "CREATE_FAILED":
			return "", fmt.Errorf("stack creation failed, contact [email protected] for assistance")
		case "ROLLBACK_COMPLETE":
			return "", fmt.Errorf("stack creation failed, contact [email protected] for assistance")
		case "DELETE_COMPLETE":
			return "", nil
		case "DELETE_FAILED":
			return "", fmt.Errorf("stack deletion failed, contact [email protected] for assistance")
		}

		time.Sleep(2 * time.Second)
	}
}
// getCloudFormationFailures returns ResourceStatusReason(s)
// of events that should be failures based on regexp match of status
func getCloudFormationFailures(stackName *string, afterTime time.Time,
	conn *cloudformation.CloudFormation) ([]string, error) {
	var failures []string
	// Only catching failures from last 100 events
	// Some extra iteration logic via NextToken could be added
	// but in reality it's nearly impossible to generate >100
	// events by a single stack update
	events, err := conn.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
		StackName: stackName,
	})

	if err != nil {
		return nil, err
	}

	failRe := regexp.MustCompile("_FAILED$")
	rollbackRe := regexp.MustCompile("^ROLLBACK_")

	for _, e := range events.StackEvents {
		if (failRe.MatchString(*e.ResourceStatus) || rollbackRe.MatchString(*e.ResourceStatus)) &&
			e.Timestamp.After(afterTime) && e.ResourceStatusReason != nil {
			failures = append(failures, *e.ResourceStatusReason)
		}
	}

	return failures, nil
}
Beispiel #3
0
func stackLambdaResources(serviceName string, cf *cloudformation.CloudFormation, logger *logrus.Logger) (provisionedResources, error) {

	resources := make(provisionedResources, 0)
	nextToken := ""
	for {
		params := &cloudformation.ListStackResourcesInput{
			StackName: aws.String(serviceName),
		}
		if "" != nextToken {
			params.NextToken = aws.String(nextToken)
		}
		resp, err := cf.ListStackResources(params)

		if err != nil {
			logger.Error(err.Error())
			return nil, err
		}
		for _, eachSummary := range resp.StackResourceSummaries {
			if *eachSummary.ResourceType == "AWS::Lambda::Function" {
				resources = append(resources, eachSummary)
			}
		}
		if nil != resp.NextToken {
			nextToken = *resp.NextToken
		} else {
			break
		}
	}
	return resources, nil
}
Beispiel #4
0
// Does a given stack exist?
func stackExists(stackNameOrID string, cf *cloudformation.CloudFormation, logger *logrus.Logger) (bool, error) {
	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackNameOrID),
	}
	describeStacksOutput, err := cf.DescribeStacks(describeStacksInput)
	logger.WithFields(logrus.Fields{
		"DescribeStackOutput": describeStacksOutput,
	}).Debug("DescribeStackOutput results")

	exists := false
	if err != nil {
		logger.WithFields(logrus.Fields{
			"DescribeStackOutputError": err,
		}).Debug("DescribeStackOutput")

		// If the stack doesn't exist, then no worries
		if strings.Contains(err.Error(), "does not exist") {
			exists = false
		} else {
			return false, err
		}
	} else {
		exists = true
	}
	return exists, nil
}
Beispiel #5
0
func destroyStack(svc *cloudformation.CloudFormation, name string) error {
	dreq := &cloudformation.DeleteStackInput{
		StackName: aws.String(name),
	}
	_, err := svc.DeleteStack(dreq)
	return err
}
Beispiel #6
0
// Return the StackEvents for the given StackName/StackID
func stackEvents(stackID string, cfService *cloudformation.CloudFormation) ([]*cloudformation.StackEvent, error) {
	var events []*cloudformation.StackEvent

	nextToken := ""
	for {
		params := &cloudformation.DescribeStackEventsInput{
			StackName: aws.String(stackID),
		}
		if len(nextToken) > 0 {
			params.NextToken = aws.String(nextToken)
		}

		resp, err := cfService.DescribeStackEvents(params)
		if nil != err {
			return nil, err
		}
		events = append(events, resp.StackEvents...)
		if nil == resp.NextToken {
			break
		} else {
			nextToken = *resp.NextToken
		}
	}
	return events, nil
}
// getLastCfEventTimestamp takes the first event in a list
// of events ordered from the newest to the oldest
// and extracts timestamp from it
// LastUpdatedTime only provides last >successful< updated time
func getLastCfEventTimestamp(stackName string, conn *cloudformation.CloudFormation) (
	*time.Time, error) {
	output, err := conn.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
		StackName: aws.String(stackName),
	})
	if err != nil {
		return nil, err
	}

	return output.StackEvents[0].Timestamp, nil
}
Beispiel #8
0
func descStack(svc *awscf.CloudFormation, stackName string) {
	input := &awscf.DescribeStackEventsInput{
		StackName: aws.String(stackName),
	}
	resp, err := svc.DescribeStackEvents(input)
	if err != nil {
		log.Fatal(err)
	}

	if len(resp.StackEvents) > 0 {
		log.Println(awsutil.StringValue(resp.StackEvents[0]))
	}
}
Beispiel #9
0
func cost(svc *awscf.CloudFormation, b []byte, params []*awscf.Parameter) {
	estInput := &awscf.EstimateTemplateCostInput{
		Parameters:   params,
		TemplateBody: aws.String(string(b)),
	}

	cost, err := svc.EstimateTemplateCost(estInput)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(*cost.URL)
}
Beispiel #10
0
func validateStack(svc *cloudformation.CloudFormation, stackBody string) (string, error) {

	input := &cloudformation.ValidateTemplateInput{
		TemplateBody: aws.String(stackBody),
	}

	validationReport, err := svc.ValidateTemplate(input)

	if err != nil {
		return "", fmt.Errorf("Invalid cloudformation stack: %v", err)
	}

	return validationReport.String(), err
}
Beispiel #11
0
func deleteStack(s Stack, distinctId string, CF *cloudformation.CloudFormation) error {
	deleteAttempts[s.StackName] += 1
	switch deleteAttempts[s.StackName] {
	case 1:
		fmt.Printf("Deleting %s...\n", s.Name)
	default:
		fmt.Printf("Retrying deleting %s...\n", s.Name)
	}

	_, err := CF.DeleteStack(&cloudformation.DeleteStackInput{
		StackName: aws.String(s.StackName),
	})
	return err
}
Beispiel #12
0
Datei: run.go Projekt: inka/tacks
func (r *Run) runUpsert(client *cf.CloudFormation, d tacks.Document, stack string) error {

	e := d.Environment

	resp, _ := client.DescribeStacks(&cf.DescribeStacksInput{
		StackName: aws.String(e.StackName),
	})

	if len(resp.Stacks) == 0 {
		return r.runCreate(client, d, stack)
	} else {
		return r.runUpdate(client, d, stack)
	}

}
Beispiel #13
0
func updateStack(svc *cloudformation.CloudFormation, stackName, stackBody string) (string, error) {

	input := &cloudformation.UpdateStackInput{
		Capabilities: []*string{aws.String(cloudformation.CapabilityCapabilityIam)},
		StackName:    aws.String(stackName),
		TemplateBody: aws.String(stackBody),
	}

	updateOutput, err := svc.UpdateStack(input)

	if err != nil {
		return "", fmt.Errorf("Error updating cloudformation stack: %v", err)
	}

	return updateOutput.String(), waitForStackUpdateComplete(svc, *updateOutput.StackId)
}
func getCloudFormationFailures(stackId string, conn *cloudformation.CloudFormation) ([]string, error) {
	var failures []string

	err := conn.DescribeStackEventsPages(&cloudformation.DescribeStackEventsInput{
		StackName: aws.String(stackId),
	}, func(page *cloudformation.DescribeStackEventsOutput, lastPage bool) bool {
		for _, e := range page.StackEvents {
			if cfStackEventIsFailure(e) {
				failures = append(failures, *e.ResourceStatusReason)
			}
		}
		return !lastPage
	})

	return failures, err
}
Beispiel #15
0
func delStack(svc *awscf.CloudFormation, stackName string) {
	input := &awscf.DeleteStackInput{
		StackName: aws.String(stackName),
	}

	_, err := svc.DeleteStack(input)
	if err != nil {
		log.Fatal(err)
	}
	// the log.Println ends up looking like
	// 2015/06/04 16:55:36 {
	//
	// }
	//
	// log.Println(awsutil.StringValue(resp))
}
Beispiel #16
0
func waitForCompletion(stack string, CloudFormation *cloudformation.CloudFormation, isDeleting bool) (string, error) {
	for {
		dres, err := CloudFormation.DescribeStacks(&cloudformation.DescribeStacksInput{
			StackName: aws.String(stack),
		})
		if err != nil {
			return "", err
		}

		err = displayProgress(stack, CloudFormation, isDeleting)
		if err != nil {
			return "", err
		}

		if len(dres.Stacks) != 1 {
			return "", fmt.Errorf("could not read stack status")
		}

		switch *dres.Stacks[0].StackStatus {
		case "CREATE_COMPLETE":
			for _, o := range dres.Stacks[0].Outputs {
				if *o.OutputKey == "Dashboard" {
					return *o.OutputValue, nil
				}
			}

			stdcli.QOSEventSend("cli-install", distinctID, stdcli.QOSEventProperties{Error: err})
			return "", fmt.Errorf("could not install stack, contact [email protected] for assistance")
		case "CREATE_FAILED":
			stdcli.QOSEventSend("cli-install", distinctID, stdcli.QOSEventProperties{Error: err})
			return "", fmt.Errorf("stack creation failed, contact [email protected] for assistance")
		case "ROLLBACK_COMPLETE":
			stdcli.QOSEventSend("cli-install", distinctID, stdcli.QOSEventProperties{Error: err})
			return "", fmt.Errorf("stack creation failed, contact [email protected] for assistance")
		case "DELETE_COMPLETE":
			stdcli.QOSEventSend("cli-install", distinctID, stdcli.QOSEventProperties{Error: err})
			return "", nil
		case "DELETE_FAILED":
			stdcli.QOSEventSend("cli-install", distinctID, stdcli.QOSEventProperties{Error: err})
			return "", fmt.Errorf("stack deletion failed, contact [email protected] for assistance")
		}

		time.Sleep(2 * time.Second)
	}
}
Beispiel #17
0
// WaitForStackOperationComplete is a blocking, polling based call that
// periodically fetches the stackID set of events and uses the state value
// to determine if an operation is complete
func WaitForStackOperationComplete(stackID string,
	pollingMessage string,
	awsCloudFormation *cloudformation.CloudFormation,
	logger *logrus.Logger) (*WaitForStackOperationCompleteResult, error) {

	result := &WaitForStackOperationCompleteResult{}

	// Poll for the current stackID state, and
	describeStacksInput := &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackID),
	}
	for waitComplete := false; !waitComplete; {
		sleepDuration := time.Duration(11+rand.Int31n(13)) * time.Second
		time.Sleep(sleepDuration)

		describeStacksOutput, err := awsCloudFormation.DescribeStacks(describeStacksInput)
		if nil != err {
			// TODO - add retry iff we're RateExceeded due to collective access
			return nil, err
		}
		if len(describeStacksOutput.Stacks) <= 0 {
			return nil, fmt.Errorf("Failed to enumerate stack info: %v", *describeStacksInput.StackName)
		}
		result.stackInfo = describeStacksOutput.Stacks[0]
		switch *(result.stackInfo).StackStatus {
		case cloudformation.StackStatusCreateComplete,
			cloudformation.StackStatusUpdateComplete:
			result.operationSuccessful = true
			waitComplete = true
		case
			// Include DeleteComplete as new provisions will automatically rollback
			cloudformation.StackStatusDeleteComplete,
			cloudformation.StackStatusCreateFailed,
			cloudformation.StackStatusDeleteFailed,
			cloudformation.StackStatusRollbackFailed,
			cloudformation.StackStatusRollbackComplete,
			cloudformation.StackStatusUpdateRollbackComplete:
			result.operationSuccessful = false
			waitComplete = true
		default:
			logger.Info(pollingMessage)
		}
	}
	return result, nil
}
Beispiel #18
0
func provisionStack(svc *awscf.CloudFormation, b []byte, onFailure string, params []*awscf.Parameter, stackName string) {
	input := &awscf.CreateStackInput{
		StackName: aws.String(stackName),
		Capabilities: []*string{
			aws.String("CAPABILITY_IAM"),
		},
		OnFailure:        aws.String(onFailure),
		Parameters:       params,
		TemplateBody:     aws.String(string(b)),
		TimeoutInMinutes: aws.Long(20),
	}
	resp, err := svc.CreateStack(input)
	if err != nil {
		log.Fatal(err)
	}

	log.Println(awsutil.StringValue(resp))
}
Beispiel #19
0
Datei: run.go Projekt: inka/tacks
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

}
Beispiel #20
0
func createStackAndWait(svc *cloudformation.CloudFormation, name, stackBody string) error {
	creq := &cloudformation.CreateStackInput{
		StackName:    aws.String(name),
		OnFailure:    aws.String("DO_NOTHING"),
		Capabilities: []*string{aws.String(cloudformation.CapabilityCapabilityIam)},
		TemplateBody: aws.String(stackBody),
	}

	resp, err := svc.CreateStack(creq)
	if err != nil {
		return err
	}

	if err := waitForStackCreateComplete(svc, aws.StringValue(resp.StackId)); err != nil {
		return err
	}

	return nil
}
Beispiel #21
0
func getStackResources(svc *cloudformation.CloudFormation, stackID string) ([]cloudformation.StackResourceSummary, error) {
	resources := make([]cloudformation.StackResourceSummary, 0)
	req := cloudformation.ListStackResourcesInput{
		StackName: aws.String(stackID),
	}
	for {
		resp, err := svc.ListStackResources(&req)
		if err != nil {
			return nil, err
		}
		for _, s := range resp.StackResourceSummaries {
			resources = append(resources, *s)
		}
		req.NextToken = resp.NextToken
		if aws.StringValue(req.NextToken) == "" {
			break
		}
	}
	return resources, nil
}
Beispiel #22
0
func runDescribeStackEevntsQuery(svc *cloudformation.CloudFormation, params *cloudformation.DescribeStackEventsInput) []*cloudformation.StackEvent {
	resp, err := svc.DescribeStackEvents(params)

	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			// Generic AWS error with Code, Message, and original error (if any)
			fmt.Println(awsErr.Code(), awsErr.Message(), awsErr.OrigErr())
			if reqErr, ok := err.(awserr.RequestFailure); ok {
				// A service error occurred
				fmt.Println(reqErr.Code(), reqErr.Message(), reqErr.StatusCode(), reqErr.RequestID())
			}
		} else {
			// This case should never be hit, the SDK should always return an
			// error which satisfies the awserr.Error interface.
			fmt.Println(err.Error())
		}
	}

	return resp.StackEvents
}
func getCloudFormationRollbackReasons(stackId string, afterTime *time.Time, conn *cloudformation.CloudFormation) ([]string, error) {
	var failures []string

	err := conn.DescribeStackEventsPages(&cloudformation.DescribeStackEventsInput{
		StackName: aws.String(stackId),
	}, func(page *cloudformation.DescribeStackEventsOutput, lastPage bool) bool {
		for _, e := range page.StackEvents {
			if afterTime != nil && !e.Timestamp.After(*afterTime) {
				continue
			}

			if cfStackEventIsFailure(e) || cfStackEventIsRollback(e) {
				failures = append(failures, *e.ResourceStatusReason)
			}
		}
		return !lastPage
	})

	return failures, err
}
Beispiel #24
0
func waitForStackCreateComplete(svc *cloudformation.CloudFormation, stackID string) error {
	req := cloudformation.DescribeStacksInput{
		StackName: aws.String(stackID),
	}
	for {
		resp, err := svc.DescribeStacks(&req)
		if err != nil {
			return err
		}
		if len(resp.Stacks) == 0 {
			return fmt.Errorf("stack not found")
		}
		switch aws.StringValue(resp.Stacks[0].StackStatus) {
		case cloudformation.ResourceStatusCreateComplete:
			return nil
		case cloudformation.ResourceStatusCreateFailed:
			return errors.New(aws.StringValue(resp.Stacks[0].StackStatusReason))
		}
		time.Sleep(3 * time.Second)
	}
}
Beispiel #25
0
func watch(svc *awscf.CloudFormation, verbose bool, interval int, stackName string) {
	req := &awscf.DescribeStacksInput{StackName: aws.String(stackName)}
	var maxError int
	previousStatus := ""
	var err error
	var firstLoop bool
	for maxError < 3 {
		if !firstLoop {
			time.Sleep(time.Duration(interval) * time.Second)
		} else {
			firstLoop = true
		}

		resp, err := svc.DescribeStacks(req)
		if err != nil {
			if previousStatus == "DELETE_IN_PROGRESS" {
				fmt.Printf("%s Finished\n", time.Now().Format(time.RFC3339))
				return
			}
			fmt.Printf("Error: %s - retrying\n", err)
			maxError++
			continue
		}
		for _, stack := range resp.Stacks {
			if *stack.StackName == stackName {
				if *stack.StackStatus != previousStatus || verbose {
					fmt.Printf("%s %s\n", time.Now().Format(time.RFC3339), *stack.StackStatus)
					previousStatus = *stack.StackStatus
				}
			}
			if strings.HasSuffix(previousStatus, "COMPLETE") {
				fmt.Printf("%s Finished\n", time.Now().Format(time.RFC3339))
				return
			}
		}
	}
	fmt.Printf("Error: %s - giving up\n", err)
}
Beispiel #26
0
func waitForStackUpdateComplete(svc *cloudformation.CloudFormation, stackID string) error {
	req := cloudformation.DescribeStacksInput{
		StackName: aws.String(stackID),
	}
	for {
		resp, err := svc.DescribeStacks(&req)
		if err != nil {
			return err
		}
		if len(resp.Stacks) == 0 {
			return fmt.Errorf("stack not found")
		}
		statusString := aws.StringValue(resp.Stacks[0].StackStatus)
		switch statusString {
		case cloudformation.ResourceStatusUpdateComplete:
			return nil
		case cloudformation.ResourceStatusUpdateFailed, cloudformation.StackStatusUpdateRollbackComplete, cloudformation.StackStatusUpdateRollbackFailed:
			errMsg := fmt.Sprintf("Stack status: %s : %s", statusString, aws.StringValue(resp.Stacks[0].StackStatusReason))
			return errors.New(errMsg)
		}
		time.Sleep(3 * time.Second)
	}
}
Beispiel #27
0
Datei: run.go Projekt: inka/tacks
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

}
Beispiel #28
0
func updateStackViaChangeSet(serviceName string,
	cfTemplateURL string,
	capabilities []*string,
	awsTags []*cloudformation.Tag,
	awsCloudFormation *cloudformation.CloudFormation,
	logger *logrus.Logger) error {

	// Create a change set name...
	changeSetRequestName := CloudFormationResourceName(fmt.Sprintf("%sChangeSet", serviceName))
	changeSetInput := &cloudformation.CreateChangeSetInput{
		Capabilities:  capabilities,
		ChangeSetName: aws.String(changeSetRequestName),
		ClientToken:   aws.String(changeSetRequestName),
		Description:   aws.String(fmt.Sprintf("Change set for service: %s", serviceName)),
		StackName:     aws.String(serviceName),
		TemplateURL:   aws.String(cfTemplateURL),
	}
	if len(awsTags) != 0 {
		changeSetInput.Tags = awsTags
	}
	_, changeSetError := awsCloudFormation.CreateChangeSet(changeSetInput)
	if nil != changeSetError {
		return changeSetError
	}

	logger.WithFields(logrus.Fields{
		"StackName": serviceName,
	}).Info("Issued CreateChangeSet request")

	describeChangeSetInput := cloudformation.DescribeChangeSetInput{
		ChangeSetName: aws.String(changeSetRequestName),
		StackName:     aws.String(serviceName),
	}

	var describeChangeSetOutput *cloudformation.DescribeChangeSetOutput
	for waitComplete := false; !waitComplete; {
		sleepDuration := time.Duration(11+rand.Int31n(13)) * time.Second
		time.Sleep(sleepDuration)

		changeSetOutput, describeChangeSetError := awsCloudFormation.DescribeChangeSet(&describeChangeSetInput)

		if nil != describeChangeSetError {
			return describeChangeSetError
		}
		describeChangeSetOutput = changeSetOutput
		waitComplete = (nil != describeChangeSetOutput)
	}
	logger.WithFields(logrus.Fields{
		"DescribeChangeSetOutput": describeChangeSetOutput,
	}).Debug("DescribeChangeSet result")

	//////////////////////////////////////////////////////////////////////////////
	// If there aren't any changes, then skip it...
	if len(describeChangeSetOutput.Changes) <= 0 {
		logger.WithFields(logrus.Fields{
			"StackName": serviceName,
		}).Info("No changes detected for service")

		// Delete it...
		deleteChangeSetInput := cloudformation.DeleteChangeSetInput{
			ChangeSetName: aws.String(changeSetRequestName),
			StackName:     aws.String(serviceName),
		}
		_, deleteChangeSetResultErr := awsCloudFormation.DeleteChangeSet(&deleteChangeSetInput)
		return deleteChangeSetResultErr
	}
	//////////////////////////////////////////////////////////////////////////////
	// Apply the change
	executeChangeSetInput := cloudformation.ExecuteChangeSetInput{
		ChangeSetName: aws.String(changeSetRequestName),
		StackName:     aws.String(serviceName),
	}
	executeChangeSetOutput, executeChangeSetError := awsCloudFormation.ExecuteChangeSet(&executeChangeSetInput)

	logger.WithFields(logrus.Fields{
		"ExecuteChangeSetOutput": executeChangeSetOutput,
	}).Debug("ExecuteChangeSet result")

	if nil == executeChangeSetError {
		logger.WithFields(logrus.Fields{
			"StackName": serviceName,
		}).Info("Issued ExecuteChangeSet request")
	}
	return executeChangeSetError

}
}

func (f *FakeCloudFormationBackend) UpdateStack(input *cloudformation.UpdateStackInput) (*cloudformation.UpdateStackOutput, error) {
	f.UpdateStackCall.Receives = input
	return f.UpdateStackCall.ReturnsResult, f.UpdateStackCall.ReturnsError
}

func (f *FakeCloudFormationBackend) DescribeStacks(input *cloudformation.DescribeStacksInput) (*cloudformation.DescribeStacksOutput, error) {
	f.DescribeStacksCall.Receives = input
	return f.DescribeStacksCall.ReturnsResult, f.DescribeStacksCall.ReturnsError
}

var _ = Describe("Mocking out the CloudFormation service", func() {
	var (
		fakeBackend *FakeCloudFormationBackend
		fakeServer  *httptest.Server
		client      *cloudformation.CloudFormation
	)

	BeforeEach(func() {
		fakeBackend = &FakeCloudFormationBackend{}
		fakeServer = httptest.NewServer(awsfaker.New(fakeBackend))
		client = cloudformation.New(newSession(fakeServer.URL))
	})

	AfterEach(func() {
		if fakeServer != nil {
			fakeServer.Close()
		}
	})
Beispiel #30
0
func displayProgress(stack string, CloudFormation *cloudformation.CloudFormation, isDeleting bool) error {
	res, err := CloudFormation.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
		StackName: aws.String(stack),
	})

	if err != nil {
		return err
	}

	for _, event := range res.StackEvents {
		if events[*event.EventId] == true {
			continue
		}

		events[*event.EventId] = true

		// Log all CREATE_FAILED to display and MixPanel
		if !isDeleting && *event.ResourceStatus == "CREATE_FAILED" {
			msg := fmt.Sprintf("Failed %s: %s", *event.ResourceType, *event.ResourceStatusReason)
			fmt.Println(msg)
			sendMixpanelEvent("convox-install-error", msg)
		}

		name := friendlyName(*event.ResourceType)

		if name == "" {
			continue
		}

		switch *event.ResourceStatus {
		case "CREATE_IN_PROGRESS":
		case "CREATE_COMPLETE":
			if !isDeleting {
				id := *event.PhysicalResourceId

				if strings.HasPrefix(id, "arn:") {
					id = *event.LogicalResourceId
				}

				fmt.Printf("Created %s: %s\n", name, id)
			}
		case "CREATE_FAILED":
		case "DELETE_IN_PROGRESS":
		case "DELETE_COMPLETE":
			id := *event.PhysicalResourceId

			if strings.HasPrefix(id, "arn:") {
				id = *event.LogicalResourceId
			}

			fmt.Printf("Deleted %s: %s\n", name, id)
		case "DELETE_SKIPPED":
			id := *event.PhysicalResourceId

			if strings.HasPrefix(id, "arn:") {
				id = *event.LogicalResourceId
			}

			fmt.Printf("Skipped %s: %s\n", name, id)
		case "DELETE_FAILED":
			return fmt.Errorf("stack deletion failed")
		case "ROLLBACK_IN_PROGRESS", "ROLLBACK_COMPLETE":
		case "UPDATE_IN_PROGRESS", "UPDATE_COMPLETE", "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_FAILED", "UPDATE_ROLLBACK_IN_PROGRESS", "UPDATE_ROLLBACK_COMPLETE", "UPDATE_ROLLBACK_FAILED":
		default:
			return fmt.Errorf("Unhandled status: %s\n", *event.ResourceStatus)
		}
	}

	return nil
}