summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/completion.go6
-rw-r--r--cmd/podman/images/build.go75
-rw-r--r--cmd/podman/networks/prune.go82
-rw-r--r--docs/source/markdown/podman-network-prune.1.md31
-rw-r--r--docs/source/markdown/podman-network.1.md1
-rw-r--r--docs/source/network.rst2
-rw-r--r--go.mod3
-rw-r--r--go.sum5
-rw-r--r--libpod/network/network.go50
-rw-r--r--pkg/api/handlers/compat/images_build.go183
-rw-r--r--pkg/api/handlers/compat/networks.go22
-rw-r--r--pkg/api/handlers/compat/swagger.go7
-rw-r--r--pkg/api/handlers/libpod/networks.go14
-rw-r--r--pkg/api/server/register_networks.go66
-rw-r--r--pkg/bindings/images/build.go154
-rw-r--r--pkg/bindings/network/network.go18
-rw-r--r--pkg/bindings/network/types.go6
-rw-r--r--pkg/bindings/network/types_prune_options.go75
-rw-r--r--pkg/domain/entities/engine_container.go1
-rw-r--r--pkg/domain/entities/network.go12
-rw-r--r--pkg/domain/infra/abi/images.go17
-rw-r--r--pkg/domain/infra/abi/network.go25
-rw-r--r--pkg/domain/infra/tunnel/images.go11
-rw-r--r--pkg/domain/infra/tunnel/network.go5
-rw-r--r--test/apiv2/rest_api/test_rest_v2_0_0.py2
-rw-r--r--test/e2e/build_test.go53
-rw-r--r--test/e2e/network_test.go50
-rw-r--r--test/e2e/rmi_test.go30
-rw-r--r--vendor/github.com/containers/buildah/CHANGELOG.md8
-rw-r--r--vendor/github.com/containers/buildah/buildah.go2
-rw-r--r--vendor/github.com/containers/buildah/changelog.txt8
-rw-r--r--vendor/github.com/containers/buildah/commit.go22
-rw-r--r--vendor/github.com/containers/buildah/go.mod2
-rw-r--r--vendor/github.com/containers/buildah/go.sum4
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/executor.go4
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/stage_executor.go3
-rw-r--r--vendor/github.com/containers/buildah/run_linux.go2
-rw-r--r--vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go301
-rw-r--r--vendor/modules.txt3
39 files changed, 1193 insertions, 172 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index bddea524d..ff15983cd 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -463,6 +463,12 @@ func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string)
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) < 1 {
+ // check if the rootfs flag is set
+ // if it is set to true provide directory completion
+ rootfs, err := cmd.Flags().GetBool("rootfs")
+ if err == nil && rootfs {
+ return nil, cobra.ShellCompDirectiveFilterDirs
+ }
return getImages(cmd, toComplete)
}
// TODO: add path completion for files in the image
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index 1f06dace9..895bdb631 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -1,9 +1,11 @@
package images
import (
+ "io"
"os"
"path/filepath"
"strings"
+ "time"
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
@@ -11,6 +13,8 @@ import (
"github.com/containers/buildah/pkg/parse"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
+ encconfig "github.com/containers/ocicrypt/config"
+ enchelpers "github.com/containers/ocicrypt/helpers"
"github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
@@ -78,7 +82,8 @@ func useLayers() string {
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
- Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+
Command: buildCmd,
})
buildFlags(buildCmd)
@@ -151,8 +156,21 @@ func buildFlags(cmd *cobra.Command) {
// Add the completion functions
fromAndBudFlagsCompletions := buildahCLI.GetFromAndBudFlagsCompletions()
completion.CompleteCommandFlags(cmd, fromAndBudFlagsCompletions)
- _ = flags.MarkHidden("signature-policy")
flags.SetNormalizeFunc(buildahCLI.AliasFlags)
+ if registry.IsRemote() {
+ flag = flags.Lookup("isolation")
+ buildOpts.Isolation = buildah.OCI
+ if err := flag.Value.Set(buildah.OCI); err != nil {
+ logrus.Errorf("unable to set --isolation to %v: %v", buildah.OCI, err)
+ }
+ flag.DefValue = buildah.OCI
+ _ = flags.MarkHidden("disable-content-trust")
+ _ = flags.MarkHidden("cache-from")
+ _ = flags.MarkHidden("sign-by")
+ _ = flags.MarkHidden("signature-policy")
+ _ = flags.MarkHidden("tls-verify")
+ _ = flags.MarkHidden("compress")
+ }
}
// build executes the build command.
@@ -246,7 +264,18 @@ func build(cmd *cobra.Command, args []string) error {
return err
}
- _, err = registry.ImageEngine().Build(registry.GetContext(), containerFiles, *apiBuildOpts)
+ report, err := registry.ImageEngine().Build(registry.GetContext(), containerFiles, *apiBuildOpts)
+
+ if cmd.Flag("iidfile").Changed {
+ f, err := os.Create(buildOpts.Iidfile)
+ if err != nil {
+ return err
+ }
+ if _, err := f.WriteString("sha256:" + report.ID); err != nil {
+ return err
+ }
+ }
+
return err
}
@@ -308,6 +337,10 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
flags.Layers = false
}
+ var stdin io.Reader
+ if flags.Stdin {
+ stdin = os.Stdin
+ }
var stdout, stderr, reporter *os.File
stdout = os.Stdout
stderr = os.Stderr
@@ -402,10 +435,21 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
runtimeFlags = append(runtimeFlags, "--systemd-cgroup")
}
+ imageOS, arch, err := parse.PlatformFromOptions(c)
+ if err != nil {
+ return nil, err
+ }
+
+ decConfig, err := getDecryptConfig(flags.DecryptionKeys)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to obtain decrypt config")
+ }
+
opts := imagebuildah.BuildOptions{
AddCapabilities: flags.CapAdd,
AdditionalTags: tags,
Annotations: flags.Annotation,
+ Architecture: arch,
Args: args,
BlobDirectory: flags.BlobCache,
CNIConfigDir: flags.CNIConfigDir,
@@ -433,17 +477,25 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
DropCapabilities: flags.CapDrop,
Err: stderr,
ForceRmIntermediateCtrs: flags.ForceRm,
+ From: flags.From,
IDMappingOptions: idmappingOptions,
- IIDFile: flags.Iidfile,
+ In: stdin,
Isolation: isolation,
+ Jobs: &flags.Jobs,
Labels: flags.Label,
Layers: flags.Layers,
+ LogRusage: flags.LogRusage,
+ Manifest: flags.Manifest,
+ MaxPullPushRetries: 3,
NamespaceOptions: nsValues,
NoCache: flags.NoCache,
+ OS: imageOS,
+ OciDecryptConfig: decConfig,
Out: stdout,
Output: output,
OutputFormat: format,
PullPolicy: pullPolicy,
+ PullPushRetryDelay: 2 * time.Second,
Quiet: flags.Quiet,
RemoveIntermediateCtrs: flags.Rm,
ReportWriter: reporter,
@@ -459,3 +511,18 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil
return &entities.BuildOptions{BuildOptions: opts}, nil
}
+
+func getDecryptConfig(decryptionKeys []string) (*encconfig.DecryptConfig, error) {
+ decConfig := &encconfig.DecryptConfig{}
+ if len(decryptionKeys) > 0 {
+ // decryption
+ dcc, err := enchelpers.CreateCryptoConfig([]string{}, decryptionKeys)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid decryption keys")
+ }
+ cc := encconfig.CombineCryptoConfigs([]encconfig.CryptoConfig{dcc})
+ decConfig = cc.DecryptConfig
+ }
+
+ return decConfig, nil
+}
diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go
new file mode 100644
index 000000000..d6c7d3a7f
--- /dev/null
+++ b/cmd/podman/networks/prune.go
@@ -0,0 +1,82 @@
+package network
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/podman/v2/cmd/podman/common"
+ "github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/cmd/podman/validate"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ networkPruneDescription = `Prune unused networks`
+ networkPruneCommand = &cobra.Command{
+ Use: "prune [options]",
+ Short: "network prune",
+ Long: networkPruneDescription,
+ RunE: networkPrune,
+ Example: `podman network prune`,
+ Args: validate.NoArgs,
+ ValidArgsFunction: common.AutocompleteNetworks,
+ }
+)
+
+var (
+ networkPruneOptions entities.NetworkPruneOptions
+ force bool
+)
+
+func networkPruneFlags(flags *pflag.FlagSet) {
+ //TODO: Not implemented but for future reference
+ //flags.StringSliceVar(&networkPruneOptions.Filters,"filters", []string{}, "provide filter values (e.g. 'until=<timestamp>')")
+ flags.BoolVarP(&force, "force", "f", false, "do not prompt for confirmation")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: networkPruneCommand,
+ Parent: networkCmd,
+ })
+ flags := networkPruneCommand.Flags()
+ networkPruneFlags(flags)
+}
+
+func networkPrune(cmd *cobra.Command, _ []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ if !force {
+ reader := bufio.NewReader(os.Stdin)
+ fmt.Println("WARNING! This will remove all networks not used by at least one container.")
+ fmt.Print("Are you sure you want to continue? [y/N] ")
+ answer, err := reader.ReadString('\n')
+ if err != nil {
+ return err
+ }
+ if strings.ToLower(answer)[0] != 'y' {
+ return nil
+ }
+ }
+ responses, err := registry.ContainerEngine().NetworkPrune(registry.Context(), networkPruneOptions)
+ if err != nil {
+ setExitCode(err)
+ return err
+ }
+ for _, r := range responses {
+ if r.Error == nil {
+ fmt.Println(r.Name)
+ } else {
+ setExitCode(r.Error)
+ errs = append(errs, r.Error)
+ }
+ }
+ return errs.PrintErrors()
+}
diff --git a/docs/source/markdown/podman-network-prune.1.md b/docs/source/markdown/podman-network-prune.1.md
new file mode 100644
index 000000000..af0a7295d
--- /dev/null
+++ b/docs/source/markdown/podman-network-prune.1.md
@@ -0,0 +1,31 @@
+% podman-network-prune(1)
+
+## NAME
+podman\-network\-prune - Remove all unused networks
+
+## SYNOPSIS
+**podman network prune** [*options*]
+
+## DESCRIPTION
+Remove all unused networks. An unused network is defined by a network which
+has no containers connected or configured to connect to it. It will not remove
+the so-called default network which goes by the name of *podman*.
+
+## OPTIONS
+#### **--force**, **-f**
+
+Do not prompt for confirmation
+
+## EXAMPLE
+Prune networks
+
+```
+podman network prune
+```
+
+
+## SEE ALSO
+podman(1), podman-network(1), podman-network-remove(1)
+
+## HISTORY
+February 2021, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-network.1.md b/docs/source/markdown/podman-network.1.md
index 3ad37b8bf..885c957b6 100644
--- a/docs/source/markdown/podman-network.1.md
+++ b/docs/source/markdown/podman-network.1.md
@@ -19,6 +19,7 @@ The network command manages CNI networks for Podman.
| exists | [podman-network-exists(1)](podman-network-exists.1.md) | Check if the given network exists |
| inspect | [podman-network-inspect(1)](podman-network-inspect.1.md) | Displays the raw CNI network configuration for one or more networks |
| ls | [podman-network-ls(1)](podman-network-ls.1.md) | Display a summary of CNI networks |
+| prune | [podman-network-prune(1)](podman-network-prune.1.md) | Remove all unused networks |
| reload | [podman-network-reload(1)](podman-network-reload.1.md) | Reload network configuration for containers |
| rm | [podman-network-rm(1)](podman-network-rm.1.md) | Remove one or more CNI networks |
diff --git a/docs/source/network.rst b/docs/source/network.rst
index b5829876e..eb0c2c7f9 100644
--- a/docs/source/network.rst
+++ b/docs/source/network.rst
@@ -13,6 +13,8 @@ Network
:doc:`ls <markdown/podman-network-ls.1>` network list
+:doc:`prune <markdown/podman-network-prune.1>` network prune
+
:doc:`reload <markdown/podman-network-reload.1>` network reload
:doc:`rm <markdown/podman-network-rm.1>` network rm
diff --git a/go.mod b/go.mod
index e3ac45844..f36134146 100644
--- a/go.mod
+++ b/go.mod
@@ -10,10 +10,11 @@ require (
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/containernetworking/cni v0.8.1
github.com/containernetworking/plugins v0.9.0
- github.com/containers/buildah v1.19.3
+ github.com/containers/buildah v1.19.4
github.com/containers/common v0.34.3-0.20210208115708-8668c76dd577
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.10.1
+ github.com/containers/ocicrypt v1.0.3
github.com/containers/psgo v1.5.2
github.com/containers/storage v1.25.0
github.com/coreos/go-systemd/v22 v22.1.0
diff --git a/go.sum b/go.sum
index 93c5cc6fa..2365581a4 100644
--- a/go.sum
+++ b/go.sum
@@ -89,7 +89,6 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
-github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI=
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI=
@@ -97,8 +96,8 @@ github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ
github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0=
github.com/containernetworking/plugins v0.9.0 h1:c+1gegKhR7+d0Caum9pEHugZlyhXPOG6v3V6xJgIGCI=
github.com/containernetworking/plugins v0.9.0/go.mod h1:dbWv4dI0QrBGuVgj+TuVQ6wJRZVOhrCQj91YyC92sxg=
-github.com/containers/buildah v1.19.3 h1:U0E1UKzqW5C11W7giHhLZI06xkZiV40ZKDK/c1jotbE=
-github.com/containers/buildah v1.19.3/go.mod h1:uZb6GuE36tmRSOcIXGfiYqdpr+GPXWmlUIJSk5sn19w=
+github.com/containers/buildah v1.19.4 h1:TygMnZAt8JCQ0i1APbSHfdn69B2vGvPoJKD+f6D6fuA=
+github.com/containers/buildah v1.19.4/go.mod h1:PfK0EiB871UFD1CT8xNsKq60s7xw2pgSOEGICf+x6O8=
github.com/containers/common v0.33.1 h1:XpDiq8Cta8+u1s4kpYSEWdB140ZmqgyIXfWkLqKx3z0=
github.com/containers/common v0.33.1/go.mod h1:mjDo/NKeweL/onaspLhZ38WnHXaYmrELHclIdvSnYpY=
github.com/containers/common v0.34.3-0.20210208115708-8668c76dd577 h1:tUJcLouJ1bC3w9gdqgKqZBsj2uCuM8D8jSR592lxbhE=
diff --git a/libpod/network/network.go b/libpod/network/network.go
index 0ff14c1f7..cdaef6c13 100644
--- a/libpod/network/network.go
+++ b/libpod/network/network.go
@@ -11,6 +11,7 @@ import (
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/libpod/define"
+ "github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/pkg/errors"
@@ -174,14 +175,9 @@ func ValidateUserNetworkIsAvailable(config *config.Config, userNet *net.IPNet) e
return nil
}
-// RemoveNetwork removes a given network by name. If the network has container associated with it, that
-// must be handled outside the context of this.
-func RemoveNetwork(config *config.Config, name string) error {
- l, err := acquireCNILock(config)
- if err != nil {
- return err
- }
- defer l.releaseCNILock()
+// removeNetwork is removes a cni network without a lock and should only be called
+// when a lock was otherwise acquired.
+func removeNetwork(config *config.Config, name string) error {
cniPath, err := GetCNIConfigPathByNameOrID(config, name)
if err != nil {
return err
@@ -213,6 +209,17 @@ func RemoveNetwork(config *config.Config, name string) error {
return nil
}
+// RemoveNetwork removes a given network by name. If the network has container associated with it, that
+// must be handled outside the context of this.
+func RemoveNetwork(config *config.Config, name string) error {
+ l, err := acquireCNILock(config)
+ if err != nil {
+ return err
+ }
+ defer l.releaseCNILock()
+ return removeNetwork(config, name)
+}
+
// InspectNetwork reads a CNI config and returns its configuration
func InspectNetwork(config *config.Config, name string) (map[string]interface{}, error) {
b, err := ReadRawCNIConfByName(config, name)
@@ -243,3 +250,30 @@ func GetNetworkID(name string) string {
hash := sha256.Sum256([]byte(name))
return hex.EncodeToString(hash[:])
}
+
+// PruneNetworks removes networks that are not being used and that is not the default
+// network. To keep proper fencing for imports, you must provide the used networks
+// to this function as a map. the key is meaningful in the map, the book is a no-op
+func PruneNetworks(rtc *config.Config, usedNetworks map[string]bool) ([]*entities.NetworkPruneReport, error) {
+ var reports []*entities.NetworkPruneReport
+ lock, err := acquireCNILock(rtc)
+ if err != nil {
+ return nil, err
+ }
+ defer lock.releaseCNILock()
+ nets, err := GetNetworkNamesFromFileSystem(rtc)
+ if err != nil {
+ return nil, err
+ }
+ for _, n := range nets {
+ _, found := usedNetworks[n]
+ // Remove is not default network and not found in the used list
+ if n != rtc.Network.DefaultNetwork && !found {
+ reports = append(reports, &entities.NetworkPruneReport{
+ Name: n,
+ Error: removeNetwork(rtc, n),
+ })
+ }
+ }
+ return reports, nil
+}
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 415ff85cd..0f27a090f 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -60,29 +60,39 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}()
query := struct {
- BuildArgs string `schema:"buildargs"`
- CacheFrom string `schema:"cachefrom"`
- CpuPeriod uint64 `schema:"cpuperiod"` // nolint
- CpuQuota int64 `schema:"cpuquota"` // nolint
- CpuSetCpus string `schema:"cpusetcpus"` // nolint
- CpuShares uint64 `schema:"cpushares"` // nolint
- Dockerfile string `schema:"dockerfile"`
- ExtraHosts string `schema:"extrahosts"`
- ForceRm bool `schema:"forcerm"`
- HTTPProxy bool `schema:"httpproxy"`
- Labels string `schema:"labels"`
- Layers bool `schema:"layers"`
- MemSwap int64 `schema:"memswap"`
- Memory int64 `schema:"memory"`
- NetworkMode string `schema:"networkmode"`
- NoCache bool `schema:"nocache"`
- Outputs string `schema:"outputs"`
- Platform string `schema:"platform"`
- Pull bool `schema:"pull"`
- Quiet bool `schema:"q"`
- Registry string `schema:"registry"`
- Remote string `schema:"remote"`
- Rm bool `schema:"rm"`
+ AddHosts string `schema:"extrahosts"`
+ AdditionalCapabilities string `schema:"addcaps"`
+ Annotations string `schema:"annotations"`
+ BuildArgs string `schema:"buildargs"`
+ CacheFrom string `schema:"cachefrom"`
+ ConfigureNetwork int64 `schema:"networkmode"`
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
+ Devices string `schema:"devices"`
+ Dockerfile string `schema:"dockerfile"`
+ DropCapabilities string `schema:"dropcaps"`
+ ForceRm bool `schema:"forcerm"`
+ From string `schema:"from"`
+ HTTPProxy bool `schema:"httpproxy"`
+ Isolation int64 `schema:"isolation"`
+ Jobs uint64 `schema:"jobs"` // nolint
+ Labels string `schema:"labels"`
+ Layers bool `schema:"layers"`
+ LogRusage bool `schema:"rusage"`
+ Manifest string `schema:"manifest"`
+ MemSwap int64 `schema:"memswap"`
+ Memory int64 `schema:"memory"`
+ NoCache bool `schema:"nocache"`
+ OutputFormat string `schema:"outputformat"`
+ Platform string `schema:"platform"`
+ Pull bool `schema:"pull"`
+ Quiet bool `schema:"q"`
+ Registry string `schema:"registry"`
+ Rm bool `schema:"rm"`
+ //FIXME SecurityOpt in remote API is not handled
+ SecurityOpt string `schema:"securityopt"`
ShmSize int `schema:"shmsize"`
Squash bool `schema:"squash"`
Tag []string `schema:"t"`
@@ -101,14 +111,57 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
return
}
+ // convert label formats
+ var addCaps = []string{}
+ if _, found := r.URL.Query()["addcaps"]; found {
+ var m = []string{}
+ if err := json.Unmarshal([]byte(query.AdditionalCapabilities), &m); err != nil {
+ utils.BadRequest(w, "addcaps", query.AdditionalCapabilities, err)
+ return
+ }
+ addCaps = m
+ }
+ addhosts := []string{}
+ if _, found := r.URL.Query()["extrahosts"]; found {
+ if err := json.Unmarshal([]byte(query.AddHosts), &addhosts); err != nil {
+ utils.BadRequest(w, "extrahosts", query.AddHosts, err)
+ return
+ }
+ }
+
+ // convert label formats
+ var dropCaps = []string{}
+ if _, found := r.URL.Query()["dropcaps"]; found {
+ var m = []string{}
+ if err := json.Unmarshal([]byte(query.DropCapabilities), &m); err != nil {
+ utils.BadRequest(w, "dropcaps", query.DropCapabilities, err)
+ return
+ }
+ dropCaps = m
+ }
+
+ // convert label formats
+ var devices = []string{}
+ if _, found := r.URL.Query()["devices"]; found {
+ var m = []string{}
+ if err := json.Unmarshal([]byte(query.DropCapabilities), &m); err != nil {
+ utils.BadRequest(w, "devices", query.DropCapabilities, err)
+ return
+ }
+ devices = m
+ }
+
var output string
if len(query.Tag) > 0 {
output = query.Tag[0]
}
-
- var additionalNames []string
+ format := buildah.Dockerv2ImageManifest
+ if utils.IsLibpodRequest(r) {
+ format = query.OutputFormat
+ }
+ var additionalTags []string
if len(query.Tag) > 1 {
- additionalNames = query.Tag[1:]
+ additionalTags = query.Tag[1:]
}
var buildArgs = map[string]string{}
@@ -120,17 +173,21 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}
// convert label formats
+ var annotations = []string{}
+ if _, found := r.URL.Query()["annotations"]; found {
+ if err := json.Unmarshal([]byte(query.Annotations), &annotations); err != nil {
+ utils.BadRequest(w, "annotations", query.Annotations, err)
+ return
+ }
+ }
+
+ // convert label formats
var labels = []string{}
if _, found := r.URL.Query()["labels"]; found {
- var m = map[string]string{}
- if err := json.Unmarshal([]byte(query.Labels), &m); err != nil {
+ if err := json.Unmarshal([]byte(query.Labels), &labels); err != nil {
utils.BadRequest(w, "labels", query.Labels, err)
return
}
-
- for k, v := range m {
- labels = append(labels, k+"="+v)
- }
}
pullPolicy := buildah.PullIfMissing
@@ -160,27 +217,14 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
reporter := channel.NewWriter(make(chan []byte, 1))
defer reporter.Close()
+
buildOptions := imagebuildah.BuildOptions{
- ContextDirectory: contextDirectory,
- PullPolicy: pullPolicy,
- Registry: query.Registry,
- IgnoreUnrecognizedInstructions: true,
- Quiet: query.Quiet,
- Layers: query.Layers,
- Isolation: buildah.IsolationChroot,
- Compression: archive.Gzip,
- Args: buildArgs,
- Output: output,
- AdditionalTags: additionalNames,
- Out: stdout,
- Err: auxout,
- ReportWriter: reporter,
- OutputFormat: buildah.Dockerv2ImageManifest,
- SystemContext: &types.SystemContext{
- AuthFilePath: authfile,
- DockerAuthConfig: creds,
- },
+ AddCapabilities: addCaps,
+ AdditionalTags: additionalTags,
+ Annotations: annotations,
+ Args: buildArgs,
CommonBuildOpts: &buildah.CommonBuildOptions{
+ AddHost: addhosts,
CPUPeriod: query.CpuPeriod,
CPUQuota: query.CpuQuota,
CPUShares: query.CpuShares,
@@ -190,12 +234,37 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
MemorySwap: query.MemSwap,
ShmSize: strconv.Itoa(query.ShmSize),
},
- Squash: query.Squash,
- Labels: labels,
- NoCache: query.NoCache,
- RemoveIntermediateCtrs: query.Rm,
- ForceRmIntermediateCtrs: query.ForceRm,
- Target: query.Target,
+ Compression: archive.Gzip,
+ ConfigureNetwork: buildah.NetworkConfigurationPolicy(query.ConfigureNetwork),
+ ContextDirectory: contextDirectory,
+ Devices: devices,
+ DropCapabilities: dropCaps,
+ Err: auxout,
+ ForceRmIntermediateCtrs: query.ForceRm,
+ From: query.From,
+ IgnoreUnrecognizedInstructions: true,
+ // FIXME, This is very broken. Buildah will only work with chroot
+ // Isolation: buildah.Isolation(query.Isolation),
+ Isolation: buildah.IsolationChroot,
+
+ Labels: labels,
+ Layers: query.Layers,
+ Manifest: query.Manifest,
+ NoCache: query.NoCache,
+ Out: stdout,
+ Output: output,
+ OutputFormat: format,
+ PullPolicy: pullPolicy,
+ Quiet: query.Quiet,
+ Registry: query.Registry,
+ RemoveIntermediateCtrs: query.Rm,
+ ReportWriter: reporter,
+ Squash: query.Squash,
+ SystemContext: &types.SystemContext{
+ AuthFilePath: authfile,
+ DockerAuthConfig: creds,
+ },
+ Target: query.Target,
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index f0b922885..f7a70816f 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -388,3 +388,25 @@ func Disconnect(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, "OK")
}
+
+// Prune removes unused networks
+func Prune(w http.ResponseWriter, r *http.Request) {
+ // TODO Filters are not implemented
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ic := abi.ContainerEngine{Libpod: runtime}
+ pruneOptions := entities.NetworkPruneOptions{}
+ pruneReports, err := ic.NetworkPrune(r.Context(), pruneOptions)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ var prunedNetworks []string //nolint
+ for _, pr := range pruneReports {
+ if pr.Error != nil {
+ logrus.Error(pr.Error)
+ continue
+ }
+ prunedNetworks = append(prunedNetworks, pr.Name)
+ }
+ utils.WriteResponse(w, http.StatusOK, prunedNetworks)
+}
diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go
index 0a514822b..1d1f1ecf2 100644
--- a/pkg/api/handlers/compat/swagger.go
+++ b/pkg/api/handlers/compat/swagger.go
@@ -77,3 +77,10 @@ type swagCompatNetworkDisconnectRequest struct {
// in:body
Body struct{ types.NetworkDisconnect }
}
+
+// Network prune
+// swagger:response NetworkPruneResponse
+type swagCompatNetworkPruneResponse struct {
+ // in:body
+ Body []string
+}
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index d3bf06988..998f89d96 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -175,3 +175,17 @@ func ExistsNetwork(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
+
+// Prune removes unused networks
+func Prune(w http.ResponseWriter, r *http.Request) {
+ // TODO Filters are not implemented
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ic := abi.ContainerEngine{Libpod: runtime}
+ pruneOptions := entities.NetworkPruneOptions{}
+ pruneReports, err := ic.NetworkPrune(r.Context(), pruneOptions)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, pruneReports)
+}
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index 3d9e7fb89..d3345d8da 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -9,19 +9,6 @@ import (
)
func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
- // swagger:operation POST /networks/prune compat compatPruneNetwork
- // ---
- // tags:
- // - networks (compat)
- // Summary: Delete unused networks
- // description: Not supported
- // produces:
- // - application/json
- // responses:
- // 404:
- // $ref: "#/responses/NoSuchNetwork"
- r.HandleFunc(VersionedPath("/networks/prune"), compat.UnsupportedHandler).Methods(http.MethodPost)
- r.HandleFunc("/networks/prune", compat.UnsupportedHandler).Methods(http.MethodPost)
// swagger:operation DELETE /networks/{name} compat compatRemoveNetwork
// ---
// tags:
@@ -172,6 +159,35 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
r.HandleFunc("/networks/{name}/disconnect", s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
+ // swagger:operation POST /networks/prune compat compatPruneNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Delete unused networks
+ // description: Remove CNI networks that do not have containers
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: query
+ // name: filters
+ // type: string
+ // description: |
+ // NOT IMPLEMENTED
+ // Filters to process on the prune list, encoded as JSON (a map[string][]string).
+ // Available filters:
+ // - until=<timestamp> Prune networks created before this timestamp. The <timestamp> can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time.
+ // - label (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune networks with (or without, in case label!=... is used) the specified labels.
+ // responses:
+ // 200:
+ // description: OK
+ // schema:
+ // type: array
+ // items:
+ // type: string
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/prune"), s.APIHandler(compat.Prune)).Methods(http.MethodPost)
+ r.HandleFunc("/networks/prune", s.APIHandler(compat.Prune)).Methods(http.MethodPost)
// swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
// ---
@@ -353,5 +369,29 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/networks/{name}/disconnect"), s.APIHandler(compat.Disconnect)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/networks/prune libpod libpodPruneNetwork
+ // ---
+ // tags:
+ // - networks
+ // summary: Delete unused networks
+ // description: Remove CNI networks that do not have containers
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: query
+ // name: filters
+ // type: string
+ // description: |
+ // NOT IMPLEMENTED
+ // Filters to process on the prune list, encoded as JSON (a map[string][]string).
+ // Available filters:
+ // - until=<timestamp> Prune networks created before this timestamp. The <timestamp> can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time.
+ // - label (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune networks with (or without, in case label!=... is used) the specified labels.
+ // responses:
+ // 200:
+ // $ref: "#/responses/NetworkPruneResponse"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/networks/prune"), s.APIHandler(libpod.Prune)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 02765816f..8ea09b881 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -31,36 +31,31 @@ import (
func Build(ctx context.Context, containerFiles []string, options entities.BuildOptions) (*entities.BuildReport, error) {
params := url.Values{}
- if t := options.Output; len(t) > 0 {
- params.Set("t", t)
+ if caps := options.AddCapabilities; len(caps) > 0 {
+ c, err := jsoniter.MarshalToString(caps)
+ if err != nil {
+ return nil, err
+ }
+ params.Add("addcaps", c)
}
+
+ if annotations := options.Annotations; len(annotations) > 0 {
+ l, err := jsoniter.MarshalToString(annotations)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("annotations", l)
+ }
+ params.Add("t", options.Output)
for _, tag := range options.AdditionalTags {
params.Add("t", tag)
}
- if options.Quiet {
- params.Set("q", "1")
- }
- if options.NoCache {
- params.Set("nocache", "1")
- }
- if options.Layers {
- params.Set("layers", "1")
- }
- // TODO cachefrom
- if options.PullPolicy == buildah.PullAlways {
- params.Set("pull", "1")
- }
- if options.RemoveIntermediateCtrs {
- params.Set("rm", "1")
- }
- if options.ForceRmIntermediateCtrs {
- params.Set("forcerm", "1")
- }
- if mem := options.CommonBuildOpts.Memory; mem > 0 {
- params.Set("memory", strconv.Itoa(int(mem)))
- }
- if memSwap := options.CommonBuildOpts.MemorySwap; memSwap > 0 {
- params.Set("memswap", strconv.Itoa(int(memSwap)))
+ if buildArgs := options.Args; len(buildArgs) > 0 {
+ bArgs, err := jsoniter.MarshalToString(buildArgs)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("buildargs", bArgs)
}
if cpuShares := options.CommonBuildOpts.CPUShares; cpuShares > 0 {
params.Set("cpushares", strconv.Itoa(int(cpuShares)))
@@ -74,22 +69,38 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if cpuQuota := options.CommonBuildOpts.CPUQuota; cpuQuota > 0 {
params.Set("cpuquota", strconv.Itoa(int(cpuQuota)))
}
- if buildArgs := options.Args; len(buildArgs) > 0 {
- bArgs, err := jsoniter.MarshalToString(buildArgs)
+ params.Set("networkmode", strconv.Itoa(int(options.ConfigureNetwork)))
+ params.Set("outputformat", options.OutputFormat)
+
+ if devices := options.Devices; len(devices) > 0 {
+ d, err := jsoniter.MarshalToString(devices)
if err != nil {
return nil, err
}
- params.Set("buildargs", bArgs)
+ params.Add("devices", d)
}
- if shmSize := options.CommonBuildOpts.ShmSize; len(shmSize) > 0 {
- shmBytes, err := units.RAMInBytes(shmSize)
+
+ if caps := options.DropCapabilities; len(caps) > 0 {
+ c, err := jsoniter.MarshalToString(caps)
if err != nil {
return nil, err
}
- params.Set("shmsize", strconv.Itoa(int(shmBytes)))
+ params.Add("dropcaps", c)
}
- if options.Squash {
- params.Set("squash", "1")
+
+ if options.ForceRmIntermediateCtrs {
+ params.Set("forcerm", "1")
+ }
+ if len(options.From) > 0 {
+ params.Set("from", options.From)
+ }
+
+ params.Set("isolation", strconv.Itoa(int(options.Isolation)))
+ if options.CommonBuildOpts.HTTPProxy {
+ params.Set("httpproxy", "1")
+ }
+ if options.Jobs != nil {
+ params.Set("jobs", strconv.FormatUint(uint64(*options.Jobs), 10))
}
if labels := options.Labels; len(labels) > 0 {
l, err := jsoniter.MarshalToString(labels)
@@ -98,10 +109,66 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("labels", l)
}
- if options.CommonBuildOpts.HTTPProxy {
- params.Set("httpproxy", "1")
+ if options.Layers {
+ params.Set("layers", "1")
+ }
+ if options.LogRusage {
+ params.Set("rusage", "1")
+ }
+ if len(options.Manifest) > 0 {
+ params.Set("manifest", options.Manifest)
+ }
+ if memSwap := options.CommonBuildOpts.MemorySwap; memSwap > 0 {
+ params.Set("memswap", strconv.Itoa(int(memSwap)))
+ }
+ if mem := options.CommonBuildOpts.Memory; mem > 0 {
+ params.Set("memory", strconv.Itoa(int(mem)))
+ }
+ if options.NoCache {
+ params.Set("nocache", "1")
+ }
+ if t := options.Output; len(t) > 0 {
+ params.Set("output", t)
+ }
+ var platform string
+ if len(options.OS) > 0 {
+ platform = options.OS
+ }
+ if len(options.Architecture) > 0 {
+ if len(platform) == 0 {
+ platform = "linux"
+ }
+ platform += "/" + options.Architecture
+ }
+ if len(platform) > 0 {
+ params.Set("platform", platform)
+ }
+ if options.PullPolicy == buildah.PullAlways {
+ params.Set("pull", "1")
+ }
+ if options.Quiet {
+ params.Set("q", "1")
+ }
+ if options.RemoveIntermediateCtrs {
+ params.Set("rm", "1")
+ }
+ if hosts := options.CommonBuildOpts.AddHost; len(hosts) > 0 {
+ h, err := jsoniter.MarshalToString(hosts)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("extrahosts", h)
+ }
+ if shmSize := options.CommonBuildOpts.ShmSize; len(shmSize) > 0 {
+ shmBytes, err := units.RAMInBytes(shmSize)
+ if err != nil {
+ return nil, err
+ }
+ params.Set("shmsize", strconv.Itoa(int(shmBytes)))
+ }
+ if options.Squash {
+ params.Set("squash", "1")
}
-
var (
headers map[string]string
err error
@@ -124,19 +191,6 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
stdout = options.Out
}
- // TODO network?
-
- var platform string
- if OS := options.OS; len(OS) > 0 {
- platform += OS
- }
- if arch := options.Architecture; len(arch) > 0 {
- platform += "/" + arch
- }
- if len(platform) > 0 {
- params.Set("platform", platform)
- }
-
entries := make([]string, len(containerFiles))
copy(entries, containerFiles)
entries = append(entries, options.ContextDirectory)
diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go
index 8debeee84..428e60cf2 100644
--- a/pkg/bindings/network/network.go
+++ b/pkg/bindings/network/network.go
@@ -180,3 +180,21 @@ func Exists(ctx context.Context, nameOrID string, options *ExistsOptions) (bool,
}
return response.IsSuccess(), nil
}
+
+// Prune removes unused CNI networks
+func Prune(ctx context.Context, options *PruneOptions) ([]*entities.NetworkPruneReport, error) {
+ // TODO Filters is not implemented
+ var (
+ prunedNetworks []*entities.NetworkPruneReport
+ )
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ response, err := conn.DoRequest(nil, http.MethodPost, "/networks/prune", nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return prunedNetworks, response.Process(&prunedNetworks)
+}
diff --git a/pkg/bindings/network/types.go b/pkg/bindings/network/types.go
index 91cbcf044..47dce67c7 100644
--- a/pkg/bindings/network/types.go
+++ b/pkg/bindings/network/types.go
@@ -74,3 +74,9 @@ type ConnectOptions struct {
// if a network exists
type ExistsOptions struct {
}
+
+//go:generate go run ../generator/generator.go PruneOptions
+// PruneOptions are optional options for removing unused
+// CNI networks
+type PruneOptions struct {
+}
diff --git a/pkg/bindings/network/types_prune_options.go b/pkg/bindings/network/types_prune_options.go
new file mode 100644
index 000000000..c56dcd0d3
--- /dev/null
+++ b/pkg/bindings/network/types_prune_options.go
@@ -0,0 +1,75 @@
+package network
+
+import (
+ "net/url"
+ "reflect"
+ "strings"
+
+ "github.com/containers/podman/v2/pkg/bindings/util"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
+)
+
+/*
+This file is generated automatically by go generate. Do not edit.
+*/
+
+// Changed
+func (o *PruneOptions) Changed(fieldName string) bool {
+ r := reflect.ValueOf(o)
+ value := reflect.Indirect(r).FieldByName(fieldName)
+ return !value.IsNil()
+}
+
+// ToParams
+func (o *PruneOptions) ToParams() (url.Values, error) {
+ params := url.Values{}
+ if o == nil {
+ return params, nil
+ }
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ s := reflect.ValueOf(o)
+ if reflect.Ptr == s.Kind() {
+ s = s.Elem()
+ }
+ sType := s.Type()
+ for i := 0; i < s.NumField(); i++ {
+ fieldName := sType.Field(i).Name
+ if !o.Changed(fieldName) {
+ continue
+ }
+ fieldName = strings.ToLower(fieldName)
+ f := s.Field(i)
+ if reflect.Ptr == f.Kind() {
+ f = f.Elem()
+ }
+ switch {
+ case util.IsSimpleType(f):
+ params.Set(fieldName, util.SimpleTypeToParam(f))
+ case f.Kind() == reflect.Slice:
+ for i := 0; i < f.Len(); i++ {
+ elem := f.Index(i)
+ if util.IsSimpleType(elem) {
+ params.Add(fieldName, util.SimpleTypeToParam(elem))
+ } else {
+ return nil, errors.New("slices must contain only simple types")
+ }
+ }
+ case f.Kind() == reflect.Map:
+ lowerCaseKeys := make(map[string][]string)
+ iter := f.MapRange()
+ for iter.Next() {
+ lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string)
+
+ }
+ s, err := json.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return nil, err
+ }
+
+ params.Set(fieldName, s)
+ }
+
+ }
+ return params, nil
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 9ff1714e7..d43b422a3 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -63,6 +63,7 @@ type ContainerEngine interface {
NetworkExists(ctx context.Context, networkname string) (*BoolReport, error)
NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error)
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
+ NetworkPrune(ctx context.Context, options NetworkPruneOptions) ([]*NetworkPruneReport, error)
NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error)
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go
index b76bfcac7..1859f920e 100644
--- a/pkg/domain/entities/network.go
+++ b/pkg/domain/entities/network.go
@@ -80,3 +80,15 @@ type NetworkConnectOptions struct {
Aliases []string
Container string
}
+
+// NetworkPruneReport containers the name of network and an error
+// associated in its pruning (removal)
+// swagger:model NetworkPruneReport
+type NetworkPruneReport struct {
+ Name string
+ Error error
+}
+
+// NetworkPruneOptions describes options for pruning
+// unused cni networks
+type NetworkPruneOptions struct{}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 8ca93e770..f2d0f2c39 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -580,12 +580,21 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
// without having to pass all local data around.
deleteImage := func(img *image.Image) error {
results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
- if err != nil {
+ switch errors.Cause(err) {
+ case nil:
+ // Removal worked, so let's report it.
+ report.Deleted = append(report.Deleted, results.Deleted)
+ report.Untagged = append(report.Untagged, results.Untagged...)
+ return nil
+ case storage.ErrImageUnknown:
+ // The image must have been removed already (see #6510).
+ report.Deleted = append(report.Deleted, img.ID())
+ report.Untagged = append(report.Untagged, img.ID())
+ return nil
+ default:
+ // Fatal error.
return err
}
- report.Deleted = append(report.Deleted, results.Deleted)
- report.Untagged = append(report.Untagged, results.Untagged...)
- return nil
}
// Delete all images from the local storage.
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index bc4328fcd..13fabe89d 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -155,3 +155,28 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
Value: exists,
}, nil
}
+
+// Network prune removes unused cni networks
+func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
+ runtimeConfig, err := ic.Libpod.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ cons, err := ic.Libpod.GetAllContainers()
+ if err != nil {
+ return nil, err
+ }
+ // Gather up all the non-default networks that the
+ // containers want
+ usedNetworks := make(map[string]bool)
+ for _, c := range cons {
+ nets, _, err := c.Networks()
+ if err != nil {
+ return nil, err
+ }
+ for _, n := range nets {
+ usedNetworks[n] = true
+ }
+ }
+ return network.PruneNetworks(runtimeConfig, usedNetworks)
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index f10c8c175..daad911cd 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -350,17 +350,6 @@ func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts en
if err != nil {
return nil, err
}
- // For remote clients, if the option for writing to a file was
- // selected, we need to write to the *client's* filesystem.
- if len(opts.IIDFile) > 0 {
- f, err := os.Create(opts.IIDFile)
- if err != nil {
- return nil, err
- }
- if _, err := f.WriteString(report.ID); err != nil {
- return nil, err
- }
- }
return report, nil
}
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index bdb1beb03..990bfa880 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -89,3 +89,8 @@ func (ic *ContainerEngine) NetworkExists(ctx context.Context, networkname string
Value: exists,
}, nil
}
+
+// Network prune removes unused cni networks
+func (ic *ContainerEngine) NetworkPrune(ctx context.Context, options entities.NetworkPruneOptions) ([]*entities.NetworkPruneReport, error) {
+ return network.Prune(ic.ClientCtx, nil)
+}
diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py
index 9ce0803fb..73db35cc1 100644
--- a/test/apiv2/rest_api/test_rest_v2_0_0.py
+++ b/test/apiv2/rest_api/test_rest_v2_0_0.py
@@ -484,7 +484,7 @@ class TestApi(unittest.TestCase):
self.assertEqual(inspect.status_code, 404, inspect.content)
prune = requests.post(PODMAN_URL + "/v1.40/networks/prune")
- self.assertEqual(prune.status_code, 404, prune.content)
+ self.assertEqual(prune.status_code, 200, prune.content)
def test_volumes_compat(self):
name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10))
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index 71b4c0089..9bab4c926 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -194,7 +194,7 @@ var _ = Describe("Podman build", func() {
inspect := podmanTest.Podman([]string{"inspect", string(id)})
inspect.WaitWithDefaultTimeout()
data := inspect.InspectImageJSON()
- Expect(data[0].ID).To(Equal(string(id)))
+ Expect("sha256:" + data[0].ID).To(Equal(string(id)))
})
It("podman Test PATH in built image", func() {
@@ -458,4 +458,55 @@ RUN [[ -L /test/dummy-symlink ]] && echo SYMLNKOK || echo SYMLNKERR`
Expect(ok).To(BeTrue())
})
+ It("podman build --from, --add-host, --cap-drop, --cap-add", func() {
+ targetPath, err := CreateTempDirInTempDir()
+ Expect(err).To(BeNil())
+
+ containerFile := filepath.Join(targetPath, "Containerfile")
+ content := `FROM scratch
+RUN cat /etc/hosts
+RUN grep CapEff /proc/self/status`
+
+ Expect(ioutil.WriteFile(containerFile, []byte(content), 0755)).To(BeNil())
+
+ defer func() {
+ Expect(os.RemoveAll(containerFile)).To(BeNil())
+ }()
+
+ // When
+ session := podmanTest.Podman([]string{
+ "build", "--cap-drop=all", "--cap-add=net_bind_service", "--add-host", "testhost:1.2.3.4", "--from", "alpine", targetPath,
+ })
+ session.WaitWithDefaultTimeout()
+
+ // Then
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(strings.Fields(session.OutputToString())).
+ To(ContainElement("alpine"))
+ Expect(strings.Fields(session.OutputToString())).
+ To(ContainElement("testhost"))
+ Expect(strings.Fields(session.OutputToString())).
+ To(ContainElement("0000000000000400"))
+ })
+
+ It("podman build --arch", func() {
+ targetPath, err := CreateTempDirInTempDir()
+ Expect(err).To(BeNil())
+
+ containerFile := filepath.Join(targetPath, "Containerfile")
+ Expect(ioutil.WriteFile(containerFile, []byte("FROM alpine"), 0755)).To(BeNil())
+
+ defer func() {
+ Expect(os.RemoveAll(containerFile)).To(BeNil())
+ }()
+
+ // When
+ session := podmanTest.Podman([]string{
+ "build", "--arch", "arm64", targetPath,
+ })
+ session.WaitWithDefaultTimeout()
+
+ // Then
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index c6010ba43..d4e1a3698 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -540,4 +540,54 @@ var _ = Describe("Podman network", func() {
nc.WaitWithDefaultTimeout()
Expect(nc.ExitCode()).To(Equal(0))
})
+
+ It("podman network prune", func() {
+ // Create two networks
+ // Check they are there
+ // Run a container on one of them
+ // Network Prune
+ // Check that one has been pruned, other remains
+ net := "macvlan" + stringid.GenerateNonCryptoID()
+ net1 := net + "1"
+ net2 := net + "2"
+ nc := podmanTest.Podman([]string{"network", "create", net1})
+ nc.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net1)
+ Expect(nc.ExitCode()).To(Equal(0))
+
+ nc2 := podmanTest.Podman([]string{"network", "create", net2})
+ nc2.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net2)
+ Expect(nc2.ExitCode()).To(Equal(0))
+
+ list := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"})
+ list.WaitWithDefaultTimeout()
+ Expect(list.ExitCode()).To(BeZero())
+
+ Expect(StringInSlice(net1, list.OutputToStringArray())).To(BeTrue())
+ Expect(StringInSlice(net2, list.OutputToStringArray())).To(BeTrue())
+ if !isRootless() {
+ Expect(StringInSlice("podman", list.OutputToStringArray())).To(BeTrue())
+ }
+
+ session := podmanTest.Podman([]string{"run", "-dt", "--net", net2, ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ prune := podmanTest.Podman([]string{"network", "prune", "-f"})
+ prune.WaitWithDefaultTimeout()
+ Expect(prune.ExitCode()).To(BeZero())
+
+ listAgain := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"})
+ listAgain.WaitWithDefaultTimeout()
+ Expect(listAgain.ExitCode()).To(BeZero())
+
+ Expect(StringInSlice(net1, listAgain.OutputToStringArray())).To(BeFalse())
+ Expect(StringInSlice(net2, listAgain.OutputToStringArray())).To(BeTrue())
+ // Make sure default network 'podman' still exists
+ if !isRootless() {
+ Expect(StringInSlice("podman", list.OutputToStringArray())).To(BeTrue())
+ }
+
+ })
})
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index 4833a282e..257570ea7 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -1,7 +1,9 @@
package integration
import (
+ "fmt"
"os"
+ "sync"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
@@ -275,4 +277,32 @@ RUN find $LOCAL
match, _ := session.ErrorGrepString("image name or ID must be specified")
Expect(match).To(BeTrue())
})
+
+ It("podman image rm - concurrent with shared layers", func() {
+ // #6510 has shown a fairly simple reproducer to force storage
+ // errors during parallel image removal. Since it's subject to
+ // a race, we may not hit the condition a 100 percent of times
+ // but ocal reproducers hit it all the time.
+
+ var wg sync.WaitGroup
+
+ buildAndRemove := func(i int) {
+ defer GinkgoRecover()
+ defer wg.Done()
+ imageName := fmt.Sprintf("rmtest:%d", i)
+ containerfile := `FROM quay.io/libpod/cirros:latest
+RUN ` + fmt.Sprintf("touch %s", imageName)
+
+ podmanTest.BuildImage(containerfile, imageName, "false")
+ session := podmanTest.Podman([]string{"rmi", "-f", imageName})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ }
+
+ wg.Add(10)
+ for i := 0; i < 10; i++ {
+ go buildAndRemove(i)
+ }
+ wg.Wait()
+ })
})
diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md
index 0ad3069ce..ccf46b324 100644
--- a/vendor/github.com/containers/buildah/CHANGELOG.md
+++ b/vendor/github.com/containers/buildah/CHANGELOG.md
@@ -2,6 +2,14 @@
# Changelog
+## v1.19.4 (2021-02-06)
+ run: fix check for host pid namespace
+ bump containernetworking/cni library to v0.8.1 - fix for CVE-2021-20206
+ Finish plumbing for buildah bud --manifest
+ buildah manifest add localimage should work
+ Fix build arg check
+ --iidfile: print hash prefix
+
## v1.19.3 (2021-01-28)
[ci:docs] Fix man page for buildah push
Vendor in containers/image v5.10.1
diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go
index 4fbc475c2..7065e00e4 100644
--- a/vendor/github.com/containers/buildah/buildah.go
+++ b/vendor/github.com/containers/buildah/buildah.go
@@ -28,7 +28,7 @@ const (
Package = "buildah"
// Version for the Package. Bump version in contrib/rpm/buildah.spec
// too.
- Version = "1.19.3"
+ Version = "1.19.4"
// The value we use to identify what type of information, currently a
// serialized Builder structure, we are using as per-container state.
// This should only be changed when we make incompatible changes to
diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt
index db2faf71a..4a0f81b04 100644
--- a/vendor/github.com/containers/buildah/changelog.txt
+++ b/vendor/github.com/containers/buildah/changelog.txt
@@ -1,3 +1,11 @@
+- Changelog for v1.19.4 (2021-02-06)
+ * run: fix check for host pid namespace
+ * bump containernetworking/cni library to v0.8.1 - fix for CVE-2021-20206
+ * Finish plumbing for buildah bud --manifest
+ * buildah manifest add localimage should work
+ * Fix build arg check
+ * --iidfile: print hash prefix
+
- Changelog for v1.19.3 (2021-01-28)
* [ci:docs] Fix man page for buildah push
* Vendor in containers/image v5.10.1
diff --git a/vendor/github.com/containers/buildah/commit.go b/vendor/github.com/containers/buildah/commit.go
index 9c6831601..f588c8043 100644
--- a/vendor/github.com/containers/buildah/commit.go
+++ b/vendor/github.com/containers/buildah/commit.go
@@ -224,7 +224,7 @@ func checkRegistrySourcesAllows(forWhat string, dest types.ImageReference) (inse
return false, nil
}
-func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpec string) error {
+func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpec string) (string, error) {
var create bool
systemContext := &types.SystemContext{}
var list manifests.List
@@ -235,13 +235,13 @@ func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpe
} else {
_, list, err = manifests.LoadFromImage(b.store, listImage.ID)
if err != nil {
- return err
+ return "", err
}
}
names, err := util.ExpandNames([]string{manifestName}, "", systemContext, b.store)
if err != nil {
- return errors.Wrapf(err, "error encountered while expanding image name %q", manifestName)
+ return "", errors.Wrapf(err, "error encountered while expanding image name %q", manifestName)
}
ref, err := alltransports.ParseImageName(imageSpec)
@@ -249,13 +249,13 @@ func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpe
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
// check if the local image exists
if ref, _, err = util.FindImage(b.store, "", systemContext, imageSpec); err != nil {
- return err
+ return "", err
}
}
}
if _, err = list.Add(ctx, systemContext, ref, true); err != nil {
- return err
+ return "", err
}
var imageID string
if create {
@@ -263,10 +263,7 @@ func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpe
} else {
imageID, err = list.SaveToImage(b.store, listImage.ID, nil, "")
}
- if err == nil {
- fmt.Printf("%s\n", imageID)
- }
- return err
+ return imageID, err
}
// Commit writes the contents of the container, along with its updated
@@ -469,7 +466,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
dest = dest2
}
if options.IIDFile != "" {
- if err = ioutil.WriteFile(options.IIDFile, []byte(img.ID), 0644); err != nil {
+ if err = ioutil.WriteFile(options.IIDFile, []byte("sha256:"+img.ID), 0644); err != nil {
return imgID, nil, "", err
}
}
@@ -489,9 +486,12 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
}
if options.Manifest != "" {
- if err := b.addManifest(ctx, options.Manifest, imgID); err != nil {
+ manifestID, err := b.addManifest(ctx, options.Manifest, imgID)
+ if err != nil {
return imgID, nil, "", err
}
+ logrus.Debugf("added imgID %s to manifestID %s", imgID, manifestID)
+
}
return imgID, ref, manifestDigest, nil
}
diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod
index cccf42895..17469ad12 100644
--- a/vendor/github.com/containers/buildah/go.mod
+++ b/vendor/github.com/containers/buildah/go.mod
@@ -4,7 +4,7 @@ go 1.12
require (
github.com/containerd/containerd v1.4.1 // indirect
- github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784
+ github.com/containernetworking/cni v0.8.1
github.com/containers/common v0.33.1
github.com/containers/image/v5 v5.10.1
github.com/containers/ocicrypt v1.0.3
diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum
index bf796c496..cab904fcf 100644
--- a/vendor/github.com/containers/buildah/go.sum
+++ b/vendor/github.com/containers/buildah/go.sum
@@ -76,8 +76,8 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
-github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784 h1:rqUVLD8I859xRgUx/WMC3v7QAFqbLKZbs+0kqYboRJc=
-github.com/containernetworking/cni v0.7.2-0.20190904153231-83439463f784/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI=
+github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/containers/common v0.33.1 h1:XpDiq8Cta8+u1s4kpYSEWdB140ZmqgyIXfWkLqKx3z0=
github.com/containers/common v0.33.1/go.mod h1:mjDo/NKeweL/onaspLhZ38WnHXaYmrELHclIdvSnYpY=
github.com/containers/image/v5 v5.9.0 h1:dRmUtcluQcmasNo3DpnRoZjfU0rOu1qZeL6wlDJr10Q=
diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go
index a72e24eea..74ed9a42b 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/executor.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go
@@ -115,6 +115,7 @@ type Executor struct {
imageInfoLock sync.Mutex
imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs
fromOverride string
+ manifest string
}
type imageTypeAndHistoryAndDiffIDs struct {
@@ -231,6 +232,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod
logRusage: options.LogRusage,
imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs),
fromOverride: options.From,
+ manifest: options.Manifest,
}
if exec.err == nil {
exec.err = os.Stderr
@@ -679,7 +681,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
}
logrus.Debugf("printing final image id %q", imageID)
if b.iidfile != "" {
- if err = ioutil.WriteFile(b.iidfile, []byte(imageID), 0644); err != nil {
+ if err = ioutil.WriteFile(b.iidfile, []byte("sha256:"+imageID), 0644); err != nil {
return imageID, ref, errors.Wrapf(err, "failed to write image ID to file %q", b.iidfile)
}
} else {
diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
index 9c15785bc..13631108e 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
@@ -838,7 +838,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
// we need to call ib.Run() to correctly put the args together before
// determining if a cached layer with the same build args already exists
// and that is done in the if block below.
- if checkForLayers && s.builder.Args == nil {
+ if checkForLayers && len(s.builder.Args) == 0 {
cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step))
if err != nil {
return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build")
@@ -1276,6 +1276,7 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
MaxRetries: s.executor.maxPullPushRetries,
RetryDelay: s.executor.retryPullPushDelay,
HistoryTimestamp: s.executor.timestamp,
+ Manifest: s.executor.manifest,
}
imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options)
if err != nil {
diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go
index 66c856884..8c7c1bbc0 100644
--- a/vendor/github.com/containers/buildah/run_linux.go
+++ b/vendor/github.com/containers/buildah/run_linux.go
@@ -2210,7 +2210,7 @@ func checkAndOverrideIsolationOptions(isolation Isolation, options *RunOptions)
case IsolationOCI:
pidns := options.NamespaceOptions.Find(string(specs.PIDNamespace))
userns := options.NamespaceOptions.Find(string(specs.UserNamespace))
- if (pidns == nil || pidns.Host) && (userns != nil && !userns.Host) {
+ if (pidns != nil && pidns.Host) && (userns != nil && !userns.Host) {
return errors.Errorf("not allowed to mix host PID namespace with container user namespace")
}
}
diff --git a/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go b/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go
new file mode 100644
index 000000000..288296ea5
--- /dev/null
+++ b/vendor/github.com/containers/ocicrypt/helpers/parse_helpers.go
@@ -0,0 +1,301 @@
+package helpers
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/containers/ocicrypt"
+ encconfig "github.com/containers/ocicrypt/config"
+ encutils "github.com/containers/ocicrypt/utils"
+
+ "github.com/pkg/errors"
+)
+
+// processRecipientKeys sorts the array of recipients by type. Recipients may be either
+// x509 certificates, public keys, or PGP public keys identified by email address or name
+func processRecipientKeys(recipients []string) ([][]byte, [][]byte, [][]byte, error) {
+ var (
+ gpgRecipients [][]byte
+ pubkeys [][]byte
+ x509s [][]byte
+ )
+ for _, recipient := range recipients {
+
+ idx := strings.Index(recipient, ":")
+ if idx < 0 {
+ return nil, nil, nil, errors.New("Invalid recipient format")
+ }
+
+ protocol := recipient[:idx]
+ value := recipient[idx+1:]
+
+ switch protocol {
+ case "pgp":
+ gpgRecipients = append(gpgRecipients, []byte(value))
+
+ case "jwe":
+ tmp, err := ioutil.ReadFile(value)
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, "Unable to read file")
+ }
+ if !encutils.IsPublicKey(tmp) {
+ return nil, nil, nil, errors.New("File provided is not a public key")
+ }
+ pubkeys = append(pubkeys, tmp)
+
+ case "pkcs7":
+ tmp, err := ioutil.ReadFile(value)
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, "Unable to read file")
+ }
+ if !encutils.IsCertificate(tmp) {
+ return nil, nil, nil, errors.New("File provided is not an x509 cert")
+ }
+ x509s = append(x509s, tmp)
+
+ default:
+ return nil, nil, nil, errors.New("Provided protocol not recognized")
+ }
+ }
+ return gpgRecipients, pubkeys, x509s, nil
+}
+
+// processx509Certs processes x509 certificate files
+func processx509Certs(keys []string) ([][]byte, error) {
+ var x509s [][]byte
+ for _, key := range keys {
+ tmp, err := ioutil.ReadFile(key)
+ if err != nil {
+ return nil, errors.Wrap(err, "Unable to read file")
+ }
+ if !encutils.IsCertificate(tmp) {
+ continue
+ }
+ x509s = append(x509s, tmp)
+
+ }
+ return x509s, nil
+}
+
+// processPwdString process a password that may be in any of the following formats:
+// - file=<passwordfile>
+// - pass=<password>
+// - fd=<filedescriptor>
+// - <password>
+func processPwdString(pwdString string) ([]byte, error) {
+ if strings.HasPrefix(pwdString, "file=") {
+ return ioutil.ReadFile(pwdString[5:])
+ } else if strings.HasPrefix(pwdString, "pass=") {
+ return []byte(pwdString[5:]), nil
+ } else if strings.HasPrefix(pwdString, "fd=") {
+ fdStr := pwdString[3:]
+ fd, err := strconv.Atoi(fdStr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not parse file descriptor %s", fdStr)
+ }
+ f := os.NewFile(uintptr(fd), "pwdfile")
+ if f == nil {
+ return nil, fmt.Errorf("%s is not a valid file descriptor", fdStr)
+ }
+ defer f.Close()
+ pwd := make([]byte, 64)
+ n, err := f.Read(pwd)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not read from file descriptor")
+ }
+ return pwd[:n], nil
+ }
+ return []byte(pwdString), nil
+}
+
+// processPrivateKeyFiles sorts the different types of private key files; private key files may either be
+// private keys or GPG private key ring files. The private key files may include the password for the
+// private key and take any of the following forms:
+// - <filename>
+// - <filename>:file=<passwordfile>
+// - <filename>:pass=<password>
+// - <filename>:fd=<filedescriptor>
+// - <filename>:<password>
+func processPrivateKeyFiles(keyFilesAndPwds []string) ([][]byte, [][]byte, [][]byte, [][]byte, error) {
+ var (
+ gpgSecretKeyRingFiles [][]byte
+ gpgSecretKeyPasswords [][]byte
+ privkeys [][]byte
+ privkeysPasswords [][]byte
+ err error
+ )
+ // keys needed for decryption in case of adding a recipient
+ for _, keyfileAndPwd := range keyFilesAndPwds {
+ var password []byte
+
+ parts := strings.Split(keyfileAndPwd, ":")
+ if len(parts) == 2 {
+ password, err = processPwdString(parts[1])
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ }
+
+ keyfile := parts[0]
+ tmp, err := ioutil.ReadFile(keyfile)
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ isPrivKey, err := encutils.IsPrivateKey(tmp, password)
+ if encutils.IsPasswordError(err) {
+ return nil, nil, nil, nil, err
+ }
+ if isPrivKey {
+ privkeys = append(privkeys, tmp)
+ privkeysPasswords = append(privkeysPasswords, password)
+ } else if encutils.IsGPGPrivateKeyRing(tmp) {
+ gpgSecretKeyRingFiles = append(gpgSecretKeyRingFiles, tmp)
+ gpgSecretKeyPasswords = append(gpgSecretKeyPasswords, password)
+ } else {
+ // ignore if file is not recognized, so as not to error if additional
+ // metadata/cert files exists
+ continue
+ }
+ }
+ return gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privkeys, privkeysPasswords, nil
+}
+
+// CreateDecryptCryptoConfig creates the CryptoConfig object that contains the necessary
+// information to perform decryption from command line options and possibly
+// LayerInfos describing the image and helping us to query for the PGP decryption keys
+func CreateDecryptCryptoConfig(keys []string, decRecipients []string) (encconfig.CryptoConfig, error) {
+ ccs := []encconfig.CryptoConfig{}
+
+ // x509 cert is needed for PKCS7 decryption
+ _, _, x509s, err := processRecipientKeys(decRecipients)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+
+ // x509 certs can also be passed in via keys
+ x509FromKeys, err := processx509Certs(keys)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ x509s = append(x509s, x509FromKeys...)
+
+ gpgSecretKeyRingFiles, gpgSecretKeyPasswords, privKeys, privKeysPasswords, err := processPrivateKeyFiles(keys)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+
+ if len(gpgSecretKeyRingFiles) > 0 {
+ gpgCc, err := encconfig.DecryptWithGpgPrivKeys(gpgSecretKeyRingFiles, gpgSecretKeyPasswords)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ ccs = append(ccs, gpgCc)
+ }
+
+ /* TODO: Add in GPG client query for secret keys in the future.
+ _, err = createGPGClient(context)
+ gpgInstalled := err == nil
+ if gpgInstalled {
+ if len(gpgSecretKeyRingFiles) == 0 && len(privKeys) == 0 && descs != nil {
+ // Get pgp private keys from keyring only if no private key was passed
+ gpgPrivKeys, gpgPrivKeyPasswords, err := getGPGPrivateKeys(context, gpgSecretKeyRingFiles, descs, true)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+
+ gpgCc, err := encconfig.DecryptWithGpgPrivKeys(gpgPrivKeys, gpgPrivKeyPasswords)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ ccs = append(ccs, gpgCc)
+
+ } else if len(gpgSecretKeyRingFiles) > 0 {
+ gpgCc, err := encconfig.DecryptWithGpgPrivKeys(gpgSecretKeyRingFiles, gpgSecretKeyPasswords)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ ccs = append(ccs, gpgCc)
+
+ }
+ }
+ */
+
+ x509sCc, err := encconfig.DecryptWithX509s(x509s)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ ccs = append(ccs, x509sCc)
+
+ privKeysCc, err := encconfig.DecryptWithPrivKeys(privKeys, privKeysPasswords)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ ccs = append(ccs, privKeysCc)
+
+ return encconfig.CombineCryptoConfigs(ccs), nil
+}
+
+// CreateCryptoConfig from the list of recipient strings and list of key paths of private keys
+func CreateCryptoConfig(recipients []string, keys []string) (encconfig.CryptoConfig, error) {
+ var decryptCc *encconfig.CryptoConfig
+ ccs := []encconfig.CryptoConfig{}
+ if len(keys) > 0 {
+ dcc, err := CreateDecryptCryptoConfig(keys, []string{})
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ decryptCc = &dcc
+ ccs = append(ccs, dcc)
+ }
+
+ if len(recipients) > 0 {
+ gpgRecipients, pubKeys, x509s, err := processRecipientKeys(recipients)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ encryptCcs := []encconfig.CryptoConfig{}
+
+ // Create GPG client with guessed GPG version and default homedir
+ gpgClient, err := ocicrypt.NewGPGClient("", "")
+ gpgInstalled := err == nil
+ if len(gpgRecipients) > 0 && gpgInstalled {
+ gpgPubRingFile, err := gpgClient.ReadGPGPubRingFile()
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+
+ gpgCc, err := encconfig.EncryptWithGpg(gpgRecipients, gpgPubRingFile)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ encryptCcs = append(encryptCcs, gpgCc)
+ }
+
+ // Create Encryption Crypto Config
+ pkcs7Cc, err := encconfig.EncryptWithPkcs7(x509s)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ encryptCcs = append(encryptCcs, pkcs7Cc)
+
+ jweCc, err := encconfig.EncryptWithJwe(pubKeys)
+ if err != nil {
+ return encconfig.CryptoConfig{}, err
+ }
+ encryptCcs = append(encryptCcs, jweCc)
+ ecc := encconfig.CombineCryptoConfigs(encryptCcs)
+ if decryptCc != nil {
+ ecc.EncryptConfig.AttachDecryptConfig(decryptCc.DecryptConfig)
+ }
+ ccs = append(ccs, ecc)
+ }
+
+ if len(ccs) > 0 {
+ return encconfig.CombineCryptoConfigs(ccs), nil
+ } else {
+ return encconfig.CryptoConfig{}, nil
+ }
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6c425ac06..a3c6d4246 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -70,7 +70,7 @@ github.com/containernetworking/plugins/pkg/utils/hwaddr
github.com/containernetworking/plugins/pkg/utils/sysctl
github.com/containernetworking/plugins/plugins/ipam/host-local/backend
github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator
-# github.com/containers/buildah v1.19.3
+# github.com/containers/buildah v1.19.4
github.com/containers/buildah
github.com/containers/buildah/bind
github.com/containers/buildah/chroot
@@ -161,6 +161,7 @@ github.com/containers/libtrust
github.com/containers/ocicrypt
github.com/containers/ocicrypt/blockcipher
github.com/containers/ocicrypt/config
+github.com/containers/ocicrypt/helpers
github.com/containers/ocicrypt/keywrap
github.com/containers/ocicrypt/keywrap/jwe
github.com/containers/ocicrypt/keywrap/pgp