diff options
Diffstat (limited to 'pkg')
60 files changed, 906 insertions, 379 deletions
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 343c0d0b3..d5da22a91 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -1,9 +1,11 @@ package libpod import ( + "fmt" "io/ioutil" "net/http" "os" + "strings" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" @@ -206,7 +208,9 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) { } func Checkpoint(w http.ResponseWriter, r *http.Request) { - var targetFile string + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + containerEngine := abi.ContainerEngine{Libpod: runtime} + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Keep bool `schema:"keep"` @@ -214,6 +218,9 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) { TCPEstablished bool `schema:"tcpEstablished"` Export bool `schema:"export"` IgnoreRootFS bool `schema:"ignoreRootFS"` + PrintStats bool `schema:"printStats"` + PreCheckpoint bool `schema:"preCheckpoint"` + WithPrevious bool `schema:"withPrevious"` }{ // override any golang type defaults } @@ -223,57 +230,70 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } + name := utils.GetName(r) - runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - ctr, err := runtime.LookupContainer(name) - if err != nil { + if _, err := runtime.LookupContainer(name); err != nil { utils.ContainerNotFound(w, name, err) return } + names := []string{name} + + options := entities.CheckpointOptions{ + Keep: query.Keep, + LeaveRunning: query.LeaveRunning, + TCPEstablished: query.TCPEstablished, + IgnoreRootFS: query.IgnoreRootFS, + PrintStats: query.PrintStats, + PreCheckPoint: query.PreCheckpoint, + WithPrevious: query.WithPrevious, + } + if query.Export { - tmpFile, err := ioutil.TempFile("", "checkpoint") + f, err := ioutil.TempFile("", "checkpoint") if err != nil { utils.InternalServerError(w, err) return } - defer os.Remove(tmpFile.Name()) - if err := tmpFile.Close(); err != nil { + defer os.Remove(f.Name()) + if err := f.Close(); err != nil { utils.InternalServerError(w, err) return } - targetFile = tmpFile.Name() - } - options := libpod.ContainerCheckpointOptions{ - Keep: query.Keep, - KeepRunning: query.LeaveRunning, - TCPEstablished: query.TCPEstablished, - IgnoreRootfs: query.IgnoreRootFS, + options.Export = f.Name() } - if query.Export { - options.TargetFile = targetFile - } - err = ctr.Checkpoint(r.Context(), options) + + reports, err := containerEngine.ContainerCheckpoint(r.Context(), names, options) if err != nil { utils.InternalServerError(w, err) return } - if query.Export { - f, err := os.Open(targetFile) - if err != nil { - utils.InternalServerError(w, err) + + if !query.Export { + if len(reports) != 1 { + utils.InternalServerError(w, fmt.Errorf("expected 1 restore report but got %d", len(reports))) return } - defer f.Close() - utils.WriteResponse(w, http.StatusOK, f) + if reports[0].Err != nil { + utils.InternalServerError(w, reports[0].Err) + return + } + utils.WriteResponse(w, http.StatusOK, reports[0]) return } - utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()}) + + f, err := os.Open(options.Export) + if err != nil { + utils.InternalServerError(w, err) + return + } + defer f.Close() + utils.WriteResponse(w, http.StatusOK, f) } func Restore(w http.ResponseWriter, r *http.Request) { - var ( - targetFile string - ) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + containerEngine := abi.ContainerEngine{Libpod: runtime} + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Keep bool `schema:"keep"` @@ -284,6 +304,8 @@ func Restore(w http.ResponseWriter, r *http.Request) { IgnoreVolumes bool `schema:"ignoreVolumes"` IgnoreStaticIP bool `schema:"ignoreStaticIP"` IgnoreStaticMAC bool `schema:"ignoreStaticMAC"` + PrintStats bool `schema:"printStats"` + PublishPorts string `schema:"publishPorts"` }{ // override any golang type defaults } @@ -292,44 +314,55 @@ func Restore(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - name := utils.GetName(r) - runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - ctr, err := runtime.LookupContainer(name) - if err != nil { - utils.ContainerNotFound(w, name, err) - return + + options := entities.RestoreOptions{ + Name: query.Name, + Keep: query.Keep, + TCPEstablished: query.TCPEstablished, + IgnoreRootFS: query.IgnoreRootFS, + IgnoreVolumes: query.IgnoreVolumes, + IgnoreStaticIP: query.IgnoreStaticIP, + IgnoreStaticMAC: query.IgnoreStaticMAC, + PrintStats: query.PrintStats, + PublishPorts: strings.Fields(query.PublishPorts), } + + var names []string if query.Import { t, err := ioutil.TempFile("", "restore") if err != nil { utils.InternalServerError(w, err) return } - defer t.Close() + defer os.Remove(t.Name()) if err := compat.SaveFromBody(t, r); err != nil { utils.InternalServerError(w, err) return } - targetFile = t.Name() + options.Import = t.Name() + } else { + name := utils.GetName(r) + if _, err := runtime.LookupContainer(name); err != nil { + utils.ContainerNotFound(w, name, err) + return + } + names = []string{name} } - options := libpod.ContainerCheckpointOptions{ - Keep: query.Keep, - TCPEstablished: query.TCPEstablished, - IgnoreRootfs: query.IgnoreRootFS, - IgnoreStaticIP: query.IgnoreStaticIP, - IgnoreStaticMAC: query.IgnoreStaticMAC, - } - if query.Import { - options.TargetFile = targetFile - options.Name = query.Name - } - err = ctr.Restore(r.Context(), options) + reports, err := containerEngine.ContainerRestore(r.Context(), names, options) if err != nil { utils.InternalServerError(w, err) return } - utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()}) + if len(reports) != 1 { + utils.InternalServerError(w, fmt.Errorf("expected 1 restore report but got %d", len(reports))) + return + } + if reports[0].Err != nil { + utils.InternalServerError(w, reports[0].Err) + return + } + utils.WriteResponse(w, http.StatusOK, reports[0]) } func InitContainer(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index 117c5e2aa..5205d875d 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -17,14 +17,15 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Name bool `schema:"useName"` - New bool `schema:"new"` - NoHeader bool `schema:"noHeader"` - RestartPolicy *string `schema:"restartPolicy"` - StopTimeout uint `schema:"stopTimeout"` - ContainerPrefix string `schema:"containerPrefix"` - PodPrefix string `schema:"podPrefix"` - Separator string `schema:"separator"` + Name bool `schema:"useName"` + New bool `schema:"new"` + NoHeader bool `schema:"noHeader"` + TemplateUnitFile bool `schema:"templateUnitFile"` + RestartPolicy *string `schema:"restartPolicy"` + StopTimeout uint `schema:"stopTimeout"` + ContainerPrefix string `schema:"containerPrefix"` + PodPrefix string `schema:"podPrefix"` + Separator string `schema:"separator"` }{ StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, ContainerPrefix: "container", @@ -40,14 +41,15 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { containerEngine := abi.ContainerEngine{Libpod: runtime} options := entities.GenerateSystemdOptions{ - Name: query.Name, - New: query.New, - NoHeader: query.NoHeader, - RestartPolicy: query.RestartPolicy, - StopTimeout: &query.StopTimeout, - ContainerPrefix: query.ContainerPrefix, - PodPrefix: query.PodPrefix, - Separator: query.Separator, + Name: query.Name, + New: query.New, + NoHeader: query.NoHeader, + TemplateUnitFile: query.TemplateUnitFile, + RestartPolicy: query.RestartPolicy, + StopTimeout: &query.StopTimeout, + ContainerPrefix: query.ContainerPrefix, + PodPrefix: query.PodPrefix, + Separator: query.Separator, } report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options) diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 35120a1a5..f850db3d8 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -116,6 +116,8 @@ type CreateContainerConfig struct { dockerContainer.Config // desired container configuration HostConfig dockerContainer.HostConfig // host dependent configuration for container NetworkingConfig dockerNetwork.NetworkingConfig // network configuration for container + UnsetEnv []string // unset specified default environment variables + UnsetEnvAll bool // unset all default environment variables } // swagger:model IDResponse diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index c4919182b..601e1251b 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1441,6 +1441,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // name: ignoreRootFS // type: boolean // description: do not include root file-system changes when exporting + // - in: query + // name: printStats + // type: boolean + // description: add checkpoint statistics to the returned CheckpointReport // produces: // - application/json // responses: @@ -1495,6 +1499,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // name: ignoreStaticMAC // type: boolean // description: ignore MAC address if set statically + // - in: query + // name: printStats + // type: boolean + // description: add restore statistics to the returned RestoreReport // produces: // - application/json // responses: diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index a2be44ab4..b2e949f67 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -145,7 +145,7 @@ func pingNewConnection(ctx context.Context) error { return err } // the ping endpoint sits at / in this case - response, err := client.DoRequest(nil, http.MethodGet, "/_ping", nil, nil) + response, err := client.DoRequest(ctx, nil, http.MethodGet, "/_ping", nil, nil) if err != nil { return err } @@ -306,7 +306,7 @@ func unixClient(_url *url.URL) Connection { } // DoRequest assembles the http request and returns the response -func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, header map[string]string, pathValues ...string) (*APIResponse, error) { +func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, header map[string]string, pathValues ...string) (*APIResponse, error) { var ( err error response *http.Response @@ -328,7 +328,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, uri := fmt.Sprintf("http://d/v%d.%d.%d/libpod"+endpoint, params...) logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri) - req, err := http.NewRequestWithContext(context.WithValue(context.Background(), clientKey, c), httpMethod, uri, httpBody) + req, err := http.NewRequestWithContext(ctx, httpMethod, uri, httpBody) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/archive.go b/pkg/bindings/containers/archive.go index 876f5340b..d64fbffd6 100644 --- a/pkg/bindings/containers/archive.go +++ b/pkg/bindings/containers/archive.go @@ -23,7 +23,7 @@ func Stat(ctx context.Context, nameOrID string, path string) (*entities.Containe params := url.Values{} params.Set("path", path) - response, err := conn.DoRequest(nil, http.MethodHead, "/containers/%s/archive", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodHead, "/containers/%s/archive", params, nil, nameOrID) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func CopyFromArchiveWithOptions(ctx context.Context, nameOrID string, path strin params.Set("path", path) return func() error { - response, err := conn.DoRequest(reader, http.MethodPut, "/containers/%s/archive", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, reader, http.MethodPut, "/containers/%s/archive", params, nil, nameOrID) if err != nil { return err } @@ -92,7 +92,7 @@ func CopyToArchive(ctx context.Context, nameOrID string, path string, writer io. params := url.Values{} params.Set("path", path) - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/archive", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/archive", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index 47de89b33..baa3f182e 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -130,7 +130,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri IdleConnTimeout: time.Duration(0), } conn.Client.Transport = t - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/attach", params, headers, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/attach", params, headers, nameOrID) if err != nil { return err } @@ -322,7 +322,7 @@ func resizeTTY(ctx context.Context, endpoint string, height *int, width *int) er params.Set("w", strconv.Itoa(*width)) } params.Set("running", "true") - rsp, err := conn.DoRequest(nil, http.MethodPost, endpoint, params, nil) + rsp, err := conn.DoRequest(ctx, nil, http.MethodPost, endpoint, params, nil) if err != nil { return err } @@ -407,7 +407,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar // We need to inspect the exec session first to determine whether to use // -t. - resp, err := conn.DoRequest(nil, http.MethodGet, "/exec/%s/json", nil, nil, sessionID) + resp, err := conn.DoRequest(ctx, nil, http.MethodGet, "/exec/%s/json", nil, nil, sessionID) if err != nil { return err } @@ -478,7 +478,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar IdleConnTimeout: time.Duration(0), } conn.Client.Transport = t - response, err := conn.DoRequest(bytes.NewReader(bodyJSON), http.MethodPost, "/exec/%s/start", nil, nil, sessionID) + response, err := conn.DoRequest(ctx, bytes.NewReader(bodyJSON), http.MethodPost, "/exec/%s/start", nil, nil, sessionID) if err != nil { return err } diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index 7f7080f13..7b4ec093d 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -2,7 +2,9 @@ package containers import ( "context" + "io" "net/http" + "os" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" @@ -23,13 +25,34 @@ func Checkpoint(ctx context.Context, nameOrID string, options *CheckpointOptions if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrID) + + // "export" is a bool for the server so override it in the parameters + // if set. + export := false + if options.Export != nil && *options.Export != "" { + export = true + params.Set("export", "true") + } + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrID) if err != nil { return nil, err } defer response.Body.Close() - return &report, response.Process(&report) + if !export { + return &report, response.Process(&report) + } + + f, err := os.OpenFile(*options.Export, os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + return nil, err + } + defer f.Close() + if _, err := io.Copy(f, response.Body); err != nil { + return nil, err + } + + return &entities.CheckpointReport{}, nil } // Restore restores a checkpointed container to running. The container is identified by the nameOrID option. All @@ -47,12 +70,26 @@ func Restore(ctx context.Context, nameOrID string, options *RestoreOptions) (*en if err != nil { return nil, err } - // The import key is a reserved golang term - params.Del("ImportArchive") - if i := options.GetImportAchive(); options.Changed("ImportArchive") { - params.Set("import", i) + + for _, p := range options.PublishPorts { + params.Add("publishPorts", p) + } + + params.Del("ImportArchive") // The import key is a reserved golang term + + // Open the to-be-imported archive if needed. + var r io.Reader + if i := options.GetImportAchive(); i != "" { + params.Set("import", "true") + r, err = os.Open(i) + if err != nil { + return nil, err + } + // Hard-code the name since it will be ignored in any case. + nameOrID = "import" } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID) + + response, err := conn.DoRequest(ctx, r, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/commit.go b/pkg/bindings/containers/commit.go index a4adebb1f..372a99d32 100644 --- a/pkg/bindings/containers/commit.go +++ b/pkg/bindings/containers/commit.go @@ -24,7 +24,7 @@ func Commit(ctx context.Context, nameOrID string, options *CommitOptions) (handl return handlers.IDResponse{}, err } params.Set("container", nameOrID) - response, err := conn.DoRequest(nil, http.MethodPost, "/commit", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/commit", params, nil) if err != nil { return id, err } diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index aafb83f65..14a173025 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -38,7 +38,7 @@ func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/json", params, nil) if err != nil { return containers, err } @@ -64,7 +64,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/prune", params, nil) if err != nil { return nil, err } @@ -90,7 +90,7 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) if err != nil { return err } @@ -115,7 +115,7 @@ func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*de if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/json", params, nil, nameOrID) if err != nil { return nil, err } @@ -140,7 +140,7 @@ func Kill(ctx context.Context, nameOrID string, options *KillOptions) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/kill", params, nil, nameOrID) if err != nil { return err } @@ -160,7 +160,7 @@ func Pause(ctx context.Context, nameOrID string, options *PauseOptions) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/pause", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/pause", nil, nil, nameOrID) if err != nil { return err } @@ -184,7 +184,7 @@ func Restart(ctx context.Context, nameOrID string, options *RestartOptions) erro if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/restart", params, nil, nameOrID) if err != nil { return err } @@ -209,7 +209,7 @@ func Start(ctx context.Context, nameOrID string, options *StartOptions) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/start", params, nil, nameOrID) if err != nil { return err } @@ -235,7 +235,7 @@ func Stats(ctx context.Context, containers []string, options *StatsOptions) (cha params.Add("containers", c) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/stats", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/stats", params, nil) if err != nil { return nil, err } @@ -293,7 +293,7 @@ func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, e psArgs := strings.Join(options.GetDescriptors(), ",") params.Add("ps_args", psArgs) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/top", params, nil, nameOrID) if err != nil { return nil, err } @@ -326,7 +326,7 @@ func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) erro if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unpause", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/unpause", nil, nil, nameOrID) if err != nil { return err } @@ -351,7 +351,7 @@ func Wait(ctx context.Context, nameOrID string, options *WaitOptions) (int32, er if err != nil { return exitCode, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/wait", params, nil, nameOrID) if err != nil { return exitCode, err } @@ -372,7 +372,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/exists", params, nil, nameOrID) if err != nil { return false, err } @@ -395,7 +395,7 @@ func Stop(ctx context.Context, nameOrID string, options *StopOptions) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/stop", params, nil, nameOrID) if err != nil { return err } @@ -416,7 +416,7 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, options *ExportOp if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/export", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/export", params, nil, nameOrID) if err != nil { return err } @@ -441,7 +441,7 @@ func ContainerInit(ctx context.Context, nameOrID string, options *InitOptions) e if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/init", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/init", nil, nil, nameOrID) if err != nil { return err } @@ -462,7 +462,7 @@ func ShouldRestart(ctx context.Context, nameOrID string, options *ShouldRestartO if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/shouldrestart", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/shouldrestart", nil, nil, nameOrID) if err != nil { return false, err } diff --git a/pkg/bindings/containers/create.go b/pkg/bindings/containers/create.go index c0b9538a6..83b5b5ac7 100644 --- a/pkg/bindings/containers/create.go +++ b/pkg/bindings/containers/create.go @@ -26,7 +26,7 @@ func CreateWithSpec(ctx context.Context, s *specgen.SpecGenerator, options *Crea return ccr, err } stringReader := strings.NewReader(specgenString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/containers/create", nil, nil) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/containers/create", nil, nil) if err != nil { return ccr, err } diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go index e4ec49809..65a4f81bb 100644 --- a/pkg/bindings/containers/diff.go +++ b/pkg/bindings/containers/diff.go @@ -22,7 +22,7 @@ func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/changes", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go index 12b31aba3..e41ed66c6 100644 --- a/pkg/bindings/containers/exec.go +++ b/pkg/bindings/containers/exec.go @@ -35,7 +35,7 @@ func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreat } jsonReader := strings.NewReader(string(requestJSON)) - resp, err := conn.DoRequest(jsonReader, http.MethodPost, "/containers/%s/exec", nil, nil, nameOrID) + resp, err := conn.DoRequest(ctx, jsonReader, http.MethodPost, "/containers/%s/exec", nil, nil, nameOrID) if err != nil { return "", err } @@ -63,7 +63,7 @@ func ExecInspect(ctx context.Context, sessionID string, options *ExecInspectOpti logrus.Debugf("Inspecting session ID %q", sessionID) - resp, err := conn.DoRequest(nil, http.MethodGet, "/exec/%s/json", nil, nil, sessionID) + resp, err := conn.DoRequest(ctx, nil, http.MethodGet, "/exec/%s/json", nil, nil, sessionID) if err != nil { return nil, err } @@ -101,7 +101,7 @@ func ExecStart(ctx context.Context, sessionID string, options *ExecStartOptions) return err } - resp, err := conn.DoRequest(bytes.NewReader(bodyJSON), http.MethodPost, "/exec/%s/start", nil, nil, sessionID) + resp, err := conn.DoRequest(ctx, bytes.NewReader(bodyJSON), http.MethodPost, "/exec/%s/start", nil, nil, sessionID) if err != nil { return err } diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go index 0e65a5a46..990d8dc69 100644 --- a/pkg/bindings/containers/healthcheck.go +++ b/pkg/bindings/containers/healthcheck.go @@ -22,7 +22,7 @@ func RunHealthCheck(ctx context.Context, nameOrID string, options *HealthCheckOp var ( status define.HealthCheckResults ) - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/healthcheck", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/healthcheck", nil, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go index 37ffdf0a5..df1dd22ea 100644 --- a/pkg/bindings/containers/logs.go +++ b/pkg/bindings/containers/logs.go @@ -29,7 +29,7 @@ func Logs(ctx context.Context, nameOrID string, options *LogOptions, stdoutChan, if options.Stdout == nil && options.Stderr == nil { params.Set("stdout", strconv.FormatBool(true)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID) if err != nil { return err } diff --git a/pkg/bindings/containers/mount.go b/pkg/bindings/containers/mount.go index c07998fd3..5756c4cab 100644 --- a/pkg/bindings/containers/mount.go +++ b/pkg/bindings/containers/mount.go @@ -21,7 +21,7 @@ func Mount(ctx context.Context, nameOrID string, options *MountOptions) (string, var ( path string ) - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/mount", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/mount", nil, nil, nameOrID) if err != nil { return path, err } @@ -41,7 +41,7 @@ func Unmount(ctx context.Context, nameOrID string, options *UnmountOptions) erro if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unmount", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/unmount", nil, nil, nameOrID) if err != nil { return err } @@ -61,7 +61,7 @@ func GetMountedContainerPaths(ctx context.Context, options *MountedContainerPath return nil, err } mounts := make(map[string]string) - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/showmounted", nil, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/showmounted", nil, nil) if err != nil { return mounts, err } diff --git a/pkg/bindings/containers/rename.go b/pkg/bindings/containers/rename.go index 172d7838a..29dfc581b 100644 --- a/pkg/bindings/containers/rename.go +++ b/pkg/bindings/containers/rename.go @@ -20,7 +20,7 @@ func Rename(ctx context.Context, nameOrID string, options *RenameOptions) error if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/rename", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/containers/%s/rename", params, nil, nameOrID) if err != nil { return err } diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 3a7d5a4c7..81a53a549 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -50,12 +50,17 @@ type CheckpointOptions struct { Keep *bool LeaveRunning *bool TCPEstablished *bool + PrintStats *bool + PreCheckpoint *bool + WithPrevious *bool + FileLocks *bool } //go:generate go run ../generator/generator.go RestoreOptions // RestoreOptions are optional options for restoring containers type RestoreOptions struct { IgnoreRootfs *bool + IgnoreVolumes *bool IgnoreStaticIP *bool IgnoreStaticMAC *bool ImportAchive *string @@ -63,6 +68,9 @@ type RestoreOptions struct { Name *string TCPEstablished *bool Pod *string + PrintStats *bool + PublishPorts []string + FileLocks *bool } //go:generate go run ../generator/generator.go CreateOptions @@ -86,7 +94,8 @@ type ExecInspectOptions struct{} //go:generate go run ../generator/generator.go ExecStartOptions // ExecStartOptions are optional options for starting // exec sessions -type ExecStartOptions struct{} +type ExecStartOptions struct { +} //go:generate go run ../generator/generator.go HealthCheckOptions // HealthCheckOptions are optional options for checking diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go index 7b28c4045..391748d76 100644 --- a/pkg/bindings/containers/types_checkpoint_options.go +++ b/pkg/bindings/containers/types_checkpoint_options.go @@ -91,3 +91,63 @@ func (o *CheckpointOptions) GetTCPEstablished() bool { } return *o.TCPEstablished } + +// WithPrintStats set field PrintStats to given value +func (o *CheckpointOptions) WithPrintStats(value bool) *CheckpointOptions { + o.PrintStats = &value + return o +} + +// GetPrintStats returns value of field PrintStats +func (o *CheckpointOptions) GetPrintStats() bool { + if o.PrintStats == nil { + var z bool + return z + } + return *o.PrintStats +} + +// WithPreCheckpoint set field PreCheckpoint to given value +func (o *CheckpointOptions) WithPreCheckpoint(value bool) *CheckpointOptions { + o.PreCheckpoint = &value + return o +} + +// GetPreCheckpoint returns value of field PreCheckpoint +func (o *CheckpointOptions) GetPreCheckpoint() bool { + if o.PreCheckpoint == nil { + var z bool + return z + } + return *o.PreCheckpoint +} + +// WithWithPrevious set field WithPrevious to given value +func (o *CheckpointOptions) WithWithPrevious(value bool) *CheckpointOptions { + o.WithPrevious = &value + return o +} + +// GetWithPrevious returns value of field WithPrevious +func (o *CheckpointOptions) GetWithPrevious() bool { + if o.WithPrevious == nil { + var z bool + return z + } + return *o.WithPrevious +} + +// WithFileLocks set field FileLocks to given value +func (o *CheckpointOptions) WithFileLocks(value bool) *CheckpointOptions { + o.FileLocks = &value + return o +} + +// GetFileLocks returns value of field FileLocks +func (o *CheckpointOptions) GetFileLocks() bool { + if o.FileLocks == nil { + var z bool + return z + } + return *o.FileLocks +} diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go index 6eea108f4..7af2bba32 100644 --- a/pkg/bindings/containers/types_restore_options.go +++ b/pkg/bindings/containers/types_restore_options.go @@ -32,6 +32,21 @@ func (o *RestoreOptions) GetIgnoreRootfs() bool { return *o.IgnoreRootfs } +// WithIgnoreVolumes set field IgnoreVolumes to given value +func (o *RestoreOptions) WithIgnoreVolumes(value bool) *RestoreOptions { + o.IgnoreVolumes = &value + return o +} + +// GetIgnoreVolumes returns value of field IgnoreVolumes +func (o *RestoreOptions) GetIgnoreVolumes() bool { + if o.IgnoreVolumes == nil { + var z bool + return z + } + return *o.IgnoreVolumes +} + // WithIgnoreStaticIP set field IgnoreStaticIP to given value func (o *RestoreOptions) WithIgnoreStaticIP(value bool) *RestoreOptions { o.IgnoreStaticIP = &value @@ -136,3 +151,48 @@ func (o *RestoreOptions) GetPod() string { } return *o.Pod } + +// WithPrintStats set field PrintStats to given value +func (o *RestoreOptions) WithPrintStats(value bool) *RestoreOptions { + o.PrintStats = &value + return o +} + +// GetPrintStats returns value of field PrintStats +func (o *RestoreOptions) GetPrintStats() bool { + if o.PrintStats == nil { + var z bool + return z + } + return *o.PrintStats +} + +// WithPublishPorts set field PublishPorts to given value +func (o *RestoreOptions) WithPublishPorts(value []string) *RestoreOptions { + o.PublishPorts = value + return o +} + +// GetPublishPorts returns value of field PublishPorts +func (o *RestoreOptions) GetPublishPorts() []string { + if o.PublishPorts == nil { + var z []string + return z + } + return o.PublishPorts +} + +// WithFileLocks set field FileLocks to given value +func (o *RestoreOptions) WithFileLocks(value bool) *RestoreOptions { + o.FileLocks = &value + return o +} + +// GetFileLocks returns value of field FileLocks +func (o *RestoreOptions) GetFileLocks() bool { + if o.FileLocks == nil { + var z bool + return z + } + return *o.FileLocks +} diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go index 742956515..641c14231 100644 --- a/pkg/bindings/generate/generate.go +++ b/pkg/bindings/generate/generate.go @@ -22,7 +22,7 @@ func Systemd(ctx context.Context, nameOrID string, options *SystemdOptions) (*en return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/systemd", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/generate/%s/systemd", params, nil, nameOrID) if err != nil { return nil, err } @@ -54,7 +54,7 @@ func Kube(ctx context.Context, nameOrIDs []string, options *KubeOptions) (*entit for _, name := range nameOrIDs { params.Add("names", name) } - response, err := conn.DoRequest(nil, http.MethodGet, "/generate/kube", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/generate/kube", params, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/generate/types.go b/pkg/bindings/generate/types.go index 3c9ea87d4..6f2594604 100644 --- a/pkg/bindings/generate/types.go +++ b/pkg/bindings/generate/types.go @@ -16,6 +16,8 @@ type SystemdOptions struct { New *bool // NoHeader - Removes autogenerated by Podman and timestamp if set to true NoHeader *bool + // TemplateUnitFile - Create a template unit file that uses the identity specifiers + TemplateUnitFile *bool // RestartPolicy - systemd restart policy. RestartPolicy *string // StopTimeout - time when stopping the container. diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go index 7a778a52b..b26aa7fc2 100644 --- a/pkg/bindings/generate/types_systemd_options.go +++ b/pkg/bindings/generate/types_systemd_options.go @@ -62,6 +62,21 @@ func (o *SystemdOptions) GetNoHeader() bool { return *o.NoHeader } +// WithTemplateUnitFile set field TemplateUnitFile to given value +func (o *SystemdOptions) WithTemplateUnitFile(value bool) *SystemdOptions { + o.TemplateUnitFile = &value + return o +} + +// GetTemplateUnitFile returns value of field TemplateUnitFile +func (o *SystemdOptions) GetTemplateUnitFile() bool { + if o.TemplateUnitFile == nil { + var z bool + return z + } + return *o.TemplateUnitFile +} + // WithRestartPolicy set field RestartPolicy to given value func (o *SystemdOptions) WithRestartPolicy(value string) *SystemdOptions { o.RestartPolicy = &value diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 403d90721..3b0bebe9f 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -392,7 +392,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, headers) + response, err := conn.DoRequest(ctx, tarfile, http.MethodPost, "/build", params, headers) if err != nil { return nil, err } diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go index 671b73089..3df0b9615 100644 --- a/pkg/bindings/images/diff.go +++ b/pkg/bindings/images/diff.go @@ -19,7 +19,7 @@ func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/%s/changes", nil, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 959481e0d..dfb500772 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -23,7 +23,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/exists", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } @@ -47,7 +47,7 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.ImageSummary, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/json", params, nil) if err != nil { return imageSummary, err } @@ -71,7 +71,7 @@ func GetImage(ctx context.Context, nameOrID string, options *GetOptions) (*entit return nil, err } inspectedData := entities.ImageInspectReport{} - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/%s/json", params, nil, nameOrID) if err != nil { return &inspectedData, err } @@ -94,7 +94,7 @@ func Tree(ctx context.Context, nameOrID string, options *TreeOptions) (*entities if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrID) if err != nil { return nil, err } @@ -114,7 +114,7 @@ func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]* if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/history", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/%s/history", nil, nil, nameOrID) if err != nil { return history, err } @@ -129,7 +129,7 @@ func Load(ctx context.Context, r io.Reader) (*entities.ImageLoadReport, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(r, http.MethodPost, "/images/load", nil, nil) + response, err := conn.DoRequest(ctx, r, http.MethodPost, "/images/load", nil, nil) if err != nil { return nil, err } @@ -155,7 +155,7 @@ func Export(ctx context.Context, nameOrIDs []string, w io.Writer, options *Expor for _, ref := range nameOrIDs { params.Add("references", ref) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/export", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/export", params, nil) if err != nil { return err } @@ -185,7 +185,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/images/prune", params, nil) if err != nil { return deleted, err } @@ -207,7 +207,7 @@ func Tag(ctx context.Context, nameOrID, tag, repo string, options *TagOptions) e params := url.Values{} params.Set("tag", tag) params.Set("repo", repo) - response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/images/%s/tag", params, nil, nameOrID) if err != nil { return err } @@ -229,7 +229,7 @@ func Untag(ctx context.Context, nameOrID, tag, repo string, options *UntagOption params := url.Values{} params.Set("tag", tag) params.Set("repo", repo) - response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/untag", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/images/%s/untag", params, nil, nameOrID) if err != nil { return err } @@ -257,7 +257,7 @@ func Import(ctx context.Context, r io.Reader, options *ImportOptions) (*entities if err != nil { return nil, err } - response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params, nil) + response, err := conn.DoRequest(ctx, r, http.MethodPost, "/images/import", params, nil) if err != nil { return nil, err } @@ -298,7 +298,7 @@ func Push(ctx context.Context, source string, destination string, options *PushO params.Set("destination", destination) path := fmt.Sprintf("/images/%s/push", source) - response, err := conn.DoRequest(nil, http.MethodPost, path, params, header) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, path, params, header) if err != nil { return err } @@ -334,7 +334,7 @@ func Search(ctx context.Context, term string, options *SearchOptions) ([]entitie return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params, header) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/images/search", params, header) if err != nil { return nil, err } diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index 7dfe9560c..be21aa593 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -47,7 +47,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params, header) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/images/pull", params, header) if err != nil { return nil, err } diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go index 461eb7729..47d7c2a4b 100644 --- a/pkg/bindings/images/rm.go +++ b/pkg/bindings/images/rm.go @@ -32,7 +32,7 @@ func Remove(ctx context.Context, images []string, options *RemoveOptions) (*enti for _, image := range images { params.Add("images", image) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/remove", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/images/remove", params, nil) if err != nil { return nil, []error{err} } diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 6aa4961f1..af74eb406 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -42,7 +42,7 @@ func Create(ctx context.Context, names, images []string, options *CreateOptions) params.Add("image", i) } - response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/create", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/create", params, nil) if err != nil { return "", err } @@ -57,7 +57,7 @@ func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, err if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/exists", nil, nil, name) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/exists", nil, nil, name) if err != nil { return false, err } @@ -77,7 +77,7 @@ func Inspect(ctx context.Context, name string, options *InspectOptions) (*manife if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/json", nil, nil, name) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", nil, nil, name) if err != nil { return nil, err } @@ -102,7 +102,7 @@ func Add(ctx context.Context, name string, options *AddOptions) (string, error) return "", err } stringReader := strings.NewReader(optionsString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/add", nil, nil, name) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/manifests/%s/add", nil, nil, name) if err != nil { return "", err } @@ -125,7 +125,7 @@ func Remove(ctx context.Context, name, digest string, options *RemoveOptions) (s } params := url.Values{} params.Set("digest", digest) - response, err := conn.DoRequest(nil, http.MethodDelete, "/manifests/%s", params, nil, name) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/manifests/%s", params, nil, name) if err != nil { return "", err } @@ -163,7 +163,7 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt } params.Set("image", name) params.Set("destination", destination) - response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, nil, name) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/push", params, nil, name) if err != nil { return "", err } @@ -187,7 +187,7 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt // return "", err // } // stringReader := strings.NewReader(optionsString) -// response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/annotate", params, name) +// response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/manifests/%s/annotate", params, name) // if err != nil { // return "", err // } diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go index 5a0a34f56..172598be1 100644 --- a/pkg/bindings/network/network.go +++ b/pkg/bindings/network/network.go @@ -28,7 +28,7 @@ func Create(ctx context.Context, network *types.Network) (types.Network, error) return report, err } reader := strings.NewReader(networkConfig) - response, err := conn.DoRequest(reader, http.MethodPost, "/networks/create", nil, nil) + response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/create", nil, nil) if err != nil { return report, err } @@ -44,7 +44,7 @@ func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Net if err != nil { return net, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/json", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/networks/%s/json", nil, nil, nameOrID) if err != nil { return net, err } @@ -69,7 +69,7 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*en if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodDelete, "/networks/%s", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/networks/%s", params, nil, nameOrID) if err != nil { return nil, err } @@ -92,7 +92,7 @@ func List(ctx context.Context, options *ListOptions) ([]types.Network, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/networks/json", params, nil) if err != nil { return netList, err } @@ -128,7 +128,7 @@ func Disconnect(ctx context.Context, networkName string, ContainerNameOrID strin return err } stringReader := strings.NewReader(body) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/networks/%s/disconnect", params, nil, networkName) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/networks/%s/disconnect", params, nil, networkName) if err != nil { return err } @@ -163,7 +163,7 @@ func Connect(ctx context.Context, networkName string, ContainerNameOrID string, return err } stringReader := strings.NewReader(body) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/networks/%s/connect", params, nil, networkName) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/networks/%s/connect", params, nil, networkName) if err != nil { return err } @@ -178,7 +178,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/exists", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/networks/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } @@ -204,7 +204,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPrune return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/networks/prune", params, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index bdd13d03d..2cd7c3997 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -45,7 +45,7 @@ func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.Pla return nil, err } - response, err := conn.DoRequest(f, http.MethodPost, "/play/kube", params, header) + response, err := conn.DoRequest(ctx, f, http.MethodPost, "/play/kube", params, header) if err != nil { return nil, err } @@ -74,7 +74,7 @@ func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error logrus.Warn(err) } }() - response, err := conn.DoRequest(f, http.MethodDelete, "/play/kube", nil, nil) + response, err := conn.DoRequest(ctx, f, http.MethodDelete, "/play/kube", nil, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index 3b5832373..9e32f766d 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -29,7 +29,7 @@ func CreatePodFromSpec(ctx context.Context, spec *entities.PodSpec) (*entities.P return nil, err } stringReader := strings.NewReader(specString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil, nil) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/pods/create", nil, nil) if err != nil { return nil, err } @@ -44,7 +44,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/exists", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } @@ -66,7 +66,7 @@ func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*en if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/%s/json", nil, nil, nameOrID) if err != nil { return nil, err } @@ -92,7 +92,7 @@ func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entities if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/kill", params, nil, nameOrID) if err != nil { return nil, err } @@ -112,7 +112,7 @@ func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entiti if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/pause", nil, nil, nameOrID) if err != nil { return nil, err } @@ -133,7 +133,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*entities.PodPruneRepo if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/prune", nil, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/prune", nil, nil) if err != nil { return nil, err } @@ -159,7 +159,7 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.ListPodsReport if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/json", params, nil) if err != nil { return podsReports, err } @@ -179,7 +179,7 @@ func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*en if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/restart", nil, nil, nameOrID) if err != nil { return nil, err } @@ -203,7 +203,7 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*enti if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/pods/%s", params, nil, nameOrID) if err != nil { return nil, err } @@ -223,7 +223,7 @@ func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entiti if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/start", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/start", nil, nil, nameOrID) if err != nil { return nil, err } @@ -252,7 +252,7 @@ func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entities if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/stop", params, nil, nameOrID) if err != nil { return nil, err } @@ -279,7 +279,7 @@ func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, e if descriptors := options.GetDescriptors(); len(descriptors) > 0 { params.Set("ps_args", strings.Join(descriptors, ",")) } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/top", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/%s/top", params, nil, nameOrID) if err != nil { return nil, err } @@ -312,7 +312,7 @@ func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*en if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/unpause", nil, nil, nameOrID) if err != nil { return nil, err } @@ -339,7 +339,7 @@ func Stats(ctx context.Context, namesOrIDs []string, options *StatsOptions) ([]* } var reports []*entities.PodStatsReport - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/stats", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/stats", params, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/secrets/secrets.go b/pkg/bindings/secrets/secrets.go index c439971c9..3847188a5 100644 --- a/pkg/bindings/secrets/secrets.go +++ b/pkg/bindings/secrets/secrets.go @@ -22,7 +22,7 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.SecretInfoRepo if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/secrets/json", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/secrets/json", params, nil) if err != nil { return secrs, err } @@ -40,7 +40,7 @@ func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*en if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/secrets/%s/json", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/secrets/%s/json", nil, nil, nameOrID) if err != nil { return inspect, err } @@ -56,7 +56,7 @@ func Remove(ctx context.Context, nameOrID string) error { return err } - response, err := conn.DoRequest(nil, http.MethodDelete, "/secrets/%s", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/secrets/%s", nil, nil, nameOrID) if err != nil { return err } @@ -80,7 +80,7 @@ func Create(ctx context.Context, reader io.Reader, options *CreateOptions) (*ent return nil, err } - response, err := conn.DoRequest(reader, http.MethodPost, "/secrets/create", params, nil) + response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/secrets/create", params, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/system/info.go b/pkg/bindings/system/info.go index 8a307a4ca..8d7c30b26 100644 --- a/pkg/bindings/system/info.go +++ b/pkg/bindings/system/info.go @@ -14,7 +14,7 @@ func Info(ctx context.Context, _ *InfoOptions) (*define.Info, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/info", nil, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index 719cde52e..3f59b3d7e 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -27,7 +27,7 @@ func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodGet, "/events", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/events", params, nil) if err != nil { return err } @@ -73,7 +73,7 @@ func Prune(ctx context.Context, options *PruneOptions) (*entities.SystemPruneRep if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/system/prune", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/system/prune", params, nil) if err != nil { return nil, err } @@ -101,7 +101,7 @@ func Version(ctx context.Context, options *VersionOptions) (*entities.SystemVers if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/version", nil, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/version", nil, nil) if err != nil { return nil, err } @@ -142,7 +142,7 @@ func DiskUsage(ctx context.Context, options *DiskOptions) (*entities.SystemDfRep if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/system/df", nil, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/system/df", nil, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/test/connection_test.go b/pkg/bindings/test/connection_test.go new file mode 100644 index 000000000..561cf32b5 --- /dev/null +++ b/pkg/bindings/test/connection_test.go @@ -0,0 +1,68 @@ +package test_bindings + +import ( + "context" + "time" + + "github.com/containers/podman/v3/pkg/bindings/containers" + "github.com/containers/podman/v3/pkg/bindings/system" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman connection", func() { + var ( + bt *bindingTest + s *gexec.Session + ) + + BeforeEach(func() { + bt = newBindingTest() + bt.RestoreImagesFromCache() + s = bt.startAPIService() + time.Sleep(1 * time.Second) + err := bt.NewConnection() + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + s.Kill() + bt.cleanup() + }) + + It("request on cancelled context results in error", func() { + ctx, cancel := context.WithCancel(bt.conn) + cancel() + _, err := system.Version(ctx, nil) + Expect(err).To(MatchError(ctx.Err())) + }) + + It("cancel request in flight reports cancelled context", func() { + var name = "top" + _, err := bt.RunTopContainer(&name, nil) + Expect(err).To(BeNil()) + + errChan := make(chan error) + ctx, cancel := context.WithCancel(bt.conn) + + go func() { + defer close(errChan) + _, err := containers.Wait(ctx, name, nil) + errChan <- err + }() + + // Wait for the goroutine to fire the request + time.Sleep(1 * time.Second) + + cancel() + + select { + case err, ok := <-errChan: + Expect(ok).To(BeTrue()) + Expect(err).To(MatchError(ctx.Err())) + case <-time.NewTimer(1 * time.Second).C: + Fail("cancelled request did not return in less than 1 second") + } + }) +}) diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index 56cf13ade..ce5a01c49 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -29,7 +29,7 @@ func Create(ctx context.Context, config entities.VolumeCreateOptions, options *C return nil, err } stringReader := strings.NewReader(createString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/volumes/create", nil, nil) + response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/volumes/create", nil, nil) if err != nil { return nil, err } @@ -51,7 +51,7 @@ func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*en if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/json", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/volumes/%s/json", nil, nil, nameOrID) if err != nil { return &inspect, err } @@ -74,7 +74,7 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.VolumeListRepo if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/json", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/volumes/json", params, nil) if err != nil { return vols, err } @@ -96,7 +96,7 @@ func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", params, nil) + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/volumes/prune", params, nil) if err != nil { return nil, err } @@ -116,7 +116,7 @@ func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodDelete, "/volumes/%s", params, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/volumes/%s", params, nil, nameOrID) if err != nil { return err } @@ -131,7 +131,7 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool, if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/exists", nil, nil, nameOrID) + response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/volumes/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index da82c9745..3a300daaf 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -16,6 +16,7 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/specgen/generate" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/storage/pkg/archive" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -195,7 +196,12 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt } if len(restoreOptions.PublishPorts) > 0 { - ports, err := generate.ParsePortMapping(restoreOptions.PublishPorts, nil) + pubPorts, err := specgenutil.CreatePortBindings(restoreOptions.PublishPorts) + if err != nil { + return nil, err + } + + ports, err := generate.ParsePortMapping(pubPorts, nil) if err != nil { return nil, err } diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 869c616ea..1677c067f 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -190,11 +190,15 @@ type CheckpointOptions struct { PreCheckPoint bool WithPrevious bool Compression archive.Compression + PrintStats bool + FileLocks bool } type CheckpointReport struct { - Err error - Id string //nolint + Err error `json:"-"` + Id string `json:"Id` //nolint + RuntimeDuration int64 `json:"runtime_checkpoint_duration"` + CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"` } type RestoreOptions struct { @@ -209,13 +213,17 @@ type RestoreOptions struct { Name string TCPEstablished bool ImportPrevious string - PublishPorts []nettypes.PortMapping + PublishPorts []string Pod string + PrintStats bool + FileLocks bool } type RestoreReport struct { - Err error - Id string //nolint + Err error `json:"-"` + Id string `json:"Id` //nolint + RuntimeDuration int64 `json:"runtime_restore_duration"` + CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"` } type ContainerCreateReport struct { diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go index 7809c5241..dfb5bfc6c 100644 --- a/pkg/domain/entities/generate.go +++ b/pkg/domain/entities/generate.go @@ -20,6 +20,8 @@ type GenerateSystemdOptions struct { Separator string // NoHeader - skip header generation NoHeader bool + // TemplateUnitFile - make use of %i and %I to differentiate between the different instances of the unit + TemplateUnitFile bool } // GenerateSystemdReport diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 70d2be1e6..b255785c2 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -248,6 +248,8 @@ type ContainerCreateOptions struct { TTY bool Timezone string Umask string + UnsetEnv []string + UnsetEnvAll bool UIDMap []string Ulimit []string User string diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index c30129001..e04c7a38a 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -515,6 +515,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ PreCheckPoint: options.PreCheckPoint, WithPrevious: options.WithPrevious, Compression: options.Compression, + PrintStats: options.PrintStats, + FileLocks: options.FileLocks, } if options.All { @@ -531,10 +533,12 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } reports := make([]*entities.CheckpointReport, 0, len(cons)) for _, con := range cons { - err = con.Checkpoint(ctx, checkOpts) + criuStatistics, runtimeCheckpointDuration, err := con.Checkpoint(ctx, checkOpts) reports = append(reports, &entities.CheckpointReport{ - Err: err, - Id: con.ID(), + Err: err, + Id: con.ID(), + RuntimeDuration: runtimeCheckpointDuration, + CRIUStatistics: criuStatistics, }) } return reports, nil @@ -557,6 +561,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st IgnoreStaticMAC: options.IgnoreStaticMAC, ImportPrevious: options.ImportPrevious, Pod: options.Pod, + PrintStats: options.PrintStats, } filterFuncs := []libpod.ContainerFilter{ @@ -579,10 +584,12 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } reports := make([]*entities.RestoreReport, 0, len(cons)) for _, con := range cons { - err := con.Restore(ctx, restoreOptions) + criuStatistics, runtimeRestoreDuration, err := con.Restore(ctx, restoreOptions) reports = append(reports, &entities.RestoreReport{ - Err: err, - Id: con.ID(), + Err: err, + Id: con.ID(), + RuntimeDuration: runtimeRestoreDuration, + CRIUStatistics: criuStatistics, }) } return reports, nil diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 5b5a1912c..2127f8749 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -302,6 +302,17 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, } func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { + options := new(containers.CheckpointOptions) + options.WithFileLocks(opts.FileLocks) + options.WithIgnoreRootfs(opts.IgnoreRootFS) + options.WithKeep(opts.Keep) + options.WithExport(opts.Export) + options.WithTCPEstablished(opts.TCPEstablished) + options.WithPrintStats(opts.PrintStats) + options.WithPreCheckpoint(opts.PreCheckPoint) + options.WithLeaveRunning(opts.LeaveRunning) + options.WithWithPrevious(opts.WithPrevious) + var ( err error ctrs = []entities.ListContainer{} @@ -325,19 +336,41 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } } reports := make([]*entities.CheckpointReport, 0, len(ctrs)) - options := new(containers.CheckpointOptions).WithExport(opts.Export).WithIgnoreRootfs(opts.IgnoreRootFS).WithKeep(opts.Keep) - options.WithLeaveRunning(opts.LeaveRunning).WithTCPEstablished(opts.TCPEstablished) for _, c := range ctrs { report, err := containers.Checkpoint(ic.ClientCtx, c.ID, options) if err != nil { reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err}) + } else { + reports = append(reports, report) } - reports = append(reports, report) } return reports, nil } func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, opts entities.RestoreOptions) ([]*entities.RestoreReport, error) { + if opts.ImportPrevious != "" { + return nil, fmt.Errorf("--import-previous is not supported on the remote client") + } + + options := new(containers.RestoreOptions) + options.WithFileLocks(opts.FileLocks) + options.WithIgnoreRootfs(opts.IgnoreRootFS) + options.WithIgnoreVolumes(opts.IgnoreVolumes) + options.WithIgnoreStaticIP(opts.IgnoreStaticIP) + options.WithIgnoreStaticMAC(opts.IgnoreStaticMAC) + options.WithKeep(opts.Keep) + options.WithName(opts.Name) + options.WithTCPEstablished(opts.TCPEstablished) + options.WithPod(opts.Pod) + options.WithPrintStats(opts.PrintStats) + options.WithPublishPorts(opts.PublishPorts) + + if opts.Import != "" { + options.WithImportAchive(opts.Import) + report, err := containers.Restore(ic.ClientCtx, "", options) + return []*entities.RestoreReport{report}, err + } + var ( err error ctrs = []entities.ListContainer{} @@ -360,7 +393,6 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } } reports := make([]*entities.RestoreReport, 0, len(ctrs)) - options := new(containers.RestoreOptions) for _, c := range ctrs { report, err := containers.Restore(ic.ClientCtx, c.ID, options) if err != nil { diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go index 9f69abb1a..3a35dd59c 100644 --- a/pkg/domain/infra/tunnel/generate.go +++ b/pkg/domain/infra/tunnel/generate.go @@ -8,7 +8,7 @@ import ( ) func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { - options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New).WithNoHeader(opts.NoHeader) + options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New).WithNoHeader(opts.NoHeader).WithTemplateUnitFile(opts.TemplateUnitFile) options.WithPodPrefix(opts.PodPrefix).WithSeparator(opts.Separator) if opts.RestartPolicy != nil { options.WithRestartPolicy(*opts.RestartPolicy) diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 42d729458..e19940b22 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -81,7 +81,7 @@ func NewIgnitionFile(ign DynamicIgnition) error { // so a listening host knows it can being interacting with it ready := `[Unit] Requires=dev-virtio\\x2dports-%s.device -After=remove-moby.service +After=remove-moby.service sshd.socket sshd.service OnFailure=emergency.target OnFailureJobMode=isolate [Service] diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index a7174aac3..57c32bf74 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -664,9 +664,6 @@ func (v *MachineVM) startHostNetworking() error { return err } - // Listen on all at port 7777 for setting up and tearing - // down forwarding - listenSocket := "tcp://0.0.0.0:7777" qemuSocket, pidFile, err := v.getSocketandPid() if err != nil { return err @@ -676,7 +673,7 @@ func (v *MachineVM) startHostNetworking() error { files := []*os.File{os.Stdin, os.Stdout, os.Stderr} attr.Files = files cmd := []string{binary} - cmd = append(cmd, []string{"-listen", listenSocket, "-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...) + cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...) // Add the ssh port cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...) if logrus.GetLevel() == logrus.DebugLevel { diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go index 43cd3d69d..727a275d2 100644 --- a/pkg/machine/qemu/options_darwin_arm64.go +++ b/pkg/machine/qemu/options_darwin_arm64.go @@ -24,7 +24,7 @@ func (v *MachineVM) addArchOptions() []string { func (v *MachineVM) prepare() error { ovmfDir := getOvmfDir(v.ImagePath, v.Name) - cmd := []string{"dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} + cmd := []string{"/bin/dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} return exec.Command(cmd[0], cmd[1:]...).Run() } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 6ce4b1e29..e71d5d999 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -19,6 +19,33 @@ #include <sys/select.h> #include <stdio.h> +#define cleanup_free __attribute__ ((cleanup (cleanup_freep))) +#define cleanup_close __attribute__ ((cleanup (cleanup_closep))) +#define cleanup_dir __attribute__ ((cleanup (cleanup_dirp))) + +static inline void +cleanup_freep (void *p) +{ + void **pp = (void **) p; + free (*pp); +} + +static inline void +cleanup_closep (void *p) +{ + int *pp = p; + if (*pp >= 0) + TEMP_FAILURE_RETRY (close (*pp)); +} + +static inline void +cleanup_dirp (DIR **p) +{ + DIR *dir = *p; + if (dir) + closedir (dir); +} + int rename_noreplace (int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { int ret; @@ -106,6 +133,11 @@ do_pause () for (i = 0; sig[i]; i++) sigaction (sig[i], &act, NULL); + /* Attempt to execv catatonit to keep the pause process alive. */ + execl ("/usr/libexec/podman/catatonit", "catatonit", "-P", NULL); + execl ("/usr/bin/catatonit", "catatonit", "-P", NULL); + /* and if the catatonit executable could not be found, fallback here... */ + prctl (PR_SET_NAME, "podman pause", NULL, NULL, NULL); while (1) pause (); @@ -114,8 +146,8 @@ do_pause () static char ** get_cmd_line_args () { - int fd; - char *buffer; + cleanup_free char *buffer = NULL; + cleanup_close int fd = -1; size_t allocated; size_t used = 0; int ret; @@ -134,10 +166,7 @@ get_cmd_line_args () { ret = TEMP_FAILURE_RETRY (read (fd, buffer + used, allocated - used)); if (ret < 0) - { - free (buffer); - return NULL; - } + return NULL; if (ret == 0) break; @@ -148,30 +177,21 @@ get_cmd_line_args () allocated += 512; char *tmp = realloc (buffer, allocated); if (tmp == NULL) - { - free (buffer); - return NULL; - } + return NULL; buffer = tmp; } } - close (fd); for (i = 0; i < used; i++) if (buffer[i] == '\0') argc++; if (argc == 0) - { - free (buffer); - return NULL; - } + return NULL; argv = malloc (sizeof (char *) * (argc + 1)); if (argv == NULL) - { - free (buffer); - return NULL; - } + return NULL; + argc = 0; argv[argc++] = buffer; @@ -181,15 +201,19 @@ get_cmd_line_args () argv[argc] = NULL; + /* Move ownership. */ + buffer = NULL; + return argv; } static bool can_use_shortcut () { - int argc; - char **argv; + cleanup_free char **argv = NULL; + cleanup_free char *argv0 = NULL; bool ret = true; + int argc; #ifdef DISABLE_JOIN_SHORTCUT return false; @@ -199,12 +223,10 @@ can_use_shortcut () if (argv == NULL) return false; + argv0 = argv[0]; + if (strstr (argv[0], "podman") == NULL) - { - free (argv[0]); - free (argv); - return false; - } + return false; for (argc = 0; argv[argc]; argc++) { @@ -229,11 +251,25 @@ can_use_shortcut () } } - free (argv[0]); - free (argv); return ret; } +static int +open_namespace (int pid_to_join, const char *ns_file) +{ + char ns_path[PATH_MAX]; + int ret; + + ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file); + if (ret == PATH_MAX) + { + fprintf (stderr, "internal error: namespace path too long\n"); + return -1; + } + + return open (ns_path, O_CLOEXEC | O_RDONLY); +} + int is_fd_inherited(int fd) { @@ -250,8 +286,7 @@ static void __attribute__((constructor)) init() const char *listen_pid; const char *listen_fds; const char *listen_fdnames; - - DIR *d; + cleanup_dir DIR *d = NULL; pause = getenv ("_PODMAN_PAUSE"); if (pause && pause[0]) @@ -299,7 +334,6 @@ static void __attribute__((constructor)) init() FD_SET (fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])); } - closedir (d); } listen_pid = getenv("LISTEN_PID"); @@ -317,7 +351,7 @@ static void __attribute__((constructor)) init() if (saved_systemd_listen_pid == NULL || saved_systemd_listen_fds == NULL) { - fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno)); + fprintf (stderr, "save socket listen environments error: %m\n"); _exit (EXIT_FAILURE); } } @@ -327,73 +361,70 @@ static void __attribute__((constructor)) init() xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) { - int r; - int fd; + cleanup_free char *cwd = NULL; + cleanup_close int userns_fd = -1; + cleanup_close int mntns_fd = -1; + cleanup_close int fd = -1; long pid; char buf[12]; uid_t uid; gid_t gid; char path[PATH_MAX]; const char *const suffix = "/libpod/tmp/pause.pid"; - char *cwd = getcwd (NULL, 0); char uid_fmt[16]; char gid_fmt[16]; size_t len; + int r; + cwd = getcwd (NULL, 0); if (cwd == NULL) { - fprintf (stderr, "error getting current working directory: %s\n", strerror (errno)); + fprintf (stderr, "error getting current working directory: %m\n"); _exit (EXIT_FAILURE); } len = snprintf (path, PATH_MAX, "%s%s", xdg_runtime_dir, suffix); if (len >= PATH_MAX) { - fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %s", strerror (ENAMETOOLONG)); + errno = ENAMETOOLONG; + fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %m"); exit (EXIT_FAILURE); } fd = open (path, O_RDONLY); if (fd < 0) - { - free (cwd); - return; - } + return; r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof (buf) - 1)); - close (fd); + if (r < 0) - { - free (cwd); - return; - } + return; buf[r] = '\0'; pid = strtol (buf, NULL, 10); if (pid == LONG_MAX) - { - free (cwd); - return; - } + return; uid = geteuid (); gid = getegid (); - sprintf (path, "/proc/%ld/ns/user", pid); - fd = open (path, O_RDONLY); - if (fd < 0 || setns (fd, 0) < 0) - { - free (cwd); - return; - } - close (fd); + userns_fd = open_namespace (pid, "user"); + if (userns_fd < 0) + return; - /* Errors here cannot be ignored as we already joined a ns. */ - sprintf (path, "/proc/%ld/ns/mnt", pid); - fd = open (path, O_RDONLY); - if (fd < 0) + mntns_fd = open_namespace (pid, "mnt"); + if (mntns_fd < 0) + return; + + if (setns (userns_fd, 0) < 0) + return; + + /* The user namespace was joined, after this point errors are + not recoverable anymore. */ + + if (setns (mntns_fd, 0) < 0) { - fprintf (stderr, "cannot open %s: %s", path, strerror (errno)); + fprintf (stderr, "cannot join mount namespace for %ld: %m", pid); exit (EXIT_FAILURE); } @@ -404,33 +435,24 @@ static void __attribute__((constructor)) init() setenv ("_CONTAINERS_ROOTLESS_UID", uid_fmt, 1); setenv ("_CONTAINERS_ROOTLESS_GID", gid_fmt, 1); - r = setns (fd, 0); - if (r < 0) - { - fprintf (stderr, "cannot join mount namespace for %ld: %s", pid, strerror (errno)); - exit (EXIT_FAILURE); - } - close (fd); - if (syscall_setresgid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresgid: %m\n"); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresuid: %m\n"); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { - fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno)); + fprintf (stderr, "cannot chdir to %s: %m\n", cwd); _exit (EXIT_FAILURE); } - free (cwd); rootless_uid_init = uid; rootless_gid_init = gid; } @@ -529,7 +551,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv) fd = mkstemp (tmp_file_path); if (fd < 0) { - fprintf (stderr, "error creating temporary file: %s\n", strerror (errno)); + fprintf (stderr, "error creating temporary file: %m\n"); kill (pid, SIGKILL); _exit (EXIT_FAILURE); } @@ -537,7 +559,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv) r = TEMP_FAILURE_RETRY (write (fd, pid_str, strlen (pid_str))); if (r < 0) { - fprintf (stderr, "cannot write to file descriptor: %s\n", strerror (errno)); + fprintf (stderr, "cannot write to file descriptor: %m\n"); kill (pid, SIGKILL); _exit (EXIT_FAILURE); } @@ -555,7 +577,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv) r = TEMP_FAILURE_RETRY (write (p[1], "0", 1)); if (r < 0) { - fprintf (stderr, "cannot write to pipe: %s\n", strerror (errno)); + fprintf (stderr, "cannot write to pipe: %m\n"); _exit (EXIT_FAILURE); } close (p[1]); @@ -590,22 +612,6 @@ create_pause_process (const char *pause_pid_file_path, char **argv) } } -static int -open_namespace (int pid_to_join, const char *ns_file) -{ - char ns_path[PATH_MAX]; - int ret; - - ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file); - if (ret == PATH_MAX) - { - fprintf (stderr, "internal error: namespace path too long\n"); - return -1; - } - - return open (ns_path, O_CLOEXEC | O_RDONLY); -} - static void join_namespace_or_die (const char *name, int ns_fd) { @@ -619,18 +625,20 @@ join_namespace_or_die (const char *name, int ns_fd) int reexec_userns_join (int pid_to_join, char *pause_pid_file_path) { + cleanup_close int userns_fd = -1; + cleanup_close int mntns_fd = -1; + cleanup_free char *cwd = NULL; char uid[16]; char gid[16]; - char **argv; + cleanup_free char *argv0 = NULL; + cleanup_free char **argv = NULL; int pid; - int mnt_ns = -1; - int user_ns = -1; - char *cwd = getcwd (NULL, 0); sigset_t sigset, oldsigset; + cwd = getcwd (NULL, 0); if (cwd == NULL) { - fprintf (stderr, "error getting current working directory: %s\n", strerror (errno)); + fprintf (stderr, "error getting current working directory: %m\n"); _exit (EXIT_FAILURE); } @@ -640,32 +648,27 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) argv = get_cmd_line_args (); if (argv == NULL) { - fprintf (stderr, "cannot read argv: %s\n", strerror (errno)); + fprintf (stderr, "cannot read argv: %m\n"); _exit (EXIT_FAILURE); } - user_ns = open_namespace (pid_to_join, "user"); - if (user_ns < 0) - return user_ns; - mnt_ns = open_namespace (pid_to_join, "mnt"); - if (mnt_ns < 0) - { - close (user_ns); - return mnt_ns; - } + argv0 = argv[0]; + + userns_fd = open_namespace (pid_to_join, "user"); + if (userns_fd < 0) + return userns_fd; + mntns_fd = open_namespace (pid_to_join, "mnt"); + if (mntns_fd < 0) + return mntns_fd; pid = fork (); if (pid < 0) - fprintf (stderr, "cannot fork: %s\n", strerror (errno)); + fprintf (stderr, "cannot fork: %m\n"); if (pid) { int f; - /* We passed down these fds, close them. */ - close (user_ns); - close (mnt_ns); - for (f = 3; f <= open_files_max_fd; f++) if (is_fd_inherited (f)) close (f); @@ -681,22 +684,22 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) if (sigfillset (&sigset) < 0) { - fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno)); + fprintf (stderr, "cannot fill sigset: %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGCHLD) < 0) { - fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGTERM) < 0) { - fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n"); _exit (EXIT_FAILURE); } if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } @@ -717,33 +720,30 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) < 0) { - fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %s\n", strerror (errno)); + fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %m\n"); _exit (EXIT_FAILURE); } - join_namespace_or_die ("user", user_ns); - join_namespace_or_die ("mnt", mnt_ns); - close (user_ns); - close (mnt_ns); + join_namespace_or_die ("user", userns_fd); + join_namespace_or_die ("mnt", mntns_fd); if (syscall_setresgid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresgid: %m\n"); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresuid: %m\n"); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { - fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno)); + fprintf (stderr, "cannot chdir to %s: %m\n", cwd); _exit (EXIT_FAILURE); } - free (cwd); if (pause_pid_file_path && pause_pid_file_path[0] != '\0') { @@ -752,7 +752,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path) } if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } @@ -784,7 +784,7 @@ static int copy_file_to_fd (const char *file_to_read, int outfd) { char buf[512]; - int fd; + cleanup_close int fd = -1; fd = open (file_to_read, O_RDONLY); if (fd < 0) @@ -796,10 +796,7 @@ copy_file_to_fd (const char *file_to_read, int outfd) r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof buf)); if (r < 0) - { - close (fd); - return r; - } + return r; if (r == 0) break; @@ -808,43 +805,40 @@ copy_file_to_fd (const char *file_to_read, int outfd) { w = TEMP_FAILURE_RETRY (write (outfd, &buf[t], r - t)); if (w < 0) - { - close (fd); - return w; - } + return w; t += w; } } - close (fd); return 0; } int reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_read, int outputfd) { + cleanup_free char **argv = NULL; + cleanup_free char *argv0 = NULL; + cleanup_free char *cwd = NULL; + sigset_t sigset, oldsigset; int ret; pid_t pid; char b; - char **argv; char uid[16]; char gid[16]; - char *cwd = getcwd (NULL, 0); - sigset_t sigset, oldsigset; + cwd = getcwd (NULL, 0); if (cwd == NULL) { - fprintf (stderr, "error getting current working directory: %s\n", strerror (errno)); + fprintf (stderr, "error getting current working directory: %m\n"); _exit (EXIT_FAILURE); } - sprintf (uid, "%d", geteuid ()); sprintf (gid, "%d", getegid ()); pid = syscall_clone (CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL); if (pid < 0) { - fprintf (stderr, "cannot clone: %s\n", strerror (errno)); + fprintf (stderr, "cannot clone: %m\n"); check_proc_sys_userns_file (_max_user_namespaces); check_proc_sys_userns_file (_unprivileged_user_namespaces); } @@ -872,32 +866,34 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re if (sigfillset (&sigset) < 0) { - fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno)); + fprintf (stderr, "cannot fill sigset: %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGCHLD) < 0) { - fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGTERM) < 0) { - fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n"); _exit (EXIT_FAILURE); } if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } argv = get_cmd_line_args (); if (argv == NULL) { - fprintf (stderr, "cannot read argv: %s\n", strerror (errno)); + fprintf (stderr, "cannot read argv: %m\n"); _exit (EXIT_FAILURE); } + argv0 = argv[0]; + if (do_socket_activation) { char s[32]; @@ -916,7 +912,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re ret = TEMP_FAILURE_RETRY (read (ready, &b, 1)); if (ret < 0) { - fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno)); + fprintf (stderr, "cannot read from sync pipe: %m\n"); _exit (EXIT_FAILURE); } if (ret != 1 || b != '0') @@ -924,25 +920,24 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re if (syscall_setresgid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresgid: %m\n"); TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresuid: %m\n"); TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { - fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno)); + fprintf (stderr, "cannot chdir to %s: %m\n", cwd); TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } - free (cwd); if (pause_pid_file_path && pause_pid_file_path[0] != '\0') { @@ -956,14 +951,14 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re ret = TEMP_FAILURE_RETRY (write (ready, "0", 1)); if (ret < 0) { - fprintf (stderr, "cannot write to ready pipe: %s\n", strerror (errno)); - _exit (EXIT_FAILURE); + fprintf (stderr, "cannot write to ready pipe: %m\n"); + _exit (EXIT_FAILURE); } close (ready); if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 002b4ace3..40a18a6ac 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -88,9 +88,6 @@ 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["container"] == "" { - defaultEnvs["container"] = "podman" - } var envs map[string]string // Image Environment defaults @@ -101,9 +98,16 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat if err != nil { return nil, errors.Wrap(err, "Env fields from image failed to parse") } - defaultEnvs = envLib.Join(defaultEnvs, envs) + defaultEnvs = envLib.Join(envLib.DefaultEnvVariables(), envLib.Join(defaultEnvs, envs)) + } + + for _, e := range s.UnsetEnv { + delete(defaultEnvs, e) } + if s.UnsetEnvAll { + defaultEnvs = make(map[string]string) + } // First transform the os env into a map. We need it for the labels later in // any case. osEnv, err := envLib.ParseSlice(os.Environ()) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index f3dc28b01..f90fef9e8 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -474,6 +474,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. UID: s.UID, GID: s.GID, Mode: s.Mode, + Target: s.Target, }) } options = append(options, libpod.WithSecrets(secrs)) diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index beccd9fc2..1b022b912 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -298,7 +298,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt for key, val := range s.Annotations { g.AddAnnotation(key, val) } - g.AddProcessEnv("container", "podman") g.Config.Linux.Resources = s.ResourceLimits // Devices @@ -332,6 +331,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) + g.ClearProcessEnv() for name, val := range s.Env { g.AddProcessEnv(name, val) } diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index bfd81739a..72dd249e7 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -29,19 +29,16 @@ func buildPauseImage(rt *libpod.Runtime, rtConfig *config.Config) (string, error return imageName, nil } - // NOTE: Having the pause binary in its own directory keeps the door - // open for replacing the image building with using an overlay root FS. - // The latter turned out to be complex and error prone (see #11956) but - // we may be able to come up with a proper solution at a later point in - // time. - pausePath, err := rtConfig.FindHelperBinary("pause/pause", false) + // Also look into the path as some distributions install catatonit in + // /usr/bin. + catatonitPath, err := rtConfig.FindHelperBinary("catatonit", true) if err != nil { return "", fmt.Errorf("finding pause binary: %w", err) } buildContent := fmt.Sprintf(`FROM scratch -COPY %s /pause -ENTRYPOINT ["/pause"]`, pausePath) +COPY %s /catatonit +ENTRYPOINT ["/catatonit", "-P"]`, catatonitPath) tmpF, err := ioutil.TempFile("", "pause.containerfile") if err != nil { diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index d777287d7..0e257ad4c 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -194,6 +194,13 @@ type ContainerBasicConfig struct { // The execution domain system allows Linux to provide limited support // for binaries compiled under other UNIX-like operating systems. Personality *spec.LinuxPersonality `json:"personality,omitempty"` + // UnsetEnv unsets the specified default environment variables from the image or from buildin or containers.conf + // Optional. + UnsetEnv []string `json:"unsetenv,omitempty"` + // UnsetEnvAll unsetall default environment variables from the image or from buildin or containers.conf + // UnsetEnvAll unsets all default environment variables from the image or from buildin + // Optional. + UnsetEnvAll bool `json:"unsetenvall,omitempty"` } // ContainerStorageConfig contains information on the storage configuration of a @@ -540,6 +547,7 @@ func (s *SpecGenerator) GetImage() (*libimage.Image, string) { type Secret struct { Source string + Target string UID uint32 GID uint32 Mode uint32 diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 04d3add32..c110b9e97 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -711,6 +711,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.Umask = c.Umask s.PidFile = c.PidFile s.Volatile = c.Rm + s.UnsetEnv = c.UnsetEnv + s.UnsetEnvAll = c.UnsetEnvAll // Initcontainers s.InitContainerType = c.InitContainerType @@ -874,6 +876,7 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) if len(split) == 1 { mountSecret := specgen.Secret{ Source: val, + Target: target, UID: uid, GID: gid, Mode: mode, @@ -939,11 +942,9 @@ func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) } if secretType == "mount" { - if target != "" { - return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets") - } mountSecret := specgen.Secret{ Source: source, + Target: target, UID: uid, GID: gid, Mode: mode, diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go index 184bfadf8..8ff770f9c 100644 --- a/pkg/specgenutil/volumes.go +++ b/pkg/specgenutil/volumes.go @@ -355,6 +355,8 @@ func getBindMount(args []string) (spec.Mount, error) { newMount.Options = append(newMount.Options, "U") } setOwnership = true + case "idmap": + newMount.Options = append(newMount.Options, "idmap") case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 3515bb3b7..24c85a27e 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -23,7 +23,7 @@ func validateRestartPolicy(restart string) error { return errors.Errorf("%s is not a valid restart policy", restart) } -const headerTemplate = `# {{{{.ServiceName}}}}.service +const headerTemplate = `# {{{{.ServiceName}}}}{{{{- if (eq .IdentifySpecifier true) }}}}@{{{{- end}}}}.service {{{{- if (eq .GenerateNoHeader false) }}}} # autogenerated by Podman {{{{.PodmanVersion}}}} {{{{- if .TimeStamp}}}} @@ -32,7 +32,7 @@ const headerTemplate = `# {{{{.ServiceName}}}}.service {{{{- end}}}} [Unit] -Description=Podman {{{{.ServiceName}}}}.service +Description=Podman {{{{.ServiceName}}}}.service{{{{- if (eq .IdentifySpecifier true) }}}} for %I{{{{- end}}}} Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 037652a6d..95ff13371 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -90,6 +90,8 @@ type containerInfo struct { // Location of the RunRoot for the container. Required for ensuring the tmpfs // or volume exists and is mounted when coming online at boot. RunRoot string + // Add %i and %I to description and execute parts + IdentifySpecifier bool } const containerTemplate = headerTemplate + ` @@ -99,7 +101,7 @@ After={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{ {{{{- end}}}} [Service] -Environment={{{{.EnvVariable}}}}=%n +Environment={{{{.EnvVariable}}}}=%n{{{{- if (eq .IdentifySpecifier true) }}}}-%i{{{{- end}}}} {{{{- if .ExtraEnvs}}}} Environment={{{{- range $index, $value := .ExtraEnvs -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}} {{{{- end}}}} @@ -204,6 +206,46 @@ func containerServiceName(ctr *libpod.Container, options entities.GenerateSystem return nameOrID, serviceName } +// setContainerNameForTemplate updates startCommand to contain the name argument with +// a value that includes the identify specifier. +// In case startCommand doesn't contain that argument it's added after "run" and its +// value will be set to info.ServiceName concated with the identify specifier %i. +func setContainerNameForTemplate(startCommand []string, info *containerInfo) ([]string, error) { + // find the index of "--name" in the command slice + nameIx := -1 + for argIx, arg := range startCommand { + if arg == "--name" { + nameIx = argIx + 1 + break + } + if strings.HasPrefix(arg, "--name=") { + nameIx = argIx + break + } + } + switch { + case nameIx == -1: + // if not found, add --name argument in the command slice before the "run" argument. + // it's assumed that the command slice contains this argument. + runIx := -1 + for argIx, arg := range startCommand { + if arg == "run" { + runIx = argIx + break + } + } + if runIx == -1 { + return startCommand, fmt.Errorf("\"run\" is missing in the command arguments") + } + startCommand = append(startCommand[:runIx+1], startCommand[runIx:]...) + startCommand[runIx+1] = fmt.Sprintf("--name=%s-%%i", info.ServiceName) + default: + // append the identity specifier (%i) to the end of the --name value + startCommand[nameIx] = fmt.Sprintf("%s-%%i", startCommand[nameIx]) + } + return startCommand, nil +} + // executeContainerTemplate executes the container template on the specified // containerInfo. Note that the containerInfo is also post processed and // completed, which allows for an easier unit testing. @@ -273,7 +315,6 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst "--rm", ) remainingCmd := info.CreateCommand[index:] - // Presence check for certain flags/options. fs := pflag.NewFlagSet("args", pflag.ContinueOnError) fs.ParseErrorsWhitelist.UnknownFlags = true @@ -389,6 +430,13 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst startCommand = append(startCommand, remainingCmd...) startCommand = escapeSystemdArguments(startCommand) + if options.TemplateUnitFile { + info.IdentifySpecifier = true + startCommand, err = setContainerNameForTemplate(startCommand, info) + if err != nil { + return "", err + } + } info.ExecStart = strings.Join(startCommand, " ") } diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index f46513459..eab2c2e67 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -1,6 +1,7 @@ package generate import ( + "fmt" "testing" "github.com/containers/podman/v3/pkg/domain/entities" @@ -522,6 +523,32 @@ NotifyAccess=all [Install] WantedBy=multi-user.target default.target ` + + templateGood := `# container-foo@.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foo.service for %I +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n-%i +Restart=on-failure +StartLimitBurst=42 +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --name=container-foo-%i --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all + +[Install] +WantedBy=multi-user.target default.target +` tests := []struct { name string info containerInfo @@ -529,6 +556,7 @@ WantedBy=multi-user.target default.target new bool noHeader bool wantErr bool + template bool }{ {"good with id", @@ -547,6 +575,7 @@ WantedBy=multi-user.target default.target false, false, false, + false, }, {"good with noHeader", containerInfo{ @@ -564,6 +593,7 @@ WantedBy=multi-user.target default.target false, true, false, + false, }, {"good with name", containerInfo{ @@ -581,6 +611,7 @@ WantedBy=multi-user.target default.target false, false, false, + false, }, {"good with name and bound to", containerInfo{ @@ -599,6 +630,7 @@ WantedBy=multi-user.target default.target false, false, false, + false, }, {"good with name and generic", containerInfo{ @@ -617,6 +649,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with name and sdnotify", containerInfo{ @@ -635,6 +668,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with explicit short detach param", containerInfo{ @@ -653,6 +687,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with explicit short detach param and podInfo", containerInfo{ @@ -674,6 +709,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with explicit full detach param", containerInfo{ @@ -692,6 +728,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with id and no param", containerInfo{ @@ -710,6 +747,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with explicit detach=true param", containerInfo{ @@ -728,6 +766,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with explicit detach=false param", containerInfo{ @@ -746,6 +785,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with explicit detach=false param", containerInfo{ @@ -764,6 +804,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with multiple detach=false params", containerInfo{ @@ -782,6 +823,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with multiple shorthand params detach first", containerInfo{ @@ -800,6 +842,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with multiple shorthand params detach last", containerInfo{ @@ -818,6 +861,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with container create", containerInfo{ @@ -836,6 +880,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with journald log tag (see #9034)", containerInfo{ @@ -854,6 +899,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with special chars", containerInfo{ @@ -872,6 +918,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with ID files", containerInfo{ @@ -890,6 +937,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with pod ID files", containerInfo{ @@ -911,6 +959,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with environment variables", containerInfo{ @@ -930,6 +979,7 @@ WantedBy=multi-user.target default.target true, false, false, + false, }, {"good with restart policy", containerInfo{ @@ -948,14 +998,34 @@ WantedBy=multi-user.target default.target true, false, false, + false, + }, + {"good template", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foo", + ContainerNameOrID: "foo", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + CreateCommand: []string{"I'll get stripped", "create", "--restart", "on-failure:42", "awesome-image:latest"}, + }, + templateGood, + true, + false, + false, + true, }, } for _, tt := range tests { test := tt t.Run(tt.name, func(t *testing.T) { opts := entities.GenerateSystemdOptions{ - New: test.new, - NoHeader: test.noHeader, + New: test.new, + NoHeader: test.noHeader, + TemplateUnitFile: test.template, } test.info.RestartPolicy = define.DefaultRestartPolicy got, err := executeContainerTemplate(&test.info, opts) @@ -967,3 +1037,48 @@ WantedBy=multi-user.target default.target }) } } + +func TestSetContainerNameForTemplate(t *testing.T) { + tt := []struct { + name string + startCommand []string + info *containerInfo + expected []string + err error + }{ + { + name: "no name argument is set", + startCommand: []string{"/usr/bin/podman", "run", "busybox", "top"}, + info: &containerInfo{ServiceName: "container-122"}, + expected: []string{"/usr/bin/podman", "run", "--name=container-122-%i", "busybox", "top"}, + err: nil, + }, + { + name: "--name=value is used in arguments", + startCommand: []string{"/usr/bin/podman", "run", "--name=lovely_james", "busybox", "top"}, + info: &containerInfo{}, + expected: []string{"/usr/bin/podman", "run", "--name=lovely_james-%i", "busybox", "top"}, + err: nil, + }, + { + name: "--name value is used in arguments", + startCommand: []string{"/usr/bin/podman", "run", "--name", "lovely_james", "busybox", "top"}, + info: &containerInfo{}, + expected: []string{"/usr/bin/podman", "run", "--name", "lovely_james-%i", "busybox", "top"}, + err: nil, + }, + { + name: "--name value is used in arguments", + startCommand: []string{"/usr/bin/podman", "create", "busybox", "top"}, + info: &containerInfo{}, + expected: []string{"/usr/bin/podman", "create", "busybox", "top"}, + err: fmt.Errorf("\"run\" is missing in the command arguments"), + }, + } + + for _, te := range tt { + res, err := setContainerNameForTemplate(te.startCommand, te.info) + assert.Equal(t, te.err, err) + assert.Equal(t, te.expected, res) + } +} diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index e755b8eea..38f7e8e3e 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -79,6 +79,8 @@ type podInfo struct { // Location of the RunRoot for the pod. Required for ensuring the tmpfs // or volume exists and is mounted when coming online at boot. RunRoot string + // Add %i and %I to description and execute parts - this should not be used + IdentifySpecifier bool } const podTemplate = headerTemplate + `Requires={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} @@ -108,6 +110,9 @@ WantedBy=multi-user.target default.target // Based on the options, the return value might be the content of all units or // the files they been written to. func PodUnits(pod *libpod.Pod, options entities.GenerateSystemdOptions) (map[string]string, error) { + if options.TemplateUnitFile { + return nil, errors.New("--template is not supported for pods") + } // Error out if the pod has no infra container, which we require to be the // main service. if !pod.HasInfraContainer() { diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go index f13dc94ec..959763dba 100644 --- a/pkg/util/mountOpts.go +++ b/pkg/util/mountOpts.go @@ -33,6 +33,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string // Some options have parameters - size, mode splitOpt := strings.SplitN(opt, "=", 2) switch splitOpt[0] { + case "idmap": case "O": if len(options) > 1 { return nil, errors.Wrapf(ErrDupeMntOption, "'O' option can not be used with other options") |