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  }