Skip to content

Commit fc7434a

Browse files
committed
#3 - implementation of ls locks
1 parent 6eb7e4f commit fc7434a

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

cmd/ls.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ type VersionTag struct {
3636
Tag []string `json:"tag" yaml:"tag"`
3737
}
3838

39+
type VersionLocks struct {
40+
Version `json:"version" yaml:"version"`
41+
LegalHold bool `yaml:"legal_hold" json:"legal_hold"`
42+
GovernanceRetention *time.Time `yaml:"governance_retention" json:"governance_retention"`
43+
ComplianceRetention *time.Time `yaml:"compliance_retention" json:"compliance_retention"`
44+
}
45+
3946
type PathVersion struct {
4047
Path string `json:"path" yaml:"path"`
4148
Versions []Version `json:"versions" yaml:"versions"`
@@ -47,6 +54,11 @@ type PathVersionTag struct {
4754
Versions []VersionTag `json:"versions" yaml:"versions"`
4855
}
4956

57+
type PathVersionLocks struct {
58+
Path string `json:"path" yaml:"path"`
59+
Versions []VersionLocks `json:"versions" yaml:"versions"`
60+
}
61+
5062
func mapPathVersions(src map[string]*PathVersion) []*PathVersion {
5163
v := make([]*PathVersion, len(src))
5264
i := 0
@@ -66,3 +78,13 @@ func mapPathVersionTags(src map[string]*PathVersionTag) []*PathVersionTag {
6678
}
6779
return v
6880
}
81+
82+
func mapPathVersionLocks(src map[string]*PathVersionLocks) []*PathVersionLocks {
83+
v := make([]*PathVersionLocks, len(src))
84+
i := 0
85+
for _, val := range src {
86+
v[i] = val
87+
i++
88+
}
89+
return v
90+
}

cmd/ls_locks.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package cmd
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"sync"
8+
"time"
9+
10+
"github.com/aws/aws-sdk-go/aws/awserr"
11+
"github.com/aws/aws-sdk-go/service/s3"
12+
"github.com/olekukonko/tablewriter"
13+
"github.com/spf13/cobra"
14+
"gopkg.in/yaml.v2"
15+
)
16+
17+
var lsLocks = &cobra.Command{
18+
Use: "locks s3://bucket/folder/ s3://bucket/folder/prefix ...",
19+
Short: "List various locks on S3 object(s) (legal hold, governance/compliance retention)",
20+
Args: cobra.MinimumNArgs(1),
21+
RunE: func(_ *cobra.Command, urls []string) error {
22+
type VTuple struct {
23+
bucket string
24+
o *s3.ObjectVersion
25+
}
26+
processChan := make(chan VTuple, 100)
27+
var wg sync.WaitGroup
28+
wg.Add(1)
29+
keysMap := make(map[string]*PathVersionLocks)
30+
go func() {
31+
defer wg.Done()
32+
for t := range processChan {
33+
ver := t.o
34+
var (
35+
legalHoldStatus bool
36+
complianceExp *time.Time
37+
governanceExp *time.Time
38+
)
39+
if holdResult, err := svc.GetObjectLegalHold(&s3.GetObjectLegalHoldInput{
40+
Bucket: &t.bucket,
41+
Key: t.o.Key,
42+
VersionId: t.o.VersionId,
43+
}); err != nil {
44+
switch errT := err.(type) {
45+
case awserr.Error:
46+
if errT.Code() != "NoSuchObjectLockConfiguration" {
47+
log.Errorf("can't get legal hold for s3://%s/%s version %s: %+v", t.bucket, *t.o.Key, *t.o.VersionId, err)
48+
}
49+
default:
50+
log.Errorf("can't get legal hold for s3://%s/%s version %s: %+v", t.bucket, *t.o.Key, *t.o.VersionId, err)
51+
}
52+
} else if holdResult.LegalHold != nil {
53+
legalHoldStatus = *holdResult.LegalHold.Status == "ON"
54+
}
55+
if retentionRes, err := svc.GetObjectRetention(&s3.GetObjectRetentionInput{
56+
Bucket: &t.bucket,
57+
Key: t.o.Key,
58+
VersionId: t.o.VersionId,
59+
}); err != nil {
60+
switch errT := err.(type) {
61+
case awserr.Error:
62+
if errT.Code() != "NoSuchObjectLockConfiguration" {
63+
log.Errorf("can't get legal hold for s3://%s/%s version %s: %+v", t.bucket, *t.o.Key, *t.o.VersionId, err)
64+
}
65+
default:
66+
log.Errorf("can't get legal hold for s3://%s/%s version %s: %+v", t.bucket, *t.o.Key, *t.o.VersionId, err)
67+
}
68+
} else if retentionRes.Retention != nil {
69+
switch *retentionRes.Retention.Mode {
70+
case s3.ObjectLockRetentionModeCompliance:
71+
complianceExp = retentionRes.Retention.RetainUntilDate
72+
case s3.ObjectLockModeGovernance:
73+
governanceExp = retentionRes.Retention.RetainUntilDate
74+
}
75+
}
76+
var (
77+
v *PathVersionLocks
78+
ok bool
79+
)
80+
vLock := VersionLocks{
81+
Version: Version{
82+
VersionId: *ver.VersionId,
83+
LastModified: *ver.LastModified,
84+
Latest: *ver.IsLatest,
85+
},
86+
LegalHold: legalHoldStatus,
87+
ComplianceRetention: complianceExp,
88+
GovernanceRetention: governanceExp,
89+
}
90+
if v, ok = keysMap[*ver.Key]; ok {
91+
v.Versions = append(v.Versions, vLock)
92+
} else {
93+
v = &PathVersionLocks{
94+
Path: fmt.Sprintf("s3://%s/%s", t.bucket, *ver.Key),
95+
Versions: []VersionLocks{vLock},
96+
}
97+
keysMap[*ver.Key] = v
98+
}
99+
}
100+
}()
101+
var tagLister accessFuncT = func(bucket string, o *s3.ObjectVersion) error {
102+
processChan <- VTuple{
103+
bucket: bucket,
104+
o: o,
105+
}
106+
return nil
107+
}
108+
if err := run(urls, accessFuncBuilder(tagLister)); err != nil {
109+
close(processChan)
110+
return err
111+
}
112+
close(processChan)
113+
wg.Wait()
114+
var renderF func(map[string]*PathVersionLocks) error
115+
switch {
116+
case lsConfig.asJson:
117+
renderF = func(keymap map[string]*PathVersionLocks) error {
118+
return json.NewEncoder(os.Stdout).Encode(mapPathVersionLocks(keymap))
119+
}
120+
case lsConfig.asYaml:
121+
renderF = func(keymap map[string]*PathVersionLocks) error {
122+
return yaml.NewEncoder(os.Stdout).Encode(mapPathVersionLocks(keymap))
123+
}
124+
default:
125+
renderF = func(keymap map[string]*PathVersionLocks) error {
126+
table := tablewriter.NewWriter(os.Stdout)
127+
table.SetHeader([]string{"Path", "Version", "Hold", "Governance", "Compliance"})
128+
table.SetAutoMergeCells(true)
129+
table.SetRowLine(true)
130+
table.SetAutoMergeCellsByColumnIndex([]int{0, 1})
131+
for _, row := range keymap {
132+
for _, v := range row.Versions {
133+
tableRow := []string{row.Path, v.VersionId, "", "", ""}
134+
if v.LegalHold {
135+
tableRow[2] = "ON"
136+
}
137+
if v.GovernanceRetention != nil {
138+
tableRow[3] = v.GovernanceRetention.Format("2006-01-02 15:04:05 -0700 MST")
139+
}
140+
if v.ComplianceRetention != nil {
141+
tableRow[4] = v.ComplianceRetention.Format("2006-01-02 15:04:05 -0700 MST")
142+
}
143+
table.Append(tableRow)
144+
}
145+
}
146+
table.Render()
147+
return nil
148+
}
149+
}
150+
renderF(keysMap)
151+
return nil
152+
return nil
153+
},
154+
}
155+
156+
func init() {
157+
initVersionsConfig(lsLocks.Flags())
158+
lsCmd.AddCommand(lsLocks)
159+
}

0 commit comments

Comments
 (0)