summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common.go4
-rw-r--r--cmd/podman/pull.go112
-rw-r--r--cmd/podman/shared/create.go10
-rw-r--r--cmd/podman/shared/intermediate.go1
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/source/markdown/podman-create.1.md7
-rw-r--r--go.mod4
-rw-r--r--go.sum4
-rw-r--r--libpod/container_internal.go28
-rw-r--r--pkg/api/handlers/libpod/containers.go7
-rw-r--r--pkg/api/handlers/libpod/images.go2
-rw-r--r--pkg/bindings/connection.go12
-rw-r--r--pkg/bindings/containers/containers.go17
-rw-r--r--pkg/bindings/images/images.go4
-rw-r--r--pkg/bindings/images/search.go2
-rw-r--r--pkg/bindings/pods/pods.go2
-rw-r--r--pkg/seccomp/seccomp.go54
-rw-r--r--pkg/spec/config_linux.go37
-rw-r--r--pkg/spec/config_linux_cgo.go11
-rw-r--r--pkg/spec/config_unsupported.go4
-rw-r--r--pkg/spec/createconfig.go47
-rw-r--r--pkg/spec/parse.go18
-rw-r--r--pkg/spec/spec.go6
-rw-r--r--test/e2e/run_test.go12
-rw-r--r--test/e2e/run_volume_test.go24
-rw-r--r--vendor/github.com/gorilla/mux/README.md91
-rw-r--r--vendor/github.com/gorilla/mux/context.go18
-rw-r--r--vendor/github.com/gorilla/mux/go.mod2
-rw-r--r--vendor/github.com/gorilla/mux/middleware.go25
-rw-r--r--vendor/github.com/gorilla/mux/mux.go28
-rw-r--r--vendor/github.com/gorilla/mux/regexp.go65
-rw-r--r--vendor/github.com/gorilla/mux/route.go38
-rw-r--r--vendor/github.com/gorilla/mux/test_helpers.go2
-rw-r--r--vendor/modules.txt4
34 files changed, 478 insertions, 225 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 7610edbc0..6fa2b3c71 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -257,6 +257,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"Add a host device to the container (default [])",
)
createFlags.StringSlice(
+ "device-cgroup-rule", []string{},
+ "Add a rule to the cgroup allowed devices list",
+ )
+ createFlags.StringSlice(
"device-read-bps", []string{},
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
)
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index b5957d7fe..f800d68fe 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -4,7 +4,6 @@ import (
"fmt"
"io"
"os"
- "strings"
buildahcli "github.com/containers/buildah/pkg/cli"
"github.com/containers/image/v5/docker"
@@ -15,6 +14,7 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/util"
+ "github.com/docker/distribution/reference"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -101,19 +101,32 @@ func pullCmd(c *cliconfig.PullValues) (retError error) {
}
}
- // FIXME: that's a bug. What if we pass "localhost:5000/no-tag" ?
- arr := strings.SplitN(args[0], ":", 2)
- if len(arr) == 2 {
- if c.Bool("all-tags") {
- return errors.Errorf("tag can't be used with --all-tags")
+ ctx := getContext()
+ imageName := args[0]
+
+ imageRef, err := alltransports.ParseImageName(imageName)
+ if err != nil {
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s://%s", docker.Transport.Name(), imageName))
+ if err != nil {
+ return errors.Errorf("invalid image reference %q", imageName)
}
}
- ctx := getContext()
- imgArg := args[0]
+ var writer io.Writer
+ if !c.Quiet {
+ writer = os.Stderr
+ }
+ // Special-case for docker-archive which allows multiple tags.
+ if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
+ newImage, err := runtime.LoadFromArchiveReference(getContext(), imageRef, c.SignaturePolicy, writer)
+ if err != nil {
+ return errors.Wrapf(err, "error pulling image %q", imageName)
+ }
+ fmt.Println(newImage[0].ID())
+ return nil
+ }
var registryCreds *types.DockerAuthConfig
-
if c.Flag("creds").Changed {
creds, err := util.ParseRegistryCreds(c.Creds)
if err != nil {
@@ -121,14 +134,6 @@ func pullCmd(c *cliconfig.PullValues) (retError error) {
}
registryCreds = creds
}
-
- var (
- writer io.Writer
- )
- if !c.Quiet {
- writer = os.Stderr
- }
-
dockerRegistryOptions := image.DockerRegistryOptions{
DockerRegistryCreds: registryCreds,
DockerCertPath: c.CertDir,
@@ -139,79 +144,52 @@ func pullCmd(c *cliconfig.PullValues) (retError error) {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify)
}
- // Special-case for docker-archive which allows multiple tags.
- if strings.HasPrefix(imgArg, dockerarchive.Transport.Name()+":") {
- srcRef, err := alltransports.ParseImageName(imgArg)
- if err != nil {
- return errors.Wrapf(err, "error parsing %q", imgArg)
- }
- newImage, err := runtime.LoadFromArchiveReference(getContext(), srcRef, c.SignaturePolicy, writer)
- if err != nil {
- return errors.Wrapf(err, "error pulling image from %q", imgArg)
- }
- fmt.Println(newImage[0].ID())
-
- return nil
- }
-
- // FIXME: the default pull consults the registries.conf's search registries
- // while the all-tags pull does not. This behavior must be fixed in the
- // future and span across c/buildah, c/image and c/libpod to avoid redundant
- // and error prone code.
- //
- // See https://bugzilla.redhat.com/show_bug.cgi?id=1701922 for background
- // information.
if !c.Bool("all-tags") {
- newImage, err := runtime.New(getContext(), imgArg, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
+ newImage, err := runtime.New(getContext(), imageName, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
if err != nil {
- return errors.Wrapf(err, "error pulling image %q", imgArg)
+ return errors.Wrapf(err, "error pulling image %q", imageName)
}
fmt.Println(newImage.ID())
return nil
}
- // FIXME: all-tags should use the libpod backend instead of baking its own bread.
- spec := imgArg
- systemContext := image.GetSystemContext("", c.Authfile, false)
- srcRef, err := alltransports.ParseImageName(spec)
+ // --all-tags requires the docker transport
+ if imageRef.Transport().Name() != docker.Transport.Name() {
+ return errors.New("--all-tags requires docker transport")
+ }
+
+ // all-tags doesn't work with a tagged reference, so let's check early
+ namedRef, err := reference.Parse(imageName)
if err != nil {
- dockerTransport := "docker://"
- logrus.Debugf("error parsing image name %q, trying with transport %q: %v", spec, dockerTransport, err)
- spec = dockerTransport + spec
- srcRef2, err2 := alltransports.ParseImageName(spec)
- if err2 != nil {
- return errors.Wrapf(err2, "error parsing image name %q", imgArg)
- }
- srcRef = srcRef2
+ return errors.Wrapf(err, "error parsing %q", imageName)
}
- var names []string
- if srcRef.DockerReference() == nil {
- return errors.New("Non-docker transport is currently not supported")
+ if _, isTagged := namedRef.(reference.Tagged); isTagged {
+ return errors.New("--all-tags requires a reference without a tag")
+
}
- tags, err := docker.GetRepositoryTags(ctx, systemContext, srcRef)
+
+ systemContext := image.GetSystemContext("", c.Authfile, false)
+ tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef)
if err != nil {
return errors.Wrapf(err, "error getting repository tags")
}
- for _, tag := range tags {
- name := spec + ":" + tag
- names = append(names, name)
- }
var foundIDs []string
- foundImage := true
- for _, name := range names {
+ for _, tag := range tags {
+ name := imageName + ":" + tag
newImage, err := runtime.New(getContext(), name, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
if err != nil {
logrus.Errorf("error pulling image %q", name)
- foundImage = false
continue
}
foundIDs = append(foundIDs, newImage.ID())
}
- if len(names) == 1 && !foundImage {
- return errors.Wrapf(err, "error pulling image %q", imgArg)
+
+ if len(tags) != len(foundIDs) {
+ return errors.Errorf("error pulling image %q", imageName)
}
- if len(names) > 1 {
+
+ if len(foundIDs) > 1 {
fmt.Println("Pulled Images:")
}
for _, id := range foundIDs {
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 3062b0ca3..be5adcccb 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -22,6 +22,7 @@ import (
"github.com/containers/libpod/pkg/inspect"
ns "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/seccomp"
cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/docker/go-connections/nat"
@@ -31,10 +32,6 @@ import (
"github.com/sirupsen/logrus"
)
-// seccompLabelKey is the key of the image annotation embedding a seccomp
-// profile.
-const seccompLabelKey = "io.containers.seccomp.profile"
-
func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) {
var (
healthCheck *manifest.Schema2HealthConfig
@@ -713,11 +710,11 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
// SECCOMP
if data != nil {
- if value, exists := labels[seccompLabelKey]; exists {
+ if value, exists := labels[seccomp.ContainerImageLabel]; exists {
secConfig.SeccompProfileFromImage = value
}
}
- if policy, err := cc.LookupSeccompPolicy(c.String("seccomp-policy")); err != nil {
+ if policy, err := seccomp.LookupPolicy(c.String("seccomp-policy")); err != nil {
return nil, err
} else {
secConfig.SeccompPolicy = policy
@@ -761,6 +758,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
CPURtPeriod: c.Uint64("cpu-rt-period"),
CPURtRuntime: c.Int64("cpu-rt-runtime"),
CPUs: c.Float64("cpus"),
+ DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
DeviceReadBps: c.StringSlice("device-read-bps"),
DeviceReadIOps: c.StringSlice("device-read-iops"),
DeviceWriteBps: c.StringSlice("device-write-bps"),
diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go
index cfb3f612c..ee212234f 100644
--- a/cmd/podman/shared/intermediate.go
+++ b/cmd/podman/shared/intermediate.go
@@ -386,6 +386,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["detach"] = newCRBool(c, "detach")
m["detach-keys"] = newCRString(c, "detach-keys")
m["device"] = newCRStringSlice(c, "device")
+ m["device-cgroup-rule"] = newCRStringSlice(c, "device-cgroup-rule")
m["device-read-bps"] = newCRStringSlice(c, "device-read-bps")
m["device-read-iops"] = newCRStringSlice(c, "device-read-iops")
m["device-write-bps"] = newCRStringSlice(c, "device-write-bps")
diff --git a/completions/bash/podman b/completions/bash/podman
index 99e6a3bcb..d1dcef0a4 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1873,6 +1873,7 @@ _podman_container_run() {
--cpuset-mems
--cpu-shares -c
--device
+ --device-cgroup-rule
--device-read-bps
--device-read-iops
--device-write-bps
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 977382e61..ca38be6a1 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -209,6 +209,13 @@ Note: if the user only has access rights via a group then accessing the device
from inside a rootless container will fail. The `crun` runtime offers a
workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`.
+**--device-cgroup-rule**="type major:minor mode"
+
+Add a rule to the cgroup allowed devices list. The rule is expected to be in the format specified in the Linux kernel documentation (Documentation/cgroup-v1/devices.txt):
+ - type: a (all), c (char), or b (block);
+ - major and minor: either a number, or * for all;
+ - mode: a composition of r (read), w (write), and m (mknod(2)).
+
**--device-read-bps**=*path*
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
diff --git a/go.mod b/go.mod
index 994746704..1aff5471a 100644
--- a/go.mod
+++ b/go.mod
@@ -38,7 +38,7 @@ require (
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/google/uuid v1.1.1
github.com/gorilla/handlers v1.4.2 // indirect
- github.com/gorilla/mux v1.7.3
+ github.com/gorilla/mux v1.7.4
github.com/gorilla/schema v1.1.0
github.com/hashicorp/go-multierror v1.0.0
github.com/hpcloud/tail v1.0.0
@@ -78,7 +78,7 @@ require (
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 // indirect
gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.17.2
- k8s.io/apimachinery v0.17.2
+ k8s.io/apimachinery v0.17.3
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a // indirect
)
diff --git a/go.sum b/go.sum
index 0b959646c..d7347fc55 100644
--- a/go.sum
+++ b/go.sum
@@ -213,6 +213,8 @@ github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/
github.com/gorilla/mux v0.0.0-20170217192616-94e7d24fd285/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
+github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
@@ -625,6 +627,8 @@ k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEj
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4=
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
+k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg=
+k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
k8s.io/client-go v0.0.0-20170217214107-bcde30fb7eae/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/client-go v0.0.0-20181219152756-3dd551c0f083/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g=
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 78ec09f29..216bbe669 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1383,18 +1383,34 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string)
}
if vol.state.NeedsCopyUp {
logrus.Debugf("Copying up contents from container %s to volume %s", c.ID(), vol.Name())
+
+ // Set NeedsCopyUp to false immediately, so we don't try this
+ // again when there are already files copied.
+ vol.state.NeedsCopyUp = false
+ if err := vol.save(); err != nil {
+ return nil, err
+ }
+
+ // If the volume is not empty, we should not copy up.
+ volMount := vol.MountPoint()
+ contents, err := ioutil.ReadDir(volMount)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error listing contents of volume %s mountpoint when copying up from container %s", vol.Name(), c.ID())
+ }
+ if len(contents) > 0 {
+ // The volume is not empty. It was likely modified
+ // outside of Podman. For safety, let's not copy up into
+ // it. Fixes CVE-2020-1726.
+ return vol, nil
+ }
+
srcDir, err := securejoin.SecureJoin(mountpoint, v.Dest)
if err != nil {
return nil, errors.Wrapf(err, "error calculating destination path to copy up container %s volume %s", c.ID(), vol.Name())
}
- if err := c.copyWithTarFromImage(srcDir, vol.MountPoint()); err != nil && !os.IsNotExist(err) {
+ if err := c.copyWithTarFromImage(srcDir, volMount); err != nil && !os.IsNotExist(err) {
return nil, errors.Wrapf(err, "error copying content from container %s into volume %s", c.ID(), vol.Name())
}
-
- vol.state.NeedsCopyUp = false
- if err := vol.save(); err != nil {
- return nil, err
- }
}
return vol, nil
}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index a64ed446c..e11e26510 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -56,7 +56,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
All bool `schema:"all"`
- Filter map[string][]string `schema:"filter"`
+ Filters map[string][]string `schema:"filters"`
Last int `schema:"last"`
Namespace bool `schema:"namespace"`
Pod bool `schema:"pod"`
@@ -71,6 +71,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
+
runtime := r.Context().Value("runtime").(*libpod.Runtime)
opts := shared.PsOptions{
All: query.All,
@@ -82,8 +83,8 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
Pod: query.Pod,
Sync: query.Sync,
}
- if len(query.Filter) > 0 {
- for k, v := range query.Filter {
+ if len(query.Filters) > 0 {
+ for k, v := range query.Filters {
for _, val := range v {
generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime)
if err != nil {
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index f6459f1eb..bcbe4977e 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -304,7 +304,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
return
} else if err != nil {
origErr := err
- imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s:%s", docker.Transport.Name(), query.Reference))
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s://%s", docker.Transport.Name(), query.Reference))
if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
errors.Wrapf(origErr, "reference %q must be a docker reference", query.Reference))
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 116af9709..f270060a6 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -130,7 +130,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string,
// if more desirable we could use url to form the encoded endpoint with params
r := req.URL.Query()
for k, v := range queryParams {
- r.Add(k, url.QueryEscape(v))
+ r.Add(k, v)
}
req.URL.RawQuery = r.Encode()
}
@@ -155,18 +155,14 @@ func GetConnectionFromContext(ctx context.Context) (*Connection, error) {
return conn, nil
}
-// FiltersToHTML converts our typical filter format of a
+// FiltersToString converts our typical filter format of a
// map[string][]string to a query/html safe string.
-func FiltersToHTML(filters map[string][]string) (string, error) {
+func FiltersToString(filters map[string][]string) (string, error) {
lowerCaseKeys := make(map[string][]string)
for k, v := range filters {
lowerCaseKeys[strings.ToLower(k)] = v
}
- unsafeString, err := jsoniter.MarshalToString(lowerCaseKeys)
- if err != nil {
- return "", err
- }
- return url.QueryEscape(unsafeString), nil
+ return jsoniter.MarshalToString(lowerCaseKeys)
}
// IsInformation returns true if the response code is 1xx
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 334a656d4..04f7f8802 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -5,8 +5,8 @@ import (
"net/http"
"strconv"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
)
@@ -15,13 +15,16 @@ import (
// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
// container state.
-func List(ctx context.Context, filters map[string][]string, last *int, pod, size, sync *bool) ([]*shared.PsContainerOutput, error) { // nolint:typecheck
+func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]lpapiv2.ListContainer, error) { // nolint:typecheck
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
- var images []*shared.PsContainerOutput
+ var containers []lpapiv2.ListContainer
params := make(map[string]string)
+ if all != nil {
+ params["all"] = strconv.FormatBool(*all)
+ }
if last != nil {
params["last"] = strconv.Itoa(*last)
}
@@ -35,7 +38,7 @@ func List(ctx context.Context, filters map[string][]string, last *int, pod, size
params["sync"] = strconv.FormatBool(*sync)
}
if filters != nil {
- filterString, err := bindings.FiltersToHTML(filters)
+ filterString, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
@@ -43,9 +46,9 @@ func List(ctx context.Context, filters map[string][]string, last *int, pod, size
}
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params)
if err != nil {
- return images, err
+ return containers, err
}
- return images, response.Process(nil)
+ return containers, response.Process(&containers)
}
// Prune removes stopped and exited containers from local storage. The optional filters can be
@@ -62,7 +65,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
}
params := make(map[string]string)
if filters != nil {
- filterString, err := bindings.FiltersToHTML(filters)
+ filterString, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index deaf93f0e..b19482943 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -38,7 +38,7 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handl
params["all"] = strconv.FormatBool(*all)
}
if filters != nil {
- strFilters, err := bindings.FiltersToHTML(filters)
+ strFilters, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
@@ -155,7 +155,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
}
params := make(map[string]string)
if filters != nil {
- stringFilter, err := bindings.FiltersToHTML(filters)
+ stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go
index d98ddf18d..58b25425b 100644
--- a/pkg/bindings/images/search.go
+++ b/pkg/bindings/images/search.go
@@ -26,7 +26,7 @@ func Search(ctx context.Context, term string, limit *int, filters map[string][]s
params["limit"] = strconv.Itoa(*limit)
}
if filters != nil {
- stringFilter, err := bindings.FiltersToHTML(filters)
+ stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index a6b74c21d..d079f01c2 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -97,7 +97,7 @@ func List(ctx context.Context, filters map[string][]string) (*[]libpod.PodInspec
}
params := make(map[string]string)
if filters != nil {
- stringFilter, err := bindings.FiltersToHTML(filters)
+ stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go
new file mode 100644
index 000000000..dcf255378
--- /dev/null
+++ b/pkg/seccomp/seccomp.go
@@ -0,0 +1,54 @@
+package seccomp
+
+import (
+ "sort"
+
+ "github.com/pkg/errors"
+)
+
+// ContianerImageLabel is the key of the image annotation embedding a seccomp
+// profile.
+const ContainerImageLabel = "io.containers.seccomp.profile"
+
+// Policy denotes a seccomp policy.
+type Policy int
+
+const (
+ // PolicyDefault - if set use SecurityConfig.SeccompProfilePath,
+ // otherwise use the default profile. The SeccompProfilePath might be
+ // explicitly set by the user.
+ PolicyDefault Policy = iota
+ // PolicyImage - if set use SecurityConfig.SeccompProfileFromImage,
+ // otherwise follow SeccompPolicyDefault.
+ PolicyImage
+)
+
+// Map for easy lookups of supported policies.
+var supportedPolicies = map[string]Policy{
+ "": PolicyDefault,
+ "default": PolicyDefault,
+ "image": PolicyImage,
+}
+
+// LookupPolicy looksup the corresponding Policy for the specified
+// string. If none is found, an errors is returned including the list of
+// supported policies.
+//
+// Note that an empty string resolved to SeccompPolicyDefault.
+func LookupPolicy(s string) (Policy, error) {
+ policy, exists := supportedPolicies[s]
+ if exists {
+ return policy, nil
+ }
+
+ // Sort the keys first as maps are non-deterministic.
+ keys := []string{}
+ for k := range supportedPolicies {
+ if k != "" {
+ keys = append(keys, k)
+ }
+ }
+ sort.Strings(keys)
+
+ return -1, errors.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys)
+}
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index 32d8cb4de..5f39b6d0d 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strconv"
"strings"
"github.com/containers/libpod/pkg/rootless"
@@ -90,6 +91,42 @@ func devicesFromPath(g *generate.Generator, devicePath string) error {
return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
}
+func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
+ for _, deviceCgroupRule := range deviceCgroupRules {
+ if err := validateDeviceCgroupRule(deviceCgroupRule); err != nil {
+ return err
+ }
+ ss := parseDeviceCgroupRule(deviceCgroupRule)
+ if len(ss[0]) != 5 {
+ return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ matches := ss[0]
+ var major, minor *int64
+ if matches[2] == "*" {
+ majorDev := int64(-1)
+ major = &majorDev
+ } else {
+ majorDev, err := strconv.ParseInt(matches[2], 10, 64)
+ if err != nil {
+ return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ major = &majorDev
+ }
+ if matches[3] == "*" {
+ minorDev := int64(-1)
+ minor = &minorDev
+ } else {
+ minorDev, err := strconv.ParseInt(matches[2], 10, 64)
+ if err != nil {
+ return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ minor = &minorDev
+ }
+ g.AddLinuxResourcesDevice(true, matches[1], major, minor, matches[4])
+ }
+ return nil
+}
+
func addDevice(g *generate.Generator, device string) error {
src, dst, permissions, err := ParseDevice(device)
if err != nil {
diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go
index ae83c9d52..05f42c4da 100644
--- a/pkg/spec/config_linux_cgo.go
+++ b/pkg/spec/config_linux_cgo.go
@@ -5,9 +5,10 @@ package createconfig
import (
"io/ioutil"
+ "github.com/containers/libpod/pkg/seccomp"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
- seccomp "github.com/seccomp/containers-golang"
+ goSeccomp "github.com/seccomp/containers-golang"
"github.com/sirupsen/logrus"
)
@@ -15,9 +16,9 @@ func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.Linu
var seccompConfig *spec.LinuxSeccomp
var err error
- if config.SeccompPolicy == SeccompPolicyImage && config.SeccompProfileFromImage != "" {
+ if config.SeccompPolicy == seccomp.PolicyImage && config.SeccompProfileFromImage != "" {
logrus.Debug("Loading seccomp profile from the security config")
- seccompConfig, err = seccomp.LoadProfile(config.SeccompProfileFromImage, configSpec)
+ seccompConfig, err = goSeccomp.LoadProfile(config.SeccompProfileFromImage, configSpec)
if err != nil {
return nil, errors.Wrap(err, "loading seccomp profile failed")
}
@@ -30,13 +31,13 @@ func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.Linu
if err != nil {
return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath)
}
- seccompConfig, err = seccomp.LoadProfile(string(seccompProfile), configSpec)
+ seccompConfig, err = goSeccomp.LoadProfile(string(seccompProfile), configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
} else {
logrus.Debug("Loading default seccomp profile")
- seccompConfig, err = seccomp.GetDefaultProfile(configSpec)
+ seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
diff --git a/pkg/spec/config_unsupported.go b/pkg/spec/config_unsupported.go
index a2c7f4416..be3e7046d 100644
--- a/pkg/spec/config_unsupported.go
+++ b/pkg/spec/config_unsupported.go
@@ -30,3 +30,7 @@ func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrott
func devicesFromPath(g *generate.Generator, devicePath string) error {
return errors.New("function not implemented")
}
+
+func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
+ return errors.New("function not implemented")
+}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index fb222083b..8010be0d4 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -2,7 +2,6 @@ package createconfig
import (
"os"
- "sort"
"strconv"
"strings"
"syscall"
@@ -11,6 +10,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/seccomp"
"github.com/containers/storage"
"github.com/docker/go-connections/nat"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -38,6 +38,7 @@ type CreateResourceConfig struct {
CPUs float64 // cpus
CPUsetCPUs string
CPUsetMems string // cpuset-mems
+ DeviceCgroupRules []string //device-cgroup-rule
DeviceReadBps []string // device-read-bps
DeviceReadIOps []string // device-read-iops
DeviceWriteBps []string // device-write-bps
@@ -107,48 +108,6 @@ type NetworkConfig struct {
PublishAll bool //publish-all
}
-// SeccompPolicy determines which seccomp profile gets applied to the container.
-type SeccompPolicy int
-
-const (
- // SeccompPolicyDefault - if set use SecurityConfig.SeccompProfilePath,
- // otherwise use the default profile. The SeccompProfilePath might be
- // explicitly set by the user.
- SeccompPolicyDefault SeccompPolicy = iota
- // SeccompPolicyImage - if set use SecurityConfig.SeccompProfileFromImage,
- // otherwise follow SeccompPolicyDefault.
- SeccompPolicyImage
-)
-
-// Map for easy lookups of supported policies.
-var supportedSeccompPolicies = map[string]SeccompPolicy{
- "": SeccompPolicyDefault,
- "default": SeccompPolicyDefault,
- "image": SeccompPolicyImage,
-}
-
-// LookupSeccompPolicy looksup the corresponding SeccompPolicy for the specified
-// string. If none is found, an errors is returned including the list of
-// supported policies.
-// Note that an empty string resolved to SeccompPolicyDefault.
-func LookupSeccompPolicy(s string) (SeccompPolicy, error) {
- policy, exists := supportedSeccompPolicies[s]
- if exists {
- return policy, nil
- }
-
- // Sort the keys first as maps are non-deterministic.
- keys := []string{}
- for k := range supportedSeccompPolicies {
- if k != "" {
- keys = append(keys, k)
- }
- }
- sort.Strings(keys)
-
- return -1, errors.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys)
-}
-
// SecurityConfig configures the security features for the container
type SecurityConfig struct {
CapAdd []string // cap-add
@@ -158,7 +117,7 @@ type SecurityConfig struct {
ApparmorProfile string //SecurityOpts
SeccompProfilePath string //SecurityOpts
SeccompProfileFromImage string // seccomp profile from the container image
- SeccompPolicy SeccompPolicy
+ SeccompPolicy seccomp.Policy
SecurityOpts []string
Privileged bool //privileged
ReadOnlyRootfs bool //read-only
diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go
index 6fa0b0636..a5dfccdb9 100644
--- a/pkg/spec/parse.go
+++ b/pkg/spec/parse.go
@@ -2,12 +2,17 @@ package createconfig
import (
"fmt"
+ "regexp"
"strconv"
"strings"
"github.com/docker/go-units"
+ "github.com/pkg/errors"
)
+// deviceCgroupRulegex defines the valid format of device-cgroup-rule
+var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`)
+
// Pod signifies a kernel namespace is being shared
// by a container with the pod it is associated with
const Pod = "pod"
@@ -205,3 +210,16 @@ func IsValidDeviceMode(mode string) bool {
}
return true
}
+
+// validateDeviceCgroupRule validates the format of deviceCgroupRule
+func validateDeviceCgroupRule(deviceCgroupRule string) error {
+ if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) {
+ return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ return nil
+}
+
+// parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice
+func parseDeviceCgroupRule(deviceCgroupRule string) [][]string {
+ return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
+}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index cae055bb0..b2a152a2d 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -232,6 +232,12 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
return nil, err
}
}
+ if len(config.Resources.DeviceCgroupRules) != 0 {
+ if err := deviceCgroupRules(&g, config.Resources.DeviceCgroupRules); err != nil {
+ return nil, err
+ }
+ addedResources = true
+ }
}
// SECURITY OPTS
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 72547ea00..3eb93b84a 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -999,4 +999,16 @@ USER mail`
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+
+ It("podman run --device-cgroup-rule", func() {
+ SkipIfRemote()
+ SkipIfRootless()
+ deviceCgroupRule := "c 42:* rwm"
+ session := podmanTest.Podman([]string{"run", "--name", "test", "-d", "--device-cgroup-rule", deviceCgroupRule, ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "test", "mknod", "newDev", "c", "42", "1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index 0c2389e40..46c27dc2e 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -397,4 +397,28 @@ var _ = Describe("Podman run with volumes", func() {
volMount.WaitWithDefaultTimeout()
Expect(volMount.ExitCode()).To(Not(Equal(0)))
})
+
+ It("Podman fix for CVE-2020-1726", func() {
+ volName := "testVol"
+ volCreate := podmanTest.Podman([]string{"volume", "create", volName})
+ volCreate.WaitWithDefaultTimeout()
+ Expect(volCreate.ExitCode()).To(Equal(0))
+
+ volPath := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Mountpoint}}", volName})
+ volPath.WaitWithDefaultTimeout()
+ Expect(volPath.ExitCode()).To(Equal(0))
+ path := volPath.OutputToString()
+
+ fileName := "thisIsATestFile"
+ file, err := os.Create(filepath.Join(path, fileName))
+ Expect(err).To(BeNil())
+ defer file.Close()
+
+ runLs := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "-v", fmt.Sprintf("%v:/etc/ssl", volName), ALPINE, "ls", "-1", "/etc/ssl"})
+ runLs.WaitWithDefaultTimeout()
+ Expect(runLs.ExitCode()).To(Equal(0))
+ outputArr := runLs.OutputToStringArray()
+ Expect(len(outputArr)).To(Equal(1))
+ Expect(strings.Contains(outputArr[0], fileName)).To(BeTrue())
+ })
})
diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md
index 92e422eed..35eea9f10 100644
--- a/vendor/github.com/gorilla/mux/README.md
+++ b/vendor/github.com/gorilla/mux/README.md
@@ -1,11 +1,10 @@
# gorilla/mux
[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
-[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux)
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
-![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
+![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png)
https://www.gorillatoolkit.org/pkg/mux
@@ -26,6 +25,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv
* [Examples](#examples)
* [Matching Routes](#matching-routes)
* [Static Files](#static-files)
+* [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.)
* [Registered URLs](#registered-urls)
* [Walking Routes](#walking-routes)
* [Graceful Shutdown](#graceful-shutdown)
@@ -212,6 +212,93 @@ func main() {
}
```
+### Serving Single Page Applications
+
+Most of the time it makes sense to serve your SPA on a separate web server from your API,
+but sometimes it's desirable to serve them both from one place. It's possible to write a simple
+handler for serving your SPA (for use with React Router's [BrowserRouter](https://reacttraining.com/react-router/web/api/BrowserRouter) for example), and leverage
+mux's powerful routing for your API endpoints.
+
+```go
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/gorilla/mux"
+)
+
+// spaHandler implements the http.Handler interface, so we can use it
+// to respond to HTTP requests. The path to the static directory and
+// path to the index file within that static directory are used to
+// serve the SPA in the given static directory.
+type spaHandler struct {
+ staticPath string
+ indexPath string
+}
+
+// ServeHTTP inspects the URL path to locate a file within the static dir
+// on the SPA handler. If a file is found, it will be served. If not, the
+// file located at the index path on the SPA handler will be served. This
+// is suitable behavior for serving an SPA (single page application).
+func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // get the absolute path to prevent directory traversal
+ path, err := filepath.Abs(r.URL.Path)
+ if err != nil {
+ // if we failed to get the absolute path respond with a 400 bad request
+ // and stop
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ // prepend the path with the path to the static directory
+ path = filepath.Join(h.staticPath, path)
+
+ // check whether a file exists at the given path
+ _, err = os.Stat(path)
+ if os.IsNotExist(err) {
+ // file does not exist, serve index.html
+ http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
+ return
+ } else if err != nil {
+ // if we got an error (that wasn't that the file doesn't exist) stating the
+ // file, return a 500 internal server error and stop
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // otherwise, use http.FileServer to serve the static dir
+ http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
+}
+
+func main() {
+ router := mux.NewRouter()
+
+ router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
+ // an example API handler
+ json.NewEncoder(w).Encode(map[string]bool{"ok": true})
+ })
+
+ spa := spaHandler{staticPath: "build", indexPath: "index.html"}
+ router.PathPrefix("/").Handler(spa)
+
+ srv := &http.Server{
+ Handler: router,
+ Addr: "127.0.0.1:8000",
+ // Good practice: enforce timeouts for servers you create!
+ WriteTimeout: 15 * time.Second,
+ ReadTimeout: 15 * time.Second,
+ }
+
+ log.Fatal(srv.ListenAndServe())
+}
+```
+
### Registered URLs
Now let's see how to build registered URLs.
diff --git a/vendor/github.com/gorilla/mux/context.go b/vendor/github.com/gorilla/mux/context.go
deleted file mode 100644
index 665940a26..000000000
--- a/vendor/github.com/gorilla/mux/context.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package mux
-
-import (
- "context"
- "net/http"
-)
-
-func contextGet(r *http.Request, key interface{}) interface{} {
- return r.Context().Value(key)
-}
-
-func contextSet(r *http.Request, key, val interface{}) *http.Request {
- if val == nil {
- return r
- }
-
- return r.WithContext(context.WithValue(r.Context(), key, val))
-}
diff --git a/vendor/github.com/gorilla/mux/go.mod b/vendor/github.com/gorilla/mux/go.mod
index cfc8ede58..df170a399 100644
--- a/vendor/github.com/gorilla/mux/go.mod
+++ b/vendor/github.com/gorilla/mux/go.mod
@@ -1 +1,3 @@
module github.com/gorilla/mux
+
+go 1.12
diff --git a/vendor/github.com/gorilla/mux/middleware.go b/vendor/github.com/gorilla/mux/middleware.go
index cf2b26dc0..cb51c565e 100644
--- a/vendor/github.com/gorilla/mux/middleware.go
+++ b/vendor/github.com/gorilla/mux/middleware.go
@@ -58,22 +58,17 @@ func CORSMethodMiddleware(r *Router) MiddlewareFunc {
func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) {
var allMethods []string
- err := r.Walk(func(route *Route, _ *Router, _ []*Route) error {
- for _, m := range route.matchers {
- if _, ok := m.(*routeRegexp); ok {
- if m.Match(req, &RouteMatch{}) {
- methods, err := route.GetMethods()
- if err != nil {
- return err
- }
-
- allMethods = append(allMethods, methods...)
- }
- break
+ for _, route := range r.routes {
+ var match RouteMatch
+ if route.Match(req, &match) || match.MatchErr == ErrMethodMismatch {
+ methods, err := route.GetMethods()
+ if err != nil {
+ return nil, err
}
+
+ allMethods = append(allMethods, methods...)
}
- return nil
- })
+ }
- return allMethods, err
+ return allMethods, nil
}
diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go
index a2cd193e4..c9ba64707 100644
--- a/vendor/github.com/gorilla/mux/mux.go
+++ b/vendor/github.com/gorilla/mux/mux.go
@@ -5,6 +5,7 @@
package mux
import (
+ "context"
"errors"
"fmt"
"net/http"
@@ -58,8 +59,7 @@ type Router struct {
// If true, do not clear the request context after handling the request.
//
- // Deprecated: No effect when go1.7+ is used, since the context is stored
- // on the request itself.
+ // Deprecated: No effect, since the context is stored on the request itself.
KeepContext bool
// Slice of middlewares to be called after a match is found
@@ -111,10 +111,8 @@ func copyRouteConf(r routeConf) routeConf {
c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
}
- c.matchers = make([]matcher, 0, len(r.matchers))
- for _, m := range r.matchers {
- c.matchers = append(c.matchers, m)
- }
+ c.matchers = make([]matcher, len(r.matchers))
+ copy(c.matchers, r.matchers)
return c
}
@@ -197,8 +195,8 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var handler http.Handler
if r.Match(req, &match) {
handler = match.Handler
- req = setVars(req, match.Vars)
- req = setCurrentRoute(req, match.Route)
+ req = requestWithVars(req, match.Vars)
+ req = requestWithRoute(req, match.Route)
}
if handler == nil && match.MatchErr == ErrMethodMismatch {
@@ -428,7 +426,7 @@ const (
// Vars returns the route variables for the current request, if any.
func Vars(r *http.Request) map[string]string {
- if rv := contextGet(r, varsKey); rv != nil {
+ if rv := r.Context().Value(varsKey); rv != nil {
return rv.(map[string]string)
}
return nil
@@ -440,18 +438,20 @@ func Vars(r *http.Request) map[string]string {
// after the handler returns, unless the KeepContext option is set on the
// Router.
func CurrentRoute(r *http.Request) *Route {
- if rv := contextGet(r, routeKey); rv != nil {
+ if rv := r.Context().Value(routeKey); rv != nil {
return rv.(*Route)
}
return nil
}
-func setVars(r *http.Request, val interface{}) *http.Request {
- return contextSet(r, varsKey, val)
+func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
+ ctx := context.WithValue(r.Context(), varsKey, vars)
+ return r.WithContext(ctx)
}
-func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
- return contextSet(r, routeKey, val)
+func requestWithRoute(r *http.Request, route *Route) *http.Request {
+ ctx := context.WithValue(r.Context(), routeKey, route)
+ return r.WithContext(ctx)
}
// ----------------------------------------------------------------------------
diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go
index ac1abcd47..96dd94ad1 100644
--- a/vendor/github.com/gorilla/mux/regexp.go
+++ b/vendor/github.com/gorilla/mux/regexp.go
@@ -181,21 +181,21 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
}
}
return r.regexp.MatchString(host)
- } else {
- if r.regexpType == regexpTypeQuery {
- return r.matchQueryString(req)
- }
- path := req.URL.Path
- if r.options.useEncodedPath {
- path = req.URL.EscapedPath()
- }
- return r.regexp.MatchString(path)
}
+
+ if r.regexpType == regexpTypeQuery {
+ return r.matchQueryString(req)
+ }
+ path := req.URL.Path
+ if r.options.useEncodedPath {
+ path = req.URL.EscapedPath()
+ }
+ return r.regexp.MatchString(path)
}
// url builds a URL part using the given values.
func (r *routeRegexp) url(values map[string]string) (string, error) {
- urlValues := make([]interface{}, len(r.varsN))
+ urlValues := make([]interface{}, len(r.varsN), len(r.varsN))
for k, v := range r.varsN {
value, ok := values[v]
if !ok {
@@ -230,14 +230,51 @@ func (r *routeRegexp) getURLQuery(req *http.Request) string {
return ""
}
templateKey := strings.SplitN(r.template, "=", 2)[0]
- for key, vals := range req.URL.Query() {
- if key == templateKey && len(vals) > 0 {
- return key + "=" + vals[0]
- }
+ val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey)
+ if ok {
+ return templateKey + "=" + val
}
return ""
}
+// findFirstQueryKey returns the same result as (*url.URL).Query()[key][0].
+// If key was not found, empty string and false is returned.
+func findFirstQueryKey(rawQuery, key string) (value string, ok bool) {
+ query := []byte(rawQuery)
+ for len(query) > 0 {
+ foundKey := query
+ if i := bytes.IndexAny(foundKey, "&;"); i >= 0 {
+ foundKey, query = foundKey[:i], foundKey[i+1:]
+ } else {
+ query = query[:0]
+ }
+ if len(foundKey) == 0 {
+ continue
+ }
+ var value []byte
+ if i := bytes.IndexByte(foundKey, '='); i >= 0 {
+ foundKey, value = foundKey[:i], foundKey[i+1:]
+ }
+ if len(foundKey) < len(key) {
+ // Cannot possibly be key.
+ continue
+ }
+ keyString, err := url.QueryUnescape(string(foundKey))
+ if err != nil {
+ continue
+ }
+ if keyString != key {
+ continue
+ }
+ valueString, err := url.QueryUnescape(string(value))
+ if err != nil {
+ continue
+ }
+ return valueString, true
+ }
+ return "", false
+}
+
func (r *routeRegexp) matchQueryString(req *http.Request) bool {
return r.regexp.MatchString(r.getURLQuery(req))
}
diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go
index 8479c68c1..750afe570 100644
--- a/vendor/github.com/gorilla/mux/route.go
+++ b/vendor/github.com/gorilla/mux/route.go
@@ -74,7 +74,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
return false
}
- if match.MatchErr == ErrMethodMismatch {
+ if match.MatchErr == ErrMethodMismatch && r.handler != nil {
// We found a route which matches request method, clear MatchErr
match.MatchErr = nil
// Then override the mis-matched handler
@@ -412,11 +412,30 @@ func (r *Route) Queries(pairs ...string) *Route {
type schemeMatcher []string
func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchInArray(m, r.URL.Scheme)
+ scheme := r.URL.Scheme
+ // https://golang.org/pkg/net/http/#Request
+ // "For [most] server requests, fields other than Path and RawQuery will be
+ // empty."
+ // Since we're an http muxer, the scheme is either going to be http or https
+ // though, so we can just set it based on the tls termination state.
+ if scheme == "" {
+ if r.TLS == nil {
+ scheme = "http"
+ } else {
+ scheme = "https"
+ }
+ }
+ return matchInArray(m, scheme)
}
// Schemes adds a matcher for URL schemes.
// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
+// If the request's URL has a scheme set, it will be matched against.
+// Generally, the URL scheme will only be set if a previous handler set it,
+// such as the ProxyHeaders handler from gorilla/handlers.
+// If unset, the scheme will be determined based on the request's TLS
+// termination state.
+// The first argument to Schemes will be used when constructing a route URL.
func (r *Route) Schemes(schemes ...string) *Route {
for k, v := range schemes {
schemes[k] = strings.ToLower(v)
@@ -493,8 +512,8 @@ func (r *Route) Subrouter() *Router {
// This also works for host variables:
//
// r := mux.NewRouter()
-// r.Host("{subdomain}.domain.com").
-// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+// Host("{subdomain}.domain.com").
// Name("article")
//
// // url.String() will be "http://news.domain.com/articles/technology/42"
@@ -502,6 +521,13 @@ func (r *Route) Subrouter() *Router {
// "category", "technology",
// "id", "42")
//
+// The scheme of the resulting url will be the first argument that was passed to Schemes:
+//
+// // url.String() will be "https://example.com"
+// r := mux.NewRouter()
+// url, err := r.Host("example.com")
+// .Schemes("https", "http").URL()
+//
// All variables defined in the route are required, and their values must
// conform to the corresponding patterns.
func (r *Route) URL(pairs ...string) (*url.URL, error) {
@@ -635,7 +661,7 @@ func (r *Route) GetQueriesRegexp() ([]string, error) {
if r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries")
}
- var queries []string
+ queries := make([]string, 0, len(r.regexp.queries))
for _, query := range r.regexp.queries {
queries = append(queries, query.regexp.String())
}
@@ -654,7 +680,7 @@ func (r *Route) GetQueriesTemplates() ([]string, error) {
if r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries")
}
- var queries []string
+ queries := make([]string, 0, len(r.regexp.queries))
for _, query := range r.regexp.queries {
queries = append(queries, query.template)
}
diff --git a/vendor/github.com/gorilla/mux/test_helpers.go b/vendor/github.com/gorilla/mux/test_helpers.go
index 32ecffde4..5f5c496de 100644
--- a/vendor/github.com/gorilla/mux/test_helpers.go
+++ b/vendor/github.com/gorilla/mux/test_helpers.go
@@ -15,5 +15,5 @@ import "net/http"
// can be set by making a route that captures the required variables,
// starting a server and sending the request to that server.
func SetURLVars(r *http.Request, val map[string]string) *http.Request {
- return setVars(r, val)
+ return requestWithVars(r, val)
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 5c2485f38..a5fd1e65e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -291,7 +291,7 @@ github.com/google/gofuzz
github.com/google/shlex
# github.com/google/uuid v1.1.1
github.com/google/uuid
-# github.com/gorilla/mux v1.7.3
+# github.com/gorilla/mux v1.7.4
github.com/gorilla/mux
# github.com/gorilla/schema v1.1.0
github.com/gorilla/schema
@@ -632,7 +632,7 @@ gopkg.in/tomb.v1
gopkg.in/yaml.v2
# k8s.io/api v0.17.2
k8s.io/api/core/v1
-# k8s.io/apimachinery v0.17.2
+# k8s.io/apimachinery v0.17.3
k8s.io/apimachinery/pkg/api/errors
k8s.io/apimachinery/pkg/api/resource
k8s.io/apimachinery/pkg/apis/meta/v1