diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/adapter/client.go | 35 | ||||
-rw-r--r-- | pkg/adapter/client_config.go | 4 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 5 | ||||
-rw-r--r-- | pkg/adapter/containers_remote.go | 35 | ||||
-rw-r--r-- | pkg/adapter/errors.go | 31 | ||||
-rw-r--r-- | pkg/adapter/info_remote.go | 7 | ||||
-rw-r--r-- | pkg/adapter/pods_remote.go | 6 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 13 | ||||
-rw-r--r-- | pkg/systemdgen/systemdgen_test.go | 120 | ||||
-rw-r--r-- | pkg/varlinkapi/attach.go | 1 | ||||
-rw-r--r-- | pkg/varlinkapi/containers.go | 17 | ||||
-rw-r--r-- | pkg/varlinkapi/pods.go | 6 | ||||
-rw-r--r-- | pkg/varlinkapi/system.go | 17 |
13 files changed, 255 insertions, 42 deletions
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go index 6512a5952..f672a92a6 100644 --- a/pkg/adapter/client.go +++ b/pkg/adapter/client.go @@ -3,30 +3,45 @@ package adapter import ( + "fmt" "os" - "github.com/sirupsen/logrus" + "github.com/pkg/errors" "github.com/varlink/go/varlink" ) -// DefaultAddress is the default address of the varlink socket -const DefaultAddress = "unix:/run/podman/io.podman" +type VarlinkConnectionInfo struct { + RemoteUserName string + RemoteHost string + VarlinkAddress string +} // Connect provides a varlink connection func (r RemoteRuntime) Connect() (*varlink.Connection, error) { - var err error - var connection *varlink.Connection - if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" { - logrus.Infof("Connecting with varlink bridge") - logrus.Debugf("%s", bridge) + var ( + err error + connection *varlink.Connection + ) + + logLevel := r.cmd.LogLevel + + // I'm leaving this here for now as a document of the birdge format. It can be removed later once the bridge + // function is more flushed out. + //bridge := `ssh -T root@192.168.122.1 "/usr/bin/varlink -A '/usr/bin/podman varlink \$VARLINK_ADDRESS' bridge"` + if len(r.cmd.RemoteHost) > 0 { + // The user has provided a remote host endpoint + if len(r.cmd.RemoteUserName) < 1 { + return nil, errors.New("you must provide a username when providing a remote host name") + } + bridge := fmt.Sprintf(`ssh -T %s@%s /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, r.cmd.RemoteUserName, r.cmd.RemoteHost, logLevel) + connection, err = varlink.NewBridge(bridge) + } else if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" { connection, err = varlink.NewBridge(bridge) } else { address := os.Getenv("PODMAN_VARLINK_ADDRESS") if address == "" { address = DefaultAddress } - logrus.Infof("Connecting with varlink address") - logrus.Debugf("%s", address) connection, err = varlink.NewConnection(address) } if err != nil { diff --git a/pkg/adapter/client_config.go b/pkg/adapter/client_config.go new file mode 100644 index 000000000..d165ef1cc --- /dev/null +++ b/pkg/adapter/client_config.go @@ -0,0 +1,4 @@ +package adapter + +// DefaultAddress is the default address of the varlink socket +const DefaultAddress = "unix:/run/podman/io.podman" diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index f17050d42..ff7b6377a 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -1025,3 +1025,8 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri } return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, ctr.Config().StaticDir, timeout) } + +// GetNamespaces returns namespace information about a container for PS +func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace { + return shared.GetNamespaces(container.Pid) +} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 201249fc3..63b0f9d2f 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -234,15 +234,25 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) if err != nil { - return ok, failures, err + return ok, failures, TranslateError(err) } for _, id := range ids { - stopped, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)) - if err != nil { + if _, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)); err != nil { + transError := TranslateError(err) + if errors.Cause(transError) == libpod.ErrCtrStopped { + ok = append(ok, id) + continue + } + if errors.Cause(transError) == libpod.ErrCtrStateInvalid && cli.All { + ok = append(ok, id) + continue + } failures[id] = err } else { - ok = append(ok, stopped) + // We should be using ID here because in varlink, only successful returns + // include the string id + ok = append(ok, id) } } return ok, failures, nil @@ -310,7 +320,7 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) { ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) if err != nil { - return nil, nil, err + return nil, nil, TranslateError(err) } var ( @@ -961,3 +971,18 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { return iopodman.GenerateSystemd().Call(r.Conn, c.InputArgs[0], c.RestartPolicy, int64(c.StopTimeout), c.Name) } + +// GetNamespaces returns namespace information about a container for PS +func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace { + ns := shared.Namespace{ + PID: container.PID, + Cgroup: container.Cgroup, + IPC: container.IPC, + MNT: container.MNT, + NET: container.NET, + PIDNS: container.PIDNS, + User: container.User, + UTS: container.UTS, + } + return &ns +} diff --git a/pkg/adapter/errors.go b/pkg/adapter/errors.go new file mode 100644 index 000000000..7fbbabd93 --- /dev/null +++ b/pkg/adapter/errors.go @@ -0,0 +1,31 @@ +// +build remoteclient + +package adapter + +import ( + iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/libpod" + "github.com/pkg/errors" +) + +// TranslateMapErrors translates the errors a typical podman output struct +// from varlink errors to libpod errors +func TranslateMapErrors(failures map[string]error) map[string]error { + for k, v := range failures { + failures[k] = TranslateError(v) + } + return failures +} + +// TranslateError converts a single varlink error to a libpod error +func TranslateError(err error) error { + switch err.(type) { + case *iopodman.ContainerNotFound: + return errors.Wrap(libpod.ErrNoSuchCtr, err.Error()) + case *iopodman.ErrCtrStopped: + return errors.Wrap(libpod.ErrCtrStopped, err.Error()) + case *iopodman.InvalidState: + return errors.Wrap(libpod.ErrCtrStateInvalid, err.Error()) + } + return err +} diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go index 3b691ed17..3b2d02a5a 100644 --- a/pkg/adapter/info_remote.go +++ b/pkg/adapter/info_remote.go @@ -20,12 +20,7 @@ func (r RemoteRuntime) Info() ([]libpod.InfoData, error) { registries := make(map[string]interface{}) insecureRegistries := make(map[string]interface{}) - conn, err := r.Connect() - if err != nil { - return nil, err - } - defer conn.Close() - info, err := iopodman.GetInfo().Call(conn) + info, err := iopodman.GetInfo().Call(r.Conn) if err != nil { return nil, err } diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index 7cf38aac0..e2c97c36a 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -172,11 +172,15 @@ func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartVal // CreatePod creates a pod for the remote client over a varlink connection func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) { + var share []string + if cli.Share != "" { + share = strings.Split(cli.Share, ",") + } pc := iopodman.PodCreate{ Name: cli.Name, CgroupParent: cli.CgroupParent, Labels: labels, - Share: strings.Split(cli.Share, ","), + Share: share, Infra: cli.Infra, InfraCommand: cli.InfraCommand, InfraImage: cli.InfraCommand, diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 4986d16f7..8803a26fb 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -38,6 +38,7 @@ type RemoteImageRuntime struct{} type RemoteRuntime struct { Conn *varlink.Connection Remote bool + cmd cliconfig.MainFlags } // LocalRuntime describes a typical libpod runtime @@ -47,17 +48,17 @@ type LocalRuntime struct { // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { - runtime := RemoteRuntime{} + runtime := RemoteRuntime{ + Remote: true, + cmd: c.GlobalFlags, + } conn, err := runtime.Connect() if err != nil { return nil, err } - + runtime.Conn = conn return &LocalRuntime{ - &RemoteRuntime{ - Conn: conn, - Remote: true, - }, + &runtime, }, nil } diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go new file mode 100644 index 000000000..f2f49e750 --- /dev/null +++ b/pkg/systemdgen/systemdgen_test.go @@ -0,0 +1,120 @@ +package systemdgen + +import ( + "testing" +) + +func TestValidateRestartPolicy(t *testing.T) { + type args struct { + restart string + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"good-on", args{restart: "no"}, false}, + {"good-on-success", args{restart: "on-success"}, false}, + {"good-on-failure", args{restart: "on-failure"}, false}, + {"good-on-abnormal", args{restart: "on-abnormal"}, false}, + {"good-on-watchdog", args{restart: "on-watchdog"}, false}, + {"good-on-abort", args{restart: "on-abort"}, false}, + {"good-always", args{restart: "always"}, false}, + {"fail", args{restart: "foobar"}, true}, + {"failblank", args{restart: ""}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ValidateRestartPolicy(tt.args.restart); (err != nil) != tt.wantErr { + t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCreateSystemdUnitAsString(t *testing.T) { + goodID := `[Unit] +Description=639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 Podman Container +[Service] +Restart=always +ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +KillMode=none +Type=forking +PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +[Install] +WantedBy=multi-user.target` + + goodName := `[Unit] +Description=foobar Podman Container +[Service] +Restart=always +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +KillMode=none +Type=forking +PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +[Install] +WantedBy=multi-user.target` + + type args struct { + name string + cid string + restart string + pidPath string + stopTimeout int + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + + {"good with id", + args{ + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "always", + "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + 10, + }, + goodID, + false, + }, + {"good with name", + args{ + "foobar", + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "always", + "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + 10, + }, + goodName, + false, + }, + {"bad restart policy", + args{ + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", + "never", + "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + 10, + }, + "", + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CreateSystemdUnitAsString(tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidPath, tt.args.stopTimeout) + if (err != nil) != tt.wantErr { + t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("CreateSystemdUnitAsString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 6c62d3514..2234899a5 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -60,6 +60,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st if !start && state != libpod.ContainerStateRunning { return call.ReplyErrorOccurred("container must be running to attach") } + call.Reply(nil) reader, writer, _, pw, streams := setupStreams(call) go func() { diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index c8be41636..8611a1a7d 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -119,6 +119,9 @@ func (i *LibpodAPI) GetContainersByContext(call iopodman.VarlinkCall, all, lates ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime) if err != nil { + if errors.Cause(err) == libpod.ErrNoSuchCtr { + return call.ReplyContainerNotFound("", err.Error()) + } return call.ReplyErrorOccurred(err.Error()) } @@ -359,7 +362,11 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused { return call.ReplyErrorOccurred("container is already running or paused") } - if err := ctr.Start(getContext(), false); err != nil { + recursive := false + if ctr.PodID() != "" { + recursive = true + } + if err := ctr.Start(getContext(), recursive); err != nil { return call.ReplyErrorOccurred(err.Error()) } return call.ReplyStartContainer(ctr.ID()) @@ -386,7 +393,13 @@ func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeou if err != nil { return call.ReplyContainerNotFound(name, err.Error()) } - if err := ctr.StopWithTimeout(uint(timeout)); err != nil && err != libpod.ErrCtrStopped { + if err := ctr.StopWithTimeout(uint(timeout)); err != nil { + if errors.Cause(err) == libpod.ErrCtrStopped { + return call.ReplyErrCtrStopped(ctr.ID()) + } + if errors.Cause(err) == libpod.ErrCtrStateInvalid { + return call.ReplyInvalidState(ctr.ID(), err.Error()) + } return call.ReplyErrorOccurred(err.Error()) } return call.ReplyStopContainer(ctr.ID()) diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go index f34375bf5..c0fd8b1f7 100644 --- a/pkg/varlinkapi/pods.go +++ b/pkg/varlinkapi/pods.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "github.com/containers/libpod/pkg/adapter/shortcuts" - "github.com/containers/libpod/pkg/rootless" "syscall" "github.com/containers/libpod/cmd/podman/shared" @@ -37,12 +36,9 @@ func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCrea if !create.Infra { return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host") } - if rootless.IsRootless() { - return call.ReplyErrorOccurred("rootless networking does not allow port binding to the host") - } portBindings, err := shared.CreatePortBindings(create.Publish) if err != nil { - return err + return call.ReplyErrorOccurred(err.Error()) } options = append(options, libpod.WithInfraContainerPorts(portBindings)) diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 7f436a954..59bfec75b 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -86,15 +86,18 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { Graph_status: graphStatus, } - registriesInterface := info[2].Data["registries"] - insecureRegistriesInterface := info[3].Data["registries"] - if registriesInterface != nil { - registries = registriesInterface.([]string) + if len(info) > 2 { + registriesInterface := info[2].Data["registries"] + if registriesInterface != nil { + registries = registriesInterface.([]string) + } } - if insecureRegistriesInterface != nil { - insecureRegistries = insecureRegistriesInterface.([]string) + if len(info) > 3 { + insecureRegistriesInterface := info[3].Data["registries"] + if insecureRegistriesInterface != nil { + insecureRegistries = insecureRegistriesInterface.([]string) + } } - podmanInfo.Store = infoStore podmanInfo.Podman = pmaninfo podmanInfo.Registries = registries |