diff options
-rw-r--r-- | cmd/podman/secrets/create.go | 20 | ||||
-rw-r--r-- | docs/source/markdown/podman-secret-create.1.md | 8 | ||||
-rw-r--r-- | pkg/api/handlers/compat/secrets.go | 5 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/secrets.go | 2 | ||||
-rw-r--r-- | pkg/api/server/register_secrets.go | 8 | ||||
-rw-r--r-- | pkg/bindings/secrets/types.go | 1 | ||||
-rw-r--r-- | pkg/bindings/secrets/types_create_options.go | 15 | ||||
-rw-r--r-- | pkg/domain/entities/secrets.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/abi/secrets.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/secrets.go | 3 | ||||
-rw-r--r-- | test/apiv2/50-secrets.at | 10 | ||||
-rw-r--r-- | test/e2e/secret_test.go | 37 |
12 files changed, 103 insertions, 15 deletions
diff --git a/cmd/podman/secrets/create.go b/cmd/podman/secrets/create.go index 01775f563..293da2103 100644 --- a/cmd/podman/secrets/create.go +++ b/cmd/podman/secrets/create.go @@ -10,6 +10,7 @@ import ( "github.com/containers/common/pkg/completion" "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/parse" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/spf13/cobra" @@ -31,6 +32,7 @@ var ( var ( createOpts = entities.SecretCreateOptions{} env = false + labels []string ) func init() { @@ -38,21 +40,24 @@ func init() { Command: createCmd, Parent: secretCmd, }) + cfg := registry.PodmanConfig() flags := createCmd.Flags() driverFlagName := "driver" - optsFlagName := "driver-opts" - - cfg := registry.PodmanConfig() - flags.StringVarP(&createOpts.Driver, driverFlagName, "d", cfg.Secrets.Driver, "Specify secret driver") - flags.StringToStringVar(&createOpts.DriverOpts, optsFlagName, cfg.Secrets.Opts, "Specify driver specific options") _ = createCmd.RegisterFlagCompletionFunc(driverFlagName, completion.AutocompleteNone) + + optsFlagName := "driver-opts" + flags.StringToStringVar(&createOpts.DriverOpts, optsFlagName, cfg.Secrets.Opts, "Specify driver specific options") _ = createCmd.RegisterFlagCompletionFunc(optsFlagName, completion.AutocompleteNone) envFlagName := "env" flags.BoolVar(&env, envFlagName, false, "Read secret data from environment variable") + + labelFlagName := "label" + flags.StringArrayVarP(&labels, labelFlagName, "l", nil, "Specify labels on the secret") + _ = createCmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) } func create(cmd *cobra.Command, args []string) error { @@ -87,6 +92,11 @@ func create(cmd *cobra.Command, args []string) error { reader = file } + createOpts.Labels, err = parse.GetAllLabels([]string{}, labels) + if err != nil { + return fmt.Errorf("unable to process labels: %w", err) + } + report, err := registry.ContainerEngine().SecretCreate(context.Background(), name, reader, createOpts) if err != nil { return err diff --git a/docs/source/markdown/podman-secret-create.1.md b/docs/source/markdown/podman-secret-create.1.md index 1aafc6c11..fc6d72efb 100644 --- a/docs/source/markdown/podman-secret-create.1.md +++ b/docs/source/markdown/podman-secret-create.1.md @@ -26,16 +26,20 @@ Specify the secret driver (default **file**, which is unencrypted). #### **--driver-opts**=*key1=val1,key2=val2* -Specify driver specific options +Specify driver specific options. #### **--env**=*false* -Read secret data from environment variable +Read secret data from environment variable. #### **--help** Print usage statement. +#### **--label**, **-l**=*key=val1,key2=val2* + +Add label to secret. These labels can be viewed in podman secrete inspect or ls. + ## EXAMPLES ``` diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go index 13b3c4e24..847f05f27 100644 --- a/pkg/api/handlers/compat/secrets.go +++ b/pkg/api/handlers/compat/secrets.go @@ -111,14 +111,11 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) return } - if len(createParams.Labels) > 0 { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("labels not supported: %w", errors.New("bad parameter"))) - return - } decoded, _ := base64.StdEncoding.DecodeString(createParams.Data) reader := bytes.NewReader(decoded) opts.Driver = createParams.Driver.Name + opts.Labels = createParams.Labels ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.SecretCreate(r.Context(), createParams.Name, reader, opts) diff --git a/pkg/api/handlers/libpod/secrets.go b/pkg/api/handlers/libpod/secrets.go index 6eba65f2b..c24ac8563 100644 --- a/pkg/api/handlers/libpod/secrets.go +++ b/pkg/api/handlers/libpod/secrets.go @@ -22,6 +22,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { Name string `schema:"name"` Driver string `schema:"driver"` DriverOpts map[string]string `schema:"driveropts"` + Labels map[string]string `schema:"labels"` }{ // override any golang type defaults } @@ -33,6 +34,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { opts.Driver = query.Driver opts.DriverOpts = query.DriverOpts + opts.Labels = query.Labels ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.SecretCreate(r.Context(), query.Name, r.Body, opts) diff --git a/pkg/api/server/register_secrets.go b/pkg/api/server/register_secrets.go index 8918ad238..a60145958 100644 --- a/pkg/api/server/register_secrets.go +++ b/pkg/api/server/register_secrets.go @@ -25,6 +25,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error { // type: string // description: Secret driver // default: "file" + // - in: query + // name: driveropts + // type: string + // description: Secret driver options + // - in: query + // name: labels + // type: string + // description: Labels on the secret // - in: body // name: request // description: Secret diff --git a/pkg/bindings/secrets/types.go b/pkg/bindings/secrets/types.go index 01c3c248d..d2f449556 100644 --- a/pkg/bindings/secrets/types.go +++ b/pkg/bindings/secrets/types.go @@ -22,4 +22,5 @@ type CreateOptions struct { Name *string Driver *string DriverOpts map[string]string + Labels map[string]string } diff --git a/pkg/bindings/secrets/types_create_options.go b/pkg/bindings/secrets/types_create_options.go index 6b1666a42..c9c88e1f3 100644 --- a/pkg/bindings/secrets/types_create_options.go +++ b/pkg/bindings/secrets/types_create_options.go @@ -61,3 +61,18 @@ func (o *CreateOptions) GetDriverOpts() map[string]string { } return o.DriverOpts } + +// WithLabels set field Labels to given value +func (o *CreateOptions) WithLabels(value map[string]string) *CreateOptions { + o.Labels = value + return o +} + +// GetLabels returns value of field Labels +func (o *CreateOptions) GetLabels() map[string]string { + if o.Labels == nil { + var z map[string]string + return z + } + return o.Labels +} diff --git a/pkg/domain/entities/secrets.go b/pkg/domain/entities/secrets.go index d8af937a7..5686b90e9 100644 --- a/pkg/domain/entities/secrets.go +++ b/pkg/domain/entities/secrets.go @@ -13,6 +13,7 @@ type SecretCreateReport struct { type SecretCreateOptions struct { Driver string DriverOpts map[string]string + Labels map[string]string } type SecretListRequest struct { @@ -55,6 +56,7 @@ type SecretVersion struct { type SecretSpec struct { Name string Driver SecretDriverSpec + Labels map[string]string } type SecretDriverSpec struct { @@ -70,6 +72,8 @@ type SecretCreateRequest struct { Data string // Driver represents a driver (default "file") Driver SecretDriverSpec + // Labels are labels on the secret + Labels map[string]string } // Secret create response diff --git a/pkg/domain/infra/abi/secrets.go b/pkg/domain/infra/abi/secrets.go index 47159d65a..2a377288b 100644 --- a/pkg/domain/infra/abi/secrets.go +++ b/pkg/domain/infra/abi/secrets.go @@ -45,6 +45,7 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader storeOpts := secrets.StoreOptions{ DriverOpts: options.DriverOpts, + Labels: options.Labels, } secretID, err := manager.Store(name, data, options.Driver, storeOpts) @@ -74,6 +75,9 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string return nil, nil, fmt.Errorf("inspecting secret %s: %w", nameOrID, err) } } + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } report := &entities.SecretInfoReport{ ID: secret.ID, CreatedAt: secret.CreatedAt, @@ -84,6 +88,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string Name: secret.Driver, Options: secret.DriverOptions, }, + Labels: secret.Labels, }, } reports = append(reports, report) diff --git a/pkg/domain/infra/tunnel/secrets.go b/pkg/domain/infra/tunnel/secrets.go index d26718b12..aa48cb764 100644 --- a/pkg/domain/infra/tunnel/secrets.go +++ b/pkg/domain/infra/tunnel/secrets.go @@ -14,7 +14,8 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader opts := new(secrets.CreateOptions). WithDriver(options.Driver). WithDriverOpts(options.DriverOpts). - WithName(name) + WithName(name). + WithLabels(options.Labels) created, err := secrets.Create(ic.ClientCtx, reader, opts) if err != nil { return nil, err diff --git a/test/apiv2/50-secrets.at b/test/apiv2/50-secrets.at index ed0e8fb6b..acd8f3de9 100644 --- a/test/apiv2/50-secrets.at +++ b/test/apiv2/50-secrets.at @@ -7,9 +7,6 @@ t POST secrets/create Name=mysecret Data=c2VjcmV0 200\ .ID~.* \ -# secret create unsupported labels -t POST secrets/create Name=mysecret Data=c2VjcmV0 Labels='{"fail":"fail"}' 400 - # secret create name already in use t POST secrets/create Name=mysecret Data=c2VjcmV0 409 @@ -59,8 +56,15 @@ t GET libpod/secrets/json?filters='garb1age}' 500 \ t GET libpod/secrets/json?filters='{"label":["testl' 500 \ .cause="unexpected end of JSON input" +# secret with labels +t POST secrets/create Name=labeledsecret Data=c2VjcmV0 Labels='{"foo":"bar"}' 200 +t GET secrets/labeledsecret 200 \ + .Spec.Labels.foo=bar + # secret rm t DELETE secrets/mysecret 204 +t DELETE secrets/labeledsecret 204 + # secret rm non-existent secret t DELETE secrets/bogus 404 diff --git a/test/e2e/secret_test.go b/test/e2e/secret_test.go index 902f422bd..91135b958 100644 --- a/test/e2e/secret_test.go +++ b/test/e2e/secret_test.go @@ -310,4 +310,41 @@ var _ = Describe("Podman secret", func() { Expect(inspect.OutputToString()).To(Equal(secrID)) }) + It("podman secret with labels", func() { + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte("mysecret"), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "--label", "foo=bar", "a", secretFilePath}) + session.WaitWithDefaultTimeout() + secrID := session.OutputToString() + Expect(session).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Labels}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("foo:bar")) + + session = podmanTest.Podman([]string{"secret", "create", "--label", "foo=bar", "--label", "a:b", "b", secretFilePath}) + session.WaitWithDefaultTimeout() + secrID = session.OutputToString() + Expect(session).Should(Exit(0)) + + inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Labels}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("foo:bar")) + Expect(inspect.OutputToString()).To(ContainSubstring("a:b")) + + session = podmanTest.Podman([]string{"secret", "create", "c", secretFilePath}) + session.WaitWithDefaultTimeout() + secrID = session.OutputToString() + Expect(session).Should(Exit(0)) + + inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Labels}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("map[]")) + + }) }) |