コード例 #1
0
ファイル: delete.go プロジェクト: mweagle/Sparta
// Delete the provided serviceName.  Failing to delete a non-existent
// service is not considered an error.  Note that the delete does
func Delete(serviceName string, logger *logrus.Logger) error {
	session := spartaAWS.NewSession(logger)
	awsCloudFormation := cloudformation.New(session)

	exists, err := spartaCF.StackExists(serviceName, session, logger)
	if nil != err {
		return err
	}
	logger.WithFields(logrus.Fields{
		"Exists": exists,
		"Name":   serviceName,
	}).Info("Stack existence check")

	if exists {

		params := &cloudformation.DeleteStackInput{
			StackName: aws.String(serviceName),
		}
		resp, err := awsCloudFormation.DeleteStack(params)
		if nil != resp {
			logger.WithFields(logrus.Fields{
				"Response": resp,
			}).Info("Delete request submitted")
		}
		return err
	}
	logger.Info("Stack does not exist")
	return nil
}
コード例 #2
0
ファイル: discovery.go プロジェクト: mweagle/Sparta
// TODO cache the data somewhere other than querying CF
func initializeDiscovery(serviceName string, lambdaAWSInfos []*LambdaAWSInfo, logger *logrus.Logger) {
	// Setup the discoveryImpl reference
	discoveryCache = make(map[string]*DiscoveryInfo, 0)
	discoverImpl = func(golangFuncName string) (*DiscoveryInfo, error) {

		// Find the LambdaAWSInfo that has this golang function
		// as its target
		lambdaCFResource := ""
		for _, eachLambda := range lambdaAWSInfos {
			if eachLambda.lambdaFunctionName() == golangFuncName {
				lambdaCFResource = eachLambda.logicalName()
			}
		}
		logger.WithFields(logrus.Fields{
			"CallerName":     golangFuncName,
			"CFResourceName": lambdaCFResource,
			"ServiceName":    serviceName,
		}).Debug("Discovery Info")
		if "" == lambdaCFResource {
			return nil, fmt.Errorf("Unsupported call site for sparta.Discover(): %s", golangFuncName)
		}

		emptyConfiguration := &DiscoveryInfo{}
		if "" != lambdaCFResource {
			cachedConfig, exists := discoveryCache[lambdaCFResource]
			if exists {
				return cachedConfig, nil
			}

			// Look it up
			awsCloudFormation := cloudformation.New(spartaAWS.NewSession(logger))
			params := &cloudformation.DescribeStackResourceInput{
				LogicalResourceId: aws.String(lambdaCFResource),
				StackName:         aws.String(serviceName),
			}
			result, err := awsCloudFormation.DescribeStackResource(params)
			if nil != err {
				// TODO - retry/cache expiry
				discoveryCache[lambdaCFResource] = emptyConfiguration
				return nil, err
			}
			metadata := result.StackResourceDetail.Metadata
			if nil == metadata {
				metadata = aws.String("{}")
			}

			// Transform this into a map
			logger.WithFields(logrus.Fields{
				"Metadata": metadata,
			}).Debug("DiscoveryInfo Metadata")
			var discoveryInfo DiscoveryInfo
			err = json.Unmarshal([]byte(*metadata), &discoveryInfo)
			if err != nil {
				logger.WithFields(logrus.Fields{
					"Metadata": *metadata,
					"Error":    err,
				}).Error("Failed to unmarshal discovery info")
			}
			discoveryCache[lambdaCFResource] = &discoveryInfo
			return &discoveryInfo, err
		}
		return emptyConfiguration, nil
	}
}
コード例 #3
0
ファイル: provision.go プロジェクト: mweagle/Sparta
// Provision compiles, packages, and provisions (either via create or update) a Sparta application.
// The serviceName is the service's logical
// identify and is used to determine create vs update operations.  The compilation options/flags are:
//
// 	TAGS:         -tags lambdabinary
// 	ENVIRONMENT:  GOOS=linux GOARCH=amd64
//
// The compiled binary is packaged with a NodeJS proxy shim to manage AWS Lambda setup & invocation per
// http://docs.aws.amazon.com/lambda/latest/dg/authoring-function-in-nodejs.html
//
// The two files are ZIP'd, posted to S3 and used as an input to a dynamically generated CloudFormation
// template (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html)
// which creates or updates the service state.
//
// More information on golang 1.5's support for vendor'd resources is documented at
//
//  https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/edit
//  https://medium.com/@freeformz/go-1-5-s-vendor-experiment-fd3e830f52c3#.voiicue1j
//
// type Configuration struct {
//     Val   string
//     Proxy struct {
//         Address string
//         Port    string
//     }
// }
func Provision(noop bool,
	serviceName string,
	serviceDescription string,
	lambdaAWSInfos []*LambdaAWSInfo,
	api *API,
	site *S3Site,
	s3Bucket string,
	buildID string,
	codePipelineTrigger string,
	buildTags string,
	linkerFlags string,
	templateWriter io.Writer,
	workflowHooks *WorkflowHooks,
	logger *logrus.Logger) error {

	err := validateSpartaPreconditions(lambdaAWSInfos, logger)
	if nil != err {
		return err
	}
	startTime := time.Now()

	ctx := &workflowContext{
		noop:               noop,
		serviceName:        serviceName,
		serviceDescription: serviceDescription,
		lambdaAWSInfos:     lambdaAWSInfos,
		api:                api,
		s3SiteContext: &s3SiteContext{
			s3Site: site,
		},
		cfTemplate:                gocf.NewTemplate(),
		s3Bucket:                  s3Bucket,
		s3BucketVersioningEnabled: false,
		buildID:                   buildID,
		codePipelineTrigger:       codePipelineTrigger,
		buildTags:                 buildTags,
		linkFlags:                 linkerFlags,
		buildTime:                 time.Now(),
		awsSession:                spartaAWS.NewSession(logger),
		templateWriter:            templateWriter,
		workflowHooks:             workflowHooks,
		workflowHooksContext:      make(map[string]interface{}, 0),
		logger:                    logger,
	}
	ctx.cfTemplate.Description = serviceDescription

	// Update the context iff it exists
	if nil != workflowHooks && nil != workflowHooks.Context {
		for eachKey, eachValue := range workflowHooks.Context {
			ctx.workflowHooksContext[eachKey] = eachValue
		}
	}

	ctx.logger.WithFields(logrus.Fields{
		"BuildID":             buildID,
		"NOOP":                noop,
		"Tags":                ctx.buildTags,
		"CodePipelineTrigger": ctx.codePipelineTrigger,
	}).Info("Provisioning service")

	if len(lambdaAWSInfos) <= 0 {
		return errors.New("No lambda functions provided to Sparta.Provision()")
	}

	// Start the workflow
	for step := verifyIAMRoles; step != nil; {
		next, err := step(ctx)
		if err != nil {
			ctx.rollback()
			// Workflow step?
			ctx.logger.Error(err)
			return err
		}
		if next == nil {
			elapsed := time.Since(startTime)
			ctx.logger.WithFields(logrus.Fields{
				"Seconds": fmt.Sprintf("%.f", elapsed.Seconds()),
			}).Info("Elapsed time")
			break
		} else {
			step = next
		}
	}
	// When we're done, execute any finalizers
	if nil != ctx.finalizerFunctions {
		ctx.logger.WithFields(logrus.Fields{
			"FinalizerCount": len(ctx.finalizerFunctions),
		}).Debug("Invoking finalizer functions")
		for _, eachFinalizer := range ctx.finalizerFunctions {
			eachFinalizer(ctx.logger)
		}
	}
	return nil
}