diff options
Diffstat (limited to 'pkg/domain/infra')
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 36 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 26 | ||||
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 102 | ||||
-rw-r--r-- | pkg/domain/infra/abi/pods.go | 25 | ||||
-rw-r--r-- | pkg/domain/infra/abi/pods_stats.go | 85 | ||||
-rw-r--r-- | pkg/domain/infra/abi/runtime.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/runtime_libpod.go | 24 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 8 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/images.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/manifest.go | 64 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/pods.go | 4 |
11 files changed, 357 insertions, 25 deletions
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 73a0d8ec3..286d37c34 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -217,12 +217,23 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { var ( + ctrs []*libpod.Container + err error reports []*entities.RestartReport ) - ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) - if err != nil { - return nil, err + + if options.Running { + ctrs, err = ic.Libpod.GetRunningContainers() + if err != nil { + return nil, err + } + } else { + ctrs, err = getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } } + for _, con := range ctrs { timeout := con.StopTimeout() if options.Timeout != nil { @@ -481,7 +492,7 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG if err := generate.CompleteSpec(ctx, ic.Libpod, s); err != nil { return nil, err } - ctr, err := generate.MakeContainer(ic.Libpod, s) + ctr, err := generate.MakeContainer(ctx, ic.Libpod, s) if err != nil { return nil, err } @@ -669,7 +680,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec); err != nil { return nil, err } - ctr, err := generate.MakeContainer(ic.Libpod, opts.Spec) + ctr, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec) if err != nil { return nil, err } @@ -837,7 +848,13 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin } for _, ctr := range ctrs { report := entities.ContainerInitReport{Id: ctr.ID()} - report.Err = ctr.Init(ctx) + err := ctr.Init(ctx) + + // If we're initializing all containers, ignore invalid state errors + if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { + err = nil + } + report.Err = err reports = append(reports, &report) } return reports, nil @@ -957,3 +974,10 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, o } return reports, nil } + +// Shutdown Libpod engine +func (ic *ContainerEngine) Shutdown(_ context.Context) { + shutdownSync.Do(func() { + _ = ic.Libpod.Shutdown(false) + }) +} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 8a2771a4c..724bc5343 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -326,16 +326,19 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if err != nil { return nil, err } - newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) - if err != nil { - return nil, errors.Wrap(err, "image loaded but no additional tags were created") - } - if len(opts.Name) > 0 { - if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { - return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + names := strings.Split(name, ",") + if len(names) <= 1 { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) + if err != nil { + return nil, errors.Wrap(err, "image loaded but no additional tags were created") + } + if len(opts.Name) > 0 { + if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { + return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + } } } - return &entities.ImageLoadReport{Name: name}, nil + return &entities.ImageLoadReport{Names: names}, nil } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { @@ -556,3 +559,10 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie return } + +// Shutdown Libpod engine +func (ir *ImageEngine) Shutdown(_ context.Context) { + shutdownSync.Do(func() { + _ = ir.Libpod.Shutdown(false) + }) +} diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go new file mode 100644 index 000000000..88331f96c --- /dev/null +++ b/pkg/domain/infra/abi/manifest.go @@ -0,0 +1,102 @@ +// +build ABISupport + +package abi + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + buildahUtil "github.com/containers/buildah/util" + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/transports/alltransports" + libpodImage "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/util" + + "github.com/pkg/errors" +) + +// ManifestCreate implements logic for creating manifest lists via ImageEngine +func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) { + fullNames, err := buildahUtil.ExpandNames(names, "", ir.Libpod.SystemContext(), ir.Libpod.GetStore()) + if err != nil { + return "", errors.Wrapf(err, "error encountered while expanding image name %q", names) + } + imageID, err := libpodImage.CreateManifestList(ir.Libpod.ImageRuntime(), *ir.Libpod.SystemContext(), fullNames, images, opts.All) + if err != nil { + return imageID, err + } + return imageID, err +} + +// ManifestInspect returns the content of a manifest list or image +func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { + dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) + _, err := alltransports.ParseImageName(name) + if err != nil { + _, err = alltransports.ParseImageName(dockerPrefix + name) + if err != nil { + return nil, errors.Errorf("invalid image reference %q", name) + } + } + image, err := ir.Libpod.ImageRuntime().New(ctx, name, "", "", nil, nil, libpodImage.SigningOptions{}, nil, util.PullImageMissing) + if err != nil { + return nil, errors.Wrapf(err, "reading image %q", name) + } + + list, err := image.InspectManifest() + if err != nil { + return nil, errors.Wrapf(err, "loading manifest %q", name) + } + buf, err := json.MarshalIndent(list, "", " ") + if err != nil { + return buf, errors.Wrapf(err, "error rendering manifest for display") + } + return buf, nil +} + +// ManifestAdd adds images to the manifest list +func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) { + imageSpec := opts.Images[0] + listImageSpec := opts.Images[1] + dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) + _, err := alltransports.ParseImageName(imageSpec) + if err != nil { + _, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, imageSpec)) + if err != nil { + return "", errors.Errorf("invalid image reference %q", imageSpec) + } + } + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(listImageSpec) + if err != nil { + return "", errors.Wrapf(err, "error retriving local image from image name %s", listImageSpec) + } + + manifestAddOpts := libpodImage.ManifestAddOpts{ + All: opts.All, + Arch: opts.Arch, + Features: opts.Features, + Images: opts.Images, + OS: opts.OS, + OSVersion: opts.OSVersion, + Variant: opts.Variant, + } + if len(opts.Annotation) != 0 { + annotations := make(map[string]string) + for _, annotationSpec := range opts.Annotation { + spec := strings.SplitN(annotationSpec, "=", 2) + if len(spec) != 2 { + return "", errors.Errorf("no value given for annotation %q", spec[0]) + } + annotations[spec[0]] = spec[1] + } + manifestAddOpts.Annotation = annotations + } + listID, err := listImage.AddManifest(*ir.Libpod.SystemContext(), manifestAddOpts) + if err != nil { + return listID, err + } + return listID, nil +} diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index c4ae9efbf..7c06f9a4e 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -145,7 +145,7 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt reports []*entities.PodStopReport ) pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) - if err != nil { + if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } for _, p := range pods { @@ -180,6 +180,7 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, errs, err := p.Restart(ctx) if err != nil { report.Errs = []error{err} + reports = append(reports, &report) continue } if len(errs) > 0 { @@ -207,6 +208,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op errs, err := p.Start(ctx) if err != nil { report.Errs = []error{err} + reports = append(reports, &report) continue } if len(errs) > 0 { @@ -226,7 +228,7 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio reports []*entities.PodRmReport ) pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) - if err != nil { + if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } for _, p := range pods { @@ -234,6 +236,7 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio err := ic.Libpod.RemovePod(ctx, p, true, options.Force) if err != nil { report.Err = err + reports = append(reports, &report) continue } reports = append(reports, &report) @@ -292,9 +295,12 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) { var ( + err error filters []libpod.PodFilter + pds []*libpod.Pod reports []*entities.ListPodsReport ) + for k, v := range options.Filters { for _, filter := range v { f, err := lpfilters.GeneratePodFilterFunc(k, filter) @@ -305,10 +311,19 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti } } - pds, err := ic.Libpod.Pods(filters...) - if err != nil { - return nil, err + if options.Latest { + pod, err := ic.Libpod.GetLatestPod() + if err != nil { + return nil, err + } + pds = append(pds, pod) + } else { + pds, err = ic.Libpod.Pods(filters...) + if err != nil { + return nil, err + } } + for _, p := range pds { var lpcs []*entities.ListPodContainer status, err := p.GetPodStatus() diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go new file mode 100644 index 000000000..a41c01da0 --- /dev/null +++ b/pkg/domain/infra/abi/pods_stats.go @@ -0,0 +1,85 @@ +package abi + +import ( + "context" + "fmt" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/cgroups" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/rootless" + "github.com/docker/go-units" + "github.com/pkg/errors" +) + +// PodStats implements printing stats about pods. +func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { + // Cgroups v2 check for rootless. + if rootless.IsRootless() { + unified, err := cgroups.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if !unified { + return nil, errors.New("pod stats is not supported in rootless mode without cgroups v2") + } + } + // Get the (running) pods and convert them to the entities format. + pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, errors.Wrap(err, "unable to get list of pods") + } + return ic.podsToStatsReport(pods) +} + +// podsToStatsReport converts a slice of pods into a corresponding slice of stats reports. +func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) { + reports := []*entities.PodStatsReport{} + for i := range pods { // Access by index to prevent potential loop-variable leaks. + podStats, err := pods[i].GetPodStats(nil) + if err != nil { + return nil, err + } + podID := pods[i].ID()[:12] + for j := range podStats { + r := entities.PodStatsReport{ + CPU: floatToPercentString(podStats[j].CPU), + MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), + Mem: floatToPercentString(podStats[j].MemPerc), + NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), + BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), + PIDS: pidsToString(podStats[j].PIDs), + CID: podStats[j].ContainerID[:12], + Name: podStats[j].Name, + Pod: podID, + } + reports = append(reports, &r) + } + } + + return reports, nil +} + +func combineHumanValues(a, b uint64) string { + if a == 0 && b == 0 { + return "-- / --" + } + return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) +} + +func floatToPercentString(f float64) string { + strippedFloat, err := libpod.RemoveScientificNotationFromFloat(f) + if err != nil || strippedFloat == 0 { + // If things go bazinga, return a safe value + return "--" + } + return fmt.Sprintf("%.2f", strippedFloat) + "%" +} + +func pidsToString(pid uint64) string { + if pid == 0 { + // If things go bazinga, return a safe value + return "--" + } + return fmt.Sprintf("%d", pid) +} diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go index 7394cadfc..fba422d8e 100644 --- a/pkg/domain/infra/abi/runtime.go +++ b/pkg/domain/infra/abi/runtime.go @@ -1,6 +1,8 @@ package abi import ( + "sync" + "github.com/containers/libpod/libpod" ) @@ -13,3 +15,5 @@ type ImageEngine struct { type ContainerEngine struct { Libpod *libpod.Runtime } + +var shutdownSync sync.Once diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index a6974d251..7c9180d43 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -6,6 +6,7 @@ import ( "context" "fmt" "os" + "sync" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/cgroups" @@ -18,6 +19,14 @@ import ( flag "github.com/spf13/pflag" ) +var ( + // runtimeSync only guards the non-specialized runtime + runtimeSync sync.Once + // The default GetRuntime() always returns the same object and error + runtimeLib *libpod.Runtime + runtimeErr error +) + type engineOpts struct { name string renumber bool @@ -63,13 +72,16 @@ func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, cfg *entities.Pod // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, flags *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) { - return getRuntime(ctx, flags, &engineOpts{ - renumber: false, - migrate: false, - noStore: false, - withFDS: true, - config: cfg, + runtimeSync.Do(func() { + runtimeLib, runtimeErr = getRuntime(ctx, flags, &engineOpts{ + renumber: false, + migrate: false, + noStore: false, + withFDS: true, + config: cfg, + }) }) + return runtimeLib, runtimeErr } // GetRuntimeNoStore generates a new libpod runtime configured by command line options diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 8867ce27f..32f9c4e36 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -115,11 +115,15 @@ func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []st t := int(*options.Timeout) timeout = &t } + ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err } for _, c := range ctrs { + if options.Running && c.State != define.ContainerStateRunning.String() { + continue + } reports = append(reports, &entities.RestartReport{ Id: c.ID, Err: containers.Restart(ic.ClientCxt, c.ID, timeout), @@ -379,3 +383,7 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, o func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) { return nil, errors.New("not implemented") } + +// Shutdown Libpod engine +func (ic *ContainerEngine) Shutdown(_ context.Context) { +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 27ed9f1ec..66e4e6e3f 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -260,3 +260,7 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { return nil, errors.New("not implemented yet") } + +// Shutdown Libpod engine +func (ir *ImageEngine) Shutdown(_ context.Context) { +} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go new file mode 100644 index 000000000..18b400533 --- /dev/null +++ b/pkg/domain/infra/tunnel/manifest.go @@ -0,0 +1,64 @@ +package tunnel + +import ( + "context" + "encoding/json" + "strings" + + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/bindings/manifests" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +// ManifestCreate implements manifest create via ImageEngine +func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) { + imageID, err := manifests.Create(ir.ClientCxt, names, images, &opts.All) + if err != nil { + return imageID, errors.Wrapf(err, "error creating manifest") + } + return imageID, err +} + +// ManifestInspect returns contents of manifest list with given name +func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { + list, err := manifests.Inspect(ir.ClientCxt, name) + if err != nil { + return nil, errors.Wrapf(err, "error getting content of manifest list or image %s", name) + } + + buf, err := json.MarshalIndent(list, "", " ") + if err != nil { + return buf, errors.Wrapf(err, "error rendering manifest for display") + } + return buf, err +} + +// ManifestAdd adds images to the manifest list +func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) { + manifestAddOpts := image.ManifestAddOpts{ + All: opts.All, + Arch: opts.Arch, + Features: opts.Features, + Images: opts.Images, + OS: opts.OS, + OSVersion: opts.OSVersion, + Variant: opts.Variant, + } + if len(opts.Annotation) != 0 { + annotations := make(map[string]string) + for _, annotationSpec := range opts.Annotation { + spec := strings.SplitN(annotationSpec, "=", 2) + if len(spec) != 2 { + return "", errors.Errorf("no value given for annotation %q", spec[0]) + } + annotations[spec[0]] = spec[1] + } + manifestAddOpts.Annotation = annotations + } + listID, err := manifests.Add(ctx, opts.Images[1], manifestAddOpts) + if err != nil { + return listID, errors.Wrapf(err, "error adding to manifest list %s", opts.Images[1]) + } + return listID, nil +} diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index e7641c077..c193c6752 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -211,3 +211,7 @@ func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodI } return pods.Inspect(ic.ClientCxt, options.NameOrID) } + +func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { + return pods.Stats(ic.ClientCxt, namesOrIds, options) +} |