diff --git a/cmd/helms3/push.go b/cmd/helms3/push.go index c150365a..510ad1f1 100644 --- a/cmd/helms3/push.go +++ b/cmd/helms3/push.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "time" "github.com/pkg/errors" "k8s.io/helm/pkg/chartutil" @@ -16,7 +17,15 @@ import ( "github.com/hypnoglow/helm-s3/pkg/index" ) +const ( + pushCommandDefaultTimeout = time.Second * 15 +) + func runPush(chartPath string, repoName string) error { + // Just one big timeout for the whole operation. + ctx, cancel := context.WithTimeout(context.Background(), pushCommandDefaultTimeout) + defer cancel() + fpath, err := filepath.Abs(chartPath) if err != nil { return errors.WithMessage(err, "get chart abs path") @@ -36,7 +45,8 @@ func runPush(chartPath string, repoName string) error { storage := awss3.NewStorage(awsConfig) - // Load chart and calculate required params like hash. + // Load chart, calculate required params like hash, + // and upload the chart right away. chart, err := chartutil.LoadFile(fname) if err != nil { @@ -48,15 +58,26 @@ func runPush(chartPath string, repoName string) error { return err } + fchart, err := os.Open(fname) + if err != nil { + return errors.Wrap(err, "open chart file") + } + + if _, err := storage.Upload(ctx, repoEntry.URL+"/"+fname, fchart); err != nil { + return errors.WithMessage(err, "upload chart to s3") + } + + // Next, update the repository index. + // The gap between index fetching and uploading should be as small as + // possible to make the best effort to avoid race conditions. + // See https://github.com/hypnoglow/helm-s3/issues/18 for more info. + hash, err := provenance.DigestFile(fname) if err != nil { return errors.WithMessage(err, "get chart digest") } - // Fetch current index. - - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() + // Fetch current index, update it and upload it back. b, err := storage.FetchRaw(ctx, repoEntry.URL+"/index.yaml") if err != nil { @@ -68,28 +89,14 @@ func runPush(chartPath string, repoName string) error { return errors.WithMessage(err, "load index from downloaded file") } - // Update index. - idx.Add(chart.GetMetadata(), fname, repoEntry.URL, hash) idx.SortEntries() - // Finally, upload both chart file and index. - - fchart, err := os.Open(fname) - if err != nil { - return errors.Wrap(err, "open chart file") - } idxReader, err := idx.Reader() if err != nil { return errors.WithMessage(err, "get index reader") } - ctx, cancel = context.WithTimeout(context.Background(), defaultTimeout*2) - defer cancel() - - if _, err := storage.Upload(ctx, repoEntry.URL+"/"+fname, fchart); err != nil { - return errors.WithMessage(err, "upload chart to s3") - } if _, err := storage.Upload(ctx, repoEntry.URL+"/index.yaml", idxReader); err != nil { return errors.WithMessage(err, "upload index to s3") }