diff --git a/pkg/cmd/pipeline/sign.go b/pkg/cmd/pipeline/sign.go index 2b233037c4..d007cebb4b 100644 --- a/pkg/cmd/pipeline/sign.go +++ b/pkg/cmd/pipeline/sign.go @@ -22,7 +22,9 @@ import ( "github.com/spf13/cobra" "github.com/tektoncd/cli/pkg/cli" "github.com/tektoncd/cli/pkg/trustedresources" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" cliopts "k8s.io/cli-runtime/pkg/genericclioptions" "sigs.k8s.io/yaml" ) @@ -31,6 +33,7 @@ type signOptions struct { keyfile string kmsKey string targetFile string + apiVersion string } func signCommand() *cobra.Command { @@ -70,7 +73,13 @@ or using kms return err } - crd := &v1beta1.Pipeline{} + var crd metav1.Object + if opts.apiVersion == "v1beta1" { + crd = &v1beta1.Pipeline{} + } else { + crd = &v1.Pipeline{} + } + if err := yaml.Unmarshal(b, &crd); err != nil { return fmt.Errorf("error unmarshalling Pipeline: %v", err) } @@ -87,7 +96,7 @@ or using kms c.Flags().StringVarP(&opts.keyfile, "key-file", "K", "", "Key file") c.Flags().StringVarP(&opts.kmsKey, "kms-key", "m", "", "KMS key url") c.Flags().StringVarP(&opts.targetFile, "file-name", "f", "", "Fle name of the signed pipeline, using the original file name will overwrite the file") - + c.Flags().StringVarP(&opts.apiVersion, "version", "v", "v1", "apiVersion of the Pipeline to be signed") return c } diff --git a/pkg/cmd/pipeline/sign_test.go b/pkg/cmd/pipeline/sign_test.go index da32355e7c..f4e8201945 100644 --- a/pkg/cmd/pipeline/sign_test.go +++ b/pkg/cmd/pipeline/sign_test.go @@ -16,6 +16,7 @@ package pipeline import ( "context" + "fmt" "os" "path/filepath" "testing" @@ -28,37 +29,52 @@ import ( func TestSign(t *testing.T) { ctx := context.Background() p := &test.Params{} - - task := Command(p) - + pipeline := Command(p) os.Setenv("PRIVATE_PASSWORD", "1234") - tmpDir := t.TempDir() - targetFile := filepath.Join(tmpDir, "signed.yaml") - out, err := test.ExecuteCommand(task, "sign", "testdata/pipeline.yaml", "-K", "testdata/cosign.key", "-f", targetFile) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - expected := "*Warning*: This is an experimental command, it's usage and behavior can change in the next release(s)\nPipeline testdata/pipeline.yaml is signed successfully \n" - test.AssertOutput(t, expected, out) - // verify the signed task - verifier, err := cosignsignature.LoadPublicKey(ctx, "testdata/cosign.pub") - if err != nil { - t.Errorf("error getting verifier from key file: %v", err) - } + testcases := []struct { + name string + taskFile string + apiVersion string + }{{ + name: "sign and verify v1beta1 Pipeline", + taskFile: "testdata/pipeline.yaml", + apiVersion: "v1beta1", + }, { + name: "sign and verify v1 Pipeline", + taskFile: "testdata/pipeline-v1.yaml", + apiVersion: "v1", + }} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + tmpDir := t.TempDir() + targetFile := filepath.Join(tmpDir, "signed.yaml") + out, err := test.ExecuteCommand(pipeline, "sign", tc.taskFile, "-K", "testdata/cosign.key", "-f", targetFile, "-v", tc.apiVersion) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + expected := fmt.Sprintf("*Warning*: This is an experimental command, it's usage and behavior can change in the next release(s)\nPipeline %s is signed successfully \n", tc.taskFile) + test.AssertOutput(t, expected, out) - signed, err := os.ReadFile(targetFile) - if err != nil { - t.Fatalf("error reading file: %v", err) - } + // verify the signed task + verifier, err := cosignsignature.LoadPublicKey(ctx, "testdata/cosign.pub") + if err != nil { + t.Errorf("error getting verifier from key file: %v", err) + } - target, signature, err := trustedresources.UnmarshalCRD(signed, "Pipeline") - if err != nil { - t.Fatalf("error unmarshalling crd: %v", err) - } + signed, err := os.ReadFile(targetFile) + if err != nil { + t.Fatalf("error reading file: %v", err) + } - if err := trustedresources.VerifyInterface(target, verifier, signature); err != nil { - t.Fatalf("VerifyInterface get error: %v", err) - } + target, signature, err := trustedresources.UnmarshalCRD(signed, "Pipeline", tc.apiVersion) + if err != nil { + t.Fatalf("error unmarshalling crd: %v", err) + } + if err := trustedresources.VerifyInterface(target, verifier, signature); err != nil { + t.Fatalf("VerifyInterface get error: %v", err) + } + }) + } } diff --git a/pkg/cmd/task/sign.go b/pkg/cmd/task/sign.go index 3c7ca0eb21..9881f30685 100644 --- a/pkg/cmd/task/sign.go +++ b/pkg/cmd/task/sign.go @@ -22,7 +22,9 @@ import ( "github.com/spf13/cobra" "github.com/tektoncd/cli/pkg/cli" "github.com/tektoncd/cli/pkg/trustedresources" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" cliopts "k8s.io/cli-runtime/pkg/genericclioptions" "sigs.k8s.io/yaml" ) @@ -36,6 +38,7 @@ type signOptions struct { keyfile string kmsKey string targetFile string + apiVersion string } func signCommand() *cobra.Command { @@ -74,8 +77,13 @@ or using kms log.Fatalf("error reading file: %v", err) return err } + var crd metav1.Object + if opts.apiVersion == "v1beta1" { + crd = &v1beta1.Task{} + } else { + crd = &v1.Task{} + } - crd := &v1beta1.Task{} if err := yaml.Unmarshal(b, &crd); err != nil { return fmt.Errorf("error unmarshalling Task: %v", err) } @@ -91,6 +99,6 @@ or using kms c.Flags().StringVarP(&opts.keyfile, "key-file", "K", "", "Key file") c.Flags().StringVarP(&opts.kmsKey, "kms-key", "m", "", "KMS key url") c.Flags().StringVarP(&opts.targetFile, "file-name", "f", "", "file name of the signed task, using the original file name will overwrite the file") - + c.Flags().StringVarP(&opts.apiVersion, "version", "v", "v1", "apiVersion of the Task to be signed") return c } diff --git a/pkg/cmd/task/sign_test.go b/pkg/cmd/task/sign_test.go index 351c9ad810..b1a87f549e 100644 --- a/pkg/cmd/task/sign_test.go +++ b/pkg/cmd/task/sign_test.go @@ -16,6 +16,7 @@ package task import ( "context" + "fmt" "os" "path/filepath" "testing" @@ -32,32 +33,50 @@ func TestSign(t *testing.T) { task := Command(p) os.Setenv("PRIVATE_PASSWORD", "1234") - tmpDir := t.TempDir() - targetFile := filepath.Join(tmpDir, "signed.yaml") - out, err := test.ExecuteCommand(task, "sign", "testdata/task.yaml", "-K", "testdata/cosign.key", "-f", targetFile) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - expected := "*Warning*: This is an experimental command, it's usage and behavior can change in the next release(s)\nTask testdata/task.yaml is signed successfully \n" - test.AssertOutput(t, expected, out) - // verify the signed task - verifier, err := cosignsignature.LoadPublicKey(ctx, "testdata/cosign.pub") - if err != nil { - t.Errorf("error getting verifier from key file: %v", err) - } + testcases := []struct { + name string + taskFile string + apiVersion string + }{{ + name: "sign and verify v1beta1 Task", + taskFile: "testdata/task.yaml", + apiVersion: "v1beta1", + }, { + name: "sign and verify v1 Task", + taskFile: "testdata/task-v1.yaml", + apiVersion: "v1", + }} - signed, err := os.ReadFile(targetFile) - if err != nil { - t.Fatalf("error reading file: %v", err) - } + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + tmpDir := t.TempDir() + targetFile := filepath.Join(tmpDir, "signed.yaml") + out, err := test.ExecuteCommand(task, "sign", tc.taskFile, "-K", "testdata/cosign.key", "-f", targetFile, "-v", tc.apiVersion) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + expected := fmt.Sprintf("*Warning*: This is an experimental command, it's usage and behavior can change in the next release(s)\nTask %s is signed successfully \n", tc.taskFile) + test.AssertOutput(t, expected, out) - target, signature, err := trustedresources.UnmarshalCRD(signed, "Task") - if err != nil { - t.Fatalf("error unmarshalling crd: %v", err) - } - if err := trustedresources.VerifyInterface(target, verifier, signature); err != nil { - t.Fatalf("VerifyTaskOCIBundle get error: %v", err) - } + // verify the signed task + verifier, err := cosignsignature.LoadPublicKey(ctx, "testdata/cosign.pub") + if err != nil { + t.Errorf("error getting verifier from key file: %v", err) + } + + signed, err := os.ReadFile(targetFile) + if err != nil { + t.Fatalf("error reading file: %v", err) + } + target, signature, err := trustedresources.UnmarshalCRD(signed, "Task", tc.apiVersion) + if err != nil { + t.Fatalf("error unmarshalling crd: %v", err) + } + if err := trustedresources.VerifyInterface(target, verifier, signature); err != nil { + t.Fatalf("VerifyInterface get error: %v", err) + } + }) + } } diff --git a/pkg/trustedresources/sign.go b/pkg/trustedresources/sign.go index 4977fa90c0..b0d140b29f 100644 --- a/pkg/trustedresources/sign.go +++ b/pkg/trustedresources/sign.go @@ -29,6 +29,7 @@ import ( "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/kms" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "golang.org/x/term" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -114,20 +115,34 @@ func signInterface(signer signature.Signer, i interface{}) ([]byte, error) { } // UnmarshalCRD will get the task/pipeline from buffer and extract the signature. -func UnmarshalCRD(buf []byte, kind string) (metav1.Object, []byte, error) { +func UnmarshalCRD(buf []byte, kind string, version string) (metav1.Object, []byte, error) { var resource metav1.Object var signature []byte switch kind { case "Task": - resource = &v1beta1.Task{} - if err := yaml.Unmarshal(buf, &resource); err != nil { - return nil, nil, err + if version == "v1beta1" { + resource = &v1beta1.Task{} + if err := yaml.Unmarshal(buf, &resource); err != nil { + return nil, nil, err + } + } else { + resource = &v1.Task{} + if err := yaml.Unmarshal(buf, &resource); err != nil { + return nil, nil, err + } } case "Pipeline": - resource = &v1beta1.Pipeline{} - if err := yaml.Unmarshal(buf, &resource); err != nil { - return nil, nil, err + if version == "v1beta1" { + resource = &v1beta1.Pipeline{} + if err := yaml.Unmarshal(buf, &resource); err != nil { + return nil, nil, err + } + } else { + resource = &v1.Pipeline{} + if err := yaml.Unmarshal(buf, &resource); err != nil { + return nil, nil, err + } } } annotations := resource.GetAnnotations() diff --git a/pkg/trustedresources/sign_test.go b/pkg/trustedresources/sign_test.go index 421c7eaada..741ef52dcc 100644 --- a/pkg/trustedresources/sign_test.go +++ b/pkg/trustedresources/sign_test.go @@ -78,16 +78,19 @@ func TestSign(t *testing.T) { resource metav1.Object kind string targetFile string + apiVersion string }{{ name: "Task Sign and pass verification", resource: getTask(), kind: "Task", targetFile: "signed-task.yaml", + apiVersion: "v1beta1", }, { name: "Pipeline Sign and pass verification", resource: getPipeline(), kind: "Pipeline", targetFile: "signed-pipeline.yaml", + apiVersion: "v1beta1", }, } @@ -101,7 +104,7 @@ func TestSign(t *testing.T) { t.Fatalf("error reading file: %v", err) } - target, signature, err := UnmarshalCRD(signed, tc.kind) + target, signature, err := UnmarshalCRD(signed, tc.kind, tc.apiVersion) if err != nil { t.Fatalf("error unmarshalling crd: %v", err) }