From 8057cadc48c2ef55820c623d2fd884405c3b8bf2 Mon Sep 17 00:00:00 2001 From: Zoran Regvart Date: Mon, 9 Oct 2023 13:26:23 +0200 Subject: [PATCH] Build deterministic bundles by default Sets the default created time to 1970-01-01T00:00:00Z (Unix epoch) instead of the current time. Fixes #2135 --- docs/cmd/tkn_bundle_push.md | 2 +- docs/man/man1/tkn-bundle-push.1 | 2 +- pkg/cmd/bundle/push.go | 23 ++++++++++++----------- pkg/cmd/bundle/push_test.go | 24 ++++++++---------------- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/docs/cmd/tkn_bundle_push.md b/docs/cmd/tkn_bundle_push.md index 9b1b2c978..7654aa708 100644 --- a/docs/cmd/tkn_bundle_push.md +++ b/docs/cmd/tkn_bundle_push.md @@ -26,7 +26,7 @@ Input: Valid input in any form is valid Tekton YAML or JSON with a fully-specified "apiVersion" and "kind". To pass multiple objects in a single input, use "---" separators in YAML or a top-level "[]" in JSON. Created time: - Setting created time of the OCI Image Configuration layer can be done by either providing it via --ctime parameter or setting the SOURCE_DATE_EPOCH environment variable. + The default created time of the OCI Image Configuration layer is set to 1970-01-01T00:00:00Z. Changing it can be done by either providing it via --ctime parameter or setting the SOURCE_DATE_EPOCH environment variable. ### Options diff --git a/docs/man/man1/tkn-bundle-push.1 b/docs/man/man1/tkn-bundle-push.1 index 6079b087f..0a4a2eac4 100644 --- a/docs/man/man1/tkn-bundle-push.1 +++ b/docs/man/man1/tkn-bundle-push.1 @@ -41,7 +41,7 @@ Input: .PP Created time: - Setting created time of the OCI Image Configuration layer can be done by either providing it via \-\-ctime parameter or setting the SOURCE\_DATE\_EPOCH environment variable. + The default created time of the OCI Image Configuration layer is set to 1970\-01\-01T00:00:00Z. Changing it can be done by either providing it via \-\-ctime parameter or setting the SOURCE\_DATE\_EPOCH environment variable. .SH OPTIONS diff --git a/pkg/cmd/bundle/push.go b/pkg/cmd/bundle/push.go index f94e468a3..9f25fe966 100644 --- a/pkg/cmd/bundle/push.go +++ b/pkg/cmd/bundle/push.go @@ -14,7 +14,6 @@ package bundle import ( - "context" "errors" "fmt" "io" @@ -23,14 +22,16 @@ import ( "time" "github.com/google/go-containerregistry/pkg/name" - "github.com/jonboulle/clockwork" "github.com/spf13/cobra" "github.com/tektoncd/cli/pkg/bundle" "github.com/tektoncd/cli/pkg/cli" "github.com/tektoncd/cli/pkg/params" ) -const sourceDateEpochEnv = "SOURCE_DATE_EPOCH" +const ( + sourceDateEpochEnv = "SOURCE_DATE_EPOCH" + defaultTimestamp = 0 +) type pushOptions struct { cliparams cli.Params @@ -64,7 +65,7 @@ Input: Valid input in any form is valid Tekton YAML or JSON with a fully-specified "apiVersion" and "kind". To pass multiple objects in a single input, use "---" separators in YAML or a top-level "[]" in JSON. Created time: - Setting created time of the OCI Image Configuration layer can be done by either providing it via --ctime parameter or setting the SOURCE_DATE_EPOCH environment variable. + The default created time of the OCI Image Configuration layer is set to 1970-01-01T00:00:00Z. Changing it can be done by either providing it via --ctime parameter or setting the SOURCE_DATE_EPOCH environment variable. ` c := &cobra.Command{ @@ -94,7 +95,7 @@ Created time: Err: cmd.OutOrStderr(), } - return opts.Run(cmd.Context(), args) + return opts.Run(args) }, } c.Flags().StringSliceVarP(&opts.bundleContentPaths, "filenames", "f", []string{}, "List of fully-qualified file paths containing YAML or JSON defined Tekton objects to include in this bundle") @@ -107,7 +108,7 @@ Created time: // Reads the positional arguments and the `-f` flag to fill in the `bunldeContents` parameter with all of the raw Tekton // contents. -func (p *pushOptions) parseArgsAndFlags(ctx context.Context, args []string) (err error) { +func (p *pushOptions) parseArgsAndFlags(args []string) (err error) { p.ref, _ = name.ParseReference(args[0], name.StrictValidation, name.Insecure) // If there are file paths specified, then read them and include their contents. @@ -136,7 +137,7 @@ func (p *pushOptions) parseArgsAndFlags(ctx context.Context, args []string) (err return err } - if p.ctime, err = determineCTime(p.ctimeParam, clockwork.FromContext(ctx)); err != nil { + if p.ctime, err = determineCTime(p.ctimeParam); err != nil { return err } @@ -144,8 +145,8 @@ func (p *pushOptions) parseArgsAndFlags(ctx context.Context, args []string) (err } // Run performs the principal logic of reading and parsing the input, creating the bundle, and publishing it. -func (p *pushOptions) Run(ctx context.Context, args []string) error { - if err := p.parseArgsAndFlags(ctx, args); err != nil { +func (p *pushOptions) Run(args []string) error { + if err := p.parseArgsAndFlags(args); err != nil { return err } @@ -162,7 +163,7 @@ func (p *pushOptions) Run(ctx context.Context, args []string) error { return err } -func determineCTime(t string, clock clockwork.Clock) (parsed time.Time, err error) { +func determineCTime(t string) (parsed time.Time, err error) { // if given the parameter don't lookup the SOURCE_DATE_EPOCH env var if t == "" { if sourceDateEpoch, found := os.LookupEnv(sourceDateEpochEnv); found && sourceDateEpoch != "" { @@ -177,7 +178,7 @@ func determineCTime(t string, clock clockwork.Clock) (parsed time.Time, err erro } if t == "" { - return clock.Now(), nil + return time.Unix(defaultTimestamp, 0), nil } parsed, err = time.Parse(time.DateOnly, t) diff --git a/pkg/cmd/bundle/push_test.go b/pkg/cmd/bundle/push_test.go index 162065f62..92972dea8 100644 --- a/pkg/cmd/bundle/push_test.go +++ b/pkg/cmd/bundle/push_test.go @@ -3,7 +3,6 @@ package bundle import ( "archive/tar" "bytes" - "context" "fmt" "io" "net/http/httptest" @@ -17,7 +16,6 @@ import ( "github.com/google/go-containerregistry/pkg/registry" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/jonboulle/clockwork" "github.com/tektoncd/cli/pkg/bundle" "github.com/tektoncd/cli/pkg/cli" tkremote "github.com/tektoncd/pipeline/pkg/remote/oci" @@ -71,7 +69,7 @@ func TestPushCommand(t *testing.T) { "simple.yaml": exampleTask, }, expectedContents: map[string]expected{exampleTaskExpected.name: exampleTaskExpected}, - expectedCTime: fixedTime, + expectedCTime: time.Unix(defaultTimestamp, 0), }, { name: "stdin-input", @@ -80,7 +78,7 @@ func TestPushCommand(t *testing.T) { }, stdin: exampleTask, expectedContents: map[string]expected{exampleTaskExpected.name: exampleTaskExpected}, - expectedCTime: fixedTime, + expectedCTime: time.Unix(defaultTimestamp, 0), }, { name: "with-annotations", @@ -93,7 +91,7 @@ func TestPushCommand(t *testing.T) { "org.opencontainers.image.license": "Apache-2.0", "org.opencontainers.image.url": "https://example.org", }, - expectedCTime: fixedTime, + expectedCTime: time.Unix(defaultTimestamp, 0), }, { name: "with-ctime", @@ -153,10 +151,7 @@ func TestPushCommand(t *testing.T) { ctimeParam: tc.ctime, } - ctx := context.Background() - ctx = clockwork.AddToContext(ctx, clockwork.NewFakeClockAt(fixedTime)) - - if err := opts.Run(ctx, []string{ref}); err != nil { + if err := opts.Run([]string{ref}); err != nil { t.Errorf("Unexpected failure calling run: %v", err) } @@ -254,7 +249,7 @@ func TestParseTime(t *testing.T) { err string expected time.Time }{ - {name: "now", expected: fixedTime}, + {name: "now", expected: time.Unix(defaultTimestamp, 0)}, {name: "date", given: "2023-09-22", expected: time.Date(2023, 9, 22, 0, 0, 0, 0, time.UTC)}, {name: "date and time", given: "2023-09-22T01:02:03", expected: time.Date(2023, 9, 22, 1, 2, 3, 0, time.UTC)}, {name: "utc with fraction", given: "2023-09-22T01:02:03.45Z", expected: time.Date(2023, 9, 22, 1, 2, 3, 45, time.UTC)}, @@ -266,7 +261,7 @@ func TestParseTime(t *testing.T) { // remove SOURCE_DATE_EPOCH if set externally t.Setenv("SOURCE_DATE_EPOCH", "") - got, err := determineCTime(c.given, clockwork.NewFakeClockAt(fixedTime)) + got, err := determineCTime(c.given) if err != nil { if err.Error() != c.err { @@ -342,7 +337,7 @@ func TestParseArgsAndFlags(t *testing.T) { annotationsParams: []string{"a=b", "c=d"}, expectedRef: "registry.io/repository:tag", expectedAnnotations: map[string]string{"a": "b", "c": "d"}, - expectedCTime: fixedTime, + expectedCTime: time.Unix(defaultTimestamp, 0), }, { name: "ctime param", @@ -405,10 +400,7 @@ func TestParseArgsAndFlags(t *testing.T) { expectedContent = append(expectedContent, c) } - ctx := context.Background() - ctx = clockwork.AddToContext(ctx, clockwork.NewFakeClockAt(fixedTime)) - - if err := opts.parseArgsAndFlags(ctx, []string{c.refArg}); err != nil { + if err := opts.parseArgsAndFlags([]string{c.refArg}); err != nil { if err.Error() != c.err { t.Errorf("unexpected error, expecting %q, got: %q", c.err, err) }