summaryrefslogtreecommitdiff
path: root/pkg/domain/infra/abi
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/domain/infra/abi')
-rw-r--r--pkg/domain/infra/abi/containers.go7
-rw-r--r--pkg/domain/infra/abi/images.go115
-rw-r--r--pkg/domain/infra/abi/network.go258
-rw-r--r--pkg/domain/infra/abi/pods.go4
-rw-r--r--pkg/domain/infra/abi/system.go38
-rw-r--r--pkg/domain/infra/abi/volumes.go6
6 files changed, 361 insertions, 67 deletions
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index bb7f0118d..249e8147c 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -194,6 +194,10 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.
filterFuncs = append(filterFuncs, generatedFunc)
}
}
+ return ic.pruneContainersHelper(ctx, filterFuncs)
+}
+
+func (ic *ContainerEngine) pruneContainersHelper(ctx context.Context, filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) {
prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs)
if err != nil {
return nil, err
@@ -524,7 +528,8 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string,
}
// If the container is in a pod, also set to recursively start dependencies
- if err := terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach {
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "")
+ if err != nil && errors.Cause(err) != define.ErrDetach {
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
}
return nil
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index be788b2bf..7ab5131f0 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -21,7 +21,6 @@ import (
domainUtils "github.com/containers/libpod/pkg/domain/utils"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
- "github.com/hashicorp/go-multierror"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -36,7 +35,11 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
}
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
- results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
+ return ir.pruneImagesHelper(ctx, opts.All, opts.Filter)
+}
+
+func (ir *ImageEngine) pruneImagesHelper(ctx context.Context, all bool, filters []string) (*entities.ImagePruneReport, error) {
+ results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, all, filters)
if err != nil {
return nil, err
}
@@ -419,8 +422,10 @@ func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.
return &entities.ImageTreeReport{Tree: results}, nil
}
-// Remove removes one or more images from local storage.
-func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, finalError error) {
+// removeErrorsToExitCode returns an exit code for the specified slice of
+// image-removal errors. The error codes are set according to the documented
+// behaviour in the Podman man pages.
+func removeErrorsToExitCode(rmErrors []error) int {
var (
// noSuchImageErrors indicates that at least one image was not found.
noSuchImageErrors bool
@@ -430,59 +435,53 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
// otherErrors indicates that at least one error other than the two
// above occured.
otherErrors bool
- // deleteError is a multierror to conveniently collect errors during
- // removal. We really want to delete as many images as possible and not
- // error out immediately.
- deleteError *multierror.Error
)
- report = &entities.ImageRemoveReport{}
+ if len(rmErrors) == 0 {
+ return 0
+ }
- // Set the removalCode and the error after all work is done.
- defer func() {
- switch {
- // 2
- case inUseErrors:
- // One of the specified images has child images or is
- // being used by a container.
- report.ExitCode = 2
- // 1
- case noSuchImageErrors && !(otherErrors || inUseErrors):
- // One of the specified images did not exist, and no other
- // failures.
- report.ExitCode = 1
- // 0
+ for _, e := range rmErrors {
+ switch errors.Cause(e) {
+ case define.ErrNoSuchImage:
+ noSuchImageErrors = true
+ case define.ErrImageInUse, storage.ErrImageUsedByContainer:
+ inUseErrors = true
default:
- // Nothing to do.
- }
- if deleteError != nil {
- // go-multierror has a trailing new line which we need to remove to normalize the string.
- finalError = deleteError.ErrorOrNil()
- finalError = errors.New(strings.TrimSpace(finalError.Error()))
+ otherErrors = true
}
+ }
+
+ switch {
+ case inUseErrors:
+ // One of the specified images has child images or is
+ // being used by a container.
+ return 2
+ case noSuchImageErrors && !(otherErrors || inUseErrors):
+ // One of the specified images did not exist, and no other
+ // failures.
+ return 1
+ default:
+ return 125
+ }
+}
+
+// Remove removes one or more images from local storage.
+func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, rmErrors []error) {
+ report = &entities.ImageRemoveReport{}
+
+ // Set the exit code at very end.
+ defer func() {
+ report.ExitCode = removeErrorsToExitCode(rmErrors)
}()
// deleteImage is an anonymous function to conveniently delete an image
// without having to pass all local data around.
deleteImage := func(img *image.Image) error {
results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
- switch errors.Cause(err) {
- case nil:
- break
- case define.ErrNoSuchImage:
- inUseErrors = true // ExitCode is expected
- case storage.ErrImageUsedByContainer:
- inUseErrors = true // Important for exit codes in Podman.
- return errors.New(
- fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
- case define.ErrImageInUse:
- inUseErrors = true
- return err
- default:
- otherErrors = true // Important for exit codes in Podman.
+ if err != nil {
return err
}
-
report.Deleted = append(report.Deleted, results.Deleted)
report.Untagged = append(report.Untagged, results.Untagged...)
return nil
@@ -495,9 +494,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
for {
storageImages, err := ir.Libpod.ImageRuntime().GetRWImages()
if err != nil {
- deleteError = multierror.Append(deleteError,
- errors.Wrapf(err, "unable to query local images"))
- otherErrors = true // Important for exit codes in Podman.
+ rmErrors = append(rmErrors, err)
return
}
// No images (left) to remove, so we're done.
@@ -506,9 +503,7 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
}
// Prevent infinity loops by making a delete-progress check.
if previousImages == len(storageImages) {
- otherErrors = true // Important for exit codes in Podman.
- deleteError = multierror.Append(deleteError,
- errors.New("unable to delete all images, check errors and re-run image removal if needed"))
+ rmErrors = append(rmErrors, errors.New("unable to delete all images, check errors and re-run image removal if needed"))
break
}
previousImages = len(storageImages)
@@ -516,15 +511,15 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
for _, img := range storageImages {
isParent, err := img.IsParent(ctx)
if err != nil {
- otherErrors = true // Important for exit codes in Podman.
- deleteError = multierror.Append(deleteError, err)
+ rmErrors = append(rmErrors, err)
+ continue
}
// Skip parent images.
if isParent {
continue
}
if err := deleteImage(img); err != nil {
- deleteError = multierror.Append(deleteError, err)
+ rmErrors = append(rmErrors, err)
}
}
}
@@ -535,21 +530,13 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
// Delete only the specified images.
for _, id := range images {
img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
- switch errors.Cause(err) {
- case nil:
- break
- case image.ErrNoSuchImage:
- noSuchImageErrors = true // Important for exit codes in Podman.
- fallthrough
- default:
- deleteError = multierror.Append(deleteError, errors.Wrapf(err, "failed to remove image '%s'", id))
+ if err != nil {
+ rmErrors = append(rmErrors, err)
continue
}
-
err = deleteImage(img)
if err != nil {
- otherErrors = true // Important for exit codes in Podman.
- deleteError = multierror.Append(deleteError, err)
+ rmErrors = append(rmErrors, err)
}
}
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
new file mode 100644
index 000000000..5c39b5374
--- /dev/null
+++ b/pkg/domain/infra/abi/network.go
@@ -0,0 +1,258 @@
+package abi
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+
+ cniversion "github.com/containernetworking/cni/pkg/version"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+func getCNIConfDir(r *libpod.Runtime) (string, error) {
+ config, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ configPath := config.Network.NetworkConfigDir
+
+ if len(config.Network.NetworkConfigDir) < 1 {
+ configPath = network.CNIConfigDir
+ }
+ return configPath, nil
+}
+
+func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) {
+ var reports []*entities.NetworkListReport
+ cniConfigPath, err := getCNIConfDir(ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ networks, err := network.LoadCNIConfsFromDir(cniConfigPath)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, n := range networks {
+ reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n})
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) {
+ var (
+ rawCNINetworks []entities.NetworkInspectReport
+ )
+ for _, name := range namesOrIds {
+ rawList, err := network.InspectNetwork(name)
+ if err != nil {
+ return nil, err
+ }
+ rawCNINetworks = append(rawCNINetworks, rawList)
+ }
+ return rawCNINetworks, nil
+}
+
+func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
+ var reports []*entities.NetworkRmReport
+ for _, name := range namesOrIds {
+ report := entities.NetworkRmReport{Name: name}
+ containers, err := ic.Libpod.GetAllContainers()
+ if err != nil {
+ return reports, err
+ }
+ // We need to iterate containers looking to see if they belong to the given network
+ for _, c := range containers {
+ if util.StringInSlice(name, c.Config().Networks) {
+ // if user passes force, we nuke containers
+ if !options.Force {
+ // Without the force option, we return an error
+ return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers", name)
+ }
+ if err := ic.Libpod.RemoveContainer(ctx, c, true, true); err != nil {
+ return reports, err
+ }
+ }
+ }
+ if err := network.RemoveNetwork(name); err != nil {
+ report.Err = err
+ }
+ reports = append(reports, &report)
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) {
+ var (
+ err error
+ fileName string
+ )
+ if len(options.MacVLAN) > 0 {
+ fileName, err = createMacVLAN(ic.Libpod, name, options)
+ } else {
+ fileName, err = createBridge(ic.Libpod, name, options)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return &entities.NetworkCreateReport{Filename: fileName}, nil
+}
+
+// createBridge creates a CNI network
+func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ isGateway := true
+ ipMasq := true
+ subnet := &options.Subnet
+ ipRange := options.Range
+ runtimeConfig, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ // if range is provided, make sure it is "in" network
+ if subnet.IP != nil {
+ // if network is provided, does it conflict with existing CNI or live networks
+ err = network.ValidateUserNetworkIsAvailable(subnet)
+ } else {
+ // if no network is provided, figure out network
+ subnet, err = network.GetFreeNetwork()
+ }
+ if err != nil {
+ return "", err
+ }
+ gateway := options.Gateway
+ if gateway == nil {
+ // if no gateway is provided, provide it as first ip of network
+ gateway = network.CalcGatewayIP(subnet)
+ }
+ // if network is provided and if gateway is provided, make sure it is "in" network
+ if options.Subnet.IP != nil && options.Gateway != nil {
+ if !subnet.Contains(gateway) {
+ return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String())
+ }
+ }
+ if options.Internal {
+ isGateway = false
+ ipMasq = false
+ }
+
+ // if a range is given, we need to ensure it is "in" the network range.
+ if options.Range.IP != nil {
+ if options.Subnet.IP == nil {
+ return "", errors.New("you must define a subnet range to define an ip-range")
+ }
+ firstIP, err := network.FirstIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ lastIP, err := network.LastIPInSubnet(&options.Range)
+ if err != nil {
+ return "", err
+ }
+ if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) {
+ return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String())
+ }
+ }
+ bridgeDeviceName, err := network.GetFreeDeviceName()
+ if err != nil {
+ return "", err
+ }
+
+ if len(name) > 0 {
+ netNames, err := network.GetNetworkNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ // If no name is given, we give the name of the bridge device
+ name = bridgeDeviceName
+ }
+
+ ncList := network.NewNcList(name, cniversion.Current())
+ var plugins []network.CNIPlugins
+ var routes []network.IPAMRoute
+
+ defaultRoute, err := network.NewIPAMDefaultRoute()
+ if err != nil {
+ return "", err
+ }
+ routes = append(routes, defaultRoute)
+ ipamConfig, err := network.NewIPAMHostLocalConf(subnet, routes, ipRange, gateway)
+ if err != nil {
+ return "", err
+ }
+
+ // TODO need to iron out the role of isDefaultGW and IPMasq
+ bridge := network.NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig)
+ plugins = append(plugins, bridge)
+ plugins = append(plugins, network.NewPortMapPlugin())
+ plugins = append(plugins, network.NewFirewallPlugin())
+ // if we find the dnsname plugin, we add configuration for it
+ if network.HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
+ }
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniConfigPath, err := getCNIConfDir(r)
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
+
+func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) {
+ var (
+ plugins []network.CNIPlugins
+ )
+ liveNetNames, err := network.GetLiveNetworkNames()
+ if err != nil {
+ return "", err
+ }
+ // Make sure the host-device exists
+ if !util.StringInSlice(options.MacVLAN, liveNetNames) {
+ return "", errors.Errorf("failed to find network interface %q", options.MacVLAN)
+ }
+ if len(name) > 0 {
+ netNames, err := network.GetNetworkNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
+ if util.StringInSlice(name, netNames) {
+ return "", errors.Errorf("the network name %s is already used", name)
+ }
+ } else {
+ name, err = network.GetFreeDeviceName()
+ if err != nil {
+ return "", err
+ }
+ }
+ ncList := network.NewNcList(name, cniversion.Current())
+ macvlan := network.NewMacVLANPlugin(options.MacVLAN)
+ plugins = append(plugins, macvlan)
+ ncList["plugins"] = plugins
+ b, err := json.MarshalIndent(ncList, "", " ")
+ if err != nil {
+ return "", err
+ }
+ cniConfigPath, err := getCNIConfDir(r)
+ if err != nil {
+ return "", err
+ }
+ cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name))
+ err = ioutil.WriteFile(cniPathName, b, 0644)
+ return cniPathName, err
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index b286bcf0d..16c222cbd 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -243,6 +243,10 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
}
func (ic *ContainerEngine) PodPrune(ctx context.Context, options entities.PodPruneOptions) ([]*entities.PodPruneReport, error) {
+ return ic.prunePodHelper(ctx)
+}
+
+func (ic *ContainerEngine) prunePodHelper(ctx context.Context) ([]*entities.PodPruneReport, error) {
var (
reports []*entities.PodPruneReport
)
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index e5c109ee6..ab1b282d8 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -175,3 +175,41 @@ func setUMask() { // nolint:deadcode,unused
func checkInput() error { // nolint:deadcode,unused
return nil
}
+
+// SystemPrune removes unsed data from the system. Pruning pods, containers, volumes and images.
+func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
+ var systemPruneReport = new(entities.SystemPruneReport)
+ podPruneReport, err := ic.prunePodHelper(ctx)
+ if err != nil {
+ return nil, err
+ }
+ systemPruneReport.PodPruneReport = podPruneReport
+
+ containerPruneReport, err := ic.pruneContainersHelper(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ systemPruneReport.ContainerPruneReport = containerPruneReport
+
+ results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, nil)
+ if err != nil {
+ return nil, err
+ }
+ report := entities.ImagePruneReport{
+ Report: entities.Report{
+ Id: results,
+ Err: nil,
+ },
+ }
+
+ systemPruneReport.ImagePruneReport = &report
+
+ if options.Volume {
+ volumePruneReport, err := ic.pruneVolumesHelper(ctx)
+ if err != nil {
+ return nil, err
+ }
+ systemPruneReport.VolumePruneReport = volumePruneReport
+ }
+ return systemPruneReport, nil
+}
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index bdae4359d..91b2440df 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
@@ -113,6 +111,10 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
}
func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+ return ic.pruneVolumesHelper(ctx)
+}
+
+func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) {
var (
reports []*entities.VolumePruneReport
)