From faa3e136d3919a233d3df3327c882450f9f89339 Mon Sep 17 00:00:00 2001 From: Roberto Dip Date: Fri, 15 Jul 2022 12:20:24 -0300 Subject: [PATCH] improve installerstore tool w/ better errors and bucket creation (#6685) This improves the installerstore CLI tool with: - The ability to create tests buckets for local development (otherwise you have to interact with another CLI or the MinIO UI) - Improved error handling and messaging. --- server/datastore/s3/s3.go | 20 ++++++++++++++++ server/datastore/s3/testing_utils.go | 6 ++--- tools/installerstore/README.md | 6 +++++ tools/installerstore/main.go | 35 ++++++++++++++++++++++------ 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/server/datastore/s3/s3.go b/server/datastore/s3/s3.go index bfa8e7c7b2..5f709e29cf 100644 --- a/server/datastore/s3/s3.go +++ b/server/datastore/s3/s3.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/session" @@ -71,3 +72,22 @@ func newS3store(config config.S3Config) (*s3store, error) { prefix: config.Prefix, }, nil } + +// CreateTestBucket creates a bucket with the provided name and a default +// bucket config. Only recommended for local testing. +func (s *s3store) CreateTestBucket(name string) error { + _, err := s.s3client.CreateBucket(&s3.CreateBucketInput{ + Bucket: &name, + CreateBucketConfiguration: &s3.CreateBucketConfiguration{}, + }) + + // Don't error if the bucket already exists + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case s3.ErrCodeBucketAlreadyExists, s3.ErrCodeBucketAlreadyOwnedByYou: + return nil + } + } + + return err +} diff --git a/server/datastore/s3/testing_utils.go b/server/datastore/s3/testing_utils.go index 2eaf43e566..a8d0c9bae6 100644 --- a/server/datastore/s3/testing_utils.go +++ b/server/datastore/s3/testing_utils.go @@ -35,10 +35,8 @@ func setupInstallerStore(tb testing.TB, bucket, prefix string) *InstallerStore { }) require.Nil(tb, err) - store.s3client.CreateBucket(&s3.CreateBucketInput{ - Bucket: &bucket, - CreateBucketConfiguration: &s3.CreateBucketConfiguration{}, - }) + err = store.CreateTestBucket(bucket) + require.NoError(tb, err) tb.Cleanup(func() { cleanupStore(tb, store) }) diff --git a/tools/installerstore/README.md b/tools/installerstore/README.md index 772e9ba380..d89f00610c 100644 --- a/tools/installerstore/README.md +++ b/tools/installerstore/README.md @@ -25,6 +25,7 @@ GLOBAL OPTIONS: --sts-assume-role-arn value ARN of role to assume for AWS [$INSTALLER_STS_ASSUME_ROLE_ARN] --disable-ssl Disable SSL (typically for local testing) (default: false) [$INSTALLER_DISABLE_SSL] --force-s3-path-style http://s3.amazonaws.com/BUCKET/KEY Set this to true to force path-style addressing, i.e., http://s3.amazonaws.com/BUCKET/KEY (default: false) [$INSTALLER_FORCE_S3_PATH_STYLE] + --create-bucket Set this to true to create the bucket if it doesn't exist. Only recommended for local testing. (default: false) [$INSTALLER_CREATE_BUCKET] --help, -h show help (default: false) ``` @@ -43,5 +44,10 @@ go run tools/installerstore/main.go \ --secret-access-key=minio123! \ --disable-ssl=true \ --force-s3-path-style=true \ + --create-bucket=true \ fleet-osquery.pkg ``` + +Tip: MinIO provides an UI you can use to explore your local buckets. If you're +running the Fleet server in development, it shoud be available at +http://localhost:9001. diff --git a/tools/installerstore/main.go b/tools/installerstore/main.go index cfcf55505c..a0ed21c505 100644 --- a/tools/installerstore/main.go +++ b/tools/installerstore/main.go @@ -17,11 +17,12 @@ func main() { app := cli.NewApp() app.Name = "installerstore" app.Usage = "Utility to upload pre-built installers to a file storage (AWS S3, MinIO, etc.)" - app.UsageText = "installerstore --enroll-secret xyz --bucket installers ~/path/to/file.pkg" + app.UsageText = "installerstore --enroll-secret=xyz --bucket=installers ~/path/to/file.pkg" + app.ExitErrHandler = exitErrHandler app.Flags = []cli.Flag{ &cli.StringFlag{ Name: "fleet-desktop", - Usage: "Wether or not the installer includes Fleet Desktop", + Usage: "Whether or not the installer includes Fleet Desktop", EnvVars: []string{"INSTALLER_FLEET_DESKTOP"}, }, &cli.StringFlag{ @@ -76,11 +77,17 @@ func main() { Usage: "Set this to true to force path-style addressing, i.e., `http://s3.amazonaws.com/BUCKET/KEY`", EnvVars: []string{"INSTALLER_FORCE_S3_PATH_STYLE"}, }, + &cli.BoolFlag{ + Name: "create-bucket", + Usage: "Set this to true to create the bucket if it doesn't exist. Only recommended for local testing.", + EnvVars: []string{"INSTALLER_CREATE_BUCKET"}, + }, } app.Action = func(c *cli.Context) error { + bucket := c.String("bucket") store, err := s3.NewInstallerStore(config.S3Config{ - Bucket: c.String("bucket"), + Bucket: bucket, Prefix: c.String("prefix"), Region: c.String("region"), EndpointURL: c.String("endpoint-url"), @@ -91,7 +98,13 @@ func main() { ForceS3PathStyle: c.Bool("force-s3-path-style"), }) if err != nil { - return fmt.Errorf("unable to setup store: %v", err) + return fmt.Errorf("unable to setup store: %w", err) + } + + if c.Bool("create-bucket") { + if err := store.CreateTestBucket(bucket); err != nil { + return fmt.Errorf("unable to create bucket: %w", err) + } } fp := c.Args().Get(0) @@ -101,7 +114,7 @@ func main() { r, err := os.Open(fp) if err != nil { - return fmt.Errorf("there was an error opening %s", fp) + return fmt.Errorf("there was an error opening %s: %w", fp, err) } key, err := store.Put(context.Background(), fleet.Installer{ @@ -111,12 +124,20 @@ func main() { Content: r, }) if err != nil { - return fmt.Errorf("there was a problem uploading the installer with key %s", key) + return fmt.Errorf("there was a problem uploading the installer with key %s: %w", key, err) } - fmt.Printf("installer uploaded with key %s\n", key) + fmt.Printf(`installer uploaded to bucket "%s" with key "%s"\n`, bucket, key) return nil } app.Run(os.Args) } + +func exitErrHandler(c *cli.Context, err error) { + if err == nil { + return + } + fmt.Fprintf(c.App.ErrWriter, "Error: %+v\n", err) + cli.OsExiter(1) +}