diff --git a/cmd/cosign/cli/commands.go b/cmd/cosign/cli/commands.go index 4c61885a2edd..280cc2781fe2 100644 --- a/cmd/cosign/cli/commands.go +++ b/cmd/cosign/cli/commands.go @@ -120,10 +120,12 @@ func New() *cobra.Command { // Add sub-commands. addPublicKey(cmd) + addGenerate(cmd) addSign(cmd) addSignBlob(cmd) addGenerateKeyPair(cmd) addAttest(cmd) + addVersion(cmd) return cmd } diff --git a/cmd/cosign/cli/generate.go b/cmd/cosign/cli/generate.go new file mode 100644 index 000000000000..917f9d173496 --- /dev/null +++ b/cmd/cosign/cli/generate.go @@ -0,0 +1,60 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "flag" + + "github.com/spf13/cobra" + + "github.com/sigstore/cosign/cmd/cosign/cli/generate" + "github.com/sigstore/cosign/cmd/cosign/cli/options" +) + +func addGenerate(topLevel *cobra.Command) { + o := &options.GenerateOptions{} + + cmd := &cobra.Command{ + Use: "generate", + Short: "Generates (unsigned) signature payloads from the supplied container image.\ncosign generate [--a key=value] ", + Long: `Generates an unsigned payload from the supplied container image and flags. +This payload matches the one generated by the "cosign sign" command and can be used if you need +to sign payloads with your own tooling or algorithms.`, + Example: ` +# Generate a simple payload for an image + cosign generate + + # Generate a payload with specific annotations + cosign generate -a foo=bar + + # Use this payload in another tool + gpg --output image.sig --detach-sig <(cosign generate )`, + + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return flag.ErrHelp + } + annotationMap, err := o.AnnotationsMap() + if err != nil { + return err + } + return generate.GenerateCmd(cmd.Context(), o.RegistryOpts, args[0], annotationMap.Annotations, cmd.OutOrStdout()) + }, + } + + options.AddGenerateOptions(cmd, o) + topLevel.AddCommand(cmd) +} diff --git a/cmd/cosign/cli/generate/generate.go b/cmd/cosign/cli/generate/generate.go index 199aa3177a02..fccbf035a0b6 100644 --- a/cmd/cosign/cli/generate/generate.go +++ b/cmd/cosign/cli/generate/generate.go @@ -20,7 +20,6 @@ import ( "flag" "fmt" "io" - "os" "github.com/google/go-containerregistry/pkg/name" "github.com/peterbourgon/ff/v3/ffcli" @@ -31,6 +30,8 @@ import ( "github.com/sigstore/sigstore/pkg/signature/payload" ) +// Generate subcommand for ffcli. +// Deprecated: this will be deleted when the migration from ffcli to cobra is done. func Generate() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign generate", flag.ExitOnError) @@ -49,7 +50,7 @@ This payload matches the one generated by the "cosign sign" command and can be u to sign payloads with your own tooling or algorithms. EXAMPLES - # Generage a simple payload for an image + # Generate a simple payload for an image cosign generate # Generate a payload with specific annotations @@ -59,10 +60,7 @@ EXAMPLES gpg --output image.sig --detach-sig <(cosign generate )`, FlagSet: flagset, Exec: func(ctx context.Context, args []string) error { - if len(args) != 1 { - return flag.ErrHelp - } - return GenerateCmd(ctx, regOpts, args[0], annotations.Annotations, os.Stdout) + panic("this command is now implemented in cobra.") }, } } diff --git a/cmd/cosign/cli/options/annotations.go b/cmd/cosign/cli/options/annotations.go new file mode 100644 index 000000000000..ae82cf081cee --- /dev/null +++ b/cmd/cosign/cli/options/annotations.go @@ -0,0 +1,51 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package options + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + sigs "github.com/sigstore/cosign/pkg/signature" +) + +// AnnotationOptions is the top level wrapper for the annotations. +type AnnotationOptions struct { + Annotations []string +} + +func (s *AnnotationOptions) AnnotationsMap() (sigs.AnnotationsMap, error) { + ann := sigs.AnnotationsMap{} + for _, a := range s.Annotations { + kv := strings.Split(a, "=") + if len(kv) != 2 { + return ann, fmt.Errorf("unable to parse annotation: %s", a) + } + if ann.Annotations == nil { + ann.Annotations = map[string]interface{}{} + } + ann.Annotations[kv[0]] = kv[1] + } + return ann, nil +} + +// AddAnnotationOptions adds annotation options to cmd. +func AddAnnotationOptions(cmd *cobra.Command, o *AnnotationOptions) { + cmd.Flags().StringSliceVarP(&o.Annotations, "annotations", "a", nil, + "extra key=value pairs to sign") +} diff --git a/cmd/cosign/cli/options/sign_test.go b/cmd/cosign/cli/options/annotations_test.go similarity index 94% rename from cmd/cosign/cli/options/sign_test.go rename to cmd/cosign/cli/options/annotations_test.go index 6799476039ff..0035b9b202b0 100644 --- a/cmd/cosign/cli/options/sign_test.go +++ b/cmd/cosign/cli/options/annotations_test.go @@ -23,7 +23,7 @@ import ( "github.com/sigstore/cosign/pkg/signature" ) -func TestSignOptions_AnnotationsMap(t *testing.T) { +func TestAnnotationOptions_AnnotationsMap(t *testing.T) { tests := []struct { name string annotations []string @@ -47,7 +47,7 @@ func TestSignOptions_AnnotationsMap(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := &SignOptions{ + s := &AnnotationOptions{ Annotations: tt.annotations, } got, err := s.AnnotationsMap() diff --git a/cmd/cosign/cli/options/generate.go b/cmd/cosign/cli/options/generate.go new file mode 100644 index 000000000000..1f28573103ad --- /dev/null +++ b/cmd/cosign/cli/options/generate.go @@ -0,0 +1,34 @@ +// +// Copyright 2021 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package options + +import ( + "github.com/spf13/cobra" +) + +// GenerateOptions is the top level wrapper for the generate command. +type GenerateOptions struct { + AnnotationOptions + RegistryOpts RegistryOpts +} + +// AddGenerateOptions adds the generate command options to cmd. +func AddGenerateOptions(cmd *cobra.Command, o *GenerateOptions) { + AddAnnotationOptions(cmd, &o.AnnotationOptions) + + cmd.Flags().BoolVar(&o.RegistryOpts.AllowInsecure, "allow-insecure-registry", false, + "whether to allow insecure connections to registries. Don't use this for anything but testing") +} diff --git a/cmd/cosign/cli/options/sign.go b/cmd/cosign/cli/options/sign.go index 2626356ceddc..138886fffc98 100644 --- a/cmd/cosign/cli/options/sign.go +++ b/cmd/cosign/cli/options/sign.go @@ -16,12 +16,7 @@ package options import ( - "fmt" - "strings" - "github.com/spf13/cobra" - - sigs "github.com/sigstore/cosign/pkg/signature" ) // SignOptions is the top level wrapper for the sign command. @@ -40,25 +35,10 @@ type SignOptions struct { OIDC OIDCOptions Attachment string - Annotations []string + AnnotationOptions RegistryOpts RegistryOpts } -func (s *SignOptions) AnnotationsMap() (sigs.AnnotationsMap, error) { - ann := sigs.AnnotationsMap{} - for _, a := range s.Annotations { - kv := strings.Split(a, "=") - if len(kv) != 2 { - return ann, fmt.Errorf("unable to parse annotation: %s", a) - } - if ann.Annotations == nil { - ann.Annotations = map[string]interface{}{} - } - ann.Annotations[kv[0]] = kv[1] - } - return ann, nil -} - // AddSignOptions adds the sign command options to cmd. func AddSignOptions(cmd *cobra.Command, o *SignOptions) { cmd.Flags().StringVar(&o.Key, "key", "", @@ -84,8 +64,7 @@ func AddSignOptions(cmd *cobra.Command, o *SignOptions) { cmd.Flags().StringVar(&o.Attachment, "attachment", "", "related image attachment to sign (sbom), default none") - cmd.Flags().StringSliceVarP(&o.Annotations, "annotations", "a", nil, - "extra key=value pairs to sign") + AddAnnotationOptions(cmd, &o.AnnotationOptions) cmd.Flags().BoolVar(&o.RegistryOpts.AllowInsecure, "allow-insecure-registry", false, "whether to allow insecure connections to registries. Don't use this for anything but testing") diff --git a/cmd/cosign/cli/version.go b/cmd/cosign/cli/version.go index 3d4c5466abd3..e7ebe70e405f 100644 --- a/cmd/cosign/cli/version.go +++ b/cmd/cosign/cli/version.go @@ -22,10 +22,13 @@ import ( "github.com/peterbourgon/ff/v3/ffcli" "github.com/pkg/errors" + "github.com/spf13/cobra" "github.com/sigstore/cosign/cmd/cosign/cli/options" ) +// Version subcommand for ffcli. +// Deprecated: this will be deleted when the migration from ffcli to cobra is done. func Version() *ffcli.Command { var ( flagset = flag.NewFlagSet("cosign version", flag.ExitOnError) @@ -52,3 +55,32 @@ func Version() *ffcli.Command { }, } } + +func addVersion(topLevel *cobra.Command) { + var outputJSON bool + + cmd := &cobra.Command{ + Use: "version", + Short: "Prints the cosign version", + Long: "Prints the cosign version", + + RunE: func(cmd *cobra.Command, args []string) error { + v := options.VersionInfo() + res := v.String() + if outputJSON { + j, err := v.JSONString() + if err != nil { + return errors.Wrap(err, "unable to generate JSON from version info") + } + res = j + } + fmt.Println(res) + return nil + }, + } + + cmd.Flags().BoolVar(&outputJSON, "json", false, + "print JSON instead of text") + + topLevel.AddCommand(cmd) +} diff --git a/cmd/cosign/main.go b/cmd/cosign/main.go index 86739869ebbf..edf072ac2d62 100644 --- a/cmd/cosign/main.go +++ b/cmd/cosign/main.go @@ -50,8 +50,9 @@ func main() { if len(os.Args) > 1 { switch os.Args[1] { case "public-key", "generate-key-pair", - "sign", "sign-blob", - "attest": + "generate", "sign", "sign-blob", + "attest", + "version": // cobra. default: // ffcli