diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/containers_create.go | 13 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images.go | 12 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_build.go | 66 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_push.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images_pull.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 17 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/play.go | 4 | ||||
-rw-r--r-- | pkg/api/server/handler_api.go | 23 | ||||
-rw-r--r-- | pkg/api/server/register_images.go | 7 | ||||
-rw-r--r-- | pkg/auth/auth.go | 189 | ||||
-rw-r--r-- | pkg/bindings/images/build.go | 25 | ||||
-rw-r--r-- | pkg/bindings/images/images.go | 4 | ||||
-rw-r--r-- | pkg/bindings/images/pull.go | 2 | ||||
-rw-r--r-- | pkg/bindings/play/play.go | 2 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 2 | ||||
-rw-r--r-- | pkg/domain/entities/volumes.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 85 | ||||
-rw-r--r-- | pkg/domain/infra/abi/volumes.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/volumes.go | 2 | ||||
-rw-r--r-- | pkg/spec/config_linux.go | 3 | ||||
-rw-r--r-- | pkg/spec/spec.go | 19 |
22 files changed, 367 insertions, 126 deletions
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/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/libpod/images.go b/pkg/api/handlers/libpod/images.go index 2f9c10746..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) 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/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/register_images.go b/pkg/api/server/register_images.go index 32e401a54..cb0d26d1e 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -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/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/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/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/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/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/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 { |