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)
	}
}
Beispiel #2
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 #3
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 #4
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 #5
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 #6
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 #7
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 #8
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)
	}
}
			[]*cloudformation.Parameter{
				&cloudformation.Parameter{
					ParameterKey:   aws.String("some-key-0"),
					ParameterValue: aws.String("some-value-0"),
				},
				&cloudformation.Parameter{
					ParameterKey:   aws.String("some-key-1"),
					ParameterValue: aws.String("some-value-1"),
				},
			},
		))
	})

	It("should call the backend method", func() {
		client.DescribeStacks(
			&cloudformation.DescribeStacksInput{
				StackName: aws.String("some-stack-name"),
			})

		Expect(fakeBackend.DescribeStacksCall.Receives).NotTo(BeNil())
		Expect(fakeBackend.DescribeStacksCall.Receives.StackName).To(Equal(aws.String("some-stack-name")))
	})

	Context("when the backend succeeds", func() {
		It("should return the data in a format parsable by the client library", func() {
			fakeBackend.DescribeStacksCall.ReturnsResult = &cloudformation.DescribeStacksOutput{
				Stacks: []*cloudformation.Stack{
					&cloudformation.Stack{
						StackName: aws.String("first stack"),
						Outputs: []*cloudformation.Output{
							&cloudformation.Output{
								OutputKey:   aws.String("some-key"),
Beispiel #10
0
// describeRackStacks uses credentials to describe CF service, app and rack stacks that belong to the rack name and region
func describeRackStacks(rackName, distinctId string, CF *cloudformation.CloudFormation) (Stacks, error) {
	res, err := CF.DescribeStacks(&cloudformation.DescribeStacksInput{})
	if err != nil {
		return Stacks{}, err
	}

	apps := []Stack{}
	rack := []Stack{}
	services := []Stack{}

	for _, stack := range res.Stacks {
		events := map[string]string{}
		outputs := map[string]string{}
		tags := map[string]string{}

		for _, output := range stack.Outputs {
			outputs[*output.OutputKey] = *output.OutputValue
		}

		for _, tag := range stack.Tags {
			tags[*tag.Key] = *tag.Value
		}

		name := tags["Name"]
		if name == "" {
			name = *stack.StackName
		}

		buckets := []string{}

		rres, err := CF.DescribeStackResources(&cloudformation.DescribeStackResourcesInput{
			StackName: stack.StackId,
		})
		if err != nil {
			return Stacks{}, err
		}

		for _, resource := range rres.StackResources {
			if *resource.ResourceType == "AWS::S3::Bucket" {
				if resource.PhysicalResourceId != nil {
					buckets = append(buckets, *resource.PhysicalResourceId)
				}
			}
		}

		eres, err := CF.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
			StackName: stack.StackId,
		})
		if err != nil {
			return Stacks{}, err
		}

		for _, event := range eres.StackEvents {
			if strings.HasSuffix(*event.ResourceStatus, "FAILED") {
				events[*event.LogicalResourceId] = *event.ResourceStatusReason
			}
		}

		s := Stack{
			Name:      name,
			StackName: *stack.StackName,
			Status:    *stack.StackStatus,
			Type:      tags["Type"],

			Buckets: buckets,
			Events:  events,
			Outputs: outputs,
		}

		// collect stacks that are explicitly related to the rack
		if tags["Rack"] == rackName {
			switch tags["Type"] {
			case "app":
				apps = append(apps, s)
			case "service":
				services = append(services, s)
			}
		}

		// collect stack that is explicitly the rack
		if *stack.StackName == rackName && outputs["Dashboard"] != "" {
			rack = append(rack, s)
		}
	}

	return Stacks{
		Apps:     apps,
		Rack:     rack,
		Services: services,
	}, nil
}