mirror of https://github.com/containers/podman.git
Add filtering functionality to http api secrets list
Filtering is missing in both compat API and libpod API, while docker has filtering functinality. This commit enables filtering option using name and id in both libpod and http API. Signed-off-by: Jakub Guzik <jakubmguzik@gmail.com>
This commit is contained in:
parent
469900406a
commit
d346e6e734
|
@ -223,7 +223,7 @@ func getSecrets(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCom
|
|||
cobra.CompErrorln(err.Error())
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
secrets, err := engine.SecretList(registry.GetContext())
|
||||
secrets, err := engine.SecretList(registry.GetContext(), entities.SecretListRequest{})
|
||||
if err != nil {
|
||||
cobra.CompErrorln(err.Error())
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
|
|
|
@ -48,7 +48,7 @@ func init() {
|
|||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,31 +11,25 @@ import (
|
|||
"github.com/containers/podman/v3/pkg/api/handlers/utils"
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/containers/podman/v3/pkg/domain/infra/abi"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/containers/podman/v3/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func ListSecrets(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder = r.Context().Value("decoder").(*schema.Decoder)
|
||||
)
|
||||
query := struct {
|
||||
Filters map[string][]string `schema:"filters"`
|
||||
}{}
|
||||
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
||||
filtersMap, err := util.PrepareFilters(r)
|
||||
if err != nil {
|
||||
utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError,
|
||||
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||
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}
|
||||
reports, err := ic.SecretList(r.Context())
|
||||
listOptions := entities.SecretListRequest{
|
||||
Filters: *filtersMap,
|
||||
}
|
||||
reports, err := ic.SecretList(r.Context(), listOptions)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
|
|
|
@ -44,6 +44,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
|
|||
// - secrets
|
||||
// summary: List 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:
|
||||
// - application/json
|
||||
// parameters:
|
||||
|
@ -110,6 +118,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error {
|
|||
// - secrets (compat)
|
||||
// summary: List 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:
|
||||
// - application/json
|
||||
// parameters:
|
||||
|
|
|
@ -18,7 +18,11 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.SecretInfoRepo
|
|||
if err != nil {
|
||||
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 {
|
||||
return secrs, err
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package secrets
|
|||
//go:generate go run ../generator/generator.go ListOptions
|
||||
// ListOptions are optional options for inspecting secrets
|
||||
type ListOptions struct {
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
//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) {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ type ContainerEngine interface {
|
|||
SetupRootless(ctx context.Context, noMoveProcess bool) error
|
||||
SecretCreate(ctx context.Context, name string, reader io.Reader, options SecretCreateOptions) (*SecretCreateReport, 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)
|
||||
Shutdown(ctx context.Context)
|
||||
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
|
||||
|
|
|
@ -16,7 +16,7 @@ type SecretCreateOptions struct {
|
|||
}
|
||||
|
||||
type SecretListRequest struct {
|
||||
Filters map[string]string
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
type SecretListReport struct {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||
"github.com/containers/podman/v3/pkg/domain/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -84,7 +85,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
|||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -95,19 +96,25 @@ func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretIn
|
|||
}
|
||||
report := make([]*entities.SecretInfoReport, 0, len(secretList))
|
||||
for _, secret := range secretList {
|
||||
reportItem := entities.SecretInfoReport{
|
||||
ID: secret.ID,
|
||||
CreatedAt: secret.CreatedAt,
|
||||
UpdatedAt: secret.CreatedAt,
|
||||
Spec: entities.SecretSpec{
|
||||
Name: secret.Name,
|
||||
Driver: entities.SecretDriverSpec{
|
||||
Name: secret.Driver,
|
||||
Options: secret.DriverOptions,
|
||||
},
|
||||
},
|
||||
result, err := utils.IfPassesSecretsFilter(secret, opts.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result {
|
||||
reportItem := entities.SecretInfoReport{
|
||||
ID: secret.ID,
|
||||
CreatedAt: secret.CreatedAt,
|
||||
UpdatedAt: secret.CreatedAt,
|
||||
Spec: entities.SecretSpec{
|
||||
Name: secret.Name,
|
||||
Driver: entities.SecretDriverSpec{
|
||||
Name: secret.Driver,
|
||||
Options: secret.DriverOptions,
|
||||
},
|
||||
},
|
||||
}
|
||||
report = append(report, &reportItem)
|
||||
}
|
||||
report = append(report, &reportItem)
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
|
|
|
@ -43,8 +43,9 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
|
|||
return allInspect, errs, nil
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretInfoReport, error) {
|
||||
secrs, _ := secrets.List(ic.ClientCtx, nil)
|
||||
func (ic *ContainerEngine) SecretList(ctx context.Context, opts entities.SecretListRequest) ([]*entities.SecretInfoReport, error) {
|
||||
options := new(secrets.ListOptions).WithFilters(opts.Filters)
|
||||
secrs, _ := secrets.List(ic.ClientCtx, options)
|
||||
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].Version.Index=1
|
||||
|
||||
# secret list unsupported filters
|
||||
t GET secrets?filters='{"name":["foo1"]}' 400
|
||||
# secret list with filters
|
||||
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
|
||||
t DELETE secrets/mysecret 204
|
||||
|
|
Loading…
Reference in New Issue