├── AUTHORS ├── CONTRIBUTORS ├── README.md ├── cmd ├── upspinserver-aws │ └── main.go └── upspin-setupstorage-aws │ └── setupstorage.go ├── CONTRIBUTING.md ├── CONDUCT.md ├── cloud └── storage │ └── s3 │ ├── s3_test.go │ └── s3.go └── LICENSE /AUTHORS: -------------------------------------------------------------------------------- 1 | # This source code refers to The Upspin Authors for copyright purposes. 2 | # The master list of authors is in the main Upspin distribution, 3 | # visible at https://github.com/upspin/upspin/blob/master/CONTRIBUTORS 4 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This source code was written by the Upspin contributors. 2 | # The master list of contributors is in the main Upspin distribution, 3 | # visible at https://github.com/upspin/upspin/blob/master/CONTRIBUTORS 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Upspin `aws` repository 2 | 3 | Note: This repository is under construction. 4 | 5 | This repository contains support for running Upspin on 6 | [Amazon Web Services](https://aws.amazon.com). 7 | 8 | See the [Setting up upspinserver](https://upspin.io/doc/server_setup.md) 9 | document for information about running an Upspin service that stores its data 10 | in Amazon S3. 11 | 12 | See the [master repository](https://github.com/upspin/upspin#readme) 13 | for more information about the Upspin project. 14 | 15 | -------------------------------------------------------------------------------- /cmd/upspinserver-aws/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Upspin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Command upspinserver-aws is a combined DirServer and StoreServer for use on 6 | // stand-alone machines. It provides the production implementations of the 7 | // dir and store servers (dir/server and store/server) with support for storage 8 | // in S3. 9 | package main // import "aws.upspin.io/cmd/upspinserver-aws" 10 | 11 | import ( 12 | "upspin.io/cloud/https" 13 | "upspin.io/serverutil/upspinserver" 14 | 15 | // Storage on S3. 16 | _ "aws.upspin.io/cloud/storage/s3" 17 | ) 18 | 19 | func main() { 20 | ready := upspinserver.Main() 21 | https.ListenAndServe(ready, https.OptionsFromFlags()) 22 | } 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Upspin 2 | 3 | Upspin is an open source project. 4 | 5 | It is the work of many contributors. We appreciate your help! 6 | 7 | 8 | ## Filing issues 9 | 10 | When filing an issue, make sure to answer these five questions: 11 | 12 | 1. What version of Upspin are you using? 13 | 2. What operating system and processor architecture are you using? 14 | 3. What did you do? 15 | 4. What did you expect to see? 16 | 5. What did you see instead? 17 | 18 | Sensitive security-related issues should be reported to the private 19 | [upspin-security@googlegroups.com](mailto:upspin-security@googlegroups.com) 20 | mailing list. 21 | 22 | 23 | ## Contributing code 24 | 25 | We use GitHub pull requests for code review. 26 | 27 | Unless otherwise noted, the Upspin source files are distributed under the 28 | Apache 2.0 license found in the LICENSE file. 29 | 30 | 31 | ## Code of Conduct 32 | 33 | Please note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). 34 | By participating in this project you agree to abide by its terms. 35 | 36 | -------------------------------------------------------------------------------- /CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [upspin@googlegroups.com](mailto:upspin@googlegroups.com). 59 | All complaints will be reviewed and investigated and will result in a response 60 | that is deemed necessary and appropriate to the circumstances. The project team 61 | is obligated to maintain confidentiality with regard to the reporter of an 62 | incident. Further details of specific enforcement policies may be posted 63 | separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 72 | available at [http://contributor-covenant.org/version/1/4][version] 73 | 74 | [homepage]: http://contributor-covenant.org 75 | [version]: http://contributor-covenant.org/version/1/4/ 76 | -------------------------------------------------------------------------------- /cloud/storage/s3/s3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Upspin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package s3 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | "testing" 12 | "time" 13 | 14 | "github.com/aws/aws-sdk-go/aws" 15 | "github.com/aws/aws-sdk-go/service/s3" 16 | 17 | "upspin.io/cloud/storage" 18 | "upspin.io/log" 19 | ) 20 | 21 | const ( 22 | defaultTestBucketName = "upspin-test-scratch" 23 | defaultTestRegion = "us-east-1" 24 | ) 25 | 26 | var ( 27 | client storage.Storage 28 | testDataStr = fmt.Sprintf("This is test at %v", time.Now()) 29 | testData = []byte(testDataStr) 30 | fileName = fmt.Sprintf("test-file-%d", time.Now().Second()) 31 | 32 | testBucket = flag.String("test_bucket", defaultTestBucketName, "bucket name to use for testing") 33 | testRegion = flag.String("test_region", defaultTestRegion, "region to use for the test bucket") 34 | useAWS = flag.Bool("use_aws", false, "enable to run aws tests; requires aws credentials") 35 | ) 36 | 37 | // This is more of a regression test as it uses the running cloud 38 | // storage in prod. However, since S3 is always available, we accept 39 | // relying on it. 40 | func TestPutAndDownload(t *testing.T) { 41 | err := client.Put(fileName, testData) 42 | if err != nil { 43 | t.Fatalf("Can't put: %v", err) 44 | } 45 | data, err := client.Download(fileName) 46 | if err != nil { 47 | t.Fatalf("Can't Download: %v", err) 48 | } 49 | if string(data) != testDataStr { 50 | t.Errorf("Expected %q got %q", testDataStr, string(data)) 51 | } 52 | } 53 | 54 | func TestDelete(t *testing.T) { 55 | err := client.Put(fileName, testData) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | err = client.Delete(fileName) 60 | if err != nil { 61 | t.Fatalf("Expected no errors, got %v", err) 62 | } 63 | // Test the side effect after Delete. 64 | _, err = client.Download(fileName) 65 | if err == nil { 66 | t.Fatal("Expected an error, but got none") 67 | } 68 | } 69 | 70 | func TestMain(m *testing.M) { 71 | flag.Parse() 72 | if !*useAWS { 73 | log.Printf(` 74 | 75 | cloud/storage/amazons3: skipping test as it requires S3 access. To enable this 76 | test, ensure you are properly authorized to upload to an S3 bucket named by flag 77 | -test_bucket and then set this test's flag -use_aws. 78 | 79 | `) 80 | os.Exit(0) 81 | } 82 | 83 | // Create client that writes to test bucket. 84 | var err error 85 | client, err = storage.Dial("S3", 86 | storage.WithKeyValue("s3Region", *testRegion), 87 | storage.WithKeyValue("s3BucketName", *testBucket), 88 | storage.WithKeyValue("defaultACL", ACLPublicRead)) 89 | if err != nil { 90 | log.Fatalf("cloud/storage/amazons3: couldn't set up client: %v", err) 91 | } 92 | if err := client.(*s3Impl).createBucket(); err != nil { 93 | log.Printf("cloud/storage/amazons3: createBucket failed: %v", err) 94 | } 95 | 96 | code := m.Run() 97 | 98 | // Clean up. 99 | if err := client.(*s3Impl).deleteBucket(); err != nil { 100 | log.Printf("cloud/storage/amazons3: deleteBucket failed: %v", err) 101 | } 102 | 103 | os.Exit(code) 104 | } 105 | 106 | func (s *s3Impl) createBucket() error { 107 | _, err := s.service.CreateBucket(&s3.CreateBucketInput{Bucket: aws.String(s.bucketName)}) 108 | return err 109 | } 110 | 111 | func (s *s3Impl) deleteBucket() error { 112 | _, err := s.service.DeleteBucket(&s3.DeleteBucketInput{Bucket: aws.String(s.bucketName)}) 113 | return err 114 | } 115 | -------------------------------------------------------------------------------- /cloud/storage/s3/s3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Upspin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package s3 implements a storage backend that saves data to Amazon 6 | // Simple Storage Service. 7 | package s3 // import "aws.upspin.io/cloud/storage/s3" 8 | 9 | import ( 10 | "bytes" 11 | 12 | "github.com/aws/aws-sdk-go/aws" 13 | "github.com/aws/aws-sdk-go/aws/awserr" 14 | "github.com/aws/aws-sdk-go/aws/session" 15 | "github.com/aws/aws-sdk-go/service/s3" 16 | "github.com/aws/aws-sdk-go/service/s3/s3manager" 17 | 18 | "upspin.io/cloud/storage" 19 | "upspin.io/errors" 20 | ) 21 | 22 | // These constants define ACLs for writing data to Amazon Simple Storage 23 | // Service. Definitions according to 24 | // http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl 25 | const ( 26 | // ACLPublicRead means owner gets FULL_CONTROL. 27 | // The AllUsers group gets READ access. 28 | ACLPublicRead = "public-read" 29 | // ACLPrivate means owner gets FULL_CONTROL. 30 | // No one else has access rights. 31 | ACLPrivate = "private" 32 | ) 33 | 34 | // Keys used for storing dial options. 35 | const ( 36 | regionName = "s3Region" 37 | bucketName = "s3BucketName" 38 | defaultACL = "defaultACL" 39 | endpointURL = "endpoint" 40 | ) 41 | 42 | // s3Impl is an implementation of Storage that connects to an Amazon Simple 43 | // Storage (S3) backend. 44 | type s3Impl struct { 45 | service *s3.S3 46 | bucketName string 47 | defaultWriteACL string 48 | } 49 | 50 | // New initializes a Storage implementation that stores data to Amazon Simple 51 | // Storage Service. 52 | func New(opts *storage.Opts) (storage.Storage, error) { 53 | const op errors.Op = "cloud/storage/amazons3.New" 54 | 55 | region, ok := opts.Opts[regionName] 56 | if !ok { 57 | return nil, errors.E(op, errors.Invalid, errors.Errorf("%q option is required", regionName)) 58 | } 59 | config := aws.Config{ 60 | Region: aws.String(region), 61 | } 62 | if endpoint, ok := opts.Opts[endpointURL]; ok { 63 | config.Endpoint = aws.String(endpoint) 64 | } 65 | bucket, ok := opts.Opts[bucketName] 66 | if !ok { 67 | return nil, errors.E(op, errors.Invalid, errors.Errorf("%q option is required", bucketName)) 68 | } 69 | acl, ok := opts.Opts[defaultACL] 70 | if !ok { 71 | return nil, errors.E(op, errors.Invalid, errors.Errorf("%q option is required", defaultACL)) 72 | } 73 | if acl != ACLPrivate && acl != ACLPublicRead { 74 | return nil, errors.E(op, errors.Invalid, 75 | errors.Errorf("valid ACL values for S3 are %s and %s", ACLPrivate, ACLPublicRead)) 76 | } 77 | 78 | sess, err := session.NewSessionWithOptions(session.Options{ 79 | Config: config, 80 | SharedConfigState: session.SharedConfigEnable, 81 | }) 82 | if err != nil { 83 | return nil, errors.E(op, errors.IO, errors.Errorf("unable to create Amazon session: %s", err)) 84 | } 85 | 86 | return &s3Impl{ 87 | service: s3.New(sess), 88 | bucketName: bucket, 89 | defaultWriteACL: acl, 90 | }, nil 91 | } 92 | 93 | func init() { 94 | storage.Register("S3", New) 95 | } 96 | 97 | // Guarantee we implement the Storage interface. 98 | var _ storage.Storage = (*s3Impl)(nil) 99 | 100 | // LinkBase implements Storage. 101 | func (s *s3Impl) LinkBase() (base string, err error) { 102 | return s.service.Endpoint + "/" + s.bucketName + "/", nil 103 | } 104 | 105 | // Download implements Storage. 106 | func (s *s3Impl) Download(ref string) ([]byte, error) { 107 | const op errors.Op = "cloud/storage/amazons3.Download" 108 | 109 | buf := aws.NewWriteAtBuffer([]byte{}) 110 | d := s3manager.NewDownloaderWithClient(s.service) 111 | _, err := d.Download(buf, &s3.GetObjectInput{ 112 | Bucket: aws.String(s.bucketName), 113 | Key: aws.String(ref), 114 | }) 115 | if err != nil { 116 | if awsErr, ok := err.(awserr.RequestFailure); ok && awsErr.StatusCode() == 404 { 117 | return nil, errors.E(op, errors.NotExist, err) 118 | } 119 | return nil, errors.E(op, errors.IO, errors.Errorf( 120 | "unable to download ref %q from bucket %q: %s", ref, s.bucketName, err)) 121 | } 122 | return buf.Bytes(), nil 123 | } 124 | 125 | // Put implements Storage. 126 | func (s *s3Impl) Put(ref string, contents []byte) error { 127 | const op errors.Op = "cloud/storage/amazons3.Put" 128 | 129 | ul := s3manager.NewUploaderWithClient(s.service) 130 | _, err := ul.Upload(&s3manager.UploadInput{ 131 | ACL: aws.String(s.defaultWriteACL), 132 | Bucket: aws.String(s.bucketName), 133 | Key: aws.String(ref), 134 | Body: bytes.NewBuffer(contents), 135 | }) 136 | if err != nil { 137 | return errors.E(op, errors.IO, errors.Errorf( 138 | "unable to upload ref %q to bucket %q: %s", ref, s.bucketName, err)) 139 | } 140 | return nil 141 | } 142 | 143 | // Delete implements Storage. 144 | func (s *s3Impl) Delete(ref string) error { 145 | const op errors.Op = "cloud/storage/amazons3.Delete" 146 | 147 | _, err := s.service.DeleteObject(&s3.DeleteObjectInput{ 148 | Bucket: aws.String(s.bucketName), 149 | Key: aws.String(ref), 150 | }) 151 | if err != nil { 152 | return errors.E(op, errors.IO, errors.Errorf( 153 | "unable to delete ref %q from bucket %q: %s", ref, s.bucketName, err)) 154 | } 155 | return nil 156 | } 157 | 158 | // Close implements Storage. 159 | func (s *s3Impl) Close() { 160 | // Not much to do, the S3 service doesn’t require any cleanup. 161 | s.service = nil 162 | s.bucketName = "" 163 | } 164 | -------------------------------------------------------------------------------- /cmd/upspin-setupstorage-aws/setupstorage.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Upspin Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The upspin-setupstorage-aws command is an external upspin subcommand that 6 | // executes the second step in establishing an upspinserver for AWS. 7 | // Run upspin setupstorage-aws -help for more information. 8 | package main // import "aws.upspin.io/cmd/upspin-setupstorage-aws" 9 | 10 | import ( 11 | "flag" 12 | "fmt" 13 | "log" 14 | "os" 15 | "path/filepath" 16 | 17 | "github.com/aws/aws-sdk-go/aws" 18 | "github.com/aws/aws-sdk-go/aws/awserr" 19 | "github.com/aws/aws-sdk-go/aws/session" 20 | "github.com/aws/aws-sdk-go/service/iam" 21 | "github.com/aws/aws-sdk-go/service/s3" 22 | 23 | "upspin.io/subcmd" 24 | ) 25 | 26 | type state struct { 27 | *subcmd.State 28 | sess *session.Session 29 | } 30 | 31 | const help = ` 32 | Setupstorage-aws is the second step in establishing an upspinserver. 33 | It sets up AWS storage for your Upspin installation. You may skip this step 34 | if you wish to store Upspin data on your server's local disk. 35 | The first step is 'setupdomain' and the final step is 'setupserver'. 36 | 37 | Setupstorage-aws creates an Amazon S3 bucket and an IAM role for accessing that 38 | bucket. It then updates the server configuration files in $where/$domain/ to use 39 | the specified bucket and region. 40 | 41 | Before running this command, you should ensure you have an AWS account and that 42 | the aws CLI command line tool works for you. For more information, visit 43 | http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-set-up.html 44 | 45 | If something goes wrong during the setup process, you can run the same command 46 | with the -clean flag. It will attempt to remove any entities previously created 47 | with the same options provided. 48 | ` 49 | 50 | func main() { 51 | const name = "setupstorage-aws" 52 | 53 | log.SetFlags(0) 54 | log.SetPrefix("upspin setupstorage-aws: ") 55 | 56 | s := &state{ 57 | State: subcmd.NewState(name), 58 | } 59 | var err error 60 | s.sess, err = session.NewSessionWithOptions(session.Options{ 61 | SharedConfigState: session.SharedConfigEnable, 62 | }) 63 | if err != nil { 64 | s.Exitf("unable to create session: %s", err) 65 | } 66 | 67 | var ( 68 | where = flag.String("where", filepath.Join(os.Getenv("HOME"), "upspin", "deploy"), "`directory` to store private configuration files") 69 | domain = flag.String("domain", "", "domain `name` for this Upspin installation") 70 | region = flag.String("region", "us-east-1", "region for the S3 bucket") 71 | roleName = flag.String("role_name", "upspinstorage", "name for the IAM Role used to access the S3 bucket") 72 | clean = flag.Bool("clean", false, "deletes all artifacts that would be created using this command") 73 | ) 74 | 75 | s.ParseFlags(flag.CommandLine, os.Args[1:], help, 76 | "setupstorage-aws -domain= [-region=] [-clean] ") 77 | if flag.NArg() != 1 { 78 | s.Exitf("a single bucket name must be provided") 79 | } 80 | if len(*domain) == 0 { 81 | s.Exitf("the -domain flag must be provided") 82 | } 83 | 84 | bucketName := flag.Arg(0) 85 | if *clean { 86 | s.clean(*roleName, bucketName, *region) 87 | s.ExitNow() 88 | } 89 | 90 | cfgPath := filepath.Join(*where, *domain) 91 | cfg := s.ReadServerConfig(cfgPath) 92 | 93 | role, err := s.createRoleAccount(*roleName) 94 | if err != nil { 95 | s.Exitf("unable to create role account: %s", err) 96 | } 97 | 98 | if err := s.createInstanceProfile(role); err != nil { 99 | s.Exitf("unable to create instance profile: %s", err) 100 | } 101 | 102 | if err := s.createBucket(role, *region, bucketName); err != nil { 103 | s.Exitf("unable to create S3 bucket: %s", err) 104 | } 105 | 106 | if err := s.attachRolePolicy(role, bucketName); err != nil { 107 | s.Exitf("unable to attach role policy: %s", err) 108 | } 109 | 110 | cfg.StoreConfig = []string{ 111 | "backend=S3", 112 | "defaultACL=public-read", 113 | "s3BucketName=" + bucketName, 114 | "s3Region=" + *region, 115 | } 116 | s.WriteServerConfig(cfgPath, cfg) 117 | 118 | fmt.Fprintf(os.Stderr, "You should now deploy the upspinserver binary and run 'upspin setupserver'.\n") 119 | s.ExitNow() 120 | } 121 | 122 | func (s *state) createRoleAccount(name string) (*iam.Role, error) { 123 | svc := iam.New(s.sess) 124 | rolePolicyDocument := aws.String(`{ 125 | "Version": "2012-10-17", 126 | "Statement": [ 127 | { 128 | "Action": "sts:AssumeRole", 129 | "Effect": "Allow", 130 | "Principal": { 131 | "Service": "ec2.amazonaws.com" 132 | } 133 | } 134 | ] 135 | }`) 136 | output, err := svc.CreateRole(&iam.CreateRoleInput{ 137 | RoleName: aws.String(name), 138 | Description: aws.String("Used for storing data from the Upspin service"), 139 | 140 | AssumeRolePolicyDocument: rolePolicyDocument, 141 | }) 142 | if err != nil { 143 | return nil, err 144 | } 145 | return output.Role, nil 146 | } 147 | 148 | func (s *state) createInstanceProfile(role *iam.Role) error { 149 | svc := iam.New(s.sess) 150 | if _, err := svc.CreateInstanceProfile(&iam.CreateInstanceProfileInput{ 151 | InstanceProfileName: role.RoleName, 152 | }); err != nil { 153 | return err 154 | } 155 | 156 | _, err := svc.AddRoleToInstanceProfile(&iam.AddRoleToInstanceProfileInput{ 157 | InstanceProfileName: role.RoleName, 158 | RoleName: role.RoleName, 159 | }) 160 | return err 161 | } 162 | 163 | func (s *state) attachRolePolicy(role *iam.Role, bucketName string) error { 164 | svc := iam.New(s.sess) 165 | _, err := svc.AttachRolePolicy(&iam.AttachRolePolicyInput{ 166 | PolicyArn: aws.String("arn:aws:iam::aws:policy/AmazonS3FullAccess"), 167 | RoleName: role.RoleName, 168 | }) 169 | return err 170 | } 171 | 172 | func (s *state) createBucket(role *iam.Role, region, bucketName string) error { 173 | svc := s3.New(s.sess, &aws.Config{ 174 | Region: aws.String(region), 175 | }) 176 | 177 | _, err := svc.CreateBucket(&s3.CreateBucketInput{ 178 | Bucket: aws.String(bucketName), 179 | }) 180 | return err 181 | } 182 | 183 | // clean makes a best-effort attempt at cleaning up entities created by this 184 | // command. Errors are reported to the user only if it wasn’t due to the entity 185 | // not being found. 186 | func (s *state) clean(roleName, bucketName, region string) { 187 | log.Println("Cleaning up...") 188 | 189 | s3svc := s3.New(s.sess, &aws.Config{ 190 | Region: aws.String(region), 191 | }) 192 | if _, err := s3svc.DeleteBucket(&s3.DeleteBucketInput{ 193 | Bucket: aws.String(bucketName), 194 | }); err != nil { 195 | if err.(awserr.RequestFailure).StatusCode() != 404 { 196 | log.Printf("unable to delete bucket %s: %s", bucketName, err) 197 | } 198 | } 199 | 200 | iamSvc := iam.New(s.sess) 201 | if _, err := iamSvc.RemoveRoleFromInstanceProfile(&iam.RemoveRoleFromInstanceProfileInput{ 202 | InstanceProfileName: aws.String(roleName), 203 | RoleName: aws.String(roleName), 204 | }); err != nil { 205 | if err.(awserr.RequestFailure).StatusCode() != 404 { 206 | log.Printf("unable to remove role from instance profile %s: %s", roleName, err) 207 | } 208 | } 209 | if _, err := iamSvc.DeleteInstanceProfile(&iam.DeleteInstanceProfileInput{ 210 | InstanceProfileName: aws.String(roleName), 211 | }); err != nil { 212 | if err.(awserr.RequestFailure).StatusCode() != 404 { 213 | log.Printf("unable to delete instance profile %s: %s", roleName, err) 214 | } 215 | } 216 | 217 | if _, err := iamSvc.DeleteRolePolicy(&iam.DeleteRolePolicyInput{ 218 | PolicyName: aws.String("upspin-access-policy"), 219 | RoleName: aws.String(roleName), 220 | }); err != nil { 221 | if err.(awserr.RequestFailure).StatusCode() != 404 { 222 | log.Printf("unable to delete role %s: %s", roleName, err) 223 | } 224 | } 225 | 226 | if _, err := iamSvc.DeleteRole(&iam.DeleteRoleInput{ 227 | RoleName: aws.String(roleName), 228 | }); err != nil { 229 | if err.(awserr.RequestFailure).StatusCode() != 404 { 230 | log.Printf("unable to delete role %s: %s", roleName, err) 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. --------------------------------------------------------------------------------