mirror of https://github.com/containers/podman.git
Merge pull request #11431 from jmguzik/secrets-ls-filters
Add filtering functionality to http api secrets list
This commit is contained in:
commit
536f23c0b7
|
@ -223,7 +223,7 @@ func getSecrets(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCom
|
||||||
cobra.CompErrorln(err.Error())
|
cobra.CompErrorln(err.Error())
|
||||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
}
|
}
|
||||||
secrets, err := engine.SecretList(registry.GetContext())
|
secrets, err := engine.SecretList(registry.GetContext(), entities.SecretListRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cobra.CompErrorln(err.Error())
|
cobra.CompErrorln(err.Error())
|
||||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
|
|
@ -48,7 +48,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ls(cmd *cobra.Command, args []string) error {
|
func ls(cmd *cobra.Command, args []string) error {
|
||||||
responses, err := registry.ContainerEngine().SecretList(context.Background())
|
responses, err := registry.ContainerEngine().SecretList(context.Background(), entities.SecretListRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,31 +11,25 @@ import (
|
||||||
"github.com/containers/podman/v3/pkg/api/handlers/utils"
|
"github.com/containers/podman/v3/pkg/api/handlers/utils"
|
||||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||||
"github.com/containers/podman/v3/pkg/domain/infra/abi"
|
"github.com/containers/podman/v3/pkg/domain/infra/abi"
|
||||||
"github.com/gorilla/schema"
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListSecrets(w http.ResponseWriter, r *http.Request) {
|
func ListSecrets(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
decoder = r.Context().Value("decoder").(*schema.Decoder)
|
|
||||||
)
|
)
|
||||||
query := struct {
|
filtersMap, err := util.PrepareFilters(r)
|
||||||
Filters map[string][]string `schema:"filters"`
|
if err != nil {
|
||||||
}{}
|
utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError,
|
||||||
|
|
||||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
|
||||||
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
|
||||||
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(query.Filters) > 0 {
|
|
||||||
utils.Error(w, "filters not supported", http.StatusBadRequest,
|
|
||||||
errors.Wrapf(errors.New("bad parameter"), "filters not supported"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ic := abi.ContainerEngine{Libpod: runtime}
|
ic := abi.ContainerEngine{Libpod: runtime}
|
||||||
reports, err := ic.SecretList(r.Context())
|
listOptions := entities.SecretListRequest{
|
||||||
|
Filters: *filtersMap,
|
||||||
|
}
|
||||||
|
reports, err := ic.SecretList(r.Context(), listOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -44,6 +44,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
|
||||||
// - secrets
|
// - secrets
|
||||||
// summary: List secrets
|
// summary: List secrets
|
||||||
// description: Returns a list of secrets
|
// description: Returns a list of secrets
|
||||||
|
// parameters:
|
||||||
|
// - in: query
|
||||||
|
// name: filters
|
||||||
|
// type: string
|
||||||
|
// description: |
|
||||||
|
// JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Currently available filters:
|
||||||
|
// - `name=[name]` Matches secrets name (accepts regex).
|
||||||
|
// - `id=[id]` Matches for full or partial ID.
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// parameters:
|
// parameters:
|
||||||
|
@ -110,6 +118,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
|
||||||
// - secrets (compat)
|
// - secrets (compat)
|
||||||
// summary: List secrets
|
// summary: List secrets
|
||||||
// description: Returns a list of secrets
|
// description: Returns a list of secrets
|
||||||
|
// parameters:
|
||||||
|
// - in: query
|
||||||
|
// name: filters
|
||||||
|
// type: string
|
||||||
|
// description: |
|
||||||
|
// JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Currently available filters:
|
||||||
|
// - `name=[name]` Matches secrets name (accepts regex).
|
||||||
|
// - `id=[id]` Matches for full or partial ID.
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// parameters:
|
// parameters:
|
||||||
|
|
|
@ -18,7 +18,11 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.SecretInfoRepo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
response, err := conn.DoRequest(nil, http.MethodGet, "/secrets/json", nil, nil)
|
params, err := options.ToParams()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response, err := conn.DoRequest(nil, http.MethodGet, "/secrets/json", params, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return secrs, err
|
return secrs, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package secrets
|
||||||
//go:generate go run ../generator/generator.go ListOptions
|
//go:generate go run ../generator/generator.go ListOptions
|
||||||
// ListOptions are optional options for inspecting secrets
|
// ListOptions are optional options for inspecting secrets
|
||||||
type ListOptions struct {
|
type ListOptions struct {
|
||||||
|
Filters map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate go run ../generator/generator.go InspectOptions
|
//go:generate go run ../generator/generator.go InspectOptions
|
||||||
|
|
|
@ -19,3 +19,19 @@ func (o *ListOptions) Changed(fieldName string) bool {
|
||||||
func (o *ListOptions) ToParams() (url.Values, error) {
|
func (o *ListOptions) ToParams() (url.Values, error) {
|
||||||
return util.ToParams(o)
|
return util.ToParams(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithFilters
|
||||||
|
func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions {
|
||||||
|
v := value
|
||||||
|
o.Filters = v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilters
|
||||||
|
func (o *ListOptions) GetFilters() map[string][]string {
|
||||||
|
var filters map[string][]string
|
||||||
|
if o.Filters == nil {
|
||||||
|
return filters
|
||||||
|
}
|
||||||
|
return o.Filters
|
||||||
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ type ContainerEngine interface {
|
||||||
SetupRootless(ctx context.Context, noMoveProcess bool) error
|
SetupRootless(ctx context.Context, noMoveProcess bool) error
|
||||||
SecretCreate(ctx context.Context, name string, reader io.Reader, options SecretCreateOptions) (*SecretCreateReport, error)
|
SecretCreate(ctx context.Context, name string, reader io.Reader, options SecretCreateOptions) (*SecretCreateReport, error)
|
||||||
SecretInspect(ctx context.Context, nameOrIDs []string) ([]*SecretInfoReport, []error, error)
|
SecretInspect(ctx context.Context, nameOrIDs []string) ([]*SecretInfoReport, []error, error)
|
||||||
SecretList(ctx context.Context) ([]*SecretInfoReport, error)
|
SecretList(ctx context.Context, opts SecretListRequest) ([]*SecretInfoReport, error)
|
||||||
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
|
SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error)
|
||||||
Shutdown(ctx context.Context)
|
Shutdown(ctx context.Context)
|
||||||
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
|
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
|
||||||
|
|
|
@ -16,7 +16,7 @@ type SecretCreateOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecretListRequest struct {
|
type SecretListRequest struct {
|
||||||
Filters map[string]string
|
Filters map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecretListReport struct {
|
type SecretListReport struct {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||||
|
"github.com/containers/podman/v3/pkg/domain/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
||||||
return reports, errs, nil
|
return reports, errs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretInfoReport, error) {
|
func (ic *ContainerEngine) SecretList(ctx context.Context, opts entities.SecretListRequest) ([]*entities.SecretInfoReport, error) {
|
||||||
manager, err := ic.Libpod.SecretsManager()
|
manager, err := ic.Libpod.SecretsManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -95,6 +96,11 @@ func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretIn
|
||||||
}
|
}
|
||||||
report := make([]*entities.SecretInfoReport, 0, len(secretList))
|
report := make([]*entities.SecretInfoReport, 0, len(secretList))
|
||||||
for _, secret := range secretList {
|
for _, secret := range secretList {
|
||||||
|
result, err := utils.IfPassesSecretsFilter(secret, opts.Filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if result {
|
||||||
reportItem := entities.SecretInfoReport{
|
reportItem := entities.SecretInfoReport{
|
||||||
ID: secret.ID,
|
ID: secret.ID,
|
||||||
CreatedAt: secret.CreatedAt,
|
CreatedAt: secret.CreatedAt,
|
||||||
|
@ -109,6 +115,7 @@ func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretIn
|
||||||
}
|
}
|
||||||
report = append(report, &reportItem)
|
report = append(report, &reportItem)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return report, nil
|
return report, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,9 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
||||||
return allInspect, errs, nil
|
return allInspect, errs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretInfoReport, error) {
|
func (ic *ContainerEngine) SecretList(ctx context.Context, opts entities.SecretListRequest) ([]*entities.SecretInfoReport, error) {
|
||||||
secrs, _ := secrets.List(ic.ClientCtx, nil)
|
options := new(secrets.ListOptions).WithFilters(opts.Filters)
|
||||||
|
secrs, _ := secrets.List(ic.ClientCtx, options)
|
||||||
return secrs, nil
|
return secrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/secrets"
|
||||||
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IfPassesSecretsFilter(s secrets.Secret, filters map[string][]string) (bool, error) {
|
||||||
|
result := true
|
||||||
|
for key, filterValues := range filters {
|
||||||
|
switch strings.ToLower(key) {
|
||||||
|
case "name":
|
||||||
|
result = util.StringMatchRegexSlice(s.Name, filterValues)
|
||||||
|
case "id":
|
||||||
|
result = util.StringMatchRegexSlice(s.ID, filterValues)
|
||||||
|
default:
|
||||||
|
return false, errors.Errorf("invalid filter %q", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
|
@ -27,8 +27,37 @@ t GET secrets 200 \
|
||||||
.[0].Spec.Name=mysecret \
|
.[0].Spec.Name=mysecret \
|
||||||
.[0].Version.Index=1
|
.[0].Version.Index=1
|
||||||
|
|
||||||
# secret list unsupported filters
|
# secret list with filters
|
||||||
t GET secrets?filters='{"name":["foo1"]}' 400
|
t GET secrets?filters='{"name":["mysecret"]}' 200 \
|
||||||
|
length=1 \
|
||||||
|
.[0].Spec.Name=mysecret \
|
||||||
|
.[0].Version.Index=1
|
||||||
|
|
||||||
|
t GET secrets?filters='{"name":["mysecret2"]}' 200 \
|
||||||
|
length=0 \
|
||||||
|
|
||||||
|
# secret libpod list with filters
|
||||||
|
t GET libpod/secrets/json?filters='{"name":["mysecret"]}' 200 \
|
||||||
|
length=1 \
|
||||||
|
.[0].Spec.Name=mysecret \
|
||||||
|
|
||||||
|
t GET libpod/secrets/json?filters='{"name":["mysecret2"]}' 200 \
|
||||||
|
length=0 \
|
||||||
|
|
||||||
|
# secret list with unsupported filters
|
||||||
|
t GET secrets?filters='{"label":["xyz"]}' 500
|
||||||
|
|
||||||
|
#compat api list secrets sanity checks
|
||||||
|
t GET secrets?filters='garb1age}' 500 \
|
||||||
|
.cause="invalid character 'g' looking for beginning of value"
|
||||||
|
t GET secrets?filters='{"label":["testl' 500 \
|
||||||
|
.cause="unexpected end of JSON input"
|
||||||
|
|
||||||
|
#libpod api list secrets sanity checks
|
||||||
|
t GET libpod/secrets/json?filters='garb1age}' 500 \
|
||||||
|
.cause="invalid character 'g' looking for beginning of value"
|
||||||
|
t GET libpod/secrets/json?filters='{"label":["testl' 500 \
|
||||||
|
.cause="unexpected end of JSON input"
|
||||||
|
|
||||||
# secret rm
|
# secret rm
|
||||||
t DELETE secrets/mysecret 204
|
t DELETE secrets/mysecret 204
|
||||||
|
|
Loading…
Reference in New Issue