summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers_attach.go6
-rw-r--r--pkg/api/handlers/compat/containers_create.go13
-rw-r--r--pkg/api/handlers/compat/exec.go6
-rw-r--r--pkg/api/handlers/compat/images.go12
-rw-r--r--pkg/api/handlers/compat/images_build.go66
-rw-r--r--pkg/api/handlers/compat/images_push.go4
-rw-r--r--pkg/api/handlers/compat/volumes.go23
-rw-r--r--pkg/api/handlers/libpod/images.go51
-rw-r--r--pkg/api/handlers/libpod/images_pull.go4
-rw-r--r--pkg/api/handlers/libpod/manifests.go17
-rw-r--r--pkg/api/handlers/libpod/networks.go4
-rw-r--r--pkg/api/handlers/libpod/play.go4
-rw-r--r--pkg/api/server/handler_api.go23
-rw-r--r--pkg/api/server/idle/tracker.go96
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/register_images.go9
-rw-r--r--pkg/api/server/register_volumes.go4
-rw-r--r--pkg/api/server/server.go22
-rw-r--r--pkg/auth/auth.go189
-rw-r--r--pkg/bindings/images/build.go25
-rw-r--r--pkg/bindings/images/images.go4
-rw-r--r--pkg/bindings/images/pull.go2
-rw-r--r--pkg/bindings/play/play.go2
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/pods.go1
-rw-r--r--pkg/domain/entities/volumes.go4
-rw-r--r--pkg/domain/infra/abi/images.go6
-rw-r--r--pkg/domain/infra/abi/manifest.go85
-rw-r--r--pkg/domain/infra/abi/network.go3
-rw-r--r--pkg/domain/infra/abi/volumes.go2
-rw-r--r--pkg/domain/infra/tunnel/helpers.go92
-rw-r--r--pkg/domain/infra/tunnel/images.go8
-rw-r--r--pkg/domain/infra/tunnel/volumes.go2
-rw-r--r--pkg/registries/registries.go5
-rw-r--r--pkg/spec/config_linux.go3
-rw-r--r--pkg/spec/spec.go19
-rw-r--r--pkg/specgen/generate/config_linux.go7
-rw-r--r--pkg/specgen/generate/container.go43
-rw-r--r--pkg/specgen/generate/pod_create.go3
-rw-r--r--pkg/specgen/generate/security.go49
-rw-r--r--pkg/specgen/pod_validate.go4
-rw-r--r--pkg/specgen/podspecgen.go3
42 files changed, 722 insertions, 279 deletions
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index e20d48d86..4a1196c89 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -6,7 +6,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -92,7 +92,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
// Perform HTTP attach.
@@ -109,7 +109,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 1d0b4c45d..0579da8de 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -82,7 +82,13 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input
}
}
- workDir := "/"
+ workDir, err := newImage.WorkingDir(ctx)
+ if err != nil {
+ return createconfig.CreateConfig{}, err
+ }
+ if workDir == "" {
+ workDir = "/"
+ }
if len(input.WorkingDir) > 0 {
workDir = input.WorkingDir
}
@@ -169,6 +175,11 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input
// away incorrectly formatted variables so we cannot reuse the
// parsing of the env input
// [Foo Other=one Blank=]
+ imgEnv, err := newImage.Env(ctx)
+ if err != nil {
+ return createconfig.CreateConfig{}, err
+ }
+ input.Env = append(imgEnv, input.Env...)
for _, e := range input.Env {
splitEnv := strings.Split(e, "=")
switch len(splitEnv) {
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index 1db950f85..df51293c2 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -10,7 +10,7 @@ import (
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/containers/podman/v2/pkg/specgen/generate"
"github.com/gorilla/mux"
"github.com/pkg/errors"
@@ -174,7 +174,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan); err != nil {
@@ -186,7 +186,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index 0900d1793..940b57343 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -93,7 +93,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
})
}
- //FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden
+ // FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden
ipr := types.ImagesPruneReport{
ImagesDeleted: idr,
SpaceReclaimed: 1, // TODO we cannot supply this right now
@@ -113,7 +113,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
Changes string `schema:"changes"`
Comment string `schema:"comment"`
Container string `schema:"container"`
- //fromSrc string # fromSrc is currently unused
+ // fromSrc string # fromSrc is currently unused
Pause bool `schema:"pause"`
Repo string `schema:"repo"`
Tag string `schema:"tag"`
@@ -224,7 +224,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) {
Status string `json:"status"`
Progress string `json:"progress"`
ProgressDetail map[string]string `json:"progressDetail"`
- Id string `json:"id"` //nolint
+ Id string `json:"id"` // nolint
}{
Status: iid,
ProgressDetail: map[string]string{},
@@ -257,9 +257,9 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
fromImage = fmt.Sprintf("%s:%s", fromImage, query.Tag)
}
- authConf, authfile, err := auth.GetCredentials(r)
+ authConf, authfile, key, err := auth.GetCredentials(r)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
@@ -299,7 +299,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
Error string `json:"error"`
Progress string `json:"progress"`
ProgressDetail map[string]string `json:"progressDetail"`
- Id string `json:"id"` //nolint
+ Id string `json:"id"` // nolint
}{
Status: fmt.Sprintf("pulling image (%s) from %s", img.Tag, strings.Join(img.Names(), ", ")),
ProgressDetail: map[string]string{},
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index fbaf8d10a..d5ccf56fe 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -2,7 +2,6 @@ package compat
import (
"context"
- "encoding/base64"
"encoding/json"
"fmt"
"io"
@@ -11,13 +10,13 @@ import (
"os"
"path/filepath"
"strconv"
- "strings"
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
+ "github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod"
- "github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/auth"
"github.com/containers/podman/v2/pkg/channel"
"github.com/containers/storage/pkg/archive"
"github.com/gorilla/schema"
@@ -26,15 +25,6 @@ import (
)
func BuildImage(w http.ResponseWriter, r *http.Request) {
- authConfigs := map[string]handlers.AuthConfig{}
- if hdr, found := r.Header["X-Registry-Config"]; found && len(hdr) > 0 {
- authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(hdr[0]))
- if json.NewDecoder(authConfigsJSON).Decode(&authConfigs) != nil {
- utils.BadRequest(w, "X-Registry-Config", hdr[0], json.NewDecoder(authConfigsJSON).Decode(&authConfigs))
- return
- }
- }
-
if hdr, found := r.Header["Content-Type"]; found && len(hdr) > 0 {
contentType := hdr[0]
switch contentType {
@@ -70,37 +60,38 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}()
query := struct {
+ BuildArgs string `schema:"buildargs"`
+ CacheFrom string `schema:"cachefrom"`
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
Dockerfile string `schema:"dockerfile"`
- Tag []string `schema:"t"`
ExtraHosts string `schema:"extrahosts"`
- Remote string `schema:"remote"`
- Quiet bool `schema:"q"`
+ ForceRm bool `schema:"forcerm"`
+ HTTPProxy bool `schema:"httpproxy"`
+ Labels string `schema:"labels"`
+ MemSwap int64 `schema:"memswap"`
+ Memory int64 `schema:"memory"`
+ NetworkMode string `schema:"networkmode"`
NoCache bool `schema:"nocache"`
- CacheFrom string `schema:"cachefrom"`
+ Outputs string `schema:"outputs"`
+ Platform string `schema:"platform"`
Pull bool `schema:"pull"`
+ Quiet bool `schema:"q"`
+ Registry string `schema:"registry"`
+ Remote string `schema:"remote"`
Rm bool `schema:"rm"`
- ForceRm bool `schema:"forcerm"`
- Memory int64 `schema:"memory"`
- MemSwap int64 `schema:"memswap"`
- CpuShares uint64 `schema:"cpushares"` // nolint
- CpuSetCpus string `schema:"cpusetcpus"` // nolint
- CpuPeriod uint64 `schema:"cpuperiod"` // nolint
- CpuQuota int64 `schema:"cpuquota"` // nolint
- BuildArgs string `schema:"buildargs"`
ShmSize int `schema:"shmsize"`
Squash bool `schema:"squash"`
- Labels string `schema:"labels"`
- NetworkMode string `schema:"networkmode"`
- Platform string `schema:"platform"`
+ Tag []string `schema:"t"`
Target string `schema:"target"`
- Outputs string `schema:"outputs"`
- Registry string `schema:"registry"`
}{
Dockerfile: "Dockerfile",
- Tag: []string{},
+ Registry: "docker.io",
Rm: true,
ShmSize: 64 * 1024 * 1024,
- Registry: "docker.io",
+ Tag: []string{},
}
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -151,6 +142,14 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
}
+ creds, authfile, key, err := auth.GetCredentials(r)
+ if err != nil {
+ // Credential value(s) not returned as their value is not human readable
+ utils.BadRequest(w, key.String(), "n/a", err)
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+
// Channels all mux'ed in select{} below to follow API build protocol
stdout := channel.NewWriter(make(chan []byte, 1))
defer stdout.Close()
@@ -179,11 +178,16 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Err: auxout,
ReportWriter: reporter,
OutputFormat: buildah.Dockerv2ImageManifest,
+ SystemContext: &types.SystemContext{
+ AuthFilePath: authfile,
+ DockerAuthConfig: creds,
+ },
CommonBuildOpts: &buildah.CommonBuildOptions{
CPUPeriod: query.CpuPeriod,
CPUQuota: query.CpuQuota,
CPUShares: query.CpuShares,
CPUSetCPUs: query.CpuSetCpus,
+ HTTPProxy: query.HTTPProxy,
Memory: query.Memory,
MemorySwap: query.MemSwap,
ShmSize: strconv.Itoa(query.ShmSize),
diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go
index e69a2212a..dd706a156 100644
--- a/pkg/api/handlers/compat/images_push.go
+++ b/pkg/api/handlers/compat/images_push.go
@@ -49,9 +49,9 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
return
}
- authConf, authfile, err := auth.GetCredentials(r)
+ authConf, authfile, key, err := auth.GetCredentials(r)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go
index 976c52acb..a45509fdb 100644
--- a/pkg/api/handlers/compat/volumes.go
+++ b/pkg/api/handlers/compat/volumes.go
@@ -93,6 +93,29 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
return
}
+ // See if the volume exists already
+ existingVolume, err := runtime.GetVolume(input.Name)
+ if err != nil && errors.Cause(err) != define.ErrNoSuchVolume {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ // if using the compat layer and the volume already exists, we
+ // must return a 201 with the same information as create
+ if existingVolume != nil && !utils.IsLibpodRequest(r) {
+ response := docker_api_types.Volume{
+ CreatedAt: existingVolume.CreatedTime().Format(time.RFC3339),
+ Driver: existingVolume.Driver(),
+ Labels: existingVolume.Labels(),
+ Mountpoint: existingVolume.MountPoint(),
+ Name: existingVolume.Name(),
+ Options: existingVolume.Options(),
+ Scope: existingVolume.Scope(),
+ }
+ utils.WriteResponse(w, http.StatusCreated, response)
+ return
+ }
+
if len(input.Name) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeName(input.Name))
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index bc1bdc287..3054922c2 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -438,9 +438,9 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
return
}
- authConf, authfile, err := auth.GetCredentials(r)
+ authConf, authfile, key, err := auth.GetCredentials(r)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
@@ -560,24 +560,41 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
func UntagImage(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := utils.GetName(r)
- newImage, err := runtime.ImageRuntime().NewFromLocal(name)
- if err != nil {
- utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
- return
- }
- tag := "latest"
- if len(r.Form.Get("tag")) > 0 {
- tag = r.Form.Get("tag")
- }
- if len(r.Form.Get("repo")) < 1 {
+ tags := []string{} // Note: if empty, all tags will be removed from the image.
+ repo := r.Form.Get("repo")
+ tag := r.Form.Get("tag")
+
+ // Do the parameter dance.
+ switch {
+ // If tag is set, repo must be as well.
+ case len(repo) == 0 && len(tag) > 0:
utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image"))
return
+
+ case len(repo) == 0:
+ break
+
+ // If repo is specified, we need to add that to the tags.
+ default:
+ if len(tag) == 0 {
+ // Normalize tag to "latest" if empty.
+ tag = "latest"
+ }
+ tags = append(tags, fmt.Sprintf("%s:%s", repo, tag))
}
- repo := r.Form.Get("repo")
- tagName := fmt.Sprintf("%s:%s", repo, tag)
- if err := newImage.UntagImage(tagName); err != nil {
- utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ opts := entities.ImageUntagOptions{}
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+
+ name := utils.GetName(r)
+ if err := imageEngine.Untag(r.Context(), name, tags, opts); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchImage {
+ utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
+ } else {
+ utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+ }
return
}
utils.WriteResponse(w, http.StatusCreated, "")
diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go
index ad8d1f38e..791ef7a48 100644
--- a/pkg/api/handlers/libpod/images_pull.go
+++ b/pkg/api/handlers/libpod/images_pull.go
@@ -74,9 +74,9 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
return
}
- authConf, authfile, err := auth.GetCredentials(r)
+ authConf, authfile, key, err := auth.GetCredentials(r)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index 8e65248e2..2031dd42f 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -6,11 +6,13 @@ import (
"github.com/containers/buildah/manifests"
copy2 "github.com/containers/image/v5/copy"
+ "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -48,17 +50,18 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
func ManifestInspect(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
- newImage, err := runtime.ImageRuntime().NewFromLocal(name)
- if err != nil {
- utils.ImageNotFound(w, name, err)
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+ inspectReport, inspectError := imageEngine.ManifestInspect(r.Context(), name)
+ if inspectError != nil {
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, inspectError)
return
}
- data, err := newImage.InspectManifest()
- if err != nil {
- utils.InternalServerError(w, err)
+ var list manifest.Schema2List
+ if err := json.Unmarshal(inspectReport, &list); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Unmarshal()"))
return
}
- utils.WriteResponse(w, http.StatusOK, data)
+ utils.WriteResponse(w, http.StatusOK, &list)
}
func ManifestAdd(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index dfece2a4e..b3c4840b8 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -92,8 +92,8 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
}
if reports[0].Err != nil {
// If the network cannot be found, we return a 404.
- if errors.Cause(err) == define.ErrNoSuchNetwork {
- utils.Error(w, "Something went wrong", http.StatusNotFound, err)
+ if errors.Cause(reports[0].Err) == define.ErrNoSuchNetwork {
+ utils.Error(w, "Something went wrong", http.StatusNotFound, reports[0].Err)
return
}
}
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
index 59f78da8c..2296e170a 100644
--- a/pkg/api/handlers/libpod/play.go
+++ b/pkg/api/handlers/libpod/play.go
@@ -48,9 +48,9 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file"))
return
}
- authConf, authfile, err := auth.GetCredentials(r)
+ authConf, authfile, key, err := auth.GetCredentials(r)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String()))
return
}
defer auth.RemoveAuthfile(authfile)
diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go
index 920811c51..28f5a0b42 100644
--- a/pkg/api/server/handler_api.go
+++ b/pkg/api/server/handler_api.go
@@ -7,7 +7,9 @@ import (
"runtime"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- log "github.com/sirupsen/logrus"
+ "github.com/containers/podman/v2/pkg/auth"
+ "github.com/google/uuid"
+ "github.com/sirupsen/logrus"
)
// APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code
@@ -19,7 +21,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc {
if err != nil {
buf := make([]byte, 1<<20)
n := runtime.Stack(buf, true)
- log.Warnf("Recovering from API handler panic: %v, %s", err, buf[:n])
+ logrus.Warnf("Recovering from API handler panic: %v, %s", err, buf[:n])
// Try to inform client things went south... won't work if handler already started writing response body
utils.InternalServerError(w, fmt.Errorf("%v", err))
}
@@ -27,10 +29,23 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc {
// Wrapper to hide some boiler plate
fn := func(w http.ResponseWriter, r *http.Request) {
- log.Debugf("APIHandler -- Method: %s URL: %s", r.Method, r.URL.String())
+ rid := uuid.New().String()
+ if logrus.IsLevelEnabled(logrus.DebugLevel) {
+ logrus.Debugf("APIHandler(%s) -- Method: %s URL: %s", rid, r.Method, r.URL.String())
+ for k, v := range r.Header {
+ switch auth.HeaderAuthName(k) {
+ case auth.XRegistryConfigHeader, auth.XRegistryAuthHeader:
+ logrus.Debugf("APIHandler(%s) -- Header: %s: <hidden>", rid, k)
+ default:
+ logrus.Debugf("APIHandler(%s) -- Header: %s: %v", rid, k, v)
+ }
+ }
+ }
+ // Set in case handler wishes to correlate logging events
+ r.Header.Set("X-Reference-Id", rid)
if err := r.ParseForm(); err != nil {
- log.Infof("Failed Request: unable to parse form: %q", err)
+ logrus.Infof("Failed Request: unable to parse form: %q (%s)", err, rid)
}
// TODO: Use r.ConnContext when ported to go 1.13
diff --git a/pkg/api/server/idle/tracker.go b/pkg/api/server/idle/tracker.go
new file mode 100644
index 000000000..1b378c492
--- /dev/null
+++ b/pkg/api/server/idle/tracker.go
@@ -0,0 +1,96 @@
+package idle
+
+import (
+ "net"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/sirupsen/logrus"
+)
+
+// Tracker holds the state for the server's idle tracking
+type Tracker struct {
+ // Duration is the API idle window
+ Duration time.Duration
+ hijacked int // count of active connections managed by handlers
+ managed map[net.Conn]struct{} // set of active connections managed by http package
+ mux sync.Mutex // protect managed map
+ timer *time.Timer
+ total int // total number of connections made to this server instance
+}
+
+// NewTracker creates and initializes a new Tracker object
+// For best behavior, duration should be 2x http idle connection timeout
+func NewTracker(idle time.Duration) *Tracker {
+ return &Tracker{
+ managed: make(map[net.Conn]struct{}),
+ Duration: idle,
+ timer: time.NewTimer(idle),
+ }
+}
+
+// ConnState is called on HTTP connection state changes.
+// - Once StateHijacked, StateClose is _NOT_ called on that connection
+// - There are two "idle" timeouts, the http idle connection (not to be confused with the TCP/IP idle socket timeout)
+// and the API idle window. The caller should set the http idle timeout to 2x the time provided to NewTacker() which
+// is the API idle window.
+func (t *Tracker) ConnState(conn net.Conn, state http.ConnState) {
+ t.mux.Lock()
+ defer t.mux.Unlock()
+
+ logrus.Debugf("IdleTracker %p:%v %dm+%dh/%dt connection(s)", conn, state, len(t.managed), t.hijacked, t.TotalConnections())
+ switch state {
+ case http.StateNew, http.StateActive:
+ // stop the API timer when the server transitions any connection to an "active" state
+ t.managed[conn] = struct{}{}
+ t.timer.Stop()
+ t.total++
+ case http.StateHijacked:
+ // hijacked connections should call Close() when finished.
+ // Note: If a handler hijack's a connection and then doesn't Close() it,
+ // the API timer will not fire and the server will _NOT_ timeout.
+ delete(t.managed, conn)
+ t.hijacked++
+ case http.StateIdle:
+ // When any connection goes into the http idle state, we know:
+ // - we have an active connection
+ // - the API timer should not be counting down (See case StateNew/StateActive)
+ break
+ case http.StateClosed:
+ oldActive := t.ActiveConnections()
+
+ // Either the server or a hijacking handler has closed the http connection to a client
+ if _, found := t.managed[conn]; found {
+ delete(t.managed, conn)
+ } else {
+ t.hijacked-- // guarded by t.mux above
+ }
+
+ // Transitioned from any "active" connection to no connections
+ if oldActive > 0 && t.ActiveConnections() == 0 {
+ t.timer.Stop() // See library source for Reset() issues and why they are not fixed
+ t.timer.Reset(t.Duration) // Restart the API window timer
+ }
+ }
+}
+
+// Close is used to update Tracker that a StateHijacked connection has been closed by handler (StateClosed)
+func (t *Tracker) Close() {
+ t.ConnState(nil, http.StateClosed)
+}
+
+// ActiveConnections returns the number of current managed or StateHijacked connections
+func (t *Tracker) ActiveConnections() int {
+ return len(t.managed) + t.hijacked
+}
+
+// TotalConnections returns total number of connections made to this instance of the service
+func (t *Tracker) TotalConnections() int {
+ return t.total
+}
+
+// Done is called when idle timer has expired
+func (t *Tracker) Done() <-chan time.Time {
+ return t.timer.C
+}
diff --git a/pkg/api/server/idletracker/idletracker.go b/pkg/api/server/idletracker/idletracker.go
deleted file mode 100644
index 1ee905a99..000000000
--- a/pkg/api/server/idletracker/idletracker.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package idletracker
-
-import (
- "net"
- "net/http"
- "sync"
- "time"
-
- "github.com/sirupsen/logrus"
-)
-
-type IdleTracker struct {
- http map[net.Conn]struct{}
- hijacked int
- total int
- mux sync.Mutex
- timer *time.Timer
- Duration time.Duration
-}
-
-func NewIdleTracker(idle time.Duration) *IdleTracker {
- return &IdleTracker{
- http: make(map[net.Conn]struct{}),
- Duration: idle,
- timer: time.NewTimer(idle),
- }
-}
-
-func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- oldActive := t.ActiveConnections()
- logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, oldActive, t.TotalConnections())
- switch state {
- case http.StateNew, http.StateActive:
- t.http[conn] = struct{}{}
- // stop the timer if we transitioned from idle
- if oldActive == 0 {
- t.timer.Stop()
- }
- t.total++
- case http.StateHijacked:
- // hijacked connections are handled elsewhere
- delete(t.http, conn)
- t.hijacked++
- case http.StateIdle, http.StateClosed:
- delete(t.http, conn)
- // Restart the timer if we've become idle
- if oldActive > 0 && len(t.http) == 0 {
- t.timer.Stop()
- t.timer.Reset(t.Duration)
- }
- }
-}
-
-func (t *IdleTracker) TrackHijackedClosed() {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- t.hijacked--
-}
-
-func (t *IdleTracker) ActiveConnections() int {
- return len(t.http) + t.hijacked
-}
-
-func (t *IdleTracker) TotalConnections() int {
- return t.total
-}
-
-func (t *IdleTracker) Done() <-chan time.Time {
- return t.timer.C
-}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index b1007fe09..cb0d26d1e 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -1175,7 +1175,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// tags:
// - images
// summary: Untag an image
- // description: Untag an image
+ // description: Untag an image. If not repo and tag are specified, all tags are removed from the image.
// parameters:
// - in: path
// name: name:.*
@@ -1423,6 +1423,13 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: |
// output configuration TBD
// (As of version 1.xx)
+ // - in: query
+ // name: httpproxy
+ // type: boolean
+ // default:
+ // description: |
+ // Inject http proxy environment variables into container
+ // (As of version 2.0.0)
// produces:
// - application/json
// responses:
diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go
index 22488b158..aa0f67604 100644
--- a/pkg/api/server/register_volumes.go
+++ b/pkg/api/server/register_volumes.go
@@ -154,7 +154,9 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
// parameters:
// - in: body
// name: create
- // description: attributes for creating a container
+ // description: |
+ // attributes for creating a container.
+ // Note: If a volume by the same name exists, a 201 response with that volume's information will be generated.
// schema:
// $ref: "#/definitions/DockerVolumeCreate"
// produces:
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index e7c031234..09a9f6370 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -16,7 +16,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/api/handlers"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/coreos/go-systemd/v22/activation"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux"
@@ -26,14 +26,14 @@ import (
)
type APIServer struct {
- http.Server // The HTTP work happens here
- *schema.Decoder // Decoder for Query parameters to structs
- context.Context // Context to carry objects to handlers
- *libpod.Runtime // Where the real work happens
- net.Listener // mux for routing HTTP API calls to libpod routines
- context.CancelFunc // Stop APIServer
- idleTracker *idletracker.IdleTracker // Track connections to support idle shutdown
- pprof *http.Server // Sidecar http server for providing performance data
+ http.Server // The HTTP work happens here
+ *schema.Decoder // Decoder for Query parameters to structs
+ context.Context // Context to carry objects to handlers
+ *libpod.Runtime // Where the real work happens
+ net.Listener // mux for routing HTTP API calls to libpod routines
+ context.CancelFunc // Stop APIServer
+ idleTracker *idle.Tracker // Track connections to support idle shutdown
+ pprof *http.Server // Sidecar http server for providing performance data
}
// Number of seconds to wait for next request, if exceeded shutdown server
@@ -70,13 +70,13 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
router := mux.NewRouter().UseEncodedPath()
- idle := idletracker.NewIdleTracker(duration)
+ idle := idle.NewTracker(duration)
server := APIServer{
Server: http.Server{
Handler: router,
ReadHeaderTimeout: 20 * time.Second,
- IdleTimeout: duration,
+ IdleTimeout: duration * 2,
ConnState: idle.ConnState,
ErrorLog: log.New(logrus.StandardLogger().Out, "", 0),
},
diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go
index 69a7da869..fcbf6fe39 100644
--- a/pkg/auth/auth.go
+++ b/pkg/auth/auth.go
@@ -3,6 +3,7 @@ package auth
import (
"encoding/base64"
"encoding/json"
+ "fmt"
"io/ioutil"
"net/http"
"os"
@@ -15,21 +16,98 @@ import (
"github.com/sirupsen/logrus"
)
-// XRegistryAuthHeader is the key to the encoded registry authentication
-// configuration in an http-request header.
-const XRegistryAuthHeader = "X-Registry-Auth"
+type HeaderAuthName string
-// GetCredentials extracts one or more DockerAuthConfigs from the request's
+func (h HeaderAuthName) String() string { return string(h) }
+
+// XRegistryAuthHeader is the key to the encoded registry authentication configuration in an http-request header.
+// This header supports one registry per header occurrence. To support N registries provided N headers, one per registry.
+// As of Docker API 1.40 and Libpod API 1.0.0, this header is supported by all endpoints.
+const XRegistryAuthHeader HeaderAuthName = "X-Registry-Auth"
+
+// XRegistryConfigHeader is the key to the encoded registry authentication configuration in an http-request header.
+// This header supports N registries in one header via a Base64 encoded, JSON map.
+// As of Docker API 1.40 and Libpod API 2.0.0, this header is supported by build endpoints.
+const XRegistryConfigHeader HeaderAuthName = "X-Registry-Config"
+
+// GetCredentials queries the http.Request for X-Registry-.* headers and extracts
+// the necessary authentication information for libpod operations
+func GetCredentials(r *http.Request) (*types.DockerAuthConfig, string, HeaderAuthName, error) {
+ has := func(key HeaderAuthName) bool { hdr, found := r.Header[string(key)]; return found && len(hdr) > 0 }
+ switch {
+ case has(XRegistryConfigHeader):
+ c, f, err := getConfigCredentials(r)
+ return c, f, XRegistryConfigHeader, err
+ case has(XRegistryAuthHeader):
+ c, f, err := getAuthCredentials(r)
+ return c, f, XRegistryAuthHeader, err
+
+ }
+ return nil, "", "", nil
+}
+
+// getConfigCredentials extracts one or more docker.AuthConfig from the request's
+// header. An empty key will be used as default while a named registry will be
+// returned as types.DockerAuthConfig
+func getConfigCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) {
+ var auth *types.DockerAuthConfig
+ configs := make(map[string]types.DockerAuthConfig)
+
+ for _, h := range r.Header[string(XRegistryConfigHeader)] {
+ param, err := base64.URLEncoding.DecodeString(h)
+ if err != nil {
+ return nil, "", errors.Wrapf(err, "failed to decode %q", XRegistryConfigHeader)
+ }
+
+ ac := make(map[string]dockerAPITypes.AuthConfig)
+ err = json.Unmarshal(param, &ac)
+ if err != nil {
+ return nil, "", errors.Wrapf(err, "failed to unmarshal %q", XRegistryConfigHeader)
+ }
+
+ for k, v := range ac {
+ configs[k] = dockerAuthToImageAuth(v)
+ }
+ }
+
+ // Empty key implies no registry given in API
+ if c, found := configs[""]; found {
+ auth = &c
+ }
+
+ // Override any default given above if specialized credentials provided
+ if registries, found := r.URL.Query()["registry"]; found {
+ for _, r := range registries {
+ for k, v := range configs {
+ if strings.Contains(k, r) {
+ v := v
+ auth = &v
+ break
+ }
+ }
+ if auth != nil {
+ break
+ }
+ }
+
+ if auth == nil {
+ logrus.Debugf("%q header found in request, but \"registry=%v\" query parameter not provided",
+ XRegistryConfigHeader, registries)
+ } else {
+ logrus.Debugf("%q header found in request for username %q", XRegistryConfigHeader, auth.Username)
+ }
+ }
+
+ authfile, err := authConfigsToAuthFile(configs)
+ return auth, authfile, err
+}
+
+// getAuthCredentials extracts one or more DockerAuthConfigs from the request's
// header. The header could specify a single-auth config in which case the
// first return value is set. In case of a multi-auth header, the contents are
// stored in a temporary auth file (2nd return value). Note that the auth file
// should be removed after usage.
-func GetCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) {
- authHeader := r.Header.Get(XRegistryAuthHeader)
- if len(authHeader) == 0 {
- return nil, "", nil
- }
-
+func getAuthCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) {
// First look for a multi-auth header (i.e., a map).
authConfigs, err := multiAuthHeader(r)
if err == nil {
@@ -51,38 +129,75 @@ func GetCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) {
return conf, "", nil
}
-// Header returns a map with the XRegistryAuthHeader set which can
+// Header builds the requested Authentication Header
+func Header(sys *types.SystemContext, headerName HeaderAuthName, authfile, username, password string) (map[string]string, error) {
+ var (
+ content string
+ err error
+ )
+ switch headerName {
+ case XRegistryAuthHeader:
+ content, err = headerAuth(sys, authfile, username, password)
+ case XRegistryConfigHeader:
+ content, err = headerConfig(sys, authfile, username, password)
+ default:
+ err = fmt.Errorf("unsupported authentication header: %q", headerName)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ if len(content) > 0 {
+ return map[string]string{string(headerName): content}, nil
+ }
+ return nil, nil
+}
+
+// headerConfig returns a map with the XRegistryConfigHeader set which can
// conveniently be used in the http stack.
-func Header(sys *types.SystemContext, authfile, username, password string) (map[string]string, error) {
- var content string
- var err error
+func headerConfig(sys *types.SystemContext, authfile, username, password string) (string, error) {
+ if sys == nil {
+ sys = &types.SystemContext{}
+ }
+ if authfile != "" {
+ sys.AuthFilePath = authfile
+ }
+ authConfigs, err := imageAuth.GetAllCredentials(sys)
+ if err != nil {
+ return "", err
+ }
if username != "" {
- content, err = encodeSingleAuthConfig(types.DockerAuthConfig{Username: username, Password: password})
- if err != nil {
- return nil, err
- }
- } else {
- if sys == nil {
- sys = &types.SystemContext{}
- }
- if authfile != "" {
- sys.AuthFilePath = authfile
- }
- authConfigs, err := imageAuth.GetAllCredentials(sys)
- if err != nil {
- return nil, err
- }
- content, err = encodeMultiAuthConfigs(authConfigs)
- if err != nil {
- return nil, err
+ authConfigs[""] = types.DockerAuthConfig{
+ Username: username,
+ Password: password,
}
}
- header := make(map[string]string)
- header[XRegistryAuthHeader] = content
+ if len(authConfigs) == 0 {
+ return "", nil
+ }
+ return encodeMultiAuthConfigs(authConfigs)
+}
- return header, nil
+// headerAuth returns a base64 encoded map with the XRegistryAuthHeader set which can
+// conveniently be used in the http stack.
+func headerAuth(sys *types.SystemContext, authfile, username, password string) (string, error) {
+ if username != "" {
+ return encodeSingleAuthConfig(types.DockerAuthConfig{Username: username, Password: password})
+ }
+
+ if sys == nil {
+ sys = &types.SystemContext{}
+ }
+ if authfile != "" {
+ sys.AuthFilePath = authfile
+ }
+ authConfigs, err := imageAuth.GetAllCredentials(sys)
+ if err != nil {
+ return "", err
+ }
+ return encodeMultiAuthConfigs(authConfigs)
}
// RemoveAuthfile is a convenience function that is meant to be called in a
@@ -180,7 +295,7 @@ func imageAuthToDockerAuth(authConfig types.DockerAuthConfig) dockerAPITypes.Aut
// singleAuthHeader extracts a DockerAuthConfig from the request's header.
// The header content is a single DockerAuthConfig.
func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) {
- authHeader := r.Header.Get(XRegistryAuthHeader)
+ authHeader := r.Header.Get(string(XRegistryAuthHeader))
authConfig := dockerAPITypes.AuthConfig{}
if len(authHeader) > 0 {
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authHeader))
@@ -196,7 +311,7 @@ func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error
// multiAuthHeader extracts a DockerAuthConfig from the request's header.
// The header content is a map[string]DockerAuthConfigs.
func multiAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) {
- authHeader := r.Header.Get(XRegistryAuthHeader)
+ authHeader := r.Header.Get(string(XRegistryAuthHeader))
if len(authHeader) == 0 {
return nil, nil
}
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 9082670a7..60ffea548 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -15,6 +15,7 @@ import (
"strings"
"github.com/containers/buildah"
+ "github.com/containers/podman/v2/pkg/auth"
"github.com/containers/podman/v2/pkg/bindings"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/docker/go-units"
@@ -60,7 +61,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
params.Set("cpushares", strconv.Itoa(int(cpuShares)))
}
if cpuSetCpus := options.CommonBuildOpts.CPUSetCPUs; len(cpuSetCpus) > 0 {
- params.Set("cpusetcpues", cpuSetCpus)
+ params.Set("cpusetcpus", cpuSetCpus)
}
if cpuPeriod := options.CommonBuildOpts.CPUPeriod; cpuPeriod > 0 {
params.Set("cpuperiod", strconv.Itoa(int(cpuPeriod)))
@@ -92,6 +93,26 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("labels", l)
}
+ if options.CommonBuildOpts.HTTPProxy {
+ params.Set("httpproxy", "1")
+ }
+
+ var (
+ headers map[string]string
+ err error
+ )
+ if options.SystemContext == nil {
+ headers, err = auth.Header(options.SystemContext, auth.XRegistryConfigHeader, "", "", "")
+ } else {
+ if options.SystemContext.DockerAuthConfig != nil {
+ headers, err = auth.Header(options.SystemContext, auth.XRegistryAuthHeader, options.SystemContext.AuthFilePath, options.SystemContext.DockerAuthConfig.Username, options.SystemContext.DockerAuthConfig.Password)
+ } else {
+ headers, err = auth.Header(options.SystemContext, auth.XRegistryConfigHeader, options.SystemContext.AuthFilePath, "", "")
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
stdout := io.Writer(os.Stdout)
if options.Out != nil {
@@ -125,7 +146,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if err != nil {
return nil, err
}
- response, err := conn.DoRequest(tarfile, http.MethodPost, "/build", params, nil)
+ response, err := conn.DoRequest(tarfile, http.MethodPost, "/build", params, headers)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 596491044..a78e7f4c6 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -282,7 +282,7 @@ func Push(ctx context.Context, source string, destination string, options entiti
}
// TODO: have a global system context we can pass around (1st argument)
- header, err := auth.Header(nil, options.Authfile, options.Username, options.Password)
+ header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password)
if err != nil {
return err
}
@@ -325,7 +325,7 @@ func Search(ctx context.Context, term string, opts entities.ImageSearchOptions)
}
// TODO: have a global system context we can pass around (1st argument)
- header, err := auth.Header(nil, opts.Authfile, "", "")
+ header, err := auth.Header(nil, auth.XRegistryAuthHeader, opts.Authfile, "", "")
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go
index 2bfbbb2ac..c827b3283 100644
--- a/pkg/bindings/images/pull.go
+++ b/pkg/bindings/images/pull.go
@@ -42,7 +42,7 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption
params.Set("allTags", strconv.FormatBool(options.AllTags))
// TODO: have a global system context we can pass around (1st argument)
- header, err := auth.Header(nil, options.Authfile, options.Username, options.Password)
+ header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go
index 32f9bb4a9..ffaee3208 100644
--- a/pkg/bindings/play/play.go
+++ b/pkg/bindings/play/play.go
@@ -33,7 +33,7 @@ func Kube(ctx context.Context, path string, options entities.PlayKubeOptions) (*
}
// TODO: have a global system context we can pass around (1st argument)
- header, err := auth.Header(nil, options.Authfile, options.Username, options.Password)
+ header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index f105dc333..803a59932 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -78,6 +78,6 @@ type ContainerEngine interface {
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
- VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
+ VolumePrune(ctx context.Context) ([]*VolumePruneReport, error)
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 7b38dbd87..426419833 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -142,6 +142,7 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
s.StaticMAC = p.Net.StaticMAC
s.PortMappings = p.Net.PublishPorts
s.CNINetworks = p.Net.CNINetworks
+ s.NetworkOptions = p.Net.NetworkOptions
if p.Net.UseImageResolvConf {
s.NoManageResolvConf = true
}
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 53d30ffdf..fb8466d04 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -113,10 +113,6 @@ type VolumeInspectReport struct {
*VolumeConfigResponse
}
-type VolumePruneOptions struct {
- Force bool
-}
-
type VolumePruneReport struct {
Err error
Id string //nolint
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index d56dc7d94..965c63bec 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -46,11 +46,7 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- return ir.pruneImagesHelper(ctx, opts.All, opts.Filter)
-}
-
-func (ir *ImageEngine) pruneImagesHelper(ctx context.Context, all bool, filters []string) (*entities.ImagePruneReport, error) {
- results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, all, filters)
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 672d0a69f..6c518e678 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -3,6 +3,7 @@
package abi
import (
+ "bytes"
"context"
"encoding/json"
"fmt"
@@ -11,15 +12,17 @@ import (
"strings"
"github.com/containers/buildah/manifests"
+ buildahManifests "github.com/containers/buildah/pkg/manifests"
+ "github.com/containers/buildah/util"
buildahUtil "github.com/containers/buildah/util"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
libpodImage "github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/util"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -41,28 +44,82 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin
// ManifestInspect returns the content of a manifest list or image
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
- dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
- _, err := alltransports.ParseImageName(name)
+ if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
+ // return the manifest in local storage
+ if list, err := newImage.InspectManifest(); err == nil {
+ buf, err := json.MarshalIndent(list, "", " ")
+ if err != nil {
+ return buf, errors.Wrapf(err, "error rendering manifest %s for display", name)
+ }
+ return buf, nil
+ // no return if local image is not a list of images type
+ // continue on getting valid manifest through remote serice
+ } else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
+ return nil, errors.Wrapf(err, "loading manifest %q", name)
+ }
+ }
+ sc := ir.Libpod.SystemContext()
+ refs, err := util.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name)
if err != nil {
- _, err = alltransports.ParseImageName(dockerPrefix + name)
+ return nil, err
+ }
+ var (
+ latestErr error
+ result []byte
+ manType string
+ b bytes.Buffer
+ )
+ appendErr := func(e error) {
+ if latestErr == nil {
+ latestErr = e
+ } else {
+ latestErr = errors.Wrapf(latestErr, "tried %v\n", e)
+ }
+ }
+ for _, ref := range refs {
+ src, err := ref.NewImageSource(ctx, sc)
+ if err != nil {
+ appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
+ continue
+ }
+ defer src.Close()
+
+ manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
if err != nil {
- return nil, errors.Errorf("invalid image reference %q", name)
+ appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref)))
+ continue
}
+
+ if !manifest.MIMETypeIsMultiImage(manifestType) {
+ appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType))
+ continue
+ }
+ result = manifestBytes
+ manType = manifestType
+ break
}
- image, err := ir.Libpod.ImageRuntime().New(ctx, name, "", "", nil, nil, libpodImage.SigningOptions{}, nil, util.PullImageMissing)
- if err != nil {
- return nil, errors.Wrapf(err, "reading image %q", name)
+ if len(result) == 0 && latestErr != nil {
+ return nil, latestErr
}
+ if manType != manifest.DockerV2ListMediaType {
+ listBlob, err := manifest.ListFromBlob(result, manType)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
+ }
+ list, err := listBlob.ConvertToMIMEType(manifest.DockerV2ListMediaType)
+ if err != nil {
+ return nil, err
+ }
+ if result, err = list.Serialize(); err != nil {
+ return nil, err
+ }
- list, err := image.InspectManifest()
- if err != nil {
- return nil, errors.Wrapf(err, "loading manifest %q", name)
}
- buf, err := json.MarshalIndent(list, "", " ")
+ err = json.Indent(&b, result, "", " ")
if err != nil {
- return buf, errors.Wrapf(err, "error rendering manifest for display")
+ return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
}
- return buf, nil
+ return b.Bytes(), nil
}
// ManifestAdd adds images to the manifest list
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index 053be6528..5acfea853 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -12,6 +12,7 @@ import (
"github.com/containernetworking/cni/libcni"
cniversion "github.com/containernetworking/cni/pkg/version"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/network"
"github.com/containers/podman/v2/pkg/util"
@@ -85,7 +86,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
// if user passes force, we nuke containers and pods
if !options.Force {
// Without the force option, we return an error
- return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers and pods", name)
+ return reports, errors.Wrapf(define.ErrNetworkInUse, "%q has associated containers with it. Use -f to forcibly delete containers and pods", name)
}
if c.IsInfra() {
// if we have a infra container we need to remove the pod
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 340f00953..946f258af 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -120,7 +120,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
return reports, nil
}
-func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
return ic.pruneVolumesHelper(ctx)
}
diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go
index 5944f855a..7d28f1bd3 100644
--- a/pkg/domain/infra/tunnel/helpers.go
+++ b/pkg/domain/infra/tunnel/helpers.go
@@ -2,74 +2,118 @@ package tunnel
import (
"context"
- "strings"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/bindings"
"github.com/containers/podman/v2/pkg/bindings/containers"
"github.com/containers/podman/v2/pkg/bindings/pods"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/util"
+ "github.com/containers/podman/v2/pkg/errorhandling"
"github.com/pkg/errors"
)
+// FIXME: the `ignore` parameter is very likely wrong here as it should rather
+// be used on *errors* from operations such as remove.
func getContainersByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string) ([]entities.ListContainer, error) {
- var (
- cons []entities.ListContainer
- )
if all && len(namesOrIDs) > 0 {
return nil, errors.New("cannot lookup containers and all")
}
- c, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, bindings.PTrue)
+
+ allContainers, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, bindings.PTrue)
if err != nil {
return nil, err
}
if all {
- return c, err
+ return allContainers, err
}
- for _, id := range namesOrIDs {
- var found bool
- for _, con := range c {
- if id == con.ID || strings.HasPrefix(con.ID, id) || util.StringInSlice(id, con.Names) {
- cons = append(cons, con)
+
+ // Note: it would be nicer if the lists endpoint would support that as
+ // we could use the libpod backend for looking up containers rather
+ // than risking diverging the local and remote lookups.
+ //
+ // A `--filter nameOrId=abc` that can be specified multiple times would
+ // be awesome to have.
+ filtered := []entities.ListContainer{}
+ for _, nameOrID := range namesOrIDs {
+ // First determine if the container exists by doing an inspect.
+ // Inspect takes supports names and IDs and let's us determine
+ // a containers full ID.
+ inspectData, err := containers.Inspect(contextWithConnection, nameOrID, bindings.PFalse)
+ if err != nil {
+ if ignore && errorhandling.Contains(err, define.ErrNoSuchCtr) {
+ continue
+ }
+ return nil, err
+ }
+
+ // Now we can do a full match of the ID to find the right
+ // container. Note that we *really* need a full ID match to
+ // prevent any ambiguities between IDs and names (see #7837).
+ found := false
+ for _, ctr := range allContainers {
+ if ctr.ID == inspectData.ID {
+ filtered = append(filtered, ctr)
found = true
break
}
+
}
+
if !found && !ignore {
- return nil, errors.Wrapf(define.ErrNoSuchCtr, "unable to find container %q", id)
+ return nil, errors.Wrapf(define.ErrNoSuchCtr, "unable to find container %q", nameOrID)
}
}
- return cons, nil
+ return filtered, nil
}
func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIDs []string) ([]*entities.ListPodsReport, error) {
- var (
- sPods []*entities.ListPodsReport
- )
if all && len(namesOrIDs) > 0 {
return nil, errors.New("cannot lookup specific pods and all")
}
- fPods, err := pods.List(contextWithConnection, nil)
+ allPods, err := pods.List(contextWithConnection, nil)
if err != nil {
return nil, err
}
if all {
- return fPods, nil
+ return allPods, nil
}
+
+ filtered := []*entities.ListPodsReport{}
+ // Note: it would be nicer if the lists endpoint would support that as
+ // we could use the libpod backend for looking up pods rather than
+ // risking diverging the local and remote lookups.
+ //
+ // A `--filter nameOrId=abc` that can be specified multiple times would
+ // be awesome to have.
for _, nameOrID := range namesOrIDs {
- var found bool
- for _, f := range fPods {
- if f.Name == nameOrID || strings.HasPrefix(f.Id, nameOrID) {
- sPods = append(sPods, f)
+ // First determine if the pod exists by doing an inspect.
+ // Inspect takes supports names and IDs and let's us determine
+ // a containers full ID.
+ inspectData, err := pods.Inspect(contextWithConnection, nameOrID)
+ if err != nil {
+ if errorhandling.Contains(err, define.ErrNoSuchPod) {
+ return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID)
+ }
+ return nil, err
+ }
+
+ // Now we can do a full match of the ID to find the right pod.
+ // Note that we *really* need a full ID match to prevent any
+ // ambiguities between IDs and names (see #7837).
+ found := false
+ for _, pod := range allPods {
+ if pod.Id == inspectData.ID {
+ filtered = append(filtered, pod)
found = true
break
}
+
}
+
if !found {
return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID)
}
}
- return sPods, nil
+ return filtered, nil
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 981884109..61ac2141c 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
- "github.com/containers/podman/v2/pkg/bindings"
images "github.com/containers/podman/v2/pkg/bindings/images"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/utils"
@@ -139,13 +138,8 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string,
}
func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error {
- // Remove all tags if none are provided
if len(tags) == 0 {
- newImage, err := images.GetImage(ir.ClientCxt, nameOrID, bindings.PFalse)
- if err != nil {
- return err
- }
- tags = newImage.NamesHistory
+ return images.Untag(ir.ClientCxt, nameOrID, "", "")
}
for _, newTag := range tags {
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index ee2786330..e432d3292 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -56,7 +56,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
return reports, nil
}
-func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
return volumes.Prune(ic.ClientCxt)
}
diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go
index 5dff25c7d..949c5d835 100644
--- a/pkg/registries/registries.go
+++ b/pkg/registries/registries.go
@@ -1,5 +1,10 @@
package registries
+// TODO: this package should not exist anymore. Users should either use
+// c/image's `sysregistriesv2` package directly OR, even better, we cache a
+// config in libpod's image runtime so we don't need to parse the
+// registries.conf files redundantly.
+
import (
"os"
"path/filepath"
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index d03663f12..319cce61f 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -200,6 +200,9 @@ func getDevices(path string) ([]*configs.Device, error) {
}
case f.Name() == "console":
continue
+ case f.Mode()&os.ModeSymlink != 0:
+ // do not add symlink'd devices to privileged devices
+ continue
}
device, err := devices.DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
if err != nil {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 42228540c..81620997f 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -182,14 +182,23 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
g.SetProcessCwd(config.WorkDir)
ProcessArgs := make([]string, 0)
- if len(config.Entrypoint) > 0 {
- ProcessArgs = config.Entrypoint
+ // We need to iterate the input for entrypoint because it is a []string
+ // but "" is a legit json input, which translates into a []string with an
+ // empty position. This messes up the eventual command being executed
+ // in the container
+ for _, a := range config.Entrypoint {
+ if len(a) > 0 {
+ ProcessArgs = append(ProcessArgs, a)
+ }
}
- if len(config.Command) > 0 {
- ProcessArgs = append(ProcessArgs, config.Command...)
+ // Same issue as explained above for config.Entrypoint.
+ for _, a := range config.Command {
+ if len(a) > 0 {
+ ProcessArgs = append(ProcessArgs, a)
+ }
}
- g.SetProcessArgs(ProcessArgs)
+ g.SetProcessArgs(ProcessArgs)
g.SetProcessTerminal(config.Tty)
for key, val := range config.Annotations {
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go
index 1d5dcd8e7..fac02ad01 100644
--- a/pkg/specgen/generate/config_linux.go
+++ b/pkg/specgen/generate/config_linux.go
@@ -248,6 +248,13 @@ func addDevice(g *generate.Generator, device string) error {
}
g.Config.Mounts = append(g.Config.Mounts, devMnt)
return nil
+ } else if src == "/dev/fuse" {
+ // if the user is asking for fuse inside the container
+ // make sure the module is loaded.
+ f, err := unix.Open(src, unix.O_RDONLY|unix.O_NONBLOCK, 0)
+ if err == nil {
+ unix.Close(f)
+ }
}
dev.Path = dst
g.AddDevice(*dev)
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 147ebd61b..2ee8f2441 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/podman/v2/pkg/specgen"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -33,7 +34,43 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
_, mediaType, err := newImage.Manifest(ctx)
if err != nil {
- return nil, err
+ if errors.Cause(err) != image.ErrImageIsBareList {
+ return nil, err
+ }
+ // if err is not runnable image
+ // use the local store image with repo@digest matches with the list, if exists
+ manifestByte, manifestType, err := newImage.GetManifest(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ list, err := manifest.ListFromBlob(manifestByte, manifestType)
+ if err != nil {
+ return nil, err
+ }
+ images, err := r.ImageRuntime().GetImages()
+ if err != nil {
+ return nil, err
+ }
+ findLocal := false
+ listDigest, err := list.ChooseInstance(r.SystemContext())
+ if err != nil {
+ return nil, err
+ }
+ for _, img := range images {
+ for _, imageDigest := range img.Digests() {
+ if imageDigest == listDigest {
+ newImage = img
+ s.Image = img.ID()
+ mediaType = manifestType
+ findLocal = true
+ logrus.Debug("image contains manifest list, using image from local storage")
+ break
+ }
+ }
+ }
+ if !findLocal {
+ return nil, image.ErrImageIsBareList
+ }
}
if s.HealthConfig == nil && mediaType == manifest.DockerV2Schema2MediaType {
@@ -75,8 +112,8 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
if err != nil {
return nil, errors.Wrap(err, "error parsing fields in containers.conf")
}
- if defaultEnvs["containers"] == "" {
- defaultEnvs["containers"] = "podman"
+ if defaultEnvs["container"] == "" {
+ defaultEnvs["container"] = "podman"
}
var envs map[string]string
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index 101201252..43caf0fe9 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -99,6 +99,9 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod
case specgen.Host:
logrus.Debugf("Pod will use host networking")
options = append(options, libpod.WithPodHostNetwork())
+ case specgen.Slirp:
+ logrus.Debugf("Pod will use slirp4netns")
+ options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions))
default:
return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode)
}
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index 87e8029a7..d17cd4a9a 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/specgen"
"github.com/containers/podman/v2/pkg/util"
@@ -130,12 +131,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
configSpec := g.Config
+ configSpec.Process.Capabilities.Ambient = []string{}
configSpec.Process.Capabilities.Bounding = caplist
+ configSpec.Process.Capabilities.Inheritable = caplist
if s.User == "" || s.User == "root" || s.User == "0" {
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Permitted = caplist
- configSpec.Process.Capabilities.Inheritable = caplist
} else {
userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd)
if err != nil {
@@ -167,7 +169,52 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
g.SetRootReadonly(s.ReadOnlyFilesystem)
+
+ // Add default sysctls
+ defaultSysctls, err := util.ValidateSysctls(rtc.Sysctls())
+ if err != nil {
+ return err
+ }
+ for sysctlKey, sysctlVal := range defaultSysctls {
+
+ // Ignore mqueue sysctls if --ipc=host
+ if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
+ logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to host", sysctlKey, sysctlVal)
+
+ continue
+ }
+
+ // Ignore net sysctls if --net=host
+ if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") {
+ logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctlKey, sysctlVal)
+ continue
+ }
+
+ // Ignore uts sysctls if --uts=host
+ if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) {
+ logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace set to host", sysctlKey, sysctlVal)
+ continue
+ }
+
+ g.AddLinuxSysctl(sysctlKey, sysctlVal)
+ }
+
for sysctlKey, sysctlVal := range s.Sysctl {
+
+ if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
+ return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since IPC Namespace set to host", sysctlKey, sysctlVal)
+ }
+
+ // Ignore net sysctls if --net=host
+ if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") {
+ return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since Host Namespace set to host", sysctlKey, sysctlVal)
+ }
+
+ // Ignore uts sysctls if --uts=host
+ if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) {
+ return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since UTS Namespace set to host", sysctlKey, sysctlVal)
+ }
+
g.AddLinuxSysctl(sysctlKey, sysctlVal)
}
diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go
index 907c0bb69..a6c61a203 100644
--- a/pkg/specgen/pod_validate.go
+++ b/pkg/specgen/pod_validate.go
@@ -72,9 +72,9 @@ func (p *PodSpecGenerator) Validate() error {
return exclusivePodOptions("NoInfra", "NoManageResolvConf")
}
}
- if p.NetNS.NSMode != "" && p.NetNS.NSMode != Bridge && p.NetNS.NSMode != Default {
+ if p.NetNS.NSMode != "" && p.NetNS.NSMode != Bridge && p.NetNS.NSMode != Slirp && p.NetNS.NSMode != Default {
if len(p.PortMappings) > 0 {
- return errors.New("PortMappings can only be used with Bridge mode networking")
+ return errors.New("PortMappings can only be used with Bridge or slirp4netns networking")
}
if len(p.CNINetworks) > 0 {
return errors.New("CNINetworks can only be used with Bridge mode networking")
diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go
index 3c32ec365..7d771f5bb 100644
--- a/pkg/specgen/podspecgen.go
+++ b/pkg/specgen/podspecgen.go
@@ -134,6 +134,9 @@ type PodNetworkConfig struct {
// Conflicts with NoInfra=true and NoManageHosts.
// Optional.
HostAdd []string `json:"hostadd,omitempty"`
+ // NetworkOptions are additional options for each network
+ // Optional.
+ NetworkOptions map[string][]string `json:"network_options,omitempty"`
}
// PodCgroupConfig contains configuration options about a pod's cgroups.