Skip to content

Commit 6eb7e4f

Browse files
committed
#3 - implementation of ls tags
1 parent 7cb1330 commit 6eb7e4f

9 files changed

Lines changed: 205 additions & 56 deletions

File tree

cmd/access_workers.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,23 @@ import (
88

99
type accessFuncT func(bucket string, o *s3.ObjectVersion) error
1010

11-
type opFunc func(bucket, key, version string) error
12-
13-
func accessFuncBuilder(op opFunc) accessFuncT {
11+
func accessFuncBuilder(op accessFuncT) accessFuncT {
1412
switch {
1513
case accessConfig.all:
1614
return func(bucket string, o *s3.ObjectVersion) error {
17-
return op(bucket, *o.Key, *o.VersionId)
15+
return op(bucket, o)
1816
}
1917
case accessConfig.version != "":
2018
return func(bucket string, o *s3.ObjectVersion) error {
2119
if o.VersionId != nil && *o.VersionId == accessConfig.version {
22-
return op(bucket, *o.Key, accessConfig.version)
20+
return op(bucket, o)
2321
}
2422
return nil
2523
}
2624
default: // use latest
2725
return func(bucket string, o *s3.ObjectVersion) error {
2826
if o.IsLatest != nil && *o.IsLatest {
29-
return op(bucket, *o.Key, *o.VersionId)
27+
return op(bucket, o)
3028
}
3129
return nil
3230
}

cmd/compliance.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ var (
3434
complianceMode = s3.ObjectLockRetentionModeCompliance
3535
)
3636

37-
func complOp(svc *s3.S3, rdr *bufio.Reader) opFunc {
38-
return func(bucket, key, version string) error {
37+
func complOp(svc *s3.S3, rdr *bufio.Reader) accessFuncT {
38+
return func(bucket string, o *s3.ObjectVersion) error {
3939
expireAt := time.Now().UTC().Add(complianceConf.duration)
40-
fmt.Printf("Locking s3://%s/%s version %s expires %s, proceed? (y/N):", bucket, key, version, expireAt.Format("2006-01-02 15:04:05"))
40+
fmt.Printf("Locking s3://%s/%s version %s expires %s, proceed? (y/N):", bucket, *o.Key, *o.VersionId, expireAt.Format("2006-01-02 15:04:05"))
4141
answer, _, err := rdr.ReadLine()
4242
switch {
4343
case err == io.EOF:
@@ -48,12 +48,12 @@ func complOp(svc *s3.S3, rdr *bufio.Reader) opFunc {
4848
_, err := svc.PutObjectRetention(
4949
&s3.PutObjectRetentionInput{
5050
Bucket: &bucket,
51-
Key: &key,
51+
Key: o.Key,
5252
Retention: &s3.ObjectLockRetention{
5353
Mode: &complianceMode,
5454
RetainUntilDate: &expireAt,
5555
},
56-
VersionId: &version,
56+
VersionId: o.VersionId,
5757
},
5858
)
5959
return err

cmd/govern.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,39 +48,39 @@ var (
4848
bypass = true
4949
)
5050

51-
func governOp(svc *s3.S3, opCode string) opFunc {
51+
func governOp(svc *s3.S3, opCode string) accessFuncT {
5252
switch opCode {
5353
case "ON":
54-
return func(bucket, key, version string) error {
55-
log.Infof("governance %s: s3://%s/%s@%s", opCode, bucket, key, version)
54+
return func(bucket string, o *s3.ObjectVersion) error {
55+
log.Infof("governance %s: s3://%s/%s@%s", opCode, bucket, *o.Key, *o.VersionId)
5656
expireAt := time.Now().UTC().Add(govConf.duration)
5757
_, err := svc.PutObjectRetention(
5858
&s3.PutObjectRetentionInput{
5959
Bucket: &bucket,
60-
Key: &key,
60+
Key: o.Key,
6161
Retention: &s3.ObjectLockRetention{
6262
Mode: &govMode,
6363
RetainUntilDate: &expireAt,
6464
},
65-
VersionId: &version,
65+
VersionId: o.VersionId,
6666
},
6767
)
6868
return err
6969
}
7070
case "OFF":
71-
return func(bucket, key, version string) error {
72-
log.Infof("governance %s: s3://%s/%s@%s", opCode, bucket, key, version)
71+
return func(bucket string, o *s3.ObjectVersion) error {
72+
log.Infof("governance %s: s3://%s/%s@%s", opCode, bucket, *o.Key, *o.VersionId)
7373
expireAt := time.Now().UTC().Add(1 * time.Second)
7474
_, err := svc.PutObjectRetention(
7575
&s3.PutObjectRetentionInput{
7676
Bucket: &bucket,
77-
Key: &key,
77+
Key: o.Key,
7878
Retention: &s3.ObjectLockRetention{
7979
Mode: &govMode,
8080
RetainUntilDate: &expireAt,
8181
},
8282
BypassGovernanceRetention: &bypass,
83-
VersionId: &version,
83+
VersionId: o.VersionId,
8484
},
8585
)
8686
return err

cmd/legal.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ func init() {
3939
legalCmd.AddCommand(legalAdd, legalRm)
4040
}
4141

42-
func holdOp(svc *s3.S3, opCode string) opFunc {
43-
return func(bucket, key, version string) error {
44-
log.Infof("hold %s: s3://%s/%s@%s", opCode, bucket, key, version)
42+
func holdOp(svc *s3.S3, opCode string) accessFuncT {
43+
return func(bucket string, o *s3.ObjectVersion) error {
44+
log.Infof("hold %s: s3://%s/%s@%s", opCode, bucket, *o.Key, *o.VersionId)
4545
_, err := svc.PutObjectLegalHold(
4646
&s3.PutObjectLegalHoldInput{
4747
Bucket: &bucket,
48-
Key: &key,
48+
Key: o.Key,
4949
LegalHold: &s3.ObjectLockLegalHold{
5050
Status: &opCode,
5151
},
52-
VersionId: &version,
52+
VersionId: o.VersionId,
5353
},
5454
)
5555
return err

cmd/ls.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cmd
22

33
import (
4+
"time"
5+
46
"github.com/spf13/cobra"
57
)
68

@@ -22,3 +24,45 @@ var lsConfig struct {
2224
asYaml bool
2325
asTable bool
2426
}
27+
28+
type Version struct {
29+
VersionId string `json:"version_id" yaml:"version_id"`
30+
LastModified time.Time `json:"last_modified" yaml:"last_modified"`
31+
Latest bool `json:"latest" yaml:"latest"`
32+
}
33+
34+
type VersionTag struct {
35+
Version `json:"version" yaml:"version"`
36+
Tag []string `json:"tag" yaml:"tag"`
37+
}
38+
39+
type PathVersion struct {
40+
Path string `json:"path" yaml:"path"`
41+
Versions []Version `json:"versions" yaml:"versions"`
42+
Latest string `json:"latest" yaml:"latest"`
43+
}
44+
45+
type PathVersionTag struct {
46+
Path string `json:"path" yaml:"path"`
47+
Versions []VersionTag `json:"versions" yaml:"versions"`
48+
}
49+
50+
func mapPathVersions(src map[string]*PathVersion) []*PathVersion {
51+
v := make([]*PathVersion, len(src))
52+
i := 0
53+
for _, val := range src {
54+
v[i] = val
55+
i++
56+
}
57+
return v
58+
}
59+
60+
func mapPathVersionTags(src map[string]*PathVersionTag) []*PathVersionTag {
61+
v := make([]*PathVersionTag, len(src))
62+
i := 0
63+
for _, val := range src {
64+
v[i] = val
65+
i++
66+
}
67+
return v
68+
}

cmd/ls_tags.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package cmd
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"strings"
8+
"sync"
9+
10+
"github.com/aws/aws-sdk-go/service/s3"
11+
"github.com/olekukonko/tablewriter"
12+
"github.com/spf13/cobra"
13+
"gopkg.in/yaml.v2"
14+
)
15+
16+
var lsTags = &cobra.Command{
17+
Use: "tags s3://bucket/folder/ s3://bucket/folder/prefix ...",
18+
Short: "List tags for object(s)",
19+
SilenceUsage: true,
20+
RunE: func(_ *cobra.Command, urls []string) error {
21+
type VTuple struct {
22+
bucket string
23+
o *s3.ObjectVersion
24+
}
25+
processChan := make(chan VTuple, 100)
26+
var wg sync.WaitGroup
27+
wg.Add(1)
28+
keysMap := make(map[string]*PathVersionTag)
29+
go func() {
30+
defer wg.Done()
31+
for t := range processChan {
32+
ver := t.o
33+
res, err := svc.GetObjectTagging(&s3.GetObjectTaggingInput{
34+
Bucket: &t.bucket,
35+
Key: t.o.Key,
36+
VersionId: t.o.VersionId,
37+
})
38+
var tags []string
39+
if err != nil {
40+
log.Errorf("can't get tags for s3://%s/%s version %s", t.bucket, *t.o.Key, *t.o.VersionId)
41+
} else {
42+
tags = make([]string, len(res.TagSet))
43+
for i, ts := range res.TagSet {
44+
tags[i] = *ts.Key + "=" + *ts.Value
45+
}
46+
}
47+
var (
48+
v *PathVersionTag
49+
ok bool
50+
)
51+
if v, ok = keysMap[*ver.Key]; ok {
52+
v.Versions = append(v.Versions, VersionTag{
53+
Version: Version{
54+
VersionId: *ver.VersionId,
55+
LastModified: *ver.LastModified,
56+
Latest: *ver.IsLatest,
57+
},
58+
Tag: tags,
59+
})
60+
} else {
61+
v = &PathVersionTag{
62+
Path: fmt.Sprintf("s3://%s/%s", t.bucket, *ver.Key),
63+
Versions: []VersionTag{
64+
{
65+
Version: Version{
66+
VersionId: *ver.VersionId,
67+
LastModified: *ver.LastModified,
68+
Latest: *ver.IsLatest,
69+
},
70+
Tag: tags,
71+
},
72+
},
73+
}
74+
keysMap[*ver.Key] = v
75+
}
76+
}
77+
}()
78+
var tagLister accessFuncT = func(bucket string, o *s3.ObjectVersion) error {
79+
processChan <- VTuple{
80+
bucket: bucket,
81+
o: o,
82+
}
83+
return nil
84+
}
85+
if err := run(urls, accessFuncBuilder(tagLister)); err != nil {
86+
close(processChan)
87+
return err
88+
}
89+
close(processChan)
90+
wg.Wait()
91+
var renderF func(map[string]*PathVersionTag) error
92+
switch {
93+
case lsConfig.asJson:
94+
renderF = func(keymap map[string]*PathVersionTag) error {
95+
return json.NewEncoder(os.Stdout).Encode(mapPathVersionTags(keymap))
96+
}
97+
case lsConfig.asYaml:
98+
renderF = func(keymap map[string]*PathVersionTag) error {
99+
return yaml.NewEncoder(os.Stdout).Encode(mapPathVersionTags(keymap))
100+
}
101+
default:
102+
renderF = func(keymap map[string]*PathVersionTag) error {
103+
table := tablewriter.NewWriter(os.Stdout)
104+
table.SetHeader([]string{"Path", "Version", "Tags"})
105+
table.SetAutoMergeCells(true)
106+
table.SetRowLine(true)
107+
table.SetAutoMergeCellsByColumnIndex([]int{0, 1})
108+
for _, row := range keymap {
109+
for _, v := range row.Versions {
110+
table.Append([]string{row.Path, v.VersionId, strings.Join(v.Tag, "\n")})
111+
}
112+
}
113+
table.Render()
114+
return nil
115+
}
116+
}
117+
renderF(keysMap)
118+
return nil
119+
},
120+
}
121+
122+
func init() {
123+
f := lsTags.Flags()
124+
initVersionsConfig(f)
125+
lsCmd.AddCommand(lsTags)
126+
}

cmd/ls_versions.go

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"os"
77
"strings"
8-
"time"
98

109
"gopkg.in/yaml.v2"
1110

@@ -14,46 +13,24 @@ import (
1413
"github.com/spf13/cobra"
1514
)
1615

17-
type Version struct {
18-
VersionId string `json:"version_id" yaml:"version_id"`
19-
LastModified time.Time `json:"last_modified" yaml:"last_modified"`
20-
Latest bool `json:"latest" yaml:"latest"`
21-
}
22-
23-
type PathVersion struct {
24-
Path string `json:"path" yaml:"path"`
25-
Versions []Version `json:"versions" yaml:"versions"`
26-
Latest string `json:"latest" yaml:"latest"`
27-
}
28-
29-
func mapValues(src map[string]*PathVersion) []*PathVersion {
30-
v := make([]*PathVersion, len(src))
31-
i := 0
32-
for _, val := range src {
33-
v[i] = val
34-
i++
35-
}
36-
return v
37-
}
38-
3916
var lsVersions = &cobra.Command{
4017
Use: "versions s3://bucket/folder/ s3://bucket/folder/prefix ...",
4118
Short: "List object version(s)",
4219
Args: cobra.MinimumNArgs(1),
4320
RunE: func(_ *cobra.Command, urls []string) error {
4421
svc := getS3()
45-
var renderF func(string, map[string]*PathVersion) error
22+
var renderF func(map[string]*PathVersion) error
4623
switch {
4724
case lsConfig.asJson:
48-
renderF = func(bucket string, keymap map[string]*PathVersion) error {
49-
return json.NewEncoder(os.Stdout).Encode(mapValues(keymap))
25+
renderF = func(keymap map[string]*PathVersion) error {
26+
return json.NewEncoder(os.Stdout).Encode(mapPathVersions(keymap))
5027
}
5128
case lsConfig.asYaml:
52-
renderF = func(bucket string, keymap map[string]*PathVersion) error {
53-
return yaml.NewEncoder(os.Stdout).Encode(mapValues(keymap))
29+
renderF = func(keymap map[string]*PathVersion) error {
30+
return yaml.NewEncoder(os.Stdout).Encode(mapPathVersions(keymap))
5431
}
5532
default:
56-
renderF = func(bucket string, keymap map[string]*PathVersion) error {
33+
renderF = func(keymap map[string]*PathVersion) error {
5734
table := tablewriter.NewWriter(os.Stdout)
5835
table.SetHeader([]string{"Path", "Version", "Last Modified", "Latest"})
5936
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER})
@@ -80,6 +57,8 @@ var lsVersions = &cobra.Command{
8057
}
8158
table.Append([]string{row.Path, verString.String(), timeString.String(), latestString.String()})
8259
verString.Reset()
60+
latestString.Reset()
61+
timeString.Reset()
8362
}
8463
table.Render()
8564
return nil
@@ -120,7 +99,7 @@ var lsVersions = &cobra.Command{
12099
}); err != nil {
121100
return err
122101
}
123-
renderF(bucket, keysMap)
102+
renderF(keysMap)
124103
}
125104
return nil
126105
},

0 commit comments

Comments
 (0)