Skip to content

Commit b66e5d2

Browse files
committed
Allow multiple distribution-id
This allows multiple values in `distribution-id`; add by repeating the flag with new values e.g.: ```bash s3deploy -bucket=mybucket -distribution-id=abcd -distribution-id=efgh ``` Fixes #142
1 parent 4d7acf3 commit b66e5d2

File tree

6 files changed

+73
-58
lines changed

6 files changed

+73
-58
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@ Note that `s3deploy` is a perfect tool to use with a continuous integration tool
2929
## Use
3030

3131
```bash
32-
Usage of ./s3deploy:
3332
-V print version and exit
3433
-acl string
3534
provide an ACL for uploaded objects. to make objects public, set to 'public-read'. all possible values are listed here: https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl (default "private")
3635
-bucket string
3736
destination bucket name on AWS
3837
-config string
3938
optional config file (default ".s3deploy.yml")
40-
-distribution-id string
41-
optional CDN distribution ID for cache invalidation
39+
-distribution-id value
40+
optional CDN distribution ID for cache invalidation, repeat flag for multiple distributions
4241
-force
4342
upload even if the etags match
4443
-h help

lib/cloudfront.go

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import (
2121
var _ remoteCDN = (*cloudFrontClient)(nil)
2222

2323
type cloudFrontClient struct {
24-
// The CloudFront distribution ID
25-
distributionID string
24+
// The CloudFront distribution IDs
25+
distributionIDs Strings
2626

2727
// Will invalidate the entire cache, e.g. "/*"
2828
force bool
@@ -36,15 +36,15 @@ func newCloudFrontClient(
3636
sess *session.Session,
3737
logger printer,
3838
cfg Config) (*cloudFrontClient, error) {
39-
if cfg.CDNDistributionID == "" {
40-
return nil, errors.New("must provide a distribution ID")
39+
if len(cfg.CDNDistributionIDs) == 0 {
40+
return nil, errors.New("must provide one or more distribution ID")
4141
}
4242
return &cloudFrontClient{
43-
distributionID: cfg.CDNDistributionID,
44-
force: cfg.Force,
45-
bucketPath: cfg.BucketPath,
46-
logger: logger,
47-
cf: cloudfront.New(sess),
43+
distributionIDs: cfg.CDNDistributionIDs,
44+
force: cfg.Force,
45+
bucketPath: cfg.BucketPath,
46+
logger: logger,
47+
cf: cloudfront.New(sess),
4848
}, nil
4949
}
5050

@@ -53,47 +53,57 @@ func (c *cloudFrontClient) InvalidateCDNCache(paths ...string) error {
5353
return nil
5454
}
5555

56-
dcfg, err := c.cf.GetDistribution(&cloudfront.GetDistributionInput{
57-
Id: &c.distributionID,
58-
})
59-
if err != nil {
60-
return err
61-
}
56+
invalidateForID := func(id string) error {
57+
dcfg, err := c.cf.GetDistribution(&cloudfront.GetDistributionInput{
58+
Id: &id,
59+
})
60+
if err != nil {
61+
return err
62+
}
6263

63-
originPath := *dcfg.Distribution.DistributionConfig.Origins.Items[0].OriginPath
64-
var root string
65-
if originPath != "" || c.bucketPath != "" {
66-
var subPath string
67-
root, subPath = c.determineRootAndSubPath(c.bucketPath, originPath)
68-
if subPath != "" {
69-
for i, p := range paths {
70-
paths[i] = strings.TrimPrefix(p, subPath)
64+
originPath := *dcfg.Distribution.DistributionConfig.Origins.Items[0].OriginPath
65+
var root string
66+
if originPath != "" || c.bucketPath != "" {
67+
var subPath string
68+
root, subPath = c.determineRootAndSubPath(c.bucketPath, originPath)
69+
if subPath != "" {
70+
for i, p := range paths {
71+
paths[i] = strings.TrimPrefix(p, subPath)
72+
}
7173
}
7274
}
73-
}
7475

75-
// This will try to reduce the number of invaldation paths to maximum 8.
76-
// If that isn't possible it will fall back to a full invalidation, e.g. "/*".
77-
// CloudFront allows 1000 free invalidations per month. After that they
78-
// cost money, so we want to keep this down.
79-
paths = c.normalizeInvalidationPaths(root, 8, c.force, paths...)
76+
// This will try to reduce the number of invaldation paths to maximum 8.
77+
// If that isn't possible it will fall back to a full invalidation, e.g. "/*".
78+
// CloudFront allows 1000 free invalidations per month. After that they
79+
// cost money, so we want to keep this down.
80+
paths = c.normalizeInvalidationPaths(root, 8, c.force, paths...)
8081

81-
if len(paths) > 10 {
82-
c.logger.Printf("Create CloudFront invalidation request for %d paths", len(paths))
83-
} else {
84-
c.logger.Printf("Create CloudFront invalidation request for %v", paths)
85-
}
82+
if len(paths) > 10 {
83+
c.logger.Printf("Create CloudFront invalidation request for %d paths", len(paths))
84+
} else {
85+
c.logger.Printf("Create CloudFront invalidation request for %v", paths)
86+
}
87+
88+
in := &cloudfront.CreateInvalidationInput{
89+
DistributionId: &id,
90+
InvalidationBatch: c.pathsToInvalidationBatch(time.Now().Format("20060102150405"), paths...),
91+
}
92+
93+
_, err = c.cf.CreateInvalidation(
94+
in,
95+
)
8696

87-
in := &cloudfront.CreateInvalidationInput{
88-
DistributionId: &c.distributionID,
89-
InvalidationBatch: c.pathsToInvalidationBatch(time.Now().Format("20060102150405"), paths...),
97+
return err
9098
}
9199

92-
_, err = c.cf.CreateInvalidation(
93-
in,
94-
)
100+
for _, id := range c.distributionIDs {
101+
if err := invalidateForID(id); err != nil {
102+
return err
103+
}
104+
}
95105

96-
return err
106+
return nil
97107
}
98108

99109
func (*cloudFrontClient) pathsToInvalidationBatch(ref string, paths ...string) *cloudfront.InvalidationBatch {
@@ -136,7 +146,6 @@ func (c *cloudFrontClient) determineRootAndSubPath(bucketPath, originPath string
136146
}
137147

138148
return
139-
140149
}
141150

142151
// For path rules, see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html

lib/cloudfront_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func TestReduceInvalidationPaths(t *testing.T) {
6161
assert.Equal([]string{"/*"}, normalized)
6262
normalized = client.normalizeInvalidationPaths("root", 5, true, rootPlusManyInDifferentFoldersNested...)
6363
assert.Equal([]string{"/root/*"}, normalized)
64-
6564
}
6665

6766
func TestDetermineRootAndSubPath(t *testing.T) {
@@ -80,7 +79,6 @@ func TestDetermineRootAndSubPath(t *testing.T) {
8079
check("/temp/forsale/", "temp", "/forsale", "temp")
8180
check("root", "root", "/", "root")
8281
check("root", "/root", "/", "root")
83-
8482
}
8583

8684
func TestPathsToInvalidationBatch(t *testing.T) {
@@ -99,13 +97,13 @@ func TestNewCloudFrontClient(t *testing.T) {
9997
assert := require.New(t)
10098
s := mock.Session
10199
c, err := newCloudFrontClient(s, newPrinter(ioutil.Discard), Config{
102-
CDNDistributionID: "12345",
103-
Force: true,
104-
BucketPath: "/mypath",
100+
CDNDistributionIDs: Strings{"12345"},
101+
Force: true,
102+
BucketPath: "/mypath",
105103
})
106104
assert.NoError(err)
107105
assert.NotNil(c)
108-
assert.Equal("12345", c.distributionID)
106+
assert.Equal("12345", c.distributionIDs[0])
109107
assert.Equal("/mypath", c.bucketPath)
110108
assert.Equal(true, c.force)
111109
}

lib/config.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ import (
1515
"strings"
1616
)
1717

18+
type Strings []string
19+
20+
func (i *Strings) String() string {
21+
return strings.Join(*i, ",")
22+
}
23+
24+
func (i *Strings) Set(value string) error {
25+
*i = append(*i, value)
26+
return nil
27+
}
28+
1829
// Config configures a deployment.
1930
type Config struct {
2031
conf fileConfig
@@ -29,8 +40,8 @@ type Config struct {
2940
BucketPath string
3041
RegionName string
3142

32-
// When set, will invalidate the CDN cache for the updated files.
33-
CDNDistributionID string
43+
// When set, will invalidate the CDN cache(s) for the updated files.
44+
CDNDistributionIDs Strings
3445

3546
// Optional configFile
3647
ConfigFile string
@@ -70,7 +81,7 @@ func flagsToConfig(f *flag.FlagSet) (*Config, error) {
7081
f.StringVar(&cfg.BucketName, "bucket", "", "destination bucket name on AWS")
7182
f.StringVar(&cfg.BucketPath, "path", "", "optional bucket sub path")
7283
f.StringVar(&cfg.SourcePath, "source", ".", "path of files to upload")
73-
f.StringVar(&cfg.CDNDistributionID, "distribution-id", "", "optional CDN distribution ID for cache invalidation")
84+
f.Var(&cfg.CDNDistributionIDs, "distribution-id", "optional CDN distribution ID for cache invalidation, repeat flag for multiple distributions")
7485
f.StringVar(&cfg.ConfigFile, "config", ".s3deploy.yml", "optional config file")
7586
f.IntVar(&cfg.MaxDelete, "max-delete", 256, "maximum number of files to delete per deploy")
7687
f.BoolVar(&cfg.PublicReadACL, "public-access", false, "DEPRECATED: please set -acl='public-read'")

lib/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestFlagsToConfig(t *testing.T) {
4747
assert.Equal("mysource", cfg.SourcePath)
4848
assert.Equal(true, cfg.Try)
4949
assert.Equal("myregion", cfg.RegionName)
50-
assert.Equal("mydistro", cfg.CDNDistributionID)
50+
assert.Equal(Strings{"mydistro"}, cfg.CDNDistributionIDs)
5151
assert.Equal("^ignored-prefix.*", cfg.Ignore)
5252
}
5353

lib/s3.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func newRemoteStore(cfg Config, logger printer) (*s3Store, error) {
5353
return nil, err
5454
}
5555

56-
if cfg.CDNDistributionID != "" {
56+
if len(cfg.CDNDistributionIDs) > 0 {
5757
cfc, err = newCloudFrontClient(sess, logger, cfg)
5858
if err != nil {
5959
return nil, err
@@ -70,7 +70,6 @@ func newRemoteStore(cfg Config, logger printer) (*s3Store, error) {
7070
s = &s3Store{svc: s3.New(sess), cfc: cfc, acl: acl, bucket: cfg.BucketName, r: cfg.conf.Routes, bucketPath: cfg.BucketPath}
7171

7272
return s, nil
73-
7473
}
7574

7675
func (s *s3Store) FileMap(opts ...opOption) (map[string]file, error) {
@@ -93,7 +92,6 @@ func (s *s3Store) FileMap(opts ...opOption) (map[string]file, error) {
9392
}
9493

9594
func (s *s3Store) Put(ctx context.Context, f localFile, opts ...opOption) error {
96-
9795
headers := f.Headers()
9896

9997
withHeaders := func(r *request.Request) {

0 commit comments

Comments
 (0)