Merge pull request from ashley-cui/seclabels

Add labels to secrets
This commit is contained in:
OpenShift Merge Robot
2022-09-21 13:15:28 +02:00
committed by GitHub
12 changed files with 103 additions and 15 deletions
cmd/podman/secrets
docs/source/markdown
pkg
api
handlers
server
bindings/secrets
domain
entities
infra
test

@ -10,6 +10,7 @@ import (
"github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/completion"
"github.com/containers/podman/v4/cmd/podman/common" "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/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -31,6 +32,7 @@ var (
var ( var (
createOpts = entities.SecretCreateOptions{} createOpts = entities.SecretCreateOptions{}
env = false env = false
labels []string
) )
func init() { func init() {
@ -38,21 +40,24 @@ func init() {
Command: createCmd, Command: createCmd,
Parent: secretCmd, Parent: secretCmd,
}) })
cfg := registry.PodmanConfig()
flags := createCmd.Flags() flags := createCmd.Flags()
driverFlagName := "driver" driverFlagName := "driver"
optsFlagName := "driver-opts"
cfg := registry.PodmanConfig()
flags.StringVarP(&createOpts.Driver, driverFlagName, "d", cfg.Secrets.Driver, "Specify secret driver") 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) _ = 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) _ = createCmd.RegisterFlagCompletionFunc(optsFlagName, completion.AutocompleteNone)
envFlagName := "env" envFlagName := "env"
flags.BoolVar(&env, envFlagName, false, "Read secret data from environment variable") 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 { func create(cmd *cobra.Command, args []string) error {
@ -87,6 +92,11 @@ func create(cmd *cobra.Command, args []string) error {
reader = file 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) report, err := registry.ContainerEngine().SecretCreate(context.Background(), name, reader, createOpts)
if err != nil { if err != nil {
return err return err

@ -26,16 +26,20 @@ Specify the secret driver (default **file**, which is unencrypted).
#### **--driver-opts**=*key1=val1,key2=val2* #### **--driver-opts**=*key1=val1,key2=val2*
Specify driver specific options Specify driver specific options.
#### **--env**=*false* #### **--env**=*false*
Read secret data from environment variable Read secret data from environment variable.
#### **--help** #### **--help**
Print usage statement. 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 ## EXAMPLES
``` ```

@ -111,14 +111,11 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) {
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err))
return 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) decoded, _ := base64.StdEncoding.DecodeString(createParams.Data)
reader := bytes.NewReader(decoded) reader := bytes.NewReader(decoded)
opts.Driver = createParams.Driver.Name opts.Driver = createParams.Driver.Name
opts.Labels = createParams.Labels
ic := abi.ContainerEngine{Libpod: runtime} ic := abi.ContainerEngine{Libpod: runtime}
report, err := ic.SecretCreate(r.Context(), createParams.Name, reader, opts) report, err := ic.SecretCreate(r.Context(), createParams.Name, reader, opts)

@ -22,6 +22,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) {
Name string `schema:"name"` Name string `schema:"name"`
Driver string `schema:"driver"` Driver string `schema:"driver"`
DriverOpts map[string]string `schema:"driveropts"` DriverOpts map[string]string `schema:"driveropts"`
Labels map[string]string `schema:"labels"`
}{ }{
// override any golang type defaults // override any golang type defaults
} }
@ -33,6 +34,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) {
opts.Driver = query.Driver opts.Driver = query.Driver
opts.DriverOpts = query.DriverOpts opts.DriverOpts = query.DriverOpts
opts.Labels = query.Labels
ic := abi.ContainerEngine{Libpod: runtime} ic := abi.ContainerEngine{Libpod: runtime}
report, err := ic.SecretCreate(r.Context(), query.Name, r.Body, opts) report, err := ic.SecretCreate(r.Context(), query.Name, r.Body, opts)

@ -25,6 +25,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
// type: string // type: string
// description: Secret driver // description: Secret driver
// default: "file" // 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 // - in: body
// name: request // name: request
// description: Secret // description: Secret

@ -22,4 +22,5 @@ type CreateOptions struct {
Name *string Name *string
Driver *string Driver *string
DriverOpts map[string]string DriverOpts map[string]string
Labels map[string]string
} }

@ -61,3 +61,18 @@ func (o *CreateOptions) GetDriverOpts() map[string]string {
} }
return o.DriverOpts 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
}

@ -13,6 +13,7 @@ type SecretCreateReport struct {
type SecretCreateOptions struct { type SecretCreateOptions struct {
Driver string Driver string
DriverOpts map[string]string DriverOpts map[string]string
Labels map[string]string
} }
type SecretListRequest struct { type SecretListRequest struct {
@ -55,6 +56,7 @@ type SecretVersion struct {
type SecretSpec struct { type SecretSpec struct {
Name string Name string
Driver SecretDriverSpec Driver SecretDriverSpec
Labels map[string]string
} }
type SecretDriverSpec struct { type SecretDriverSpec struct {
@ -70,6 +72,8 @@ type SecretCreateRequest struct {
Data string Data string
// Driver represents a driver (default "file") // Driver represents a driver (default "file")
Driver SecretDriverSpec Driver SecretDriverSpec
// Labels are labels on the secret
Labels map[string]string
} }
// Secret create response // Secret create response

@ -45,6 +45,7 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
storeOpts := secrets.StoreOptions{ storeOpts := secrets.StoreOptions{
DriverOpts: options.DriverOpts, DriverOpts: options.DriverOpts,
Labels: options.Labels,
} }
secretID, err := manager.Store(name, data, options.Driver, storeOpts) 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) return nil, nil, fmt.Errorf("inspecting secret %s: %w", nameOrID, err)
} }
} }
if secret.Labels == nil {
secret.Labels = make(map[string]string)
}
report := &entities.SecretInfoReport{ report := &entities.SecretInfoReport{
ID: secret.ID, ID: secret.ID,
CreatedAt: secret.CreatedAt, CreatedAt: secret.CreatedAt,
@ -84,6 +88,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
Name: secret.Driver, Name: secret.Driver,
Options: secret.DriverOptions, Options: secret.DriverOptions,
}, },
Labels: secret.Labels,
}, },
} }
reports = append(reports, report) reports = append(reports, report)

@ -14,7 +14,8 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
opts := new(secrets.CreateOptions). opts := new(secrets.CreateOptions).
WithDriver(options.Driver). WithDriver(options.Driver).
WithDriverOpts(options.DriverOpts). WithDriverOpts(options.DriverOpts).
WithName(name) WithName(name).
WithLabels(options.Labels)
created, err := secrets.Create(ic.ClientCtx, reader, opts) created, err := secrets.Create(ic.ClientCtx, reader, opts)
if err != nil { if err != nil {
return nil, err return nil, err

@ -7,9 +7,6 @@
t POST secrets/create Name=mysecret Data=c2VjcmV0 200\ t POST secrets/create Name=mysecret Data=c2VjcmV0 200\
.ID~.* \ .ID~.* \
# secret create unsupported labels
t POST secrets/create Name=mysecret Data=c2VjcmV0 Labels='{"fail":"fail"}' 400
# secret create name already in use # secret create name already in use
t POST secrets/create Name=mysecret Data=c2VjcmV0 409 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 \ t GET libpod/secrets/json?filters='{"label":["testl' 500 \
.cause="unexpected end of JSON input" .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 # secret rm
t DELETE secrets/mysecret 204 t DELETE secrets/mysecret 204
t DELETE secrets/labeledsecret 204
# secret rm non-existent secret # secret rm non-existent secret
t DELETE secrets/bogus 404 t DELETE secrets/bogus 404

@ -310,4 +310,41 @@ var _ = Describe("Podman secret", func() {
Expect(inspect.OutputToString()).To(Equal(secrID)) 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[]"))
})
}) })