github.com/aavshr/aws-sdk-go@v1.41.3/example/service/s3/listObjectsConcurrently/listObjectsConcurrently.go (about) 1 //go:build example 2 // +build example 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "sort" 10 "sync" 11 "time" 12 13 "github.com/aavshr/aws-sdk-go/aws" 14 "github.com/aavshr/aws-sdk-go/aws/session" 15 "github.com/aavshr/aws-sdk-go/service/s3" 16 ) 17 18 func exit(msg ...interface{}) { 19 fmt.Fprintln(os.Stderr, msg...) 20 os.Exit(1) 21 } 22 23 // Lists all encrypted objects owned by an account. The `accounts` string 24 // contains a list of profiles to use. 25 // 26 // Usage: 27 // listObjectsConcurrentlv 28 func main() { 29 accounts := []string{"default", "default2", "otherprofile"} 30 31 // Spin off a worker for each account to retrieve that account's 32 bucketCh := make(chan *Bucket, 5) 33 var wg sync.WaitGroup 34 for _, acc := range accounts { 35 wg.Add(1) 36 go func(acc string) { 37 defer wg.Done() 38 39 sess, err := session.NewSessionWithOptions(session.Options{ 40 Profile: acc, 41 }) 42 if err != nil { 43 fmt.Fprintf(os.Stderr, "failed to create session for account, %s, %v\n", acc, err) 44 return 45 } 46 if err = getAccountBuckets(sess, bucketCh, acc); err != nil { 47 fmt.Fprintf(os.Stderr, "failed to get account %s's bucket info, %v\n", acc, err) 48 } 49 }(acc) 50 } 51 // Spin off a goroutine which will wait until all account buckets have been collected and 52 // added to the bucketCh. Close the bucketCh so the for range below will exit once all 53 // bucket info is printed. 54 go func() { 55 wg.Wait() 56 close(bucketCh) 57 }() 58 59 // Receive from the bucket channel printing the information for each bucket to the console 60 // when the bucketCh channel is drained. 61 buckets := []*Bucket{} 62 for b := range bucketCh { 63 buckets = append(buckets, b) 64 } 65 66 sortBuckets(buckets) 67 for _, b := range buckets { 68 if b.Error != nil { 69 fmt.Printf("Bucket %s, owned by: %s, failed: %v\n", b.Name, b.Owner, b.Error) 70 continue 71 } 72 73 encObjs := b.encryptedObjects() 74 fmt.Printf("Bucket: %s, owned by: %s, total objects: %d, failed objects: %d, encrypted objects: %d\n", 75 b.Name, b.Owner, len(b.Objects), len(b.ErrObjects), len(encObjs)) 76 if len(encObjs) > 0 { 77 for _, encObj := range encObjs { 78 fmt.Printf("\t%s %s:%s/%s\n", encObj.EncryptionType, b.Region, b.Name, encObj.Key) 79 } 80 } 81 } 82 } 83 84 func sortBuckets(buckets []*Bucket) { 85 s := sortalbeBuckets(buckets) 86 sort.Sort(s) 87 } 88 89 type sortalbeBuckets []*Bucket 90 91 func (s sortalbeBuckets) Len() int { return len(s) } 92 func (s sortalbeBuckets) Swap(a, b int) { s[a], s[b] = s[b], s[a] } 93 func (s sortalbeBuckets) Less(a, b int) bool { 94 if s[a].Owner == s[b].Owner && s[a].Name < s[b].Name { 95 return true 96 } 97 98 if s[a].Owner < s[b].Owner { 99 return true 100 } 101 102 return false 103 } 104 105 func getAccountBuckets(sess *session.Session, bucketCh chan<- *Bucket, owner string) error { 106 svc := s3.New(sess) 107 buckets, err := listBuckets(svc) 108 if err != nil { 109 return fmt.Errorf("failed to list buckets, %v", err) 110 } 111 for _, bucket := range buckets { 112 bucket.Owner = owner 113 if bucket.Error != nil { 114 continue 115 } 116 117 bckSvc := s3.New(sess, &aws.Config{ 118 Region: aws.String(bucket.Region), 119 Credentials: svc.Config.Credentials, 120 }) 121 bucketDetails(bckSvc, bucket) 122 bucketCh <- bucket 123 } 124 125 return nil 126 } 127 128 func bucketDetails(svc *s3.S3, bucket *Bucket) { 129 objs, errObjs, err := listBucketObjects(svc, bucket.Name) 130 if err != nil { 131 bucket.Error = err 132 } else { 133 bucket.Objects = objs 134 bucket.ErrObjects = errObjs 135 } 136 } 137 138 // A Object provides details of an S3 object 139 type Object struct { 140 Bucket string 141 Key string 142 Encrypted bool 143 EncryptionType string 144 } 145 146 // An ErrObject provides details of the error occurred retrieving 147 // an object's status. 148 type ErrObject struct { 149 Bucket string 150 Key string 151 Error error 152 } 153 154 // A Bucket provides details about a bucket and its objects 155 type Bucket struct { 156 Owner string 157 Name string 158 CreationDate time.Time 159 Region string 160 Objects []Object 161 Error error 162 ErrObjects []ErrObject 163 } 164 165 func (b *Bucket) encryptedObjects() []Object { 166 encObjs := []Object{} 167 for _, obj := range b.Objects { 168 if obj.Encrypted { 169 encObjs = append(encObjs, obj) 170 } 171 } 172 return encObjs 173 } 174 175 func listBuckets(svc *s3.S3) ([]*Bucket, error) { 176 res, err := svc.ListBuckets(&s3.ListBucketsInput{}) 177 if err != nil { 178 return nil, err 179 } 180 181 buckets := make([]*Bucket, len(res.Buckets)) 182 for i, b := range res.Buckets { 183 buckets[i] = &Bucket{ 184 Name: *b.Name, 185 CreationDate: *b.CreationDate, 186 } 187 188 locRes, err := svc.GetBucketLocation(&s3.GetBucketLocationInput{ 189 Bucket: b.Name, 190 }) 191 if err != nil { 192 buckets[i].Error = err 193 continue 194 } 195 196 if locRes.LocationConstraint == nil { 197 buckets[i].Region = "us-east-1" 198 } else { 199 buckets[i].Region = *locRes.LocationConstraint 200 } 201 } 202 203 return buckets, nil 204 } 205 206 func listBucketObjects(svc *s3.S3, bucket string) ([]Object, []ErrObject, error) { 207 listRes, err := svc.ListObjects(&s3.ListObjectsInput{ 208 Bucket: &bucket, 209 }) 210 if err != nil { 211 return nil, nil, err 212 } 213 214 objs := make([]Object, 0, len(listRes.Contents)) 215 errObjs := []ErrObject{} 216 for _, listObj := range listRes.Contents { 217 objData, err := svc.HeadObject(&s3.HeadObjectInput{ 218 Bucket: &bucket, 219 Key: listObj.Key, 220 }) 221 222 if err != nil { 223 errObjs = append(errObjs, ErrObject{Bucket: bucket, Key: *listObj.Key, Error: err}) 224 continue 225 } 226 227 obj := Object{Bucket: bucket, Key: *listObj.Key} 228 if objData.ServerSideEncryption != nil { 229 obj.Encrypted = true 230 obj.EncryptionType = *objData.ServerSideEncryption 231 } 232 233 objs = append(objs, obj) 234 } 235 236 return objs, errObjs, nil 237 }