diff options
author | Robert van Gent <[email protected]> | 2019-05-01 13:25:06 -0700 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2019-05-06 21:09:33 +0200 |
commit | f4956d9aae69b1cb5715114cf5242fd80a9cabc7 (patch) | |
tree | d6dd5884a457576ccd826c41deb18b5ee1593db3 /deploy | |
parent | 2838d58b1daa0f6a337125c5a64d06215901c5d6 (diff) | |
download | hugo-f4956d9aae69b1cb5715114cf5242fd80a9cabc7.tar.gz hugo-f4956d9aae69b1cb5715114cf5242fd80a9cabc7.zip |
deploy: Support invalidating a CloudFront CDN cache
Diffstat (limited to 'deploy')
-rw-r--r-- | deploy/cloudfront.go | 51 | ||||
-rw-r--r-- | deploy/deploy.go | 57 | ||||
-rw-r--r-- | deploy/deployConfig.go | 2 | ||||
-rw-r--r-- | deploy/deployConfig_test.go | 5 |
4 files changed, 90 insertions, 25 deletions
diff --git a/deploy/cloudfront.go b/deploy/cloudfront.go new file mode 100644 index 000000000..dbdf9baf4 --- /dev/null +++ b/deploy/cloudfront.go @@ -0,0 +1,51 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package deploy + +import ( + "context" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudfront" +) + +// InvalidateCloudFront invalidates the CloudFront cache for distributionID. +// It uses the default AWS credentials from the environment. +func InvalidateCloudFront(ctx context.Context, distributionID string) error { + // SharedConfigEnable enables loading "shared config (~/.aws/config) and + // shared credentials (~/.aws/credentials) files". + // See https://docs.aws.amazon.com/sdk-for-go/api/aws/session/ for more + // details. + // This is the same codepath used by Go CDK when creating an s3 URL. + // TODO: Update this to a Go CDK helper once available + // (https://github.com/google/go-cloud/issues/2003). + sess, err := session.NewSessionWithOptions(session.Options{SharedConfigState: session.SharedConfigEnable}) + if err != nil { + return err + } + req := &cloudfront.CreateInvalidationInput{ + DistributionId: aws.String(distributionID), + InvalidationBatch: &cloudfront.InvalidationBatch{ + CallerReference: aws.String(time.Now().Format("20060102150405")), + Paths: &cloudfront.Paths{ + Items: []*string{aws.String("/*")}, + Quantity: aws.Int64(1), + }, + }, + } + _, err = cloudfront.New(sess).CreateInvalidationWithContext(ctx, req) + return err +} diff --git a/deploy/deploy.go b/deploy/deploy.go index 6ba348dd8..0cea4a9e3 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -45,18 +45,19 @@ import ( type Deployer struct { localFs afero.Fs - targetURL string // the Go Cloud blob URL to deploy to - matchers []*matcher // matchers to apply to uploaded files - quiet bool // true reduces STDOUT - confirm bool // true enables confirmation before making changes - dryRun bool // true skips conformations and prints changes instead of applying them - force bool // true forces upload of all files - maxDeletes int // caps the # of files to delete; -1 to disable + target *target // the target to deploy to + matchers []*matcher // matchers to apply to uploaded files + quiet bool // true reduces STDOUT + confirm bool // true enables confirmation before making changes + dryRun bool // true skips conformations and prints changes instead of applying them + force bool // true forces upload of all files + invalidateCDN bool // true enables invalidate CDN cache (if possible) + maxDeletes int // caps the # of files to delete; -1 to disable } // New constructs a new *Deployer. func New(cfg config.Provider, localFs afero.Fs) (*Deployer, error) { - target := cfg.GetString("target") + targetName := cfg.GetString("target") // Load the [deployment] section of the config. dcfg, err := decodeConfig(cfg) @@ -65,24 +66,25 @@ func New(cfg config.Provider, localFs afero.Fs) (*Deployer, error) { } // Find the target to deploy to. - var targetURL string + var tgt *target for _, t := range dcfg.Targets { - if t.Name == target { - targetURL = t.URL + if t.Name == targetName { + tgt = t } } - if targetURL == "" { - return nil, fmt.Errorf("deployment target %q not found", target) + if tgt == nil { + return nil, fmt.Errorf("deployment target %q not found", targetName) } return &Deployer{ - localFs: localFs, - targetURL: targetURL, - matchers: dcfg.Matchers, - quiet: cfg.GetBool("quiet"), - confirm: cfg.GetBool("confirm"), - dryRun: cfg.GetBool("dryRun"), - force: cfg.GetBool("force"), - maxDeletes: cfg.GetInt("maxDeletes"), + localFs: localFs, + target: tgt, + matchers: dcfg.Matchers, + quiet: cfg.GetBool("quiet"), + confirm: cfg.GetBool("confirm"), + dryRun: cfg.GetBool("dryRun"), + force: cfg.GetBool("force"), + invalidateCDN: cfg.GetBool("invalidateCDN"), + maxDeletes: cfg.GetInt("maxDeletes"), }, nil } @@ -90,7 +92,7 @@ func New(cfg config.Provider, localFs afero.Fs) (*Deployer, error) { func (d *Deployer) Deploy(ctx context.Context) error { // TODO: This opens the root path in the bucket/container. // Consider adding support for targeting a subdirectory. - bucket, err := blob.OpenBucket(ctx, d.targetURL) + bucket, err := blob.OpenBucket(ctx, d.target.URL) if err != nil { return err } @@ -203,9 +205,14 @@ func (d *Deployer) Deploy(ctx context.Context) error { jww.FEEDBACK.Println("Success!") } - // TODO: Add support for CloudFront invalidation similar to s3deploy, - // and possibly similar functionality for other providers. - + if d.invalidateCDN && d.target.CloudFrontDistributionID != "" { + jww.FEEDBACK.Println("Invalidating CloudFront CDN...") + if err := InvalidateCloudFront(ctx, d.target.CloudFrontDistributionID); err != nil { + jww.FEEDBACK.Printf("Failed to invalidate CloudFront CDN: %v\n", err) + return err + } + jww.FEEDBACK.Println("Success!") + } return nil } diff --git a/deploy/deployConfig.go b/deploy/deployConfig.go index 86321e75b..066fa0ef8 100644 --- a/deploy/deployConfig.go +++ b/deploy/deployConfig.go @@ -32,6 +32,8 @@ type deployConfig struct { type target struct { Name string URL string + + CloudFrontDistributionID string } // matcher represents configuration to be applied to files whose paths match diff --git a/deploy/deployConfig_test.go b/deploy/deployConfig_test.go index d7aa9b438..3e29d8edf 100644 --- a/deploy/deployConfig_test.go +++ b/deploy/deployConfig_test.go @@ -32,9 +32,12 @@ someOtherValue = "foo" [[deployment.targets]] Name = "name1" URL = "url1" +CloudFrontDistributionID = "cdn1" + [[deployment.targets]] name = "name2" url = "url2" +cloudfrontdistributionid = "cdn2" [[deployment.matchers]] Pattern = "^pattern1$" @@ -59,8 +62,10 @@ content-type = "contenttype2" assert.Equal(2, len(dcfg.Targets)) assert.Equal("name1", dcfg.Targets[0].Name) assert.Equal("url1", dcfg.Targets[0].URL) + assert.Equal("cdn1", dcfg.Targets[0].CloudFrontDistributionID) assert.Equal("name2", dcfg.Targets[1].Name) assert.Equal("url2", dcfg.Targets[1].URL) + assert.Equal("cdn2", dcfg.Targets[1].CloudFrontDistributionID) assert.Equal(2, len(dcfg.Matchers)) assert.Equal("^pattern1$", dcfg.Matchers[0].Pattern) |