diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/spec/createconfig.go | 59 | ||||
-rw-r--r-- | pkg/util/utils.go | 10 | ||||
-rw-r--r-- | pkg/varlinkapi/containers.go | 35 | ||||
-rw-r--r-- | pkg/varlinkapi/containers_create.go | 7 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 282 | ||||
-rw-r--r-- | pkg/varlinkapi/system.go | 25 | ||||
-rw-r--r-- | pkg/varlinkapi/transfers.go | 2 | ||||
-rw-r--r-- | pkg/varlinkapi/util.go | 35 | ||||
-rw-r--r-- | pkg/varlinkapi/volumes.go | 74 |
9 files changed, 336 insertions, 193 deletions
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index f6c77d61c..8da44a2f0 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -13,6 +13,7 @@ import ( "github.com/containers/libpod/pkg/namespaces" "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" + "github.com/containers/storage/pkg/stringid" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-connections/nat" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -134,8 +135,8 @@ type CreateConfig struct { SeccompProfilePath string //SecurityOpts SecurityOpts []string Rootfs string - LocalVolumes []string //Keeps track of the built-in volumes of container used in the --volumes-from flag - Syslog bool // Whether to enable syslog on exit commands + LocalVolumes []spec.Mount //Keeps track of the built-in volumes of container used in the --volumes-from flag + Syslog bool // Whether to enable syslog on exit commands } func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } @@ -216,7 +217,7 @@ func (c *CreateConfig) initFSMounts() []spec.Mount { //GetVolumeMounts takes user provided input for bind mounts and creates Mount structs func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) { - var m []spec.Mount + m := c.LocalVolumes for _, i := range c.Volumes { var options []string spliti := strings.Split(i, ":") @@ -234,22 +235,31 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e logrus.Debugf("User mount %s:%s options %v", spliti[0], spliti[1], options) } - // volumes from image config - if c.ImageVolumeType != "tmpfs" { + if c.ImageVolumeType == "ignore" { return m, nil } + for vol := range c.BuiltinImgVolumes { if libpod.MountExists(specMounts, vol) { continue } + mount := spec.Mount{ Destination: vol, - Type: string(TypeTmpfs), - Source: string(TypeTmpfs), - Options: []string{"rprivate", "rw", "noexec", "nosuid", "nodev", "tmpcopyup"}, + Type: c.ImageVolumeType, + Options: []string{"rprivate", "rw", "nodev"}, + } + if c.ImageVolumeType == "tmpfs" { + mount.Source = "tmpfs" + mount.Options = append(mount.Options, "tmpcopyup") + } else { + // This will cause a new local Volume to be created on your system + mount.Source = stringid.GenerateNonCryptoID() + mount.Options = append(mount.Options, "bind") } m = append(m, mount) } + return m, nil } @@ -257,6 +267,11 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e // and adds it to c.Volumes of the current container. func (c *CreateConfig) GetVolumesFrom() error { var options string + + if rootless.SkipStorageSetup() { + return nil + } + for _, vol := range c.VolumesFrom { splitVol := strings.SplitN(vol, ":", 2) if len(splitVol) == 2 { @@ -266,6 +281,10 @@ func (c *CreateConfig) GetVolumesFrom() error { if err != nil { return errors.Wrapf(err, "error looking up container %q", splitVol[0]) } + inspect, err := ctr.Inspect(false) + if err != nil { + return errors.Wrapf(err, "error inspecting %q", splitVol[0]) + } var createArtifact CreateConfig artifact, err := ctr.GetArtifact("create-config") if err != nil { @@ -274,9 +293,13 @@ func (c *CreateConfig) GetVolumesFrom() error { if err := json.Unmarshal(artifact, &createArtifact); err != nil { return err } - for key := range createArtifact.BuiltinImgVolumes { - c.LocalVolumes = append(c.LocalVolumes, key) + for _, m := range inspect.Mounts { + if m.Destination == key { + c.LocalVolumes = append(c.LocalVolumes, m) + break + } + } } for _, i := range createArtifact.Volumes { @@ -340,7 +363,13 @@ func (c *CreateConfig) createExitCommand() []string { if c.Syslog { command = append(command, "--syslog") } - return append(command, []string{"container", "cleanup"}...) + command = append(command, []string{"container", "cleanup"}...) + + if c.Rm { + command = append(command, "--rm") + } + + return command } // GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions @@ -518,11 +547,9 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l if c.CgroupParent != "" { options = append(options, libpod.WithCgroupParent(c.CgroupParent)) } - // For a rootless container always cleanup the storage/network as they - // run in a different namespace thus not reusable when we restart. - if c.Detach || rootless.IsRootless() { - options = append(options, libpod.WithExitCommand(c.createExitCommand())) - } + + // Always use a cleanup process to clean up Podman after termination + options = append(options, libpod.WithExitCommand(c.createExitCommand())) return options, nil } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 52f431881..db8a3d5bb 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -259,8 +259,8 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) { return opts, nil } -// GetRootlessVolumeInfo returns where all the name volumes will be created in rootless mode -func GetRootlessVolumeInfo() (string, error) { +// GetRootlessVolumePath returns where all the name volumes will be created in rootless mode +func GetRootlessVolumePath() (string, error) { dataDir, _, err := GetRootlessDirInfo() if err != nil { return "", err @@ -307,15 +307,13 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) { err error ) storageOpts := storage.DefaultStoreOptions - volumePath := "/var/lib/containers/storage" - + volumePath := filepath.Join(storageOpts.GraphRoot, "volumes") if rootless.IsRootless() { storageOpts, err = GetRootlessStorageOpts() if err != nil { return storageOpts, volumePath, err } - - volumePath, err = GetRootlessVolumeInfo() + volumePath, err = GetRootlessVolumePath() if err != nil { return storageOpts, volumePath, err } diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index 737e2dd96..8a52efa61 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -21,7 +21,7 @@ import ( // ListContainers ... func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error { var ( - listContainers []iopodman.ListContainerData + listContainers []iopodman.Container ) containers, err := i.Runtime.GetAllContainers() @@ -44,10 +44,10 @@ func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error { } // GetContainer ... -func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, name string) error { - ctr, err := i.Runtime.LookupContainer(name) +func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error { + ctr, err := i.Runtime.LookupContainer(id) if err != nil { - return call.ReplyContainerNotFound(name) + return call.ReplyContainerNotFound(id) } opts := shared.PsOptions{ Namespace: true, @@ -247,11 +247,6 @@ func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) er return call.ReplyGetContainerStats(cs) } -// ResizeContainerTty ... -func (i *LibpodAPI) ResizeContainerTty(call iopodman.VarlinkCall) error { - return call.ReplyMethodNotImplemented("ResizeContainerTty") -} - // StartContainer ... func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) @@ -324,16 +319,6 @@ func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal return call.ReplyKillContainer(ctr.ID()) } -// UpdateContainer ... -func (i *LibpodAPI) UpdateContainer(call iopodman.VarlinkCall) error { - return call.ReplyMethodNotImplemented("UpdateContainer") -} - -// RenameContainer ... -func (i *LibpodAPI) RenameContainer(call iopodman.VarlinkCall) error { - return call.ReplyMethodNotImplemented("RenameContainer") -} - // PauseContainer ... func (i *LibpodAPI) PauseContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) @@ -358,12 +343,6 @@ func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) err return call.ReplyUnpauseContainer(ctr.ID()) } -// AttachToContainer ... -// TODO: DO we also want a different one for websocket? -func (i *LibpodAPI) AttachToContainer(call iopodman.VarlinkCall) error { - return call.ReplyMethodNotImplemented("AttachToContainer") -} - // WaitContainer ... func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) @@ -379,13 +358,13 @@ func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string) error } // RemoveContainer ... -func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool) error { +func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error { ctx := getContext() ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name) } - if err := i.Runtime.RemoveContainer(ctx, ctr, force); err != nil { + if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil { return call.ReplyErrorOccurred(err.Error()) } return call.ReplyRemoveContainer(ctr.ID()) @@ -406,7 +385,7 @@ func (i *LibpodAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { return call.ReplyErrorOccurred(err.Error()) } if state != libpod.ContainerStateRunning { - if err := i.Runtime.RemoveContainer(ctx, ctr, false); err != nil { + if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil { return call.ReplyErrorOccurred(err.Error()) } deletedContainers = append(deletedContainers, ctr.ID()) diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go index f1835a189..6b53b22c6 100644 --- a/pkg/varlinkapi/containers_create.go +++ b/pkg/varlinkapi/containers_create.go @@ -131,9 +131,14 @@ func varlinkCreateToCreateConfig(ctx context.Context, create iopodman.Create, ru } imageID := data.ID + var ImageVolumes map[string]struct{} + if data != nil && create.Image_volume_type != "ignore" { + ImageVolumes = data.Config.Volumes + } + config := &cc.CreateConfig{ Runtime: runtime, - BuiltinImgVolumes: data.Config.Volumes, + BuiltinImgVolumes: ImageVolumes, ConmonPidFile: create.Conmon_pidfile, ImageVolumeType: create.Image_volume_type, CapAdd: create.Cap_add, diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 5e0889645..534419f6f 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -24,10 +25,11 @@ import ( sysreg "github.com/containers/libpod/pkg/registries" "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/utils" - "github.com/docker/go-units" + "github.com/containers/storage/pkg/archive" "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // ListImages lists all the images in the store @@ -37,7 +39,7 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error { if err != nil { return call.ReplyErrorOccurred(fmt.Sprintf("unable to get list of images %q", err)) } - var imageList []iopodman.ImageInList + var imageList []iopodman.Image for _, image := range images { labels, _ := image.Labels(getContext()) containers, _ := image.Containers() @@ -52,12 +54,12 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error { return call.ReplyErrorOccurred(err.Error()) } - i := iopodman.ImageInList{ + i := iopodman.Image{ Id: image.ID(), ParentId: image.Parent, RepoTags: image.Names(), RepoDigests: repoDigests, - Created: image.Created().String(), + Created: image.Created().Format(time.RFC3339), Size: int64(*size), VirtualSize: image.VirtualSize, Containers: int64(len(containers)), @@ -69,11 +71,11 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error { return call.ReplyListImages(imageList) } -// GetImage returns a single image in the form of a ImageInList -func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error { - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) +// GetImage returns a single image in the form of a Image +func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error { + newImage, err := i.Runtime.ImageRuntime().NewFromLocal(id) if err != nil { - return call.ReplyImageNotFound(err.Error()) + return call.ReplyImageNotFound(id) } labels, err := newImage.Labels(getContext()) if err != nil { @@ -92,12 +94,12 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error { return err } - il := iopodman.ImageInList{ + il := iopodman.Image{ Id: newImage.ID(), ParentId: newImage.Parent, RepoTags: newImage.Names(), RepoDigests: repoDigests, - Created: newImage.Created().String(), + Created: newImage.Created().Format(time.RFC3339), Size: int64(*size), VirtualSize: newImage.VirtualSize, Containers: int64(len(containers)), @@ -109,83 +111,46 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, name string) error { // BuildImage ... func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildInfo) error { var ( - memoryLimit int64 - memorySwap int64 - namespace []buildah.NamespaceOption - err error + namespace []buildah.NamespaceOption + err error ) systemContext := types.SystemContext{} - dockerfiles := config.Dockerfile - contextDir := "" - - for i := range dockerfiles { - if strings.HasPrefix(dockerfiles[i], "http://") || - strings.HasPrefix(dockerfiles[i], "https://") || - strings.HasPrefix(dockerfiles[i], "git://") || - strings.HasPrefix(dockerfiles[i], "github.com/") { - continue - } - absFile, err := filepath.Abs(dockerfiles[i]) - if err != nil { - return errors.Wrapf(err, "error determining path to file %q", dockerfiles[i]) - } - contextDir = filepath.Dir(absFile) - dockerfiles[i], err = filepath.Rel(contextDir, absFile) - if err != nil { - return errors.Wrapf(err, "error determining path to file %q", dockerfiles[i]) - } - break - } + contextDir := config.ContextDir - pullPolicy := imagebuildah.PullNever - if config.Pull { - pullPolicy = imagebuildah.PullIfMissing - } - - if config.Pull_always { - pullPolicy = imagebuildah.PullAlways - } - manifestType := "oci" //nolint - if config.Image_format != "" { - manifestType = config.Image_format - } - - if strings.HasPrefix(manifestType, "oci") { - manifestType = buildah.OCIv1ImageManifest - } else if strings.HasPrefix(manifestType, "docker") { - manifestType = buildah.Dockerv2ImageManifest - } else { - return call.ReplyErrorOccurred(fmt.Sprintf("unrecognized image type %q", manifestType)) + newContextDir, err := ioutil.TempDir("", "buildTarball") + if err != nil { + call.ReplyErrorOccurred("unable to create tempdir") } + logrus.Debugf("created new context dir at %s", newContextDir) - if config.Memory != "" { - memoryLimit, err = units.RAMInBytes(config.Memory) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } + reader, err := os.Open(contextDir) + if err != nil { + logrus.Errorf("failed to open the context dir tar file %s", contextDir) + return call.ReplyErrorOccurred(fmt.Sprintf("unable to open context dir tar file %s", contextDir)) } - - if config.Memory_swap != "" { - memorySwap, err = units.RAMInBytes(config.Memory_swap) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } + defer reader.Close() + if err := archive.Untar(reader, newContextDir, &archive.TarOptions{}); err != nil { + logrus.Errorf("fail to untar the context dir tarball (%s) to the context dir (%s)", contextDir, newContextDir) + return call.ReplyErrorOccurred(fmt.Sprintf("unable to untar context dir %s", contextDir)) } + logrus.Debugf("untar of %s successful", contextDir) + // All output (stdout, stderr) is captured in output as well output := bytes.NewBuffer([]byte{}) + commonOpts := &buildah.CommonBuildOptions{ - AddHost: config.Add_hosts, - CgroupParent: config.Cgroup_parent, - CPUPeriod: uint64(config.Cpu_period), - CPUQuota: config.Cpu_quota, - CPUSetCPUs: config.Cpuset_cpus, - CPUSetMems: config.Cpuset_mems, - Memory: memoryLimit, - MemorySwap: memorySwap, - ShmSize: config.Shm_size, - Ulimit: config.Ulimit, - Volumes: config.Volume, + AddHost: config.BuildOptions.AddHosts, + CgroupParent: config.BuildOptions.CgroupParent, + CPUPeriod: uint64(config.BuildOptions.CpuPeriod), + CPUQuota: config.BuildOptions.CpuQuota, + CPUSetCPUs: config.BuildOptions.CpusetCpus, + CPUSetMems: config.BuildOptions.CpusetMems, + Memory: config.BuildOptions.Memory, + MemorySwap: config.BuildOptions.MemorySwap, + ShmSize: config.BuildOptions.ShmSize, + Ulimit: config.BuildOptions.Ulimit, + Volumes: config.BuildOptions.Volume, } hostNetwork := buildah.NamespaceOption{ @@ -196,37 +161,68 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI namespace = append(namespace, hostNetwork) options := imagebuildah.BuildOptions{ - ContextDirectory: contextDir, - PullPolicy: pullPolicy, - Compression: imagebuildah.Gzip, - Quiet: false, - //SignaturePolicyPath: - Args: config.Build_args, - //Output: - AdditionalTags: config.Tags, - //Runtime: runtime. - //RuntimeArgs: , - OutputFormat: manifestType, - SystemContext: &systemContext, - CommonBuildOpts: commonOpts, - Squash: config.Squash, - Labels: config.Label, - Annotations: config.Annotations, - ReportWriter: output, - NamespaceOptions: namespace, + CommonBuildOpts: commonOpts, + AdditionalTags: config.AdditionalTags, + Annotations: config.Annotations, + Args: config.BuildArgs, + CNIConfigDir: config.CniConfigDir, + CNIPluginPath: config.CniPluginDir, + Compression: stringCompressionToArchiveType(config.Compression), + ContextDirectory: newContextDir, + DefaultMountsFilePath: config.DefaultsMountFilePath, + Err: output, + ForceRmIntermediateCtrs: config.ForceRmIntermediateCtrs, + IIDFile: config.Iidfile, + Labels: config.Label, + Layers: config.Layers, + NoCache: config.Nocache, + Out: output, + Output: config.Output, + NamespaceOptions: namespace, + OutputFormat: config.OutputFormat, + PullPolicy: stringPullPolicyToType(config.PullPolicy), + Quiet: config.Quiet, + RemoveIntermediateCtrs: config.RemoteIntermediateCtrs, + ReportWriter: output, + RuntimeArgs: config.RuntimeArgs, + SignaturePolicyPath: config.SignaturePolicyPath, + Squash: config.Squash, + SystemContext: &systemContext, } if call.WantsMore() { call.Continues = true } - c := build(i.Runtime, options, config.Dockerfile) + var newPathDockerFiles []string + + for _, d := range config.Dockerfiles { + if strings.HasPrefix(d, "http://") || + strings.HasPrefix(d, "https://") || + strings.HasPrefix(d, "git://") || + strings.HasPrefix(d, "github.com/") { + newPathDockerFiles = append(newPathDockerFiles, d) + continue + } + base := filepath.Base(d) + newPathDockerFiles = append(newPathDockerFiles, filepath.Join(newContextDir, base)) + } + + c := build(i.Runtime, options, newPathDockerFiles) var log []string done := false for { - line, err := output.ReadString('\n') + outputLine, err := output.ReadString('\n') if err == nil { - log = append(log, line) + log = append(log, outputLine) + if call.WantsMore() { + // we want to reply with what we have + br := iopodman.MoreResponse{ + Logs: log, + } + call.ReplyBuildImage(br) + log = []string{} + } continue } else if err == io.EOF { select { @@ -236,15 +232,10 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI } done = true default: - if !call.WantsMore() { + if call.WantsMore() { time.Sleep(1 * time.Second) break } - br := iopodman.BuildResponse{ - Logs: log, - } - call.ReplyBuildImage(br) - log = []string{} } } else { return call.ReplyErrorOccurred(err.Error()) @@ -254,11 +245,12 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI } } call.Continues = false - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(config.Tags[0]) + + newImage, err := i.Runtime.ImageRuntime().NewFromLocal(config.Output) if err != nil { return call.ReplyErrorOccurred(err.Error()) } - br := iopodman.BuildResponse{ + br := iopodman.MoreResponse{ Logs: log, Id: newImage.ID(), } @@ -276,12 +268,6 @@ func build(runtime *libpod.Runtime, options imagebuildah.BuildOptions, dockerfil return c } -// CreateImage ... -// TODO With Pull being added, should we skip Create? -func (i *LibpodAPI) CreateImage(call iopodman.VarlinkCall) error { - return call.ReplyMethodNotImplemented("CreateImage") -} - // InspectImage returns an image's inspect information as a string that can be serialized. // Requires an image ID or name func (i *LibpodAPI) InspectImage(call iopodman.VarlinkCall, name string) error { @@ -315,7 +301,7 @@ func (i *LibpodAPI) HistoryImage(call iopodman.VarlinkCall, name string) error { for _, hist := range history { imageHistory := iopodman.ImageHistory{ Id: hist.ID, - Created: hist.Created.String(), + Created: hist.Created.Format(time.RFC3339), CreatedBy: hist.CreatedBy, Tags: newImage.Names(), Size: hist.Size, @@ -332,7 +318,6 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVe registryCreds *types.DockerAuthConfig manifestType string ) - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyImageNotFound(err.Error()) @@ -372,10 +357,59 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, tlsVe SignBy: signBy, } - if err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", signaturePolicy, nil, compress, so, &dockerRegistryOptions, nil); err != nil { - return call.ReplyErrorOccurred(err.Error()) + if call.WantsMore() { + call.Continues = true + } + + output := bytes.NewBuffer([]byte{}) + c := make(chan error) + go func() { + err := newImage.PushImageToHeuristicDestination(getContext(), destname, manifestType, "", signaturePolicy, output, compress, so, &dockerRegistryOptions, nil) + c <- err + close(c) + }() + + // TODO When pull output gets fixed for the remote client, we need to look into how we can turn below + // into something re-usable. it is in build too + var log []string + done := false + for { + line, err := output.ReadString('\n') + if err == nil { + log = append(log, line) + continue + } else if err == io.EOF { + select { + case err := <-c: + if err != nil { + logrus.Errorf("reading of output during push failed for %s", newImage.ID()) + return call.ReplyErrorOccurred(err.Error()) + } + done = true + default: + if !call.WantsMore() { + time.Sleep(1 * time.Second) + break + } + br := iopodman.MoreResponse{ + Logs: log, + } + call.ReplyPushImage(br) + log = []string{} + } + } else { + return call.ReplyErrorOccurred(err.Error()) + } + if done { + break + } } - return call.ReplyPushImage(newImage.ID()) + call.Continues = false + + br := iopodman.MoreResponse{ + Logs: log, + } + return call.ReplyPushImage(br) } // TagImage accepts an image name and tag as strings and tags an image in the local store. @@ -405,17 +439,21 @@ func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bo return call.ReplyRemoveImage(newImage.ID()) } -// SearchImage searches all registries configured in /etc/containers/registries.conf for an image +// SearchImages searches all registries configured in /etc/containers/registries.conf for an image // Requires an image name and a search limit as int -func (i *LibpodAPI) SearchImage(call iopodman.VarlinkCall, name string, limit int64) error { +func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64) error { sc := image.GetSystemContext("", "", false) registries, err := sysreg.GetRegistries() if err != nil { return call.ReplyErrorOccurred(fmt.Sprintf("unable to get system registries: %q", err)) } - var imageResults []iopodman.ImageSearch + var imageResults []iopodman.ImageSearchResult for _, reg := range registries { - results, err := docker.SearchRegistry(getContext(), sc, reg, name, int(limit)) + var lim = 1000 + if limit != nil { + lim = int(*limit) + } + results, err := docker.SearchRegistry(getContext(), sc, reg, query, lim) if err != nil { // If we are searching multiple registries, don't make something like an // auth error fatal. Unfortunately we cannot differentiate between auth @@ -426,7 +464,7 @@ func (i *LibpodAPI) SearchImage(call iopodman.VarlinkCall, name string, limit in return call.ReplyErrorOccurred(err.Error()) } for _, result := range results { - i := iopodman.ImageSearch{ + i := iopodman.ImageSearchResult{ Description: result.Description, Is_official: result.IsOfficial, Is_automated: result.IsAutomated, @@ -436,7 +474,7 @@ func (i *LibpodAPI) SearchImage(call iopodman.VarlinkCall, name string, limit in imageResults = append(imageResults, i) } } - return call.ReplySearchImage(imageResults) + return call.ReplySearchImages(imageResults) } // DeleteUnusedImages deletes any images that do not have containers associated with it. diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 376502f21..3f32615ec 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -3,6 +3,7 @@ package varlinkapi import ( goruntime "runtime" "strings" + "time" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" @@ -15,22 +16,14 @@ func (i *LibpodAPI) GetVersion(call iopodman.VarlinkCall) error { return err } - return call.ReplyGetVersion(iopodman.Version{ - Remote_api_version: versionInfo.RemoteAPIVersion, - Version: versionInfo.Version, - Go_version: versionInfo.GoVersion, - Git_commit: versionInfo.GitCommit, - Built: versionInfo.Built, - Os_arch: versionInfo.OsArch, - }) -} - -// Ping returns a simple string "OK" response for clients to make sure -// the service is working. -func (i *LibpodAPI) Ping(call iopodman.VarlinkCall) error { - return call.ReplyPing(iopodman.StringResponse{ - Message: "OK", - }) + return call.ReplyGetVersion( + versionInfo.Version, + versionInfo.GoVersion, + versionInfo.GitCommit, + time.Unix(versionInfo.Built, 0).Format(time.RFC3339), + versionInfo.OsArch, + versionInfo.RemoteAPIVersion, + ) } // GetInfo returns details about the podman host and its stores diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go index 0cb7e5e2e..9a97bc810 100644 --- a/pkg/varlinkapi/transfers.go +++ b/pkg/varlinkapi/transfers.go @@ -8,6 +8,7 @@ import ( "os" "github.com/containers/libpod/cmd/podman/varlink" + "github.com/sirupsen/logrus" ) // SendFile allows a client to send a file to the varlink server @@ -34,6 +35,7 @@ func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int return err } + logrus.Debugf("successfully received %s", outputFile.Name()) // Send an ACK to the client call.Call.Writer.WriteString(fmt.Sprintf("%s:", outputFile.Name())) call.Call.Writer.Flush() diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go index a80c8db41..7e487c03a 100644 --- a/pkg/varlinkapi/util.go +++ b/pkg/varlinkapi/util.go @@ -3,11 +3,14 @@ package varlinkapi import ( "context" "strconv" + "strings" "time" + "github.com/containers/buildah" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" + "github.com/containers/storage/pkg/archive" ) // getContext returns a non-nil, empty context @@ -15,7 +18,7 @@ func getContext() context.Context { return context.TODO() } -func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct) iopodman.ListContainerData { +func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct) iopodman.Container { var ( mounts []iopodman.ContainerMount ports []iopodman.ContainerPortMappings @@ -56,12 +59,12 @@ func makeListContainer(containerID string, batchInfo shared.BatchContainerStruct Ipc: ns.IPC, } - lc := iopodman.ListContainerData{ + lc := iopodman.Container{ Id: containerID, Image: batchInfo.ConConfig.RootfsImageName, Imageid: batchInfo.ConConfig.RootfsImageID, Command: batchInfo.ConConfig.Spec.Process.Args, - Createdat: batchInfo.ConConfig.CreatedTime.String(), + Createdat: batchInfo.ConConfig.CreatedTime.Format(time.RFC3339), Runningfor: time.Since(batchInfo.ConConfig.CreatedTime).String(), Status: batchInfo.ConState.String(), Ports: ports, @@ -107,7 +110,7 @@ func makeListPod(pod *libpod.Pod, batchInfo shared.PsOptions) (iopodman.ListPodD listPodsContainers = append(listPodsContainers, makeListPodContainers(ctr.ID(), batchInfo)) } listPod := iopodman.ListPodData{ - Createdat: pod.CreatedTime().String(), + Createdat: pod.CreatedTime().Format(time.RFC3339), Id: pod.ID(), Name: pod.Name(), Status: status, @@ -133,3 +136,27 @@ func handlePodCall(call iopodman.VarlinkCall, pod *libpod.Pod, ctrErrs map[strin return nil } + +func stringCompressionToArchiveType(s string) archive.Compression { + switch strings.ToUpper(s) { + case "BZIP2": + return archive.Bzip2 + case "GZIP": + return archive.Gzip + case "XZ": + return archive.Xz + } + return archive.Uncompressed +} + +func stringPullPolicyToType(s string) buildah.PullPolicy { + switch strings.ToUpper(s) { + case "PULLIFMISSING": + return buildah.PullIfMissing + case "PULLALWAYS": + return buildah.PullAlways + case "PULLNEVER": + return buildah.PullNever + } + return buildah.PullIfMissing +} diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go new file mode 100644 index 000000000..d41b07065 --- /dev/null +++ b/pkg/varlinkapi/volumes.go @@ -0,0 +1,74 @@ +package varlinkapi + +import ( + "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/libpod" +) + +// VolumeCreate creates a libpod volume based on input from a varlink connection +func (i *LibpodAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.VolumeCreateOpts) error { + var volumeOptions []libpod.VolumeCreateOption + + if len(options.VolumeName) > 0 { + volumeOptions = append(volumeOptions, libpod.WithVolumeName(options.VolumeName)) + } + if len(options.Driver) > 0 { + volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(options.Driver)) + } + if len(options.Labels) > 0 { + volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels)) + } + if len(options.Options) > 0 { + volumeOptions = append(volumeOptions, libpod.WithVolumeOptions(options.Options)) + } + newVolume, err := i.Runtime.NewVolume(getContext(), volumeOptions...) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + return call.ReplyVolumeCreate(newVolume.Name()) +} + +// VolumeRemove removes volumes by options.All or options.Volumes +func (i *LibpodAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error { + deletedVolumes, err := i.Runtime.RemoveVolumes(getContext(), options.Volumes, options.All, options.Force) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + return call.ReplyVolumeRemove(deletedVolumes) +} + +// GetVolumes returns all the volumes known to the remote system +func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error { + var ( + err error + reply []*libpod.Volume + volumes []iopodman.Volume + ) + if all { + reply, err = i.Runtime.GetAllVolumes() + } else { + for _, v := range args { + vol, err := i.Runtime.GetVolume(v) + if err != nil { + return err + } + reply = append(reply, vol) + } + } + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + // Build the iopodman.volume struct for the return + for _, v := range reply { + newVol := iopodman.Volume{ + Driver: v.Driver(), + Labels: v.Labels(), + MountPoint: v.MountPoint(), + Name: v.Name(), + Options: v.Options(), + Scope: v.Scope(), + } + volumes = append(volumes, newVol) + } + return call.ReplyGetVolumes(volumes) +} |