-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ISSUE-30 Migrate CLI from kingpin to cobra
- Loading branch information
Showing
30 changed files
with
927 additions
and
499 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ linters: | |
- gofmt | ||
- goheader | ||
- goimports | ||
- gomnd | ||
# - gomnd | ||
- gomodguard | ||
- goprintffuncname | ||
- gosec | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/hypnoglow/helm-s3/internal/awss3" | ||
"github.com/hypnoglow/helm-s3/internal/awsutil" | ||
"github.com/hypnoglow/helm-s3/internal/helmutil" | ||
) | ||
|
||
const deleteDesc = `This command removes a chart from the repository. | ||
'helm s3 init' takes two arguments: | ||
- NAME - name of the chart to delete, | ||
- REPO - target repository. | ||
` | ||
|
||
const deleteExample = ` helm s3 delete epicservice --version 0.5.1 my-repo - removes the chart with name 'epicservice' and version 0.5.1 from the repository with name 'my-repo'.` | ||
|
||
func newDeleteCommand(opts *options) *cobra.Command { | ||
act := &deleteAction{ | ||
printer: nil, | ||
acl: "", | ||
chartName: "", | ||
repoName: "", | ||
version: "", | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "delete NAME REPO", | ||
Aliases: []string{"del"}, | ||
Short: "Delete chart from the repository.", | ||
Long: deleteDesc, | ||
Example: deleteExample, | ||
Args: wrapPositionalArgsBadUsage(cobra.ExactArgs(2)), | ||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { | ||
// No completions for the NAME and REPO arguments. | ||
return nil, cobra.ShellCompDirectiveNoFileComp | ||
}, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
act.printer = cmd | ||
act.acl = opts.acl | ||
act.chartName = args[0] | ||
act.repoName = args[1] | ||
return act.run(cmd.Context()) | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
flags.StringVar(&act.version, "version", act.version, "Version of the chart to delete.") | ||
_ = cobra.MarkFlagRequired(flags, "version") | ||
|
||
return cmd | ||
} | ||
|
||
type deleteAction struct { | ||
printer printer | ||
|
||
// global flags | ||
|
||
acl string | ||
|
||
// args | ||
|
||
chartName string | ||
repoName string | ||
|
||
// flags | ||
|
||
version string | ||
} | ||
|
||
func (act *deleteAction) run(ctx context.Context) error { | ||
repoEntry, err := helmutil.LookupRepoEntry(act.repoName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
sess, err := awsutil.Session(awsutil.DynamicBucketRegion(repoEntry.URL())) | ||
if err != nil { | ||
return err | ||
} | ||
storage := awss3.New(sess) | ||
|
||
// Fetch current index. | ||
b, err := storage.FetchRaw(ctx, repoEntry.IndexURL()) | ||
if err != nil { | ||
return errors.WithMessage(err, "fetch current repo index") | ||
} | ||
|
||
idx := helmutil.NewIndex() | ||
if err := idx.UnmarshalBinary(b); err != nil { | ||
return errors.WithMessage(err, "load index from downloaded file") | ||
} | ||
|
||
// Update index. | ||
|
||
url, err := idx.Delete(act.chartName, act.version) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
idxReader, err := idx.Reader() | ||
if err != nil { | ||
return errors.Wrap(err, "get index reader") | ||
} | ||
|
||
// Delete the file from S3 and replace index file. | ||
|
||
if url != "" { | ||
// For relative URLs we need to prepend base URL. | ||
if !strings.HasPrefix(url, repoEntry.URL()) { | ||
url = strings.TrimSuffix(repoEntry.URL(), "/") + "/" + url | ||
} | ||
|
||
if err := storage.Delete(ctx, url); err != nil { | ||
return errors.WithMessage(err, "delete chart file from s3") | ||
} | ||
} | ||
|
||
if err := storage.PutIndex(ctx, repoEntry.URL(), act.acl, idxReader); err != nil { | ||
return errors.WithMessage(err, "upload new index to s3") | ||
} | ||
|
||
if err := idx.WriteFile(repoEntry.CacheFile(), helmutil.DefaultIndexFilePerm); err != nil { | ||
return errors.WithMessage(err, "update local index") | ||
} | ||
|
||
act.printer.Printf("Successfully deleted the chart from the repository.\n") | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/hypnoglow/helm-s3/internal/awss3" | ||
"github.com/hypnoglow/helm-s3/internal/awsutil" | ||
) | ||
|
||
const downloadDesc = `This command downloads a chart from AWS S3. | ||
Note that this command basically implements downloader plugin for Helm | ||
and not intended to be run explicitly. For more information, see: | ||
https://helm.sh/docs/topics/plugins/#downloader-plugins | ||
'helm s3 download' takes four arguments: | ||
- CERT - certificate file, | ||
- KEY - key file, | ||
- CA - certificate authority file, | ||
- URL - full url. | ||
` | ||
|
||
func newDownloadCommand() *cobra.Command { | ||
act := &downloadAction{ | ||
certFile: "", | ||
keyFile: "", | ||
caFile: "", | ||
url: "", | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "download CERT KEY CA URL", | ||
Short: "Download chart from AWS S3.", | ||
Long: downloadDesc, | ||
Example: "", | ||
Args: wrapPositionalArgsBadUsage(cobra.ExactArgs(4)), | ||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { | ||
// No completions for the arguments. | ||
return nil, cobra.ShellCompDirectiveNoFileComp | ||
}, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
act.printer = cmd | ||
act.certFile = args[0] | ||
act.keyFile = args[1] | ||
act.caFile = args[2] | ||
act.url = args[3] | ||
return act.run(cmd.Context()) | ||
}, | ||
Hidden: true, | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
type downloadAction struct { | ||
printer printer | ||
|
||
// args | ||
|
||
certFile string | ||
keyFile string | ||
caFile string | ||
url string | ||
} | ||
|
||
func (act *downloadAction) run(ctx context.Context) error { | ||
const indexYaml = "index.yaml" | ||
|
||
sess, err := awsutil.Session( | ||
awsutil.AssumeRoleTokenProvider(awsutil.StderrTokenProvider), | ||
awsutil.DynamicBucketRegion(act.url), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
storage := awss3.New(sess) | ||
|
||
b, err := storage.FetchRaw(ctx, act.url) | ||
if err != nil { | ||
if strings.HasSuffix(act.url, indexYaml) && err == awss3.ErrObjectNotFound { | ||
act.printer.PrintErrf( | ||
"The index file does not exist by the path %s. "+ | ||
"If you haven't initialized the repository yet, try running `helm s3 init %s`", | ||
act.url, | ||
strings.TrimSuffix(strings.TrimSuffix(act.url, indexYaml), "/"), | ||
) | ||
return newSilentError() | ||
} | ||
|
||
return errors.WithMessage(err, fmt.Sprintf("fetch from s3 url=%s", act.url)) | ||
} | ||
|
||
// Do not use printer, use os.Stdout directly, as required by Helm. | ||
fmt.Print(string(b)) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/hypnoglow/helm-s3/internal/awss3" | ||
"github.com/hypnoglow/helm-s3/internal/awsutil" | ||
"github.com/hypnoglow/helm-s3/internal/helmutil" | ||
) | ||
|
||
const initDesc = `This command initializes an empty repository on AWS S3. | ||
'helm s3 init' takes one argument: | ||
- URI - URI of the repository. | ||
` | ||
|
||
const initExample = ` helm s3 init s3://awesome-bucket/charts - inits chart repository in 'awesome-bucket' bucket under 'charts' path.` | ||
|
||
func newInitCommand(opts *options) *cobra.Command { | ||
act := &initAction{ | ||
printer: nil, | ||
acl: "", | ||
uri: "", | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "init URI", | ||
Short: "Initialize empty repository on AWS S3.", | ||
Long: initDesc, | ||
Example: initExample, | ||
Args: wrapPositionalArgsBadUsage(cobra.ExactArgs(1)), | ||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { | ||
// No completions for the URI argument. | ||
return nil, cobra.ShellCompDirectiveNoFileComp | ||
}, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
act.printer = cmd | ||
act.acl = opts.acl | ||
act.uri = args[0] | ||
return act.run(cmd.Context()) | ||
}, | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
type initAction struct { | ||
printer printer | ||
|
||
// global flags | ||
|
||
acl string | ||
|
||
// args | ||
|
||
uri string | ||
} | ||
|
||
func (act *initAction) run(ctx context.Context) error { | ||
r, err := helmutil.NewIndex().Reader() | ||
if err != nil { | ||
return errors.WithMessage(err, "get index reader") | ||
} | ||
|
||
sess, err := awsutil.Session(awsutil.DynamicBucketRegion(act.uri)) | ||
if err != nil { | ||
return err | ||
} | ||
storage := awss3.New(sess) | ||
|
||
if err := storage.PutIndex(ctx, act.uri, act.acl, r); err != nil { | ||
return errors.WithMessage(err, "upload index to s3") | ||
} | ||
|
||
// TODO: | ||
// do we need to automatically do `helm repo add <name> <uri>`, | ||
// like we are doing `helm repo update` when we push a chart | ||
// with this plugin? | ||
|
||
act.printer.Printf("Initialized empty repository at %s\n", act.uri) | ||
return nil | ||
} |
Oops, something went wrong.