diff --git a/release/awscommunity/cicd.yml b/release/awscommunity/cicd.yml index 18092fed..713d101c 100644 --- a/release/awscommunity/cicd.yml +++ b/release/awscommunity/cicd.yml @@ -192,6 +192,19 @@ Resources: ManagedPolicyArns: - Fn::ImportValue: !Sub "cep-${Env}-common-build-project-policy" + TimeOffsetBuildProjectRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Version: '2012-10-17' + ManagedPolicyArns: + - Fn::ImportValue: !Sub "cep-${Env}-common-build-project-policy" + CloudFrontWebAclAssociationBuildProjectRole: Type: AWS::IAM::Role Properties: @@ -456,6 +469,40 @@ Resources: Roles: - !Ref TimeSleepBuildProjectRole + TimeOffsetBuildProjectRolePolicy: + Type: AWS::IAM::Policy + Properties: + PolicyDocument: + Statement: + - Action: + - iam:CreateRole + - iam:DeleteRole + - iam:GetRole + - dynamodb:* + Effect: Allow + Resource: "*" + - Action: + - codebuild:StartBuild + - codebuild:BatchGetBuilds + - codebuild:StopBuild + - codebuild:RetryBuild + - codebuild:StartBuildBatch + - codebuild:RetryBuildBatch + - codebuild:StopBuildBatch + Effect: Allow + Resource: + - !GetAtt TimeOffsetBuildProject.Arn + - Action: + - ssm:DeleteParameter + - ssm:GetParameter + - ssm:PutParameter + Effect: Allow + Resource: "*" + Version: '2012-10-17' + PolicyName: delete-bucket-contents-build-project-policy + Roles: + - !Ref TimeOffsetBuildProjectRole + CloudFrontWebAclAssociationBuildProjectPolicy: Type: AWS::IAM::Policy Properties: @@ -719,6 +766,28 @@ Resources: BuildSpec: !Sub "resources/${Env}-buildspec-go.yml" TimeoutInMinutes: 480 + TimeOffsetBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "${PrefixLower}-${Env}-time-offset" + Artifacts: + Type: CODEPIPELINE + Environment: + ComputeType: BUILD_GENERAL1_LARGE + Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/cep-cicd:latest" + ImagePullCredentialsType: SERVICE_ROLE + PrivilegedMode: true + Type: LINUX_CONTAINER + EnvironmentVariables: + - Name: RESOURCE_PATH + Type: PLAINTEXT + Value: "placeholder-for-path-to-resource" + ServiceRole: !GetAtt TimeOffsetBuildProjectRole.Arn + Source: + Type: CODEPIPELINE + BuildSpec: !Sub "resources/${Env}-buildspec-go.yml" + TimeoutInMinutes: 480 + S3BucketVersioningEnabledBuildProject: Type: AWS::CodeBuild::Project Properties: @@ -992,6 +1061,7 @@ Resources: - !GetAtt S3PublicAccessControlsRestrictedBuildProject.Arn - !GetAtt TimeStaticBuildProject.Arn - !GetAtt TimeSleepBuildProject.Arn + - !GetAtt TimeOffsetBuildProject.Arn - !GetAtt CloudFrontLoggingEnabledBuildProject.Arn - !GetAtt CloudFrontWebAclAssociationBuildProject.Arn - !GetAtt S3BucketModuleBuildProject.Arn @@ -1058,6 +1128,7 @@ Resources: - !GetAtt S3PublicAccessControlsRestrictedBuildProjectRole.Arn - !GetAtt TimeStaticBuildProjectRole.Arn - !GetAtt TimeSleepBuildProjectRole.Arn + - !GetAtt TimeOffsetBuildProjectRole.Arn - !GetAtt AlternateContactBuildProjectRole.Arn - !GetAtt CloudFrontLoggingEnabledBuildProjectRole.Arn - !GetAtt CloudFrontWebAclAssociationBuildProjectRole.Arn @@ -1240,6 +1311,30 @@ Resources: } ] RunOrder: 1 + - Name: TimeOffset + InputArtifacts: + - Name: extensions-source + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: 1 + Configuration: + ProjectName: !Ref TimeOffsetBuildProject + # BatchEnabled: + # !If + # - IsProd + # - true + # - false + EnvironmentVariables: |- + [ + { + "name": "RESOURCE_PATH", + "type": "PLAINTEXT", + "value": "resources/Time_Offset" + } + ] + RunOrder: 1 - Name: Account_AlternateContact InputArtifacts: - Name: extensions-source diff --git a/resources/Time_Offset/.gitignore b/resources/Time_Offset/.gitignore new file mode 100644 index 00000000..5c7bffc7 --- /dev/null +++ b/resources/Time_Offset/.gitignore @@ -0,0 +1,17 @@ +# macOS +.DS_Store +._* + +# our logs +rpdk.log* + +#compiled file +bin/ + +#vender +vender/ + +# contains credentials +sam-tests/ +.hypothesis/ +/awscommunity-time-offset.zip diff --git a/resources/Time_Offset/.rpdk-config b/resources/Time_Offset/.rpdk-config new file mode 100644 index 00000000..c21cbf36 --- /dev/null +++ b/resources/Time_Offset/.rpdk-config @@ -0,0 +1,21 @@ +{ + "artifact_type": "RESOURCE", + "typeName": "AwsCommunity::Time::Offset", + "language": "go", + "runtime": "go1.x", + "entrypoint": "handler", + "testEntrypoint": "handler", + "settings": { + "version": false, + "subparser_name": null, + "verbose": 0, + "force": false, + "type_name": null, + "artifact_type": null, + "endpoint_url": null, + "region": null, + "target_schemas": [], + "import_path": "github.com/aws-cloudformation/awscommunity-registry-extensions/resources/Time_Offset", + "protocolVersion": "2.0.0" + } +} diff --git a/resources/Time_Offset/Makefile b/resources/Time_Offset/Makefile new file mode 100644 index 00000000..bc13ba53 --- /dev/null +++ b/resources/Time_Offset/Makefile @@ -0,0 +1,11 @@ +.PHONY: build test clean + +build: + make -f makebuild # this runs build steps required by the cfn cli + +test: + cfn generate + env GOOS=linux go build -ldflags="-s -w" -o bin/handler cmd/main.go + +clean: + rm -rf bin diff --git a/resources/Time_Offset/README.md b/resources/Time_Offset/README.md new file mode 100644 index 00000000..fb6a57e7 --- /dev/null +++ b/resources/Time_Offset/README.md @@ -0,0 +1,74 @@ +# AwsCommunity::Time::Offset + +Creates a time based resource with an offset. + +## Example + +```yaml +Resources: + StarTime: + Type: AwsCommunity::Time::Offset + Properties: + OffsetHours: 1 + EndTime: + Type: AwsCommunity::Time::Offset + Properties: + OffsetYears: 5 + DailyNotice: + Type: AWS::Pinpoint::Campaign + Properties: + ... + Schedule: + EndTime: !GetAtt EndTime.Utc + Frequency: "DAILY" + IsLocalTime: true + StartTime: !GetAtt StartTime.Utc + TimeZone: "UTC-07" +``` + +## Properties +| Properties | Type | Description | +| ------------- | ------------- | ------------- | +| **Time** | string | Utc time to do the offset from. If none is provided now will be used. +| **OffsetYears** | integer | Number of years to offset the base timestamp. +| **OffsetMonths** | integer | Number of months to offset the base timestamp. +| **OffsetDays** | integer | Number of days to offset the base timestamp. +| **OffsetHours** | integer | Number of hours to offset the base timestamp. +| **OffsetMinutes** | integer | Number of minutes to offset the base timestamp. +| **OffsetSeconds** | integer | Number of seconds to offset the base timestamp. + +## Attributes +The following properties you can use in a `GetAtt` + +| Attribute | Type | Description | +| ------------- | ------------- | ------------- | +| **Id** | string | Unique ID that identifies the resource. +| **Day** | integer | Day returns the day of the month. +| **Hour** | integer | Hour returns the hour within the day, in the range [0, 23]. +| **Minute** | integer | Minute returns the minute offset within the hour, in the range [0, 59]. +| **Month** | integer | Month returns the month of the year. +| **Second** | integer | Second returns the second offset within the minute, in the range [0, 59]. +| **Unix** | integer | Unix returns a Unix time, the number of seconds elapsed since January 1, 1970 UTC. +| **Utc** | string | The time represented in UTC time +| **Year** | integer | Year returns the year. + +## Development + +Open two tabs in your terminal. + +Run SAM +```sh +sam local start-lambda +``` + +In another tab, run cfn test: + +```sh +cd resources/Time_Offset +cfn test +``` + +## Notes + +### SSM Parameter +Since this extension does not represent an actual resource, we have to create a Systems Manager Parameter Store entry to track its state for the purposes of satisfying the CloudFormation registry contract. We store a key in parameter store at `/CloudFormation/AwsCommunity/Time/Offset/unique-identifier`. diff --git a/resources/Time_Offset/awscommunity-time-offset.json b/resources/Time_Offset/awscommunity-time-offset.json new file mode 100644 index 00000000..8a08974b --- /dev/null +++ b/resources/Time_Offset/awscommunity-time-offset.json @@ -0,0 +1,147 @@ +{ + "typeName": "AwsCommunity::Time::Offset", + "description": "Creates a time based resource with an offset from the provided time or now.", + "sourceUrl": "https://github.com/aws-cloudformation/community-registry-extensions.git", + "definitions": {}, + "properties": { + "Id": { + "description": "Id is a unique identifier that is auto generated.", + "type": "string" + }, + "Time": { + "description": "Optional parameter to represent the time or default is now.", + "type": "string", + "pattern": "^\\d{4}-[0-1][0-3]-[0-3]\\d{1}T[0-2]\\d{1}:[0-5]\\d{1}:[0-5]\\d{1}Z$" + }, + "OffsetDays": { + "description": "Number of days to offset the base timestamp.", + "type": "integer" + }, + "OffsetHours": { + "description": "Number of hours to offset the base timestamp.", + "type": "integer" + }, + "OffsetMinutes": { + "description": "Number of minutes to offset the base timestamp.", + "type": "integer" + }, + "OffsetMonths": { + "description": "Number of months to offset the base timestamp.", + "type": "integer" + }, + "OffsetSeconds": { + "description": "Number of seconds to offset the base timestamp.", + "type": "integer" + }, + "OffsetYears": { + "description": "Number of years to offset the base timestamp.", + "type": "integer" + }, + "Utc": { + "description": "UTC returns the time in UTC format.", + "type": "string" + }, + "Day": { + "description": "Day returns the day of the month.", + "type": "string" + }, + "Hour": { + "description": "Hour returns the hour within the day, in the range [0, 23].", + "type": "string" + }, + "Minute": { + "description": "Minute returns the minute offset within the hour, in the range [0, 59].", + "type": "string" + }, + "Month": { + "description": "Month returns the month of the year.", + "type": "string" + }, + "Second": { + "description": "Second returns the second offset within the minute, in the range [0, 59].", + "type": "string" + }, + "Unix": { + "description": "Unix returns a Unix time, the number of seconds elapsed since January 1, 1970 UTC.", + "type": "string" + }, + "Year": { + "description": "Year returns the year.", + "type": "string" + }, + "Triggers": { + "description": "A value to represent when an update to the time should occur.", + "type": "array", + "items": { + "type": "string" + }, + "insertionOrder": true + } + }, + "additionalProperties": false, + "anyOf": [ + { + "required": ["OffsetDays"] + }, + { + "required": ["OffsetHours"] + }, + { + "required": ["OffsetMinutes"] + }, + { + "required": ["OffsetMonths"] + }, + { + "required": ["OffsetSeconds"] + }, + { + "required": ["OffsetYears"] + } + ], + "readOnlyProperties": [ + "/properties/Id", + "/properties/Utc", + "/properties/Day", + "/properties/Hour", + "/properties/Minute", + "/properties/Month", + "/properties/Second", + "/properties/Unix", + "/properties/Year" + ], + "createOnlyProperties": [ + "/properties/Time", + "/properties/OffsetDays", + "/properties/OffsetHours", + "/properties/OffsetMinutes", + "/properties/OffsetMonths", + "/properties/OffsetSeconds", + "/properties/OffsetYears" + ], + "primaryIdentifier": [ + "/properties/Id" + ], + "handlers": { + "create": { + "permissions": [ + "ssm:PutParameter" + ] + }, + "delete": { + "permissions": [ + "ssm:DeleteParameter" + ] + }, + "read": { + "permissions": [ + "ssm:GetParameter" + ] + }, + "update": { + "permissions": [ + "ssm:GetParameter" + ] + } + } +} \ No newline at end of file diff --git a/resources/Time_Offset/cmd/main.go b/resources/Time_Offset/cmd/main.go new file mode 100644 index 00000000..a753c34f --- /dev/null +++ b/resources/Time_Offset/cmd/main.go @@ -0,0 +1,85 @@ +// Code generated by 'cfn generate', changes will be undone by the next invocation. DO NOT EDIT. +package main + +import ( + "errors" + "fmt" + "log" + + "github.com/aws-cloudformation/awscommunity-registry-extensions/resources/Time_Offset/cmd/resource" + "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn" + "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" +) + +// Handler is a container for the CRUDL actions exported by resources +type Handler struct{} + +// Create wraps the related Create function exposed by the resource code +func (r *Handler) Create(req handler.Request) handler.ProgressEvent { + return wrap(req, resource.Create) +} + +// Read wraps the related Read function exposed by the resource code +func (r *Handler) Read(req handler.Request) handler.ProgressEvent { + return wrap(req, resource.Read) +} + +// Update wraps the related Update function exposed by the resource code +func (r *Handler) Update(req handler.Request) handler.ProgressEvent { + return wrap(req, resource.Update) +} + +// Delete wraps the related Delete function exposed by the resource code +func (r *Handler) Delete(req handler.Request) handler.ProgressEvent { + return wrap(req, resource.Delete) +} + +// List wraps the related List function exposed by the resource code +func (r *Handler) List(req handler.Request) handler.ProgressEvent { + return wrap(req, resource.List) +} + +// main is the entry point of the application. +func main() { + cfn.Start(&Handler{}) +} + +type handlerFunc func(handler.Request, *resource.Model, *resource.Model) (handler.ProgressEvent, error) + +func wrap(req handler.Request, f handlerFunc) (response handler.ProgressEvent) { + defer func() { + // Catch any panics and return a failed ProgressEvent + if r := recover(); r != nil { + err, ok := r.(error) + if !ok { + err = errors.New(fmt.Sprint(r)) + } + + log.Printf("Trapped error in handler: %v", err) + + response = handler.NewFailedEvent(err) + } + }() + + // Populate the previous model + prevModel := &resource.Model{} + if err := req.UnmarshalPrevious(prevModel); err != nil { + log.Printf("Error unmarshaling prev model: %v", err) + return handler.NewFailedEvent(err) + } + + // Populate the current model + currentModel := &resource.Model{} + if err := req.Unmarshal(currentModel); err != nil { + log.Printf("Error unmarshaling model: %v", err) + return handler.NewFailedEvent(err) + } + + response, err := f(req, prevModel, currentModel) + if err != nil { + log.Printf("Error returned from handler function: %v", err) + return handler.NewFailedEvent(err) + } + + return response +} diff --git a/resources/Time_Offset/cmd/resource/config.go b/resources/Time_Offset/cmd/resource/config.go new file mode 100644 index 00000000..4d9eb783 --- /dev/null +++ b/resources/Time_Offset/cmd/resource/config.go @@ -0,0 +1,19 @@ +// Code generated by 'cfn generate', changes will be undone by the next invocation. DO NOT EDIT. +// Updates to this type are made my editing the schema file and executing the 'generate' command. +package resource + +import "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" + +// TypeConfiguration is autogenerated from the json schema +type TypeConfiguration struct { +} + +// Configuration returns a resource's configuration. +func Configuration(req handler.Request) (*TypeConfiguration, error) { + // Populate the type configuration + typeConfig := &TypeConfiguration{} + if err := req.UnmarshalTypeConfig(typeConfig); err != nil { + return typeConfig, err + } + return typeConfig, nil +} diff --git a/resources/Time_Offset/cmd/resource/model.go b/resources/Time_Offset/cmd/resource/model.go new file mode 100644 index 00000000..99eeb3a2 --- /dev/null +++ b/resources/Time_Offset/cmd/resource/model.go @@ -0,0 +1,24 @@ +// Code generated by 'cfn generate', changes will be undone by the next invocation. DO NOT EDIT. +// Updates to this type are made my editing the schema file and executing the 'generate' command. +package resource + +// Model is autogenerated from the json schema +type Model struct { + Id *string `json:",omitempty"` + Time *string `json:",omitempty"` + OffsetDays *int `json:",omitempty"` + OffsetHours *int `json:",omitempty"` + OffsetMinutes *int `json:",omitempty"` + OffsetMonths *int `json:",omitempty"` + OffsetSeconds *int `json:",omitempty"` + OffsetYears *int `json:",omitempty"` + Utc *string `json:",omitempty"` + Day *string `json:",omitempty"` + Hour *string `json:",omitempty"` + Minute *string `json:",omitempty"` + Month *string `json:",omitempty"` + Second *string `json:",omitempty"` + Unix *string `json:",omitempty"` + Year *string `json:",omitempty"` + Triggers []string `json:",omitempty"` +} diff --git a/resources/Time_Offset/cmd/resource/resource.go b/resources/Time_Offset/cmd/resource/resource.go new file mode 100644 index 00000000..92fff34c --- /dev/null +++ b/resources/Time_Offset/cmd/resource/resource.go @@ -0,0 +1,247 @@ +package resource + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudformation" + "github.com/aws/aws-sdk-go/service/ssm" + "github.com/google/uuid" +) + +func buildSsmParameterString(model *Model) string { + resourceType := "AwsCommunity::Time::Offset" + resourceTypeSplits := strings.Split(resourceType, "::") + return fmt.Sprintf("/CloudFormation/%s/%s/%s/%s", resourceTypeSplits[0], resourceTypeSplits[1], resourceTypeSplits[2], *model.Id) +} + +// Create handles the Create event from the Cloudformation service. +func Create(req handler.Request, _ *Model, currentModel *Model) (handler.ProgressEvent, error) { + + // If Time isn't specified we use now + if currentModel.Time == nil { + now := timeToString(time.Now()) + currentModel.Time = &now + } + id := uuid.New().String() + currentModel.Id = &id + + // determine the offset + t, err := updateTimeWithOffset(currentModel) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + Message: err.Error(), + HandlerErrorCode: cloudformation.HandlerErrorCodeAlreadyExists, + ResourceModel: nil, + }, nil + } + + // convert a timestamp into the model so we have all the attributes + timeToModel(currentModel, t) + + // Save the unique identifier in SSM if it exists its a duplicate + ssmParameter := buildSsmParameterString(currentModel) + svc := ssm.New(req.Session) + _, err = svc.PutParameter(&ssm.PutParameterInput{ + Name: &ssmParameter, + Value: &id, + Overwrite: aws.Bool(false), + Tier: aws.String("Standard"), + Type: aws.String("String"), + }) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + Message: err.Error(), + HandlerErrorCode: cloudformation.HandlerErrorCodeAlreadyExists, + ResourceModel: nil, + }, nil + } + + response := handler.ProgressEvent{ + OperationStatus: handler.Success, + Message: "Create complete", + ResourceModel: currentModel, + } + return response, nil +} + +// Read handles the Read event from the Cloudformation service. +func Read(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) { + + // See if the SSM parameter exists to determine if the resource exists + ssmParameter := buildSsmParameterString(currentModel) + svc := ssm.New(req.Session) + _, err := svc.GetParameter(&ssm.GetParameterInput{ + Name: &ssmParameter, + }) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + HandlerErrorCode: cloudformation.HandlerErrorCodeNotFound, + Message: "Resource not found", + }, nil + } + + // determine the offset + t, err := updateTimeWithOffset(currentModel) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + Message: err.Error(), + HandlerErrorCode: cloudformation.HandlerErrorCodeAlreadyExists, + ResourceModel: nil, + }, nil + } + + // convert a timestamp into the model so we have all the attributes + timeToModel(currentModel, t) + + response := handler.ProgressEvent{ + OperationStatus: handler.Success, + Message: "Read complete", + ResourceModel: currentModel, + } + + return response, nil +} + +// Update handles the Update event from the Cloudformation service. +func Update(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) { + + // Validate teh Id exists already + ssmParameter := buildSsmParameterString(currentModel) + svc := ssm.New(req.Session) + _, err := svc.GetParameter(&ssm.GetParameterInput{ + Name: &ssmParameter, + }) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + Message: err.Error(), + HandlerErrorCode: cloudformation.HandlerErrorCodeNotFound, + ResourceModel: nil, + }, nil + } + + // If Time isn't specified we use now + if currentModel.Time == nil { + now := timeToString(time.Now()) + currentModel.Time = &now + } + + // determine the offset + t, err := updateTimeWithOffset(currentModel) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + Message: err.Error(), + HandlerErrorCode: cloudformation.HandlerErrorCodeAlreadyExists, + ResourceModel: nil, + }, nil + } + + // convert a timestamp into the model so we have all the attributes + timeToModel(currentModel, t) + + response := handler.ProgressEvent{ + OperationStatus: handler.Success, + Message: "Create complete", + ResourceModel: currentModel, + } + return response, nil +} + +// Delete handles the Delete event from the Cloudformation service. +func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) { + + // See if the SSM parameter exists to determine if the resource exists + ssmParameter := buildSsmParameterString(currentModel) + svc := ssm.New(req.Session) + _, err := svc.DeleteParameter(&ssm.DeleteParameterInput{ + Name: &ssmParameter, + }) + if err != nil { + return handler.ProgressEvent{ + OperationStatus: handler.Failed, + HandlerErrorCode: cloudformation.HandlerErrorCodeNotFound, + Message: "Resource not found", + }, nil + } + + response := handler.ProgressEvent{ + OperationStatus: handler.Success, + Message: "Delete complete", + ResourceModel: nil, + } + + return response, nil +} + +// List handles the List event from the Cloudformation service. +func List(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) { + return handler.ProgressEvent{}, errors.New("this resource type does not support list") +} + +// Convert time to a string +func timeToString(t time.Time) string { + return t.Format(time.RFC3339) +} + +func updateTimeWithOffset(m *Model) (*time.Time, error) { + + t, err := time.Parse(time.RFC3339, *m.Time) + if err != nil { + return nil, err + } + + if m.OffsetDays != nil { + t = t.AddDate(0, 0, *m.OffsetDays) + } + if m.OffsetMonths != nil { + t = t.AddDate(0, *m.OffsetMonths, 0) + } + if m.OffsetYears != nil { + t = t.AddDate(*m.OffsetYears, 0, 0) + } + if m.OffsetHours != nil { + t = t.Add(time.Hour * time.Duration(*m.OffsetHours)) + } + if m.OffsetMinutes != nil { + t = t.Add(time.Minute * time.Duration(*m.OffsetMinutes)) + } + if m.OffsetSeconds != nil { + t = t.Add(time.Second * time.Duration(*m.OffsetSeconds)) + } + + return &t, nil + +} + +// Convert time into a model +func timeToModel(m *Model, t *time.Time) { + + utc := t.Format(time.RFC3339) + day := fmt.Sprintf("%02d", t.Day()) + hour := fmt.Sprintf("%02d", t.Hour()) + minute := fmt.Sprintf("%02d", t.Minute()) + month := fmt.Sprintf("%02d", int(t.Month())) + second := fmt.Sprintf("%02d", t.Second()) + unix := fmt.Sprintf("%02d", int(t.Unix())) + year := fmt.Sprintf("%02d", t.Year()) + + m.Utc = &utc + m.Day = &day + m.Hour = &hour + m.Minute = &minute + m.Month = &month + m.Second = &second + m.Unix = &unix + m.Year = &year + +} diff --git a/resources/Time_Offset/docs/README.md b/resources/Time_Offset/docs/README.md new file mode 100644 index 00000000..e97d3ad6 --- /dev/null +++ b/resources/Time_Offset/docs/README.md @@ -0,0 +1,174 @@ +# AwsCommunity::Time::Offset + +Creates a time based resource with an offset from the provided time or now. + +## Syntax + +To declare this entity in your AWS CloudFormation template, use the following syntax: + +### JSON + +
+{ + "Type" : "AwsCommunity::Time::Offset", + "Properties" : { + "Time" : String, + "OffsetDays" : Integer, + "OffsetHours" : Integer, + "OffsetMinutes" : Integer, + "OffsetMonths" : Integer, + "OffsetSeconds" : Integer, + "OffsetYears" : Integer, + "Triggers" : [ String, ... ] + } +} ++ +### YAML + +
+Type: AwsCommunity::Time::Offset +Properties: + Time: String + OffsetDays: Integer + OffsetHours: Integer + OffsetMinutes: Integer + OffsetMonths: Integer + OffsetSeconds: Integer + OffsetYears: Integer + Triggers: + - String ++ +## Properties + +#### Time + +Optional parameter to represent the time or default is now. + +_Required_: No + +_Type_: String + +_Pattern_:
^\d{4}-[0-1][0-3]-[0-3]\d{1}T[0-2]\d{1}:[0-5]\d{1}:[0-5]\d{1}Z$
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### OffsetDays
+
+Number of days to offset the base timestamp.
+
+_Required_: No
+
+_Type_: Integer
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### OffsetHours
+
+Number of hours to offset the base timestamp.
+
+_Required_: No
+
+_Type_: Integer
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### OffsetMinutes
+
+Number of minutes to offset the base timestamp.
+
+_Required_: No
+
+_Type_: Integer
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### OffsetMonths
+
+Number of months to offset the base timestamp.
+
+_Required_: No
+
+_Type_: Integer
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### OffsetSeconds
+
+Number of seconds to offset the base timestamp.
+
+_Required_: No
+
+_Type_: Integer
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### OffsetYears
+
+Number of years to offset the base timestamp.
+
+_Required_: No
+
+_Type_: Integer
+
+_Update requires_: [Replacement](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)
+
+#### Triggers
+
+A value to represent when an update to the time should occur.
+
+_Required_: No
+
+_Type_: List of String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+## Return Values
+
+### Ref
+
+When you pass the logical ID of this resource to the intrinsic `Ref` function, Ref returns the Id.
+
+### Fn::GetAtt
+
+The `Fn::GetAtt` intrinsic function returns a value for a specified attribute of this type. The following are the available attributes and sample return values.
+
+For more information about using the `Fn::GetAtt` intrinsic function, see [Fn::GetAtt](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html).
+
+#### Id
+
+Id is a unique identifier that is auto generated.
+
+#### Utc
+
+UTC returns the time in UTC format.
+
+#### Day
+
+Day returns the day of the month.
+
+#### Hour
+
+Hour returns the hour within the day, in the range [0, 23].
+
+#### Minute
+
+Minute returns the minute offset within the hour, in the range [0, 59].
+
+#### Month
+
+Month returns the month of the year.
+
+#### Second
+
+Second returns the second offset within the minute, in the range [0, 59].
+
+#### Unix
+
+Unix returns a Unix time, the number of seconds elapsed since January 1, 1970 UTC.
+
+#### Year
+
+Year returns the year.
+
diff --git a/resources/Time_Offset/go.mod b/resources/Time_Offset/go.mod
new file mode 100644
index 00000000..893dc18c
--- /dev/null
+++ b/resources/Time_Offset/go.mod
@@ -0,0 +1,16 @@
+module github.com/aws-cloudformation/awscommunity-registry-extensions/resources/Time_Offset
+
+go 1.19
+
+require (
+ github.com/aws-cloudformation/cloudformation-cli-go-plugin v1.2.0
+ github.com/aws/aws-sdk-go v1.44.235
+ github.com/google/uuid v1.3.0
+)
+
+require (
+ github.com/aws/aws-lambda-go v1.37.0 // indirect
+ github.com/jmespath/go-jmespath v0.4.0 // indirect
+ github.com/segmentio/ksuid v1.0.4 // indirect
+ gopkg.in/validator.v2 v2.0.1 // indirect
+)
diff --git a/resources/Time_Offset/go.sum b/resources/Time_Offset/go.sum
new file mode 100644
index 00000000..99f83d04
--- /dev/null
+++ b/resources/Time_Offset/go.sum
@@ -0,0 +1,58 @@
+github.com/aws-cloudformation/cloudformation-cli-go-plugin v1.2.0 h1:NHNKs4hOKBz9kufu2Ylce+P20x6mSxS2ryrYoW6AlX8=
+github.com/aws-cloudformation/cloudformation-cli-go-plugin v1.2.0/go.mod h1:u3nqs3hHrn8D51m7+N+6ya7Sksyd6OG3xK3RpXdRb1g=
+github.com/aws/aws-lambda-go v1.37.0 h1:WXkQ/xhIcXZZ2P5ZBEw+bbAKeCEcb5NtiYpSwVVzIXg=
+github.com/aws/aws-lambda-go v1.37.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=
+github.com/aws/aws-sdk-go v1.44.235 h1:5MS1ZW1Pr27mmHFqqjuXYwGMlNTW/g6DqU5ekamPMeU=
+github.com/aws/aws-sdk-go v1.44.235/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
+github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
+gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/resources/Time_Offset/inputs/inputs_1_create.json b/resources/Time_Offset/inputs/inputs_1_create.json
new file mode 100644
index 00000000..99c4a9e5
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_1_create.json
@@ -0,0 +1,10 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00",
+ "OffsetYears": "0",
+ "OffsetMonths": "0",
+ "OffsetDays": "1",
+ "OffsetHours": "0",
+ "OffsetMinutes": "0",
+ "OffsetSeconds": "0",
+ "Triggers": ["A"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_1_invalid.json b/resources/Time_Offset/inputs/inputs_1_invalid.json
new file mode 100644
index 00000000..4d78d253
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_1_invalid.json
@@ -0,0 +1,3 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00"
+}
diff --git a/resources/Time_Offset/inputs/inputs_1_update.json b/resources/Time_Offset/inputs/inputs_1_update.json
new file mode 100644
index 00000000..45186130
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_1_update.json
@@ -0,0 +1,10 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00",
+ "OffsetYears": "0",
+ "OffsetMonths": "0",
+ "OffsetDays": "1",
+ "OffsetHours": "0",
+ "OffsetMinutes": "0",
+ "OffsetSeconds": "0",
+ "Triggers": ["B"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_2_create.json b/resources/Time_Offset/inputs/inputs_2_create.json
new file mode 100644
index 00000000..878a05b4
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_2_create.json
@@ -0,0 +1,10 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00",
+ "OffsetYears": "0",
+ "OffsetMonths": "0",
+ "OffsetDays": "0",
+ "OffsetHours": "0",
+ "OffsetMinutes": "1",
+ "OffsetSeconds": "0",
+ "Triggers": ["A"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_2_update.json b/resources/Time_Offset/inputs/inputs_2_update.json
new file mode 100644
index 00000000..11d6f35d
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_2_update.json
@@ -0,0 +1,10 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00",
+ "OffsetYears": "0",
+ "OffsetMonths": "0",
+ "OffsetDays": "0",
+ "OffsetHours": "0",
+ "OffsetMinutes": "1",
+ "OffsetSeconds": "0",
+ "Triggers": ["B"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_3_create.json b/resources/Time_Offset/inputs/inputs_3_create.json
new file mode 100644
index 00000000..b495de44
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_3_create.json
@@ -0,0 +1,10 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00",
+ "OffsetYears": "10",
+ "OffsetMonths": "0",
+ "OffsetDays": "0",
+ "OffsetHours": "0",
+ "OffsetMinutes": "0",
+ "OffsetSeconds": "0",
+ "Triggers": ["A"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_3_update.json b/resources/Time_Offset/inputs/inputs_3_update.json
new file mode 100644
index 00000000..b495de44
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_3_update.json
@@ -0,0 +1,10 @@
+{
+ "Time": "2023-04-04T18:13:32+00:00",
+ "OffsetYears": "10",
+ "OffsetMonths": "0",
+ "OffsetDays": "0",
+ "OffsetHours": "0",
+ "OffsetMinutes": "0",
+ "OffsetSeconds": "0",
+ "Triggers": ["A"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_4_create.json b/resources/Time_Offset/inputs/inputs_4_create.json
new file mode 100644
index 00000000..e07acc73
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_4_create.json
@@ -0,0 +1,9 @@
+{
+ "OffsetYears": "0",
+ "OffsetMonths": "0",
+ "OffsetDays": "1",
+ "OffsetHours": "0",
+ "OffsetMinutes": "0",
+ "OffsetSeconds": "0",
+ "Triggers": ["A"]
+}
diff --git a/resources/Time_Offset/inputs/inputs_4_update.json b/resources/Time_Offset/inputs/inputs_4_update.json
new file mode 100644
index 00000000..a655ad66
--- /dev/null
+++ b/resources/Time_Offset/inputs/inputs_4_update.json
@@ -0,0 +1,9 @@
+{
+ "OffsetYears": "0",
+ "OffsetMonths": "0",
+ "OffsetDays": "1",
+ "OffsetHours": "0",
+ "OffsetMinutes": "0",
+ "OffsetSeconds": "0",
+ "Triggers": ["B"]
+}
diff --git a/resources/Time_Offset/makebuild b/resources/Time_Offset/makebuild
new file mode 100644
index 00000000..390b673b
--- /dev/null
+++ b/resources/Time_Offset/makebuild
@@ -0,0 +1,7 @@
+# This file is autogenerated, do not edit;
+# changes will be undone by the next 'generate' command.
+
+.PHONY: build
+build:
+ cfn generate
+ env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -tags="$(TAGS)" -o bin/handler cmd/main.go
diff --git a/resources/Time_Offset/resource-role-prod.yaml b/resources/Time_Offset/resource-role-prod.yaml
new file mode 100644
index 00000000..852f2007
--- /dev/null
+++ b/resources/Time_Offset/resource-role-prod.yaml
@@ -0,0 +1,33 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Description: >
+ This CloudFormation template creates a role assumed by CloudFormation
+ during CRUDL operations to mutate resources on behalf of the customer.
+
+Resources:
+ ExecutionRole:
+ Type: AWS::IAM::Role
+ Properties:
+ MaxSessionDuration: 8400
+ AssumeRolePolicyDocument:
+ Version: '2012-10-17'
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: resources.cloudformation.amazonaws.com
+ Action: sts:AssumeRole
+ Path: "/"
+ Policies:
+ - PolicyName: ResourceTypePolicy
+ PolicyDocument:
+ Version: '2012-10-17'
+ Statement:
+ - Effect: Allow
+ Action:
+ - "ssm:DeleteParameter"
+ - "ssm:GetParameter"
+ - "ssm:PutParameter"
+ Resource: "*"
+Outputs:
+ ExecutionRoleArn:
+ Value:
+ Fn::GetAtt: ExecutionRole.Arn
diff --git a/resources/Time_Offset/resource-role.yaml b/resources/Time_Offset/resource-role.yaml
new file mode 100644
index 00000000..0e70d2cc
--- /dev/null
+++ b/resources/Time_Offset/resource-role.yaml
@@ -0,0 +1,40 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Description: >
+ This CloudFormation template creates a role assumed by CloudFormation
+ during CRUDL operations to mutate resources on behalf of the customer.
+
+Resources:
+ ExecutionRole:
+ Type: AWS::IAM::Role
+ Properties:
+ MaxSessionDuration: 8400
+ AssumeRolePolicyDocument:
+ Version: '2012-10-17'
+ Statement:
+ - Effect: Allow
+ Principal:
+ Service: resources.cloudformation.amazonaws.com
+ Action: sts:AssumeRole
+ Condition:
+ StringEquals:
+ aws:SourceAccount:
+ Ref: AWS::AccountId
+ StringLike:
+ aws:SourceArn:
+ Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/resource/AwsCommunity-Time-Offset/*
+ Path: "/"
+ Policies:
+ - PolicyName: ResourceTypePolicy
+ PolicyDocument:
+ Version: '2012-10-17'
+ Statement:
+ - Effect: Allow
+ Action:
+ - "ssm:DeleteParameter"
+ - "ssm:GetParameter"
+ - "ssm:PutParameter"
+ Resource: "*"
+Outputs:
+ ExecutionRoleArn:
+ Value:
+ Fn::GetAtt: ExecutionRole.Arn
diff --git a/resources/Time_Offset/template.yml b/resources/Time_Offset/template.yml
new file mode 100644
index 00000000..2b361008
--- /dev/null
+++ b/resources/Time_Offset/template.yml
@@ -0,0 +1,27 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Transform: AWS::Serverless-2016-10-31
+Description: AWS SAM template for the AwsCommunity::Time::Offset resource type
+
+Globals:
+ Function:
+ Timeout: 180 # docker start-up times can be long for SAM CLI
+ MemorySize: 256
+
+Resources:
+ TypeFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: handler
+ Runtime: go1.x
+ CodeUri: bin/
+
+ TestEntrypoint:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: handler
+ Runtime: go1.x
+ CodeUri: bin/
+ Environment:
+ Variables:
+ MODE: Test
+
diff --git a/resources/Time_Offset/test/integ.yml b/resources/Time_Offset/test/integ.yml
new file mode 100644
index 00000000..f9b989ef
--- /dev/null
+++ b/resources/Time_Offset/test/integ.yml
@@ -0,0 +1,7 @@
+Resources:
+ Time:
+ Type: AwsCommunity::Time::Offset
+ Properties:
+ Time: "2023-03-31T18:51:56Z"
+ OffsetDays: 1
+
diff --git a/resources/Time_Offset/test/setup.yml b/resources/Time_Offset/test/setup.yml
new file mode 100644
index 00000000..86489a31
--- /dev/null
+++ b/resources/Time_Offset/test/setup.yml
@@ -0,0 +1,13 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: 'CloudFormation exports'
+
+Conditions:
+ HasNot: !Equals [ 'true', 'false' ]
+
+# dummy (null) resource, never created
+Resources:
+ NullResource:
+ Type: 'AWS::CloudFormation::CustomResource'
+ Condition: HasNot
+ Properties:
+ ServiceToken: "null"
\ No newline at end of file
diff --git a/resources/alpha-buildspec-go.yml b/resources/alpha-buildspec-go.yml
index f68c7c97..4b6440d4 100644
--- a/resources/alpha-buildspec-go.yml
+++ b/resources/alpha-buildspec-go.yml
@@ -8,12 +8,12 @@ phases:
- echo Entered the install phase...
- echo About to build $RESOURCE_PATH
- export PATH="/usr/local/bin:$PATH"
- - ENTRY_PATH=$(python scripts/resourceName.py $RESOURCE_PATH/.rpdk-config)
+ - TYPE_NAME=$(python scripts/type_name.py $RESOURCE_PATH/.rpdk-config)
+ - TYPE_CLEAN_NAME="$(echo $TYPE_NAME | sed s/::/-/g | tr ‘[:upper:]’ ‘[:lower:]’)"
- /usr/local/bin/dockerd-entrypoint.sh
- cat /var/log/docker.log
- - echo ENTRY_PATH is $ENTRY_PATH
- - BUILD_FILE_NAME=$(cat ENTRY_PATH | sed s/_/-/g)
- - BUILD_FILE_NAME="${BUILD_FILE_NAME}.zip"
+ - echo TYPE_CLEAN_NAME is $TYPE_CLEAN_NAME
+ - BUILD_FILE_NAME="${TYPE_CLEAN_NAME}.zip"
- pip install git+https://github.com/aws-cloudformation/cloudformation-cli-go-plugin.git@master
- cd $RESOURCE_PATH
- ls /usr/local/bin
@@ -27,7 +27,7 @@ phases:
- echo Entered the build phase...
- cfn validate
- cfn generate
- - SETUP_STACK_NAME="setup-$(echo $ENTRY_PATH | sed s/_/-/g)"
+ - SETUP_STACK_NAME="setup-$(echo $TYPE_CLEAN_NAME | sed s/_/-/g)"
- rain deploy test/setup.yml $SETUP_STACK_NAME -y
- make
- ls -ltra
diff --git a/resources/beta-buildspec-go.yml b/resources/beta-buildspec-go.yml
index 732052ac..1c657815 100644
--- a/resources/beta-buildspec-go.yml
+++ b/resources/beta-buildspec-go.yml
@@ -8,9 +8,10 @@ phases:
- echo Entered the install phase...
- echo About to build $RESOURCE_PATH
- export PATH="/usr/local/bin:$PATH"
- - ENTRY_PATH=$(python scripts/resourceName.py $RESOURCE_PATH/.rpdk-config)
- - echo ENTRY_PATH is $ENTRY_PATH
- - BUILD_FILE_NAME=$(cat ENTRY_PATH | sed s/_/-/g)
+ - TYPE_NAME=$(python scripts/type_name.py $RESOURCE_PATH/.rpdk-config)
+ - TYPE_CLEAN_NAME="$(echo $TYPE_NAME | sed s/::/-/g | tr ‘[:upper:]’ ‘[:lower:]’)"
+ - echo TYPE_CLEAN_NAME is $TYPE_CLEAN_NAME
+ - BUILD_FILE_NAME=$(cat TYPE_CLEAN_NAME | sed s/_/-/g)
- BUILD_FILE_NAME="${BUILD_FILE_NAME}.zip"
- pip install git+https://github.com/aws-cloudformation/cloudformation-cli-python-plugin.git@master
- cd $RESOURCE_PATH
@@ -25,7 +26,7 @@ phases:
- ../../release/deregister-all.sh us-east-1 RESOURCE
- make
- cfn submit --set-default
- - INTEG_STACK_NAME="integ-$(echo $ENTRY_PATH | sed s/_/-/g)"
+ - INTEG_STACK_NAME="integ-$(echo $TYPE_CLEAN_NAME | sed s/_/-/g)"
- rain deploy test/integ.yml $INTEG_STACK_NAME -y
finally:
- cat rpdk.log
diff --git a/resources/prod-buildspec-go.yml b/resources/prod-buildspec-go.yml
index d4bd3720..12eddf17 100644
--- a/resources/prod-buildspec-go.yml
+++ b/resources/prod-buildspec-go.yml
@@ -10,8 +10,6 @@ phases:
- export PATH="/usr/local/bin:$PATH"
- /usr/local/bin/dockerd-entrypoint.sh
- cat /var/log/docker.log
- - ENTRY_PATH=$(python scripts/resourceName.py $RESOURCE_PATH/.rpdk-config)
- - echo ENTRY_PATH is $ENTRY_PATH
- TYPE_NAME=$(python scripts/type_name.py $RESOURCE_PATH/.rpdk-config)
- echo TYPE_NAME is $TYPE_NAME
- cd $RESOURCE_PATH
diff --git a/scripts/resourceName.py b/scripts/resourceName.py
deleted file mode 100644
index d8155a0b..00000000
--- a/scripts/resourceName.py
+++ /dev/null
@@ -1,15 +0,0 @@
-"Gets the name of the folder with the handler code"
-
-import json
-import sys
-
-def main(rpdk_path):
- "Get the source folder from rpdk config"
-
- with open(rpdk_path) as f:
- obj = json.load(f)
- entrypoint = obj["typeName"]
- print(entrypoint.replace("::", "_").lower())
-
-if __name__ == "__main__":
- main(sys.argv[1])