diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/containers.go | 22 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_prune.go | 17 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_build.go | 48 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 24 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 19 | ||||
-rw-r--r-- | pkg/api/server/register_images.go | 9 | ||||
-rw-r--r-- | pkg/domain/filters/containers.go | 24 | ||||
-rw-r--r-- | pkg/domain/filters/pods.go | 21 | ||||
-rw-r--r-- | pkg/domain/filters/volumes.go | 17 | ||||
-rw-r--r-- | pkg/specgen/generate/security.go | 36 | ||||
-rw-r--r-- | pkg/util/filters.go | 27 | ||||
-rw-r--r-- | pkg/util/filters_test.go | 113 |
12 files changed, 262 insertions, 115 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index d3277b815..e7146a5d8 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -19,6 +19,7 @@ import ( "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/ps" "github.com/containers/podman/v3/pkg/signal" + "github.com/containers/podman/v3/pkg/util" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" @@ -92,23 +93,24 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - All bool `schema:"all"` - Limit int `schema:"limit"` - Size bool `schema:"size"` - Filters map[string][]string `schema:"filters"` + All bool `schema:"all"` + Limit int `schema:"limit"` + Size bool `schema:"size"` }{ // override any golang type defaults } - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + filterMap, err := util.PrepareFilters(r) + + if dErr := decoder.Decode(&query, r.URL.Query()); dErr != nil || err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - filterFuncs := make([]libpod.ContainerFilter, 0, len(query.Filters)) + filterFuncs := make([]libpod.ContainerFilter, 0, len(*filterMap)) all := query.All || query.Limit > 0 - if len(query.Filters) > 0 { - for k, v := range query.Filters { + if len((*filterMap)) > 0 { + for k, v := range *filterMap { generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) if err != nil { utils.InternalServerError(w, err) @@ -120,7 +122,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { // Docker thinks that if status is given as an input, then we should override // the all setting and always deal with all containers. - if len(query.Filters["status"]) > 0 { + if len((*filterMap)["status"]) > 0 { all = true } if !all { diff --git a/pkg/api/handlers/compat/containers_prune.go b/pkg/api/handlers/compat/containers_prune.go index dc4d53af6..e37929d27 100644 --- a/pkg/api/handlers/compat/containers_prune.go +++ b/pkg/api/handlers/compat/containers_prune.go @@ -9,23 +9,20 @@ import ( "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/domain/filters" - "github.com/gorilla/schema" + "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) func PruneContainers(w http.ResponseWriter, r *http.Request) { 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, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + filtersMap, err := util.PrepareFilters(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - filterFuncs := make([]libpod.ContainerFilter, 0, len(query.Filters)) - for k, v := range query.Filters { + + filterFuncs := make([]libpod.ContainerFilter, 0, len(*filtersMap)) + for k, v := range *filtersMap { generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) if err != nil { utils.InternalServerError(w, err) diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 7751b91a7..0a63d6e1c 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -69,7 +69,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { BuildArgs string `schema:"buildargs"` CacheFrom string `schema:"cachefrom"` Compression uint64 `schema:"compression"` - ConfigureNetwork int64 `schema:"networkmode"` + ConfigureNetwork string `schema:"networkmode"` CpuPeriod uint64 `schema:"cpuperiod"` // nolint CpuQuota int64 `schema:"cpuquota"` // nolint CpuSetCpus string `schema:"cpusetcpus"` // nolint @@ -84,7 +84,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { ForceRm bool `schema:"forcerm"` From string `schema:"from"` HTTPProxy bool `schema:"httpproxy"` - Isolation int64 `schema:"isolation"` + Isolation string `schema:"isolation"` Ignore bool `schema:"ignore"` Jobs int `schema:"jobs"` // nolint Labels string `schema:"labels"` @@ -205,9 +205,15 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { isolation := buildah.IsolationDefault */ if utils.IsLibpodRequest(r) { - // isolation = buildah.Isolation(query.Isolation) + // isolation = parseLibPodIsolation(query.Isolation) registry = "" format = query.OutputFormat + } else { + if _, found := r.URL.Query()["isolation"]; found { + if query.Isolation != "" && query.Isolation != "default" { + logrus.Debugf("invalid `isolation` parameter: %q", query.Isolation) + } + } } var additionalTags []string if len(query.Tag) > 1 { @@ -329,7 +335,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { CNIConfigDir: rtc.Network.CNIPluginDirs[0], CNIPluginPath: util.DefaultCNIPluginPath, Compression: compression, - ConfigureNetwork: buildah.NetworkConfigurationPolicy(query.ConfigureNetwork), + ConfigureNetwork: parseNetworkConfigurationPolicy(query.ConfigureNetwork), ContextDirectory: contextDirectory, Devices: devices, DropCapabilities: dropCaps, @@ -459,6 +465,40 @@ loop: } } +func parseNetworkConfigurationPolicy(network string) buildah.NetworkConfigurationPolicy { + if val, err := strconv.Atoi(network); err == nil { + return buildah.NetworkConfigurationPolicy(val) + } + switch network { + case "NetworkDefault": + return buildah.NetworkDefault + case "NetworkDisabled": + return buildah.NetworkDisabled + case "NetworkEnabled": + return buildah.NetworkEnabled + default: + return buildah.NetworkDefault + } +} + +func parseLibPodIsolation(isolation string) buildah.Isolation { // nolint + if val, err := strconv.Atoi(isolation); err == nil { + return buildah.Isolation(val) + } + switch isolation { + case "IsolationDefault", "default": + return buildah.IsolationDefault + case "IsolationOCI": + return buildah.IsolationOCI + case "IsolationChroot": + return buildah.IsolationChroot + case "IsolationOCIRootless": + return buildah.IsolationOCIRootless + default: + return buildah.IsolationDefault + } +} + func extractTarFile(r *http.Request) (string, error) { // build a home for the request body anchorDir, err := ioutil.TempDir("", "libpod_builder") diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 01b9ec101..77269db8b 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -11,6 +11,7 @@ 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/containers/podman/v3/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -59,20 +60,21 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) { func ListContainers(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - All bool `schema:"all"` - External bool `schema:"external"` - Filters map[string][]string `schema:"filters"` - Last int `schema:"last"` // alias for limit - Limit int `schema:"limit"` - Namespace bool `schema:"namespace"` - Size bool `schema:"size"` - Sync bool `schema:"sync"` + All bool `schema:"all"` + External bool `schema:"external"` + Last int `schema:"last"` // alias for limit + Limit int `schema:"limit"` + Namespace bool `schema:"namespace"` + Size bool `schema:"size"` + Sync bool `schema:"sync"` }{ // override any golang type defaults } - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + filterMap, err := util.PrepareFilters(r) + + if dErr := decoder.Decode(&query, r.URL.Query()); dErr != nil || err != nil { + utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } @@ -94,7 +96,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { opts := entities.ContainerListOptions{ All: query.All, External: query.External, - Filters: query.Filters, + Filters: *filterMap, Last: limit, Namespace: query.Namespace, // Always return Pod, should not be part of the API. diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 83fe23621..1f306a533 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -319,18 +319,6 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { func ImagesLoad(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) - query := struct { - Reference string `schema:"reference"` - }{ - // Add defaults here once needed. - } - - 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())) - return - } tmpfile, err := ioutil.TempFile("", "libpod-images-load.tar") if err != nil { @@ -338,14 +326,15 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) { return } defer os.Remove(tmpfile.Name()) - defer tmpfile.Close() - if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF { + _, err = io.Copy(tmpfile, r.Body) + tmpfile.Close() + + if err != nil && err != io.EOF { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file")) return } - tmpfile.Close() loadedImage, err := runtime.LoadImage(context.Background(), tmpfile.Name(), os.Stderr, "") if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image")) diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 3d86e5d38..423766bd8 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -810,11 +810,14 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // summary: Load image // description: Load an image (oci-archive or docker-archive) stream. // parameters: - // - in: formData + // - in: body // name: upload - // description: tarball of container image - // type: file // required: true + // description: tarball of container image + // schema: + // type: string + // consumes: + // - application/x-tar // produces: // - application/json // responses: diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 02727e841..84cf03764 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -23,27 +23,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo case "label": // we have to match that all given labels exits on that container return func(c *libpod.Container) bool { - labels := c.Labels() - for _, filterValue := range filterValues { - matched := false - filterArray := strings.SplitN(filterValue, "=", 2) - filterKey := filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } - for labelKey, labelValue := range labels { - if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { - matched = true - break - } - } - if !matched { - return false - } - } - return true + return util.MatchLabelFilters(filterValues, c.Labels()) }, nil case "name": // we only have to match one name @@ -185,7 +165,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo return false }, nil case "until": - until, err := util.ComputeUntilTimestamp(filter, filterValues) + until, err := util.ComputeUntilTimestamp(filterValues) if err != nil { return nil, err } diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 0490a4848..9a1c7d19d 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -114,26 +114,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string) ( case "label": return func(p *libpod.Pod) bool { labels := p.Labels() - for _, filterValue := range filterValues { - matched := false - filterArray := strings.SplitN(filterValue, "=", 2) - filterKey := filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } - for labelKey, labelValue := range labels { - if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { - matched = true - break - } - } - if !matched { - return false - } - } - return true + return util.MatchLabelFilters(filterValues, labels) }, nil case "network": return func(p *libpod.Pod) bool { diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go index bc1756cf5..9b2fc5280 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/containers/podman/v3/libpod" + "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) @@ -29,21 +30,9 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { return v.Scope() == scopeVal }) case "label": - filterArray := strings.SplitN(val, "=", 2) - filterKey := filterArray[0] - var filterVal string - if len(filterArray) > 1 { - filterVal = filterArray[1] - } else { - filterVal = "" - } + filter := val vf = append(vf, func(v *libpod.Volume) bool { - for labelKey, labelValue := range v.Labels() { - if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) { - return true - } - } - return false + return util.MatchLabelFilters([]string{filter}, v.Labels()) }) case "opt": filterArray := strings.SplitN(val, "=", 2) diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index 56aac8bfd..e0e4a47a4 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -89,12 +89,28 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, // NOTE: Must happen before SECCOMP if s.Privileged { g.SetupPrivileged(true) - caplist = capabilities.AllCapabilities() + caplist, err = capabilities.BoundingSet() + if err != nil { + return err + } } else { - caplist, err = capabilities.MergeCapabilities(rtc.Containers.DefaultCapabilities, s.CapAdd, s.CapDrop) + mergedCaps, err := capabilities.MergeCapabilities(rtc.Containers.DefaultCapabilities, s.CapAdd, s.CapDrop) + if err != nil { + return err + } + boundingSet, err := capabilities.BoundingSet() if err != nil { return err } + boundingCaps := make(map[string]interface{}) + for _, b := range boundingSet { + boundingCaps[b] = b + } + for _, c := range mergedCaps { + if _, ok := boundingCaps[c]; ok { + caplist = append(caplist, c) + } + } privCapsRequired := []string{} @@ -139,10 +155,24 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec.Process.Capabilities.Permitted = caplist configSpec.Process.Capabilities.Inheritable = caplist } else { - userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) + mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ",")) } + boundingSet, err := capabilities.BoundingSet() + if err != nil { + return err + } + boundingCaps := make(map[string]interface{}) + for _, b := range boundingSet { + boundingCaps[b] = b + } + var userCaps []string + for _, c := range mergedCaps { + if _, ok := boundingCaps[c]; ok { + userCaps = append(userCaps, c) + } + } configSpec.Process.Capabilities.Effective = userCaps configSpec.Process.Capabilities.Permitted = userCaps configSpec.Process.Capabilities.Inheritable = userCaps diff --git a/pkg/util/filters.go b/pkg/util/filters.go index 51b2c5331..43bf646f1 100644 --- a/pkg/util/filters.go +++ b/pkg/util/filters.go @@ -11,11 +11,11 @@ import ( "github.com/pkg/errors" ) -// ComputeUntilTimestamp extracts unitil timestamp from filters -func ComputeUntilTimestamp(filter string, filterValues []string) (time.Time, error) { +// ComputeUntilTimestamp extracts until timestamp from filters +func ComputeUntilTimestamp(filterValues []string) (time.Time, error) { invalid := time.Time{} if len(filterValues) != 1 { - return invalid, errors.Errorf("specify exactly one timestamp for %s", filter) + return invalid, errors.Errorf("specify exactly one timestamp for until") } ts, err := timetype.GetTimestamp(filterValues[0], time.Now()) if err != nil { @@ -93,3 +93,24 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) { } return &filterMap, nil } + +// MatchLabelFilters matches labels and returs true if they are valid +func MatchLabelFilters(filterValues []string, labels map[string]string) bool { +outer: + for _, filterValue := range filterValues { + filterArray := strings.SplitN(filterValue, "=", 2) + filterKey := filterArray[0] + if len(filterArray) > 1 { + filterValue = filterArray[1] + } else { + filterValue = "" + } + for labelKey, labelValue := range labels { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { + continue outer + } + } + return false + } + return true +} diff --git a/pkg/util/filters_test.go b/pkg/util/filters_test.go new file mode 100644 index 000000000..47259013e --- /dev/null +++ b/pkg/util/filters_test.go @@ -0,0 +1,113 @@ +package util + +import ( + "testing" +) + +func TestMatchLabelFilters(t *testing.T) { + testLabels := map[string]string{ + "label1": "", + "label2": "test", + "label3": "", + } + type args struct { + filterValues []string + labels map[string]string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Match when all filters the same as labels", + args: args{ + filterValues: []string{"label1", "label3", "label2=test"}, + labels: testLabels, + }, + want: true, + }, + { + name: "Match when filter value not provided in args", + args: args{ + filterValues: []string{"label2"}, + labels: testLabels, + }, + want: true, + }, + { + name: "Match when no filter value is given", + args: args{ + filterValues: []string{"label2="}, + labels: testLabels, + }, + want: true, + }, + { + name: "Do not match when filter value differs", + args: args{ + filterValues: []string{"label2=differs"}, + labels: testLabels, + }, + want: false, + }, + { + name: "Do not match when filter value not listed in labels", + args: args{ + filterValues: []string{"label1=xyz"}, + labels: testLabels, + }, + want: false, + }, + { + name: "Do not match when one from many not ok", + args: args{ + filterValues: []string{"label1=xyz", "invalid=valid"}, + labels: testLabels, + }, + want: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + if got := MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want { + t.Errorf("MatchLabelFilters() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestComputeUntilTimestamp(t *testing.T) { + tests := []struct { + name string + args []string + wantErr bool + }{ + { + name: "Return error when more values in list", + args: []string{"5h", "6s"}, + wantErr: true, + }, + { + name: "Return error when invalid time", + args: []string{"invalidTime"}, + wantErr: true, + }, + { + name: "Do not return error when correct time format supplied", + args: []string{"44m"}, + wantErr: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + _, err := ComputeUntilTimestamp(tt.args) + if (err != nil) != tt.wantErr { + t.Errorf("ComputeUntilTimestamp() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} |