diff options
31 files changed, 860 insertions, 120 deletions
diff --git a/cmd/podman/README.md b/cmd/podman/README.md index c1b8f48a7..da92d0216 100644 --- a/cmd/podman/README.md +++ b/cmd/podman/README.md @@ -1,23 +1,16 @@ -# Adding a podman V2 commands +# Podman CLI -## Build podman V2 +The following is an example of how to add a new primary command (`manifest`) and a sub-command (`inspect`) to the Podman CLI. +This is example code, the production code has additional error checking and the business logic provided. -```shell script -$ cd $GOPATH/src/github.com/containers/libpod/cmd/podmanV2 -``` -If you wish to include the libpod library in your program, -```shell script -$ go build -tags 'ABISupport' . -``` -The `--remote` flag may be used to connect to the Podman service using the API. -Otherwise, direct calls will be made to the Libpod library. -```shell script -$ go build -tags '!ABISupport' . -``` -The Libpod library is not linked into the executable. -All calls are made via the API and `--remote=False` is an error condition. +See items below for details on building, installing, contributing to Podman: + - [Readme](README.md) + - [Contributing](CONTRIBUTING.md) + - [Podman Usage](transfer.md) + - [Trouble Shooting](troubleshooting.md) + - [Code Of Conduct](CODE-OF-CONDUCT.md) -## Adding a new command `podman manifests` +## Adding a new command `podman manifest` ```shell script $ mkdir -p $GOPATH/src/github.com/containers/libpod/cmd/podmanV2/manifests ``` @@ -27,6 +20,7 @@ package manifests import ( "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/validate" "github.com/containers/libpod/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -36,11 +30,11 @@ var ( manifestCmd = &cobra.Command{ Use: "manifest", Short: "Manage manifests", + Args: cobra.ExactArgs(1), Long: "Manage manifests", - Example: "podman manifests IMAGE", + Example: "podman manifest IMAGE", TraverseChildren: true, - PersistentPreRunE: preRunE, - RunE: registry.SubCommandExists, // Report error if there is no sub command given + RunE: validate.SubCommandExists, // Report error if there is no sub command given } ) func init() { @@ -51,15 +45,6 @@ func init() { // The definition for this command Command: manifestCmd, }) - // Setup cobra templates, sub commands will inherit - manifestCmd.SetHelpTemplate(registry.HelpTemplate()) - manifestCmd.SetUsageTemplate(registry.UsageTemplate()) -} - -// preRunE populates the image engine for sub commands -func preRunE(cmd *cobra.Command, args []string) error { - _, err := registry.NewImageEngine(cmd, args) - return err } ``` To "wire" in the `manifest` command, edit the file ```$GOPATH/src/github.com/containers/libpod/cmd/podmanV2/main.go``` to add: @@ -87,7 +72,11 @@ var ( Short: "Display manifest from image", Long: "Displays the low-level information on a manifest identified by image name or ID", RunE: inspect, - Example: "podman manifest DEADBEEF", + Annotations: map[string]string{ + // Add this annotation if this command cannot be run rootless + // registry.ParentNSRequired: "", + }, + Example: "podman manifest inspect DEADBEEF", } ) @@ -98,6 +87,7 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, // The definition for this command Command: inspectCmd, + // The parent command to proceed this command on the CLI Parent: manifestCmd, }) @@ -106,8 +96,22 @@ func init() { // Business logic: cmd is inspectCmd, args is the positional arguments from os.Args func inspect(cmd *cobra.Command, args []string) error { - // Business logic using registry.ImageEngine + // Business logic using registry.ImageEngine() // Do not pull from libpod directly use the domain objects and types return nil } ``` + +## Helper functions + +The complete set can be found in the `validate` package, here are some examples: + + - `cobra.Command{ Args: validate.NoArgs }` used when the command does not accept errors + - `cobra.Command{ Args: validate.IdOrLatestArgs }` used to ensure either a list of ids given or the --latest flag + - `cobra.Command{ RunE: validate.SubCommandExists }` used to validate a subcommand given to a command + - `validate.ChoiceValue` used to create a `pflag.Value` that validate user input against a provided slice of values. For example: + ```go + flags := cobraCommand.Flags() + created := validate.ChoiceValue(&opts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") + flags.Var(created, "sort", "Sort output by: "+created.Choices()) + ``` diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index a0aed984c..0f9476754 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -49,9 +49,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "cap-drop", []string{}, "Drop capabilities from the container", ) - cgroupNS := "" - createFlags.StringVar( - &cgroupNS, + createFlags.String( "cgroupns", containerConfig.CgroupNS(), "cgroup namespace to use", ) @@ -155,9 +153,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "device-write-iops", []string{}, "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", ) - createFlags.StringVar( - &cf.Entrypoint, - "entrypoint", "", + createFlags.String("entrypoint", "", "Overwrite the default ENTRYPOINT of the image", ) createFlags.StringArrayVarP( @@ -248,9 +244,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "interactive", "i", false, "Keep STDIN open even if not attached", ) - ipcNS := "" - createFlags.StringVar( - &ipcNS, + createFlags.String( "ipc", containerConfig.IPCNS(), "IPC namespace to use", ) @@ -331,9 +325,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "use `OS` instead of the running OS for choosing images", ) // markFlagHidden(createFlags, "override-os") - pid := "" - createFlags.StringVar( - &pid, + createFlags.String( "pid", containerConfig.PidNS(), "PID namespace to use", ) @@ -397,9 +389,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "security-opt", containerConfig.SecurityOptions(), "Security Options", ) - shmSize := "" - createFlags.StringVar( - &shmSize, + createFlags.String( "shm-size", containerConfig.ShmSize(), "Size of /dev/shm "+sizeWithUnitFormat, ) @@ -464,15 +454,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])", ) - userNS := "" - createFlags.StringVar( - &userNS, + createFlags.String( "userns", containerConfig.Containers.UserNS, "User namespace to use", ) - utsNS := "" - createFlags.StringVar( - &utsNS, + createFlags.String( "uts", containerConfig.Containers.UTSNS, "UTS namespace to use", ) diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 2f08bb6a6..c275b1674 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -31,7 +31,7 @@ type ContainerCLIOpts struct { DeviceReadIOPs []string DeviceWriteBPs []string DeviceWriteIOPs []string - Entrypoint string + Entrypoint *string env []string EnvHost bool EnvFile []string diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 96cd630a3..33cba30cd 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -364,20 +364,20 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.WorkDir = workDir entrypoint := []string{} userCommand := []string{} - if ep := c.Entrypoint; len(ep) > 0 { - // Check if entrypoint specified is json - if err := json.Unmarshal([]byte(c.Entrypoint), &entrypoint); err != nil { - entrypoint = append(entrypoint, ep) + if c.Entrypoint != nil { + if ep := *c.Entrypoint; len(ep) > 0 { + // Check if entrypoint specified is json + if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil { + entrypoint = append(entrypoint, ep) + } } + s.Entrypoint = entrypoint } - var command []string - s.Entrypoint = entrypoint - // Build the command // If we have an entry point, it goes first - if len(entrypoint) > 0 { + if c.Entrypoint != nil { command = entrypoint } if len(inputCommand) > 0 { @@ -386,9 +386,12 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string userCommand = append(userCommand, inputCommand...) } - if len(inputCommand) > 0 { + switch { + case len(inputCommand) > 0: s.Command = userCommand - } else { + case c.Entrypoint != nil: + s.Command = []string{} + default: s.Command = command } @@ -400,6 +403,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } s.ShmSize = &shmSize } + s.CNINetworks = c.Net.CNINetworks s.HostAdd = c.Net.AddHosts s.UseImageResolvConf = c.Net.UseImageResolvConf s.DNSServers = c.Net.DNSServers diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index f3f9d18f3..7927da04d 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -1,8 +1,10 @@ package containers import ( + "context" "fmt" "os" + "strings" "github.com/containers/common/pkg/config" "github.com/containers/libpod/cmd/podman/common" @@ -106,6 +108,10 @@ func create(cmd *cobra.Command, args []string) error { return err } + if _, err := createPodIfNecessary(s); err != nil { + return err + } + report, err := registry.ContainerEngine().ContainerCreate(registry.GetContext(), s) if err != nil { return err @@ -160,6 +166,10 @@ func createInit(c *cobra.Command) error { if c.Flag("cgroupns").Changed { cliVals.CGroupsNS = c.Flag("cgroupns").Value.String() } + if c.Flag("entrypoint").Changed { + val := c.Flag("entrypoint").Value.String() + cliVals.Entrypoint = &val + } // Docker-compatibility: the "-h" flag for run/create is reserved for // the hostname (see https://github.com/containers/libpod/issues/1367). @@ -181,8 +191,10 @@ func pullImage(imageName string) error { return errors.New("unable to find a name and tag match for busybox in repotags: no such image") } _, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ - Authfile: cliVals.Authfile, - Quiet: cliVals.Quiet, + Authfile: cliVals.Authfile, + Quiet: cliVals.Quiet, + OverrideArch: cliVals.OverrideArch, + OverrideOS: cliVals.OverrideOS, }) if pullErr != nil { return pullErr @@ -204,3 +216,25 @@ func openCidFile(cidfile string) (*os.File, error) { } return cidFile, nil } + +// createPodIfNecessary automatically creates a pod when requested. if the pod name +// has the form new:ID, the pod ID is created and the name in the spec generator is replaced +// with ID. +func createPodIfNecessary(s *specgen.SpecGenerator) (*entities.PodCreateReport, error) { + if !strings.HasPrefix(s.Pod, "new:") { + return nil, nil + } + podName := strings.Replace(s.Pod, "new:", "", 1) + if len(podName) < 1 { + return nil, errors.Errorf("new pod name must be at least one character") + } + createOptions := entities.PodCreateOptions{ + Name: podName, + Infra: true, + Net: &entities.NetOptions{ + PublishPorts: s.PortMappings, + }, + } + s.Pod = podName + return registry.ContainerEngine().PodCreate(context.Background(), createOptions) +} diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index c5696a158..e9146bda7 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -67,8 +67,8 @@ func listFlagSet(flags *pflag.FlagSet) { flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime") flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds") - created := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") - flags.Var(created, "sort", "Sort output by: "+created.Choices()) + sort := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") + flags.Var(sort, "sort", "Sort output by: "+sort.Choices()) if registry.IsRemote() { _ = flags.MarkHidden("latest") diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 1e4d47b42..b13983e37 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -146,6 +146,10 @@ func run(cmd *cobra.Command, args []string) error { } runOpts.Spec = s + if _, err := createPodIfNecessary(s); err != nil { + return err + } + report, err := registry.ContainerEngine().ContainerRun(registry.GetContext(), runOpts) // report.ExitCode is set by ContainerRun even it it returns an error if report != nil { diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go index 381bf8e26..ce78d24ed 100644 --- a/cmd/podman/containers/start.go +++ b/cmd/podman/containers/start.go @@ -99,7 +99,7 @@ func start(cmd *cobra.Command, args []string) error { for _, r := range responses { if r.Err == nil { - fmt.Println(r.Id) + fmt.Println(r.RawInput) } else { errs = append(errs, r.Err) } diff --git a/cmd/podman/images/search.go b/cmd/podman/images/search.go index a8abfb339..a259b2419 100644 --- a/cmd/podman/images/search.go +++ b/cmd/podman/images/search.go @@ -105,6 +105,10 @@ func imageSearch(cmd *cobra.Command, args []string) error { return errors.Errorf("search requires exactly one argument") } + if searchOptions.Limit > 100 { + return errors.Errorf("Limit %d is outside the range of [1, 100]", searchOptions.Limit) + } + // TLS verification in c/image is controlled via a `types.OptionalBool` // which allows for distinguishing among set-true, set-false, unspecified // which is important to implement a sane way of dealing with defaults of diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 3a8958b6d..422dee90b 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -9,6 +9,7 @@ import ( _ "github.com/containers/libpod/cmd/podman/healthcheck" _ "github.com/containers/libpod/cmd/podman/images" _ "github.com/containers/libpod/cmd/podman/manifest" + _ "github.com/containers/libpod/cmd/podman/networks" _ "github.com/containers/libpod/cmd/podman/pods" "github.com/containers/libpod/cmd/podman/registry" _ "github.com/containers/libpod/cmd/podman/system" diff --git a/cmd/podman/networks/create.go b/cmd/podman/networks/create.go new file mode 100644 index 000000000..2bb75ea9e --- /dev/null +++ b/cmd/podman/networks/create.go @@ -0,0 +1,81 @@ +package network + +import ( + "fmt" + "net" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/network" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +var ( + networkCreateDescription = `create CNI networks for containers and pods` + networkCreateCommand = &cobra.Command{ + Use: "create [flags] [NETWORK]", + Short: "network create", + Long: networkCreateDescription, + RunE: networkCreate, + Example: `podman network create podman1`, + Annotations: map[string]string{ + registry.ParentNSRequired: "", + }, + } +) + +var ( + networkCreateOptions entities.NetworkCreateOptions +) + +func networkCreateFlags(flags *pflag.FlagSet) { + flags.StringVarP(&networkCreateOptions.Driver, "driver", "d", "bridge", "driver to manage the network") + flags.IPVar(&networkCreateOptions.Gateway, "gateway", nil, "IPv4 or IPv6 gateway for the subnet") + flags.BoolVar(&networkCreateOptions.Internal, "internal", false, "restrict external access from this network") + flags.IPNetVar(&networkCreateOptions.Range, "ip-range", net.IPNet{}, "allocate container IP from range") + flags.StringVar(&networkCreateOptions.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device") + // TODO not supported yet + //flags.StringVar(&networkCreateOptions.IPamDriver, "ipam-driver", "", "IP Address Management Driver") + // TODO enable when IPv6 is working + //flags.BoolVar(&networkCreateOptions.IPV6, "IPv6", false, "enable IPv6 networking") + flags.IPNetVar(&networkCreateOptions.Subnet, "subnet", net.IPNet{}, "subnet in CIDR format") + flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") +} +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: networkCreateCommand, + Parent: networkCmd, + }) + flags := networkCreateCommand.Flags() + networkCreateFlags(flags) + +} + +func networkCreate(cmd *cobra.Command, args []string) error { + var ( + name string + ) + if err := network.IsSupportedDriver(networkCreateOptions.Driver); err != nil { + return err + } + if len(args) > 1 { + return errors.Errorf("only one network can be created at a time") + } + if len(args) > 0 && !libpod.NameRegex.MatchString(args[0]) { + return libpod.RegexError + } + + if len(args) > 0 { + name = args[0] + } + response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions) + if err != nil { + return err + } + fmt.Println(response.Filename) + return nil +} diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go new file mode 100644 index 000000000..0bc73579a --- /dev/null +++ b/cmd/podman/networks/inspect.go @@ -0,0 +1,46 @@ +package network + +import ( + "encoding/json" + "fmt" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + networkinspectDescription = `Inspect network` + networkinspectCommand = &cobra.Command{ + Use: "inspect NETWORK [NETWORK...] [flags] ", + Short: "network inspect", + Long: networkinspectDescription, + RunE: networkInspect, + Example: `podman network inspect podman`, + Args: cobra.MinimumNArgs(1), + Annotations: map[string]string{ + registry.ParentNSRequired: "", + }, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: networkinspectCommand, + Parent: networkCmd, + }) +} + +func networkInspect(cmd *cobra.Command, args []string) error { + responses, err := registry.ContainerEngine().NetworkInspect(registry.Context(), args, entities.NetworkInspectOptions{}) + if err != nil { + return err + } + b, err := json.MarshalIndent(responses, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil +} diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go new file mode 100644 index 000000000..e27062255 --- /dev/null +++ b/cmd/podman/networks/list.go @@ -0,0 +1,133 @@ +package network + +import ( + "encoding/json" + "fmt" + "html/template" + "io" + "os" + "strings" + + "github.com/containers/libpod/cmd/podman/validate" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/network" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +var ( + networklistDescription = `List networks` + networklistCommand = &cobra.Command{ + Use: "ls", + Args: validate.NoArgs, + Short: "network list", + Long: networklistDescription, + RunE: networkList, + Example: `podman network list`, + Annotations: map[string]string{ + registry.ParentNSRequired: "", + }, + } +) + +var ( + networkListOptions entities.NetworkListOptions + headers string = "NAME\tVERSION\tPLUGINS\n" + defaultListRow string = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" +) + +func networkListFlags(flags *pflag.FlagSet) { + // TODO enable filters based on something + //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") + flags.StringVarP(&networkListOptions.Format, "format", "f", "", "Pretty-print containers to JSON or using a Go template") + flags.BoolVarP(&networkListOptions.Quiet, "quiet", "q", false, "display only names") +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: networklistCommand, + Parent: networkCmd, + }) + flags := networklistCommand.Flags() + networkListFlags(flags) +} + +func networkList(cmd *cobra.Command, args []string) error { + var ( + w io.Writer = os.Stdout + nlprs []NetworkListPrintReports + ) + + responses, err := registry.ContainerEngine().NetworkList(registry.Context(), networkListOptions) + if err != nil { + return err + } + + // quiet means we only print the network names + if networkListOptions.Quiet { + return quietOut(responses) + } + + if networkListOptions.Format == "json" { + return jsonOut(responses) + } + + for _, r := range responses { + nlprs = append(nlprs, NetworkListPrintReports{r}) + } + + row := networkListOptions.Format + if len(row) < 1 { + row = defaultListRow + } + if !strings.HasSuffix(row, "\n") { + row += "\n" + } + + format := "{{range . }}" + row + "{{end}}" + if !cmd.Flag("format").Changed { + format = headers + format + } + tmpl, err := template.New("listNetworks").Parse(format) + if err != nil { + return err + } + if err := tmpl.Execute(w, nlprs); err != nil { + return err + } + if flusher, ok := w.(interface{ Flush() error }); ok { + return flusher.Flush() + } + return nil +} + +func quietOut(responses []*entities.NetworkListReport) error { + for _, r := range responses { + fmt.Println(r.Name) + } + return nil +} + +func jsonOut(responses []*entities.NetworkListReport) error { + b, err := json.MarshalIndent(responses, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil +} + +type NetworkListPrintReports struct { + *entities.NetworkListReport +} + +func (n NetworkListPrintReports) Version() string { + return n.CNIVersion +} + +func (n NetworkListPrintReports) Plugins() string { + return network.GetCNIPlugins(n.NetworkConfigList) +} diff --git a/cmd/podman/networks/network.go b/cmd/podman/networks/network.go index e2a928312..56dd390ea 100644 --- a/cmd/podman/networks/network.go +++ b/cmd/podman/networks/network.go @@ -1,4 +1,4 @@ -package images +package network import ( "github.com/containers/libpod/cmd/podman/registry" @@ -9,7 +9,7 @@ import ( var ( // Command: podman _network_ - cmd = &cobra.Command{ + networkCmd = &cobra.Command{ Use: "network", Short: "Manage networks", Long: "Manage networks", @@ -18,12 +18,9 @@ var ( } ) -// TODO add the following to main.go to get networks back onto the -// command list. -// _ "github.com/containers/libpod/cmd/podman/networks" func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode}, - Command: cmd, + Command: networkCmd, }) } diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go new file mode 100644 index 000000000..dc1eb9909 --- /dev/null +++ b/cmd/podman/networks/rm.go @@ -0,0 +1,63 @@ +package network + +import ( + "fmt" + + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +var ( + networkrmDescription = `Remove networks` + networkrmCommand = &cobra.Command{ + Use: "rm [flags] NETWORK [NETWORK...]", + Short: "network rm", + Long: networkrmDescription, + RunE: networkRm, + Example: `podman network rm podman`, + Args: cobra.MinimumNArgs(1), + Annotations: map[string]string{ + registry.ParentNSRequired: "", + }, + } +) + +var ( + networkRmOptions entities.NetworkRmOptions +) + +func networkRmFlags(flags *pflag.FlagSet) { + flags.BoolVarP(&networkRmOptions.Force, "force", "f", false, "remove any containers using network") +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: networkrmCommand, + Parent: networkCmd, + }) + flags := networkrmCommand.Flags() + networkRmFlags(flags) +} + +func networkRm(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + + responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions) + if err != nil { + return err + } + for _, r := range responses { + if r.Err == nil { + fmt.Println(r.Name) + } else { + errs = append(errs, r.Err) + } + } + return errs.PrintErrors() +} diff --git a/docs/source/markdown/podman-search.1.md b/docs/source/markdown/podman-search.1.md index 31de6f839..75bfeb058 100644 --- a/docs/source/markdown/podman-search.1.md +++ b/docs/source/markdown/podman-search.1.md @@ -59,7 +59,7 @@ Valid placeholders for the Go template are listed below: **--limit**=*limit* -Limit the number of results +Limit the number of results. This value can be in the range between 1 and 100. The default number of results is 25. Note: The results from each registry will be limited to this value. Example if limit is 10 and two registries are being searched, the total number of results will be 20, 10 from each (if there are at least 10 matches in each). diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index e58258b75..622e8eb5b 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -227,6 +227,7 @@ type ContainerStartOptions struct { // containers from the cli type ContainerStartReport struct { Id string + RawInput string Err error ExitCode int } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index d98aee6df..734f72e5f 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -45,6 +45,10 @@ type ContainerEngine interface { SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) + NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error) + NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error) + NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error) + NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go new file mode 100644 index 000000000..cffd40899 --- /dev/null +++ b/pkg/domain/entities/network.go @@ -0,0 +1,52 @@ +package entities + +import ( + "net" + + "github.com/containernetworking/cni/libcni" +) + +// NetworkListOptions describes options for listing networks in cli +type NetworkListOptions struct { + Format string + Quiet bool +} + +// NetworkListReport describes the results from listing networks +type NetworkListReport struct { + *libcni.NetworkConfigList +} + +// NetworkInspectOptions describes options for inspect networks +type NetworkInspectOptions struct { +} + +// NetworkInspectReport describes the results from inspect networks +type NetworkInspectReport map[string]interface{} + +// NetworkRmOptions describes options for removing networks +type NetworkRmOptions struct { + Force bool +} + +//NetworkRmReport describes the results of network removal +type NetworkRmReport struct { + Name string + Err error +} + +// NetworkCreateOptions describes options to create a network +type NetworkCreateOptions struct { + DisableDNS bool + Driver string + Gateway net.IP + Internal bool + MacVLAN string + Range net.IPNet + Subnet net.IPNet +} + +// NetworkCreateReport describes a created network for the cli +type NetworkCreateReport struct { + Filename string +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index cb61176b8..244fbc5cd 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -32,9 +32,9 @@ import ( "github.com/sirupsen/logrus" ) -// getContainersByContext gets pods whether all, latest, or a slice of names/ids -// is specified. -func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { +// getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids +// is specified. It also returns a list of the corresponding input name used to lookup each container. +func getContainersAndInputByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) { var ctr *libpod.Container ctrs = []*libpod.Container{} @@ -43,6 +43,7 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru ctrs, err = runtime.GetAllContainers() case latest: ctr, err = runtime.GetLatestContainer() + rawInput = append(rawInput, ctr.ID()) ctrs = append(ctrs, ctr) default: for _, n := range names { @@ -54,6 +55,7 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru err = e } } else { + rawInput = append(rawInput, n) ctrs = append(ctrs, ctr) } } @@ -61,6 +63,13 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru return } +// getContainersByContext gets containers whether all, latest, or a slice of names/ids +// is specified. +func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { + ctrs, _, err = getContainersAndInputByContext(all, latest, names, runtime) + return +} + // TODO: Should return *entities.ContainerExistsReport, error func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { _, err := ic.Libpod.LookupContainer(nameOrId) @@ -518,7 +527,8 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, } // If the container is in a pod, also set to recursively start dependencies - if err := terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach { + err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "") + if err != nil && errors.Cause(err) != define.ErrDetach { return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) } return nil @@ -559,12 +569,14 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { var reports []*entities.ContainerStartReport var exitCode = define.ExecErrorCodeGeneric - ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) + ctrs, rawInputs, err := getContainersAndInputByContext(false, options.Latest, namesOrIds, ic.Libpod) if err != nil { return nil, err } // There can only be one container if attach was used - for _, ctr := range ctrs { + for i := range ctrs { + ctr := ctrs[i] + rawInput := rawInputs[i] ctrState, err := ctr.State() if err != nil { return nil, err @@ -578,6 +590,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri // Exit cleanly immediately reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), + RawInput: rawInput, Err: nil, ExitCode: 0, }) @@ -588,6 +601,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri logrus.Debugf("Deadlock error: %v", err) reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), + RawInput: rawInput, Err: err, ExitCode: define.ExitCode(err), }) @@ -597,6 +611,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if ctrRunning { reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), + RawInput: rawInput, Err: nil, ExitCode: 0, }) @@ -606,6 +621,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri if err != nil { reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), + RawInput: rawInput, Err: err, ExitCode: exitCode, }) @@ -628,6 +644,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), + RawInput: rawInput, Err: err, ExitCode: exitCode, }) @@ -640,6 +657,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri // If the container is in a pod, also set to recursively start dependencies report := &entities.ContainerStartReport{ Id: ctr.ID(), + RawInput: rawInput, ExitCode: 125, } if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil { diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go new file mode 100644 index 000000000..5c39b5374 --- /dev/null +++ b/pkg/domain/infra/abi/network.go @@ -0,0 +1,258 @@ +package abi + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + + cniversion "github.com/containernetworking/cni/pkg/version" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/network" + "github.com/containers/libpod/pkg/util" + "github.com/pkg/errors" +) + +func getCNIConfDir(r *libpod.Runtime) (string, error) { + config, err := r.GetConfig() + if err != nil { + return "", err + } + configPath := config.Network.NetworkConfigDir + + if len(config.Network.NetworkConfigDir) < 1 { + configPath = network.CNIConfigDir + } + return configPath, nil +} + +func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { + var reports []*entities.NetworkListReport + cniConfigPath, err := getCNIConfDir(ic.Libpod) + if err != nil { + return nil, err + } + networks, err := network.LoadCNIConfsFromDir(cniConfigPath) + if err != nil { + return nil, err + } + + for _, n := range networks { + reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n}) + } + return reports, nil +} + +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { + var ( + rawCNINetworks []entities.NetworkInspectReport + ) + for _, name := range namesOrIds { + rawList, err := network.InspectNetwork(name) + if err != nil { + return nil, err + } + rawCNINetworks = append(rawCNINetworks, rawList) + } + return rawCNINetworks, nil +} + +func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { + var reports []*entities.NetworkRmReport + for _, name := range namesOrIds { + report := entities.NetworkRmReport{Name: name} + containers, err := ic.Libpod.GetAllContainers() + if err != nil { + return reports, err + } + // We need to iterate containers looking to see if they belong to the given network + for _, c := range containers { + if util.StringInSlice(name, c.Config().Networks) { + // if user passes force, we nuke containers + if !options.Force { + // Without the force option, we return an error + return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers", name) + } + if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil { + return reports, err + } + } + } + if err := network.RemoveNetwork(name); err != nil { + report.Err = err + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { + var ( + err error + fileName string + ) + if len(options.MacVLAN) > 0 { + fileName, err = createMacVLAN(ic.Libpod, name, options) + } else { + fileName, err = createBridge(ic.Libpod, name, options) + } + if err != nil { + return nil, err + } + return &entities.NetworkCreateReport{Filename: fileName}, nil +} + +// createBridge creates a CNI network +func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) { + isGateway := true + ipMasq := true + subnet := &options.Subnet + ipRange := options.Range + runtimeConfig, err := r.GetConfig() + if err != nil { + return "", err + } + // if range is provided, make sure it is "in" network + if subnet.IP != nil { + // if network is provided, does it conflict with existing CNI or live networks + err = network.ValidateUserNetworkIsAvailable(subnet) + } else { + // if no network is provided, figure out network + subnet, err = network.GetFreeNetwork() + } + if err != nil { + return "", err + } + gateway := options.Gateway + if gateway == nil { + // if no gateway is provided, provide it as first ip of network + gateway = network.CalcGatewayIP(subnet) + } + // if network is provided and if gateway is provided, make sure it is "in" network + if options.Subnet.IP != nil && options.Gateway != nil { + if !subnet.Contains(gateway) { + return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String()) + } + } + if options.Internal { + isGateway = false + ipMasq = false + } + + // if a range is given, we need to ensure it is "in" the network range. + if options.Range.IP != nil { + if options.Subnet.IP == nil { + return "", errors.New("you must define a subnet range to define an ip-range") + } + firstIP, err := network.FirstIPInSubnet(&options.Range) + if err != nil { + return "", err + } + lastIP, err := network.LastIPInSubnet(&options.Range) + if err != nil { + return "", err + } + if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) { + return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String()) + } + } + bridgeDeviceName, err := network.GetFreeDeviceName() + if err != nil { + return "", err + } + + if len(name) > 0 { + netNames, err := network.GetNetworkNamesFromFileSystem() + if err != nil { + return "", err + } + if util.StringInSlice(name, netNames) { + return "", errors.Errorf("the network name %s is already used", name) + } + } else { + // If no name is given, we give the name of the bridge device + name = bridgeDeviceName + } + + ncList := network.NewNcList(name, cniversion.Current()) + var plugins []network.CNIPlugins + var routes []network.IPAMRoute + + defaultRoute, err := network.NewIPAMDefaultRoute() + if err != nil { + return "", err + } + routes = append(routes, defaultRoute) + ipamConfig, err := network.NewIPAMHostLocalConf(subnet, routes, ipRange, gateway) + if err != nil { + return "", err + } + + // TODO need to iron out the role of isDefaultGW and IPMasq + bridge := network.NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig) + plugins = append(plugins, bridge) + plugins = append(plugins, network.NewPortMapPlugin()) + plugins = append(plugins, network.NewFirewallPlugin()) + // if we find the dnsname plugin, we add configuration for it + if network.HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS { + // Note: in the future we might like to allow for dynamic domain names + plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName)) + } + ncList["plugins"] = plugins + b, err := json.MarshalIndent(ncList, "", " ") + if err != nil { + return "", err + } + cniConfigPath, err := getCNIConfDir(r) + if err != nil { + return "", err + } + cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name)) + err = ioutil.WriteFile(cniPathName, b, 0644) + return cniPathName, err +} + +func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) { + var ( + plugins []network.CNIPlugins + ) + liveNetNames, err := network.GetLiveNetworkNames() + if err != nil { + return "", err + } + // Make sure the host-device exists + if !util.StringInSlice(options.MacVLAN, liveNetNames) { + return "", errors.Errorf("failed to find network interface %q", options.MacVLAN) + } + if len(name) > 0 { + netNames, err := network.GetNetworkNamesFromFileSystem() + if err != nil { + return "", err + } + if util.StringInSlice(name, netNames) { + return "", errors.Errorf("the network name %s is already used", name) + } + } else { + name, err = network.GetFreeDeviceName() + if err != nil { + return "", err + } + } + ncList := network.NewNcList(name, cniversion.Current()) + macvlan := network.NewMacVLANPlugin(options.MacVLAN) + plugins = append(plugins, macvlan) + ncList["plugins"] = plugins + b, err := json.MarshalIndent(ncList, "", " ") + if err != nil { + return "", err + } + cniConfigPath, err := getCNIConfDir(r) + if err != nil { + return "", err + } + cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name)) + err = ioutil.WriteFile(cniPathName, b, 0644) + return cniPathName, err +} diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go new file mode 100644 index 000000000..4ff72dcfc --- /dev/null +++ b/pkg/domain/infra/tunnel/network.go @@ -0,0 +1,23 @@ +package tunnel + +import ( + "context" + "errors" + + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { + return nil, errors.New("not implemented") +} + +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { + return nil, errors.New("not implemented") +} +func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { + return nil, errors.New("not implemented") +} + +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { + return nil, errors.New("not implemented") +} diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index a62344640..41ed5f1f0 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -16,6 +16,8 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) const CpuPeriod = 100000 @@ -534,11 +536,31 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error { // If not explicitly overridden by the user, default number of open // files and number of processes to the maximum they can be set to // (without overriding a sysctl) - if !nofileSet && !isRootless { - g.AddProcessRlimits("RLIMIT_NOFILE", kernelMax, kernelMax) - } - if !nprocSet && !isRootless { - g.AddProcessRlimits("RLIMIT_NPROC", kernelMax, kernelMax) + if !nofileSet { + max := kernelMax + current := kernelMax + if isRootless { + var rlimit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { + logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err) + } + current = rlimit.Cur + max = rlimit.Max + } + g.AddProcessRlimits("RLIMIT_NOFILE", current, max) + } + if !nprocSet { + max := kernelMax + current := kernelMax + if isRootless { + var rlimit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { + logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err) + } + current = rlimit.Cur + max = rlimit.Max + } + g.AddProcessRlimits("RLIMIT_NPROC", current, max) } return nil diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 01ddcf9c8..14836035d 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -24,11 +24,10 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener // If joining a pod, retrieve the pod for use. var pod *libpod.Pod if s.Pod != "" { - foundPod, err := rt.LookupPod(s.Pod) + pod, err = rt.LookupPod(s.Pod) if err != nil { return nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) } - pod = foundPod } // Set defaults for unset namespaces @@ -130,12 +129,8 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l logrus.Debugf("setting container name %s", s.Name) options = append(options, libpod.WithName(s.Name)) } - if s.Pod != "" { - pod, err := rt.LookupPod(s.Pod) - if err != nil { - return nil, err - } - logrus.Debugf("adding container to pod %s", s.Pod) + if pod != nil { + logrus.Debugf("adding container to pod %s", pod.Name()) options = append(options, rt.WithPod(pod)) } destinations := []string{} @@ -160,11 +155,12 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l options = append(options, libpod.WithNamedVolumes(vols)) } - if len(s.Command) != 0 { + if s.Command != nil { options = append(options, libpod.WithCommand(s.Command)) } - - options = append(options, libpod.WithEntrypoint(s.Entrypoint)) + if s.Entrypoint != nil { + options = append(options, libpod.WithEntrypoint(s.Entrypoint)) + } if s.StopSignal != nil { options = append(options, libpod.WithStopSignal(*s.StopSignal)) } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 87262684e..7993777fb 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -13,6 +13,8 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { @@ -41,11 +43,31 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error { // If not explicitly overridden by the user, default number of open // files and number of processes to the maximum they can be set to // (without overriding a sysctl) - if !nofileSet && !isRootless { - g.AddProcessRlimits("RLIMIT_NOFILE", kernelMax, kernelMax) - } - if !nprocSet && !isRootless { - g.AddProcessRlimits("RLIMIT_NPROC", kernelMax, kernelMax) + if !nofileSet { + max := kernelMax + current := kernelMax + if isRootless { + var rlimit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil { + logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err) + } + current = rlimit.Cur + max = rlimit.Max + } + g.AddProcessRlimits("RLIMIT_NOFILE", current, max) + } + if !nprocSet { + max := kernelMax + current := kernelMax + if isRootless { + var rlimit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil { + logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err) + } + current = rlimit.Cur + max = rlimit.Max + } + g.AddProcessRlimits("RLIMIT_NPROC", current, max) } return nil @@ -67,7 +89,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image finalCommand = append(finalCommand, entrypoint...) command := s.Command - if len(command) == 0 && img != nil { + if command == nil && img != nil { newCmd, err := img.Cmd(ctx) if err != nil { return nil, err diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 82346823a..10742a0e8 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -18,7 +18,6 @@ var _ = Describe("Podman create", func() { ) BeforeEach(func() { - Skip(v2fail) tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 19dabced7..7eccaa9ab 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -76,7 +76,6 @@ var _ = Describe("Podman network create", func() { ) BeforeEach(func() { - Skip(v2fail) SkipIfRootless() tempdir, err = CreateTempDirInTempDir() if err != nil { diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index 2cb7eb144..8d575d7f9 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -34,7 +34,7 @@ var _ = Describe("Podman network", func() { ) BeforeEach(func() { - Skip(v2fail) + SkipIfRootless() tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -80,7 +80,6 @@ var _ = Describe("Podman network", func() { ) It("podman network list", func() { - SkipIfRootless() // Setup, use uuid to prevent conflict with other tests uuid := stringid.GenerateNonCryptoID() secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) @@ -94,7 +93,6 @@ var _ = Describe("Podman network", func() { }) It("podman network list -q", func() { - SkipIfRootless() // Setup, use uuid to prevent conflict with other tests uuid := stringid.GenerateNonCryptoID() secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) @@ -108,14 +106,12 @@ var _ = Describe("Podman network", func() { }) It("podman network rm no args", func() { - SkipIfRootless() session := podmanTest.Podman([]string{"network", "rm"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).ToNot(BeZero()) }) It("podman network rm", func() { - SkipIfRootless() // Setup, use uuid to prevent conflict with other tests uuid := stringid.GenerateNonCryptoID() secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) @@ -138,14 +134,12 @@ var _ = Describe("Podman network", func() { }) It("podman network inspect no args", func() { - SkipIfRootless() session := podmanTest.Podman([]string{"network", "inspect"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).ToNot(BeZero()) }) It("podman network inspect", func() { - SkipIfRootless() // Setup, use uuid to prevent conflict with other tests uuid := stringid.GenerateNonCryptoID() secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) @@ -159,7 +153,6 @@ var _ = Describe("Podman network", func() { }) It("podman inspect container single CNI network", func() { - SkipIfRootless() netName := "testNetSingleCNI" network := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.50.0/24", netName}) network.WaitWithDefaultTimeout() @@ -190,7 +183,6 @@ var _ = Describe("Podman network", func() { }) It("podman inspect container two CNI networks", func() { - SkipIfRootless() netName1 := "testNetTwoCNI1" network1 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.0/25", netName1}) network1.WaitWithDefaultTimeout() diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go index ebc06b36c..b1344a371 100644 --- a/test/e2e/run_entrypoint_test.go +++ b/test/e2e/run_entrypoint_test.go @@ -18,7 +18,6 @@ var _ = Describe("Podman run entrypoint", func() { ) BeforeEach(func() { - Skip(v2fail) tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index d94c6c169..59215c7e5 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -634,7 +634,6 @@ USER mail` }) It("podman run --volumes-from flag with built-in volumes", func() { - Skip(v2fail) session := podmanTest.Podman([]string{"create", redis, "sh"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -729,7 +728,6 @@ USER mail` }) It("podman run --pod automatically", func() { - Skip(v2fail) session := podmanTest.Podman([]string{"run", "--pod", "new:foobar", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 5f6f5a8cf..6af0b7068 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -17,7 +17,6 @@ var _ = Describe("Podman start", func() { ) BeforeEach(func() { - Skip(v2fail) tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -66,10 +65,11 @@ var _ = Describe("Podman start", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) cid := session.OutputToString() - session = podmanTest.Podman([]string{"container", "start", cid[0:10]}) + shortID := cid[0:10] + session = podmanTest.Podman([]string{"container", "start", shortID}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal(cid)) + Expect(session.OutputToString()).To(Equal(shortID)) }) It("podman start single container by name", func() { |