Esempio n. 1
0
// Explore supports interactive command line invocation of the previously
// provisioned Sparta service
func Explore(lambdaAWSInfos []*LambdaAWSInfo, port int, logger *logrus.Logger) error {
	if 0 == port {
		port = 9999
	}
	urlHost := fmt.Sprintf("http://*****:*****@testEvent.json %s", functionURL)
		}
	}
	logger.Info("Functions can be invoked via application/json over POST")
	logger.Info(msgText)
	logger.Info("Where @testEvent.json is a local file with top level `context` and `event` properties:")
	logger.Info("\t{context: {}, event: {}}")
	// Start up the localhost server and publish the info
	return Execute(lambdaAWSInfos, port, 0, logger)
}
Esempio n. 2
0
// 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 := awsSession(logger)
	awsCloudFormation := cloudformation.New(session)

	exists, err := stackExists(serviceName, awsCloudFormation, 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
}
Esempio n. 3
0
// Execute creates an HTTP listener to dispatch execution. Typically
// called via Main() via command line arguments.
func Execute(lambdaAWSInfos []*LambdaAWSInfo, port int, parentProcessPID int, logger *logrus.Logger) error {
	if port <= 0 {
		port = defaultHTTPPort
	}
	logger.Info("Execute!")

	lookupMap := make(dispatchMap, 0)
	for _, eachLambdaInfo := range lambdaAWSInfos {
		lookupMap[eachLambdaInfo.lambdaFnName] = eachLambdaInfo
	}
	server := &http.Server{
		Addr:         fmt.Sprintf(":%d", port),
		Handler:      &lambdaHandler{lookupMap, logger},
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	if 0 != parentProcessPID {
		logger.Debug("Sending SIGUSR2 to parent process: ", parentProcessPID)
		syscall.Kill(parentProcessPID, syscall.SIGUSR2)
	}
	logger.Debug("Binding to port: ", port)
	err := server.ListenAndServe()
	if err != nil {
		logger.Error("FAILURE: " + err.Error())
		return err
	}
	logger.Debug("Server available at: ", port)
	return nil
}
func (command HelloWorldResource) create(session *session.Session,
	logger *logrus.Logger) (map[string]interface{}, error) {
	logger.Info("create: Hello ", command.Message)
	return map[string]interface{}{
		"Resource": "Created message: " + command.Message,
	}, nil
}
Esempio n. 5
0
func s3LambdaProcessor(event *json.RawMessage, context *LambdaContext, w http.ResponseWriter, logger *logrus.Logger) {
	logger.WithFields(logrus.Fields{
		"RequestID": context.AWSRequestID,
	}).Info("S3Event")

	logger.Info("Event data: ", string(*event))
}
Esempio n. 6
0
func exploreTestHelloWorld(event *json.RawMessage,
	context *LambdaContext,
	w http.ResponseWriter,
	logger *logrus.Logger) {
	logger.Info("Hello World: ", string(*event))

	fmt.Fprint(w, string(*event))
}
Esempio n. 7
0
// NOTE: your application MUST use `package main` and define a `main()` function.  The
// example text is to make the documentation compatible with godoc.
func echoS3SiteAPIGatewayEvent(event *json.RawMessage,
	context *LambdaContext,
	w http.ResponseWriter,
	logger *logrus.Logger) {

	logger.Info("Hello World: ", string(*event))
	fmt.Fprint(w, string(*event))
}
Esempio n. 8
0
// Return a string representation of a JS function call that can be exposed
// to AWS Lambda
func createNewNodeJSProxyEntry(lambdaInfo *LambdaAWSInfo, logger *logrus.Logger) string {
	// Create an entry of the form:
	logger.Info("Creating NodeJS proxy entry: " + lambdaInfo.jsHandlerName())
	primaryEntry := fmt.Sprintf("exports[\"%s\"] = createForwarder(\"/%s\");\n",
		lambdaInfo.jsHandlerName(),
		lambdaInfo.lambdaFnName)
	return primaryEntry
}
Esempio n. 9
0
func transformImage(event *json.RawMessage, context *sparta.LambdaContext, w http.ResponseWriter, logger *logrus.Logger) {
	logger.WithFields(logrus.Fields{
		"RequestID": context.AWSRequestID,
		"Event":     string(*event),
	}).Info("Request received :)")

	var lambdaEvent spartaS3.Event
	err := json.Unmarshal([]byte(*event), &lambdaEvent)
	if err != nil {
		logger.Error("Failed to unmarshal event data: ", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	logger.WithFields(logrus.Fields{
		"S3Event": lambdaEvent,
	}).Info("S3 Notification")

	for _, eachRecord := range lambdaEvent.Records {
		err = nil
		// What happened?
		switch eachRecord.EventName {
		case "ObjectCreated:Put":
			{
				err = stampImage(eachRecord.S3.Bucket.Name, eachRecord.S3.Object.Key, logger)
			}
		case "s3:ObjectRemoved:Delete":
			{
				deleteKey := fmt.Sprintf("%s%s", transformPrefix, eachRecord.S3.Object.Key)
				awsSession := awsSession(logger)
				svc := s3.New(awsSession)

				params := &s3.DeleteObjectInput{
					Bucket: aws.String(eachRecord.S3.Bucket.Name),
					Key:    aws.String(deleteKey),
				}
				resp, err := svc.DeleteObject(params)
				logger.WithFields(logrus.Fields{
					"Response": resp,
					"Error":    err,
				}).Info("Deleted object")
			}
		default:
			{
				logger.Info("Unsupported event: ", eachRecord.EventName)
			}
		}

		//
		if err != nil {
			logger.Error("Failed to process event: ", err.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}
Esempio n. 10
0
func cloudWatchEventProcessor(event *json.RawMessage,
	context *LambdaContext,
	w http.ResponseWriter,
	logger *logrus.Logger) {

	logger.WithFields(logrus.Fields{
		"RequestID": context.AWSRequestID,
	}).Info("Request received")

	logger.Info("CloudWatch Event data: ", string(*event))
}
Esempio n. 11
0
func ensureConfiguratorLambdaResource(awsPrincipalName string, sourceArn string, resources ArbitraryJSONObject, S3Bucket string, S3Key string, logger *logrus.Logger) (string, error) {
	// AWS service basename
	awsServiceName := awsPrincipalToService(awsPrincipalName)
	configuratorExportName := strings.ToLower(awsServiceName)

	//////////////////////////////////////////////////////////////////////////////
	// IAM Role definition
	// TODO - Check sourceArn for equivalence
	iamResourceName, err := ensureIAMRoleResource(awsPrincipalName, sourceArn, resources, logger)
	if nil != err {
		return "", err
	}

	iamRoleRef := ArbitraryJSONObject{
		"Fn::GetAtt": []string{iamResourceName, "Arn"},
	}
	// Custom handler resource for this service type
	subscriberHandlerName := fmt.Sprintf("%sSubscriber", awsServiceName)
	_, exists := resources[subscriberHandlerName]
	if !exists {
		logger.Info("Creating Subscription Lambda Resource for AWS service: ", awsServiceName)

		//////////////////////////////////////////////////////////////////////////////
		// Custom Resource Lambda Handler
		// NOTE: This brittle function name has an analog in ./resources/index.js b/c the
		// AWS Lamba execution treats the entire ZIP file as a module.  So all module exports
		// need to be forwarded through the module's index.js file.
		handlerName := fmt.Sprintf("index.%sConfiguration", configuratorExportName)
		logger.Debug("Lambda Configuration handler: ", handlerName)

		customResourceHandlerDef := ArbitraryJSONObject{
			"Type": "AWS::Lambda::Function",
			"Properties": ArbitraryJSONObject{
				"Code": ArbitraryJSONObject{
					"S3Bucket": S3Bucket,
					"S3Key":    S3Key,
				},
				"Role":    iamRoleRef,
				"Handler": handlerName,
				"Runtime": "nodejs",
				"Timeout": "30",
			},
		}
		resources[subscriberHandlerName] = customResourceHandlerDef
	}
	return subscriberHandlerName, nil
}
Esempio n. 12
0
func archiveHook(context map[string]interface{},
	serviceName string,
	zipWriter *zip.Writer,
	awsSession *session.Session,
	noop bool,
	logger *logrus.Logger) error {

	logger.Info("Adding userResource")
	resourceFileName := "userResource.json"
	binaryWriter, binaryWriterErr := zipWriter.Create(resourceFileName)
	if nil != binaryWriterErr {
		return binaryWriterErr
	}
	userdataReader := strings.NewReader(userdataResourceContents)
	_, copyErr := io.Copy(binaryWriter, userdataReader)
	return copyErr
}
Esempio n. 13
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
}
Esempio n. 14
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.Debug("DescribeStackOutput: ", describeStacksOutput)
	exists := false
	if err != nil {
		logger.Info("DescribeStackOutputError: ", err)
		// 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
}
Esempio n. 15
0
// Explore supports interactive command line invocation of the previously
// provisioned Sparta service
func Explore(serviceName string, logger *logrus.Logger) error {
	session := awsSession(logger)
	awsCloudFormation := cloudformation.New(session)

	exists, err := stackExists(serviceName, awsCloudFormation, logger)
	if nil != err {
		return err
	} else if !exists {
		logger.Info("Stack does not exist: ", serviceName)
		return nil
	} else {
		resources, err := stackLambdaResources(serviceName, awsCloudFormation, logger)
		if nil != err {
			return nil
		}
		selected := promptForSelection(resources)
		if nil != selected {
			logger.Info("TODO: Invoke", selected)
		}
	}
	return nil
}
Esempio n. 16
0
// MigratorMain is the entry point for the "migrate" cli command
func MigratorMain(log *logrus.Logger) {
	opts := NewOptions()
	if opts.Debug {
		log.Level = logrus.DebugLevel
	}

	log.Debug("spinning up database")

	db, err := metadata.NewDatabase(opts.DatabaseURL, log)
	if err != nil {
		log.Fatal(err)
	}

	log.Debug("migrating")

	err = db.Migrate(log)
	if err != nil {
		log.Fatal(err)
	}

	log.Info("database migration complete")
}
Esempio n. 17
0
func buildGoBinary(executableOutput string, buildTags string, linkFlags string, logger *logrus.Logger) error {
	// Go generate
	cmd := exec.Command("go", "generate")
	if logger.Level == logrus.DebugLevel {
		cmd = exec.Command("go", "generate", "-v", "-x")
	}
	cmd.Env = os.Environ()
	commandString := fmt.Sprintf("%s", cmd.Args)
	logger.Info(fmt.Sprintf("Running `%s`", strings.Trim(commandString, "[]")))
	goGenerateErr := runOSCommand(cmd, logger)
	if nil != goGenerateErr {
		return goGenerateErr
	}

	// TODO: Smaller binaries via linker flags
	// Ref: https://blog.filippo.io/shrink-your-go-binaries-with-this-one-weird-trick/
	allBuildTags := fmt.Sprintf("lambdabinary %s", buildTags)

	buildArgs := []string{
		"build",
		"-o",
		executableOutput,
		"-tags",
		allBuildTags,
	}
	// Append all the linker flags
	if len(linkFlags) != 0 {
		buildArgs = append(buildArgs, "-ldflags", linkFlags)
	}
	buildArgs = append(buildArgs, ".")
	cmd = exec.Command("go", buildArgs...)
	cmd.Env = os.Environ()
	cmd.Env = append(cmd.Env, "GOOS=linux", "GOARCH=amd64")
	logger.WithFields(logrus.Fields{
		"Name": executableOutput,
	}).Info("Compiling binary")
	return runOSCommand(cmd, logger)
}
Esempio n. 18
0
func stampImage(bucket string, key string, logger *logrus.Logger) error {

	// Only transform if the key doesn't have the _xformed part
	if !strings.Contains(key, transformPrefix) {
		awsSession := awsSession(logger)
		svc := s3.New(awsSession)
		result, err := svc.GetObject(&s3.GetObjectInput{
			Bucket: aws.String(bucket),
			Key:    aws.String(key),
		})
		if nil != err {
			return err
		}
		defer result.Body.Close()
		transformed, err := transforms.StampImage(result.Body, logger)
		if err != nil {
			return err
		}
		// Put the encoded image to a byte buffer, then wrap a reader around it.
		uploadResult, err := svc.PutObject(&s3.PutObjectInput{
			Body:   transformed,
			Bucket: aws.String(bucket),
			Key:    aws.String(fmt.Sprintf("%s%s", transformPrefix, key)),
		})
		if err != nil {
			return err
		}
		logger.WithFields(logrus.Fields{
			"Transformed": uploadResult,
		}).Info("Image transformed")

	} else {
		logger.Info("File already transformed")
	}
	return nil
}
Esempio n. 19
0
// 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 := awsSession(logger)
	awsCloudFormation := cloudformation.New(session)

	exists, err := stackExists(serviceName, awsCloudFormation, logger)
	if nil != err {
		return err
	}
	if exists {
		logger.Info("Stack exists: ", serviceName)
		params := &cloudformation.DeleteStackInput{
			StackName: aws.String(serviceName),
		}
		resp, err := awsCloudFormation.DeleteStack(params)
		if nil != resp {
			logger.Info("Stack delete issued: ", resp)
		}
		return err
	}

	logger.Info("Stack does not exist: ", serviceName)
	return nil
}
func (command HelloWorldResource) delete(session *session.Session,
	logger *logrus.Logger) (map[string]interface{}, error) {
	logger.Info("delete: ", command.Message)
	return nil, nil
}
Esempio n. 21
0
func StampImage(reader io.Reader, logger *logrus.Logger) (io.ReadSeeker, error) {

	target, imageType, err := image.Decode(reader)
	if err != nil {
		logger.WithFields(logrus.Fields{
			"Error": err,
		}).Info("Failed to decode image")
		return nil, err
	}

	// Pick the longer edge and a reasonably sized stamp
	maxEdge := math.Max(float64(target.Bounds().Max.X), float64(target.Bounds().Max.Y))
	edgeLog := int(math.Floor(math.Log2(maxEdge))) - 1
	logger.WithFields(logrus.Fields{
		"ImageType":    imageType,
		"MaxEdge":      maxEdge,
		"EdgeLog":      edgeLog,
		"TargetBounds": target.Bounds(),
	}).Info("Target Dimensions")
	watermarkSuffix := int(math.Max(32, math.Pow(2, math.Min(float64(8), float64(edgeLog)))))
	resourceName := watermarkName(watermarkSuffix)
	logger.Info("Watermark resource: ", resourceName)

	byteSource, err := assets.FSByte(false, resourceName)
	if err != nil {
		logger.WithFields(logrus.Fields{
			"Error":        err,
			"Name":         resourceName,
			"TargetBounds": target.Bounds(),
		}).Warn("Failed to load computed watermark. Falling to default.")
		byteSource = assets.FSMustByte(false, watermarkName(16))
	}
	stampReader := bytes.NewReader(byteSource)
	stamp, _, err := image.Decode(stampReader)
	if err != nil {
		logger.WithFields(logrus.Fields{
			"Error": err,
		}).Info("Failed to load stamp image")
		return nil, err
	}

	// Save it...
	compositedImage := image.NewRGBA(image.Rect(0, 0, target.Bounds().Max.X, target.Bounds().Max.Y))
	draw.Draw(compositedImage, compositedImage.Bounds(), target, image.Point{0, 0}, draw.Src)

	// Bottom right corner
	targetRect := target.Bounds()
	targetRect.Min.X = (targetRect.Max.X - stamp.Bounds().Max.X)
	targetRect.Min.Y = (targetRect.Max.Y - stamp.Bounds().Max.Y)

	logger.WithFields(logrus.Fields{
		"TargetBounds": target.Bounds(),
		"StampBounds":  stamp.Bounds(),
		"TargetRect":   targetRect,
	}).Info("Drawing")

	logger.Debug("Composing image")
	draw.Draw(compositedImage, targetRect, stamp, image.Point{0, 0}, draw.Over)
	buf := new(bytes.Buffer)
	err = png.Encode(buf, compositedImage)
	return bytes.NewReader(buf.Bytes()), nil
}
Esempio n. 22
0
func apiStageInfo(apiName string, stageName string, session *session.Session, noop bool, logger *logrus.Logger) (*apigateway.Stage, error) {
	logger.WithFields(logrus.Fields{
		"APIName":   apiName,
		"StageName": stageName,
	}).Info("Checking current APIGateway stage status")

	if noop {
		logger.Info("Bypassing APIGateway check to -n/-noop command line argument")
		return nil, nil
	}

	svc := apigateway.New(session)
	restApisInput := &apigateway.GetRestApisInput{
		Limit: aws.Int64(500),
	}

	restApisOutput, restApisOutputErr := svc.GetRestApis(restApisInput)
	if nil != restApisOutputErr {
		return nil, restApisOutputErr
	}
	// Find the entry that has this name
	restAPIID := ""
	for _, eachRestAPI := range restApisOutput.Items {
		if *eachRestAPI.Name == apiName {
			if restAPIID != "" {
				return nil, fmt.Errorf("Multiple RestAPI matches for API Name: %s", apiName)
			}
			restAPIID = *eachRestAPI.Id
		}
	}
	if "" == restAPIID {
		return nil, nil
	}
	// API exists...does the stage name exist?
	stagesInput := &apigateway.GetStagesInput{
		RestApiId: aws.String(restAPIID),
	}
	stagesOutput, stagesOutputErr := svc.GetStages(stagesInput)
	if nil != stagesOutputErr {
		return nil, stagesOutputErr
	}

	// Find this stage name...
	var matchingStageOutput *apigateway.Stage
	for _, eachStage := range stagesOutput.Item {
		if *eachStage.StageName == stageName {
			if nil != matchingStageOutput {
				return nil, fmt.Errorf("Multiple stage matches for name: %s", stageName)
			}
			matchingStageOutput = eachStage
		}
	}
	if nil != matchingStageOutput {
		logger.WithFields(logrus.Fields{
			"DeploymentId": *matchingStageOutput.DeploymentId,
			"LastUpdated":  matchingStageOutput.LastUpdatedDate,
			"CreatedDate":  matchingStageOutput.CreatedDate,
		}).Info("Checking current APIGateway stage status")
	} else {
		logger.Info("APIGateway stage has not been deployed")
	}

	return matchingStageOutput, nil
}