diff options
Diffstat (limited to 'pkg')
32 files changed, 741 insertions, 145 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 5751a9c3a..0ea89a72c 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -20,9 +20,11 @@ import ( "github.com/containers/image/manifest" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/cmd/podman/shared/parse" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/adapter/shortcuts" "github.com/containers/libpod/pkg/systemdgen" "github.com/containers/psgo" @@ -242,7 +244,7 @@ func (r *LocalRuntime) UmountRootFilesystems(ctx context.Context, cli *cliconfig logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error()) continue } - if state == libpod.ContainerStateRunning { + if state == define.ContainerStateRunning { logrus.Debugf("Error umounting container %s, is running", ctr.ID()) continue } @@ -283,13 +285,14 @@ func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.Wait } // Log logs one or more containers -func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error { +func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *logs.LogOptions) error { + var wg sync.WaitGroup options.WaitGroup = &wg if len(c.InputArgs) > 1 { options.Multi = true } - logChannel := make(chan *libpod.LogLine, int(c.Tail)*len(c.InputArgs)+1) + logChannel := make(chan *logs.LogLine, int(c.Tail)*len(c.InputArgs)+1) containers, err := shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime) if err != nil { return err @@ -381,8 +384,18 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode } } } + + config, err := r.Runtime.GetConfig() + if err != nil { + return exitCode, err + } + detachKeys := c.String("detach-keys") + if detachKeys == "" { + detachKeys = config.DetachKeys + } + // if the container was created as part of a pod, also start its dependencies, if any. - if err := StartAttachCtr(ctx, ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), true, c.IsSet("pod")); err != nil { + if err := StartAttachCtr(ctx, ctr, outputStream, errorStream, inputStream, detachKeys, c.Bool("sig-proxy"), true, c.IsSet("pod")); err != nil { // We've manually detached from the container // Do not perform cleanup, or wait for container exit code // Just exit immediately @@ -407,10 +420,6 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode if errors.Cause(err) == define.ErrNoSuchCtr { // The container may have been removed // Go looking for an exit file - config, err := r.Runtime.GetConfig() - if err != nil { - return exitCode, err - } ctrExitCode, err := ReadExitFile(config.TmpDir, ctr.ID()) if err != nil { logrus.Errorf("Cannot get exit code: %v", err) @@ -488,7 +497,7 @@ func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) er if err != nil { return errors.Wrapf(err, "unable to determine state of %s", ctr.ID()) } - if conState != libpod.ContainerStateRunning { + if conState != define.ContainerStateRunning { return errors.Errorf("you can only attach to running containers") } @@ -539,16 +548,23 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error { } // Restore one or more containers -func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error { +func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues) error { var ( containers []*libpod.Container err, lastError error filterFuncs []libpod.ContainerFilter ) + options := libpod.ContainerCheckpointOptions{ + Keep: c.Keep, + TCPEstablished: c.TcpEstablished, + TargetFile: c.Import, + Name: c.Name, + } + filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { state, _ := c.State() - return state == libpod.ContainerStateExited + return state == define.ContainerStateExited }) if c.Import != "" { @@ -606,7 +622,7 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP return exitCode, errors.Wrapf(err, "unable to get container state") } - ctrRunning := ctrState == libpod.ContainerStateRunning + ctrRunning := ctrState == define.ContainerStateRunning if c.Attach { inputStream := os.Stdin @@ -732,7 +748,7 @@ func (r *LocalRuntime) UnpauseContainers(ctx context.Context, cli *cliconfig.Unp var filterFuncs []libpod.ContainerFilter filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { state, _ := c.State() - return state == libpod.ContainerStatePaused + return state == define.ContainerStatePaused }) ctrs, err = r.GetContainers(filterFuncs...) } else { @@ -929,7 +945,7 @@ func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([ if c.PodID() != "" { return false } - if state == libpod.ContainerStateStopped || state == libpod.ContainerStateExited { + if state == define.ContainerStateStopped || state == define.ContainerStateExited { return true } return false @@ -1020,7 +1036,7 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { //Convert libpod containers to adapter Containers for _, con := range containers { - if state, _ := con.State(); state != libpod.ContainerStateRunning { + if state, _ := con.State(); state != define.ContainerStateRunning { continue } portContainers = append(portContainers, &Container{con}) @@ -1042,7 +1058,14 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri if c.Name { name = ctr.Name() } - return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, ctr.Config().StaticDir, timeout) + + config := ctr.Config() + conmonPidFile := config.ConmonPidFile + if conmonPidFile == "" { + return "", errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, conmonPidFile, timeout) } // GetNamespaces returns namespace information about a container for PS @@ -1101,3 +1124,61 @@ func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, co } return newImage.ID(), nil } + +// Exec a command in a container +func (r *LocalRuntime) Exec(c *cliconfig.ExecValues, cmd []string) error { + var ctr *Container + var err error + + if c.Latest { + ctr, err = r.GetLatestContainer() + } else { + ctr, err = r.LookupContainer(c.InputArgs[0]) + } + if err != nil { + return errors.Wrapf(err, "unable to exec into %s", c.InputArgs[0]) + } + + if c.PreserveFDs > 0 { + entries, err := ioutil.ReadDir("/proc/self/fd") + if err != nil { + 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 { + if err != nil { + return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) + } + } + m[i] = true + } + for i := 3; i < 3+c.PreserveFDs; i++ { + if _, found := m[i]; !found { + return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") + } + } + } + + // ENVIRONMENT VARIABLES + env := map[string]string{} + + if err := parse.ReadKVStrings(env, []string{}, c.Env); err != nil { + return errors.Wrapf(err, "unable to process environment variables") + } + envs := []string{} + for k, v := range env { + envs = append(envs, fmt.Sprintf("%s=%s", k, v)) + } + + streams := new(libpod.AttachStreams) + streams.OutputStream = os.Stdout + streams.ErrorStream = os.Stderr + streams.InputStream = os.Stdin + streams.AttachOutput = true + streams.AttachError = true + streams.AttachInput = true + + return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams, c.PreserveFDs) +} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index c52dc1d7a..5836d0788 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -17,6 +17,7 @@ import ( iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/varlinkapi/virtwriter" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/docker/pkg/term" @@ -411,8 +412,8 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai return bcs, nil } -// Logs one or more containers over a varlink connection -func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) error { +// Log one or more containers over a varlink connection +func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *logs.LogOptions) error { // GetContainersLogs reply, err := iopodman.GetContainersLogs().Send(r.Conn, uint64(varlink.More), c.InputArgs, c.Follow, c.Latest, options.Since.Format(time.RFC3339Nano), int64(c.Tail), c.Timestamps) if err != nil { @@ -434,7 +435,7 @@ func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *libpod.LogOptions) if err != nil { return errors.Wrapf(err, "unable to parse time of log %s", log.Time) } - logLine := libpod.LogLine{ + logLine := logs.LogLine{ Device: log.Device, ParseLogType: log.ParseLogType, Time: lTime, @@ -516,7 +517,7 @@ func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]share RootFsSize: ctr.RootFsSize, RwSize: ctr.RwSize, } - state, err := libpod.StringToContainerStatus(ctr.State) + state, err := define.StringToContainerStatus(ctr.State) if err != nil { return nil, err } @@ -645,7 +646,7 @@ func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) er if err != nil { return nil } - if ctr.state.State != libpod.ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { return errors.New("you can only attach to running containers") } inputStream := os.Stdin @@ -682,7 +683,7 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error { if err != nil { return err } - if ctr.state.State == libpod.ContainerStateRunning { + if ctr.state.State == define.ContainerStateRunning { runningIds = append(runningIds, id) } } @@ -703,7 +704,7 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error { } // Restore one or more containers -func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error { +func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues) error { if c.Import != "" { return errors.New("the remote client does not support importing checkpoints") } @@ -722,7 +723,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues, if err != nil { return err } - if ctr.state.State != libpod.ContainerStateRunning { + if ctr.state.State != define.ContainerStateRunning { exitedIDs = append(exitedIDs, id) } } @@ -730,7 +731,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues, } for _, id := range ids { - if _, err := iopodman.ContainerRestore().Call(r.Conn, id, options.Keep, options.TCPEstablished); err != nil { + if _, err := iopodman.ContainerRestore().Call(r.Conn, id, c.Keep, c.TcpEstablished); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } @@ -797,7 +798,7 @@ func (r *LocalRuntime) PauseContainers(ctx context.Context, cli *cliconfig.Pause ) if cli.All { - filters := []string{libpod.ContainerStateRunning.String()} + filters := []string{define.ContainerStateRunning.String()} ctrs, err = r.LookupContainersWithStatus(filters) } else { ctrs, err = r.LookupContainers(cli.InputArgs) @@ -834,7 +835,7 @@ func (r *LocalRuntime) UnpauseContainers(ctx context.Context, cli *cliconfig.Unp logrus.Debugf("Setting maximum rm workers to %d", maxWorkers) if cli.All { - filters := []string{libpod.ContainerStatePaused.String()} + filters := []string{define.ContainerStatePaused.String()} ctrs, err = r.LookupContainersWithStatus(filters) } else { ctrs, err = r.LookupContainers(cli.InputArgs) @@ -873,7 +874,7 @@ func (r *LocalRuntime) Restart(ctx context.Context, c *cliconfig.RestartValues) } restartContainers = append(restartContainers, lastCtr) } else if c.Running { - containers, err = r.LookupContainersWithStatus([]string{libpod.ContainerStateRunning.String()}) + containers, err = r.LookupContainersWithStatus([]string{define.ContainerStateRunning.String()}) if err != nil { return nil, nil, err } @@ -941,7 +942,7 @@ func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([ ) logrus.Debugf("Setting maximum rm workers to %d", maxWorkers) - filters := []string{libpod.ContainerStateExited.String()} + filters := []string{define.ContainerStateExited.String()} ctrs, err = r.LookupContainersWithStatus(filters) if err != nil { return ok, failures, err @@ -974,7 +975,7 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) { containers, err = r.GetContainersByContext(false, c.Latest, c.InputArgs) } else { // we need to only use running containers if all - filters := []string{libpod.ContainerStateRunning.String()} + filters := []string{define.ContainerStateRunning.String()} containers, err = r.LookupContainersWithStatus(filters) } if err != nil { @@ -1025,3 +1026,8 @@ func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, co } return iid, nil } + +// Exec executes a container in a running container +func (r *LocalRuntime) Exec(c *cliconfig.ExecValues, cmd []string) error { + return define.ErrNotImplemented +} diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go index 3b2d02a5a..3170e5b3d 100644 --- a/pkg/adapter/info_remote.go +++ b/pkg/adapter/info_remote.go @@ -4,16 +4,16 @@ package adapter import ( "encoding/json" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/cmd/podman/varlink" - "github.com/containers/libpod/libpod" ) // Info returns information for the host system and its components -func (r RemoteRuntime) Info() ([]libpod.InfoData, error) { +func (r RemoteRuntime) Info() ([]define.InfoData, error) { // TODO the varlink implementation for info should be updated to match the output for regular info var ( - reply []libpod.InfoData + reply []define.InfoData hostInfo map[string]interface{} store map[string]interface{} ) @@ -43,9 +43,9 @@ func (r RemoteRuntime) Info() ([]libpod.InfoData, error) { insecureRegistries["registries"] = info.Insecure_registries // Add everything to the reply - reply = append(reply, libpod.InfoData{Type: "host", Data: hostInfo}) - reply = append(reply, libpod.InfoData{Type: "registries", Data: registries}) - reply = append(reply, libpod.InfoData{Type: "insecure registries", Data: insecureRegistries}) - reply = append(reply, libpod.InfoData{Type: "store", Data: store}) + reply = append(reply, define.InfoData{Type: "host", Data: hostInfo}) + reply = append(reply, define.InfoData{Type: "registries", Data: registries}) + reply = append(reply, define.InfoData{Type: "insecure registries", Data: insecureRegistries}) + reply = append(reply, define.InfoData{Type: "store", Data: store}) return reply, nil } diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index bb7d9cce6..a28e1ab4b 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -4,14 +4,33 @@ package adapter import ( "context" + "fmt" + "io" + "io/ioutil" + "os" "strings" + "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter/shortcuts" + ns "github.com/containers/libpod/pkg/namespaces" + createconfig "github.com/containers/libpod/pkg/spec" + "github.com/containers/storage" + "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" +) + +const ( + // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath + createDirectoryPermission = 0755 + // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath + createFilePermission = 0644 ) // PodContainerStats is struct containing an adapter Pod and a libpod @@ -420,3 +439,286 @@ func (r *LocalRuntime) GetStatPods(c *cliconfig.PodStatsValues) ([]*Pod, error) } return adapterPods, nil } + +// PlayKubeYAML creates pods and containers from a kube YAML file +func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayValues, yamlFile string) (*Pod, error) { + var ( + containers []*libpod.Container + pod *libpod.Pod + podOptions []libpod.PodCreateOption + podYAML v1.Pod + registryCreds *types.DockerAuthConfig + writer io.Writer + ) + + content, err := ioutil.ReadFile(yamlFile) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(content, &podYAML); err != nil { + return nil, errors.Wrapf(err, "unable to read %s as YAML", yamlFile) + } + + // check for name collision between pod and container + podName := podYAML.ObjectMeta.Name + for _, n := range podYAML.Spec.Containers { + if n.Name == podName { + fmt.Printf("a container exists with the same name (%s) as the pod in your YAML file; changing pod name to %s_pod\n", podName, podName) + podName = fmt.Sprintf("%s_pod", podName) + } + } + + podOptions = append(podOptions, libpod.WithInfraContainer()) + podOptions = append(podOptions, libpod.WithPodName(podName)) + // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml + + nsOptions, err := shared.GetNamespaceOptions(strings.Split(shared.DefaultKernelNamespaces, ",")) + if err != nil { + return nil, err + } + podOptions = append(podOptions, nsOptions...) + podPorts := getPodPorts(podYAML.Spec.Containers) + podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts)) + + // Create the Pod + pod, err = r.NewPod(ctx, podOptions...) + if err != nil { + return nil, err + } + + podInfraID, err := pod.InfraContainerID() + if err != nil { + return nil, err + } + + namespaces := map[string]string{ + // Disabled during code review per mheon + //"pid": fmt.Sprintf("container:%s", podInfraID), + "net": fmt.Sprintf("container:%s", podInfraID), + "user": fmt.Sprintf("container:%s", podInfraID), + "ipc": fmt.Sprintf("container:%s", podInfraID), + "uts": fmt.Sprintf("container:%s", podInfraID), + } + if !c.Quiet { + writer = os.Stderr + } + + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerRegistryCreds: registryCreds, + DockerCertPath: c.CertDir, + } + if c.Flag("tls-verify").Changed { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) + } + + // map from name to mount point + volumes := make(map[string]string) + for _, volume := range podYAML.Spec.Volumes { + hostPath := volume.VolumeSource.HostPath + if hostPath == nil { + return nil, errors.Errorf("HostPath is currently the only supported VolumeSource") + } + if hostPath.Type != nil { + switch *hostPath.Type { + case v1.HostPathDirectoryOrCreate: + if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { + if err := os.Mkdir(hostPath.Path, createDirectoryPermission); err != nil { + return nil, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path) + } + } + // unconditionally label a newly created volume as private + if err := libpod.LabelVolumePath(hostPath.Path, false); err != nil { + return nil, errors.Wrapf(err, "Error giving %s a label", hostPath.Path) + } + break + case v1.HostPathFileOrCreate: + if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { + f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, createFilePermission) + if err != nil { + return nil, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path) + } + if err := f.Close(); err != nil { + logrus.Warnf("Error in closing newly created HostPath file: %v", err) + } + } + // unconditionally label a newly created volume as private + if err := libpod.LabelVolumePath(hostPath.Path, false); err != nil { + return nil, errors.Wrapf(err, "Error giving %s a label", hostPath.Path) + } + break + case v1.HostPathDirectory: + case v1.HostPathFile: + case v1.HostPathUnset: + // do nothing here because we will verify the path exists in validateVolumeHostDir + break + default: + return nil, errors.Errorf("Directories are the only supported HostPath type") + } + } + + if err := createconfig.ValidateVolumeHostDir(hostPath.Path); err != nil { + return nil, errors.Wrapf(err, "Error in parsing HostPath in YAML") + } + volumes[volume.Name] = hostPath.Path + } + + for _, container := range podYAML.Spec.Containers { + newImage, err := r.ImageRuntime().New(ctx, container.Image, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, false, nil) + if err != nil { + return nil, err + } + createConfig, err := kubeContainerToCreateConfig(ctx, container, r.Runtime, newImage, namespaces, volumes, pod.ID()) + if err != nil { + return nil, err + } + ctr, err := shared.CreateContainerFromCreateConfig(r.Runtime, createConfig, ctx, pod) + if err != nil { + return nil, err + } + containers = append(containers, ctr) + } + + // start the containers + for _, ctr := range containers { + if err := ctr.Start(ctx, true); err != nil { + // Making this a hard failure here to avoid a mess + // the other containers are in created status + return nil, err + } + } + + // We've now successfully converted this YAML into a pod + // print our pod and containers, signifying we succeeded + fmt.Printf("Pod:\n%s\n", pod.ID()) + if len(containers) == 1 { + fmt.Printf("Container:\n") + } + if len(containers) > 1 { + fmt.Printf("Containers:\n") + } + for _, ctr := range containers { + fmt.Println(ctr.ID()) + } + + if err := playcleanup(ctx, r, pod, nil); err != nil { + logrus.Errorf("unable to remove pod %s after failing to play kube", pod.ID()) + } + return nil, nil +} + +func playcleanup(ctx context.Context, runtime *LocalRuntime, pod *libpod.Pod, err error) error { + if err != nil && pod != nil { + return runtime.RemovePod(ctx, pod, true, true) + } + return nil +} + +// getPodPorts converts a slice of kube container descriptions to an +// array of ocicni portmapping descriptions usable in libpod +func getPodPorts(containers []v1.Container) []ocicni.PortMapping { + var infraPorts []ocicni.PortMapping + for _, container := range containers { + for _, p := range container.Ports { + portBinding := ocicni.PortMapping{ + HostPort: p.HostPort, + ContainerPort: p.ContainerPort, + Protocol: strings.ToLower(string(p.Protocol)), + } + if p.HostIP != "" { + logrus.Debug("HostIP on port bindings is not supported") + } + infraPorts = append(infraPorts, portBinding) + } + } + return infraPorts +} + +// 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 string) (*createconfig.CreateConfig, error) { + var ( + containerConfig createconfig.CreateConfig + ) + + // The default for MemorySwappiness is -1, not 0 + containerConfig.Resources.MemorySwappiness = -1 + + containerConfig.Image = containerYAML.Image + containerConfig.ImageID = newImage.ID() + containerConfig.Name = containerYAML.Name + containerConfig.Tty = containerYAML.TTY + containerConfig.WorkDir = containerYAML.WorkingDir + + containerConfig.Pod = podID + + imageData, _ := newImage.Inspect(ctx) + + containerConfig.User = "0" + if imageData != nil { + containerConfig.User = imageData.Config.User + } + + if containerConfig.SecurityOpts != nil { + if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil { + containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem + } + if containerYAML.SecurityContext.Privileged != nil { + containerConfig.Privileged = *containerYAML.SecurityContext.Privileged + } + + if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil { + containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation + } + } + + containerConfig.Command = []string{} + if imageData != nil && imageData.Config != nil { + containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...) + } + if len(containerConfig.Command) != 0 { + containerConfig.Command = append(containerConfig.Command, containerYAML.Command...) + } else if imageData != nil && imageData.Config != nil { + containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...) + } + if imageData != nil && len(containerConfig.Command) == 0 { + return nil, errors.Errorf("No command specified in container YAML or as CMD or ENTRYPOINT in this image for %s", containerConfig.Name) + } + + containerConfig.StopSignal = 15 + + // If the user does not pass in ID mappings, just set to basics + if containerConfig.IDMappings == nil { + containerConfig.IDMappings = &storage.IDMappingOptions{} + } + + containerConfig.NetMode = ns.NetworkMode(namespaces["net"]) + containerConfig.IpcMode = ns.IpcMode(namespaces["ipc"]) + containerConfig.UtsMode = ns.UTSMode(namespaces["uts"]) + // disabled in code review per mheon + //containerConfig.PidMode = ns.PidMode(namespaces["pid"]) + containerConfig.UsernsMode = ns.UsernsMode(namespaces["user"]) + if len(containerConfig.WorkDir) == 0 { + containerConfig.WorkDir = "/" + } + + // Set default environment variables and incorporate data from image, if necessary + envs := shared.EnvVariablesFromData(imageData) + + // Environment Variables + for _, e := range containerYAML.Env { + envs[e.Name] = e.Value + } + containerConfig.Env = envs + + for _, volume := range containerYAML.VolumeMounts { + hostPath, exists := volumes[volume.Name] + if !exists { + return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) + } + if err := createconfig.ValidateVolumeCtrDir(volume.MountPath); err != nil { + return nil, errors.Wrapf(err, "error in parsing MountPath") + } + containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s", hostPath, volume.MountPath)) + } + return &containerConfig, nil +} diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index 125d057b0..0c62ac923 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -258,25 +258,25 @@ func (p *Pod) AllContainers() ([]*Container, error) { } // Status ... -func (p *Pod) Status() (map[string]libpod.ContainerStatus, error) { - ctrs := make(map[string]libpod.ContainerStatus) +func (p *Pod) Status() (map[string]define.ContainerStatus, error) { + ctrs := make(map[string]define.ContainerStatus) for _, i := range p.containers { - var status libpod.ContainerStatus + var status define.ContainerStatus switch i.State { case "exited": - status = libpod.ContainerStateExited + status = define.ContainerStateExited case "stopped": - status = libpod.ContainerStateStopped + status = define.ContainerStateStopped case "running": - status = libpod.ContainerStateRunning + status = define.ContainerStateRunning case "paused": - status = libpod.ContainerStatePaused + status = define.ContainerStatePaused case "created": - status = libpod.ContainerStateCreated - case "configured": - status = libpod.ContainerStateConfigured + status = define.ContainerStateCreated + case "define.red": + status = define.ContainerStateConfigured default: - status = libpod.ContainerStateUnknown + status = define.ContainerStateUnknown } ctrs[i.ID] = status } @@ -564,3 +564,8 @@ func (r *LocalRuntime) PrunePods(ctx context.Context, cli *cliconfig.PodPruneVal } return ok, failures, nil } + +// PlayKubeYAML creates pods and containers from a kube YAML file +func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayValues, yamlFile string) (*Pod, error) { + return nil, define.ErrNotImplemented +} diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 37ee1b737..8ef88f36b 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -5,6 +5,7 @@ package adapter import ( "bufio" "context" + "github.com/containers/libpod/libpod/define" "io" "io/ioutil" "os" @@ -57,12 +58,26 @@ type Volume struct { // VolumeFilter is for filtering volumes on the client type VolumeFilter func(*Volume) bool +// GetRuntimeNoStore returns a localruntime struct wit an embedded runtime but +// without a configured storage. +func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { + runtime, err := libpodruntime.GetRuntimeNoStore(ctx, c) + if err != nil { + return nil, err + } + return getRuntime(runtime) +} + // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { runtime, err := libpodruntime.GetRuntime(ctx, c) if err != nil { return nil, err } + return getRuntime(runtime) +} + +func getRuntime(runtime *libpod.Runtime) (*LocalRuntime, error) { return &LocalRuntime{ Runtime: runtime, }, nil @@ -313,8 +328,13 @@ func IsImageNotFound(err error) bool { } // HealthCheck is a wrapper to same named function in libpod -func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) { - return r.Runtime.HealthCheck(c.InputArgs[0]) +func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) { + output := "unhealthy" + status, err := r.Runtime.HealthCheck(c.InputArgs[0]) + if status == libpod.HealthCheckSuccess { + output = "healthy" + } + return output, err } // Events is a wrapper to libpod to obtain libpod/podman events @@ -395,8 +415,8 @@ func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*libpod.Pod, error) } // GetVersion is an alias to satisfy interface{} -func (r *LocalRuntime) GetVersion() (libpod.Version, error) { - return libpod.GetVersion() +func (r *LocalRuntime) GetVersion() (define.Version, error) { + return define.GetVersion() } // RemoteEndpoint resolve interface requirement diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 97e28e901..800ed7569 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -50,6 +50,12 @@ type LocalRuntime struct { *RemoteRuntime } +// GetRuntimeNoStore returns a LocalRuntime struct with the actual runtime embedded in it +// The nostore is ignored +func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { + return GetRuntime(ctx, c) +} + // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { var ( @@ -771,8 +777,8 @@ func IsImageNotFound(err error) bool { } // HealthCheck executes a container's healthcheck over a varlink connection -func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) { - return -1, define.ErrNotImplemented +func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) { + return "", define.ErrNotImplemented } // Events monitors libpod/podman events over a varlink connection @@ -907,22 +913,22 @@ func (r *LocalRuntime) GetContainersByContext(all bool, latest bool, namesOrIDs } // GetVersion returns version information from service -func (r *LocalRuntime) GetVersion() (libpod.Version, error) { +func (r *LocalRuntime) GetVersion() (define.Version, error) { version, goVersion, gitCommit, built, osArch, apiVersion, err := iopodman.GetVersion().Call(r.Conn) if err != nil { - return libpod.Version{}, errors.Wrapf(err, "Unable to obtain server version information") + return define.Version{}, errors.Wrapf(err, "Unable to obtain server version information") } var buildTime int64 if built != "" { t, err := time.Parse(time.RFC3339, built) if err != nil { - return libpod.Version{}, nil + return define.Version{}, nil } buildTime = t.Unix() } - return libpod.Version{ + return define.Version{ RemoteAPIVersion: apiVersion, Version: version, GoVersion: goVersion, diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 426bda559..d6c19212b 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -30,7 +30,7 @@ type CgroupControl struct { additionalControllers []controller } -// CPUUsage keeps stats for the CPU usage +// CPUUsage keeps stats for the CPU usage (unit: nanoseconds) type CPUUsage struct { Kernel uint64 Total uint64 diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go index 3f969fd3c..8640d490e 100644 --- a/pkg/cgroups/cpu.go +++ b/pkg/cgroups/cpu.go @@ -85,12 +85,14 @@ func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error { if err != nil { return err } + usage.Kernel *= 1000 } if val, found := values["system_usec"]; found { usage.Total, err = strconv.ParseUint(cleanString(val[0]), 10, 0) if err != nil { return err } + usage.Total *= 1000 } // FIXME: How to read usage.PerCPU? } else { diff --git a/pkg/hooks/0.1.0/hook.go b/pkg/hooks/0.1.0/hook.go index ba68b0f10..88a387647 100644 --- a/pkg/hooks/0.1.0/hook.go +++ b/pkg/hooks/0.1.0/hook.go @@ -6,7 +6,7 @@ import ( "errors" "strings" - hooks "github.com/containers/libpod/pkg/hooks" + "github.com/containers/libpod/pkg/hooks" current "github.com/containers/libpod/pkg/hooks/1.0.0" rspec "github.com/opencontainers/runtime-spec/specs-go" ) diff --git a/pkg/hooks/1.0.0/when_test.go b/pkg/hooks/1.0.0/when_test.go index 7187b297b..a749063ff 100644 --- a/pkg/hooks/1.0.0/when_test.go +++ b/pkg/hooks/1.0.0/when_test.go @@ -30,7 +30,7 @@ func TestAlways(t *testing.T) { for _, always := range []bool{true, false} { for _, or := range []bool{true, false} { for _, process := range []*rspec.Process{processStruct, nil} { - t.Run(fmt.Sprintf("always %t, or %t, has process %t", always, or, (process != nil)), func(t *testing.T) { + t.Run(fmt.Sprintf("always %t, or %t, has process %t", always, or, process != nil), func(t *testing.T) { config.Process = process when := When{Always: &always, Or: or} match, err := when.Match(config, map[string]string{}, false) diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index 7fb5c7ea8..0f684750e 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -29,6 +29,7 @@ import ( "time" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -134,7 +135,7 @@ func parseCRILog(log []byte, msg *logMessage) error { } // Keep this forward compatible. tags := bytes.Split(log[:idx], tagDelimiter) - partial := (LogTag(tags[0]) == LogTagPartial) + partial := LogTag(tags[0]) == LogTagPartial // Trim the tailing new line if this is a partial line. if partial && len(log) > 0 && log[len(log)-1] == '\n' { log = log[:len(log)-1] @@ -209,7 +210,7 @@ func followLog(reader *bufio.Reader, writer *logWriter, opts *LogOptions, ctr *l if err != nil { return err } - if state != libpod.ContainerStateRunning && state != libpod.ContainerStatePaused { + if state != define.ContainerStateRunning && state != define.ContainerStatePaused { break } continue diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index c409e3343..19b76f387 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -82,7 +82,7 @@ do_pause () struct sigaction act; int const sig[] = { - SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM, SIGPOLL, + SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGPOLL, SIGPROF, SIGVTALRM, SIGXCPU, SIGXFSZ, 0 }; @@ -244,7 +244,7 @@ static void __attribute__((constructor)) init() /* Shortcut. If we are able to join the pause pid file, do it now so we don't need to re-exec. */ xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); - if (xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) + if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) { int r; int fd; @@ -542,6 +542,11 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path) fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); _exit (EXIT_FAILURE); } + if (sigdelset (&sigset, SIGTERM) < 0) + { + fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) { fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); @@ -736,6 +741,11 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); _exit (EXIT_FAILURE); } + if (sigdelset (&sigset, SIGTERM) < 0) + { + fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno)); + _exit (EXIT_FAILURE); + } if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) { fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index ca8faecbd..8028a359c 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -1,4 +1,4 @@ -// +build linux +// +build linux,cgo package rootless @@ -9,14 +9,17 @@ import ( "os/exec" gosignal "os/signal" "os/user" + "path/filepath" "runtime" "strconv" + "strings" "sync" "syscall" "unsafe" "github.com/containers/storage/pkg/idtools" "github.com/docker/docker/pkg/signal" + "github.com/godbus/dbus" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -198,24 +201,90 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) { } } -func enableLinger(pausePid string) { - if pausePid == "" { - return +// EnableLinger configures the system to not kill the user processes once the session +// terminates +func EnableLinger() (string, error) { + uid := fmt.Sprintf("%d", GetRootlessUID()) + + conn, err := dbus.SystemBus() + if err == nil { + defer conn.Close() + } + + lingerEnabled := false + + // If we have a D-BUS connection, attempt to read the LINGER property from it. + if conn != nil { + path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid)) + ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.Linger") + if err == nil && ret.Value().(bool) { + lingerEnabled = true + } + } + + xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") + lingerFile := "" + if xdgRuntimeDir != "" && !lingerEnabled { + lingerFile = filepath.Join(xdgRuntimeDir, "libpod/linger") + _, err := os.Stat(lingerFile) + if err == nil { + lingerEnabled = true + } } - // If we are trying to write a pause pid file, make sure we can leave processes - // running longer than the user session. - err := exec.Command("loginctl", "enable-linger", fmt.Sprintf("%d", GetRootlessUID())).Run() + + if !lingerEnabled { + // First attempt with D-BUS, if it fails, then attempt with "loginctl enable-linger" + if conn != nil { + o := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1") + ret := o.Call("org.freedesktop.login1.Manager.SetUserLinger", 0, uint32(GetRootlessUID()), true, true) + if ret.Err == nil { + lingerEnabled = true + } + } + if !lingerEnabled { + err := exec.Command("loginctl", "enable-linger", uid).Run() + if err == nil { + lingerEnabled = true + } else { + logrus.Debugf("cannot run `loginctl enable-linger` for the current user: %v", err) + } + } + if lingerEnabled && lingerFile != "" { + f, err := os.Create(lingerFile) + if err == nil { + f.Close() + } else { + logrus.Debugf("could not create linger file: %v", err) + } + } + } + + if !lingerEnabled { + return "", nil + } + + // If we have a D-BUS connection, attempt to read the RUNTIME PATH from it. + if conn != nil { + path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid)) + ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.RuntimePath") + if err == nil { + return strings.Trim(ret.String(), "\"\n"), nil + } + } + + // If XDG_RUNTIME_DIR is not set and the D-BUS call didn't work, try to get the runtime path with "loginctl" + output, err := exec.Command("loginctl", "-pRuntimePath", "show-user", uid).Output() if err != nil { - logrus.Warnf("cannot run `loginctl enable-linger` for the current user: %v", err) + logrus.Debugf("could not get RuntimePath using loginctl: %v", err) + return "", nil } + return strings.Trim(strings.Replace(string(output), "RuntimePath=", "", -1), "\"\n"), nil } // joinUserAndMountNS re-exec podman in a new userNS and join the user and mount // namespace of the specified PID without looking up its parent. Useful to join directly // the conmon process. func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) { - enableLinger(pausePid) - if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" { return false, -1, nil } @@ -406,7 +475,6 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, // If podman was re-executed the caller needs to propagate the error code returned by the child // process. func BecomeRootInUserNS(pausePid string) (bool, int, error) { - enableLinger(pausePid) return becomeRootInUserNS(pausePid, "", nil) } diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index c063adee5..a8485c083 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -1,14 +1,21 @@ -// +build !linux +// +build !linux !cgo package rootless import ( + "os" + "github.com/pkg/errors" ) -// IsRootless returns false on all non-linux platforms +// IsRootless returns whether the user is rootless func IsRootless() bool { - return false + uid := os.Geteuid() + // os.Geteuid() on Windows returns -1 + if uid == -1 { + return false + } + return uid != 0 } // BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed @@ -29,6 +36,12 @@ func GetRootlessGID() int { return -1 } +// EnableLinger configures the system to not kill the user processes once the session +// terminates +func EnableLinger() (string, error) { + return "", nil +} + // TryJoinFromFilePaths attempts to join the namespaces of the pid files in paths. // This is useful when there are already running containers and we // don't have a pause process yet. We can use the paths to the conmon diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go index eb2acf984..9f6a4a058 100644 --- a/pkg/spec/config_linux.go +++ b/pkg/spec/config_linux.go @@ -4,12 +4,10 @@ package createconfig import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" - "github.com/docker/docker/profiles/seccomp" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -130,29 +128,6 @@ func (c *CreateConfig) addPrivilegedDevices(g *generate.Generator) error { return nil } -func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { - var seccompConfig *spec.LinuxSeccomp - var err error - - if config.SeccompProfilePath != "" { - seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath) - if err != nil { - return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath) - } - seccompConfig, err = seccomp.LoadProfile(string(seccompProfile), configSpec) - if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) - } - } else { - seccompConfig, err = seccomp.GetDefaultProfile(configSpec) - if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) - } - } - - return seccompConfig, nil -} - func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) { var ret *spec.LinuxBlockIO bio := &spec.LinuxBlockIO{} diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go new file mode 100644 index 000000000..e6e92a7cc --- /dev/null +++ b/pkg/spec/config_linux_cgo.go @@ -0,0 +1,34 @@ +// +build linux,cgo + +package createconfig + +import ( + "io/ioutil" + + "github.com/docker/docker/profiles/seccomp" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { + var seccompConfig *spec.LinuxSeccomp + var err error + + if config.SeccompProfilePath != "" { + seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath) + if err != nil { + return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath) + } + seccompConfig, err = seccomp.LoadProfile(string(seccompProfile), configSpec) + if err != nil { + return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) + } + } else { + seccompConfig, err = seccomp.GetDefaultProfile(configSpec) + if err != nil { + return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) + } + } + + return seccompConfig, nil +} diff --git a/pkg/spec/config_linux_nocgo.go b/pkg/spec/config_linux_nocgo.go new file mode 100644 index 000000000..10329ff3b --- /dev/null +++ b/pkg/spec/config_linux_nocgo.go @@ -0,0 +1,11 @@ +// +build linux,!cgo + +package createconfig + +import ( + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { + return nil, nil +} diff --git a/pkg/sysinfo/sysinfo_test.go b/pkg/sysinfo/sysinfo_test.go index b61fbcf54..895828f26 100644 --- a/pkg/sysinfo/sysinfo_test.go +++ b/pkg/sysinfo/sysinfo_test.go @@ -20,7 +20,7 @@ func TestIsCpusetListAvailable(t *testing.T) { for _, c := range cases { r, err := isCpusetListAvailable(c.provided, c.available) if (c.err && err == nil) && r != c.res { - t.Fatalf("Expected pair: %v, %v for %s, %s. Got %v, %v instead", c.res, c.err, c.provided, c.available, (c.err && err == nil), r) + t.Fatalf("Expected pair: %v, %v for %s, %s. Got %v, %v instead", c.res, c.err, c.provided, c.available, c.err && err == nil, r) } } } diff --git a/pkg/systemdgen/systemdgen.go b/pkg/systemdgen/systemdgen.go index 3d1c31b5d..06c5ebde5 100644 --- a/pkg/systemdgen/systemdgen.go +++ b/pkg/systemdgen/systemdgen.go @@ -2,17 +2,18 @@ package systemdgen import ( "fmt" - "path/filepath" + "os" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) var template = `[Unit] Description=%s Podman Container [Service] Restart=%s -ExecStart=/usr/bin/podman start %s -ExecStop=/usr/bin/podman stop -t %d %s +ExecStart=%s start %s +ExecStop=%s stop -t %d %s KillMode=none Type=forking PIDFile=%s @@ -33,11 +34,26 @@ func ValidateRestartPolicy(restart string) error { // CreateSystemdUnitAsString takes variables to create a systemd unit file used to control // a libpod container -func CreateSystemdUnitAsString(name, cid, restart, pidPath string, stopTimeout int) (string, error) { +func CreateSystemdUnitAsString(name, cid, restart, pidFile string, stopTimeout int) (string, error) { + podmanExe := getPodmanExecutable() + return createSystemdUnitAsString(podmanExe, name, cid, restart, pidFile, stopTimeout) +} + +func createSystemdUnitAsString(exe, name, cid, restart, pidFile string, stopTimeout int) (string, error) { if err := ValidateRestartPolicy(restart); err != nil { return "", err } - pidFile := filepath.Join(pidPath, fmt.Sprintf("%s.pid", cid)) - unit := fmt.Sprintf(template, name, restart, name, stopTimeout, name, pidFile) + + unit := fmt.Sprintf(template, name, restart, exe, name, exe, stopTimeout, name, pidFile) return unit, nil } + +func getPodmanExecutable() string { + podmanExe, err := os.Executable() + if err != nil { + podmanExe = "/usr/bin/podman" + logrus.Warnf("Could not obtain podman executable location, using default %s", podmanExe) + } + + return podmanExe +} diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go index f2f49e750..e413b24ce 100644 --- a/pkg/systemdgen/systemdgen_test.go +++ b/pkg/systemdgen/systemdgen_test.go @@ -41,7 +41,7 @@ ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4 ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 KillMode=none Type=forking -PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid [Install] WantedBy=multi-user.target` @@ -53,15 +53,16 @@ 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 +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid [Install] WantedBy=multi-user.target` type args struct { + exe string name string cid string restart string - pidPath string + pidFile string stopTimeout int } tests := []struct { @@ -73,10 +74,11 @@ WantedBy=multi-user.target` {"good with id", args{ + "/usr/bin/podman", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "always", - "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", 10, }, goodID, @@ -84,10 +86,11 @@ WantedBy=multi-user.target` }, {"good with name", args{ + "/usr/bin/podman", "foobar", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "always", - "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", 10, }, goodName, @@ -95,10 +98,11 @@ WantedBy=multi-user.target` }, {"bad restart policy", args{ + "/usr/bin/podman", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "never", - "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", 10, }, "", @@ -107,7 +111,7 @@ WantedBy=multi-user.target` } 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) + got, err := createSystemdUnitAsString(tt.args.exe, tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidFile, tt.args.stopTimeout) if (err != nil) != tt.wantErr { t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index cae76dee8..d028ddf8f 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -4,9 +4,9 @@ import ( "fmt" "io" - opentracing "github.com/opentracing/opentracing-go" - jaeger "github.com/uber/jaeger-client-go" - config "github.com/uber/jaeger-client-go/config" + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" ) // Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout. diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go index 9a75474ae..3bfe4bda1 100644 --- a/pkg/trust/trust.go +++ b/pkg/trust/trust.go @@ -14,7 +14,7 @@ import ( "github.com/containers/image/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" ) // PolicyContent struct for policy.json file diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 61cdbbf38..9e49f08a0 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -337,3 +337,14 @@ func GetGlobalOpts(c *cliconfig.RunlabelValues) string { }) return strings.Join(optsCommand, " ") } + +// OpenExclusiveFile opens a file for writing and ensure it doesn't already exist +func OpenExclusiveFile(path string) (*os.File, error) { + baseDir := filepath.Dir(path) + if baseDir != "" { + if _, err := os.Stat(baseDir); err != nil { + return nil, err + } + } + return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) +} diff --git a/pkg/util/utils_darwin.go b/pkg/util/utils_darwin.go new file mode 100644 index 000000000..33a46a5d4 --- /dev/null +++ b/pkg/util/utils_darwin.go @@ -0,0 +1,11 @@ +//+build darwin + +package util + +import ( + "github.com/pkg/errors" +) + +func GetContainerPidInformationDescriptors() ([]string, error) { + return []string{}, errors.New("this function is not supported on darwin") +} diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go new file mode 100644 index 000000000..47fa1031f --- /dev/null +++ b/pkg/util/utils_linux.go @@ -0,0 +1,11 @@ +package util + +import ( + "github.com/containers/psgo" +) + +// GetContainerPidInformationDescriptors returns a string slice of all supported +// format descriptors of GetContainerPidInformation. +func GetContainerPidInformationDescriptors() ([]string, error) { + return psgo.ListDescriptors(), nil +} diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index f8045f855..99c9e4f1e 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -7,11 +7,12 @@ package util import ( "fmt" - "github.com/containers/libpod/pkg/rootless" - "github.com/pkg/errors" "os" "path/filepath" "syscall" + + "github.com/containers/libpod/pkg/rootless" + "github.com/pkg/errors" ) // GetRootlessRuntimeDir returns the runtime directory when running as non root diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index 3faa6f10c..635558bf7 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -6,18 +6,24 @@ import ( "github.com/pkg/errors" ) -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { - return "", errors.New("this function is not implemented for windows") -} - // IsCgroup2UnifiedMode returns whether we are running in cgroup 2 unified mode. func IsCgroup2UnifiedMode() (bool, error) { return false, errors.New("this function is not implemented for windows") } +// GetContainerPidInformationDescriptors returns a string slice of all supported +// format descriptors of GetContainerPidInformation. +func GetContainerPidInformationDescriptors() ([]string, error) { + return nil, errors.New("this function is not implemented for windows") +} + // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { return "", errors.New("this function is not implemented for windows") } + +// GetRootlessRuntimeDir returns the runtime directory when running as non root +func GetRootlessRuntimeDir() (string, error) { + return "", errors.New("this function is not implemented for windows") +} diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 3f0a119f4..afa88e6a3 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -58,7 +58,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st if err != nil { return call.ReplyErrorOccurred(err.Error()) } - if !start && state != libpod.ContainerStateRunning { + if !start && state != define.ContainerStateRunning { return call.ReplyErrorOccurred("container must be running to attach") } @@ -73,7 +73,7 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st } }() - if state == libpod.ContainerStateRunning { + if state == define.ContainerStateRunning { finalErr = attach(ctr, streams, detachKeys, resize, errChan) } else { finalErr = startAndAttach(ctr, streams, detachKeys, resize, errChan) diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index ed3243f21..6855a7231 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/adapter/shortcuts" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/storage/pkg/archive" @@ -139,7 +140,7 @@ func (i *LibpodAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses [] containers []iopodman.Container ) for _, status := range statuses { - lpstatus, err := libpod.StringToContainerStatus(status) + lpstatus, err := define.StringToContainerStatus(status) if err != nil { return call.ReplyErrorOccurred(err.Error()) } @@ -199,7 +200,7 @@ func (i *LibpodAPI) ListContainerProcesses(call iopodman.VarlinkCall, name strin if err != nil { return call.ReplyErrorOccurred(err.Error()) } - if containerState != libpod.ContainerStateRunning { + if containerState != define.ContainerStateRunning { return call.ReplyErrorOccurred(fmt.Sprintf("container %s is not running", name)) } var psArgs []string @@ -230,7 +231,7 @@ func (i *LibpodAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) err return call.ReplyErrorOccurred(err.Error()) } if _, err := os.Stat(logPath); err != nil { - if containerState == libpod.ContainerStateConfigured { + if containerState == define.ContainerStateConfigured { return call.ReplyGetContainerLogs(logs) } } @@ -260,7 +261,7 @@ func (i *LibpodAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) err if err != nil { return call.ReplyErrorOccurred(err.Error()) } - if state != libpod.ContainerStateRunning && state != libpod.ContainerStatePaused { + if state != define.ContainerStateRunning && state != define.ContainerStatePaused { return call.ReplyErrorOccurred(fmt.Sprintf("%s is no longer running", ctr.ID())) } @@ -360,7 +361,7 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error if err != nil { return call.ReplyErrorOccurred(err.Error()) } - if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused { + if state == define.ContainerStateRunning || state == define.ContainerStatePaused { return call.ReplyErrorOccurred("container is already running or paused") } recursive := false @@ -511,7 +512,7 @@ func (i *LibpodAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { if err != nil { return call.ReplyErrorOccurred(err.Error()) } - if state != libpod.ContainerStateRunning { + if state != define.ContainerStateRunning { if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil { return call.ReplyErrorOccurred(err.Error()) } @@ -535,7 +536,7 @@ func (i *LibpodAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) err // If the container hasn't been run, we need to run init // so the conmon sockets get created. - if status == libpod.ContainerStateConfigured || status == libpod.ContainerStateStopped { + if status == define.ContainerStateConfigured || status == define.ContainerStateStopped { if err := ctr.Init(getContext()); err != nil { return call.ReplyErrorOccurred(err.Error()) } @@ -720,7 +721,7 @@ func (i *LibpodAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, if err != nil { return call.ReplyErrorOccurred(err.Error()) } - options := libpod.LogOptions{ + options := logs.LogOptions{ Follow: follow, Since: sinceTime, Tail: uint64(tail), @@ -731,7 +732,7 @@ func (i *LibpodAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, if len(names) > 1 { options.Multi = true } - logChannel := make(chan *libpod.LogLine, int(tail)*len(names)+1) + logChannel := make(chan *logs.LogLine, int(tail)*len(names)+1) containers, err := shortcuts.GetContainersByContext(false, latest, names, i.Runtime) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -753,7 +754,7 @@ func (i *LibpodAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, return call.ReplyGetContainersLogs(iopodman.LogLine{}) } -func newPodmanLogLine(line *libpod.LogLine) iopodman.LogLine { +func newPodmanLogLine(line *logs.LogLine) iopodman.LogLine { return iopodman.LogLine{ Device: line.Device, ParseLogType: line.ParseLogType, diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 59bfec75b..9b5b3a5b1 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -3,17 +3,17 @@ package varlinkapi import ( + "github.com/containers/libpod/libpod/define" goruntime "runtime" "strings" "time" "github.com/containers/libpod/cmd/podman/varlink" - "github.com/containers/libpod/libpod" ) // GetVersion ... func (i *LibpodAPI) GetVersion(call iopodman.VarlinkCall) error { - versionInfo, err := libpod.GetVersion() + versionInfo, err := define.GetVersion() if err != nil { return err } @@ -30,7 +30,7 @@ func (i *LibpodAPI) GetVersion(call iopodman.VarlinkCall) error { // GetInfo returns details about the podman host and its stores func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { - versionInfo, err := libpod.GetVersion() + versionInfo, err := define.GetVersion() if err != nil { return err } diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go index 8716c963a..a74105795 100644 --- a/pkg/varlinkapi/util.go +++ b/pkg/varlinkapi/util.go @@ -12,6 +12,7 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/storage/pkg/archive" ) @@ -73,7 +74,7 @@ func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct Names: batchInfo.ConConfig.Name, Labels: batchInfo.ConConfig.Labels, Mounts: mounts, - Containerrunning: batchInfo.ConState == libpod.ContainerStateRunning, + Containerrunning: batchInfo.ConState == define.ContainerStateRunning, Namespaces: namespace, } if batchInfo.Size != nil { |