summaryrefslogtreecommitdiffhomepage
path: root/deploy
diff options
context:
space:
mode:
authorRobert van Gent <[email protected]>2019-05-01 13:25:06 -0700
committerBjørn Erik Pedersen <[email protected]>2019-05-06 21:09:33 +0200
commitf4956d9aae69b1cb5715114cf5242fd80a9cabc7 (patch)
treed6dd5884a457576ccd826c41deb18b5ee1593db3 /deploy
parent2838d58b1daa0f6a337125c5a64d06215901c5d6 (diff)
downloadhugo-f4956d9aae69b1cb5715114cf5242fd80a9cabc7.tar.gz
hugo-f4956d9aae69b1cb5715114cf5242fd80a9cabc7.zip
deploy: Support invalidating a CloudFront CDN cache
Diffstat (limited to 'deploy')
-rw-r--r--deploy/cloudfront.go51
-rw-r--r--deploy/deploy.go57
-rw-r--r--deploy/deployConfig.go2
-rw-r--r--deploy/deployConfig_test.go5
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)