diff options
122 files changed, 2163 insertions, 1223 deletions
diff --git a/.golangci.yml b/.golangci.yml index 33a8b4f59..4d5ff549c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,6 +10,7 @@ run: - pkg/spec - pkg/varlink - pkg/varlinkapi + - docs/varlink skip-files: - iopodman.go - swagger.go @@ -25,10 +26,6 @@ linters: - gosec - lll - maligned - - misspell - - prealloc - - unparam - - nakedret linters-settings: errcheck: check-blank: false diff --git a/Dockerfile b/Dockerfile index 623747295..16552e6b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,11 +10,7 @@ ENV SCRIPT_BASE=./contrib/cirrus ENV PACKER_BASE=$SCRIPT_BASE/packer # Only add minimal tooling necessary to complete setup. -ADD /$SCRIPT_BASE $GOSRC/$SCRIPT_BASE -ADD /hack/install_catatonit.sh $GOSRC/hack/ -ADD /cni/*.conflist $GOSRC/cni/ -ADD /test/*.json $GOSRC/test/ -ADD /test/*.conf $GOSRC/test/ +ADD / $GOSRC WORKDIR $GOSRC # Re-use repositories and package setup as in VMs under CI diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 86cd51643..e79c5c20b 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -338,6 +338,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "pod", "", "Run container in an existing pod", ) + createFlags.StringVar( + &cf.PodIDFile, + "pod-id-file", "", + "Read the pod ID from the file", + ) createFlags.BoolVar( &cf.Privileged, "privileged", false, diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 4cba5daf7..98dc6744c 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -68,6 +68,7 @@ type ContainerCLIOpts struct { PID string PIDsLimit int64 Pod string + PodIDFile string Privileged bool PublishAll bool Pull string diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 2286e67de..0b44ef544 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -23,7 +23,7 @@ import ( "github.com/pkg/errors" ) -func getCPULimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*specs.LinuxCPU, error) { +func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { cpu := &specs.LinuxCPU{} hasLimits := false @@ -67,12 +67,12 @@ func getCPULimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) } if !hasLimits { - return nil, nil + return nil } - return cpu, nil + return cpu } -func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*specs.LinuxBlockIO, error) { +func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlockIO, error) { var err error io := &specs.LinuxBlockIO{} hasLimits := false @@ -87,7 +87,7 @@ func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) ( } if len(c.BlkIOWeightDevice) > 0 { - if err := parseWeightDevices(c.BlkIOWeightDevice, s); err != nil { + if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { return nil, err } hasLimits = true @@ -127,7 +127,7 @@ func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) ( return io, nil } -func getPidsLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) *specs.LinuxPids { +func getPidsLimits(c *ContainerCLIOpts) *specs.LinuxPids { pids := &specs.LinuxPids{} if c.CGroupsMode == "disabled" && c.PIDsLimit != 0 { return nil @@ -146,7 +146,7 @@ func getPidsLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) return nil } -func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) (*specs.LinuxMemory, error) { +func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxMemory, error) { var err error memory := &specs.LinuxMemory{} hasLimits := false @@ -254,6 +254,17 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.PublishExposedPorts = c.PublishAll s.Pod = c.Pod + if len(c.PodIDFile) > 0 { + if len(s.Pod) > 0 { + return errors.New("Cannot specify both --pod and --pod-id-file") + } + podID, err := ReadPodIDFile(c.PodIDFile) + if err != nil { + return err + } + s.Pod = podID + } + expose, err := createExpose(c.Expose) if err != nil { return err @@ -374,11 +385,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } s.Annotations = annotations - workDir := "/" - if wd := c.Workdir; len(wd) > 0 { - workDir = wd - } - s.WorkDir = workDir + s.WorkDir = c.Workdir entrypoint := []string{} userCommand := []string{} if c.Entrypoint != nil { @@ -439,19 +446,16 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if s.ResourceLimits == nil { s.ResourceLimits = &specs.LinuxResources{} } - s.ResourceLimits.Memory, err = getMemoryLimits(s, c, args) - if err != nil { - return err - } - s.ResourceLimits.BlockIO, err = getIOLimits(s, c, args) + s.ResourceLimits.Memory, err = getMemoryLimits(s, c) if err != nil { return err } - s.ResourceLimits.Pids = getPidsLimits(s, c, args) - s.ResourceLimits.CPU, err = getCPULimits(s, c, args) + s.ResourceLimits.BlockIO, err = getIOLimits(s, c) if err != nil { return err } + s.ResourceLimits.Pids = getPidsLimits(c) + s.ResourceLimits.CPU = getCPULimits(c) if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil { s.ResourceLimits = nil } @@ -693,7 +697,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start return &hc, nil } -func parseWeightDevices(weightDevs []string, s *specgen.SpecGenerator) error { +func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { for _, val := range weightDevs { split := strings.SplitN(val, ":", 2) if len(split) != 2 { diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go index 0d9f3ba26..ce323a4ba 100644 --- a/cmd/podman/common/util.go +++ b/cmd/podman/common/util.go @@ -1,6 +1,7 @@ package common import ( + "io/ioutil" "net" "strconv" "strings" @@ -10,6 +11,30 @@ import ( "github.com/sirupsen/logrus" ) +// ReadPodIDFile reads the specified file and returns its content (i.e., first +// line). +func ReadPodIDFile(path string) (string, error) { + content, err := ioutil.ReadFile(path) + if err != nil { + return "", errors.Wrap(err, "error reading pod ID file") + } + return strings.Split(string(content), "\n")[0], nil +} + +// ReadPodIDFiles reads the specified files and returns their content (i.e., +// first line). +func ReadPodIDFiles(files []string) ([]string, error) { + ids := []string{} + for _, file := range files { + id, err := ReadPodIDFile(file) + if err != nil { + return nil, err + } + ids = append(ids, id) + } + return ids, nil +} + // createExpose parses user-provided exposed port definitions and converts them // into SpecGen format. // TODO: The SpecGen format should really handle ranges more sanely - we could diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go index af4d52caa..7f15616de 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -71,7 +71,6 @@ func init() { func mount(cmd *cobra.Command, args []string) error { var ( errs utils.OutputErrors - mrs []mountReporter ) reports, err := registry.ContainerEngine().ContainerMount(registry.GetContext(), args, mountOpts) if err != nil { @@ -90,6 +89,7 @@ func mount(cmd *cobra.Command, args []string) error { if mountOpts.Format == "json" { return printJSON(reports) } + mrs := make([]mountReporter, 0, len(reports)) for _, r := range reports { mrs = append(mrs, mountReporter{r}) } @@ -110,7 +110,7 @@ func printJSON(reports []*entities.ContainerMountReport) error { Names []string Mountpoint string `json:"mountpoint"` } - var jreports []jreport + jreports := make([]jreport, 0, len(reports)) for _, r := range reports { jreports = append(jreports, jreport{ diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index a29b4da3d..ffd2054a6 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -74,7 +74,7 @@ func listFlagSet(flags *pflag.FlagSet) { _ = flags.MarkHidden("latest") } } -func checkFlags(c *cobra.Command, args []string) error { +func checkFlags(c *cobra.Command) error { // latest, and last are mutually exclusive. if listOpts.Last >= 0 && listOpts.Latest { return errors.Errorf("last and latest are mutually exclusive") @@ -144,8 +144,7 @@ func getResponses() ([]entities.ListContainer, error) { } func ps(cmd *cobra.Command, args []string) error { - var responses []psReporter - if err := checkFlags(cmd, args); err != nil { + if err := checkFlags(cmd); err != nil { return err } for _, f := range filters { @@ -172,6 +171,7 @@ func ps(cmd *cobra.Command, args []string) error { return quietOut(listContainers) } + responses := make([]psReporter, 0, len(listContainers)) for _, r := range listContainers { responses = append(responses, psReporter{r}) } @@ -351,7 +351,8 @@ func portsToString(ports []ocicni.PortMapping) string { first int32 last int32 } - var portDisplay []string + portDisplay := []string{} + if len(ports) == 0 { return "" } diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index 11aa3a4d2..260cbd25d 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -134,7 +134,7 @@ func outputStats(reports []*define.ContainerStats) error { tm.MoveCursor(1, 1) tm.Flush() } - var stats []*containerStats + stats := make([]*containerStats, 0, len(reports)) for _, r := range reports { stats = append(stats, &containerStats{r}) } @@ -228,7 +228,7 @@ func outputJSON(stats []*containerStats) error { BlockIO string `json:"block_io"` Pids string `json:"pids"` } - var jstats []jstat + jstats := make([]jstat, 0, len(stats)) for _, j := range stats { jstats = append(jstats, jstat{ Id: j.ID(), diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 75031e070..e4fdd8690 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -41,7 +41,7 @@ func init() { flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one") flags.StringVar(&systemdOptions.ContainerPrefix, "container-prefix", "container", "Systemd unit name prefix for containers") flags.StringVar(&systemdOptions.PodPrefix, "pod-prefix", "pod", "Systemd unit name prefix for pods") - flags.StringVar(&systemdOptions.Separator, "separator", "-", "Systemd unit name seperator between name/id and prefix") + flags.StringVar(&systemdOptions.Separator, "separator", "-", "Systemd unit name separator between name/id and prefix") flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index 17a80557e..ea4b9983f 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -100,7 +100,7 @@ func history(cmd *cobra.Command, args []string) error { } return err } - var hr []historyreporter + hr := make([]historyreporter, 0, len(results.Layers)) for _, l := range results.Layers { hr = append(hr, historyreporter{l}) } diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go index 223ce00f0..1ed033ec3 100644 --- a/cmd/podman/inspect/inspect.go +++ b/cmd/podman/inspect/inspect.go @@ -77,7 +77,7 @@ func newInspector(options entities.InspectOptions) (*inspector, error) { // inspect inspects the specified container/image names or IDs. func (i *inspector) inspect(namesOrIDs []string) error { // data - dumping place for inspection results. - var data []interface{} + var data []interface{} //nolint ctx := context.Background() if len(namesOrIDs) == 0 { @@ -132,7 +132,7 @@ func (i *inspector) inspect(namesOrIDs []string) error { } func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, error) { - var data []interface{} + var data []interface{} //nolint for _, name := range namesOrIDs { imgData, err := i.imageEngine.Inspect(ctx, []string{name}, i.options) if err == nil { diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 498a4dc18..747fef26d 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -56,10 +56,6 @@ func init() { } func networkList(cmd *cobra.Command, args []string) error { - var ( - nlprs []ListPrintReports - ) - // validate the filter pattern. if len(networkListOptions.Filter) > 0 { tokens := strings.Split(networkListOptions.Filter, "=") @@ -82,6 +78,7 @@ func networkList(cmd *cobra.Command, args []string) error { return jsonOut(responses) } + nlprs := make([]ListPrintReports, 0, len(responses)) for _, r := range responses { nlprs = append(nlprs, ListPrintReports{r}) } diff --git a/cmd/podman/parse/common.go b/cmd/podman/parse/common.go index 13f425b6d..b3aa88da2 100644 --- a/cmd/podman/parse/common.go +++ b/cmd/podman/parse/common.go @@ -5,6 +5,10 @@ import ( "github.com/spf13/cobra" ) +// TODO: the two functions here are almost identical. It may be worth looking +// into generalizing the two a bit more and share code but time is scarce and +// we only live once. + // CheckAllLatestAndCIDFile checks that --all and --latest are used correctly. // If cidfile is set, also check for the --cidfile flag. func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { @@ -55,3 +59,54 @@ func CheckAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool } return nil } + +// CheckAllLatestAndPodIDFile checks that --all and --latest are used correctly. +// If withIDFile is set, also check for the --pod-id-file flag. +func CheckAllLatestAndPodIDFile(c *cobra.Command, args []string, ignoreArgLen bool, withIDFile bool) error { + argLen := len(args) + if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { + if !withIDFile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("pod-id-file") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'pod-id-file'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedPodIDFile := false + if pid, _ := c.Flags().GetStringArray("pod-id-file"); len(pid) > 0 { + specifiedPodIDFile = true + } + + if specifiedPodIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --pod-id-file cannot be used together") + } else if specifiedAll && specifiedLatest { + return errors.Errorf("--all and --latest cannot be used together") + } + + if (argLen > 0) && specifiedAll { + return errors.Errorf("no arguments are needed with --all") + } + + if ignoreArgLen { + return nil + } + + if argLen > 0 { + if specifiedLatest { + return errors.Errorf("no arguments are needed with --latest") + } else if withIDFile && (specifiedLatest || specifiedPodIDFile) { + return errors.Errorf("no arguments are needed with --latest or --pod-id-file") + } + } + + if specifiedPodIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedPodIDFile { + return errors.Errorf("you must provide at least one name or id") + } + return nil +} diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 5ed5fa57c..51b7a7d52 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -53,6 +53,7 @@ func init() { flags.AddFlagSet(common.GetNetFlags()) flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") + flags.StringVar(&createOptions.InfraConmonPidFile, "infra-conmon-pidfile", "", "Path to the file that will receive the POD of the infra container's conmon") flags.StringVar(&createOptions.InfraImage, "infra-image", containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") flags.StringVar(&createOptions.InfraCommand, "infra-command", containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels") @@ -83,6 +84,9 @@ func create(cmd *cobra.Command, args []string) error { if !createOptions.Infra { logrus.Debugf("Not creating an infra container") + if cmd.Flag("infra-conmon-pidfile").Changed { + return errors.New("cannot set infra-conmon-pid without an infra container") + } if cmd.Flag("infra-command").Changed { return errors.New("cannot set infra-command without an infra container") } diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index bcd1db84c..0171bb243 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -68,7 +68,6 @@ func pods(cmd *cobra.Command, args []string) error { var ( w io.Writer = os.Stdout row string - lpr []ListPodReporter ) if psInput.Quiet && len(psInput.Format) > 0 { @@ -102,6 +101,7 @@ func pods(cmd *cobra.Command, args []string) error { return nil } + lpr := make([]ListPodReporter, 0, len(responses)) for _, r := range responses { lpr = append(lpr, ListPodReporter{r}) } @@ -220,7 +220,7 @@ func (l ListPodReporter) InfraId() string { //nolint } func (l ListPodReporter) ContainerIds() string { - var ctrids []string + ctrids := make([]string, 0, len(l.Containers)) for _, c := range l.Containers { id := c.Id if !noTrunc { @@ -232,7 +232,7 @@ func (l ListPodReporter) ContainerIds() string { } func (l ListPodReporter) ContainerNames() string { - var ctrNames []string + ctrNames := make([]string, 0, len(l.Containers)) for _, c := range l.Containers { ctrNames = append(ctrNames, c.Names) } @@ -240,7 +240,7 @@ func (l ListPodReporter) ContainerNames() string { } func (l ListPodReporter) ContainerStatuses() string { - var statuses []string + statuses := make([]string, 0, len(l.Containers)) for _, c := range l.Containers { statuses = append(statuses, c.Status) } diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 4b9882f8a..8de0bce9e 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/parse" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/cmd/podman/utils" @@ -11,7 +12,15 @@ import ( "github.com/spf13/cobra" ) +// allows for splitting API and CLI-only options +type podRmOptionsWrapper struct { + entities.PodRmOptions + + PodIDFiles []string +} + var ( + rmOptions = podRmOptionsWrapper{} podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) @@ -21,7 +30,7 @@ var ( Long: podRmDescription, RunE: rm, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod rm mywebserverpod podman pod rm -f 860a4b23 @@ -29,10 +38,6 @@ var ( } ) -var ( - rmOptions = entities.PodRmOptions{} -) - func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, @@ -45,6 +50,7 @@ func init() { flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Remove the latest pod podman is aware of") + flags.StringArrayVarP(&rmOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") if registry.IsRemote() { _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") @@ -55,7 +61,14 @@ func rm(cmd *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) - responses, err := registry.ContainerEngine().PodRm(context.Background(), args, rmOptions) + + ids, err := common.ReadPodIDFiles(rmOptions.PodIDFiles) + if err != nil { + return err + } + args = append(args, ids...) + + responses, err := registry.ContainerEngine().PodRm(context.Background(), args, rmOptions.PodRmOptions) if err != nil { return err } diff --git a/cmd/podman/pods/start.go b/cmd/podman/pods/start.go index d0150a3c2..97020b360 100644 --- a/cmd/podman/pods/start.go +++ b/cmd/podman/pods/start.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/parse" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/cmd/podman/utils" @@ -11,6 +12,13 @@ import ( "github.com/spf13/cobra" ) +// allows for splitting API and CLI-only options +type podStartOptionsWrapper struct { + entities.PodStartOptions + + PodIDFiles []string +} + var ( podStartDescription = `The pod name or ID can be used. @@ -21,7 +29,7 @@ var ( Long: podStartDescription, RunE: start, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod start podID podman pod start --latest @@ -30,7 +38,7 @@ var ( ) var ( - startOptions = entities.PodStartOptions{} + startOptions = podStartOptionsWrapper{} ) func init() { @@ -43,6 +51,7 @@ func init() { flags := startCommand.Flags() flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods") flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + flags.StringArrayVarP(&startOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") if registry.IsRemote() { _ = flags.MarkHidden("latest") } @@ -52,7 +61,14 @@ func start(cmd *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) - responses, err := registry.ContainerEngine().PodStart(context.Background(), args, startOptions) + + ids, err := common.ReadPodIDFiles(startOptions.PodIDFiles) + if err != nil { + return err + } + args = append(args, ids...) + + responses, err := registry.ContainerEngine().PodStart(context.Background(), args, startOptions.PodStartOptions) if err != nil { return err } diff --git a/cmd/podman/pods/stop.go b/cmd/podman/pods/stop.go index daf05d640..628e8a536 100644 --- a/cmd/podman/pods/stop.go +++ b/cmd/podman/pods/stop.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/parse" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/cmd/podman/utils" @@ -11,7 +12,18 @@ import ( "github.com/spf13/cobra" ) +// allows for splitting API and CLI-only options +type podStopOptionsWrapper struct { + entities.PodStopOptions + + PodIDFiles []string + TimeoutCLI uint +} + var ( + stopOptions = podStopOptionsWrapper{ + PodStopOptions: entities.PodStopOptions{Timeout: -1}, + } podStopDescription = `The pod name or ID can be used. This command will stop all running containers in each of the specified pods.` @@ -22,7 +34,7 @@ var ( Long: podStopDescription, RunE: stop, Args: func(cmd *cobra.Command, args []string) error { - return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + return parse.CheckAllLatestAndPodIDFile(cmd, args, false, true) }, Example: `podman pod stop mywebserverpod podman pod stop --latest @@ -30,13 +42,6 @@ var ( } ) -var ( - stopOptions = entities.PodStopOptions{ - Timeout: -1, - } - timeout uint -) - func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, @@ -47,7 +52,8 @@ func init() { flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running pods") flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") - flags.UintVarP(&timeout, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for pod stop before killing the container") + flags.UintVarP(&stopOptions.TimeoutCLI, "time", "t", containerConfig.Engine.StopTimeout, "Seconds to wait for pod stop before killing the container") + flags.StringArrayVarP(&stopOptions.PodIDFiles, "pod-id-file", "", nil, "Read the pod ID from the file") if registry.IsRemote() { _ = flags.MarkHidden("latest") _ = flags.MarkHidden("ignore") @@ -60,9 +66,16 @@ func stop(cmd *cobra.Command, args []string) error { errs utils.OutputErrors ) if cmd.Flag("time").Changed { - stopOptions.Timeout = int(timeout) + stopOptions.Timeout = int(stopOptions.TimeoutCLI) + } + + ids, err := common.ReadPodIDFiles(stopOptions.PodIDFiles) + if err != nil { + return err } - responses, err := registry.ContainerEngine().PodStop(context.Background(), args, stopOptions) + args = append(args, ids...) + + responses, err := registry.ContainerEngine().PodStop(context.Background(), args, stopOptions.PodStopOptions) if err != nil { return err } diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index 9318bba12..c56990cb5 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -147,15 +147,13 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { func printVerbose(reports *entities.SystemDfReport) error { var ( - dfImages []*dfImage - dfContainers []*dfContainer - dfVolumes []*dfVolume - w io.Writer = os.Stdout + w io.Writer = os.Stdout ) // Images fmt.Print("\nImages space usage:\n\n") // convert to dfImage for output + dfImages := make([]*dfImage, 0, len(reports.Images)) for _, d := range reports.Images { dfImages = append(dfImages, &dfImage{SystemDfImageReport: d}) } @@ -170,6 +168,7 @@ func printVerbose(reports *entities.SystemDfReport) error { fmt.Print("\nContainers space usage:\n\n") // convert to dfContainers for output + dfContainers := make([]*dfContainer, 0, len(reports.Containers)) for _, d := range reports.Containers { dfContainers = append(dfContainers, &dfContainer{SystemDfContainerReport: d}) } @@ -183,6 +182,7 @@ func printVerbose(reports *entities.SystemDfReport) error { // Volumes fmt.Print("\nLocal Volumes space usage:\n\n") + dfVolumes := make([]*dfVolume, 0, len(reports.Volumes)) // convert to dfVolume for output for _, d := range reports.Volumes { dfVolumes = append(dfVolumes, &dfVolume{SystemDfVolumeReport: d}) diff --git a/completions/bash/podman b/completions/bash/podman index a58becaf0..6dbe645fe 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2102,6 +2102,7 @@ _podman_container_run() { --pid --pids-limit --pod + --pod-id-file --publish -p --pull --runtime @@ -2206,7 +2207,7 @@ _podman_container_run() { __podman_complete_capabilities return ;; - --cidfile|--env-file|--init-path|--label-file) + --cidfile|--env-file|--init-path|--label-file|--pod-id-file) _filedir return ;; @@ -3097,6 +3098,7 @@ _podman_pod_create() { --dns-opt --dns-search --infra-command + --infra-conmon-pidfile --infra-image --ip --label-file @@ -3223,6 +3225,7 @@ _podman_pod_restart() { _podman_pod_rm() { local options_with_args=" + --pod-id-file " local boolean_options=" @@ -3250,6 +3253,7 @@ _podman_pod_rm() { _podman_pod_start() { local options_with_args=" + --pod-id-file " local boolean_options=" @@ -3275,6 +3279,7 @@ _podman_pod_stop() { local options_with_args=" -t --time + --pod-id-file " local boolean_options=" diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 8d3cba612..260de7b20 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -77,8 +77,9 @@ BuildRequires: systemd-devel Requires: skopeo-containers Requires: containernetworking-plugins >= 0.6.0-3 Requires: iptables -%if 0%{?rhel} <= 7 +%if 0%{?rhel} < 8 || 0%{?centos} < 8 Requires: container-selinux +Requires: runc %else %if 0%{?rhel} || 0%{?centos} Requires: runc diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index a69ef04d1..dbc835920 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -593,6 +593,10 @@ Tune the container's pids limit. Set `0` to have unlimited pids for the containe Run container in an existing pod. If you want Podman to make the pod for you, preference the pod name with `new:`. To make a pod with more granular options, use the `podman pod create` command before creating a container. +**--pod-id-file**=*path* + +Run container in an existing pod and read the pod's ID from the specified file. If a container is run within a pod, and the pod has an infra-container, the infra-container will be started before the container is. + **--privileged**=*true|false* Give extended privileges to this container. The default is *false*. diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index 72031b19b..2facd754c 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -26,10 +26,7 @@ Use the name of the container for the start, stop, and description in the unit f **--new** -Create a new container via podman-run instead of starting an existing one. This option relies on container configuration files, which may not map directly to podman CLI flags; please review the generated output carefully before placing in production. -Since we use systemd `Type=forking` service, using this option will force the container run with the detached param `-d`. - -Note: Generating systemd unit files with `--new` flag is not yet supported for pods. +Using this flag will yield unit files that do not expect containers and pods to exist. Instead, new containers and pods are created based on their configuration files. The unit files are created best effort and may need to be further edited; please review the generated files carefully before using them in production. **--time**, **-t**=*value* diff --git a/docs/source/markdown/podman-login.1.md b/docs/source/markdown/podman-login.1.md index a69b311eb..79c7ff640 100644 --- a/docs/source/markdown/podman-login.1.md +++ b/docs/source/markdown/podman-login.1.md @@ -12,7 +12,9 @@ and password. If the registry is not specified, the first registry under [regist from registries.conf will be used. **podman login** reads in the username and password from STDIN. The username and password can also be set using the **username** and **password** flags. The path of the authentication file can be specified by the user by setting the **authfile** -flag. The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**. +flag. The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**. If there is a valid +username and password in the **authfile** , Podman will use those existing credentials if the user does not pass in a username. +If those credentials are not present, Podman will then use any existing credentials found in **$HOME/.docker/config.json**. **podman [GLOBAL OPTIONS]** diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index 489c9b32e..de6b600f0 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -47,6 +47,10 @@ Set a hostname to the pod Create an infra container and associate it with the pod. An infra container is a lightweight container used to coordinate the shared kernel namespace of a pod. Default: true. +**--infra-conmon-pidfile**=*file* + +Write the pid of the infra container's **conmon** process to a file. As **conmon** runs in a separate process than Podman, this is necessary when using systemd to manage Podman containers and pods. + **--infra-command**=*command* The command that will be run to start the infra container. Default: "/pause". diff --git a/docs/source/markdown/podman-pod-rm.1.md b/docs/source/markdown/podman-pod-rm.1.md index 14da2071f..95e7ab002 100644 --- a/docs/source/markdown/podman-pod-rm.1.md +++ b/docs/source/markdown/podman-pod-rm.1.md @@ -31,6 +31,10 @@ The latest option is not supported on the remote client. Stop running containers and delete all stopped containers before removal of pod. +**--pod-id-file** + +Read pod ID from the specified file and remove the pod. Can be specified multiple times. + ## EXAMPLE podman pod rm mywebserverpod @@ -43,6 +47,8 @@ podman pod rm -f -a podman pod rm -fa +podman pod rm --pod-id-file /path/to/id/file + ## SEE ALSO podman-pod(1) diff --git a/docs/source/markdown/podman-pod-start.1.md b/docs/source/markdown/podman-pod-start.1.md index 29960d6aa..6c6cfa2cf 100644 --- a/docs/source/markdown/podman-pod-start.1.md +++ b/docs/source/markdown/podman-pod-start.1.md @@ -22,6 +22,10 @@ Instead of providing the pod name or ID, start the last created pod. The latest option is not supported on the remote client. +**--pod-id-file** + +Read pod ID from the specified file and start the pod. Can be specified multiple times. + ## EXAMPLE podman pod start mywebserverpod @@ -32,6 +36,7 @@ podman pod start --latest podman pod start --all +podman pod start --pod-id-file /path/to/id/file ## SEE ALSO podman-pod(1), podman-pod-stop(1), podman-start(1) diff --git a/docs/source/markdown/podman-pod-stop.1.md b/docs/source/markdown/podman-pod-stop.1.md index b5e7aef7d..7ce9ff941 100644 --- a/docs/source/markdown/podman-pod-stop.1.md +++ b/docs/source/markdown/podman-pod-stop.1.md @@ -31,6 +31,10 @@ The latest option is not supported on the remote client. Timeout to wait before forcibly stopping the containers in the pod. +**--pod-id-file** + +Read pod ID from the specified file and stop the pod. Can be specified multiple times. + ## EXAMPLE Stop a pod called *mywebserverpod* @@ -62,6 +66,13 @@ $ podman pod stop --all cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907 ``` +Stop two pods via --pod-id-file +``` +$ podman pod stop --pod-id-file file1 --pod-id-file file2 +19456b4cd557eaf9629825113a552681a6013f8c8cad258e36ab825ef536e818 +cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907 +``` + Stop all pods with a timeout of 1 second. ``` $ podman pod stop -a -t 1 diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 02db8b205..22f7cae09 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -605,6 +605,10 @@ Run container in an existing pod. If you want Podman to make the pod for you, pr To make a pod with more granular options, use the **podman pod create** command before creating a container. If a container is run with a pod, and the pod has an infra-container, the infra-container will be started before the container is. +**--pod-id-file**=*path* + +Run container in an existing pod and read the pod's ID from the specified file. If a container is run within a pod, and the pod has an infra-container, the infra-container will be started before the container is. + **--privileged**=**true**|**false** Give extended privileges to this container. The default is **false**. @@ -11,7 +11,7 @@ require ( github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921 github.com/containernetworking/plugins v0.8.6 github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224 - github.com/containers/common v0.13.0 + github.com/containers/common v0.13.1 github.com/containers/conmon v2.0.17+incompatible github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 github.com/containers/psgo v1.5.1 @@ -70,8 +70,8 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224 h1:EqwBZRqyUYvU7JOmmSSPviSaAoUP1wN0cefXXDZ9ATo= github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224/go.mod h1:5ZkWjOuK90yl55L5R+purJNLfUo0VUr8pstJazNtYck= github.com/containers/common v0.11.2/go.mod h1:2w3QE6VUmhltGYW4wV00h4okq1Crs7hNI1ZD2I0QRUY= -github.com/containers/common v0.13.0 h1:+7FHpPNz3YR2YcVIVNnPg2sVrXytxNgNHbd3n7SosL0= -github.com/containers/common v0.13.0/go.mod h1:LJlijBz9zi7pJqZvlbxCOsw6qNn31rzb7Zo6NBJNQxU= +github.com/containers/common v0.13.1 h1:6aE/IIxPPQk8DmND87tsWU1Aop/7mCC0T3dk/fZdm3k= +github.com/containers/common v0.13.1/go.mod h1:Kg9Enw+WOUa9pwx47fzxEdzOn5+ofYXjBJdCyaCeSSA= github.com/containers/conmon v2.0.17+incompatible h1:8BooocmNIwjOwAUGAoDD6fi3u0RrFyQ/fDkQzdiVtrI= github.com/containers/conmon v2.0.17+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.4.3/go.mod h1:pN0tvp3YbDd7BWavK2aE0mvJUqVd2HmhPjekyWSFm0U= diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 4b6ff2c1d..be2787670 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -243,9 +243,7 @@ func (s *BoltState) Refresh() error { return errors.Wrapf(err, "error unmarshalling state for container %s", string(id)) } - if err := resetState(state); err != nil { - return errors.Wrapf(err, "error resetting state for container %s", string(id)) - } + resetState(state) newStateBytes, err := json.Marshal(state) if err != nil { diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 21d55bf77..70abbb39c 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -407,10 +407,7 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt. ociRuntime, ok := s.runtime.ociRuntimes[runtimeName] if !ok { // Use a MissingRuntime implementation - ociRuntime, err = getMissingRuntime(runtimeName, s.runtime) - if err != nil { - return err - } + ociRuntime = getMissingRuntime(runtimeName, s.runtime) } ctr.ociRuntime = ociRuntime } diff --git a/libpod/container_api.go b/libpod/container_api.go index d366ffb84..d43cb4829 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -39,7 +39,7 @@ func (c *Container) Init(ctx context.Context) (err error) { } // don't recursively start - if err := c.checkDependenciesAndHandleError(ctx); err != nil { + if err := c.checkDependenciesAndHandleError(); err != nil { return err } @@ -146,7 +146,7 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e } } - if err = c.checkDependenciesAndHandleError(ctx); err != nil { + if err = c.checkDependenciesAndHandleError(); err != nil { return err } diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index b26dcddf6..c6d9e1a65 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -90,7 +90,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) } namedVolumes, mounts := c.sortUserVolumes(ctrSpec) - inspectMounts, err := c.getInspectMounts(ctrSpec, namedVolumes, mounts) + inspectMounts, err := c.getInspectMounts(namedVolumes, mounts) if err != nil { return nil, err } @@ -164,10 +164,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) } data.NetworkSettings = networkConfig - inspectConfig, err := c.generateInspectContainerConfig(ctrSpec) - if err != nil { - return nil, err - } + inspectConfig := c.generateInspectContainerConfig(ctrSpec) data.Config = inspectConfig hostConfig, err := c.generateInspectContainerHostConfig(ctrSpec, namedVolumes, mounts) @@ -195,7 +192,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) // Get inspect-formatted mounts list. // Only includes user-specified mounts. Only includes bind mounts and named // volumes, not tmpfs volumes. -func (c *Container) getInspectMounts(ctrSpec *spec.Spec, namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) { +func (c *Container) getInspectMounts(namedVolumes []*ContainerNamedVolume, mounts []spec.Mount) ([]define.InspectMount, error) { inspectMounts := []define.InspectMount{} // No mounts, return early @@ -278,7 +275,7 @@ func parseMountOptionsForInspect(options []string, mount *define.InspectMount) { } // Generate the InspectContainerConfig struct for the Config field of Inspect. -func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*define.InspectContainerConfig, error) { +func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.InspectContainerConfig { ctrConfig := new(define.InspectContainerConfig) ctrConfig.Hostname = c.Hostname() @@ -325,7 +322,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) (*define.Ins ctrConfig.CreateCommand = c.config.CreateCommand - return ctrConfig, nil + return ctrConfig } // Generate the InspectContainerHostConfig struct for the HostConfig field of diff --git a/libpod/container_internal.go b/libpod/container_internal.go index f6fc3c1a4..73e0b2118 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -239,7 +239,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er logrus.Debugf("Restarting container %s due to restart policy %s", c.ID(), c.config.RestartPolicy) // Need to check if dependencies are alive. - if err = c.checkDependenciesAndHandleError(ctx); err != nil { + if err = c.checkDependenciesAndHandleError(); err != nil { return false, err } @@ -513,7 +513,7 @@ func (c *Container) teardownStorage() error { // Reset resets state fields to default values. // It is performed before a refresh and clears the state after a reboot. // It does not save the results - assumes the database will do that for us. -func resetState(state *ContainerState) error { +func resetState(state *ContainerState) { state.PID = 0 state.ConmonPID = 0 state.Mountpoint = "" @@ -527,8 +527,6 @@ func resetState(state *ContainerState) error { state.StoppedByUser = false state.RestartPolicyMatch = false state.RestartCount = 0 - - return nil } // Refresh refreshes the container's state after a restart. @@ -756,7 +754,7 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err } if !recursive { - if err := c.checkDependenciesAndHandleError(ctx); err != nil { + if err := c.checkDependenciesAndHandleError(); err != nil { return err } } else { @@ -792,7 +790,7 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err } // checks dependencies are running and prints a helpful message -func (c *Container) checkDependenciesAndHandleError(ctx context.Context) error { +func (c *Container) checkDependenciesAndHandleError() error { notRunning, err := c.checkDependenciesRunning() if err != nil { return errors.Wrapf(err, "error checking dependencies for container %s", c.ID()) diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 9afe11b2b..12c1abf1c 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1313,7 +1313,7 @@ func (c *Container) generateResolvConf() (string, error) { } } - var dns []net.IP + dns := make([]net.IP, 0, len(c.runtime.config.Containers.DNSServers)) for _, i := range c.runtime.config.Containers.DNSServers { result := net.ParseIP(i) if result == nil { @@ -1393,7 +1393,9 @@ func (c *Container) generateHosts(path string) (string, error) { // local hosts file. netCtr is the container from which the netNS information is // taken. // path is the basis of the hosts file, into which netCtr's netNS information will be appended. -func (c *Container) appendHosts(path string, netCtr *Container) (string, error) { +// FIXME. Path should be used by this function,but I am not sure what is correct; remove //lint +// once this is fixed +func (c *Container) appendHosts(path string, netCtr *Container) (string, error) { //nolint return c.appendStringToRundir("hosts", netCtr.getHosts()) } diff --git a/libpod/define/info.go b/libpod/define/info.go index f136936f7..47c53d067 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -43,7 +43,7 @@ type RemoteSocket struct { Exists bool `json:"exists,omitempty"` } -// SlirpInfo describes the slirp exectuable that +// SlirpInfo describes the slirp executable that // is being being used. type SlirpInfo struct { Executable string `json:"executable"` diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 26fd2cab4..7f06e16fc 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -18,6 +18,9 @@ type InspectPodData struct { Namespace string `json:"Namespace,omitempty"` // Created is the time when the pod was created. Created time.Time + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string `json:"CreateCommand,omitempty"` // State represents the current state of the pod. State string `json:"State"` // Hostname is the hostname that the pod will set. diff --git a/libpod/events.go b/libpod/events.go index 20ebecc66..3d07c5d76 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -85,10 +85,7 @@ func (r *Runtime) Events(options events.ReadOptions) error { // GetEvents reads the event log and returns events based on input filters func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { - var ( - logEvents []*events.Event - readErr error - ) + var readErr error eventChannel := make(chan *events.Event) options := events.ReadOptions{ EventChannel: eventChannel, @@ -106,6 +103,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) { if readErr != nil { return nil, readErr } + logEvents := make([]*events.Event, 0, len(eventChannel)) for e := range eventChannel { logEvents = append(logEvents, e) } diff --git a/libpod/events/filters.go b/libpod/events/filters.go index b3c5eda6e..6eed1f61d 100644 --- a/libpod/events/filters.go +++ b/libpod/events/filters.go @@ -81,7 +81,7 @@ func parseFilter(filter string) (string, string, error) { } func generateEventOptions(filters []string, since, until string) ([]EventFilter, error) { - var options []EventFilter + options := make([]EventFilter, 0, len(filters)) for _, filter := range filters { key, val, err := parseFilter(filter) if err != nil { diff --git a/libpod/image/image.go b/libpod/image/image.go index 60787b826..1101e35dc 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -172,8 +172,6 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile // LoadFromArchiveReference creates a new image object for images pulled from a tar archive and the like (podman load) // This function is needed because it is possible for a tar archive to have multiple tags for one image func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*Image, error) { - var newImages []*Image - if signaturePolicyPath == "" { signaturePolicyPath = ir.SignaturePolicyPath } @@ -182,6 +180,7 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef)) } + newImages := make([]*Image, 0, len(imageNames)) for _, name := range imageNames { newImage, err := ir.NewFromLocal(name) if err != nil { @@ -475,11 +474,11 @@ func (ir *Runtime) GetRWImages() ([]*Image, error) { // getImages retrieves all images present in storage func (ir *Runtime) getImages(rwOnly bool) ([]*Image, error) { - var newImages []*Image images, err := ir.store.Images() if err != nil { return nil, err } + newImages := make([]*Image, 0, len(images)) for _, i := range images { if rwOnly && i.ReadOnly { continue diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index 3cd368cdc..74067853e 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -44,7 +44,7 @@ func cleanup(workdir string, ir *Runtime) { } } -func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) { +func makeLocalMatrix(b, bg *Image) []localImageTest { var l []localImageTest // busybox busybox := localImageTest{ @@ -65,7 +65,7 @@ func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) { busyboxGlibc.names = bbGlibcNames l = append(l, busybox, busyboxGlibc) - return l, nil + return l } @@ -100,9 +100,7 @@ func TestImage_NewFromLocal(t *testing.T) { bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) - tm, err := makeLocalMatrix(bb, bbglibc) - assert.NoError(t, err) - + tm := makeLocalMatrix(bb, bbglibc) for _, image := range tm { // tag our images err = image.img.TagImage(image.taggedName) diff --git a/libpod/image/prune.go b/libpod/image/prune.go index 3b4ea74c4..518795173 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -104,10 +104,7 @@ func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []I // PruneImages prunes dangling and optionally all unused images from the local // image store func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) { - var ( - prunedCids []string - filterFuncs []ImageFilter - ) + filterFuncs := make([]ImageFilter, 0, len(filter)) for _, f := range filter { filterSplit := strings.SplitN(f, "=", 2) if len(filterSplit) < 2 { @@ -125,6 +122,7 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( if err != nil { return nil, errors.Wrap(err, "unable to get images to prune") } + prunedCids := make([]string, 0, len(pruneImages)) for _, p := range pruneImages { repotags, err := p.RepoTags() if err != nil { diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 6b4c40ba2..24909a59a 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -366,7 +366,7 @@ func (ir *Runtime) pullGoalFromPossiblyUnqualifiedName(inputName string) (*pullG if err != nil { return nil, err } - var refPairs []pullRefPair + refPairs := make([]pullRefPair, 0, len(searchRegistries)) for _, registry := range searchRegistries { ref, err := decomposedImage.referenceWithRegistry(registry) if err != nil { diff --git a/libpod/image/search.go b/libpod/image/search.go index fd29dac45..f8d45d576 100644 --- a/libpod/image/search.go +++ b/libpod/image/search.go @@ -93,8 +93,8 @@ func SearchImages(term string, options SearchOptions) ([]SearchResult, error) { searchImageInRegistryHelper := func(index int, registry string) { defer sem.Release(1) defer wg.Done() - searchOutput, err := searchImageInRegistry(term, registry, options) - data[index] = searchOutputData{data: searchOutput, err: err} + searchOutput := searchImageInRegistry(term, registry, options) + data[index] = searchOutputData{data: searchOutput} } ctx := context.Background() @@ -131,7 +131,7 @@ func getRegistries(registry string) ([]string, error) { return registries, nil } -func searchImageInRegistry(term string, registry string, options SearchOptions) ([]SearchResult, error) { +func searchImageInRegistry(term string, registry string, options SearchOptions) []SearchResult { // Max number of queries by default is 25 limit := maxQueries if options.Limit > 0 { @@ -147,7 +147,7 @@ func searchImageInRegistry(term string, registry string, options SearchOptions) results, err := docker.SearchRegistry(context.TODO(), sc, registry, term, limit) if err != nil { logrus.Errorf("error searching registry %q: %v", registry, err) - return []SearchResult{}, nil + return []SearchResult{} } index := registry arr := strings.Split(registry, ".") @@ -201,7 +201,7 @@ func searchImageInRegistry(term string, registry string, options SearchOptions) } paramsArr = append(paramsArr, params) } - return paramsArr, nil + return paramsArr } // ParseSearchFilter turns the filter into a SearchFilter that can be used for diff --git a/libpod/kube.go b/libpod/kube.go index a3c5e912f..90acd2541 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -31,8 +31,8 @@ func (c *Container) GenerateForKube() (*v1.Pod, error) { func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { // Generate the v1.Pod yaml description var ( - servicePorts []v1.ServicePort - ports []v1.ContainerPort + ports []v1.ContainerPort //nolint + servicePorts []v1.ServicePort //nolint ) allContainers, err := p.allContainers() @@ -99,7 +99,7 @@ func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) v1 // containerPortsToServicePorts takes a slice of containerports and generates a // slice of service ports func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.ServicePort { - var sps []v1.ServicePort + sps := make([]v1.ServicePort, 0, len(containerPorts)) for _, cp := range containerPorts { nodePort := 30000 + rand.Intn(32767-30000+1) servicePort := v1.ServicePort{ @@ -116,11 +116,11 @@ func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.Servic // containersToServicePorts takes a slice of v1.Containers and generates an // inclusive list of serviceports to expose func containersToServicePorts(containers []v1.Container) []v1.ServicePort { - var sps []v1.ServicePort // Without the call to rand.Seed, a program will produce the same sequence of pseudo-random numbers // for each execution. Legal nodeport range is 30000-32767 rand.Seed(time.Now().UnixNano()) + sps := make([]v1.ServicePort, 0, len(containers)) for _, ctr := range containers { sps = append(sps, containerPortsToServicePorts(ctr.Ports)...) } @@ -128,11 +128,9 @@ func containersToServicePorts(containers []v1.Container) []v1.ServicePort { } func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort) (*v1.Pod, error) { - var ( - podContainers []v1.Container - ) deDupPodVolumes := make(map[string]*v1.Volume) first := true + podContainers := make([]v1.Container, 0, len(containers)) for _, ctr := range containers { if !ctr.IsInfra() { ctr, volumes, err := containerToV1Container(ctr) @@ -201,13 +199,11 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1. // simplePodWithV1Container is a function used by inspect when kube yaml needs to be generated // for a single container. we "insert" that container description in a pod. func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) { - var containers []v1.Container kubeCtr, kubeVols, err := containerToV1Container(ctr) if err != nil { return nil, err } - containers = append(containers, kubeCtr) - return addContainersAndVolumesToPodObject(containers, kubeVols, ctr.Name()), nil + return addContainersAndVolumesToPodObject([]v1.Container{kubeCtr}, kubeVols, ctr.Name()), nil } @@ -223,11 +219,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { if len(c.config.Spec.Linux.Devices) > 0 { // TODO Enable when we can support devices and their names - devices, err := generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) - if err != nil { - return kubeContainer, kubeVolumes, err - } - kubeContainer.VolumeDevices = devices + kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) return kubeContainer, kubeVolumes, errors.Wrapf(define.ErrNotImplemented, "linux devices") } @@ -283,7 +275,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts // it to a v1.ContainerPort format for kube output func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.ContainerPort, error) { - var containerPorts []v1.ContainerPort + containerPorts := make([]v1.ContainerPort, 0, len(portMappings)) for _, p := range portMappings { var protocol v1.Protocol switch strings.ToUpper(p.Protocol) { @@ -308,7 +300,7 @@ func ocicniPortMappingToContainerPort(portMappings []ocicni.PortMapping) ([]v1.C // libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { - var envVars []v1.EnvVar + envVars := make([]v1.EnvVar, 0, len(envs)) for _, e := range envs { split := strings.SplitN(e, "=", 2) if len(split) != 2 { @@ -325,11 +317,10 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { // libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, error) { - var vms []v1.VolumeMount - var vos []v1.Volume - // TjDO when named volumes are supported in play kube, also parse named volumes here _, mounts := c.sortUserVolumes(c.config.Spec) + vms := make([]v1.VolumeMount, 0, len(mounts)) + vos := make([]v1.Volume, 0, len(mounts)) for _, m := range mounts { vm, vo, err := generateKubeVolumeMount(m) if err != nil { @@ -404,8 +395,8 @@ func convertVolumePathToName(hostSourcePath string) (string, error) { func determineCapAddDropFromCapabilities(defaultCaps, containerCaps []string) *v1.Capabilities { var ( - drop []v1.Capability - add []v1.Capability + drop = []v1.Capability{} + add = []v1.Capability{} ) dedupDrop := make(map[string]bool) dedupAdd := make(map[string]bool) @@ -518,8 +509,8 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) { } // generateKubeVolumeDeviceFromLinuxDevice takes a list of devices and makes a VolumeDevice struct for kube -func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) ([]v1.VolumeDevice, error) { - var volumeDevices []v1.VolumeDevice +func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) []v1.VolumeDevice { + volumeDevices := make([]v1.VolumeDevice, 0, len(devices)) for _, d := range devices { vd := v1.VolumeDevice{ // TBD How are we going to sync up these names @@ -528,7 +519,7 @@ func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) ([]v1. } volumeDevices = append(volumeDevices, vd) } - return volumeDevices, nil + return volumeDevices } func removeUnderscores(s string) string { diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 90e90cc6c..8caf00e6e 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -32,7 +32,7 @@ type MissingRuntime struct { // Get a new MissingRuntime for the given name. // Requires a libpod Runtime so we can make a sane path for the exits dir. -func getMissingRuntime(name string, r *Runtime) (OCIRuntime, error) { +func getMissingRuntime(name string, r *Runtime) OCIRuntime { missingRuntimesLock.Lock() defer missingRuntimesLock.Unlock() @@ -42,7 +42,7 @@ func getMissingRuntime(name string, r *Runtime) (OCIRuntime, error) { runtime, ok := missingRuntimes[name] if ok { - return runtime, nil + return runtime } // Once for each missing runtime, we want to error. @@ -54,7 +54,7 @@ func getMissingRuntime(name string, r *Runtime) (OCIRuntime, error) { missingRuntimes[name] = newRuntime - return newRuntime, nil + return newRuntime } // Name is the name of the missing runtime diff --git a/libpod/options.go b/libpod/options.go index 8e0d3df86..5a0f60093 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1538,6 +1538,30 @@ func WithPodHostname(hostname string) PodCreateOption { } } +// WithPodCreateCommand adds the full command plus arguments of the current +// process to the pod config. +func WithPodCreateCommand() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + pod.config.CreateCommand = os.Args + return nil + } +} + +// WithInfraConmonPidFile sets the path to a custom conmon PID file for the +// infra container. +func WithInfraConmonPidFile(path string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + pod.config.InfraContainer.ConmonPidFile = path + return nil + } +} + // WithPodLabels sets the labels of a pod. func WithPodLabels(labels map[string]string) PodCreateOption { return func(pod *Pod) error { diff --git a/libpod/pod.go b/libpod/pod.go index 8afaa6052..bf0d7a397 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -64,6 +64,10 @@ type PodConfig struct { // Time pod was created CreatedTime time.Time `json:"created"` + // CreateCommand is the full command plus arguments of the process the + // container has been created with. + CreateCommand []string `json:"CreateCommand,omitempty"` + // ID of the pod's lock LockID uint32 `json:"lockID"` } @@ -79,6 +83,7 @@ type podState struct { // InfraContainerConfig is the configuration for the pod's infra container type InfraContainerConfig struct { + ConmonPidFile string `json:"conmonPidFile"` HasInfraContainer bool `json:"makeInfraContainer"` HostNetwork bool `json:"infraHostNetwork,omitempty"` PortBindings []ocicni.PortMapping `json:"infraPortBindings"` @@ -124,6 +129,12 @@ func (p *Pod) CreatedTime() time.Time { return p.config.CreatedTime } +// CreateCommand returns the os.Args of the process with which the pod has been +// created. +func (p *Pod) CreateCommand() []string { + return p.config.CreateCommand +} + // CgroupParent returns the pod's CGroup parent func (p *Pod) CgroupParent() string { return p.config.CgroupParent @@ -246,6 +257,20 @@ func (p *Pod) InfraContainerID() (string, error) { return p.state.InfraContainerID, nil } +// InfraContainer returns the infra container. +func (p *Pod) InfraContainer() (*Container, error) { + if !p.HasInfraContainer() { + return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") + } + + id, err := p.InfraContainerID() + if err != nil { + return nil, err + } + + return p.runtime.state.Container(id) +} + // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) diff --git a/libpod/pod_api.go b/libpod/pod_api.go index e2c4b515d..98f4cad73 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -432,10 +432,6 @@ func containerStatusFromContainers(allCtrs []*Container) (map[string]define.Cont // Inspect returns a PodInspect struct to describe the pod func (p *Pod) Inspect() (*define.InspectPodData, error) { - var ( - ctrs []define.InspectPodContainerInfo - ) - p.lock.Lock() defer p.lock.Unlock() if err := p.updatePod(); err != nil { @@ -446,6 +442,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { if err != nil { return nil, err } + ctrs := make([]define.InspectPodContainerInfo, 0, len(containers)) ctrStatuses := make(map[string]define.ContainerStatus, len(containers)) for _, c := range containers { containerStatus := "unknown" @@ -489,6 +486,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { Name: p.Name(), Namespace: p.Namespace(), Created: p.CreatedTime(), + CreateCommand: p.config.CreateCommand, State: podState, Hostname: p.config.Hostname, Labels: p.Labels(), diff --git a/libpod/pod_internal.go b/libpod/pod_internal.go index 851f52a4e..9e60d3c07 100644 --- a/libpod/pod_internal.go +++ b/libpod/pod_internal.go @@ -13,7 +13,7 @@ import ( ) // Creates a new, empty pod -func newPod(runtime *Runtime) (*Pod, error) { +func newPod(runtime *Runtime) *Pod { pod := new(Pod) pod.config = new(PodConfig) pod.config.ID = stringid.GenerateNonCryptoID() @@ -23,7 +23,7 @@ func newPod(runtime *Runtime) (*Pod, error) { pod.state = new(podState) pod.runtime = runtime - return pod, nil + return pod } // Update pod state from database diff --git a/libpod/runtime.go b/libpod/runtime.go index 4744de1a2..b1e48b3b3 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -286,9 +286,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { return errors.Wrapf(err, "error retrieving runtime configuration from database") } - if err := runtime.mergeDBConfig(dbConfig); err != nil { - return errors.Wrapf(err, "error merging database config into runtime config") - } + runtime.mergeDBConfig(dbConfig) logrus.Debugf("Using graph driver %s", runtime.storageConfig.GraphDriverName) logrus.Debugf("Using graph root %s", runtime.storageConfig.GraphRoot) @@ -696,11 +694,7 @@ func (r *Runtime) configureStore() error { // Set up a storage service for creating container root filesystems from // images - storageService, err := getStorageService(r.store) - if err != nil { - return err - } - r.storageService = storageService + r.storageService = getStorageService(r.store) ir := image.NewImageRuntimeFromStore(r.store) ir.SignaturePolicyPath = r.config.Engine.SignaturePolicyPath @@ -751,7 +745,7 @@ type DBConfig struct { } // mergeDBConfig merges the configuration from the database. -func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) error { +func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) { c := &r.config.Engine if !r.storageSet.RunRootSet && dbConfig.StorageTmp != "" { @@ -802,7 +796,6 @@ func (r *Runtime) mergeDBConfig(dbConfig *DBConfig) error { } c.VolumePath = dbConfig.VolumePath } - return nil } func (r *Runtime) EnableLabeling() bool { diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index aa91dff03..f0beb0941 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -813,7 +813,7 @@ func (r *Runtime) GetRunningContainers() ([]*Container, error) { // GetContainersByList is a helper function for GetContainers // which takes a []string of container IDs or names func (r *Runtime) GetContainersByList(containers []string) ([]*Container, error) { - var ctrs []*Container + ctrs := make([]*Container, 0, len(containers)) for _, inputContainer := range containers { ctr, err := r.LookupContainer(inputContainer) if err != nil { diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 06a7b3936..a0dee3aa1 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -130,6 +130,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) options = append(options, WithName(containerName)) options = append(options, withIsInfra()) + if len(p.config.InfraContainer.ConmonPidFile) > 0 { + options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) + } return r.newContainer(ctx, g.Config, options...) } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 73b6c5d9b..25584c5ad 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -28,10 +28,7 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po return nil, define.ErrRuntimeStopped } - pod, err := newPod(r) - if err != nil { - return nil, errors.Wrapf(err, "error creating pod") - } + pod := newPod(r) // Set default namespace to runtime's namespace // Do so before options run so they can override it diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index d4b46cc94..f6ecae4ab 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -29,11 +29,7 @@ func (r *Runtime) NewVolume(ctx context.Context, options ...VolumeCreateOption) // newVolume creates a new empty volume func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) (_ *Volume, deferredErr error) { - volume, err := newVolume(r) - if err != nil { - return nil, errors.Wrapf(err, "error creating volume") - } - + volume := newVolume(r) for _, option := range options { if err := option(volume); err != nil { return nil, errors.Wrapf(err, "error running volume create option") diff --git a/libpod/storage.go b/libpod/storage.go index 34e40f699..c90020833 100644 --- a/libpod/storage.go +++ b/libpod/storage.go @@ -21,8 +21,8 @@ type storageService struct { // getStorageService returns a storageService which can create container root // filesystems from images -func getStorageService(store storage.Store) (*storageService, error) { - return &storageService{store: store}, nil +func getStorageService(store storage.Store) *storageService { + return &storageService{store: store} } // ContainerInfo wraps a subset of information about a container: the locations diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 781ff77ca..d7d5a2494 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -9,7 +9,7 @@ import ( ) // Creates a new volume -func newVolume(runtime *Runtime) (*Volume, error) { +func newVolume(runtime *Runtime) *Volume { volume := new(Volume) volume.config = new(VolumeConfig) volume.state = new(VolumeState) @@ -17,8 +17,7 @@ func newVolume(runtime *Runtime) (*Volume, error) { volume.config.Labels = make(map[string]string) volume.config.Options = make(map[string]string) volume.state.NeedsCopyUp = true - - return volume, nil + return volume } // teardownStorage deletes the volume from volumePath 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) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index f40472a7c..b9a1ff83d 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -2,6 +2,7 @@ package integration import ( "fmt" + "io/ioutil" "os" "path/filepath" @@ -221,6 +222,42 @@ var _ = Describe("Podman create", func() { Expect(match).To(BeTrue()) }) + It("podman create --pod-id-file", func() { + // First, make sure that --pod and --pod-id-file yield an error + // if used together. + session := podmanTest.Podman([]string{"create", "--pod", "foo", "--pod-id-file", "bar", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + defer os.RemoveAll(tmpDir) + + podName := "rudoplh" + ctrName := "prancer" + podIDFile := tmpDir + "pod-id-file" + + // Now, let's create a pod with --pod-id-file. + session = podmanTest.Podman([]string{"pod", "create", "--pod-id-file", podIDFile, "--name", podName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "inspect", podName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.IsJSONOutputValid()).To(BeTrue()) + podData := session.InspectPodToJSON() + + // Finally we can create a container with --pod-id-file and do + // some checks to make sure it's working as expected. + session = podmanTest.Podman([]string{"create", "--pod-id-file", podIDFile, "--name", ctrName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + ctrJSON := podmanTest.InspectContainer(ctrName) + Expect(podData.ID).To(Equal(ctrJSON[0].Pod)) // Make sure the container's pod matches the pod's ID + }) + It("podman run entrypoint and cmd test", func() { name := "test101" create := podmanTest.Podman([]string{"create", "--name", name, redis}) diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index d5ae441e2..497e8f71e 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -3,6 +3,7 @@ package integration import ( + "io/ioutil" "os" . "github.com/containers/libpod/test/utils" @@ -191,7 +192,7 @@ var _ = Describe("Podman generate systemd", func() { found, _ := session.GrepString("# container-foo.service") Expect(found).To(BeTrue()) - found, _ = session.GrepString("stop --ignore --cidfile %t/%n-cid -t 42") + found, _ = session.GrepString("stop --ignore --cidfile %t/container-foo.ctr-id -t 42") Expect(found).To(BeTrue()) }) @@ -230,7 +231,7 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session.ExitCode()).To(Equal(0)) }) It("podman generate systemd --container-prefix con", func() { @@ -325,4 +326,49 @@ var _ = Describe("Podman generate systemd", func() { found, _ = session.GrepString("BindsTo=p_foo.service") Expect(found).To(BeTrue()) }) + + It("podman generate systemd pod with containers --new", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "podID" + defer os.RemoveAll(tmpDir) + + n := podmanTest.Podman([]string{"pod", "create", "--pod-id-file", tmpFile, "--name", "foo"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"generate", "systemd", "--new", "--name", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Grepping the output (in addition to unit tests) + found, _ := session.GrepString("# pod-foo.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("BindsTo=pod-foo.service") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("ExecStartPre=/usr/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10") + Expect(found).To(BeTrue()) + + found, _ = session.GrepString("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id") + Expect(found).To(BeTrue()) + }) }) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index b16cff411..0ee7260c2 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -186,6 +186,17 @@ RUN apk update && apk add strace Expect(len(result.OutputToStringArray()) >= 1).To(BeTrue()) }) + It("podman images workingdir from image", func() { + dockerfile := `FROM docker.io/library/alpine:latest +WORKDIR /test +` + podmanTest.BuildImage(dockerfile, "foobar.com/workdir:latest", "false") + result := podmanTest.Podman([]string{"run", "foobar.com/workdir:latest", "pwd"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal("/test")) + }) + It("podman images filter after image", func() { podmanTest.RestoreAllArtifacts() rmi := podmanTest.PodmanNoCache([]string{"rmi", "busybox"}) diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 8040adf1e..f1acd3750 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -57,4 +57,26 @@ var _ = Describe("Podman pod inspect", func() { podData := inspect.InspectPodToJSON() Expect(podData.ID).To(Equal(podid)) }) + + It("podman pod inspect (CreateCommand)", func() { + podName := "myTestPod" + createCommand := []string{"pod", "create", "--name", podName, "--hostname", "rudolph", "--share", "net"} + + // Create the pod. + session := podmanTest.Podman(createCommand) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Inspect the pod and make sure that the create command is + // exactly how we created the pod. + inspect := podmanTest.Podman([]string{"pod", "inspect", podName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodToJSON() + // Let's get the last len(createCommand) items in the command. + inspectCreateCommand := podData.CreateCommand + index := len(inspectCreateCommand) - len(createCommand) + Expect(inspectCreateCommand[index:]).To(Equal(createCommand)) + }) }) diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 4060e1268..d0ece7b53 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -2,6 +2,7 @@ package integration import ( "fmt" + "io/ioutil" "os" "path/filepath" "strings" @@ -229,4 +230,72 @@ var _ = Describe("Podman pod rm", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) + + It("podman pod start/remove single pod via --pod-id-file", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "podID" + defer os.RemoveAll(tmpDir) + + podName := "rudolph" + + // Create a pod with --pod-id-file. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create container inside the pod. + session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "start", "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top + + session = podmanTest.Podman([]string{"pod", "rm", "--pod-id-file", tmpFile, "--force"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) + + It("podman pod start/remove multiple pods via --pod-id-file", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + defer os.RemoveAll(tmpDir) + + podIDFiles := []string{} + for _, i := range "0123456789" { + tmpFile := tmpDir + "cid" + string(i) + podName := "rudolph" + string(i) + // Create a pod with --pod-id-file. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create container inside the pod. + session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Append the id files along with the command. + podIDFiles = append(podIDFiles, "--pod-id-file") + podIDFiles = append(podIDFiles, tmpFile) + } + + cmd := []string{"pod", "start"} + cmd = append(cmd, podIDFiles...) + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) + + cmd = []string{"pod", "rm", "--force"} + cmd = append(cmd, podIDFiles...) + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) }) diff --git a/test/e2e/pod_start_test.go b/test/e2e/pod_start_test.go index 8e78cadfd..d7d623d6e 100644 --- a/test/e2e/pod_start_test.go +++ b/test/e2e/pod_start_test.go @@ -1,7 +1,11 @@ package integration import ( + "fmt" + "io/ioutil" "os" + "strconv" + "strings" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" @@ -136,4 +140,94 @@ var _ = Describe("Podman pod start", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) }) + + It("podman pod start single pod via --pod-id-file", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "podID" + defer os.RemoveAll(tmpDir) + + podName := "rudolph" + + // Create a pod with --pod-id-file. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create container inside the pod. + session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "start", "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top + }) + + It("podman pod start multiple pods via --pod-id-file", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + defer os.RemoveAll(tmpDir) + + podIDFiles := []string{} + for _, i := range "0123456789" { + tmpFile := tmpDir + "cid" + string(i) + podName := "rudolph" + string(i) + // Create a pod with --pod-id-file. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create container inside the pod. + session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Append the id files along with the command. + podIDFiles = append(podIDFiles, "--pod-id-file") + podIDFiles = append(podIDFiles, tmpFile) + } + + cmd := []string{"pod", "start"} + cmd = append(cmd, podIDFiles...) + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) + }) + + It("podman pod create --infra-conmon-pod create + start", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "podID" + defer os.RemoveAll(tmpDir) + + podName := "rudolph" + // Create a pod with --infra-conmon-pid. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--infra-conmon-pidfile", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "start", podName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) // infra + + readFirstLine := func(path string) string { + content, err := ioutil.ReadFile(path) + Expect(err).To(BeNil()) + return strings.Split(string(content), "\n")[0] + } + + // Read the infra-conmon-pidfile and perform some sanity checks + // on the pid. + infraConmonPID := readFirstLine(tmpFile) + _, err = strconv.Atoi(infraConmonPID) // Make sure it's a proper integer + Expect(err).To(BeNil()) + + cmdline := readFirstLine(fmt.Sprintf("/proc/%s/cmdline", infraConmonPID)) + Expect(cmdline).To(ContainSubstring("/conmon")) + }) + }) diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go index 0a46b07c9..0fe580921 100644 --- a/test/e2e/pod_stop_test.go +++ b/test/e2e/pod_stop_test.go @@ -1,6 +1,7 @@ package integration import ( + "io/ioutil" "os" . "github.com/containers/libpod/test/utils" @@ -175,4 +176,72 @@ var _ = Describe("Podman pod stop", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) }) + + It("podman pod start/stop single pod via --pod-id-file", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "podID" + defer os.RemoveAll(tmpDir) + + podName := "rudolph" + + // Create a pod with --pod-id-file. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create container inside the pod. + session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "start", "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top + + session = podmanTest.Podman([]string{"pod", "stop", "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) + + It("podman pod start/stop multiple pods via --pod-id-file", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + defer os.RemoveAll(tmpDir) + + podIDFiles := []string{} + for _, i := range "0123456789" { + tmpFile := tmpDir + "cid" + string(i) + podName := "rudolph" + string(i) + // Create a pod with --pod-id-file. + session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Create container inside the pod. + session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Append the id files along with the command. + podIDFiles = append(podIDFiles, "--pod-id-file") + podIDFiles = append(podIDFiles, tmpFile) + } + + cmd := []string{"pod", "start"} + cmd = append(cmd, podIDFiles...) + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) + + cmd = []string{"pod", "stop"} + cmd = append(cmd, podIDFiles...) + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) }) diff --git a/vendor/github.com/containers/common/pkg/auth/auth.go b/vendor/github.com/containers/common/pkg/auth/auth.go index 1aa9f8b31..c52dfa01f 100644 --- a/vendor/github.com/containers/common/pkg/auth/auth.go +++ b/vendor/github.com/containers/common/pkg/auth/auth.go @@ -232,11 +232,20 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri } err = config.RemoveAuthentication(systemContext, server) - switch err { + switch errors.Cause(err) { case nil: fmt.Fprintf(opts.Stdout, "Removed login credentials for %s\n", server) return nil case config.ErrNotLoggedIn: + authConfig, err := config.GetCredentials(systemContext, server) + if err != nil { + return errors.Wrapf(err, "error reading auth file") + } + authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, server) + if authConfig.Username != "" && authConfig.Password != "" && authInvalid == nil { + fmt.Printf("Not logged into %s with current tool. Existing credentials were established via docker login. Please use docker logout instead.\n", server) + return nil + } return errors.Errorf("Not logged into %s\n", server) default: return errors.Wrapf(err, "error logging out of %q", server) diff --git a/vendor/modules.txt b/vendor/modules.txt index a44d0e88d..a470e037f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,7 +84,7 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.13.0 +# github.com/containers/common v0.13.1 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/auth github.com/containers/common/pkg/capabilities |