diff options
Diffstat (limited to 'pkg')
25 files changed, 394 insertions, 186 deletions
diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index 223eb2cd5..d8197415c 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -1,13 +1,8 @@ package compat import ( - "bytes" - "encoding/base64" - "encoding/json" "fmt" "net/http" - "os" - "time" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" @@ -15,6 +10,7 @@ import ( "github.com/containers/podman/v2/pkg/copy" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func Archive(w http.ResponseWriter, r *http.Request) { @@ -71,12 +67,12 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) return } - statHeader, err := fileInfoToDockerStats(info) + statHeader, err := copy.EncodeFileInfo(info) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - w.Header().Add("X-Docker-Container-Path-Stat", statHeader) + w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader) // Our work is done when the user is interested in the header only. if r.Method == http.MethodHead { @@ -91,47 +87,16 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De return } - w.WriteHeader(http.StatusOK) - if err := copy.Copy(&source, &destination, false); err != nil { + copier, err := copy.GetCopier(&source, &destination, false) + if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } -} - -func fileInfoToDockerStats(info *copy.FileInfo) (string, error) { - dockerStats := struct { - Name string `json:"name"` - Size int64 `json:"size"` - Mode os.FileMode `json:"mode"` - ModTime time.Time `json:"mtime"` - LinkTarget string `json:"linkTarget"` - }{ - Name: info.Name, - Size: info.Size, - Mode: info.Mode, - ModTime: info.ModTime, - LinkTarget: info.LinkTarget, - } - - jsonBytes, err := json.Marshal(&dockerStats) - if err != nil { - return "", errors.Wrap(err, "failed to serialize file stats") - } - - buff := bytes.NewBuffer(make([]byte, 0, 128)) - base64encoder := base64.NewEncoder(base64.StdEncoding, buff) - - _, err = base64encoder.Write(jsonBytes) - if err != nil { - return "", err - } - - err = base64encoder.Close() - if err != nil { - return "", err + w.WriteHeader(http.StatusOK) + if err := copier.Copy(); err != nil { + logrus.Errorf("Error during copy: %v", err) + return } - - return buff.String(), nil } func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { @@ -170,9 +135,14 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, return } - w.WriteHeader(http.StatusOK) - if err := copy.Copy(&source, &destination, false); err != nil { + copier, err := copy.GetCopier(&source, &destination, false) + if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + w.WriteHeader(http.StatusOK) + if err := copier.Copy(); err != nil { + logrus.Errorf("Error during copy: %v", err) + return + } } diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 43478c1d3..415ff85cd 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -71,6 +71,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { ForceRm bool `schema:"forcerm"` HTTPProxy bool `schema:"httpproxy"` Labels string `schema:"labels"` + Layers bool `schema:"layers"` MemSwap int64 `schema:"memswap"` Memory int64 `schema:"memory"` NetworkMode string `schema:"networkmode"` @@ -165,6 +166,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Registry: query.Registry, IgnoreUnrecognizedInstructions: true, Quiet: query.Quiet, + Layers: query.Layers, Isolation: buildah.IsolationChroot, Compression: archive.Gzip, Args: buildArgs, diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index 33bb75391..b3b8c1f16 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -60,7 +60,8 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Service bool `schema:"service"` + Names []string `schema:"names"` + Service bool `schema:"service"` }{ // Defaults would go here. } @@ -73,7 +74,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { containerEngine := abi.ContainerEngine{Libpod: runtime} options := entities.GenerateKubeOptions{Service: query.Service} - report, err := containerEngine.GenerateKube(r.Context(), utils.GetName(r), options) + report, err := containerEngine.GenerateKube(r.Context(), query.Names, options) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error generating YAML")) return diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go index 60e5b03f7..bce5484ab 100644 --- a/pkg/api/server/register_generate.go +++ b/pkg/api/server/register_generate.go @@ -70,7 +70,7 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error { // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/generate/{name:.*}/systemd"), s.APIHandler(libpod.GenerateSystemd)).Methods(http.MethodGet) - // swagger:operation GET /libpod/generate/{name:.*}/kube libpod libpodGenerateKube + // swagger:operation GET /libpod/generate/kube libpod libpodGenerateKube // --- // tags: // - containers @@ -78,9 +78,11 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error { // summary: Generate a Kubernetes YAML file. // description: Generate Kubernetes YAML based on a pod or container. // parameters: - // - in: path - // name: name:.* - // type: string + // - in: query + // name: names + // type: array + // items: + // type: string // required: true // description: Name or ID of the container or pod. // - in: query @@ -98,6 +100,6 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error { // format: binary // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/generate/{name:.*}/kube"), s.APIHandler(libpod.GenerateKube)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/generate/kube"), s.APIHandler(libpod.GenerateKube)).Methods(http.MethodGet) return nil } diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go index dde1cc29c..8d0146ec1 100644 --- a/pkg/bindings/generate/generate.go +++ b/pkg/bindings/generate/generate.go @@ -2,6 +2,7 @@ package generate import ( "context" + "errors" "net/http" "net/url" "strconv" @@ -37,15 +38,21 @@ func Systemd(ctx context.Context, nameOrID string, options entities.GenerateSyst return report, response.Process(&report.Units) } -func Kube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { +func Kube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } + if len(nameOrIDs) < 1 { + return nil, errors.New("must provide the name or ID of one container or pod") + } params := url.Values{} + for _, name := range nameOrIDs { + params.Add("names", name) + } params.Set("service", strconv.FormatBool(options.Service)) - response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/kube", params, nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/generate/kube", params, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 815ab4e86..d34ab87d9 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -41,6 +41,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO if options.NoCache { params.Set("nocache", "1") } + if options.Layers { + params.Set("layers", "1") + } // TODO cachefrom if options.PullPolicy == buildah.PullAlways { params.Set("pull", "1") diff --git a/pkg/copy/copy.go b/pkg/copy/copy.go index 0e68eb450..13893deb2 100644 --- a/pkg/copy/copy.go +++ b/pkg/copy/copy.go @@ -25,31 +25,61 @@ import ( // // **************************************************************************** -// Copy the source item to destination. Use extract to untar the source if -// it's a tar archive. -func Copy(source *CopyItem, destination *CopyItem, extract bool) error { +// Copier copies data from a source to a destination CopyItem. +type Copier struct { + copyFunc func() error + cleanUpFuncs []deferFunc +} + +// cleanUp releases resources the Copier may hold open. +func (c *Copier) cleanUp() { + for _, f := range c.cleanUpFuncs { + f() + } +} + +// Copy data from a source to a destination CopyItem. +func (c *Copier) Copy() error { + defer c.cleanUp() + return c.copyFunc() +} + +// GetCopiers returns a Copier to copy the source item to destination. Use +// extract to untar the source if it's a tar archive. +func GetCopier(source *CopyItem, destination *CopyItem, extract bool) (*Copier, error) { + copier := &Copier{} + // First, do the man-page dance. See podman-cp(1) for details. if err := enforceCopyRules(source, destination); err != nil { - return err + return nil, err } // Destination is a stream (e.g., stdout or an http body). if destination.info.IsStream { // Source is a stream (e.g., stdin or an http body). if source.info.IsStream { - _, err := io.Copy(destination.writer, source.reader) - return err + copier.copyFunc = func() error { + _, err := io.Copy(destination.writer, source.reader) + return err + } + return copier, nil } root, glob, err := source.buildahGlobs() if err != nil { - return err + return nil, err } - return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer) + copier.copyFunc = func() error { + return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer) + } + return copier, nil } // Destination is either a file or a directory. if source.info.IsStream { - return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader) + copier.copyFunc = func() error { + return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader) + } + return copier, nil } tarOptions := &archive.TarOptions{ @@ -71,33 +101,36 @@ func Copy(source *CopyItem, destination *CopyItem, extract bool) error { var tarReader io.ReadCloser if extract && archive.IsArchivePath(source.resolved) { if !destination.info.IsDir { - return errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original) + return nil, errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original) } reader, err := os.Open(source.resolved) if err != nil { - return err + return nil, err } - defer reader.Close() + copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) // The stream from stdin may be compressed (e.g., via gzip). decompressedStream, err := archive.DecompressStream(reader) if err != nil { - return err + return nil, err } - defer decompressedStream.Close() + copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { decompressedStream.Close() }) tarReader = decompressedStream } else { reader, err := archive.TarWithOptions(source.resolved, tarOptions) if err != nil { - return err + return nil, err } - defer reader.Close() + copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) tarReader = reader } - return buildahCopiah.Put(root, dir, source.putOptions(), tarReader) + copier.copyFunc = func() error { + return buildahCopiah.Put(root, dir, source.putOptions(), tarReader) + } + return copier, nil } // enforceCopyRules enforces the rules for copying from a source to a @@ -114,7 +147,6 @@ func enforceCopyRules(source, destination *CopyItem) error { return nil } - // Source is a *stream*. if source.info.IsStream { if !(destination.info.IsDir || destination.info.IsStream) { return errors.New("destination must be a directory or stream when copying from a stream") diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go new file mode 100644 index 000000000..08b4eb377 --- /dev/null +++ b/pkg/copy/fileinfo.go @@ -0,0 +1,56 @@ +package copy + +import ( + "encoding/base64" + "encoding/json" + "net/http" + "os" + "strings" + "time" + + "github.com/pkg/errors" +) + +// XDockerContainerPathStatHeader is the *key* in http headers pointing to the +// base64 encoded JSON payload of stating a path in a container. +const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat" + +// FileInfo describes a file or directory and is returned by +// (*CopyItem).Stat(). +type FileInfo struct { + Name string `json:"name"` + Size int64 `json:"size"` + Mode os.FileMode `json:"mode"` + ModTime time.Time `json:"mtime"` + IsDir bool `json:"isDir"` + IsStream bool `json:"isStream"` + LinkTarget string `json:"linkTarget"` +} + +// EncodeFileInfo serializes the specified FileInfo as a base64 encoded JSON +// payload. Intended for Docker compat. +func EncodeFileInfo(info *FileInfo) (string, error) { + buf, err := json.Marshal(&info) + if err != nil { + return "", errors.Wrap(err, "failed to serialize file stats") + } + return base64.URLEncoding.EncodeToString(buf), nil +} + +// ExtractFileInfoFromHeader extracts a base64 encoded JSON payload of a +// FileInfo in the http header. If no such header entry is found, nil is +// returned. Intended for Docker compat. +func ExtractFileInfoFromHeader(header *http.Header) (*FileInfo, error) { + rawData := header.Get(XDockerContainerPathStatHeader) + if len(rawData) == 0 { + return nil, nil + } + + info := FileInfo{} + base64Decoder := base64.NewDecoder(base64.URLEncoding, strings.NewReader(rawData)) + if err := json.NewDecoder(base64Decoder).Decode(&info); err != nil { + return nil, err + } + + return &info, nil +} diff --git a/pkg/copy/item.go b/pkg/copy/item.go index db6bca610..df8bf30b9 100644 --- a/pkg/copy/item.go +++ b/pkg/copy/item.go @@ -5,7 +5,6 @@ import ( "os" "path/filepath" "strings" - "time" buildahCopiah "github.com/containers/buildah/copier" "github.com/containers/buildah/pkg/chrootuser" @@ -75,18 +74,6 @@ type CopyItem struct { // deferFunc allows for returning functions that must be deferred at call sites. type deferFunc func() -// FileInfo describes a file or directory and is returned by -// (*CopyItem).Stat(). -type FileInfo struct { - Name string `json:"name"` - Size int64 `json:"size"` - Mode os.FileMode `json:"mode"` - ModTime time.Time `json:"mtime"` - IsDir bool `json:"isDir"` - IsStream bool `json:"isStream"` - LinkTarget string `json:"linkTarget"` -} - // Stat returns the FileInfo. func (item *CopyItem) Stat() (*FileInfo, error) { return &item.info, item.statError diff --git a/pkg/copy/parse.go b/pkg/copy/parse.go new file mode 100644 index 000000000..39e0e1547 --- /dev/null +++ b/pkg/copy/parse.go @@ -0,0 +1,61 @@ +package copy + +import ( + "strings" + + "github.com/pkg/errors" +) + +// ParseSourceAndDestination parses the source and destination input into a +// possibly specified container and path. The input format is described in +// podman-cp(1) as "[nameOrID:]path". Colons in paths are supported as long +// they start with a dot or slash. +// +// It returns, in order, the source container and path, followed by the +// destination container and path, and an error. Note that exactly one +// container must be specified. +func ParseSourceAndDestination(source, destination string) (string, string, string, string, error) { + sourceContainer, sourcePath := parseUserInput(source) + destContainer, destPath := parseUserInput(destination) + + numContainers := 0 + if len(sourceContainer) > 0 { + numContainers++ + } + if len(destContainer) > 0 { + numContainers++ + } + + if numContainers != 1 { + return "", "", "", "", errors.Errorf("invalid arguments %q, %q: exactly 1 container expected but %d specified", source, destination, numContainers) + } + + if len(sourcePath) == 0 || len(destPath) == 0 { + return "", "", "", "", errors.Errorf("invalid arguments %q, %q: you must specify paths", source, destination) + } + + return sourceContainer, sourcePath, destContainer, destPath, nil +} + +// parseUserInput parses the input string and returns, if specified, the name +// or ID of the container and the path. The input format is described in +// podman-cp(1) as "[nameOrID:]path". Colons in paths are supported as long +// they start with a dot or slash. +func parseUserInput(input string) (container string, path string) { + if len(input) == 0 { + return + } + path = input + + // If the input starts with a dot or slash, it cannot refer to a + // container. + if input[0] == '.' || input[0] == '/' { + return + } + + if spl := strings.SplitN(path, ":", 2); len(spl) == 2 { + container = spl[0] + path = spl[1] + } + return +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index e1f40e307..5ad475133 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -46,7 +46,7 @@ type ContainerEngine interface { ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) Events(ctx context.Context, opts EventsOptions) error GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error) - GenerateKube(ctx context.Context, nameOrID string, opts GenerateKubeOptions) (*GenerateKubeReport, error) + GenerateKube(ctx context.Context, nameOrIDs []string, opts GenerateKubeOptions) (*GenerateKubeReport, error) SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error) HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) @@ -55,6 +55,7 @@ type ContainerEngine interface { NetworkDisconnect(ctx context.Context, networkname string, options NetworkDisconnectOptions) error NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error) NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error) + NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error) NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index 65a110fd9..b76bfcac7 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -22,6 +22,19 @@ type NetworkListReport struct { // NetworkInspectReport describes the results from inspect networks type NetworkInspectReport map[string]interface{} +// NetworkReloadOptions describes options for reloading container network +// configuration. +type NetworkReloadOptions struct { + All bool + Latest bool +} + +// NetworkReloadReport describes the results of reloading a container network. +type NetworkReloadReport struct { + Id string + Err error +} + // NetworkRmOptions describes options for removing networks type NetworkRmOptions struct { Force bool diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go index 9409df743..362053cce 100644 --- a/pkg/domain/infra/abi/cp.go +++ b/pkg/domain/infra/abi/cp.go @@ -2,46 +2,53 @@ package abi import ( "context" - "strings" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/pkg/copy" "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/pkg/errors" ) func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - srcCtr, srcPath := parsePath(ic.Libpod, source) - destCtr, destPath := parsePath(ic.Libpod, dest) - - if srcCtr != nil && destCtr != nil { - return errors.Errorf("invalid arguments %q, %q: you must use just one container", source, dest) + // Parse user input. + sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest) + if err != nil { + return err } - if srcCtr == nil && destCtr == nil { - return errors.Errorf("invalid arguments %q, %q: you must specify one container", source, dest) + + // Look up containers. + var sourceContainer, destContainer *libpod.Container + if len(sourceContainerStr) > 0 { + sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr) + if err != nil { + return err + } } - if len(srcPath) == 0 || len(destPath) == 0 { - return errors.Errorf("invalid arguments %q, %q: you must specify paths", source, dest) + if len(destContainerStr) > 0 { + destContainer, err = ic.Libpod.LookupContainer(destContainerStr) + if err != nil { + return err + } } var sourceItem, destinationItem copy.CopyItem - var err error - // Copy from the container to the host. - if srcCtr != nil { - sourceItem, err = copy.CopyItemForContainer(srcCtr, srcPath, options.Pause, true) + + // Source ... container OR host. + if sourceContainer != nil { + sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true) defer sourceItem.CleanUp() if err != nil { return err } } else { - sourceItem, err = copy.CopyItemForHost(srcPath, true) + sourceItem, err = copy.CopyItemForHost(sourcePath, true) if err != nil { return err } } - if destCtr != nil { - destinationItem, err = copy.CopyItemForContainer(destCtr, destPath, options.Pause, false) + // Destination ... container OR host. + if destContainer != nil { + destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false) defer destinationItem.CleanUp() if err != nil { return err @@ -55,22 +62,9 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, } // Copy from the host to the container. - return copy.Copy(&sourceItem, &destinationItem, options.Extract) -} - -func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) { - if len(path) == 0 { - return nil, "" - } - if path[0] == '.' || path[0] == '/' { // A path cannot point to a container. - return nil, path - } - pathArr := strings.SplitN(path, ":", 2) - if len(pathArr) == 2 { - ctr, err := runtime.LookupContainer(pathArr[0]) - if err == nil { - return ctr, pathArr[1] - } + copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract) + if err != nil { + return err } - return nil, path + return copier.Copy() } diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index 79bf2291e..79f55e2bd 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -41,28 +41,48 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, return &entities.GenerateSystemdReport{Units: units}, nil } -func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { +func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { var ( - pod *libpod.Pod + pods []*libpod.Pod podYAML *k8sAPI.Pod err error - ctr *libpod.Container + ctrs []*libpod.Container servicePorts []k8sAPI.ServicePort serviceYAML k8sAPI.Service ) - // Get the container in question. - ctr, err = ic.Libpod.LookupContainer(nameOrID) - if err != nil { - pod, err = ic.Libpod.LookupPod(nameOrID) + for _, nameOrID := range nameOrIDs { + // Get the container in question + ctr, err := ic.Libpod.LookupContainer(nameOrID) if err != nil { - return nil, err + pod, err := ic.Libpod.LookupPod(nameOrID) + if err != nil { + return nil, err + } + pods = append(pods, pod) + if len(pods) > 1 { + return nil, errors.New("can only generate single pod at a time") + } + } else { + if len(ctr.Dependencies()) > 0 { + return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies") + } + // we cannot deal with ctrs already in a pod + if len(ctr.PodID()) > 0 { + return nil, errors.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID()) + } + ctrs = append(ctrs, ctr) } - podYAML, servicePorts, err = pod.GenerateForKube() + } + + // check our inputs + if len(pods) > 0 && len(ctrs) > 0 { + return nil, errors.New("cannot generate pods and containers at the same time") + } + + if len(pods) == 1 { + podYAML, servicePorts, err = pods[0].GenerateForKube() } else { - if len(ctr.Dependencies()) > 0 { - return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies") - } - podYAML, err = ctr.GenerateForKube() + podYAML, err = libpod.GenerateForKube(ctrs) } if err != nil { return nil, err @@ -72,7 +92,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, op serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts) } - content, err := generateKubeOutput(podYAML, &serviceYAML) + content, err := generateKubeOutput(podYAML, &serviceYAML, options.Service) if err != nil { return nil, err } @@ -80,7 +100,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, op return &entities.GenerateKubeReport{Reader: bytes.NewReader(content)}, nil } -func generateKubeOutput(podYAML *k8sAPI.Pod, serviceYAML *k8sAPI.Service) ([]byte, error) { +func generateKubeOutput(podYAML *k8sAPI.Pod, serviceYAML *k8sAPI.Service, hasService bool) ([]byte, error) { var ( output []byte marshalledPod []byte @@ -93,7 +113,7 @@ func generateKubeOutput(podYAML *k8sAPI.Pod, serviceYAML *k8sAPI.Service) ([]byt return nil, err } - if serviceYAML != nil { + if hasService { marshalledService, err = yaml.Marshal(serviceYAML) if err != nil { return nil, err @@ -114,7 +134,7 @@ func generateKubeOutput(podYAML *k8sAPI.Pod, serviceYAML *k8sAPI.Service) ([]byt output = append(output, []byte(fmt.Sprintf(header, podmanVersion.Version))...) output = append(output, marshalledPod...) - if serviceYAML != nil { + if hasService { output = append(output, []byte("---\n")...) output = append(output, marshalledService...) } diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index ad7128b42..600d64b1d 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -54,7 +54,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte } return buf, nil // no return if local image is not a list of images type - // continue on getting valid manifest through remote serice + // continue on getting valid manifest through remote service } else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported { return nil, errors.Wrapf(err, "loading manifest %q", name) } diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 6a219edd5..e5ecf5c72 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -60,6 +60,26 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri return rawCNINetworks, errs, nil } +func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { + ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) + if err != nil { + return nil, err + } + + reports := make([]*entities.NetworkReloadReport, 0, len(ctrs)) + for _, ctr := range ctrs { + report := new(entities.NetworkReloadReport) + report.Id = ctr.ID() + report.Err = ctr.ReloadNetwork() + if options.All && errors.Cause(report.Err) == define.ErrCtrStateInvalid { + continue + } + reports = append(reports, report) + } + + return reports, nil +} + func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { reports := []*entities.NetworkRmReport{} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index ec2532bea..7ed58092b 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -168,37 +168,61 @@ func checkInput() error { // nolint:deadcode,unused // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) - podPruneReport, err := ic.prunePodHelper(ctx) - if err != nil { - return nil, err - } - systemPruneReport.PodPruneReport = podPruneReport - - containerPruneReport, err := ic.pruneContainersHelper(nil) - if err != nil { - return nil, err - } - systemPruneReport.ContainerPruneReport = containerPruneReport - - results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil) - if err != nil { - return nil, err - } - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } + found := true + for found { + found = false + podPruneReport, err := ic.prunePodHelper(ctx) + if err != nil { + return nil, err + } + if len(podPruneReport) > 0 { + found = true + } + systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...) + containerPruneReport, err := ic.pruneContainersHelper(nil) + if err != nil { + return nil, err + } + if len(containerPruneReport.ID) > 0 { + found = true + } + if systemPruneReport.ContainerPruneReport == nil { + systemPruneReport.ContainerPruneReport = containerPruneReport + } else { + for name, val := range containerPruneReport.ID { + systemPruneReport.ContainerPruneReport.ID[name] = val + } + } - systemPruneReport.ImagePruneReport = &report + results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil) - if options.Volume { - volumePruneReport, err := ic.pruneVolumesHelper(ctx) if err != nil { return nil, err } - systemPruneReport.VolumePruneReport = volumePruneReport + if len(results) > 0 { + found = true + } + + if systemPruneReport.ImagePruneReport == nil { + systemPruneReport.ImagePruneReport = &entities.ImagePruneReport{ + Report: entities.Report{ + Id: results, + Err: nil, + }, + } + } else { + systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...) + } + if options.Volume { + volumePruneReport, err := ic.pruneVolumesHelper(ctx) + if err != nil { + return nil, err + } + if len(volumePruneReport) > 0 { + found = true + } + systemPruneReport.VolumePruneReport = append(systemPruneReport.VolumePruneReport, volumePruneReport...) + } } return systemPruneReport, nil } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 3584668c7..e65fef0a4 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -732,7 +732,8 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o } func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - return errors.New("not implemented") + return nil + // return containers.Copy(ic.ClientCxt, source, dest, options) } // Shutdown Libpod engine diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go index 966f707b1..ebbfa143f 100644 --- a/pkg/domain/infra/tunnel/generate.go +++ b/pkg/domain/infra/tunnel/generate.go @@ -11,6 +11,6 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, return generate.Systemd(ic.ClientCxt, nameOrID, options) } -func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { - return generate.Kube(ic.ClientCxt, nameOrID, options) +func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { + return generate.Kube(ic.ClientCxt, nameOrIDs, options) } diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 10ae03045..4845980f6 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -35,6 +35,10 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri return reports, errs, nil } +func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { + return nil, errors.New("not implemented") +} + func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds)) for _, name := range namesOrIds { diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index 1808f99b8..e0b039fb7 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -167,22 +167,23 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask g.AddLinuxMaskedPaths(mp) } } + for _, rp := range []string{ + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger", + } { + if !util.StringInSlice(rp, unmask) { + g.AddLinuxReadonlyPaths(rp) + } + } } if pidModeIsHost && rootless.IsRootless() { return } - - for _, rp := range []string{ - "/proc/asound", - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger", - } { - g.AddLinuxReadonlyPaths(rp) - } } // mask the paths provided by the user diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 5f72d28bb..5cc7891ac 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -148,6 +148,11 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI // Environment Variables envs := map[string]string{} + for _, env := range imageData.Config.Env { + keyval := strings.Split(env, "=") + envs[keyval[0]] = keyval[1] + } + for _, env := range containerYAML.Env { value := envVarValue(env, configMaps) diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index 2ef0f4c23..bb8edabb7 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -103,7 +103,7 @@ func VolumeFromSource(volumeSource v1.VolumeSource) (*KubeVolume, error) { } else if volumeSource.PersistentVolumeClaim != nil { return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim) } else { - return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the conly supported VolumeSource") + return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the only supported VolumeSource") } } diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go index 5c13c95b2..83ded059f 100644 --- a/pkg/specgen/generate/ports.go +++ b/pkg/specgen/generate/ports.go @@ -107,7 +107,11 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, var index uint16 for index = 0; index < len; index++ { cPort := containerPort + index - hPort := hostPort + index + hPort := hostPort + // Only increment host port if it's not 0. + if hostPort != 0 { + hPort += index + } if cPort == 0 { return nil, nil, nil, errors.Errorf("container port cannot be 0") @@ -162,8 +166,8 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, tempMappings, tempMapping{ mapping: cniPort, - startOfRange: port.Range > 0 && index == 0, - isInRange: port.Range > 0, + startOfRange: port.Range > 1 && index == 0, + isInRange: port.Range > 1, }, ) } @@ -183,7 +187,7 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, for _, tmp := range tempMappings { p := tmp.mapping - if p.HostPort != 0 && !tmp.isInRange { + if p.HostPort != 0 { remadeMappings = append(remadeMappings, p) continue } diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index dee140282..56947ff24 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -141,7 +141,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec.Process.Capabilities.Effective = caplist configSpec.Process.Capabilities.Permitted = caplist } else { - userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd) + userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ",")) } |