diff --git a/gov2/cloudfront/CreateDistribution/CreateDistribution.go b/gov2/cloudfront/CreateDistribution/CreateDistribution.go new file mode 100644 index 00000000000..2b2d8b0f3d7 --- /dev/null +++ b/gov2/cloudfront/CreateDistribution/CreateDistribution.go @@ -0,0 +1,194 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// snippet-start:[cloudfront.go-v2.CreateDistribution] + +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "log" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/cloudfront" + cloudfrontTypes "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "github.com/aws/aws-sdk-go-v2/service/s3" +) + +// CFDistributionAPI defines the interface for the CreateDistribution function. +// We use this interface to test the function using a mocked service. +type CFDistributionAPI interface { + CreateDistribution(bucketName, certificateSSLArn, domain string) (*cloudfront.CreateDistributionOutput, error) + createoriginAccessIdentity(domainName string) (string, error) +} + +type CFDistributionAPIImpl struct { + s3Client *s3.Client + cloudfrontClient *cloudfront.Client +} + +func createCFDistribution(s3client *s3.Client, cloudfront *cloudfront.Client) CFDistributionAPI { + return &CFDistributionAPIImpl{ + s3Client: s3client, + cloudfrontClient: cloudfront, + } +} + +func (c *CFDistributionAPIImpl) CreateDistribution(bucketName, certificateSSLArn, domain string) (*cloudfront.CreateDistributionOutput, error) { + locationOutput, err := c.s3Client.GetBucketLocation(context.Background(), &s3.GetBucketLocationInput{Bucket: aws.String(bucketName)}) + + if err != nil { + return nil, err + } + originDomain := bucketName + ".s3." + string(locationOutput.LocationConstraint) + ".amazonaws.com" + + if err != nil { + return nil, err + } + + originAccessIdentityID, err := c.createoriginAccessIdentity(domain) + if err != nil { + return nil, err + } + + cloudfrontResponse, err := c.cloudfrontClient.CreateDistribution(context.TODO(), &cloudfront.CreateDistributionInput{ + DistributionConfig: &cloudfrontTypes.DistributionConfig{ + Enabled: aws.Bool(true), + CallerReference: &originDomain, + Comment: &originDomain, + IsIPV6Enabled: aws.Bool(false), + PriceClass: cloudfrontTypes.PriceClassPriceClass100, + HttpVersion: cloudfrontTypes.HttpVersionHttp11, + DefaultRootObject: aws.String("index.html"), + Aliases: &cloudfrontTypes.Aliases{ + Quantity: aws.Int32(1), + Items: []string{domain}, + }, + ViewerCertificate: &cloudfrontTypes.ViewerCertificate{ + ACMCertificateArn: aws.String(certificateSSLArn), + SSLSupportMethod: cloudfrontTypes.SSLSupportMethodSniOnly, + }, + CustomErrorResponses: &cloudfrontTypes.CustomErrorResponses{ + Quantity: aws.Int32(1), + Items: []cloudfrontTypes.CustomErrorResponse{ + { + ErrorCode: aws.Int32(403), + ResponseCode: aws.String("200"), + ErrorCachingMinTTL: aws.Int64(10), + ResponsePagePath: aws.String("/index.html"), + }, + }, + }, + Origins: &cloudfrontTypes.Origins{ + Quantity: aws.Int32(1), + Items: []cloudfrontTypes.Origin{ + { + DomainName: aws.String(originDomain), + Id: aws.String(originDomain), + S3OriginConfig: &cloudfrontTypes.S3OriginConfig{ + OriginAccessIdentity: aws.String("origin-access-identity/cloudfront/" + originAccessIdentityID), + }, + }, + }, + }, + CacheBehaviors: nil, + DefaultCacheBehavior: &cloudfrontTypes.DefaultCacheBehavior{ + TargetOriginId: aws.String(originDomain), + Compress: aws.Bool(true), + ViewerProtocolPolicy: cloudfrontTypes.ViewerProtocolPolicyRedirectToHttps, + AllowedMethods: &cloudfrontTypes.AllowedMethods{ + Quantity: aws.Int32(2), + Items: []cloudfrontTypes.Method{ + cloudfrontTypes.MethodGet, + cloudfrontTypes.MethodHead, + }, + }, + }, + }, + }) + + if err != nil { + return nil, err + } + + return cloudfrontResponse, nil +} + +func (c *CFDistributionAPIImpl) createoriginAccessIdentity(domainName string) (string, error) { + ctx := context.Background() + oai, err := c.cloudfrontClient.CreateCloudFrontOriginAccessIdentity(ctx, &cloudfront.CreateCloudFrontOriginAccessIdentityInput{ + CloudFrontOriginAccessIdentityConfig: &cloudfrontTypes.CloudFrontOriginAccessIdentityConfig{ + CallerReference: aws.String(domainName), + Comment: aws.String(domainName), + }, + }) + if err != nil { + return "", err + } + return *oai.CloudFrontOriginAccessIdentity.Id, nil +} + +var ( + // bucketName is the name of the S3 bucket to create a CloudFront distribution for. + bucketName = "" + // certificateSSLArn is the ARN value of the certificate issued by the AWS Certificate Manager (ACM). + // When testing, please check and copy and paste the ARN of the pre-issued certificate. + // If you don't know how to create a TLS/SSL certificate using ACM, follow the link below. + // https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html + certificateSSLArn = "" + // domain refers to the domain that will be used in conjunction with CloudFront and Amazon Route 53. + // For testing, please enter a domain that is registered in Route 53 and will be used in conjunction with CloudFront. + domain = "" +) + +// main uses the AWS SDK for Go V2 to create an Amazon CloudFront distribution. +// This example uses the default settings specified in your shared credentials +// and config files. +func main() { + + flag.StringVar(&bucketName, "bucket", "", "") + flag.StringVar(&certificateSSLArn, "cert", "", "") + flag.StringVar(&domain, "domain", "", "") + flag.Parse() + if bucketName == "" { + log.Println(errors.New("please setup bucket name")) + return + } + + if certificateSSLArn == "" { + log.Println(errors.New("please setup certificate ARN")) + return + } + + if domain == "" { + log.Println(errors.New("please setup your domain")) + return + } + + sdkConfig, err := config.LoadDefaultConfig(context.TODO()) + + if err != nil { + fmt.Println("Couldn't load default configuration. Have you set up your AWS account?") + fmt.Println(err) + return + } + + s3Client := s3.NewFromConfig(sdkConfig) + cloudfrontClient := cloudfront.NewFromConfig(sdkConfig) + + cfDistribution := createCFDistribution(s3Client, cloudfrontClient) + + result, err := cfDistribution.CreateDistribution(bucketName, certificateSSLArn, domain) + if err != nil { + fmt.Println("Couldn't create distribution. Please check error message and try again.") + fmt.Println(err) + return + } + fmt.Println(result.Distribution.ARN) +} + +// snippet-end:[cloudfront.go-v2.CreateDistribution] diff --git a/gov2/cloudfront/CreateDistribution/CreateDistribution_test.go b/gov2/cloudfront/CreateDistribution/CreateDistribution_test.go new file mode 100644 index 00000000000..ace2234bf8b --- /dev/null +++ b/gov2/cloudfront/CreateDistribution/CreateDistribution_test.go @@ -0,0 +1,81 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package main + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/cloudfront" + "github.com/aws/aws-sdk-go-v2/service/cloudfront/types" + "github.com/aws/aws-sdk-go-v2/service/s3" +) + +type MockCFDistributionAPI struct { + s3Client *s3.Client + cloudfrontClient *cloudfront.Client +} + +func (m *MockCFDistributionAPI) CreateDistribution(bucketName, certificateSSLArn, domain string) (*cloudfront.CreateDistributionOutput, error) { + if bucketName == "" || certificateSSLArn == "" || domain == "" { + return nil, errors.New("bucket name, certificate SSL ARN, and domain are required") + } + return &cloudfront.CreateDistributionOutput{ + Distribution: &types.Distribution{ + ARN: aws.String("arn:aws:cloudfront::1234567890:distribution/AAAAAAAAAAAA"), + DomainName: aws.String(domain), + DistributionConfig: &types.DistributionConfig{ + ViewerCertificate: &types.ViewerCertificate{ + ACMCertificateArn: aws.String(certificateSSLArn), + }, + }, + }, + }, nil +} + +func (m *MockCFDistributionAPI) createoriginAccessIdentity(domainName string) (string, error) { + return domainName, nil +} + +func createMockCFDistribution(s3client *s3.Client, cloudfront *cloudfront.Client) CFDistributionAPI { + return &MockCFDistributionAPI{ + s3Client: s3client, + cloudfrontClient: cloudfront, + } +} + +func TestCreateDistribution(t *testing.T) { + thisTime := time.Now() + nowString := thisTime.Format("2006-01-02 15:04:05 Monday") + t.Log("Starting integration test at " + nowString) + + sdkConfig, err := config.LoadDefaultConfig(context.TODO()) + + if err != nil { + t.Log("Got an error ...:") + t.Log(err) + return + } + + s3Client := s3.NewFromConfig(sdkConfig) + cloudfrontClient := cloudfront.NewFromConfig(sdkConfig) + + mockCFDistribution := createMockCFDistribution(s3Client, cloudfrontClient) + + bucketName := "example-com" + certificateSSLArn := "arn:aws:acm:ap-northeast-2:123456789000:certificate/000000000-0000-0000-0000-000000000000" + domain := "example.com" + + result, err := mockCFDistribution.CreateDistribution(bucketName, certificateSSLArn, domain) + + if err != nil { + t.Error(err) + return + } + + t.Log("Created Distribution with ARN: " + *result.Distribution.ARN + " for name: " + *result.Distribution.DomainName) +} diff --git a/gov2/cloudfront/go.mod b/gov2/cloudfront/go.mod new file mode 100644 index 00000000000..1aa23cfdacd --- /dev/null +++ b/gov2/cloudfront/go.mod @@ -0,0 +1,26 @@ +module example.aws/go-v2/examples/cloudfront + +go 1.18 + +require ( + github.com/aws/aws-sdk-go-v2 v1.17.4 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect + github.com/aws/aws-sdk-go-v2/config v1.18.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 // indirect + github.com/aws/aws-sdk-go-v2/service/cloudfront v1.24.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect + github.com/aws/smithy-go v1.13.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect +) diff --git a/gov2/cloudfront/go.sum b/gov2/cloudfront/go.sum new file mode 100644 index 00000000000..60c4f5a4bbc --- /dev/null +++ b/gov2/cloudfront/go.sum @@ -0,0 +1,47 @@ +github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= +github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= +github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= +github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8= +github.com/aws/aws-sdk-go-v2/credentials v1.13.12 h1:Cb+HhuEnV19zHRaYYVglwvdHGMJWbdsyP4oHhw04xws= +github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 h1:FGvpyTg2LKEmMrLlpjOgkoNp9XF5CGeyAyo33LdqZW8= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.24.1 h1:+JyTKAmvtszyz+LH0ag+Jm1BdcjfyXK3O2KJKSHIXtM= +github.com/aws/aws-sdk-go-v2/service/cloudfront v1.24.1/go.mod h1:SJRh4g8v7lNgjBVlSalPRp7Vfi2PU0LZN0PNEPjm9qk= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 h1:c5+bNdV8E4fIPteWx4HZSkqI07oY9exbfQ7JH7Yx4PI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23/go.mod h1:1jcUfF+FAOEwtIcNiHPaV4TSoZqkUIPzrohmD7fb95c= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 h1:ISLJ2BKXe4zzyZ7mp5ewKECiw0U7KpLgS3S6OxY9Cm0= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22/go.mod h1:QFVbqK54XArazLvn2wvWMRBi/jGrWii46qbr5DyPGjc= +github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 h1:5EQWIFO+Hc8E2hFcXQJ1vm6ufl/PMt/6RVRDZRju2vM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2/go.mod h1:SXDHd6fI2RhqB7vmAzyYQCTQnpZrIprVJvYxpzW3JAM= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 h1:s49mSnsBZEXjfGBkRfmK+nPqzT7Lt3+t2SmAKNyHblw= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=