diff options
Diffstat (limited to 'pkg')
53 files changed, 1431 insertions, 1028 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index b90f3d625..8ce2180ab 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -465,8 +465,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, ports := make(nat.PortSet) for p := range inspect.HostConfig.PortBindings { - splitp := strings.Split(p, "/") - port, err := nat.NewPort(splitp[0], splitp[1]) + splitp := strings.SplitN(p, "/", 2) + if len(splitp) != 2 { + return nil, errors.Errorf("PORT/PROTOCOL Format required for %q", p) + } + port, err := nat.NewPort(splitp[1], splitp[0]) if err != nil { return nil, err } diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 3d4bd4fb5..3ae9d9ab3 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -62,10 +62,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { func makeCreateConfig(containerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) { var ( - err error - init bool - tmpfs []string - volumes []string + err error + init bool ) env := make(map[string]string) stopSignal := unix.SIGTERM @@ -137,6 +135,7 @@ func makeCreateConfig(containerConfig *config.Config, input handlers.CreateConta User: input.User, } pidConfig := createconfig.PidConfig{PidMode: namespaces.PidMode(input.HostConfig.PidMode)} + volumes := make([]string, 0, len(input.Volumes)) for k := range input.Volumes { volumes = append(volumes, k) } @@ -158,6 +157,7 @@ func makeCreateConfig(containerConfig *config.Config, input handlers.CreateConta } // format the tmpfs mounts into a []string from map + tmpfs := make([]string, 0, len(input.HostConfig.Tmpfs)) for k, v := range input.HostConfig.Tmpfs { tmpfs = append(tmpfs, fmt.Sprintf("%s:%s", k, v)) } diff --git a/pkg/api/handlers/compat/images_history.go b/pkg/api/handlers/compat/images_history.go index afadf4c48..7c0bbf828 100644 --- a/pkg/api/handlers/compat/images_history.go +++ b/pkg/api/handlers/compat/images_history.go @@ -12,7 +12,6 @@ import ( func HistoryImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) - var allHistory []handlers.HistoryResponse newImage, err := runtime.ImageRuntime().NewFromLocal(name) if err != nil { @@ -25,6 +24,7 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + allHistory := make([]handlers.HistoryResponse, 0, len(history)) for _, h := range history { l := handlers.HistoryResponse{ ID: h.ID, diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go index d4a933c54..5c3f610a4 100644 --- a/pkg/api/handlers/compat/info.go +++ b/pkg/api/handlers/compat/info.go @@ -129,7 +129,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { } func getGraphStatus(storeInfo map[string]string) [][2]string { - var graphStatus [][2]string + graphStatus := make([][2]string, 0, len(storeInfo)) for k, v := range storeInfo { graphStatus = append(graphStatus, [2]string{k, v}) } diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index 8734ba405..0f1eca5e5 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -162,9 +162,6 @@ func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byt } func ListNetworks(w http.ResponseWriter, r *http.Request) { - var ( - reports []*types.NetworkResource - ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { @@ -191,6 +188,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + reports := make([]*types.NetworkResource, 0, len(netNames)) for _, name := range netNames { report, err := getNetworkResourceByName(name, runtime) if err != nil { @@ -215,7 +213,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { if len(networkCreate.Name) > 0 { name = networkCreate.Name } - // At present I think we should just suport the bridge driver + // At present I think we should just support the bridge driver // and allow demand to make us consider more if networkCreate.Driver != network.DefaultNetworkDriver { utils.InternalServerError(w, errors.New("network create only supports the bridge driver")) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 7d4d03144..4b57ef26a 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -89,7 +89,6 @@ func PodStop(w http.ResponseWriter, r *http.Request) { runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) responses map[string]error - errs []error ) query := struct { Timeout int `schema:"t"` @@ -128,6 +127,7 @@ func PodStop(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + var errs []error //nolint for _, err := range responses { errs = append(errs, err) } @@ -139,9 +139,7 @@ func PodStop(w http.ResponseWriter, r *http.Request) { } func PodStart(w http.ResponseWriter, r *http.Request) { - var ( - errs []error - ) + var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -206,9 +204,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { } func PodRestart(w http.ResponseWriter, r *http.Request) { - var ( - errs []error - ) + var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -243,12 +239,12 @@ func PodPrune(w http.ResponseWriter, r *http.Request) { func PodPruneHelper(w http.ResponseWriter, r *http.Request) ([]*entities.PodPruneReport, error) { var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) - reports []*entities.PodPruneReport ) responses, err := runtime.PrunePods(r.Context()) if err != nil { return nil, err } + reports := make([]*entities.PodPruneReport, 0, len(responses)) for k, v := range responses { reports = append(reports, &entities.PodPruneReport{ Err: v, @@ -259,9 +255,7 @@ func PodPruneHelper(w http.ResponseWriter, r *http.Request) ([]*entities.PodPrun } func PodPause(w http.ResponseWriter, r *http.Request) { - var ( - errs []error - ) + var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -285,9 +279,7 @@ func PodPause(w http.ResponseWriter, r *http.Request) { } func PodUnpause(w http.ResponseWriter, r *http.Request) { - var ( - errs []error - ) + var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -357,7 +349,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) { runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) signal = "SIGKILL" - errs []error + errs []error //nolint ) query := struct { Signal string `schema:"signal"` diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go index f575546c9..52d3b91ab 100644 --- a/pkg/api/handlers/libpod/system.go +++ b/pkg/api/handlers/libpod/system.go @@ -61,7 +61,7 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { systemPruneReport.ImagePruneReport = &report if query.Volumes { - volumePruneReport, err := pruneVolumesHelper(w, r) + volumePruneReport, err := pruneVolumesHelper(r) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index b5574b87b..ea035fc4d 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -102,9 +102,8 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { func ListVolumes(w http.ResponseWriter, r *http.Request) { var ( - decoder = r.Context().Value("decoder").(*schema.Decoder) - runtime = r.Context().Value("runtime").(*libpod.Runtime) - volumeConfigs []*entities.VolumeListReport + decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value("runtime").(*libpod.Runtime) ) query := struct { Filters map[string][]string `schema:"filters"` @@ -129,6 +128,7 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { config := entities.VolumeConfigResponse{ Name: v.Name(), @@ -147,7 +147,7 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } func PruneVolumes(w http.ResponseWriter, r *http.Request) { - reports, err := pruneVolumesHelper(w, r) + reports, err := pruneVolumesHelper(r) if err != nil { utils.InternalServerError(w, err) return @@ -155,15 +155,15 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, reports) } -func pruneVolumesHelper(w http.ResponseWriter, r *http.Request) ([]*entities.VolumePruneReport, error) { +func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) { var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) - reports []*entities.VolumePruneReport ) pruned, err := runtime.PruneVolumes(r.Context()) if err != nil { return nil, err } + reports := make([]*entities.VolumePruneReport, 0, len(pruned)) for k, v := range pruned { reports = append(reports, &entities.VolumePruneReport{ Err: v, diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 7fb31a177..521f727be 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -3,6 +3,7 @@ package utils import ( "fmt" "net/http" + "strings" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/storage" @@ -77,9 +78,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { if len(query.Filters) > 0 { for k, v := range query.Filters { - for _, val := range v { - filters = append(filters, fmt.Sprintf("%s=%s", k, val)) - } + filters = append(filters, fmt.Sprintf("%s=%s", k, strings.Join(v, "="))) } images, err = runtime.ImageRuntime().GetImagesWithFilters(filters) if err != nil { @@ -94,7 +93,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { if query.All { return images, nil } - var returnImages []*image.Image + returnImages := []*image.Image{} for _, img := range images { if len(img.Names()) == 0 { parent, err := img.IsParent(r.Context()) diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go index 4a5cbd05c..0bb818c1c 100644 --- a/pkg/api/handlers/utils/pods.go +++ b/pkg/api/handlers/utils/pods.go @@ -11,7 +11,6 @@ import ( func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) { var ( - lps []*entities.ListPodsReport pods []*libpod.Pod filters []libpod.PodFilter ) @@ -45,6 +44,11 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport return nil, err } + if len(pods) == 0 { + return nil, nil + } + + lps := make([]*entities.ListPodsReport, 0, len(pods)) for _, pod := range pods { status, err := pod.GetPodStatus() if err != nil { diff --git a/pkg/api/server/docs.go b/pkg/api/server/docs.go index c989c7927..124c16092 100644 --- a/pkg/api/server/docs.go +++ b/pkg/api/server/docs.go @@ -4,6 +4,31 @@ // only as experimental as this point. The endpoints, parameters, inputs, and // return values can all change. // +// To start the service and keep it running for 5,000 seconds (-t 0 runs forever): +// +// podman system service -t 5000 & +// +// You can then use cURL on the socket using requests documented below. +// +// NOTE: if you install the package podman-docker, it will create a symbolic +// link for /var/run/docker.sock to /run/podman/podman.sock +// +// See podman-service(1) for more information. +// +// Quick Examples: +// +// 'podman info' +// +// curl --unix-socket /run/podman/podman.sock http://d/v1.0.0/libpod/info +// +// 'podman pull quay.io/containers/podman' +// +// curl -XPOST --unix-socket /run/podman/podman.sock -v 'http://d/v1.0.0/images/create?fromImage=quay.io%2Fcontainers%2Fpodman' +// +// 'podman list images' +// +// curl --unix-socket /run/podman/podman.sock -v 'http://d/v1.0.0/libpod/images/json' | jq +// // Terms Of Service: // // Schemes: http, https diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index aa7f3707c..a9c61e5ae 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -115,12 +115,12 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, passPhrase strin _url.Path = JoinURL(_url.Host, _url.Path) _url.Host = "" } - connection, err = unixClient(_url) + connection = unixClient(_url) case "tcp": if !strings.HasPrefix(uri, "tcp://") { return nil, errors.New("tcp URIs should begin with tcp://") } - connection, err = tcpClient(_url) + connection = tcpClient(_url) default: return nil, errors.Errorf("'%s' is not a supported schema", _url.Scheme) } @@ -135,7 +135,7 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, passPhrase strin return ctx, nil } -func tcpClient(_url *url.URL) (Connection, error) { +func tcpClient(_url *url.URL) Connection { connection := Connection{ URI: _url, } @@ -147,7 +147,7 @@ func tcpClient(_url *url.URL) (Connection, error) { DisableCompression: true, }, } - return connection, nil + return connection } // pingNewConnection pings to make sure the RESTFUL service is up @@ -186,8 +186,7 @@ func pingNewConnection(ctx context.Context) error { } func sshClient(_url *url.URL, secure bool, passPhrase string, identities ...string) (Connection, error) { - var authMethods []ssh.AuthMethod - + authMethods := []ssh.AuthMethod{} for _, i := range identities { auth, err := publicKey(i, []byte(passPhrase)) if err != nil { @@ -256,7 +255,7 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identities ...stri return connection, nil } -func unixClient(_url *url.URL) (Connection, error) { +func unixClient(_url *url.URL) Connection { connection := Connection{URI: _url} connection.Client = &http.Client{ Transport: &http.Transport{ @@ -266,7 +265,7 @@ func unixClient(_url *url.URL) (Connection, error) { DisableCompression: true, }, } - return connection, nil + return connection } // DoRequest assembles the http request and returns the response diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index 916ec8071..8a3932e80 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -42,7 +42,7 @@ func Checkpoint(ctx context.Context, nameOrID string, keep, leaveRunning, tcpEst } // Restore restores a checkpointed container to running. The container is identified by the nameOrID option. All -// additional options are optional and allow finer control of the restore processs. +// additional options are optional and allow finer control of the restore process. func Restore(ctx context.Context, nameOrID string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) { var report entities.RestoreReport conn, err := bindings.GetClient(ctx) diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 31daaf565..f288c2944 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -12,7 +12,7 @@ type LogOptions struct { Until *string } -// CommitOptions describe details about the resulting commited +// CommitOptions describe details about the resulting committed // image as defined by repo and tag. None of these options // are required. type CommitOptions struct { diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index a82a9080b..9cb6a0ac5 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -394,7 +394,7 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption } // Push is the binding for libpod's v2 endpoints for push images. Note that -// `source` must be a refering to an image in the remote's container storage. +// `source` must be a referring to an image in the remote's container storage. // The destination must be a reference to a registry (i.e., of docker transport // or be normalized to one). Other transports are rejected as they do not make // sense in a remote context. diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 3b56f944f..399072108 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -133,7 +133,7 @@ func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) if err != nil { return nil, errors.Wrapf(err, "read directory %s", cgroupRoot) } - var controllers []controller + controllers := []controller{} for _, i := range infos { name := i.Name() if _, found := exclude[name]; found { @@ -505,7 +505,7 @@ func (c *CgroupControl) AddPid(pid int) error { return nil } - var names []string + names := make([]string, 0, len(handlers)) for n := range handlers { names = append(names, n) } diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go index 5f0a18031..3745c6e50 100644 --- a/pkg/cgroups/cpu.go +++ b/pkg/cgroups/cpu.go @@ -29,13 +29,12 @@ func readAcct(ctr *CgroupControl, name string) (uint64, error) { } func readAcctList(ctr *CgroupControl, name string) ([]uint64, error) { - var r []uint64 - p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name) data, err := ioutil.ReadFile(p) if err != nil { return nil, errors.Wrapf(err, "reading %s", p) } + r := []uint64{} for _, s := range strings.Split(string(data), " ") { s = cleanString(s) if s == "" { diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go index 68a42d897..a8ad13705 100644 --- a/pkg/domain/entities/generate.go +++ b/pkg/domain/entities/generate.go @@ -18,7 +18,7 @@ type GenerateSystemdOptions struct { ContainerPrefix string // PodPrefix - systemd unit name prefix for pods PodPrefix string - // Separator - systemd unit name seperator between name/id and prefix + // Separator - systemd unit name separator between name/id and prefix Separator string } diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index a85333c75..fc76ddd41 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -103,15 +103,16 @@ type PodRmReport struct { } type PodCreateOptions struct { - CGroupParent string - Hostname string - Infra bool - InfraImage string - InfraCommand string - Labels map[string]string - Name string - Net *NetOptions - Share []string + CGroupParent string + Hostname string + Infra bool + InfraImage string + InfraCommand string + InfraConmonPidFile string + Labels map[string]string + Name string + Net *NetOptions + Share []string } type PodCreateReport struct { @@ -127,6 +128,9 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { if len(p.InfraCommand) > 0 { s.InfraCommand = strings.Split(p.InfraCommand, " ") } + if len(p.InfraConmonPidFile) > 0 { + s.InfraConmonPidFile = p.InfraConmonPidFile + } s.InfraImage = p.InfraImage s.SharedNamespaces = p.Share diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 4d6d0d59a..d2c8aefdc 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -84,13 +84,11 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { - var ( - responses []entities.WaitReport - ) ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } + responses := make([]entities.WaitReport, 0, len(ctrs)) for _, c := range ctrs { response := entities.WaitReport{Id: c.ID()} exitCode, err := c.WaitForConditionWithInterval(options.Interval, options.Condition) @@ -106,10 +104,9 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { var ( - ctrs []*libpod.Container - err error - report []*entities.PauseUnpauseReport + err error ) + ctrs := []*libpod.Container{} //nolint if options.All { ctrs, err = ic.Libpod.GetAllContainers() } else { @@ -118,6 +115,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri if err != nil { return nil, err } + report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := c.Pause() report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) @@ -127,10 +125,9 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { var ( - ctrs []*libpod.Container - err error - report []*entities.PauseUnpauseReport + err error ) + ctrs := []*libpod.Container{} //nolint if options.All { ctrs, err = ic.Libpod.GetAllContainers() } else { @@ -139,6 +136,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st if err != nil { return nil, err } + report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := c.Unpause() report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) @@ -146,9 +144,6 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st return report, nil } func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { - var ( - reports []*entities.StopReport - ) names := namesOrIds for _, cidFile := range options.CIDFiles { content, err := ioutil.ReadFile(cidFile) @@ -184,6 +179,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin if err != nil { return nil, err } + reports := make([]*entities.StopReport, 0, len(errMap)) for ctr, err := range errMap { report := new(entities.StopReport) report.Id = ctr.ID() @@ -204,10 +200,10 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. filterFuncs = append(filterFuncs, generatedFunc) } } - return ic.pruneContainersHelper(ctx, filterFuncs) + return ic.pruneContainersHelper(filterFuncs) } -func (ic *ContainerEngine) pruneContainersHelper(ctx context.Context, filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { +func (ic *ContainerEngine) pruneContainersHelper(filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) if err != nil { return nil, err @@ -220,9 +216,6 @@ func (ic *ContainerEngine) pruneContainersHelper(ctx context.Context, filterFunc } func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { - var ( - reports []*entities.KillReport - ) sig, err := signal.ParseSignalNameOrNumber(options.Signal) if err != nil { return nil, err @@ -231,6 +224,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin if err != nil { return nil, err } + reports := make([]*entities.KillReport, 0, len(ctrs)) for _, con := range ctrs { reports = append(reports, &entities.KillReport{ Id: con.ID(), @@ -241,9 +235,8 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { var ( - ctrs []*libpod.Container - err error - reports []*entities.RestartReport + ctrs []*libpod.Container + err error ) if options.Running { @@ -258,6 +251,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st } } + reports := make([]*entities.RestartReport, 0, len(ctrs)) for _, con := range ctrs { timeout := con.StopTimeout() if options.Timeout != nil { @@ -272,9 +266,7 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { - var ( - reports []*entities.RmReport - ) + reports := []*entities.RmReport{} if options.Storage { for _, ctr := range namesOrIds { report := entities.RmReport{Id: ctr} @@ -347,11 +339,11 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, } func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) { - var reports []*entities.ContainerInspectReport ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } + reports := make([]*entities.ContainerInspectReport, 0, len(ctrs)) for _, c := range ctrs { data, err := c.Inspect(options.Size) if err != nil { @@ -439,9 +431,8 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { var ( - err error - cons []*libpod.Container - reports []*entities.CheckpointReport + err error + cons []*libpod.Container ) checkOpts := libpod.ContainerCheckpointOptions{ Keep: options.Keep, @@ -463,6 +454,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ if err != nil { return nil, err } + reports := make([]*entities.CheckpointReport, 0, len(cons)) for _, con := range cons { err = con.Checkpoint(ctx, checkOpts) reports = append(reports, &entities.CheckpointReport{ @@ -475,10 +467,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) { var ( - cons []*libpod.Container - err error - filterFuncs []libpod.ContainerFilter - reports []*entities.RestoreReport + cons []*libpod.Container + err error ) restoreOptions := libpod.ContainerCheckpointOptions{ @@ -491,10 +481,12 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st IgnoreStaticMAC: options.IgnoreStaticMAC, } - filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { - state, _ := c.State() - return state == define.ContainerStateExited - }) + filterFuncs := []libpod.ContainerFilter{ + func(c *libpod.Container) bool { + state, _ := c.State() + return state == define.ContainerStateExited + }, + } switch { case options.Import != "": @@ -507,6 +499,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st if err != nil { return nil, err } + reports := make([]*entities.RestoreReport, 0, len(cons)) for _, con := range cons { err := con.Restore(ctx, restoreOptions) reports = append(reports, &entities.RestoreReport{ @@ -565,34 +558,34 @@ func makeExecConfig(options entities.ExecOptions) *libpod.ExecConfig { return execConfig } -func checkExecPreserveFDs(options entities.ExecOptions) (int, error) { - ec := define.ExecErrorCodeGeneric +func checkExecPreserveFDs(options entities.ExecOptions) error { if options.PreserveFDs > 0 { entries, err := ioutil.ReadDir("/proc/self/fd") if err != nil { - return ec, errors.Wrapf(err, "unable to read /proc/self/fd") + return errors.Wrapf(err, "unable to read /proc/self/fd") } m := make(map[int]bool) for _, e := range entries { i, err := strconv.Atoi(e.Name()) if err != nil { - return ec, errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) + return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) } m[i] = true } for i := 3; i < 3+int(options.PreserveFDs); i++ { if _, found := m[i]; !found { - return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available") + return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") } } } - return ec, nil + return nil } func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { - ec, err := checkExecPreserveFDs(options) + ec := define.ExecErrorCodeGeneric + err := checkExecPreserveFDs(options) if err != nil { return ec, err } @@ -609,7 +602,7 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, o } func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { - _, err := checkExecPreserveFDs(options) + err := checkExecPreserveFDs(options) if err != nil { return "", err } @@ -648,7 +641,7 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s } func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { - var reports []*entities.ContainerStartReport + reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric ctrs, rawInputs, err := getContainersAndInputByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { @@ -907,7 +900,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin } func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { - var reports []*entities.ContainerCleanupReport + reports := []*entities.ContainerCleanupReport{} ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err @@ -958,11 +951,11 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { - var reports []*entities.ContainerInitReport ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } + reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) for _, ctr := range ctrs { report := entities.ContainerInitReport{Id: ctr.ID()} err := ctr.Init(ctx) @@ -993,11 +986,11 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin os.Exit(ret) } } - var reports []*entities.ContainerMountReport ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod) if err != nil { return nil, err } + reports := make([]*entities.ContainerMountReport, 0, len(ctrs)) for _, ctr := range ctrs { report := entities.ContainerMountReport{Id: ctr.ID()} report.Path, report.Err = ctr.Mount() @@ -1030,11 +1023,11 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []strin } func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { - var reports []*entities.ContainerUnmountReport ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod) if err != nil { return nil, err } + reports := []*entities.ContainerUnmountReport{} for _, ctr := range ctrs { state, err := ctr.State() if err != nil { @@ -1065,11 +1058,11 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { } func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { - var reports []*entities.ContainerPortReport ctrs, err := getContainersByContext(options.All, options.Latest, []string{nameOrID}, ic.Libpod) if err != nil { return nil, err } + reports := []*entities.ContainerPortReport{} for _, con := range ctrs { state, err := con.State() if err != nil { diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go index 41f4444d5..37422aac5 100644 --- a/pkg/domain/infra/abi/containers_runlabel.go +++ b/pkg/domain/infra/abi/containers_runlabel.go @@ -116,7 +116,7 @@ func generateRunlabelCommand(runlabel string, img *image.Image, args []string, o err error name, imageName string globalOpts string - cmd, env []string + cmd []string ) // TODO: How do we get global opts as done in v1? @@ -149,7 +149,7 @@ func generateRunlabelCommand(runlabel string, img *image.Image, args []string, o return nil, nil, err } - env = generateRunEnvironment(name, imageName, options) + env := generateRunEnvironment(options) env = append(env, "PODMAN_RUNLABEL_NESTED=1") envmap, err := envLib.ParseSlice(env) if err != nil { @@ -185,9 +185,6 @@ func generateRunlabelCommand(runlabel string, img *image.Image, args []string, o // generateCommand takes a label (string) and converts it to an executable command func generateCommand(command, imageName, name, globalOpts string) ([]string, error) { - var ( - newCommand []string - ) if name == "" { name = imageName } @@ -201,8 +198,7 @@ func generateCommand(command, imageName, name, globalOpts string) ([]string, err if err != nil { return nil, err } - newCommand = append(newCommand, prog) - + newCommand := []string{prog} for _, arg := range cmd[1:] { var newArg string switch arg { @@ -234,7 +230,7 @@ func generateCommand(command, imageName, name, globalOpts string) ([]string, err // GenerateRunEnvironment merges the current environment variables with optional // environment variables provided by the user -func generateRunEnvironment(name, imageName string, options entities.ContainerRunlabelOptions) []string { +func generateRunEnvironment(options entities.ContainerRunlabelOptions) []string { newEnv := os.Environ() if options.Optional1 != "" { newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", options.Optional1)) diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go index 9fc1e3bee..7567d5a70 100644 --- a/pkg/domain/infra/abi/cp.go +++ b/pkg/domain/infra/abi/cp.go @@ -92,7 +92,7 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, if isFromHostToCtr { if isVol, volDestName, volName := isVolumeDestName(destPath, ctr); isVol { //nolint(gocritic) - path, err := pathWithVolumeMount(ctr, ic.Libpod, volDestName, volName, destPath) + path, err := pathWithVolumeMount(ic.Libpod, volDestName, volName, destPath) if err != nil { return nil, errors.Wrapf(err, "error getting destination path from volume %s", volDestName) } @@ -126,7 +126,7 @@ func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, } else { destOwner = idtools.IDPair{UID: os.Getuid(), GID: os.Getgid()} if isVol, volDestName, volName := isVolumeDestName(srcPath, ctr); isVol { //nolint(gocritic) - path, err := pathWithVolumeMount(ctr, ic.Libpod, volDestName, volName, srcPath) + path, err := pathWithVolumeMount(ic.Libpod, volDestName, volName, srcPath) if err != nil { return nil, errors.Wrapf(err, "error getting source path from volume %s", volDestName) } @@ -384,7 +384,7 @@ func isVolumeDestName(path string, ctr *libpod.Container) (bool, string, string) } // if SRCPATH or DESTPATH is from volume mount's destination -v or --mount type=volume, generates the path with volume mount point -func pathWithVolumeMount(ctr *libpod.Container, runtime *libpod.Runtime, volDestName, volName, path string) (string, error) { +func pathWithVolumeMount(runtime *libpod.Runtime, volDestName, volName, path string) (string, error) { destVolume, err := runtime.GetVolume(volName) if err != nil { return "", errors.Wrapf(err, "error getting volume destination %s", volName) diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index abb5e2911..8853303d5 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "strings" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" @@ -16,165 +15,29 @@ import ( ) func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { - opts := generate.Options{ - Files: options.Files, - New: options.New, - } - // First assume it's a container. - if info, found, err := ic.generateSystemdgenContainerInfo(nameOrID, nil, options); found && err != nil { - return nil, err - } else if found && err == nil { - output, err := generate.CreateContainerSystemdUnit(info, opts) - if err != nil { - return nil, err + ctr, ctrErr := ic.Libpod.LookupContainer(nameOrID) + if ctrErr == nil { + // Generate the unit for the container. + s, err := generate.ContainerUnit(ctr, options) + if err == nil { + return &entities.GenerateSystemdReport{Output: s}, nil } - return &entities.GenerateSystemdReport{Output: output}, nil - } - - // --new does not support pods. - if options.New { - return nil, errors.Errorf("error generating systemd unit files: cannot generate generic files for a pod") } - // We're either having a pod or garbage. + // If it's not a container, we either have a pod or garbage. pod, err := ic.Libpod.LookupPod(nameOrID) if err != nil { - return nil, err - } - - // Error out if the pod has no infra container, which we require to be the - // main service. - if !pod.HasInfraContainer() { - return nil, fmt.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name()) + err = errors.Wrap(ctrErr, err.Error()) + return nil, errors.Wrapf(err, "%s does not refer to a container or pod", nameOrID) } - // Generate a systemdgen.ContainerInfo for the infra container. This - // ContainerInfo acts as the main service of the pod. - infraID, err := pod.InfraContainerID() - if err != nil { - return nil, nil - } - podInfo, _, err := ic.generateSystemdgenContainerInfo(infraID, pod, options) + // Generate the units for the pod and all its containers. + s, err := generate.PodUnits(pod, options) if err != nil { return nil, err } - - // Compute the container-dependency graph for the Pod. - containers, err := pod.AllContainers() - if err != nil { - return nil, err - } - if len(containers) == 0 { - return nil, fmt.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name()) - } - graph, err := libpod.BuildContainerGraph(containers) - if err != nil { - return nil, err - } - - // Traverse the dependency graph and create systemdgen.ContainerInfo's for - // each container. - containerInfos := []*generate.ContainerInfo{podInfo} - for ctr, dependencies := range graph.DependencyMap() { - // Skip the infra container as we already generated it. - if ctr.ID() == infraID { - continue - } - ctrInfo, _, err := ic.generateSystemdgenContainerInfo(ctr.ID(), nil, options) - if err != nil { - return nil, err - } - // Now add the container's dependencies and at the container as a - // required service of the infra container. - for _, dep := range dependencies { - if dep.ID() == infraID { - ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, podInfo.ServiceName) - } else { - _, serviceName := generateServiceName(dep, nil, options) - ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, serviceName) - } - } - podInfo.RequiredServices = append(podInfo.RequiredServices, ctrInfo.ServiceName) - containerInfos = append(containerInfos, ctrInfo) - } - - // Now generate the systemd service for all containers. - builder := strings.Builder{} - for i, info := range containerInfos { - if i > 0 { - builder.WriteByte('\n') - } - out, err := generate.CreateContainerSystemdUnit(info, opts) - if err != nil { - return nil, err - } - builder.WriteString(out) - } - - return &entities.GenerateSystemdReport{Output: builder.String()}, nil -} - -// generateSystemdgenContainerInfo is a helper to generate a -// systemdgen.ContainerInfo for `GenerateSystemd`. -func (ic *ContainerEngine) generateSystemdgenContainerInfo(nameOrID string, pod *libpod.Pod, options entities.GenerateSystemdOptions) (*generate.ContainerInfo, bool, error) { - ctr, err := ic.Libpod.LookupContainer(nameOrID) - if err != nil { - return nil, false, err - } - - timeout := ctr.StopTimeout() - if options.StopTimeout != nil { - timeout = *options.StopTimeout - } - - config := ctr.Config() - conmonPidFile := config.ConmonPidFile - if conmonPidFile == "" { - return nil, true, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") - } - - createCommand := []string{} - if config.CreateCommand != nil { - createCommand = config.CreateCommand - } else if options.New { - return nil, true, errors.Errorf("cannot use --new on container %q: no create command found", nameOrID) - } - - name, serviceName := generateServiceName(ctr, pod, options) - info := &generate.ContainerInfo{ - ServiceName: serviceName, - ContainerName: name, - RestartPolicy: options.RestartPolicy, - PIDFile: conmonPidFile, - StopTimeout: timeout, - GenerateTimestamp: true, - CreateCommand: createCommand, - } - - return info, true, nil -} - -// generateServiceName generates the container name and the service name for systemd service. -func generateServiceName(ctr *libpod.Container, pod *libpod.Pod, options entities.GenerateSystemdOptions) (string, string) { - var kind, name, ctrName string - if pod == nil { - kind = options.ContainerPrefix //defaults to container - name = ctr.ID() - if options.Name { - name = ctr.Name() - } - ctrName = name - } else { - kind = options.PodPrefix //defaults to pod - name = pod.ID() - ctrName = ctr.ID() - if options.Name { - name = pod.Name() - ctrName = ctr.Name() - } - } - return ctrName, fmt.Sprintf("%s%s%s", kind, options.Separator, name) + return &entities.GenerateSystemdReport{Output: s}, nil } func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 67f331aac..e630d9bc8 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -167,7 +167,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti return nil, errors.Wrapf(err, "error getting repository tags") } - var foundIDs []string + foundIDs := []string{} for _, tag := range tags { name := rawImage + ":" + tag newImage, err := ir.Libpod.ImageRuntime().New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways) @@ -443,7 +443,7 @@ func removeErrorsToExitCode(rmErrors []error) int { // container. inUseErrors bool // otherErrors indicates that at least one error other than the two - // above occured. + // above occurred. otherErrors bool ) @@ -549,8 +549,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie rmErrors = append(rmErrors, err) } } - - return + return //nolint } // Shutdown Libpod engine diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index 3034e36ec..98c041c15 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -8,17 +8,12 @@ import ( ) func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { - var ( - images []*libpodImage.Image - err error - ) - - images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) + images, err := ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) if err != nil { return nil, err } - var summaries []*entities.ImageSummary + summaries := []*entities.ImageSummary{} for _, img := range images { var repoTags []string if opts.All { diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index a2b5fc0fc..a6f5bab6b 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -153,7 +153,7 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri } listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) if err != nil { - return "", errors.Wrapf(err, "error retriving local image from image name %s", names[0]) + return "", errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) } updatedListID, err := listImage.RemoveManifest(instanceDigest) if err == nil { @@ -166,7 +166,7 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) if err != nil { - return errors.Wrapf(err, "error retriving local image from image name %s", names[0]) + return errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) } dest, err := alltransports.ParseImageName(names[1]) if err != nil { diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 8e3515824..eba1af362 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -48,15 +48,12 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net } func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { - var ( - rawCNINetworks []entities.NetworkInspectReport - ) - config, err := ic.Libpod.GetConfig() if err != nil { return nil, err } + rawCNINetworks := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) for _, name := range namesOrIds { rawList, err := network.InspectNetwork(config, name) if err != nil { @@ -68,7 +65,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { - var reports []*entities.NetworkRmReport + reports := []*entities.NetworkRmReport{} config, err := ic.Libpod.GetConfig() if err != nil { diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index f5b93c51b..7053cec9e 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -109,9 +109,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { var ( - containers []*libpod.Container pod *libpod.Pod - podOptions []libpod.PodCreateOption registryCreds *types.DockerAuthConfig writer io.Writer playKubePod entities.PlayKubePod @@ -130,8 +128,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } } - podOptions = append(podOptions, libpod.WithInfraContainer()) - podOptions = append(podOptions, libpod.WithPodName(podName)) + podOptions := []libpod.PodCreateOption{ + libpod.WithInfraContainer(), + libpod.WithPodName(podName), + } // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml hostname := podYAML.Spec.Hostname @@ -271,6 +271,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, err } + containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers)) for _, container := range podYAML.Spec.Containers { pullPolicy := util.PullImageMissing if len(container.ImagePullPolicy) > 0 { @@ -293,7 +294,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - conf, err := kubeContainerToCreateConfig(ctx, container, ic.Libpod, newImage, namespaces, volumes, pod.ID(), podName, podInfraID, seccompPaths) + conf, err := kubeContainerToCreateConfig(ctx, container, newImage, namespaces, volumes, pod.ID(), podName, podInfraID, seccompPaths) if err != nil { return nil, err } @@ -407,7 +408,7 @@ func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfi } // kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container -func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, podName, infraID string, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) { +func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, podName, infraID string, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) { var ( containerConfig createconfig.CreateConfig pidConfig createconfig.PidConfig diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index eb6f1e191..4a122f54d 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -54,9 +54,7 @@ func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*ent } func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) { - var ( - reports []*entities.PodKillReport - ) + reports := []*entities.PodKillReport{} sig, err := signal.ParseSignalNameOrNumber(options.Signal) if err != nil { return nil, err @@ -87,9 +85,7 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt } func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { - var ( - reports []*entities.PodPauseReport - ) + reports := []*entities.PodPauseReport{} pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err @@ -114,9 +110,7 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op } func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) { - var ( - reports []*entities.PodUnpauseReport - ) + reports := []*entities.PodUnpauseReport{} pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err @@ -141,9 +135,7 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, } func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { - var ( - reports []*entities.PodStopReport - ) + reports := []*entities.PodStopReport{} pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err @@ -168,9 +160,7 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt } func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) { - var ( - reports []*entities.PodRestartReport - ) + reports := []*entities.PodRestartReport{} pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err @@ -196,13 +186,12 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, } func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) { - var ( - reports []*entities.PodStartReport - ) + reports := []*entities.PodStartReport{} pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } + for _, p := range pods { report := entities.PodStartReport{Id: p.ID()} errs, err := p.Start(ctx) @@ -224,13 +213,11 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op } func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { - var ( - reports []*entities.PodRmReport - ) pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } + reports := make([]*entities.PodRmReport, 0, len(pods)) for _, p := range pods { report := entities.PodRmReport{Id: p.ID()} err := ic.Libpod.RemovePod(ctx, p, true, options.Force) @@ -247,13 +234,11 @@ func (ic *ContainerEngine) PodPrune(ctx context.Context, options entities.PodPru } func (ic *ContainerEngine) prunePodHelper(ctx context.Context) ([]*entities.PodPruneReport, error) { - var ( - reports []*entities.PodPruneReport - ) response, err := ic.Libpod.PrunePods(ctx) if err != nil { return nil, err } + reports := make([]*entities.PodPruneReport, 0, len(response)) for k, v := range response { reports = append(reports, &entities.PodPruneReport{ Err: v, @@ -298,9 +283,8 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) { var ( err error - filters []libpod.PodFilter - pds []*libpod.Pod - reports []*entities.ListPodsReport + filters = []libpod.PodFilter{} + pds = []*libpod.Pod{} ) for k, v := range options.Filters { @@ -326,6 +310,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti } } + reports := make([]*entities.ListPodsReport, 0, len(pds)) for _, p := range pds { var lpcs []*entities.ListPodContainer status, err := p.GetPodStatus() diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 33ba58558..90002326e 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -172,7 +172,7 @@ func checkInput() error { // nolint:deadcode,unused return nil } -// SystemPrune removes unsed data from the system. Pruning pods, containers, volumes and images. +// SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) podPruneReport, err := ic.prunePodHelper(ctx) @@ -181,7 +181,7 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys } systemPruneReport.PodPruneReport = podPruneReport - containerPruneReport, err := ic.pruneContainersHelper(ctx, nil) + containerPruneReport, err := ic.pruneContainersHelper(nil) if err != nil { return nil, err } @@ -212,10 +212,7 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) { var ( - dfImages []*entities.SystemDfImageReport - dfContainers []*entities.SystemDfContainerReport - dfVolumes []*entities.SystemDfVolumeReport - runningContainers []string + dfImages = []*entities.SystemDfImageReport{} ) // Get Images and iterate them @@ -282,6 +279,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System if err != nil { return nil, err } + dfContainers := make([]*entities.SystemDfContainerReport, 0, len(cons)) for _, c := range cons { iid, _ := c.Image() conSize, err := c.RootFsSize() @@ -320,10 +318,12 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System if err != nil { return nil, err } + runningContainers := make([]string, 0, len(running)) for _, c := range running { runningContainers = append(runningContainers, c.ID()) } + dfVolumes := make([]*entities.SystemDfVolumeReport, 0, len(vols)) for _, v := range vols { var consInUse int volSize, err := sizeOfPath(v.MountPoint()) diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index a311e0c4e..702e11003 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -40,9 +40,10 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) { var ( err error - reports []*entities.VolumeRmReport vols []*libpod.Volume + reports = []*entities.VolumeRmReport{} ) + if opts.All { vols, err = ic.Libpod.Volumes() if err != nil { @@ -72,9 +73,8 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) { var ( - err error - reports []*entities.VolumeInspectReport - vols []*libpod.Volume + err error + vols []*libpod.Volume ) // Note: as with previous implementation, a single failure here @@ -93,6 +93,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin vols = append(vols, vol) } } + reports := make([]*entities.VolumeInspectReport, 0, len(vols)) for _, v := range vols { config := entities.VolumeConfigResponse{ Name: v.Name(), @@ -115,13 +116,11 @@ func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.Volume } func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) { - var ( - reports []*entities.VolumePruneReport - ) pruned, err := ic.Libpod.PruneVolumes(ctx) if err != nil { return nil, err } + reports := make([]*entities.VolumePruneReport, 0, len(pruned)) for k, v := range pruned { reports = append(reports, &entities.VolumePruneReport{ Err: v, @@ -132,9 +131,6 @@ func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities. } func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) { - var ( - reports []*entities.VolumeListReport - ) volumeFilters, err := filters.GenerateVolumeFilters(opts.Filter) if err != nil { return nil, err @@ -143,6 +139,7 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL if err != nil { return nil, err } + reports := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { config := entities.VolumeConfigResponse{ Name: v.Name(), diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 68a8b0329..4bd813847 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -32,13 +32,11 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) } func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { - var ( - responses []entities.WaitReport - ) cons, err := getContainersByContext(ic.ClientCxt, false, namesOrIds) if err != nil { return nil, err } + responses := make([]entities.WaitReport, 0, len(cons)) for _, c := range cons { response := entities.WaitReport{Id: c.ID} exitCode, err := containers.Wait(ic.ClientCxt, c.ID, &options.Condition) @@ -53,13 +51,11 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - var ( - reports []*entities.PauseUnpauseReport - ) ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := containers.Pause(ic.ClientCxt, c.ID) reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) @@ -68,13 +64,11 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - var ( - reports []*entities.PauseUnpauseReport - ) ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := containers.Unpause(ic.ClientCxt, c.ID) reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) @@ -83,9 +77,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { - var ( - reports []*entities.StopReport - ) + reports := []*entities.StopReport{} for _, cidFile := range options.CIDFiles { content, err := ioutil.ReadFile(cidFile) if err != nil { @@ -125,13 +117,11 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { - var ( - reports []*entities.KillReport - ) ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.KillReport, 0, len(ctrs)) for _, c := range ctrs { reports = append(reports, &entities.KillReport{ Id: c.ID, @@ -143,7 +133,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { var ( - reports []*entities.RestartReport + reports = []*entities.RestartReport{} timeout *int ) if options.Timeout != nil { @@ -168,9 +158,6 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { - var ( - reports []*entities.RmReport - ) for _, cidFile := range options.CIDFiles { content, err := ioutil.ReadFile(cidFile) if err != nil { @@ -184,6 +171,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return nil, err } // TODO there is no endpoint for container eviction. Need to discuss + reports := make([]*entities.RmReport, 0, len(ctrs)) for _, c := range ctrs { reports = append(reports, &entities.RmReport{ Id: c.ID, @@ -198,13 +186,11 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. } func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) { - var ( - reports []*entities.ContainerInspectReport - ) ctrs, err := getContainersByContext(ic.ClientCxt, false, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.ContainerInspectReport, 0, len(ctrs)) for _, con := range ctrs { data, err := containers.Inspect(ic.ClientCxt, con.ID, &options.Size) if err != nil { @@ -282,9 +268,8 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { var ( - reports []*entities.CheckpointReport - err error - ctrs []entities.ListContainer + err error + ctrs = []entities.ListContainer{} ) if options.All { @@ -305,6 +290,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ return nil, err } } + reports := make([]*entities.CheckpointReport, 0, len(ctrs)) for _, c := range ctrs { report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRunning, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export) if err != nil { @@ -317,9 +303,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) { var ( - reports []*entities.RestoreReport - err error - ctrs []entities.ListContainer + err error + ctrs = []entities.ListContainer{} ) if options.All { allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{}) @@ -339,6 +324,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st return nil, err } } + reports := make([]*entities.RestoreReport, 0, len(ctrs)) for _, c := range ctrs { report, err := containers.Restore(ic.ClientCxt, c.ID, &options.Keep, &options.TCPEstablished, &options.IgnoreRootFS, &options.IgnoreStaticIP, &options.IgnoreStaticMAC, &options.Name, &options.Import) if err != nil { @@ -467,7 +453,7 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, } func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { - var reports []*entities.ContainerStartReport + reports := []*entities.ContainerStartReport{} for _, name := range namesOrIds { report := entities.ContainerStartReport{ Id: name, @@ -535,11 +521,11 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { - var reports []*entities.ContainerInitReport ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) for _, ctr := range ctrs { err := containers.ContainerInit(ic.ClientCxt, ctr.ID) // When using all, it is NOT considered an error if a container @@ -569,8 +555,8 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { var ( - reports []*entities.ContainerPortReport - namesOrIds []string + reports = []*entities.ContainerPortReport{} + namesOrIds = []string{} ) if len(nameOrID) > 0 { namesOrIds = append(namesOrIds, nameOrID) diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index fc7ac0aa8..ec2c53c4f 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -39,7 +39,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) return nil, err } - is := make([]*entities.ImageSummary, len(images)) + is := make([]*entities.ImageSummary, 0, len(images)) for i, img := range images { hold := entities.ImageSummary{} if err := utils.DeepCopy(&hold, img); err != nil { diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 7725d8257..e7cc5fb26 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -12,7 +12,7 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net } func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { - var reports []entities.NetworkInspectReport + reports := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) for _, name := range namesOrIds { report, err := network.Inspect(ic.ClientCxt, name) if err != nil { @@ -24,7 +24,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { - var reports []*entities.NetworkRmReport + reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds)) for _, name := range namesOrIds { report, err := network.Remove(ic.ClientCxt, name, &options.Force) if err != nil { diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index 5ca4a6a80..d18e9937c 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -17,10 +17,6 @@ func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*ent } func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) { - var ( - reports []*entities.PodKillReport - ) - _, err := util.ParseSignal(options.Signal) if err != nil { return nil, err @@ -30,6 +26,7 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt if err != nil { return nil, err } + reports := make([]*entities.PodKillReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Kill(ic.ClientCxt, p.Id, &options.Signal) if err != nil { @@ -46,13 +43,11 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt } func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { - var ( - reports []*entities.PodPauseReport - ) foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.PodPauseReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Pause(ic.ClientCxt, p.Id) if err != nil { @@ -69,13 +64,11 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op } func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) { - var ( - reports []*entities.PodUnpauseReport - ) foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.PodUnpauseReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Unpause(ic.ClientCxt, p.Id) if err != nil { @@ -92,10 +85,7 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, } func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { - var ( - reports []*entities.PodStopReport - timeout = -1 - ) + timeout := -1 foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err @@ -103,6 +93,7 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt if options.Timeout != -1 { timeout = options.Timeout } + reports := make([]*entities.PodStopReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Stop(ic.ClientCxt, p.Id, &timeout) if err != nil { @@ -119,11 +110,11 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt } func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) { - var reports []*entities.PodRestartReport foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.PodRestartReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Restart(ic.ClientCxt, p.Id) if err != nil { @@ -140,11 +131,11 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, } func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) { - var reports []*entities.PodStartReport foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } + reports := make([]*entities.PodStartReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Start(ic.ClientCxt, p.Id) if err != nil { @@ -161,11 +152,11 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op } func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { - var reports []*entities.PodRmReport foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } + reports := make([]*entities.PodRmReport, 0, len(foundPods)) for _, p := range foundPods { response, err := pods.Remove(ic.ClientCxt, p.Id, &options.Force) if err != nil { diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index 5b65c66ea..af7273ac4 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -16,10 +16,6 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum } func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) { - var ( - reports []*entities.VolumeRmReport - ) - if opts.All { vols, err := volumes.List(ic.ClientCxt, nil) if err != nil { @@ -29,6 +25,7 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op namesOrIds = append(namesOrIds, v.Name) } } + reports := make([]*entities.VolumeRmReport, 0, len(namesOrIds)) for _, id := range namesOrIds { reports = append(reports, &entities.VolumeRmReport{ Err: volumes.Remove(ic.ClientCxt, id, &opts.Force), @@ -39,9 +36,6 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op } func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []string, opts entities.VolumeInspectOptions) ([]*entities.VolumeInspectReport, error) { - var ( - reports []*entities.VolumeInspectReport - ) if opts.All { vols, err := volumes.List(ic.ClientCxt, nil) if err != nil { @@ -51,6 +45,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin namesOrIds = append(namesOrIds, v.Name) } } + reports := make([]*entities.VolumeInspectReport, 0, len(namesOrIds)) for _, id := range namesOrIds { data, err := volumes.Inspect(ic.ClientCxt, id) if err != nil { diff --git a/pkg/hooks/exec/runtimeconfigfilter_test.go b/pkg/hooks/exec/runtimeconfigfilter_test.go index 48dd2f998..f4b6cf86a 100644 --- a/pkg/hooks/exec/runtimeconfigfilter_test.go +++ b/pkg/hooks/exec/runtimeconfigfilter_test.go @@ -12,21 +12,11 @@ import ( "github.com/stretchr/testify/assert" ) -func pointerInt(value int) *int { - return &value -} - -func pointerUInt32(value uint32) *uint32 { - return &value -} - -func pointerFileMode(value os.FileMode) *os.FileMode { - return &value -} - func TestRuntimeConfigFilter(t *testing.T) { unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) //nolint - + fileMode := os.FileMode(0600) + rootUint32 := uint32(0) + binUser := int(1) for _, tt := range []struct { name string contextTimeout time.Duration @@ -77,9 +67,9 @@ func TestRuntimeConfigFilter(t *testing.T) { Type: "c", Major: 10, Minor: 229, - FileMode: pointerFileMode(0600), - UID: pointerUInt32(0), - GID: pointerUInt32(0), + FileMode: &fileMode, + UID: &rootUint32, + GID: &rootUint32, }, }, }, @@ -96,18 +86,18 @@ func TestRuntimeConfigFilter(t *testing.T) { Type: "c", Major: 10, Minor: 229, - FileMode: pointerFileMode(0600), - UID: pointerUInt32(0), - GID: pointerUInt32(0), + FileMode: &fileMode, + UID: &rootUint32, + GID: &rootUint32, }, { Path: "/dev/sda", Type: "b", Major: 8, Minor: 0, - FileMode: pointerFileMode(0600), - UID: pointerUInt32(0), - GID: pointerUInt32(0), + FileMode: &fileMode, + UID: &rootUint32, + GID: &rootUint32, }, }, }, @@ -137,9 +127,9 @@ func TestRuntimeConfigFilter(t *testing.T) { Type: "c", Major: 10, Minor: 229, - FileMode: pointerFileMode(0600), - UID: pointerUInt32(0), - GID: pointerUInt32(0), + FileMode: &fileMode, + UID: &rootUint32, + GID: &rootUint32, }, }, }, @@ -156,18 +146,18 @@ func TestRuntimeConfigFilter(t *testing.T) { Type: "c", Major: 10, Minor: 229, - FileMode: pointerFileMode(0600), - UID: pointerUInt32(0), - GID: pointerUInt32(0), + FileMode: &fileMode, + UID: &rootUint32, + GID: &rootUint32, }, { Path: "/dev/sdb", Type: "b", Major: 8, Minor: 0, - FileMode: pointerFileMode(0600), - UID: pointerUInt32(0), - GID: pointerUInt32(0), + FileMode: &fileMode, + UID: &rootUint32, + GID: &rootUint32, }, }, }, @@ -203,7 +193,7 @@ func TestRuntimeConfigFilter(t *testing.T) { { Path: path, Args: []string{"sh", "-c", "sleep 2"}, - Timeout: pointerInt(1), + Timeout: &binUser, }, }, input: &spec.Spec{ diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go index dff25f74f..8f241edf2 100644 --- a/pkg/lookup/lookup.go +++ b/pkg/lookup/lookup.go @@ -79,7 +79,6 @@ func GetContainerGroups(groups []string, containerMount string, override *Overri var ( groupDest string err error - uintgids []uint32 ) groupPath := etcgroup @@ -96,6 +95,7 @@ func GetContainerGroups(groups []string, containerMount string, override *Overri if err != nil { return nil, err } + uintgids := make([]uint32, 0, len(gids)) // For libpod, we want []uint32s for _, gid := range gids { uintgids = append(uintgids, uint32(gid)) diff --git a/pkg/network/files.go b/pkg/network/files.go index 81c0e1a28..beb3289f3 100644 --- a/pkg/network/files.go +++ b/pkg/network/files.go @@ -22,13 +22,13 @@ func GetCNIConfDir(config *config.Config) string { // LoadCNIConfsFromDir loads all the CNI configurations from a dir func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) { - var configs []*libcni.NetworkConfigList files, err := libcni.ConfFiles(dir, []string{".conflist"}) if err != nil { return nil, err } sort.Strings(files) + configs := make([]*libcni.NetworkConfigList, 0, len(files)) for _, confFile := range files { conf, err := libcni.ConfListFromFile(confFile) if err != nil { @@ -72,7 +72,7 @@ func ReadRawCNIConfByName(config *config.Config, name string) ([]byte, error) { // GetCNIPlugins returns a list of plugins that a given network // has in the form of a string func GetCNIPlugins(list *libcni.NetworkConfigList) string { - var plugins []string + plugins := make([]string, 0, len(list.Plugins)) for _, plug := range list.Plugins { plugins = append(plugins, plug.Network.Type) } @@ -106,12 +106,11 @@ func GetNetworksFromFilesystem(config *config.Config) ([]*allocator.Net, error) // GetNetworkNamesFromFileSystem gets all the names from the cni network // configuration files func GetNetworkNamesFromFileSystem(config *config.Config) ([]string, error) { - var networkNames []string - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) if err != nil { return nil, err } + networkNames := []string{} for _, n := range networks { networkNames = append(networkNames, n.Name) } @@ -144,12 +143,12 @@ func GetInterfaceNameFromConfig(path string) (string, error) { // GetBridgeNamesFromFileSystem is a convenience function to get all the bridge // names from the configured networks func GetBridgeNamesFromFileSystem(config *config.Config) ([]string, error) { - var bridgeNames []string - networks, err := LoadCNIConfsFromDir(GetCNIConfDir(config)) if err != nil { return nil, err } + + bridgeNames := []string{} for _, n := range networks { var name string // iterate network conflists diff --git a/pkg/network/network.go b/pkg/network/network.go index 3ff664316..997aaf8a2 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -30,11 +30,11 @@ func IsSupportedDriver(driver string) error { // GetLiveNetworks returns a slice of networks representing what the system // has defined as network interfaces func GetLiveNetworks() ([]*net.IPNet, error) { - var nets []*net.IPNet addrs, err := net.InterfaceAddrs() if err != nil { return nil, err } + nets := make([]*net.IPNet, 0, len(addrs)) for _, address := range addrs { _, n, err := net.ParseCIDR(address.String()) if err != nil { @@ -47,11 +47,11 @@ func GetLiveNetworks() ([]*net.IPNet, error) { // GetLiveNetworkNames returns a list of network interfaces on the system func GetLiveNetworkNames() ([]string, error) { - var interfaceNames []string liveInterfaces, err := net.Interfaces() if err != nil { return nil, err } + interfaceNames := make([]string, 0, len(liveInterfaces)) for _, i := range liveInterfaces { interfaceNames = append(interfaceNames, i.Name) } diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index ec96367cb..b07eb7f9a 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -21,7 +21,7 @@ import ( func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOptions) ([]entities.ListContainer, error) { var ( filterFuncs []libpod.ContainerFilter - pss []entities.ListContainer + pss = []entities.ListContainer{} ) all := options.All || options.Last > 0 if len(options.Filters) > 0 { diff --git a/pkg/signal/signal_linux.go b/pkg/signal/signal_linux.go index 6eebf7e5a..72ab1b97b 100644 --- a/pkg/signal/signal_linux.go +++ b/pkg/signal/signal_linux.go @@ -93,7 +93,7 @@ var signalMap = map[string]syscall.Signal{ // CatchAll catches all signals and relays them to the specified channel. func CatchAll(sigc chan os.Signal) { - var handledSigs []os.Signal + handledSigs := make([]os.Signal, 0, len(signalMap)) for _, s := range signalMap { handledSigs = append(handledSigs, s) } diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index cd2d69cfb..5ccb1ba80 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -93,5 +93,9 @@ func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, er options = append(options, libpod.WithInfraContainerPorts(ports)) } options = append(options, libpod.WithPodCgroups()) + options = append(options, libpod.WithPodCreateCommand()) + if len(p.InfraConmonPidFile) > 0 { + options = append(options, libpod.WithInfraConmonPidFile(p.InfraConmonPidFile)) + } return options, nil } diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 11976233a..600d27004 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -25,6 +25,9 @@ type PodBasicConfig struct { // InfraCommand and InfraImages in this struct. // Optional. NoInfra bool `json:"no_infra,omitempty"` + // InfraConmonPidFile is a custom path to store the infra container's + // conmon PID. + InfraConmonPidFile string `json:"infra_conmon_pid_file,omitempty"` // InfraCommand sets the command that will be used to start the infra // container. // If not set, the default set in the Libpod configuration file will be diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go new file mode 100644 index 000000000..fe56dc874 --- /dev/null +++ b/pkg/systemd/generate/common.go @@ -0,0 +1,50 @@ +package generate + +import ( + "github.com/pkg/errors" +) + +// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and +// is set to the unit's (unique) name. +const EnvVariable = "PODMAN_SYSTEMD_UNIT" + +// restartPolicies includes all valid restart policies to be used in a unit +// file. +var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} + +// validateRestartPolicy checks that the user-provided policy is valid. +func validateRestartPolicy(restart string) error { + for _, i := range restartPolicies { + if i == restart { + return nil + } + } + return errors.Errorf("%s is not a valid restart policy", restart) +} + +const headerTemplate = `# {{.ServiceName}}.service +# autogenerated by Podman {{.PodmanVersion}} +{{- if .TimeStamp}} +# {{.TimeStamp}} +{{- end}} + +[Unit] +Description=Podman {{.ServiceName}}.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +` + +// filterPodFlags removes --pod and --pod-id-file from the specified command. +func filterPodFlags(command []string) []string { + processed := []string{} + for i := 0; i < len(command); i++ { + s := command[i] + if s == "--pod" || s == "--pod-id-file" { + i++ + continue + } + processed = append(processed, s) + } + return processed +} diff --git a/pkg/systemd/generate/common_test.go b/pkg/systemd/generate/common_test.go new file mode 100644 index 000000000..f53bb7828 --- /dev/null +++ b/pkg/systemd/generate/common_test.go @@ -0,0 +1,25 @@ +package generate + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFilterPodFlags(t *testing.T) { + + tests := []struct { + input []string + }{ + {[]string{"podman", "pod", "create"}}, + {[]string{"podman", "pod", "create", "--name", "foo"}}, + {[]string{"podman", "pod", "create", "--pod-id-file", "foo"}}, + {[]string{"podman", "run", "--pod", "foo"}}, + } + + for _, test := range tests { + processed := filterPodFlags(test.input) + assert.NotContains(t, processed, "--pod-id-file") + assert.NotContains(t, processed, "--pod") + } +} diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go new file mode 100644 index 000000000..dced1a3da --- /dev/null +++ b/pkg/systemd/generate/containers.go @@ -0,0 +1,289 @@ +package generate + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "text/template" + "time" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/version" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// containerInfo contains data required for generating a container's systemd +// unit file. +type containerInfo struct { + // ServiceName of the systemd service. + ServiceName string + // Name or ID of the container. + ContainerNameOrID string + // StopTimeout sets the timeout Podman waits before killing the container + // during service stop. + StopTimeout uint + // RestartPolicy of the systemd unit (e.g., no, on-failure, always). + RestartPolicy string + // PIDFile of the service. Required for forking services. Must point to the + // PID of the associated conmon process. + PIDFile string + // ContainerIDFile to be used in the unit. + ContainerIDFile string + // GenerateTimestamp, if set the generated unit file has a time stamp. + GenerateTimestamp bool + // BoundToServices are the services this service binds to. Note that this + // service runs after them. + BoundToServices []string + // PodmanVersion for the header. Will be set internally. Will be auto-filled + // if left empty. + PodmanVersion string + // Executable is the path to the podman executable. Will be auto-filled if + // left empty. + Executable string + // TimeStamp at the time of creating the unit file. Will be set internally. + TimeStamp string + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string + // EnvVariable is generate.EnvVariable and must not be set. + EnvVariable string + // ExecStartPre of the unit. + ExecStartPre string + // ExecStart of the unit. + ExecStart string + // ExecStop of the unit. + ExecStop string + // ExecStopPost of the unit. + ExecStopPost string + + // If not nil, the container is part of the pod. We can use the + // podInfo to extract the relevant data. + pod *podInfo +} + +const containerTemplate = headerTemplate + ` +{{- if .BoundToServices}} +RefuseManualStart=yes +RefuseManualStop=yes +BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +{{- end}} + +[Service] +Environment={{.EnvVariable}}=%n +Restart={{.RestartPolicy}} +{{- if .ExecStartPre}} +ExecStartPre={{.ExecStartPre}} +{{- end}} +ExecStart={{.ExecStart}} +ExecStop={{.ExecStop}} +{{- if .ExecStopPost}} +ExecStopPost={{.ExecStopPost}} +{{- end}} +PIDFile={{.PIDFile}} +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + +// ContainerUnit generates a systemd unit for the specified container. Based +// on the options, the return value might be the entire unit or a file it has +// been written to. +func ContainerUnit(ctr *libpod.Container, options entities.GenerateSystemdOptions) (string, error) { + info, err := generateContainerInfo(ctr, options) + if err != nil { + return "", err + } + return executeContainerTemplate(info, options) +} + +func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSystemdOptions) (*containerInfo, error) { + timeout := ctr.StopTimeout() + if options.StopTimeout != nil { + timeout = *options.StopTimeout + } + + config := ctr.Config() + conmonPidFile := config.ConmonPidFile + if conmonPidFile == "" { + return nil, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + createCommand := []string{} + if config.CreateCommand != nil { + createCommand = config.CreateCommand + } else if options.New { + return nil, errors.Errorf("cannot use --new on container %q: no create command found", ctr.ID()) + } + + nameOrID, serviceName := containerServiceName(ctr, options) + + info := containerInfo{ + ServiceName: serviceName, + ContainerNameOrID: nameOrID, + RestartPolicy: options.RestartPolicy, + PIDFile: conmonPidFile, + StopTimeout: timeout, + GenerateTimestamp: true, + CreateCommand: createCommand, + } + + return &info, nil +} + +// containerServiceName returns the nameOrID and the service name of the +// container. +func containerServiceName(ctr *libpod.Container, options entities.GenerateSystemdOptions) (string, string) { + nameOrID := ctr.ID() + if options.Name { + nameOrID = ctr.Name() + } + serviceName := fmt.Sprintf("%s%s%s", options.ContainerPrefix, options.Separator, nameOrID) + return nameOrID, serviceName +} + +// 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. +func executeContainerTemplate(info *containerInfo, options entities.GenerateSystemdOptions) (string, error) { + if err := validateRestartPolicy(info.RestartPolicy); err != nil { + return "", err + } + + // Make sure the executable is set. + if info.Executable == "" { + executable, err := os.Executable() + if err != nil { + executable = "/usr/bin/podman" + logrus.Warnf("Could not obtain podman executable location, using default %s", executable) + } + info.Executable = executable + } + + info.EnvVariable = EnvVariable + info.ExecStart = "{{.Executable}} start {{.ContainerNameOrID}}" + info.ExecStop = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerNameOrID}}" + + // Assemble the ExecStart command when creating a new container. + // + // Note that we cannot catch all corner cases here such that users + // *must* manually check the generated files. A container might have + // been created via a Python script, which would certainly yield an + // invalid `info.CreateCommand`. Hence, we're doing a best effort unit + // generation and don't try aiming at completeness. + if options.New { + info.PIDFile = "%t/" + info.ServiceName + ".pid" + info.ContainerIDFile = "%t/" + info.ServiceName + ".ctr-id" + // The create command must at least have three arguments: + // /usr/bin/podman run $IMAGE + index := 2 + if info.CreateCommand[1] == "container" { + index = 3 + } + if len(info.CreateCommand) < index+1 { + return "", errors.Errorf("container's create command is too short or invalid: %v", info.CreateCommand) + } + // We're hard-coding the first five arguments and append the + // CreateCommand with a stripped command and subcomand. + startCommand := []string{ + info.Executable, + "run", + "--conmon-pidfile", "{{.PIDFile}}", + "--cidfile", "{{.ContainerIDFile}}", + "--cgroups=no-conmon", + } + // If the container is in a pod, make sure that the + // --pod-id-file is set correctly. + if info.pod != nil { + podFlags := []string{"--pod-id-file", info.pod.PodIDFile} + startCommand = append(startCommand, podFlags...) + info.CreateCommand = filterPodFlags(info.CreateCommand) + } + + // Enforce detaching + // + // since we use systemd `Type=forking` service + // @see https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= + // when we generated systemd service file with the --new param, + // `ExecStart` will have `/usr/bin/podman run ...` + // if `info.CreateCommand` has no `-d` or `--detach` param, + // podman will run the container in default attached mode, + // as a result, `systemd start` will wait the `podman run` command exit until failed with timeout error. + hasDetachParam := false + for _, p := range info.CreateCommand[index:] { + if p == "--detach" || p == "-d" { + hasDetachParam = true + } + } + if !hasDetachParam { + startCommand = append(startCommand, "-d") + } + startCommand = append(startCommand, info.CreateCommand[index:]...) + + info.ExecStartPre = "/usr/bin/rm -f {{.PIDFile}} {{.ContainerIDFile}}" + info.ExecStart = strings.Join(startCommand, " ") + info.ExecStop = "{{.Executable}} stop --ignore --cidfile {{.ContainerIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" + info.ExecStopPost = "{{.Executable}} rm --ignore -f --cidfile {{.ContainerIDFile}}" + } + + if info.PodmanVersion == "" { + info.PodmanVersion = version.Version + } + if info.GenerateTimestamp { + info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) + } + + // Sort the slices to assure a deterministic output. + sort.Strings(info.BoundToServices) + + // Generate the template and compile it. + // + // Note that we need a two-step generation process to allow for fields + // embedding other fields. This way we can replace `A -> B -> C` and + // make the code easier to maintain at the cost of a slightly slower + // generation. That's especially needed for embedding the PID and ID + // files in other fields which will eventually get replaced in the 2nd + // template execution. + templ, err := template.New("container_template").Parse(containerTemplate) + if err != nil { + return "", errors.Wrap(err, "error parsing systemd service template") + } + + var buf bytes.Buffer + if err := templ.Execute(&buf, info); err != nil { + return "", err + } + + // Now parse the generated template (i.e., buf) and execute it. + templ, err = template.New("container_template").Parse(buf.String()) + if err != nil { + return "", errors.Wrap(err, "error parsing systemd service template") + } + + buf = bytes.Buffer{} + if err := templ.Execute(&buf, info); err != nil { + return "", err + } + + if !options.Files { + return buf.String(), nil + } + + buf.WriteByte('\n') + cwd, err := os.Getwd() + if err != nil { + return "", errors.Wrap(err, "error getting current working directory") + } + path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName)) + if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil { + return "", errors.Wrap(err, "error generating systemd unit") + } + return path, nil +} diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go new file mode 100644 index 000000000..8365ecd7a --- /dev/null +++ b/pkg/systemd/generate/containers_test.go @@ -0,0 +1,366 @@ +package generate + +import ( + "testing" + + "github.com/containers/libpod/pkg/domain/entities" +) + +func TestValidateRestartPolicyContainer(t *testing.T) { + type containerInfo struct { + restart string + } + tests := []struct { + name string + containerInfo containerInfo + wantErr bool + }{ + {"good-on", containerInfo{restart: "no"}, false}, + {"good-on-success", containerInfo{restart: "on-success"}, false}, + {"good-on-failure", containerInfo{restart: "on-failure"}, false}, + {"good-on-abnormal", containerInfo{restart: "on-abnormal"}, false}, + {"good-on-watchdog", containerInfo{restart: "on-watchdog"}, false}, + {"good-on-abort", containerInfo{restart: "on-abort"}, false}, + {"good-always", containerInfo{restart: "always"}, false}, + {"fail", containerInfo{restart: "foobar"}, true}, + {"failblank", containerInfo{restart: ""}, true}, + } + for _, tt := range tests { + test := tt + t.Run(tt.name, func(t *testing.T) { + if err := validateRestartPolicy(test.containerInfo.restart); (err != nil) != test.wantErr { + t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, test.wantErr) + } + }) + } +} + +func TestCreateContainerSystemdUnit(t *testing.T) { + goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + goodName := `# container-foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foobar.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + goodNameBoundTo := `# container-foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-foobar.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +RefuseManualStart=yes +RefuseManualStop=yes +BindsTo=a.service b.service c.service pod.service +After=a.service b.service c.service pod.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + goodNameNew := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + goodNameNewWithPodFile := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file /tmp/pod-foobar.pod-id-file -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + goodNameNewDetach := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +# autogenerated by Podman CI + +[Unit] +Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStartPre=/usr/bin/rm -f %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id --cgroups=no-conmon -d awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -t 10 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id +PIDFile=%t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + tests := []struct { + name string + info containerInfo + want string + new bool + wantErr bool + }{ + + {"good with id", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + EnvVariable: EnvVariable, + }, + goodID, + false, + false, + }, + {"good with name", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foobar", + ContainerNameOrID: "foobar", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + EnvVariable: EnvVariable, + }, + goodName, + false, + false, + }, + {"good with name and bound to", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-foobar", + ContainerNameOrID: "foobar", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + BoundToServices: []string{"pod", "a", "b", "c"}, + EnvVariable: EnvVariable, + }, + goodNameBoundTo, + false, + false, + }, + {"bad restart policy", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "never", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + EnvVariable: EnvVariable, + }, + "", + false, + true, + }, + {"good with name and generic", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + EnvVariable: EnvVariable, + }, + goodNameNew, + true, + false, + }, + {"good with explicit short detach param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + EnvVariable: EnvVariable, + }, + goodNameNew, + true, + false, + }, + {"good with explicit short detach param and podInfo", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + EnvVariable: EnvVariable, + pod: &podInfo{ + PodIDFile: "/tmp/pod-foobar.pod-id-file", + }, + }, + goodNameNewWithPodFile, + true, + false, + }, + {"good with explicit full detach param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + EnvVariable: EnvVariable, + }, + goodNameNewDetach, + true, + false, + }, + {"good with id and no param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + goodIDNew, + true, + false, + }, + } + for _, tt := range tests { + test := tt + t.Run(tt.name, func(t *testing.T) { + opts := entities.GenerateSystemdOptions{ + Files: false, + New: test.new, + } + got, err := executeContainerTemplate(&test.info, opts) + if (err != nil) != test.wantErr { + t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) + return + } + if got != test.want { + t.Errorf("CreateContainerSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) + } + }) + } +} diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go new file mode 100644 index 000000000..5cfd5ab0a --- /dev/null +++ b/pkg/systemd/generate/pods.go @@ -0,0 +1,341 @@ +package generate + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "text/template" + "time" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/version" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// podInfo contains data required for generating a pod's systemd +// unit file. +type podInfo struct { + // ServiceName of the systemd service. + ServiceName string + // Name or ID of the infra container. + InfraNameOrID string + // StopTimeout sets the timeout Podman waits before killing the container + // during service stop. + StopTimeout uint + // RestartPolicy of the systemd unit (e.g., no, on-failure, always). + RestartPolicy string + // PIDFile of the service. Required for forking services. Must point to the + // PID of the associated conmon process. + PIDFile string + // PodIDFile of the unit. + PodIDFile string + // GenerateTimestamp, if set the generated unit file has a time stamp. + GenerateTimestamp bool + // RequiredServices are services this service requires. Note that this + // service runs before them. + RequiredServices []string + // PodmanVersion for the header. Will be set internally. Will be auto-filled + // if left empty. + PodmanVersion string + // Executable is the path to the podman executable. Will be auto-filled if + // left empty. + Executable string + // TimeStamp at the time of creating the unit file. Will be set internally. + TimeStamp string + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string + // PodCreateCommand - a post-processed variant of CreateCommand to use + // when creating the pod. + PodCreateCommand string + // EnvVariable is generate.EnvVariable and must not be set. + EnvVariable string + // ExecStartPre1 of the unit. + ExecStartPre1 string + // ExecStartPre2 of the unit. + ExecStartPre2 string + // ExecStart of the unit. + ExecStart string + // ExecStop of the unit. + ExecStop string + // ExecStopPost of the unit. + ExecStopPost string +} + +const podTemplate = headerTemplate + `Requires={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} +Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} + +[Service] +Environment={{.EnvVariable}}=%n +Restart={{.RestartPolicy}} +{{- if .ExecStartPre1}} +ExecStartPre={{.ExecStartPre1}} +{{- end}} +{{- if .ExecStartPre2}} +ExecStartPre={{.ExecStartPre2}} +{{- end}} +ExecStart={{.ExecStart}} +ExecStop={{.ExecStop}} +{{- if .ExecStopPost}} +ExecStopPost={{.ExecStopPost}} +{{- end}} +PIDFile={{.PIDFile}} +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + +// PodUnits generates systemd units for the specified pod and its containers. +// 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) (string, error) { + // Error out if the pod has no infra container, which we require to be the + // main service. + if !pod.HasInfraContainer() { + return "", errors.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name()) + } + + podInfo, err := generatePodInfo(pod, options) + if err != nil { + return "", err + } + + infraID, err := pod.InfraContainerID() + if err != nil { + return "", err + } + + // Compute the container-dependency graph for the Pod. + containers, err := pod.AllContainers() + if err != nil { + return "", err + } + if len(containers) == 0 { + return "", errors.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name()) + } + graph, err := libpod.BuildContainerGraph(containers) + if err != nil { + return "", err + } + + // Traverse the dependency graph and create systemdgen.containerInfo's for + // each container. + containerInfos := []*containerInfo{} + for ctr, dependencies := range graph.DependencyMap() { + // Skip the infra container as we already generated it. + if ctr.ID() == infraID { + continue + } + ctrInfo, err := generateContainerInfo(ctr, options) + if err != nil { + return "", err + } + // Now add the container's dependencies and at the container as a + // required service of the infra container. + for _, dep := range dependencies { + if dep.ID() == infraID { + ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, podInfo.ServiceName) + } else { + _, serviceName := containerServiceName(dep, options) + ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, serviceName) + } + } + podInfo.RequiredServices = append(podInfo.RequiredServices, ctrInfo.ServiceName) + containerInfos = append(containerInfos, ctrInfo) + } + + // Now generate the systemd service for all containers. + builder := strings.Builder{} + out, err := executePodTemplate(podInfo, options) + if err != nil { + return "", err + } + builder.WriteString(out) + for _, info := range containerInfos { + info.pod = podInfo + builder.WriteByte('\n') + out, err := executeContainerTemplate(info, options) + if err != nil { + return "", err + } + builder.WriteString(out) + } + + return builder.String(), nil +} + +func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) (*podInfo, error) { + // Generate a systemdgen.containerInfo for the infra container. This + // containerInfo acts as the main service of the pod. + infraCtr, err := pod.InfraContainer() + if err != nil { + return nil, errors.Wrap(err, "could not find infra container") + } + + timeout := infraCtr.StopTimeout() + if options.StopTimeout != nil { + timeout = *options.StopTimeout + } + + config := infraCtr.Config() + conmonPidFile := config.ConmonPidFile + if conmonPidFile == "" { + return nil, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + createCommand := pod.CreateCommand() + if options.New && len(createCommand) == 0 { + return nil, errors.Errorf("cannot use --new on pod %q: no create command found", pod.ID()) + } + + nameOrID := pod.ID() + ctrNameOrID := infraCtr.ID() + if options.Name { + nameOrID = pod.Name() + ctrNameOrID = infraCtr.Name() + } + serviceName := fmt.Sprintf("%s%s%s", options.PodPrefix, options.Separator, nameOrID) + + info := podInfo{ + ServiceName: serviceName, + InfraNameOrID: ctrNameOrID, + RestartPolicy: options.RestartPolicy, + PIDFile: conmonPidFile, + StopTimeout: timeout, + GenerateTimestamp: true, + CreateCommand: createCommand, + } + return &info, nil +} + +// executePodTemplate executes the pod template on the specified podInfo. Note +// that the podInfo is also post processed and completed, which allows for an +// easier unit testing. +func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) (string, error) { + if err := validateRestartPolicy(info.RestartPolicy); err != nil { + return "", err + } + + // Make sure the executable is set. + if info.Executable == "" { + executable, err := os.Executable() + if err != nil { + executable = "/usr/bin/podman" + logrus.Warnf("Could not obtain podman executable location, using default %s", executable) + } + info.Executable = executable + } + + info.EnvVariable = EnvVariable + info.ExecStart = "{{.Executable}} start {{.InfraNameOrID}}" + info.ExecStop = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.InfraNameOrID}}" + + // Assemble the ExecStart command when creating a new pod. + // + // Note that we cannot catch all corner cases here such that users + // *must* manually check the generated files. A pod might have been + // created via a Python script, which would certainly yield an invalid + // `info.CreateCommand`. Hence, we're doing a best effort unit + // generation and don't try aiming at completeness. + if options.New { + info.PIDFile = "%t/" + info.ServiceName + ".pid" + info.PodIDFile = "%t/" + info.ServiceName + ".pod-id" + + podCreateIndex := 0 + var podRootArgs, podCreateArgs []string + switch len(info.CreateCommand) { + case 0, 1, 2: + return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) + default: + // Make sure that pod was created with `pod create` and + // not something else, such as `run --pod new`. + for i := 1; i < len(info.CreateCommand); i++ { + if info.CreateCommand[i-1] == "pod" && info.CreateCommand[i] == "create" { + podCreateIndex = i + break + } + } + if podCreateIndex == 0 { + return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) + } + podRootArgs = info.CreateCommand[1 : podCreateIndex-2] + podCreateArgs = filterPodFlags(info.CreateCommand[podCreateIndex+1:]) + } + // We're hard-coding the first five arguments and append the + // CreateCommand with a stripped command and subcomand. + startCommand := []string{info.Executable} + startCommand = append(startCommand, podRootArgs...) + startCommand = append(startCommand, + []string{"pod", "create", + "--infra-conmon-pidfile", "{{.PIDFile}}", + "--pod-id-file", "{{.PodIDFile}}"}...) + + startCommand = append(startCommand, podCreateArgs...) + + info.ExecStartPre1 = "/usr/bin/rm -f {{.PIDFile}} {{.PodIDFile}}" + info.ExecStartPre2 = strings.Join(startCommand, " ") + info.ExecStart = "{{.Executable}} pod start --pod-id-file {{.PodIDFile}}" + info.ExecStop = "{{.Executable}} pod stop --ignore --pod-id-file {{.PodIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" + info.ExecStopPost = "{{.Executable}} pod rm --ignore -f --pod-id-file {{.PodIDFile}}" + } + if info.PodmanVersion == "" { + info.PodmanVersion = version.Version + } + if info.GenerateTimestamp { + info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) + } + + // Sort the slices to assure a deterministic output. + sort.Strings(info.RequiredServices) + + // Generate the template and compile it. + // + // Note that we need a two-step generation process to allow for fields + // embedding other fields. This way we can replace `A -> B -> C` and + // make the code easier to maintain at the cost of a slightly slower + // generation. That's especially needed for embedding the PID and ID + // files in other fields which will eventually get replaced in the 2nd + // template execution. + templ, err := template.New("pod_template").Parse(podTemplate) + if err != nil { + return "", errors.Wrap(err, "error parsing systemd service template") + } + + var buf bytes.Buffer + if err := templ.Execute(&buf, info); err != nil { + return "", err + } + + // Now parse the generated template (i.e., buf) and execute it. + templ, err = template.New("pod_template").Parse(buf.String()) + if err != nil { + return "", errors.Wrap(err, "error parsing systemd service template") + } + + buf = bytes.Buffer{} + if err := templ.Execute(&buf, info); err != nil { + return "", err + } + + if !options.Files { + return buf.String(), nil + } + + buf.WriteByte('\n') + cwd, err := os.Getwd() + if err != nil { + return "", errors.Wrap(err, "error getting current working directory") + } + path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName)) + if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil { + return "", errors.Wrap(err, "error generating systemd unit") + } + return path, nil +} diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go new file mode 100644 index 000000000..f6e225c35 --- /dev/null +++ b/pkg/systemd/generate/pods_test.go @@ -0,0 +1,100 @@ +package generate + +import ( + "testing" + + "github.com/containers/libpod/pkg/domain/entities" +) + +func TestValidateRestartPolicyPod(t *testing.T) { + type podInfo struct { + restart string + } + tests := []struct { + name string + podInfo podInfo + wantErr bool + }{ + {"good-on", podInfo{restart: "no"}, false}, + {"good-on-success", podInfo{restart: "on-success"}, false}, + {"good-on-failure", podInfo{restart: "on-failure"}, false}, + {"good-on-abnormal", podInfo{restart: "on-abnormal"}, false}, + {"good-on-watchdog", podInfo{restart: "on-watchdog"}, false}, + {"good-on-abort", podInfo{restart: "on-abort"}, false}, + {"good-always", podInfo{restart: "always"}, false}, + {"fail", podInfo{restart: "foobar"}, true}, + {"failblank", podInfo{restart: ""}, true}, + } + for _, tt := range tests { + test := tt + t.Run(tt.name, func(t *testing.T) { + if err := validateRestartPolicy(test.podInfo.restart); (err != nil) != test.wantErr { + t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, test.wantErr) + } + }) + } +} + +func TestCreatePodSystemdUnit(t *testing.T) { + podGoodName := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +ExecStart=/usr/bin/podman start jadda-jadda-infra +ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target` + + tests := []struct { + name string + info podInfo + want string + wantErr bool + }{ + {"pod", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + }, + podGoodName, + false, + }, + } + + for _, tt := range tests { + test := tt + t.Run(tt.name, func(t *testing.T) { + opts := entities.GenerateSystemdOptions{ + Files: false, + } + got, err := executePodTemplate(&test.info, opts) + if (err != nil) != test.wantErr { + t.Errorf("CreatePodSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) + return + } + if got != test.want { + t.Errorf("CreatePodSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) + } + }) + } +} diff --git a/pkg/systemd/generate/systemdgen.go b/pkg/systemd/generate/systemdgen.go deleted file mode 100644 index 73fe52c0e..000000000 --- a/pkg/systemd/generate/systemdgen.go +++ /dev/null @@ -1,237 +0,0 @@ -package generate - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" - "text/template" - "time" - - "github.com/containers/libpod/version" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and -// is set to the unit's (unique) name. -const EnvVariable = "PODMAN_SYSTEMD_UNIT" - -// ContainerInfo contains data required for generating a container's systemd -// unit file. -type ContainerInfo struct { - // ServiceName of the systemd service. - ServiceName string - // Name or ID of the container. - ContainerName string - // InfraContainer of the pod. - InfraContainer string - // StopTimeout sets the timeout Podman waits before killing the container - // during service stop. - StopTimeout uint - // RestartPolicy of the systemd unit (e.g., no, on-failure, always). - RestartPolicy string - // PIDFile of the service. Required for forking services. Must point to the - // PID of the associated conmon process. - PIDFile string - // GenerateTimestamp, if set the generated unit file has a time stamp. - GenerateTimestamp bool - // BoundToServices are the services this service binds to. Note that this - // service runs after them. - BoundToServices []string - // RequiredServices are services this service requires. Note that this - // service runs before them. - RequiredServices []string - // PodmanVersion for the header. Will be set internally. Will be auto-filled - // if left empty. - PodmanVersion string - // Executable is the path to the podman executable. Will be auto-filled if - // left empty. - Executable string - // TimeStamp at the time of creating the unit file. Will be set internally. - TimeStamp string - // New controls if a new container is created or if an existing one is started. - New bool - // CreateCommand is the full command plus arguments of the process the - // container has been created with. - CreateCommand []string - // RunCommand is a post-processed variant of CreateCommand and used for - // the ExecStart field in generic unit files. - RunCommand string - // EnvVariable is generate.EnvVariable and must not be set. - EnvVariable string -} - -var restartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} - -// validateRestartPolicy checks that the user-provided policy is valid. -func validateRestartPolicy(restart string) error { - for _, i := range restartPolicies { - if i == restart { - return nil - } - } - return errors.Errorf("%s is not a valid restart policy", restart) -} - -const containerTemplate = `# {{.ServiceName}}.service -# autogenerated by Podman {{.PodmanVersion}} -{{- if .TimeStamp}} -# {{.TimeStamp}} -{{- end}} - -[Unit] -Description=Podman {{.ServiceName}}.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target -{{- if .BoundToServices}} -RefuseManualStart=yes -RefuseManualStop=yes -BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} -After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} -{{- end}} -{{- if .RequiredServices}} -Requires={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} -Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} -{{- end}} - -[Service] -Environment={{.EnvVariable}}=%n -Restart={{.RestartPolicy}} -{{- if .New}} -ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid -ExecStart={{.RunCommand}} -ExecStop={{.Executable}} stop --ignore --cidfile %t/%n-cid {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} -ExecStopPost={{.Executable}} rm --ignore -f --cidfile %t/%n-cid -PIDFile=%t/%n-pid -{{- else}} -ExecStart={{.Executable}} start {{.ContainerName}} -ExecStop={{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerName}} -PIDFile={{.PIDFile}} -{{- end}} -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - -// Options include different options to control the unit file generation. -type Options struct { - // When set, generate service files in the current working directory and - // return the paths to these files instead of returning all contents in one - // big string. - Files bool - // New controls if a new container is created or if an existing one is started. - New bool -} - -// CreateContainerSystemdUnit creates a systemd unit file for a container. -func CreateContainerSystemdUnit(info *ContainerInfo, opts Options) (string, error) { - if err := validateRestartPolicy(info.RestartPolicy); err != nil { - return "", err - } - - // Make sure the executable is set. - if info.Executable == "" { - executable, err := os.Executable() - if err != nil { - executable = "/usr/bin/podman" - logrus.Warnf("Could not obtain podman executable location, using default %s", executable) - } - info.Executable = executable - } - - info.EnvVariable = EnvVariable - - // Assemble the ExecStart command when creating a new container. - // - // Note that we cannot catch all corner cases here such that users - // *must* manually check the generated files. A container might have - // been created via a Python script, which would certainly yield an - // invalid `info.CreateCommand`. Hence, we're doing a best effort unit - // generation and don't try aiming at completeness. - if opts.New { - // The create command must at least have three arguments: - // /usr/bin/podman run $IMAGE - index := 2 - if info.CreateCommand[1] == "container" { - index = 3 - } - if len(info.CreateCommand) < index+1 { - return "", errors.Errorf("container's create command is too short or invalid: %v", info.CreateCommand) - } - // We're hard-coding the first five arguments and append the - // CreateCommand with a stripped command and subcomand. - command := []string{ - info.Executable, - "run", - "--conmon-pidfile", "%t/%n-pid", - "--cidfile", "%t/%n-cid", - "--cgroups=no-conmon", - } - - // Enforce detaching - // - // since we use systemd `Type=forking` service - // @see https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= - // when we generated systemd service file with the --new param, - // `ExecStart` will have `/usr/bin/podman run ...` - // if `info.CreateCommand` has no `-d` or `--detach` param, - // podman will run the container in default attached mode, - // as a result, `systemd start` will wait the `podman run` command exit until failed with timeout error. - hasDetachParam := false - for _, p := range info.CreateCommand[index:] { - if p == "--detach" || p == "-d" { - hasDetachParam = true - } - } - if !hasDetachParam { - command = append(command, "-d") - } - - command = append(command, info.CreateCommand[index:]...) - info.RunCommand = strings.Join(command, " ") - info.New = true - } - - if info.PodmanVersion == "" { - info.PodmanVersion = version.Version - } - if info.GenerateTimestamp { - info.TimeStamp = fmt.Sprintf("%v", time.Now().Format(time.UnixDate)) - } - - // Sort the slices to assure a deterministic output. - sort.Strings(info.RequiredServices) - sort.Strings(info.BoundToServices) - - // Generate the template and compile it. - templ, err := template.New("systemd_service_file").Parse(containerTemplate) - if err != nil { - return "", errors.Wrap(err, "error parsing systemd service template") - } - - var buf bytes.Buffer - if err := templ.Execute(&buf, info); err != nil { - return "", err - } - - if !opts.Files { - return buf.String(), nil - } - - buf.WriteByte('\n') - cwd, err := os.Getwd() - if err != nil { - return "", errors.Wrap(err, "error getting current working directory") - } - path := filepath.Join(cwd, fmt.Sprintf("%s.service", info.ServiceName)) - if err := ioutil.WriteFile(path, buf.Bytes(), 0644); err != nil { - return "", errors.Wrap(err, "error generating systemd unit") - } - return path, nil -} diff --git a/pkg/systemd/generate/systemdgen_test.go b/pkg/systemd/generate/systemdgen_test.go deleted file mode 100644 index cc5db5e24..000000000 --- a/pkg/systemd/generate/systemdgen_test.go +++ /dev/null @@ -1,347 +0,0 @@ -package generate - -import ( - "testing" -) - -func TestValidateRestartPolicy(t *testing.T) { - type ContainerInfo struct { - restart string - } - tests := []struct { - name string - ContainerInfo ContainerInfo - wantErr bool - }{ - {"good-on", ContainerInfo{restart: "no"}, false}, - {"good-on-success", ContainerInfo{restart: "on-success"}, false}, - {"good-on-failure", ContainerInfo{restart: "on-failure"}, false}, - {"good-on-abnormal", ContainerInfo{restart: "on-abnormal"}, false}, - {"good-on-watchdog", ContainerInfo{restart: "on-watchdog"}, false}, - {"good-on-abort", ContainerInfo{restart: "on-abort"}, false}, - {"good-always", ContainerInfo{restart: "always"}, false}, - {"fail", ContainerInfo{restart: "foobar"}, true}, - {"failblank", ContainerInfo{restart: ""}, true}, - } - for _, tt := range tests { - test := tt - t.Run(tt.name, func(t *testing.T) { - if err := validateRestartPolicy(test.ContainerInfo.restart); (err != nil) != test.wantErr { - t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, test.wantErr) - } - }) - } -} - -func TestCreateContainerSystemdUnit(t *testing.T) { - goodID := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service -# autogenerated by Podman CI - -[Unit] -Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - goodName := `# container-foobar.service -# autogenerated by Podman CI - -[Unit] -Description=Podman container-foobar.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - goodNameBoundTo := `# container-foobar.service -# autogenerated by Podman CI - -[Unit] -Description=Podman container-foobar.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target -RefuseManualStart=yes -RefuseManualStop=yes -BindsTo=a.service b.service c.service pod.service -After=a.service b.service c.service pod.service - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - podGoodName := `# pod-123abc.service -# autogenerated by Podman CI - -[Unit] -Description=Podman pod-123abc.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target -Requires=container-1.service container-2.service -Before=container-1.service container-2.service - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - goodNameNew := `# jadda-jadda.service -# autogenerated by Podman CI - -[Unit] -Description=Podman jadda-jadda.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid -ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid -PIDFile=%t/%n-pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - goodNameNewDetach := `# jadda-jadda.service -# autogenerated by Podman CI - -[Unit] -Description=Podman jadda-jadda.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid -ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid -PIDFile=%t/%n-pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service -# autogenerated by Podman CI - -[Unit] -Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service -Documentation=man:podman-generate-systemd(1) -Wants=network.target -After=network-online.target - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid -ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid -PIDFile=%t/%n-pid -KillMode=none -Type=forking - -[Install] -WantedBy=multi-user.target default.target` - - tests := []struct { - name string - info ContainerInfo - want string - wantErr bool - }{ - - {"good with id", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - }, - goodID, - false, - }, - {"good with name", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "container-foobar", - ContainerName: "foobar", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - }, - goodName, - false, - }, - {"good with name and bound to", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "container-foobar", - ContainerName: "foobar", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - BoundToServices: []string{"pod", "a", "b", "c"}, - }, - goodNameBoundTo, - false, - }, - {"pod", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "pod-123abc", - ContainerName: "jadda-jadda-infra", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - RequiredServices: []string{"container-1", "container-2"}, - }, - podGoodName, - false, - }, - {"bad restart policy", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "never", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - }, - "", - true, - }, - {"good with name and generic", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "jadda-jadda", - ContainerName: "jadda-jadda", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, - PodmanVersion: "CI", - New: true, - CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, - }, - goodNameNew, - false, - }, - {"good with explicit short detach param", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "jadda-jadda", - ContainerName: "jadda-jadda", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, - PodmanVersion: "CI", - New: true, - CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, - }, - goodNameNew, - false, - }, - {"good with explicit full detach param", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "jadda-jadda", - ContainerName: "jadda-jadda", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, - PodmanVersion: "CI", - New: true, - CreateCommand: []string{"I'll get stripped", "container", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, - }, - goodNameNewDetach, - false, - }, - {"good with id and no param", - ContainerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - New: true, - CreateCommand: []string{"I'll get stripped", "container", "run", "awesome-image:latest"}, - }, - goodIDNew, - false, - }, - } - for _, tt := range tests { - test := tt - t.Run(tt.name, func(t *testing.T) { - opts := Options{ - Files: false, - New: test.info.New, - } - got, err := CreateContainerSystemdUnit(&test.info, opts) - if (err != nil) != test.wantErr { - t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) - return - } - if got != test.want { - t.Errorf("CreateContainerSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) - } - }) - } -} diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go index 929223244..416e60728 100644 --- a/pkg/util/mountOpts.go +++ b/pkg/util/mountOpts.go @@ -28,8 +28,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string foundWrite, foundSize, foundProp, foundMode, foundExec, foundSuid, foundDev, foundCopyUp, foundBind, foundZ bool ) - var newOptions []string - + newOptions := make([]string, 0, len(options)) for _, opt := range options { // Some options have parameters - size, mode splitOpt := strings.SplitN(opt, "=", 2) |