diff options
-rw-r--r-- | cmd/podman/main_local.go | 1 | ||||
-rw-r--r-- | cmd/podmanV2/common/create.go | 1 | ||||
-rw-r--r-- | cmd/podmanV2/containers/run.go | 125 | ||||
-rw-r--r-- | cmd/podmanV2/system/info.go | 74 | ||||
-rw-r--r-- | pkg/bindings/system/info.go | 8 | ||||
-rw-r--r-- | pkg/cgroups/pids.go | 6 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 21 | ||||
-rw-r--r-- | pkg/domain/entities/engine_container.go | 3 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 74 | ||||
-rw-r--r-- | pkg/domain/infra/abi/system.go | 13 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/system.go | 11 | ||||
-rw-r--r-- | test/e2e/login_logout_test.go | 23 |
13 files changed, 344 insertions, 20 deletions
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index a65e6acf8..e71dbbf97 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -253,7 +253,6 @@ func executeCommandInUserNS(cmd *cobra.Command) bool { case _migrateCommand, _mountCommand, _renumberCommand, - _infoCommand, _searchCommand, _versionCommand: return false diff --git a/cmd/podmanV2/common/create.go b/cmd/podmanV2/common/create.go index f81d021c8..e2eb8cbda 100644 --- a/cmd/podmanV2/common/create.go +++ b/cmd/podmanV2/common/create.go @@ -29,7 +29,6 @@ func getDefaultContainerConfig() *config.Config { } func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { - //createFlags := c.Flags() createFlags := pflag.FlagSet{} createFlags.StringSliceVar( &cf.Annotation, diff --git a/cmd/podmanV2/containers/run.go b/cmd/podmanV2/containers/run.go new file mode 100644 index 000000000..bd90aee2f --- /dev/null +++ b/cmd/podmanV2/containers/run.go @@ -0,0 +1,125 @@ +package containers + +import ( + "fmt" + "os" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/containers/libpod/pkg/domain/entities" + + "github.com/containers/libpod/cmd/podmanV2/common" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/specgen" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + runDescription = "Runs a command in a new container from the given image" + runCommand = &cobra.Command{ + Use: "run [flags] IMAGE [COMMAND [ARG...]]", + Short: "Run a command in a new container", + Long: runDescription, + PreRunE: preRunE, + RunE: run, + Example: `podman run imageID ls -alF /etc + podman run --network=host imageID dnf -y install java + podman run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash`, + } +) + +var ( + runOpts = entities.ContainerRunOptions{ + OutputStream: os.Stdout, + InputStream: os.Stdin, + ErrorStream: os.Stderr, + } + runRmi bool +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode}, + Command: runCommand, + }) + flags := runCommand.Flags() + flags.AddFlagSet(common.GetCreateFlags(&cliVals)) + flags.AddFlagSet(common.GetNetFlags()) + flags.SetNormalizeFunc(common.AliasFlags) + flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process") + flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers") + if registry.IsRemote() { + _ = flags.MarkHidden("authfile") + } +} + +func run(cmd *cobra.Command, args []string) error { + var ( + err error + ) + cliVals.Net, err = common.NetFlagsToNetOptions(cmd) + if err != nil { + return err + } + if af := cliVals.Authfile; len(af) > 0 { + if _, err := os.Stat(af); err != nil { + return errors.Wrapf(err, "error checking authfile path %s", af) + } + } + runOpts.Rm = cliVals.Rm + if err := createInit(cmd); err != nil { + return err + } + + // If -i is not set, clear stdin + if !cliVals.Interactive { + runOpts.InputStream = nil + } + + // If attach is set, clear stdin/stdout/stderr and only attach requested + if cmd.Flag("attach").Changed { + runOpts.OutputStream = nil + runOpts.ErrorStream = nil + if !cliVals.Interactive { + runOpts.InputStream = nil + } + + for _, stream := range cliVals.Attach { + switch strings.ToLower(stream) { + case "stdout": + runOpts.OutputStream = os.Stdout + case "stderr": + runOpts.ErrorStream = os.Stderr + case "stdin": + runOpts.InputStream = os.Stdin + default: + return errors.Wrapf(define.ErrInvalidArg, "invalid stream %q for --attach - must be one of stdin, stdout, or stderr", stream) + } + } + } + runOpts.Detach = cliVals.Detach + runOpts.DetachKeys = cliVals.DetachKeys + s := specgen.NewSpecGenerator(args[0]) + if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { + return err + } + runOpts.Spec = s + report, err := registry.ContainerEngine().ContainerRun(registry.GetContext(), runOpts) + if err != nil { + return err + } + if cliVals.Detach { + fmt.Println(report.Id) + } + registry.SetExitCode(report.ExitCode) + if runRmi { + _, err := registry.ImageEngine().Delete(registry.GetContext(), []string{report.Id}, entities.ImageDeleteOptions{}) + if err != nil { + logrus.Errorf("%s", errors.Wrapf(err, "failed removing image")) + } + } + return nil +} diff --git a/cmd/podmanV2/system/info.go b/cmd/podmanV2/system/info.go new file mode 100644 index 000000000..69b2871b7 --- /dev/null +++ b/cmd/podmanV2/system/info.go @@ -0,0 +1,74 @@ +package system + +import ( + "encoding/json" + "fmt" + "os" + "text/template" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +var ( + infoDescription = `Display information pertaining to the host, current storage stats, and build of podman. + + Useful for the user and when reporting issues. +` + infoCommand = &cobra.Command{ + Use: "info", + Args: cobra.NoArgs, + Long: infoDescription, + Short: "Display podman system information", + PreRunE: preRunE, + RunE: info, + Example: `podman info`, + } +) + +var ( + inFormat string + debug bool +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: infoCommand, + }) + flags := infoCommand.Flags() + flags.BoolVarP(&debug, "debug", "D", false, "Display additional debug information") + flags.StringVarP(&inFormat, "format", "f", "", "Change the output format to JSON or a Go template") +} + +func info(cmd *cobra.Command, args []string) error { + info, err := registry.ContainerEngine().Info(registry.GetContext()) + if err != nil { + return err + } + + if inFormat == "json" { + b, err := json.MarshalIndent(info, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil + } + if !cmd.Flag("format").Changed { + b, err := yaml.Marshal(info) + if err != nil { + return err + } + fmt.Println(string(b)) + return nil + } + tmpl, err := template.New("info").Parse(inFormat) + if err != nil { + return err + } + err = tmpl.Execute(os.Stdout, info) + return err +} diff --git a/pkg/bindings/system/info.go b/pkg/bindings/system/info.go index f8269cfd8..13e12645d 100644 --- a/pkg/bindings/system/info.go +++ b/pkg/bindings/system/info.go @@ -9,15 +9,15 @@ import ( ) // Info returns information about the libpod environment and its stores -func Info(ctx context.Context) (define.Info, error) { +func Info(ctx context.Context) (*define.Info, error) { info := define.Info{} conn, err := bindings.GetClient(ctx) if err != nil { - return info, err + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil) if err != nil { - return info, err + return nil, err } - return info, response.Process(&info) + return &info, response.Process(&info) } diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go index 65b9b5b34..92f82553b 100644 --- a/pkg/cgroups/pids.go +++ b/pkg/cgroups/pids.go @@ -44,8 +44,12 @@ func (c *pidHandler) Destroy(ctr *CgroupControl) error { // Stat fills a metrics structure with usage stats for the controller func (c *pidHandler) Stat(ctr *CgroupControl, m *Metrics) error { - var PIDRoot string + if ctr.path != "" { + // nothing we can do to retrieve the pids.current path + return nil + } + var PIDRoot string if ctr.cgroup2 { PIDRoot = filepath.Join(cgroupRoot, ctr.path) } else { diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 4f94e009f..5d302058b 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -6,6 +6,7 @@ import ( "time" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/specgen" ) type WaitOptions struct { @@ -224,3 +225,23 @@ type ContainerListOptions struct { Sync bool Watch uint } + +// ContainerRunOptions describes the options needed +// to run a container from the CLI +type ContainerRunOptions struct { + Detach bool + DetachKeys string + ErrorStream *os.File + InputStream *os.File + OutputStream *os.File + Rm bool + SigProxy bool + Spec *specgen.SpecGenerator +} + +// ContainerRunReport describes the results of running +//a container +type ContainerRunReport struct { + ExitCode int + Id string +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 36c20c38d..0fc390ea2 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -23,6 +23,7 @@ type ContainerEngine interface { ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) + ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) @@ -47,4 +48,6 @@ type ContainerEngine interface { VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error) VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error) + + Info(ctx context.Context) (*define.Info, error) } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 54f91a189..828ee56f0 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -622,3 +622,77 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { return ps.GetContainerLists(ic.Libpod, options) } + +func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { + var ( + joinPod bool + ) + if err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec); err != nil { + return nil, err + } + ctr, err := generate.MakeContainer(ic.Libpod, opts.Spec) + if err != nil { + return nil, err + } + + if len(ctr.PodID()) > 0 { + joinPod = true + } + report := entities.ContainerRunReport{Id: ctr.ID()} + + if logrus.GetLevel() == logrus.DebugLevel { + cgroupPath, err := ctr.CGroupPath() + if err == nil { + logrus.Debugf("container %q has CgroupParent %q", ctr.ID(), cgroupPath) + } + } + if opts.Detach { + // if the container was created as part of a pod, also start its dependencies, if any. + if err := ctr.Start(ctx, joinPod); err != nil { + // This means the command did not exist + report.ExitCode = define.ExitCode(err) + return &report, err + } + + return &report, nil + } + + // if the container was created as part of a pod, also start its dependencies, if any. + if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true, joinPod); err != nil { + // We've manually detached from the container + // Do not perform cleanup, or wait for container exit code + // Just exit immediately + if errors.Cause(err) == define.ErrDetach { + report.ExitCode = 0 + return &report, nil + } + if opts.Rm { + if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false); deleteError != nil { + logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) + } + } + if errors.Cause(err) == define.ErrWillDeadlock { + logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err) + report.ExitCode = define.ExitCode(err) + return &report, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID()) + } + report.ExitCode = define.ExitCode(err) + return &report, err + } + + if ecode, err := ctr.Wait(); err != nil { + if errors.Cause(err) == define.ErrNoSuchCtr { + // Check events + event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited) + if err != nil { + logrus.Errorf("Cannot get exit code: %v", err) + report.ExitCode = define.ExecErrorCodeNotFound + } else { + report.ExitCode = event.ContainerExitCode + } + } + } else { + report.ExitCode = int(ecode) + } + return &report, nil +} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go new file mode 100644 index 000000000..8aaa69847 --- /dev/null +++ b/pkg/domain/infra/abi/system.go @@ -0,0 +1,13 @@ +// +build ABISupport + +package abi + +import ( + "context" + + "github.com/containers/libpod/libpod/define" +) + +func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { + return ic.Libpod.Info() +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index b6807d5b6..e96200c5b 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -320,3 +320,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Pod, &options.Size, &options.Sync) } + +func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { + return nil, errors.New("not implemented") +} diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 5bafef1fe..5d6346234 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -1 +1,12 @@ package tunnel + +import ( + "context" + + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/bindings/system" +) + +func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { + return system.Info(ic.ClientCxt) +} diff --git a/test/e2e/login_logout_test.go b/test/e2e/login_logout_test.go index 42698d270..3f76daa67 100644 --- a/test/e2e/login_logout_test.go +++ b/test/e2e/login_logout_test.go @@ -24,6 +24,7 @@ var _ = Describe("Podman login and logout", func() { podmanTest *PodmanTestIntegration authPath string certPath string + certDirPath string port int server string testImg string @@ -70,12 +71,12 @@ var _ = Describe("Podman login and logout", func() { testImg = strings.Join([]string{server, "test-apline"}, "/") - os.MkdirAll(filepath.Join("/etc/containers/certs.d", server), os.ModePerm) - + certDirPath = filepath.Join(os.Getenv("HOME"), ".config/containers/certs.d", server) + os.MkdirAll(certDirPath, os.ModePerm) cwd, _ := os.Getwd() certPath = filepath.Join(cwd, "../", "certs") - setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join("/etc/containers/certs.d", server, "ca.crt")}) + setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join(certDirPath, "ca.crt")}) setup.WaitWithDefaultTimeout() session = podmanTest.Podman([]string{"run", "-d", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), @@ -95,11 +96,10 @@ var _ = Describe("Podman login and logout", func() { AfterEach(func() { podmanTest.Cleanup() os.RemoveAll(authPath) - os.RemoveAll(filepath.Join("/etc/containers/certs.d", server)) + os.RemoveAll(certDirPath) }) It("podman login and logout", func() { - SkipIfRootless() session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test", server}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -150,7 +150,6 @@ var _ = Describe("Podman login and logout", func() { }) It("podman login and logout with flag --authfile", func() { - SkipIfRootless() authFile := filepath.Join(podmanTest.TempDir, "auth.json") session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--authfile", authFile, server}) session.WaitWithDefaultTimeout() @@ -183,7 +182,6 @@ var _ = Describe("Podman login and logout", func() { }) It("podman login and logout with --tls-verify", func() { - SkipIfRootless() session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--tls-verify=false", server}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -197,7 +195,6 @@ var _ = Describe("Podman login and logout", func() { Expect(session.ExitCode()).To(Equal(0)) }) It("podman login and logout with --cert-dir", func() { - SkipIfRootless() certDir := filepath.Join(podmanTest.TempDir, "certs") os.MkdirAll(certDir, os.ModePerm) @@ -208,7 +205,7 @@ var _ = Describe("Podman login and logout", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"push", ALPINE, testImg}) + session = podmanTest.Podman([]string{"push", "--cert-dir", certDir, ALPINE, testImg}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -217,15 +214,15 @@ var _ = Describe("Podman login and logout", func() { Expect(session.ExitCode()).To(Equal(0)) }) It("podman login and logout with multi registry", func() { - SkipIfRootless() - os.MkdirAll("/etc/containers/certs.d/localhost:9001", os.ModePerm) + certDir := filepath.Join(os.Getenv("HOME"), ".config/containers/certs.d", "localhost:9001") + os.MkdirAll(certDir, os.ModePerm) cwd, _ := os.Getwd() certPath = filepath.Join(cwd, "../", "certs") - setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), "/etc/containers/certs.d/localhost:9001/ca.crt"}) + setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join(certDir, "ca.crt")}) setup.WaitWithDefaultTimeout() - defer os.RemoveAll("/etc/containers/certs.d/localhost:9001") + defer os.RemoveAll(certDir) session := podmanTest.Podman([]string{"run", "-d", "-p", "9001:9001", "-e", "REGISTRY_HTTP_ADDR=0.0.0.0:9001", "--name", "registry1", "-v", strings.Join([]string{authPath, "/auth"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", |