summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile12
-rw-r--r--cmd/podman/common/create.go17
-rw-r--r--cmd/podman/common/create_opts.go7
-rw-r--r--cmd/podman/common/netflags.go53
-rw-r--r--cmd/podman/containers/checkpoint.go4
-rw-r--r--cmd/podman/containers/create.go5
-rw-r--r--cmd/podman/containers/run.go4
-rw-r--r--cmd/podman/machine/init.go48
-rw-r--r--cmd/podman/machine/list.go23
-rw-r--r--cmd/podman/machine/machine.go6
-rw-r--r--cmd/podman/machine/machine_unsupported.go2
-rw-r--r--cmd/podman/machine/platform.go12
-rw-r--r--cmd/podman/machine/platform_windows.go10
-rw-r--r--cmd/podman/machine/rm.go15
-rw-r--r--cmd/podman/machine/ssh.go29
-rw-r--r--cmd/podman/machine/start.go22
-rw-r--r--cmd/podman/machine/stop.go15
-rw-r--r--cmd/winpath/main.go184
-rwxr-xr-xcontrib/cirrus/setup_environment.sh5
-rw-r--r--contrib/msi/podman.wxs12
-rw-r--r--docs/source/_static/api.html2
-rw-r--r--docs/source/markdown/podman-container-checkpoint.1.md18
-rw-r--r--docs/source/markdown/podman-container-restore.1.md10
-rw-r--r--docs/source/markdown/podman-create.1.md37
-rw-r--r--docs/source/markdown/podman-pod-create.1.md9
-rw-r--r--docs/source/markdown/podman-run.1.md42
-rw-r--r--docs/source/markdown/podman-search.1.md2
-rw-r--r--docs/tutorials/mac_experimental.md2
-rw-r--r--go.mod11
-rw-r--r--go.sum41
-rwxr-xr-xhack/libsubid_tag.sh10
-rw-r--r--libpod/container.go9
-rw-r--r--libpod/container_config.go4
-rw-r--r--libpod/container_inspect.go39
-rw-r--r--libpod/container_internal.go12
-rw-r--r--libpod/container_internal_linux.go210
-rw-r--r--libpod/define/annotations.go12
-rw-r--r--libpod/define/container_inspect.go40
-rw-r--r--libpod/healthcheck_linux.go10
-rw-r--r--libpod/kube.go2
-rw-r--r--libpod/network/internal/util/util.go2
-rw-r--r--libpod/options.go22
-rw-r--r--libpod/runtime_img.go17
-rw-r--r--pkg/api/handlers/compat/containers.go2
-rw-r--r--pkg/api/handlers/compat/info.go1
-rw-r--r--pkg/api/handlers/libpod/containers_create.go6
-rw-r--r--pkg/api/handlers/utils/images.go2
-rw-r--r--pkg/api/server/docs.go4
-rw-r--r--pkg/criu/criu.go22
-rw-r--r--pkg/criu/criu_unsupported.go7
-rw-r--r--pkg/domain/entities/containers.go1
-rw-r--r--pkg/domain/entities/pods.go2
-rw-r--r--pkg/domain/infra/abi/containers.go1
-rw-r--r--pkg/machine/config.go16
-rw-r--r--pkg/machine/connection.go3
-rw-r--r--pkg/machine/fcos.go29
-rw-r--r--pkg/machine/fedora.go122
-rw-r--r--pkg/machine/ignition.go55
-rw-r--r--pkg/machine/ignition_schema.go2
-rw-r--r--pkg/machine/ignition_windows.go7
-rw-r--r--pkg/machine/keys.go47
-rw-r--r--pkg/machine/machine_unsupported.go2
-rw-r--r--pkg/machine/pull.go96
-rw-r--r--pkg/machine/qemu/config.go4
-rw-r--r--pkg/machine/qemu/machine.go72
-rw-r--r--pkg/machine/qemu/machine_unsupported.go2
-rw-r--r--pkg/machine/wsl/machine.go1119
-rw-r--r--pkg/machine/wsl/machine_unsupported.go3
-rw-r--r--pkg/machine/wsl/util_windows.go338
-rw-r--r--pkg/rootless/rootless_linux.go4
-rw-r--r--pkg/specgen/generate/container.go4
-rw-r--r--pkg/specgen/generate/container_create.go14
-rw-r--r--pkg/specgen/generate/validate.go4
-rw-r--r--pkg/specgen/specgen.go5
-rw-r--r--pkg/specgenutil/specgen.go12
-rw-r--r--pkg/util/utils.go12
-rw-r--r--test/apiv2/10-images.at4
-rw-r--r--test/apiv2/20-containers.at4
-rw-r--r--test/e2e/build_test.go12
-rw-r--r--test/e2e/checkpoint_test.go173
-rw-r--r--test/e2e/generate_kube_test.go24
-rw-r--r--test/e2e/run_memory_test.go42
-rw-r--r--test/e2e/run_passwd_test.go12
-rw-r--r--test/e2e/run_staticip_test.go15
-rw-r--r--test/python/docker/compat/test_images.py1
-rw-r--r--test/system/010-images.bats60
-rw-r--r--test/system/030-run.bats47
-rw-r--r--test/system/170-run-userns.bats38
-rw-r--r--test/system/500-networking.bats3
-rw-r--r--test/system/helpers.bash10
-rw-r--r--vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go1
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/Makefile9
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/features.go45
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/main.go14
-rw-r--r--vendor/github.com/containers/common/pkg/config/config.go2
-rw-r--r--vendor/github.com/containers/common/pkg/config/containers.conf5
-rw-r--r--vendor/github.com/containers/common/pkg/config/default.go3
-rw-r--r--vendor/github.com/containers/common/pkg/config/default_linux.go11
-rw-r--r--vendor/github.com/containers/common/pkg/config/default_unsupported.go13
-rw-r--r--vendor/github.com/containers/common/pkg/config/default_windows.go28
-rw-r--r--vendor/github.com/containers/storage/go.mod2
-rw-r--r--vendor/github.com/containers/storage/go.sum3
-rw-r--r--vendor/github.com/containers/storage/layers.go43
-rw-r--r--vendor/github.com/containers/storage/pkg/homedir/homedir.go52
-rw-r--r--vendor/github.com/containers/storage/pkg/homedir/homedir_others.go15
-rw-r--r--vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go45
-rw-r--r--vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go7
-rw-r--r--vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go10
-rw-r--r--vendor/github.com/containers/storage/types/options.go33
-rw-r--r--vendor/golang.org/x/sys/windows/registry/key.go207
-rw-r--r--vendor/golang.org/x/sys/windows/registry/mksyscall.go10
-rw-r--r--vendor/golang.org/x/sys/windows/registry/syscall.go33
-rw-r--r--vendor/golang.org/x/sys/windows/registry/value.go387
-rw-r--r--vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go117
-rw-r--r--vendor/modules.txt12
115 files changed, 4093 insertions, 527 deletions
diff --git a/Makefile b/Makefile
index 3009f618d..37ad99412 100644
--- a/Makefile
+++ b/Makefile
@@ -358,6 +358,16 @@ podman-remote-windows: ## Build podman-remote for Windows
GOOS=windows \
bin/windows/podman.exe
+.PHONY: podman-winpath
+podman-winpath: .gopathok $(SOURCES) go.mod go.sum
+ CGO_ENABLED=0 \
+ GOOS=windows \
+ $(GO) build \
+ $(BUILDFLAGS) \
+ -ldflags -H=windowsgui \
+ -o bin/windows/winpath.exe \
+ ./cmd/winpath
+
.PHONY: podman-remote-darwin
podman-remote-darwin: ## Build podman-remote for macOS
$(MAKE) \
@@ -685,7 +695,7 @@ podman-remote-release-%.zip: test/version/version ## Build podman-remote for %=$
.PHONY: podman.msi
podman.msi: test/version/version ## Build podman-remote, package for installation on Windows
$(MAKE) podman-v$(RELEASE_NUMBER).msi
-podman-v$(RELEASE_NUMBER).msi: podman-remote-windows podman-remote-windows-docs
+podman-v$(RELEASE_NUMBER).msi: podman-remote-windows podman-remote-windows-docs podman-winpath
$(eval DOCFILE := docs/build/remote/windows)
find $(DOCFILE) -print | \
wixl-heat --var var.ManSourceDir --component-group ManFiles \
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index dad79348d..f02c5713b 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -292,6 +292,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"Set proxy environment variables in the container based on the host proxy vars",
)
+ hostUserFlagName := "hostuser"
+ createFlags.StringSliceVar(
+ &cf.HostUsers,
+ hostUserFlagName, []string{},
+ "Host user account to add to /etc/passwd within container",
+ )
+ _ = cmd.RegisterFlagCompletionFunc(hostUserFlagName, completion.AutocompleteNone)
+
imageVolumeFlagName := "image-volume"
createFlags.StringVar(
&cf.ImageVolume,
@@ -327,13 +335,10 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace)
- kernelMemoryFlagName := "kernel-memory"
- createFlags.StringVar(
- &cf.KernelMemory,
- kernelMemoryFlagName, "",
- "Kernel memory limit "+sizeWithUnitFormat,
+ createFlags.String(
+ "kernel-memory", "",
+ "DEPRECATED: Option is just hear for compatibility with Docker",
)
- _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone)
// kernel-memory is deprecated in the runtime spec.
_ = createFlags.MarkHidden("kernel-memory")
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index 990c1c063..f2335a2be 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -18,7 +18,6 @@ import (
"github.com/containers/podman/v3/pkg/specgen"
"github.com/docker/docker/api/types/mount"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
func stringMaptoArray(m map[string]string) []string {
@@ -385,9 +384,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
if cc.HostConfig.Memory > 0 {
cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
}
- if cc.HostConfig.KernelMemory > 0 {
- logrus.Warnf("The --kernel-memory flag has been deprecated. May not work properly on your system.")
- }
if cc.HostConfig.MemoryReservation > 0 {
cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
@@ -409,9 +405,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
cliOpts.ShmSize = strconv.Itoa(int(cc.HostConfig.ShmSize))
}
- if cc.HostConfig.KernelMemory > 0 {
- cliOpts.KernelMemory = strconv.Itoa(int(cc.HostConfig.KernelMemory))
- }
if len(cc.HostConfig.RestartPolicy.Name) > 0 {
policy := cc.HostConfig.RestartPolicy.Name
// only add restart count on failure
diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go
index ba8ab7a8b..425d85c9d 100644
--- a/cmd/podman/common/netflags.go
+++ b/cmd/podman/common/netflags.go
@@ -53,6 +53,13 @@ func DefineNetFlags(cmd *cobra.Command) {
)
_ = cmd.RegisterFlagCompletionFunc(ipFlagName, completion.AutocompleteNone)
+ ip6FlagName := "ip6"
+ netFlags.String(
+ ip6FlagName, "",
+ "Specify a static IPv6 address for the container",
+ )
+ _ = cmd.RegisterFlagCompletionFunc(ip6FlagName, completion.AutocompleteNone)
+
macAddressFlagName := "mac-address"
netFlags.String(
macAddressFlagName, "",
@@ -185,7 +192,7 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
opts.Networks = networks
}
- if flags.Changed("ip") || flags.Changed("mac-address") || flags.Changed("network-alias") {
+ if flags.Changed("ip") || flags.Changed("ip6") || flags.Changed("mac-address") || flags.Changed("network-alias") {
// if there is no network we add the default
if len(opts.Networks) == 0 {
opts.Networks = map[string]types.PerNetworkOptions{
@@ -193,29 +200,31 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
}
}
- ip, err := flags.GetString("ip")
- if err != nil {
- return nil, err
- }
- if ip != "" {
- // if pod create --infra=false
- if infra, err := flags.GetBool("infra"); err == nil && !infra {
- return nil, errors.Wrap(define.ErrInvalidArg, "cannot set --ip without infra container")
+ for _, ipFlagName := range []string{"ip", "ip6"} {
+ ip, err := flags.GetString(ipFlagName)
+ if err != nil {
+ return nil, err
}
+ if ip != "" {
+ // if pod create --infra=false
+ if infra, err := flags.GetBool("infra"); err == nil && !infra {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set --%s without infra container", ipFlagName)
+ }
- staticIP := net.ParseIP(ip)
- if staticIP == nil {
- return nil, errors.Errorf("%s is not an ip address", ip)
- }
- if !opts.Network.IsBridge() && !opts.Network.IsDefault() {
- return nil, errors.Wrap(define.ErrInvalidArg, "--ip can only be set when the network mode is bridge")
- }
- if len(opts.Networks) != 1 {
- return nil, errors.Wrap(define.ErrInvalidArg, "--ip can only be set for a single network")
- }
- for name, netOpts := range opts.Networks {
- netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
- opts.Networks[name] = netOpts
+ staticIP := net.ParseIP(ip)
+ if staticIP == nil {
+ return nil, errors.Errorf("%q is not an ip address", ip)
+ }
+ if !opts.Network.IsBridge() && !opts.Network.IsDefault() {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "--%s can only be set when the network mode is bridge", ipFlagName)
+ }
+ if len(opts.Networks) != 1 {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "--%s can only be set for a single network", ipFlagName)
+ }
+ for name, netOpts := range opts.Networks {
+ netOpts.StaticIPs = append(netOpts.StaticIPs, staticIP)
+ opts.Networks[name] = netOpts
+ }
}
}
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go
index e8dd25978..43a1b75e5 100644
--- a/cmd/podman/containers/checkpoint.go
+++ b/cmd/podman/containers/checkpoint.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
"github.com/containers/podman/v3/cmd/podman/validate"
+ "github.com/containers/podman/v3/pkg/criu"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/storage/pkg/archive"
@@ -113,6 +114,9 @@ func checkpoint(cmd *cobra.Command, args []string) error {
if checkpointOptions.WithPrevious && checkpointOptions.PreCheckPoint {
return errors.Errorf("--with-previous can not be used with --pre-checkpoint")
}
+ if (checkpointOptions.WithPrevious || checkpointOptions.PreCheckPoint) && !criu.MemTrack() {
+ return errors.New("system (architecture/kernel/CRIU) does not support memory tracking")
+ }
responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions)
if err != nil {
return err
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index e004f4ab2..9610c29dc 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -21,6 +21,7 @@ import (
"github.com/containers/podman/v3/pkg/util"
"github.com/mattn/go-isatty"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -191,6 +192,10 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
vals.UserNS = "private"
}
}
+ if c.Flag("kernel-memory") != nil && c.Flag("kernel-memory").Changed {
+ logrus.Warnf("The --kernel-memory flag is no longer supported. This flag is a noop.")
+ }
+
if cliVals.LogDriver == define.PassthroughLogging {
if isatty.IsTerminal(0) || isatty.IsTerminal(1) || isatty.IsTerminal(2) {
return vals, errors.New("the '--log-driver passthrough' option cannot be used on a TTY")
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index cfb89ce57..b9a2c3bb5 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -83,6 +83,9 @@ func runFlags(cmd *cobra.Command) {
_ = cmd.RegisterFlagCompletionFunc(gpuFlagName, completion.AutocompleteNone)
_ = flags.MarkHidden("gpus")
+ passwdFlagName := "passwd"
+ flags.BoolVar(&runOpts.Passwd, passwdFlagName, true, "add entries to /etc/passwd and /etc/group")
+
if registry.IsRemote() {
_ = flags.MarkHidden("preserve-fds")
_ = flags.MarkHidden("conmon-pidfile")
@@ -191,6 +194,7 @@ func run(cmd *cobra.Command, args []string) error {
return err
}
s.RawImageName = rawImageName
+ s.Passwd = &runOpts.Passwd
runOpts.Spec = s
if _, err := createPodIfNecessary(cmd, s, cliVals.Net); err != nil {
diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go
index bee6844df..14e87c201 100644
--- a/cmd/podman/machine/init.go
+++ b/cmd/podman/machine/init.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -8,7 +8,6 @@ import (
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -38,6 +37,8 @@ func init() {
})
flags := initCmd.Flags()
cfg := registry.PodmanConfig()
+ initOpts.Username = cfg.Config.Machine.User
+
cpusFlagName := "cpus"
flags.Uint64Var(
&initOpts.CPUS,
@@ -76,6 +77,13 @@ func init() {
flags.StringVar(&initOpts.TimeZone, timezoneFlagName, defaultTz, "Set timezone")
_ = initCmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteDefault)
+ flags.BoolVar(
+ &initOpts.ReExec,
+ "reexec", false,
+ "process was rexeced",
+ )
+ flags.MarkHidden("reexec")
+
ImagePathFlagName := "image-path"
flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Machine.Image, "Path to qcow image")
_ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault)
@@ -88,33 +96,47 @@ func init() {
// TODO should we allow for a users to append to the qemu cmdline?
func initMachine(cmd *cobra.Command, args []string) error {
var (
- vm machine.VM
- vmType string
- err error
+ vm machine.VM
+ err error
)
+
+ provider := getSystemDefaultProvider()
initOpts.Name = defaultMachineName
if len(args) > 0 {
initOpts.Name = args[0]
}
- switch vmType {
- default: // qemu is the default
- if _, err := qemu.LoadVMByName(initOpts.Name); err == nil {
- return errors.Wrap(machine.ErrVMAlreadyExists, initOpts.Name)
- }
- vm, err = qemu.NewMachine(initOpts)
+ if _, err := provider.LoadVMByName(initOpts.Name); err == nil {
+ return errors.Wrap(machine.ErrVMAlreadyExists, initOpts.Name)
}
+
+ vm, err = provider.NewMachine(initOpts)
if err != nil {
return err
}
- err = vm.Init(initOpts)
- if err != nil {
+
+ if finished, err := vm.Init(initOpts); err != nil || !finished {
+ // Finished = true, err = nil - Success! Log a message with further instructions
+ // Finished = false, err = nil - The installation is partially complete and podman should
+ // exit gracefully with no error and no success message.
+ // Examples:
+ // - a user has chosen to perform their own reboot
+ // - reexec for limited admin operations, returning to parent
+ // Finished = *, err != nil - Exit with an error message
+
return err
}
+ fmt.Println("Machine init complete")
if now {
err = vm.Start(initOpts.Name, machine.StartOptions{})
if err == nil {
fmt.Printf("Machine %q started successfully\n", initOpts.Name)
}
+ } else {
+ extra := ""
+ if initOpts.Name != defaultMachineName {
+ extra = " " + initOpts.Name
+ }
+ fmt.Printf("To start your machine run:\n\n\tpodman machine start%s\n\n", extra)
}
return err
}
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 774ab4fd0..858d87401 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -1,4 +1,5 @@
-// +build amd64,!windows arm64,!windows
+//go:build amd64 || arm64
+// +build amd64 arm64
package machine
@@ -16,7 +17,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -69,9 +69,14 @@ func init() {
}
func list(cmd *cobra.Command, args []string) error {
- var opts machine.ListOptions
- // We only have qemu VM's for now
- listResponse, err := qemu.List(opts)
+ var (
+ opts machine.ListOptions
+ listResponse []*machine.ListResponse
+ err error
+ )
+
+ provider := getSystemDefaultProvider()
+ listResponse, err = provider.List(opts)
if err != nil {
return errors.Wrap(err, "error listing vms")
}
@@ -182,8 +187,8 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
response.Stream = streamName(vm.Stream)
response.VMType = vm.VMType
response.CPUs = vm.CPUs
- response.Memory = strUint(vm.Memory * units.MiB)
- response.DiskSize = strUint(vm.DiskSize * units.GiB)
+ response.Memory = strUint(vm.Memory)
+ response.DiskSize = strUint(vm.DiskSize)
machineResponses = append(machineResponses, response)
}
@@ -214,8 +219,8 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago"
response.VMType = vm.VMType
response.CPUs = vm.CPUs
- response.Memory = units.HumanSize(float64(vm.Memory) * units.MiB)
- response.DiskSize = units.HumanSize(float64(vm.DiskSize) * units.GiB)
+ response.Memory = units.HumanSize(float64(vm.Memory))
+ response.DiskSize = units.HumanSize(float64(vm.DiskSize))
humanResponses = append(humanResponses, response)
}
diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go
index 8ff9055f0..22ffbbee7 100644
--- a/cmd/podman/machine/machine.go
+++ b/cmd/podman/machine/machine.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -8,7 +8,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/spf13/cobra"
)
@@ -51,7 +50,8 @@ func autocompleteMachine(cmd *cobra.Command, args []string, toComplete string) (
func getMachines(toComplete string) ([]string, cobra.ShellCompDirective) {
suggestions := []string{}
- machines, err := qemu.List(machine.ListOptions{})
+ provider := getSystemDefaultProvider()
+ machines, err := provider.List(machine.ListOptions{})
if err != nil {
cobra.CompErrorln(err.Error())
return nil, cobra.ShellCompDirectiveNoFileComp
diff --git a/cmd/podman/machine/machine_unsupported.go b/cmd/podman/machine/machine_unsupported.go
index f8392694a..2f4189446 100644
--- a/cmd/podman/machine/machine_unsupported.go
+++ b/cmd/podman/machine/machine_unsupported.go
@@ -1,4 +1,4 @@
-// +build !amd64 amd64,windows
+// +build !amd64,!arm64
package machine
diff --git a/cmd/podman/machine/platform.go b/cmd/podman/machine/platform.go
new file mode 100644
index 000000000..fc3186205
--- /dev/null
+++ b/cmd/podman/machine/platform.go
@@ -0,0 +1,12 @@
+// +build amd64,!windows arm64,!windows
+
+package machine
+
+import (
+ "github.com/containers/podman/v3/pkg/machine"
+ "github.com/containers/podman/v3/pkg/machine/qemu"
+)
+
+func getSystemDefaultProvider() machine.Provider {
+ return qemu.GetQemuProvider()
+}
diff --git a/cmd/podman/machine/platform_windows.go b/cmd/podman/machine/platform_windows.go
new file mode 100644
index 000000000..a4a35e712
--- /dev/null
+++ b/cmd/podman/machine/platform_windows.go
@@ -0,0 +1,10 @@
+package machine
+
+import (
+ "github.com/containers/podman/v3/pkg/machine"
+ "github.com/containers/podman/v3/pkg/machine/wsl"
+)
+
+func getSystemDefaultProvider() machine.Provider {
+ return wsl.GetWSLProvider()
+}
diff --git a/cmd/podman/machine/rm.go b/cmd/podman/machine/rm.go
index c17399c78..c58e74a42 100644
--- a/cmd/podman/machine/rm.go
+++ b/cmd/podman/machine/rm.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -10,7 +10,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/spf13/cobra"
)
@@ -52,18 +51,16 @@ func init() {
func rm(cmd *cobra.Command, args []string) error {
var (
- err error
- vm machine.VM
- vmType string
+ err error
+ vm machine.VM
)
vmName := defaultMachineName
if len(args) > 0 && len(args[0]) > 0 {
vmName = args[0]
}
- switch vmType {
- default:
- vm, err = qemu.LoadVMByName(vmName)
- }
+
+ provider := getSystemDefaultProvider()
+ vm, err = provider.LoadVMByName(vmName)
if err != nil {
return err
}
diff --git a/cmd/podman/machine/ssh.go b/cmd/podman/machine/ssh.go
index da0a09338..5ef34afc6 100644
--- a/cmd/podman/machine/ssh.go
+++ b/cmd/podman/machine/ssh.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -9,7 +9,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -47,27 +46,24 @@ func ssh(cmd *cobra.Command, args []string) error {
err error
validVM bool
vm machine.VM
- vmType string
)
// Set the VM to default
vmName := defaultMachineName
+ provider := getSystemDefaultProvider()
// If len is greater than 0, it means we may have been
// provided the VM name. If so, we check. The VM name,
// if provided, must be in args[0].
if len(args) > 0 {
- switch vmType {
- default:
- validVM, err = qemu.IsValidVMName(args[0])
- if err != nil {
- return err
- }
- if validVM {
- vmName = args[0]
- } else {
- sshOpts.Args = append(sshOpts.Args, args[0])
- }
+ validVM, err = provider.IsValidVMName(args[0])
+ if err != nil {
+ return err
+ }
+ if validVM {
+ vmName = args[0]
+ } else {
+ sshOpts.Args = append(sshOpts.Args, args[0])
}
}
@@ -88,10 +84,7 @@ func ssh(cmd *cobra.Command, args []string) error {
}
}
- switch vmType {
- default:
- vm, err = qemu.LoadVMByName(vmName)
- }
+ vm, err = provider.LoadVMByName(vmName)
if err != nil {
return errors.Wrapf(err, "vm %s not found", vmName)
}
diff --git a/cmd/podman/machine/start.go b/cmd/podman/machine/start.go
index 4ae31e6de..9c9c24f64 100644
--- a/cmd/podman/machine/start.go
+++ b/cmd/podman/machine/start.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -7,7 +7,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -33,30 +32,31 @@ func init() {
func start(cmd *cobra.Command, args []string) error {
var (
- err error
- vm machine.VM
- vmType string
+ err error
+ vm machine.VM
)
vmName := defaultMachineName
if len(args) > 0 && len(args[0]) > 0 {
vmName = args[0]
}
- // We only have qemu VM's for now
- active, activeName, err := qemu.CheckActiveVM()
+ provider := getSystemDefaultProvider()
+ vm, err = provider.LoadVMByName(vmName)
if err != nil {
return err
}
+
+ active, activeName, cerr := provider.CheckExclusiveActiveVM()
+ if cerr != nil {
+ return cerr
+ }
if active {
if vmName == activeName {
return errors.Wrapf(machine.ErrVMAlreadyRunning, "cannot start VM %s", vmName)
}
return errors.Wrapf(machine.ErrMultipleActiveVM, "cannot start VM %s. VM %s is currently running", vmName, activeName)
}
- switch vmType {
- default:
- vm, err = qemu.LoadVMByName(vmName)
- }
+ vm, err = provider.LoadVMByName(vmName)
if err != nil {
return err
}
diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go
index 75666f734..17969298b 100644
--- a/cmd/podman/machine/stop.go
+++ b/cmd/podman/machine/stop.go
@@ -1,4 +1,5 @@
-// +build amd64,!windows arm64,!windows
+//go:build amd64 || arm64
+// +build amd64 arm64
package machine
@@ -7,7 +8,6 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
- "github.com/containers/podman/v3/pkg/machine/qemu"
"github.com/spf13/cobra"
)
@@ -33,18 +33,15 @@ func init() {
// TODO Name shouldn't be required, need to create a default vm
func stop(cmd *cobra.Command, args []string) error {
var (
- err error
- vm machine.VM
- vmType string
+ err error
+ vm machine.VM
)
vmName := defaultMachineName
if len(args) > 0 && len(args[0]) > 0 {
vmName = args[0]
}
- switch vmType {
- default:
- vm, err = qemu.LoadVMByName(vmName)
- }
+ provider := getSystemDefaultProvider()
+ vm, err = provider.LoadVMByName(vmName)
if err != nil {
return err
}
diff --git a/cmd/winpath/main.go b/cmd/winpath/main.go
new file mode 100644
index 000000000..494d1cf3c
--- /dev/null
+++ b/cmd/winpath/main.go
@@ -0,0 +1,184 @@
+//go:build windows
+// +build windows
+
+package main
+
+import (
+ "errors"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows/registry"
+)
+
+type operation int
+
+const (
+ HWND_BROADCAST = 0xFFFF
+ WM_SETTINGCHANGE = 0x001A
+ SMTO_ABORTIFHUNG = 0x0002
+ ERR_BAD_ARGS = 0x000A
+ OPERATION_FAILED = 0x06AC
+ Environment = "Environment"
+ Add operation = iota
+ Remove
+ NotSpecified
+)
+
+func main() {
+ op := NotSpecified
+ if len(os.Args) >= 2 {
+ switch os.Args[1] {
+ case "add":
+ op = Add
+ case "remove":
+ op = Remove
+ }
+ }
+
+ // Stay silent since ran from an installer
+ if op == NotSpecified {
+ alert("Usage: " + filepath.Base(os.Args[0]) + " [add|remove]\n\nThis utility adds or removes the podman directory to the Windows Path.")
+ os.Exit(ERR_BAD_ARGS)
+ }
+
+ if err := modify(op); err != nil {
+ os.Exit(OPERATION_FAILED)
+ }
+}
+
+func modify(op operation) error {
+ exe, err := os.Executable()
+ if err != nil {
+ return err
+ }
+ exe, err = filepath.EvalSymlinks(exe)
+ if err != nil {
+ return err
+ }
+ target := filepath.Dir(exe)
+
+ if op == Remove {
+ return removePathFromRegistry(target)
+ }
+
+ return addPathToRegistry(target)
+}
+
+// Appends a directory to the Windows Path stored in the registry
+func addPathToRegistry(dir string) error {
+ k, _, err := registry.CreateKey(registry.CURRENT_USER, Environment, registry.WRITE|registry.READ)
+ if err != nil {
+ return err
+ }
+
+ defer k.Close()
+
+ existing, typ, err := k.GetStringValue("Path")
+ if err != nil {
+ return err
+ }
+
+ // Is this directory already on the windows path?
+ for _, element := range strings.Split(existing, ";") {
+ if strings.EqualFold(element, dir) {
+ // Path already added
+ return nil
+ }
+ }
+
+ // If the existing path is empty we don't want to start with a delimiter
+ if len(existing) > 0 {
+ existing += ";"
+ }
+
+ existing += dir
+
+ // It's important to preserve the registry key type so that it will be interpreted correctly
+ // EXPAND = evaluate variables in the expression, e.g. %PATH% should be expanded to the system path
+ // STRING = treat the contents as a string literal
+ if typ == registry.EXPAND_SZ {
+ err = k.SetExpandStringValue("Path", existing)
+ } else {
+ err = k.SetStringValue("Path", existing)
+ }
+
+ if err == nil {
+ broadcastEnvironmentChange()
+ }
+
+ return err
+}
+
+// Removes all occurences of a directory path from the Windows path stored in the registry
+func removePathFromRegistry(path string) error {
+ k, err := registry.OpenKey(registry.CURRENT_USER, Environment, registry.READ|registry.WRITE)
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ // Nothing to cleanup, the Environment registry key does not exist.
+ return nil
+ }
+ return err
+ }
+
+ defer k.Close()
+
+ existing, typ, err := k.GetStringValue("Path")
+ if err != nil {
+ return err
+ }
+
+ var elements []string
+ for _, element := range strings.Split(existing, ";") {
+ if strings.EqualFold(element, path) {
+ continue
+ }
+ elements = append(elements, element)
+ }
+
+ newPath := strings.Join(elements, ";")
+ // Preserve value type (see corresponding comment above)
+ if typ == registry.EXPAND_SZ {
+ err = k.SetExpandStringValue("Path", newPath)
+ } else {
+ err = k.SetStringValue("Path", newPath)
+ }
+
+ if err == nil {
+ broadcastEnvironmentChange()
+ }
+
+ return err
+}
+
+// Sends a notification message to all top level windows informing them the environmental setings have changed.
+// Applications such as the Windows command prompt and powershell will know to stop caching stale values on
+// subsequent restarts. Since applications block the sender when receiving a message, we set a 3 second timeout
+func broadcastEnvironmentChange() {
+ env, _ := syscall.UTF16PtrFromString(Environment)
+ user32 := syscall.NewLazyDLL("user32")
+ proc := user32.NewProc("SendMessageTimeoutW")
+ millis := 3000
+ _, _, _ = proc.Call(HWND_BROADCAST, WM_SETTINGCHANGE, 0, uintptr(unsafe.Pointer(env)), SMTO_ABORTIFHUNG, uintptr(millis), 0)
+}
+
+// Creates an "error" style pop-up window
+func alert(caption string) int {
+ // Error box style
+ format := 0x10
+
+ user32 := syscall.NewLazyDLL("user32.dll")
+ captionPtr, _ := syscall.UTF16PtrFromString(caption)
+ titlePtr, _ := syscall.UTF16PtrFromString("winpath")
+ ret, _, _ := user32.NewProc("MessageBoxW").Call(
+ uintptr(0),
+ uintptr(unsafe.Pointer(captionPtr)),
+ uintptr(unsafe.Pointer(titlePtr)),
+ uintptr(format))
+
+ return int(ret)
+}
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index 8f535c7e7..43c709228 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -118,6 +118,11 @@ fi
case "$OS_RELEASE_ID" in
ubuntu) ;;
fedora)
+ # Force a crun version that has this fix: https://github.com/containers/crun/pull/819
+ # FIXME: Remove once a fixed crun made its way into Fedora
+ if test "$OS_RELEASE_VER" == "35"; then
+ yum upgrade -y https://kojipkgs.fedoraproject.org//work/tasks/684/80280684/crun-1.3-2.fc35.x86_64.rpm
+ fi
if ((CONTAINER==0)); then
# All SELinux distros need this for systemd-in-a-container
msg "Enabling container_manage_cgroup"
diff --git a/contrib/msi/podman.wxs b/contrib/msi/podman.wxs
index aade2afdb..c1b8f1a23 100644
--- a/contrib/msi/podman.wxs
+++ b/contrib/msi/podman.wxs
@@ -26,18 +26,21 @@
<Component Id="MainExecutable" Guid="73752F94-6589-4C7B-ABED-39D655A19714" Win64="Yes">
<File Id="520C6E17-77A2-4F41-9611-30FA763A0702" Name="podman.exe" Source="bin/windows/podman.exe" KeyPath="yes"/>
</Component>
+ <Component Id="WinPathExecutable" Guid="00F5B731-D4A6-4B69-87B0-EA4EBAB89F95" Win64="Yes">
+ <File Id="8F507E28-A61D-4E64-A92B-B5A00F023AE8" Name="winpath.exe" Source="bin/windows/winpath.exe" KeyPath="yes"/>
+ </Component>
</Directory>
</Directory>
</Directory>
</Directory>
- <Property Id="setx" Value="setx.exe"/>
- <!-- Directory table entries have a trailing slash, so an extra backslash is needed to prevent escaping the quote -->
- <CustomAction Id="ChangePath" ExeCommand="PATH &quot;%PATH%;[INSTALLDIR]\&quot;" Property="setx" Execute="deferred" Impersonate="yes" Return="check"/>
+ <CustomAction Id="AddPath" ExeCommand="add" FileKey="8F507E28-A61D-4E64-A92B-B5A00F023AE8" Execute="deferred" Impersonate="yes" Return="check"/>
+ <CustomAction Id="RemovePath" ExeCommand="remove" FileKey="8F507E28-A61D-4E64-A92B-B5A00F023AE8" Execute="deferred" Impersonate="yes" Return="check"/>
<Feature Id="Complete" Level="1">
<ComponentRef Id="INSTALLDIR_Component"/>
<ComponentRef Id="MainExecutable"/>
+ <ComponentRef Id="WinPathExecutable"/>
<ComponentGroupRef Id="ManFiles"/>
</Feature>
@@ -46,7 +49,8 @@
<InstallExecuteSequence>
<RemoveExistingProducts Before="InstallInitialize"/>
- <Custom Action="ChangePath" After="InstallServices">NOT Installed</Custom>
+ <Custom Action="AddPath" After="InstallFiles">NOT Installed</Custom>
+ <Custom Action="RemovePath" Before="RemoveFiles" After="InstallInitiailize">(REMOVE="ALL") AND (NOT UPGRADINGPRODUCTCODE)</Custom>
</InstallExecuteSequence>
</Product>
diff --git a/docs/source/_static/api.html b/docs/source/_static/api.html
index fbc945d87..6d467d099 100644
--- a/docs/source/_static/api.html
+++ b/docs/source/_static/api.html
@@ -18,7 +18,7 @@
</style>
</head>
<body>
- <redoc spec-url='https://storage.googleapis.com/libpod-master-releases/swagger-latest.yaml' sort-props-alphabetically></redoc>
+ <redoc spec-url='https://storage.googleapis.com/libpod-master-releases/swagger-latest.yaml' sort-props-alphabetically sort-operations-alphabetically></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>
diff --git a/docs/source/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md
index 200920ca9..00d8f7095 100644
--- a/docs/source/markdown/podman-container-checkpoint.1.md
+++ b/docs/source/markdown/podman-container-checkpoint.1.md
@@ -9,6 +9,8 @@ podman\-container\-checkpoint - Checkpoints one or more running containers
## DESCRIPTION
**podman container checkpoint** checkpoints all the processes in one or more *containers*. A *container* can be restored from a checkpoint with **[podman-container-restore](podman-container-restore.1.md)**. The *container IDs* or *names* are used as input.
+*IMPORTANT: If the container is using __systemd__ as __entrypoint__ checkpointing the container might not be possible.*
+
## OPTIONS
#### **--all**, **-a**
@@ -37,7 +39,7 @@ root file-system, if not explicitly disabled using **--ignore-rootfs**.
If a checkpoint is exported to a tar.gz file it is possible with the help of **--ignore-rootfs** to explicitly disable including changes to the root file-system into the checkpoint archive file.\
The default is **false**.\
-*IMPORTANT: This OPTION only works in combination with **--export, -e**.*
+*IMPORTANT: This OPTION only works in combination with __--export, -e__.*
#### **--ignore-volumes**
@@ -68,6 +70,13 @@ Dump the *container's* memory information only, leaving the *container* running.
operations will supersede prior dumps. It only works on `runc 1.0-rc3` or `higher`.\
The default is **false**.
+The functionality to only checkpoint the memory of the container and in a second
+checkpoint only write out the memory pages which have changed since the first
+checkpoint relies on the Linux kernel's soft-dirty bit, which is not available
+on all systems as it depends on the system architecture and the configuration
+of the Linux kernel. Podman will verify if the current system supports this
+functionality and return an error if the current system does not support it.
+
#### **--print-stats**
Print out statistics about checkpointing the container(s). The output is
@@ -122,8 +131,13 @@ The default is **false**.
Check out the *container* with previous criu image files in pre-dump. It only works on `runc 1.0-rc3` or `higher`.\
The default is **false**.\
-*IMPORTANT: This OPTION is not available with **--pre-checkpoint***.
+*IMPORTANT: This OPTION is not available with __--pre-checkpoint__*.
+
+This option requires that the option __--pre-checkpoint__ has been used before on the
+same container. Without an existing pre-checkpoint, this option will fail.
+Also see __--pre-checkpoint__ for additional information about __--pre-checkpoint__
+availability on different systems.
## EXAMPLES
Make a checkpoint for the container "mywebserver".
diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md
index a4630dedf..3dfa063b8 100644
--- a/docs/source/markdown/podman-container-restore.1.md
+++ b/docs/source/markdown/podman-container-restore.1.md
@@ -39,7 +39,7 @@ The default is **false**.\
If a *container* is restored from a checkpoint tar.gz file it is possible that it also contains all root file-system changes. With **--ignore-rootfs** it is possible to explicitly disable applying these root file-system changes to the restored *container*.\
The default is **false**.\
-*IMPORTANT: This OPTION is only available in combination with **--import, -i**.*
+*IMPORTANT: This OPTION is only available in combination with __--import, -i__.*
#### **--ignore-static-ip**
@@ -98,14 +98,14 @@ If the **--name, -n** option is used, Podman will not attempt to assign the same
address to the *container* it was using before checkpointing as each IP address can only
be used once and the restored *container* will have another IP address. This also means
that **--name, -n** cannot be used in combination with **--tcp-established**.\
-*IMPORTANT: This OPTION is only available in combination with **--import, -i**.*
+*IMPORTANT: This OPTION is only available in combination with __--import, -i__.*
#### **--pod**=*name*
Restore a container into the pod *name*. The destination pod for this restore
has to have the same namespaces shared as the pod this container was checkpointed
-from (see **[podman pod create --share](podman-pod-create.1.md#--share)**).
-*IMPORTANT: This OPTION is only available in combination with **--import, -i**.*
+from (see **[podman pod create --share](podman-pod-create.1.md#--share)**).\
+*IMPORTANT: This OPTION is only available in combination with __--import, -i__.*
This option requires at least CRIU 3.16.
@@ -168,7 +168,7 @@ Import a checkpoint file and a pre-checkpoint file.
# podman container restore --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz
```
-Remove the container "mywebserver". Make a checkpoint of the container and export it. Restore the container with other port ranges from the exported file.
+Start the container "mywebserver". Make a checkpoint of the container and export it. Restore the container with other port ranges from the exported file.
```
$ podman run --rm -p 2345:80 -d webserver
# podman container checkpoint -l --export=dump.tar
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index c8f1ec3a5..e3647b194 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -410,6 +410,11 @@ Container host name
Sets the container host name that is available inside the container. Can only be used with a private UTS namespace `--uts=private` (default). If `--pod` is specified and the pod shares the UTS namespace (default) the pod's hostname will be used.
+#### **--hostuser**=*name*
+
+Add a user account to /etc/passwd from the host to the container. The Username
+or UID must exist on the host system.
+
#### **--help**
Print usage statement
@@ -469,19 +474,24 @@ Path to the container-init binary.
Keep STDIN open even if not attached. The default is *false*.
-#### **--ip6**=*ip*
-
-Not implemented
+#### **--ip**=*ipv4*
-#### **--ip**=*ip*
-
-Specify a static IP address for the container, for example **10.88.64.128**.
+Specify a static IPv4 address for the container, for example **10.88.64.128**.
This option can only be used if the container is joined to only a single network - i.e., **--network=network-name** is used at most once -
and if the container is not joining another container's network namespace via **--network=container:_id_**.
The address must be within the network's IP address pool (default **10.88.0.0/16**).
To specify multiple static IP addresses per container, set multiple networks using the **--network** option with a static IP address specified for each using the `ip` mode for that option.
+#### **--ip6**=*ipv6*
+
+Specify a static IPv6 address for the container, for example **fd46:db93:aa76:ac37::10**.
+This option can only be used if the container is joined to only a single network - i.e., **--network=network-name** is used at most once -
+and if the container is not joining another container's network namespace via **--network=container:_id_**.
+The address must be within the network's IPv6 address pool.
+
+To specify multiple static IPv6 addresses per container, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option.
+
#### **--ipc**=*ipc*
@@ -949,12 +959,13 @@ Note: Labeling can be disabled for all containers by setting label=false in the
- `no-new-privileges` : Disable container processes from gaining additional privileges
-- `seccomp=unconfined` : Turn off seccomp confinement for the container
-- `seccomp=profile.json` : White listed syscalls seccomp Json file to be used as a seccomp filter
+- `seccomp=unconfined` : Turn off seccomp confinement for the container.
+- `seccomp=profile.json` : JSON file to be used as a seccomp filter. Note that the `io.podman.annotations.seccomp` annotation is set with the specified value as shown in `podman inspect`.
- `proc-opts=OPTIONS` : Comma-separated list of options to use for the /proc mount. More details for the
possible mount options are specified in the **proc(5)** man page.
+
- **unmask**=_ALL_ or _/path/1:/path/2_, or shell expanded paths (/proc/*): Paths to unmask separated by a colon. If set to **ALL**, it will unmask all the paths that are masked or made read only by default.
The default masked paths are **/proc/acpi, /proc/kcore, /proc/keys, /proc/latency_stats, /proc/sched_debug, /proc/scsi, /proc/timer_list, /proc/timer_stats, /sys/firmware, and /sys/fs/selinux.** The default paths that are read only are **/proc/asound, /proc/bus, /proc/fs, /proc/irq, /proc/sys, /proc/sysrq-trigger, /sys/fs/cgroup**.
@@ -1142,10 +1153,20 @@ If for example _amount_ is **5** the second mapping step would look like:
| _from_uid_ + 3 | _container_uid_ + 3 |
| _from_uid_ + 4 | _container_uid_ + 4 |
+The current user ID is mapped to UID=0 in the rootless user namespace.
+Every additional range is added sequentially afterward:
+
+| host |rootless user namespace | length |
+| - | - | - |
+| $UID | 0 | 1 |
+| 1 | $FIRST_RANGE_ID | $FIRST_RANGE_LENGTH |
+| 1+$FIRST_RANGE_LENGTH | $SECOND_RANGE_ID | $SECOND_RANGE_LENGTH|
+
Even if a user does not have any subordinate UIDs in _/etc/subuid_,
**--uidmap** could still be used to map the normal UID of the user to a
container UID by running `podman create --uidmap $container_uid:0:1 --user $container_uid ...`.
+
#### **--ulimit**=*option*
Ulimit options
diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md
index b1b029429..56c3e7d34 100644
--- a/docs/source/markdown/podman-pod-create.1.md
+++ b/docs/source/markdown/podman-pod-create.1.md
@@ -127,6 +127,15 @@ The address must be within the network's IP address pool (default **10.88.0.0/16
To specify multiple static IP addresses per pod, set multiple networks using the **--network** option with a static IP address specified for each using the `ip` mode for that option.
+#### **--ip6**=*ipv6*
+
+Specify a static IPv6 address for the pod, for example **fd46:db93:aa76:ac37::10**.
+This option can only be used if the pod is joined to only a single network - i.e., **--network=network-name** is used at most once -
+and if the pod is not joining another container's network namespace via **--network=container:_id_**.
+The address must be within the network's IPv6 address pool.
+
+To specify multiple static IPv6 addresses per pod, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option.
+
#### **--label**=*label*, **-l**
Add metadata to a pod (e.g., --label com.example.key=value).
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index a6687e656..b98e563ef 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -446,6 +446,11 @@ The initialization time needed for a container to bootstrap. The value can be ex
The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the
value can be expressed in a time format such as **1m22s**. The default value is **30s**.
+#### **--hostuser**=*name*
+
+Add a user account to /etc/passwd from the host to the container. The Username
+or UID must exist on the host system.
+
#### **--help**
Print usage statement
@@ -492,19 +497,24 @@ Path to the container-init binary.
When set to **true**, keep stdin open even if not attached. The default is **false**.
-#### **--ip6**=*ip*
-
-Not implemented.
-
-#### **--ip**=*ip*
+#### **--ip**=*ipv4*
-Specify a static IP address for the container, for example **10.88.64.128**.
+Specify a static IPv4 address for the container, for example **10.88.64.128**.
This option can only be used if the container is joined to only a single network - i.e., **--network=network-name** is used at most once -
and if the container is not joining another container's network namespace via **--network=container:_id_**.
The address must be within the network's IP address pool (default **10.88.0.0/16**).
To specify multiple static IP addresses per container, set multiple networks using the **--network** option with a static IP address specified for each using the `ip` mode for that option.
+#### **--ip6**=*ipv6*
+
+Specify a static IPv6 address for the container, for example **fd46:db93:aa76:ac37::10**.
+This option can only be used if the container is joined to only a single network - i.e., **--network=network-name** is used at most once -
+and if the container is not joining another container's network namespace via **--network=container:_id_**.
+The address must be within the network's IPv6 address pool.
+
+To specify multiple static IPv6 addresses per container, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option.
+
#### **--ipc**=*mode*
Set the IPC namespace mode for a container. The default is to create
@@ -762,6 +772,11 @@ Tune the host's OOM preferences for containers (accepts values from **-1000** to
#### **--os**=*OS*
Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`.
+#### **--passwd**
+
+Allow Podman to add entries to /etc/passwd and /etc/group when used in conjunction with the --user option.
+This is used to override the Podman provided user setup in favor of entrypoint configurations such as libnss-extrausers.
+
#### **--personality**=*persona*
Personality sets the execution domain via Linux personality(2).
@@ -992,8 +1007,8 @@ Note: Labeling can be disabled for all containers by setting label=false in the
- **no-new-privileges**: Disable container processes from gaining additional privileges
-- **seccomp=unconfined**: Turn off seccomp confinement for the container
-- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter
+- **seccomp=unconfined**: Turn off seccomp confinement for the container.
+- **seccomp=profile.json**: JSON file to be used as a seccomp filter. Note that the `io.podman.annotations.seccomp` annotation is set with the specified value as shown in `podman inspect`.
- **proc-opts**=_OPTIONS_ : Comma-separated list of options to use for the /proc mount. More details
for the possible mount options are specified in the **proc(5)** man page.
@@ -1211,6 +1226,17 @@ If for example _amount_ is **5** the second mapping step would look like:
| _from_uid_ + 3 | _container_uid_ + 3 |
| _from_uid_ + 4 | _container_uid_ + 4 |
+When running as rootless, Podman will use all the ranges configured in the _/etc/subuid_ file.
+
+The current user ID is mapped to UID=0 in the rootless user namespace.
+Every additional range is added sequentially afterward:
+
+| host |rootless user namespace | length |
+| - | - | - |
+| $UID | 0 | 1 |
+| 1 | $FIRST_RANGE_ID | $FIRST_RANGE_LENGTH |
+| 1+$FIRST_RANGE_LENGTH | $SECOND_RANGE_ID | $SECOND_RANGE_LENGTH|
+
Even if a user does not have any subordinate UIDs in _/etc/subuid_,
**--uidmap** could still be used to map the normal UID of the user to a
container UID by running `podman run --uidmap $container_uid:0:1 --user $container_uid ...`.
diff --git a/docs/source/markdown/podman-search.1.md b/docs/source/markdown/podman-search.1.md
index 9e166fcc2..9c075a1e0 100644
--- a/docs/source/markdown/podman-search.1.md
+++ b/docs/source/markdown/podman-search.1.md
@@ -62,7 +62,7 @@ Valid placeholders for the Go template are listed below:
| --------------- | ---------------------------- |
| .Index | Registry |
| .Name | Image name |
-| .Descriptions | Image description |
+| .Description | Image description |
| .Stars | Star count of image |
| .Official | "[OK]" if image is official |
| .Automated | "[OK]" if image is automated |
diff --git a/docs/tutorials/mac_experimental.md b/docs/tutorials/mac_experimental.md
index 8df64dc99..b5b815fe5 100644
--- a/docs/tutorials/mac_experimental.md
+++ b/docs/tutorials/mac_experimental.md
@@ -90,7 +90,7 @@ that you were given. It will be used in two of the steps below.
## Test podman
-1. podman machine init --image-path /path/to/image
+1. podman machine init --image-path /path/to/image --cpus 2
2. podman machine start
3. podman images
4. git clone http://github.com/baude/alpine_nginx && cd alpine_nginx
diff --git a/go.mod b/go.mod
index 663bad753..a432c2d5e 100644
--- a/go.mod
+++ b/go.mod
@@ -6,18 +6,18 @@ require (
github.com/BurntSushi/toml v0.4.1
github.com/blang/semver v3.5.1+incompatible
github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37
- github.com/checkpoint-restore/checkpointctl v0.0.0-20210922093614-c31748bec9f2
- github.com/checkpoint-restore/go-criu/v5 v5.2.0
+ github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681
+ github.com/checkpoint-restore/go-criu/v5 v5.3.0
github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.0.1
github.com/containers/buildah v1.23.1
- github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9
+ github.com/containers/common v0.46.1-0.20211209220542-24f363480347
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.17.1-0.20211207161909-6f3c8453e1a7
github.com/containers/ocicrypt v1.1.2
github.com/containers/psgo v1.7.1
- github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518
+ github.com/containers/storage v1.37.1-0.20211213220314-73a749e4fec5
github.com/coreos/go-systemd/v22 v22.3.2
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
github.com/cyphar/filepath-securejoin v0.2.3
@@ -60,12 +60,15 @@ require (
github.com/stretchr/testify v1.7.0
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/uber/jaeger-client-go v2.30.0+incompatible
+ github.com/ulikunitz/xz v0.5.10
github.com/vbauerster/mpb/v6 v6.0.4
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d
+ golang.org/x/text v0.3.7
+ google.golang.org/protobuf v1.27.1
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.22.4
diff --git a/go.sum b/go.sum
index 0f832d974..cc0c9e0e0 100644
--- a/go.sum
+++ b/go.sum
@@ -130,7 +130,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
@@ -152,13 +151,13 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/checkpoint-restore/checkpointctl v0.0.0-20210922093614-c31748bec9f2 h1:z7G4H5f1Z/n3di9qnGtKDm6jmP434HD7dIEh3YyLn9I=
-github.com/checkpoint-restore/checkpointctl v0.0.0-20210922093614-c31748bec9f2/go.mod h1:yvaQuauIKzvfX/PIqINxWxoOYd35Dk/U2MS8onfkRHU=
-github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
+github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681 h1:Jj8mYL2K6peLJdvT10oGTyYyBPqOynmly37D+iL3xNw=
+github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681/go.mod h1:67kWC1PXQLR3lM/mmNnu3Kzn7K4TSWZAGUuQP1JSngk=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
-github.com/checkpoint-restore/go-criu/v5 v5.2.0 h1:QwsRK9EdBr2kQr44DqSdBrP4dULp2+4EkqounYQOnF8=
github.com/checkpoint-restore/go-criu/v5 v5.2.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
+github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8=
+github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
@@ -166,7 +165,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
-github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s=
github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
@@ -205,7 +203,6 @@ github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f2
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
-github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
@@ -286,8 +283,8 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB
github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY=
github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ=
github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo=
-github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9 h1:BoPxjWIPX+cn3CGNxd1FC10jcq9TBMk1uvGQEWTXWto=
-github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9/go.mod h1:cxAKmvKoYBl/iLZ1YD/SKnJF7wPR9H6xM/Hu75ZN/oA=
+github.com/containers/common v0.46.1-0.20211209220542-24f363480347 h1:6CS7RroQLJu/SgUJXGZ3bOs2vnh9rxEnxczDcGjStBw=
+github.com/containers/common v0.46.1-0.20211209220542-24f363480347/go.mod h1:SoHWZESBD7dbqIOkvKrIg5D8EuVIQgL6vkOvv0Yebws=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4=
@@ -303,16 +300,14 @@ github.com/containers/ocicrypt v1.1.2 h1:Ez+GAMP/4GLix5Ywo/fL7O0nY771gsBIigiqUm1
github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
github.com/containers/psgo v1.7.1 h1:2N6KADeFvBm1aI2iXxu6+/Xh7CCkdh8p8F3F/cpIU5I=
github.com/containers/psgo v1.7.1/go.mod h1:mWGpFzW73qWFA+blhF6l7GuKzbrACkYgr/ajiNQR+RM=
-github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
github.com/containers/storage v1.35.0/go.mod h1:qzYhasQP2/V9D9XdO+vRwkHBhsBO0oznMLzzRDQ8s20=
github.com/containers/storage v1.36.0/go.mod h1:vbd3SKVQNHdmU5qQI6hTEcKPxnZkGqydG4f6uwrI5a8=
github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4=
github.com/containers/storage v1.37.1-0.20211119174841-bf170b3ddac0/go.mod h1:XjCNlt5JUUmRuTJXhFxHb9hHGPho7DNg3o4N/14prdQ=
-github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518 h1:p44O35V8XCefRxOxU1aY6eT9XNMxkWA1drtJpsl211c=
-github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518/go.mod h1:T5DX08T/eKKRs0WGDhC/ztngMSth6YuHq15eF8C/Y5A=
+github.com/containers/storage v1.37.1-0.20211213220314-73a749e4fec5 h1:DOpYQGCHIJfrErey3FyondnZGfZrbfGpHAN6nQssE1o=
+github.com/containers/storage v1.37.1-0.20211213220314-73a749e4fec5/go.mod h1:5qRpx96WJRTCQCsArfrWjUh398JSNCaTJG6RbOhMlqY=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
@@ -672,7 +667,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
@@ -698,7 +692,6 @@ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -722,13 +715,12 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -747,7 +739,6 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
-github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -767,7 +758,6 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
-github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI=
@@ -787,7 +777,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mtrmac/gpgme v0.1.2 h1:dNOmvYmsrakgW7LcgiprD0yfRuQQe8/C8F6Z+zogO3s=
@@ -804,7 +793,7 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -835,7 +824,6 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc91/go.mod h1:3Sm6Dt7OT8z88EbdQqqcRN2oCT54jbi72tT/HqgflT8=
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
github.com/opencontainers/runc v1.0.1/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
@@ -845,7 +833,6 @@ github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.m
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
@@ -853,7 +840,6 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7 h1:6JHkPc2wUOsj2XBpYzyvmCL5Y/fA3TFaomYv/Iggt1g=
github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
-github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
@@ -884,7 +870,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -967,7 +952,6 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
@@ -980,7 +964,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I=
@@ -1022,7 +1005,6 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
-github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/vbauerster/mpb/v6 v6.0.4 h1:h6J5zM/2wimP5Hj00unQuV8qbo5EPcj6wbkCqgj7KcY=
@@ -1191,7 +1173,6 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -1293,7 +1274,6 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1624,7 +1604,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
diff --git a/hack/libsubid_tag.sh b/hack/libsubid_tag.sh
index ab6af9e30..31412b3e6 100755
--- a/hack/libsubid_tag.sh
+++ b/hack/libsubid_tag.sh
@@ -7,9 +7,19 @@ mkdir -p "$tmpdir"
trap 'rm -fr "$tmpdir"' EXIT
cc -o "$tmpdir"/libsubid_tag -l subid -x c - > /dev/null 2> /dev/null << EOF
#include <shadow/subid.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const char *Prog = "test";
+FILE *shadow_logfd = NULL;
+
int main() {
struct subid_range *ranges = NULL;
+#if SUBID_ABI_MAJOR >= 4
+ subid_get_uid_ranges("root", &ranges);
+#else
get_subuid_ranges("root", &ranges);
+#endif
free(ranges);
return 0;
}
diff --git a/libpod/container.go b/libpod/container.go
index 1270f2112..c746f97c7 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -213,6 +213,15 @@ type ContainerState struct {
// containerPlatformState holds platform-specific container state.
containerPlatformState
+
+ // Following checkpoint/restore related information is displayed
+ // if the container has been checkpointed or restored.
+ CheckpointedTime time.Time `json:"checkpointedTime,omitempty"`
+ RestoredTime time.Time `json:"restoredTime,omitempty"`
+ CheckpointLog string `json:"checkpointLog,omitempty"`
+ CheckpointPath string `json:"checkpointPath,omitempty"`
+ RestoreLog string `json:"restoreLog,omitempty"`
+ Restored bool `json:"restored,omitempty"`
}
// ContainerNamedVolume is a named volume that will be mounted into the
diff --git a/libpod/container_config.go b/libpod/container_config.go
index adc585fa1..a43fd632b 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -163,6 +163,8 @@ type ContainerRootFSConfig struct {
// Volatile specifies whether the container storage can be optimized
// at the cost of not syncing all the dirty files in memory.
Volatile bool `json:"volatile,omitempty"`
+ // Passwd allows to user to override podman's passwd/group file setup
+ Passwd *bool `json:"passwd,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
@@ -196,6 +198,8 @@ type ContainerSecurityConfig struct {
// Groups are additional groups to add the container's user to. These
// are resolved within the container using the container's /etc/passwd.
Groups []string `json:"groups,omitempty"`
+ // HostUsers are a list of host user accounts to add to /etc/passwd
+ HostUsers []string `json:"HostUsers,omitempty"`
// AddCurrentUserPasswdEntry indicates that Libpod should ensure that
// the container's /etc/passwd contains an entry for the user running
// Libpod - mostly used in rootless containers where the user running
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 83b643266..f72700ab6 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -113,20 +113,26 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
Path: path,
Args: args,
State: &define.InspectContainerState{
- OciVersion: ctrSpec.Version,
- Status: runtimeInfo.State.String(),
- Running: runtimeInfo.State == define.ContainerStateRunning,
- Paused: runtimeInfo.State == define.ContainerStatePaused,
- OOMKilled: runtimeInfo.OOMKilled,
- Dead: runtimeInfo.State.String() == "bad state",
- Pid: runtimeInfo.PID,
- ConmonPid: runtimeInfo.ConmonPID,
- ExitCode: runtimeInfo.ExitCode,
- Error: "", // can't get yet
- StartedAt: runtimeInfo.StartedTime,
- FinishedAt: runtimeInfo.FinishedTime,
- Checkpointed: runtimeInfo.Checkpointed,
- CgroupPath: cgroupPath,
+ OciVersion: ctrSpec.Version,
+ Status: runtimeInfo.State.String(),
+ Running: runtimeInfo.State == define.ContainerStateRunning,
+ Paused: runtimeInfo.State == define.ContainerStatePaused,
+ OOMKilled: runtimeInfo.OOMKilled,
+ Dead: runtimeInfo.State.String() == "bad state",
+ Pid: runtimeInfo.PID,
+ ConmonPid: runtimeInfo.ConmonPID,
+ ExitCode: runtimeInfo.ExitCode,
+ Error: "", // can't get yet
+ StartedAt: runtimeInfo.StartedTime,
+ FinishedAt: runtimeInfo.FinishedTime,
+ Checkpointed: runtimeInfo.Checkpointed,
+ CgroupPath: cgroupPath,
+ RestoredAt: runtimeInfo.RestoredTime,
+ CheckpointedAt: runtimeInfo.CheckpointedTime,
+ Restored: runtimeInfo.Restored,
+ CheckpointPath: runtimeInfo.CheckpointPath,
+ CheckpointLog: runtimeInfo.CheckpointLog,
+ RestoreLog: runtimeInfo.RestoreLog,
},
Image: config.RootfsImageID,
ImageName: config.RootfsImageName,
@@ -371,6 +377,8 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.Umask = c.config.Umask
}
+ ctrConfig.Passwd = c.config.Passwd
+
return ctrConfig
}
@@ -485,9 +493,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
if ctrSpec.Linux.Resources.Memory.Limit != nil {
hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
}
- if ctrSpec.Linux.Resources.Memory.Kernel != nil {
- hostConfig.KernelMemory = *ctrSpec.Linux.Resources.Memory.Kernel
- }
if ctrSpec.Linux.Resources.Memory.Reservation != nil {
hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 7df82eb18..7ae9daefa 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -634,6 +634,12 @@ func resetState(state *ContainerState) {
state.RestartPolicyMatch = false
state.RestartCount = 0
state.Checkpointed = false
+ state.Restored = false
+ state.CheckpointedTime = time.Time{}
+ state.RestoredTime = time.Time{}
+ state.CheckpointPath = ""
+ state.CheckpointLog = ""
+ state.RestoreLog = ""
}
// Refresh refreshes the container's state after a restart.
@@ -1111,6 +1117,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
}
c.state.Checkpointed = false
+ c.state.Restored = false
+ c.state.CheckpointedTime = time.Time{}
+ c.state.RestoredTime = time.Time{}
+ c.state.CheckpointPath = ""
+ c.state.CheckpointLog = ""
+ c.state.RestoreLog = ""
c.state.ExitCode = 0
c.state.Exited = false
c.state.State = define.ContainerStateCreated
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index f4b629a83..7745646b6 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -305,13 +305,40 @@ func (c *Container) getUserOverrides() *lookup.Overrides {
return &overrides
}
+func lookupHostUser(name string) (*runcuser.ExecUser, error) {
+ var execUser runcuser.ExecUser
+ // Lookup User on host
+ u, err := util.LookupUser(name)
+ if err != nil {
+ return &execUser, err
+ }
+ uid, err := strconv.ParseUint(u.Uid, 8, 32)
+ if err != nil {
+ return &execUser, err
+ }
+
+ gid, err := strconv.ParseUint(u.Gid, 8, 32)
+ if err != nil {
+ return &execUser, err
+ }
+ execUser.Uid = int(uid)
+ execUser.Gid = int(gid)
+ execUser.Home = u.HomeDir
+ return &execUser, nil
+}
+
// Generate spec for a container
// Accepts a map of the container's dependencies
func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
overrides := c.getUserOverrides()
execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, overrides)
if err != nil {
- return nil, err
+ if util.StringInSlice(c.config.User, c.config.HostUsers) {
+ execUser, err = lookupHostUser(c.config.User)
+ }
+ if err != nil {
+ return nil, err
+ }
}
g := generate.NewFromSpec(c.config.Spec)
@@ -990,6 +1017,7 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
includeFiles := []string{
"artifacts",
+ metadata.DevShmCheckpointTar,
metadata.ConfigDumpFile,
metadata.SpecDumpFile,
metadata.NetworkStatusFile,
@@ -1134,11 +1162,38 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
return nil, 0, err
}
+ // Setting CheckpointLog early in case there is a failure.
+ c.state.CheckpointLog = path.Join(c.bundlePath(), "dump.log")
+ c.state.CheckpointPath = c.CheckpointPath()
+
runtimeCheckpointDuration, err := c.ociRuntime.CheckpointContainer(c, options)
if err != nil {
return nil, 0, err
}
+ // Keep the content of /dev/shm directory
+ if c.config.ShmDir != "" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
+ shmDirTarFileFullPath := filepath.Join(c.bundlePath(), metadata.DevShmCheckpointTar)
+
+ shmDirTarFile, err := os.Create(shmDirTarFileFullPath)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer shmDirTarFile.Close()
+
+ input, err := archive.TarWithOptions(c.config.ShmDir, &archive.TarOptions{
+ Compression: archive.Uncompressed,
+ IncludeSourceDir: true,
+ })
+ if err != nil {
+ return nil, 0, err
+ }
+
+ if _, err = io.Copy(shmDirTarFile, input); err != nil {
+ return nil, 0, err
+ }
+ }
+
// Save network.status. This is needed to restore the container with
// the same IP. Currently limited to one IP address in a container
// with one interface.
@@ -1169,6 +1224,9 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
if !options.KeepRunning && !options.PreCheckPoint {
c.state.State = define.ContainerStateStopped
c.state.Checkpointed = true
+ c.state.CheckpointedTime = time.Now()
+ c.state.Restored = false
+ c.state.RestoredTime = time.Time{}
// Cleanup Storage and Network
if err := c.cleanup(ctx); err != nil {
@@ -1216,6 +1274,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
logrus.Debugf("Unable to remove file %s", file)
}
}
+ // The file has been deleted. Do not mention it.
+ c.state.CheckpointLog = ""
}
c.state.FinishedTime = time.Now()
@@ -1293,6 +1353,10 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return nil, 0, err
}
+ // Setting RestoreLog early in case there is a failure.
+ c.state.RestoreLog = path.Join(c.bundlePath(), "restore.log")
+ c.state.CheckpointPath = c.CheckpointPath()
+
// Read network configuration from checkpoint
var netStatus map[string]types.StatusBlock
_, err := metadata.ReadJSONFile(&netStatus, c.bundlePath(), metadata.NetworkStatusFile)
@@ -1473,6 +1537,24 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
}
+ // Restore /dev/shm content
+ if c.config.ShmDir != "" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir {
+ shmDirTarFileFullPath := filepath.Join(c.bundlePath(), metadata.DevShmCheckpointTar)
+ if _, err := os.Stat(shmDirTarFileFullPath); err != nil {
+ logrus.Debug("Container checkpoint doesn't contain dev/shm: ", err.Error())
+ } else {
+ shmDirTarFile, err := os.Open(shmDirTarFileFullPath)
+ if err != nil {
+ return nil, 0, err
+ }
+ defer shmDirTarFile.Close()
+
+ if err := archive.UntarUncompressed(shmDirTarFile, c.config.ShmDir, nil); err != nil {
+ return nil, 0, err
+ }
+ }
+ }
+
// Cleanup for a working restore.
if err := c.removeConmonFiles(); err != nil {
return nil, 0, err
@@ -1559,6 +1641,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
c.state.State = define.ContainerStateRunning
c.state.Checkpointed = false
+ c.state.Restored = true
+ c.state.CheckpointedTime = time.Time{}
+ c.state.RestoredTime = time.Now()
if !options.Keep {
// Delete all checkpoint related files. At this point, in theory, all files
@@ -1569,6 +1654,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err != nil {
logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
}
+ c.state.CheckpointPath = ""
err = os.RemoveAll(c.PreCheckPointPath())
if err != nil {
logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err)
@@ -1589,6 +1675,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
logrus.Debugf("Non-fatal: removal of checkpoint file (%s) failed: %v", file, err)
}
}
+ c.state.CheckpointLog = ""
+ c.state.RestoreLog = ""
}
return criuStatistics, runtimeRestoreDuration, c.save()
@@ -1716,11 +1804,9 @@ func (c *Container) makeBindMounts() error {
}
if !c.config.UseImageHosts {
- newHosts, err := c.generateHosts("/etc/hosts")
- if err != nil {
+ if err := c.updateHosts("/etc/hosts"); err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
- c.state.BindMounts["/etc/hosts"] = newHosts
}
}
@@ -1737,32 +1823,32 @@ func (c *Container) makeBindMounts() error {
}
} else {
if !c.config.UseImageHosts && c.state.BindMounts["/etc/hosts"] == "" {
- newHosts, err := c.generateHosts("/etc/hosts")
- if err != nil {
+ if err := c.updateHosts("/etc/hosts"); err != nil {
return errors.Wrapf(err, "error creating hosts file for container %s", c.ID())
}
- c.state.BindMounts["/etc/hosts"] = newHosts
}
}
// SHM is always added when we mount the container
c.state.BindMounts["/dev/shm"] = c.config.ShmDir
- newPasswd, newGroup, err := c.generatePasswdAndGroup()
- if err != nil {
- return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
- }
- if newPasswd != "" {
- // Make /etc/passwd
- // If it already exists, delete so we can recreate
- delete(c.state.BindMounts, "/etc/passwd")
- c.state.BindMounts["/etc/passwd"] = newPasswd
- }
- if newGroup != "" {
- // Make /etc/group
- // If it already exists, delete so we can recreate
- delete(c.state.BindMounts, "/etc/group")
- c.state.BindMounts["/etc/group"] = newGroup
+ if c.config.Passwd == nil || *c.config.Passwd {
+ newPasswd, newGroup, err := c.generatePasswdAndGroup()
+ if err != nil {
+ return errors.Wrapf(err, "error creating temporary passwd file for container %s", c.ID())
+ }
+ if newPasswd != "" {
+ // Make /etc/passwd
+ // If it already exists, delete so we can recreate
+ delete(c.state.BindMounts, "/etc/passwd")
+ c.state.BindMounts["/etc/passwd"] = newPasswd
+ }
+ if newGroup != "" {
+ // Make /etc/group
+ // If it already exists, delete so we can recreate
+ delete(c.state.BindMounts, "/etc/group")
+ c.state.BindMounts["/etc/group"] = newGroup
+ }
}
// Make /etc/hostname
@@ -2029,18 +2115,29 @@ func (c *Container) generateResolvConf() (string, error) {
return destPath, nil
}
-// generateHosts creates a containers hosts file
-func (c *Container) generateHosts(path string) (string, error) {
+// updateHosts updates the container's hosts file
+func (c *Container) updateHosts(path string) error {
+ var hosts string
+
orig, err := ioutil.ReadFile(path)
if err != nil {
- return "", err
+ // Ignore if the path does not exist
+ if !os.IsNotExist(err) {
+ return err
+ }
+ } else {
+ hosts = string(orig)
}
- hosts := string(orig)
- hosts += c.getHosts()
+ hosts += c.getHosts()
hosts = c.appendLocalhost(hosts)
- return c.writeStringToRundir("hosts", hosts)
+ newHosts, err := c.writeStringToRundir("hosts", hosts)
+ if err != nil {
+ return err
+ }
+ c.state.BindMounts["/etc/hosts"] = newHosts
+ return nil
}
// based on networking mode we may want to append the localhost
@@ -2135,11 +2232,24 @@ func (c *Container) getHosts() string {
}
}
} else if c.config.NetMode.IsSlirp4netns() {
- gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet)
- if err != nil {
- logrus.Warn("Failed to determine gatewayIP: ", err.Error())
- } else {
- hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String())
+ // getLocalIP returns the non loopback local IP of the host
+ getLocalIP := func() string {
+ addrs, err := net.InterfaceAddrs()
+ if err != nil {
+ return ""
+ }
+ for _, address := range addrs {
+ // check the address type and if it is not a loopback the display it
+ if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
+ if ipnet.IP.To4() != nil {
+ return ipnet.IP.String()
+ }
+ }
+ }
+ return ""
+ }
+ if ip := getLocalIP(); ip != "" {
+ hosts += fmt.Sprintf("%s\t%s\n", ip, "host.containers.internal")
}
} else {
logrus.Debug("Network configuration does not support host.containers.internal address")
@@ -2265,12 +2375,25 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, int, error) {
// /etc/passwd via AddCurrentUserPasswdEntry (though this does not trigger if
// the user in question already exists in /etc/passwd) or the UID to be added
// is 0).
+// 3. The user specified additional host user accounts to add the the /etc/passwd file
// Returns password entry (as a string that can be appended to /etc/passwd) and
// any error that occurred.
func (c *Container) generatePasswdEntry() (string, error) {
passwdString := ""
addedUID := 0
+ for _, userid := range c.config.HostUsers {
+ // Lookup User on host
+ u, err := util.LookupUser(userid)
+ if err != nil {
+ return "", err
+ }
+ entry, err := c.userPasswdEntry(u)
+ if err != nil {
+ return "", err
+ }
+ passwdString += entry
+ }
if c.config.AddCurrentUserPasswdEntry {
entry, uid, _, err := c.generateCurrentUserPasswdEntry()
if err != nil {
@@ -2303,17 +2426,25 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, int, int, error) {
if err != nil {
return "", 0, 0, errors.Wrapf(err, "failed to get current user")
}
+ pwd, err := c.userPasswdEntry(u)
+ if err != nil {
+ return "", 0, 0, err
+ }
+
+ return pwd, uid, rootless.GetRootlessGID(), nil
+}
+func (c *Container) userPasswdEntry(u *user.User) (string, error) {
// Lookup the user to see if it exists in the container image.
- _, err = lookup.GetUser(c.state.Mountpoint, u.Username)
+ _, err := lookup.GetUser(c.state.Mountpoint, u.Username)
if err != runcuser.ErrNoPasswdEntries {
- return "", 0, 0, err
+ return "", err
}
// Lookup the UID to see if it exists in the container image.
_, err = lookup.GetUser(c.state.Mountpoint, u.Uid)
if err != runcuser.ErrNoPasswdEntries {
- return "", 0, 0, err
+ return "", err
}
// If the user's actual home directory exists, or was mounted in - use
@@ -2347,7 +2478,7 @@ func (c *Container) generateCurrentUserPasswdEntry() (string, int, int, error) {
c.config.Spec.Process.Env = append(c.config.Spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir))
}
- return fmt.Sprintf("%s:*:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Name, homeDir), uid, rootless.GetRootlessGID(), nil
+ return fmt.Sprintf("%s:*:%s:%s:%s:%s:/bin/sh\n", u.Username, u.Uid, u.Gid, u.Name, homeDir), nil
}
// generateUserPasswdEntry generates an /etc/passwd entry for the container user
@@ -2402,7 +2533,7 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// generatePasswdAndGroup generates container-specific passwd and group files
// iff g.config.User is a number or we are configured to make a passwd entry for
-// the current user.
+// the current user or the user specified HostsUsers
// Returns path to file to mount at /etc/passwd, path to file to mount at
// /etc/group, and any error that occurred. If no passwd/group file were
// required, the empty string will be returned for those path (this may occur
@@ -2413,7 +2544,8 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// with a bind mount). This is done in cases where the container is *not*
// read-only. In this case, the function will return nothing ("", "", nil).
func (c *Container) generatePasswdAndGroup() (string, string, error) {
- if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" {
+ if !c.config.AddCurrentUserPasswdEntry && c.config.User == "" &&
+ len(c.config.HostUsers) == 0 {
return "", "", nil
}
diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go
index f6b1c06ea..3964a1237 100644
--- a/libpod/define/annotations.go
+++ b/libpod/define/annotations.go
@@ -66,3 +66,15 @@ const (
// annotation.
InspectResponseFalse = "FALSE"
)
+
+// IsReservedAnnotation returns true if the specified value corresponds to an
+// already reserved annotation that Podman sets during container creation.
+func IsReservedAnnotation(value string) bool {
+ switch value {
+ case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationVolumesFrom, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse:
+ return true
+
+ default:
+ return false
+ }
+}
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 677b39218..ba73e4196 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -68,6 +68,8 @@ type InspectContainerConfig struct {
Timeout uint `json:"Timeout"`
// StopTimeout is time before container is stopped when calling stop
StopTimeout uint `json:"StopTimeout"`
+ // Passwd determines whether or not podman can add entries to /etc/passwd and /etc/group
+ Passwd *bool `json:"Passwd,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
@@ -189,22 +191,28 @@ type InspectMount struct {
// Docker, but here we see more fields that are unused (nonsensical in the
// context of Libpod).
type InspectContainerState struct {
- OciVersion string `json:"OciVersion"`
- Status string `json:"Status"`
- Running bool `json:"Running"`
- Paused bool `json:"Paused"`
- Restarting bool `json:"Restarting"` // TODO
- OOMKilled bool `json:"OOMKilled"`
- Dead bool `json:"Dead"`
- Pid int `json:"Pid"`
- ConmonPid int `json:"ConmonPid,omitempty"`
- ExitCode int32 `json:"ExitCode"`
- Error string `json:"Error"` // TODO
- StartedAt time.Time `json:"StartedAt"`
- FinishedAt time.Time `json:"FinishedAt"`
- Health HealthCheckResults `json:"Health,omitempty"`
- Checkpointed bool `json:"Checkpointed,omitempty"`
- CgroupPath string `json:"CgroupPath,omitempty"`
+ OciVersion string `json:"OciVersion"`
+ Status string `json:"Status"`
+ Running bool `json:"Running"`
+ Paused bool `json:"Paused"`
+ Restarting bool `json:"Restarting"` // TODO
+ OOMKilled bool `json:"OOMKilled"`
+ Dead bool `json:"Dead"`
+ Pid int `json:"Pid"`
+ ConmonPid int `json:"ConmonPid,omitempty"`
+ ExitCode int32 `json:"ExitCode"`
+ Error string `json:"Error"` // TODO
+ StartedAt time.Time `json:"StartedAt"`
+ FinishedAt time.Time `json:"FinishedAt"`
+ Health HealthCheckResults `json:"Health,omitempty"`
+ Checkpointed bool `json:"Checkpointed,omitempty"`
+ CgroupPath string `json:"CgroupPath,omitempty"`
+ CheckpointedAt time.Time `json:"CheckpointedAt,omitempty"`
+ RestoredAt time.Time `json:"RestoredAt,omitempty"`
+ CheckpointLog string `json:"CheckpointLog,omitempty"`
+ CheckpointPath string `json:"CheckpointPath,omitempty"`
+ RestoreLog string `json:"RestoreLog,omitempty"`
+ Restored bool `json:"Restored,omitempty"`
}
// Healthcheck returns the HealthCheckResults. This is used for old podman compat
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 2c19e0a61..a1f3e8491 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -73,6 +73,16 @@ func (c *Container) removeTransientFiles(ctx context.Context) error {
defer conn.Close()
timerFile := fmt.Sprintf("%s.timer", c.ID())
serviceFile := fmt.Sprintf("%s.service", c.ID())
+
+ // If the service has failed (the healthcheck has failed), then
+ // the .service file is not removed on stopping the unit file. If
+ // we check the properties of the service, it will automatically
+ // reset the state. But checking the state takes msecs vs usecs to
+ // blindly call reset.
+ if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil {
+ logrus.Debugf("failed to reset unit file: %q", err)
+ }
+
// We want to ignore errors where the timer unit and/or service unit has already
// been removed. The error return is generic so we have to check against the
// string in the error
diff --git a/libpod/kube.go b/libpod/kube.go
index 4e61b5377..d667616d0 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -747,7 +747,7 @@ func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar,
defaultEnv := env.DefaultEnvVariables()
envVars := make([]v1.EnvVar, 0, len(envs))
imageMap := make(map[string]string, len(imageEnvs))
- for _, ie := range envs {
+ for _, ie := range imageEnvs {
split := strings.SplitN(ie, "=", 2)
imageMap[split[0]] = split[1]
}
diff --git a/libpod/network/internal/util/util.go b/libpod/network/internal/util/util.go
index bf9d70aba..d9b9a8dc0 100644
--- a/libpod/network/internal/util/util.go
+++ b/libpod/network/internal/util/util.go
@@ -78,7 +78,7 @@ func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) {
return append(subnets, liveSubnets...), nil
}
-// GetFreeIPv6NetworkSubnet returns a unused ipv4 subnet
+// GetFreeIPv4NetworkSubnet returns a unused ipv4 subnet
func GetFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
// the default podman network is 10.88.0.0/16
// start locking for free /24 networks
diff --git a/libpod/options.go b/libpod/options.go
index e6fa987a8..204f2a457 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1768,6 +1768,17 @@ func WithPidFile(pidFile string) CtrCreateOption {
}
}
+// WithHostUsers indicates host users to add to /etc/passwd
+func WithHostUsers(hostUsers []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ ctr.config.HostUsers = hostUsers
+ return nil
+ }
+}
+
// WithInitCtrType indicates the container is a initcontainer
func WithInitCtrType(containerType string) CtrCreateOption {
return func(ctr *Container) error {
@@ -1794,6 +1805,17 @@ func WithHostDevice(dev []specs.LinuxDevice) CtrCreateOption {
}
}
+// WithSelectedPasswordManagement makes it so that the container either does or does not setup /etc/passwd or /etc/group
+func WithSelectedPasswordManagement(passwd *bool) CtrCreateOption {
+ return func(c *Container) error {
+ if c.valid {
+ return define.ErrCtrFinalized
+ }
+ c.config.Passwd = passwd
+ return nil
+ }
+}
+
// Pod Creation Options
// WithPodCreateCommand adds the full command plus arguments of the current
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 52ac0d4d7..bf0fc4585 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -36,10 +36,21 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage
return err
}
for _, ctr := range ctrs {
- if ctr.config.RootfsImageID == imageID {
- var timeout *uint
+ if ctr.config.RootfsImageID != imageID {
+ continue
+ }
+ var timeout *uint
+ if ctr.config.IsInfra {
+ pod, err := r.state.Pod(ctr.config.Pod)
+ if err != nil {
+ return errors.Wrapf(err, "container %s is in pod %s, but pod cannot be retrieved", ctr.ID(), pod.ID())
+ }
+ if err := r.removePod(ctx, pod, true, true, timeout); err != nil {
+ return errors.Wrapf(err, "removing image %s: container %s using image could not be removed", imageID, ctr.ID())
+ }
+ } else {
if err := r.removeContainer(ctx, ctr, true, false, false, timeout); err != nil {
- return errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", imageID, ctr.ID())
+ return errors.Wrapf(err, "removing image %s: container %s using image could not be removed", imageID, ctr.ID())
}
}
}
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 4f101ce84..5a06722ec 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -360,7 +360,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error
ID: l.ID(),
Names: []string{fmt.Sprintf("/%s", l.Name())},
Image: imageName,
- ImageID: imageID,
+ ImageID: "sha256:" + imageID,
Command: strings.Join(l.Command(), " "),
Created: l.CreatedTime().Unix(),
Ports: ports,
diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go
index 941718a8b..777009f0a 100644
--- a/pkg/api/handlers/compat/info.go
+++ b/pkg/api/handlers/compat/info.go
@@ -84,7 +84,6 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
InitBinary: "",
InitCommit: docker.Commit{},
Isolation: "",
- KernelMemory: sysInfo.KernelMemory,
KernelMemoryTCP: false,
KernelVersion: infoData.Host.Kernel,
Labels: nil,
diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go
index 77bfe7b50..d1841769a 100644
--- a/pkg/api/handlers/libpod/containers_create.go
+++ b/pkg/api/handlers/libpod/containers_create.go
@@ -19,11 +19,15 @@ import (
func CreateContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
var sg specgen.SpecGenerator
+
if err := json.NewDecoder(r.Body).Decode(&sg); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
-
+ if sg.Passwd == nil {
+ t := true
+ sg.Passwd = &t
+ }
warn, err := generate.CompleteSpec(r.Context(), runtime, &sg)
if err != nil {
utils.InternalServerError(w, err)
diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go
index d874165e3..3f3f48193 100644
--- a/pkg/api/handlers/utils/images.go
+++ b/pkg/api/handlers/utils/images.go
@@ -35,7 +35,7 @@ func NormalizeToDockerHub(r *http.Request, nameOrID string) (string, error) {
if errors.Cause(err) != storage.ErrImageUnknown {
return "", fmt.Errorf("normalizing name for compat API: %v", err)
}
- } else if strings.HasPrefix(img.ID(), nameOrID) {
+ } else if strings.HasPrefix(img.ID(), strings.TrimPrefix(nameOrID, "sha256:")) {
return img.ID(), nil
}
diff --git a/pkg/api/server/docs.go b/pkg/api/server/docs.go
index 83d9ef160..2127e7d82 100644
--- a/pkg/api/server/docs.go
+++ b/pkg/api/server/docs.go
@@ -1,4 +1,4 @@
-// Package api Provides an API for the Libpod library
+// Package api Provides an API for the Libpod library
//
// This documentation describes the Podman v2.0 RESTful API.
// It replaces the Podman v1.0 API and was initially delivered
@@ -45,7 +45,7 @@
// Schemes: http, https
// Host: podman.io
// BasePath: /
-// Version: 3.2.0
+// Version: 4.0.0
// License: Apache-2.0 https://opensource.org/licenses/Apache-2.0
// Contact: Podman <podman@lists.podman.io> https://podman.io/community/
//
diff --git a/pkg/criu/criu.go b/pkg/criu/criu.go
index 2a6805979..967da0dca 100644
--- a/pkg/criu/criu.go
+++ b/pkg/criu/criu.go
@@ -1,7 +1,12 @@
+// +build linux
+
package criu
import (
"github.com/checkpoint-restore/go-criu/v5"
+ "github.com/checkpoint-restore/go-criu/v5/rpc"
+
+ "google.golang.org/protobuf/proto"
)
// MinCriuVersion for Podman at least CRIU 3.11 is required
@@ -21,3 +26,20 @@ func CheckForCriu(version int) bool {
}
return result
}
+
+func MemTrack() bool {
+ features, err := criu.MakeCriu().FeatureCheck(
+ &rpc.CriuFeatures{
+ MemTrack: proto.Bool(true),
+ },
+ )
+ if err != nil {
+ return false
+ }
+
+ if features == nil || features.MemTrack == nil {
+ return false
+ }
+
+ return *features.MemTrack
+}
diff --git a/pkg/criu/criu_unsupported.go b/pkg/criu/criu_unsupported.go
new file mode 100644
index 000000000..51cd0c1fd
--- /dev/null
+++ b/pkg/criu/criu_unsupported.go
@@ -0,0 +1,7 @@
+// +build !linux
+
+package criu
+
+func MemTrack() bool {
+ return false
+}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 1677c067f..ae441b7f3 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -341,6 +341,7 @@ type ContainerRunOptions struct {
Rm bool
SigProxy bool
Spec *specgen.SpecGenerator
+ Passwd bool
}
// ContainerRunReport describes the results of running
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 14127e468..f9850e5a8 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -189,13 +189,13 @@ type ContainerCreateOptions struct {
HealthTimeout string
Hostname string `json:"hostname,omitempty"`
HTTPProxy bool
+ HostUsers []string
ImageVolume string
Init bool
InitContainerType string
InitPath string
Interactive bool
IPC string
- KernelMemory string
Label []string
LabelFile []string
LogDriver string
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index d1af4a479..bf4dcff62 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -927,6 +927,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
for _, w := range warn {
fmt.Fprintf(os.Stderr, "%s\n", w)
}
+
rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec)
if err != nil {
return nil, err
diff --git a/pkg/machine/config.go b/pkg/machine/config.go
index e5e701303..4f2947ac0 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -24,6 +24,15 @@ type InitOptions struct {
TimeZone string
URI url.URL
Username string
+ ReExec bool
+}
+
+type Provider interface {
+ NewMachine(opts InitOptions) (VM, error)
+ LoadVMByName(name string) (VM, error)
+ List(opts ListOptions) ([]*ListResponse, error)
+ IsValidVMName(name string) (bool, error)
+ CheckExclusiveActiveVM() (bool, string, error)
}
type RemoteConnectionType string
@@ -49,6 +58,7 @@ type Download struct {
Sha256sum string
URL *url.URL
VMName string
+ Size int64
}
type ListOptions struct{}
@@ -81,7 +91,7 @@ type RemoveOptions struct {
}
type VM interface {
- Init(opts InitOptions) error
+ Init(opts InitOptions) (bool, error)
Remove(name string, opts RemoveOptions) (string, func() error, error)
SSH(name string, opts SSHOptions) error
Start(name string, opts StartOptions) error
@@ -89,7 +99,7 @@ type VM interface {
}
type DistributionDownload interface {
- DownloadImage() error
+ HasUsableCache() (bool, error)
Get() *Download
}
diff --git a/pkg/machine/connection.go b/pkg/machine/connection.go
index ed1093264..d28ffcef1 100644
--- a/pkg/machine/connection.go
+++ b/pkg/machine/connection.go
@@ -1,4 +1,5 @@
-// +build amd64,!windows arm64,!windows
+//go:build amd64 || arm64
+// +build amd64 arm64
package machine
diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go
index 99197ac0e..60ab471ee 100644
--- a/pkg/machine/fcos.go
+++ b/pkg/machine/fcos.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -65,25 +65,6 @@ func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload
return fcd, nil
}
-func (f FcosDownload) getLocalUncompressedName() string {
- uncompressedFilename := filepath.Join(filepath.Dir(f.LocalPath), f.VMName+"_"+f.ImageName)
- return strings.TrimSuffix(uncompressedFilename, ".xz")
-}
-
-func (f FcosDownload) DownloadImage() error {
- // check if the latest image is already present
- ok, err := UpdateAvailable(&f.Download)
- if err != nil {
- return err
- }
- if !ok {
- if err := DownloadVMImage(f.URL, f.LocalPath); err != nil {
- return err
- }
- }
- return Decompress(f.LocalPath, f.getLocalUncompressedName())
-}
-
func (f FcosDownload) Get() *Download {
return &f.Download
}
@@ -95,14 +76,14 @@ type fcosDownloadInfo struct {
Sha256Sum string
}
-func UpdateAvailable(d *Download) (bool, error) {
+func (f FcosDownload) HasUsableCache() (bool, error) {
// check the sha of the local image if it exists
// get the sha of the remote image
// == dont bother to pull
- if _, err := os.Stat(d.LocalPath); os.IsNotExist(err) {
+ if _, err := os.Stat(f.LocalPath); os.IsNotExist(err) {
return false, nil
}
- fd, err := os.Open(d.LocalPath)
+ fd, err := os.Open(f.LocalPath)
if err != nil {
return false, err
}
@@ -115,7 +96,7 @@ func UpdateAvailable(d *Download) (bool, error) {
if err != nil {
return false, err
}
- return sum.Encoded() == d.Sha256sum, nil
+ return sum.Encoded() == f.Sha256sum, nil
}
func getFcosArch() string {
diff --git a/pkg/machine/fedora.go b/pkg/machine/fedora.go
new file mode 100644
index 000000000..cd713dde7
--- /dev/null
+++ b/pkg/machine/fedora.go
@@ -0,0 +1,122 @@
+// +build amd64 arm64
+
+package machine
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "path/filepath"
+ "regexp"
+
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+const (
+ githubURL = "http://github.com/fedora-cloud/docker-brew-fedora/"
+)
+
+type FedoraDownload struct {
+ Download
+}
+
+func NewFedoraDownloader(vmType, vmName, releaseStream string) (DistributionDownload, error) {
+ imageName, downloadURL, size, err := getFedoraDownload(releaseStream)
+ if err != nil {
+ return nil, err
+ }
+
+ dataDir, err := GetDataDir(vmType)
+ if err != nil {
+ return nil, err
+ }
+
+ f := FedoraDownload{
+ Download: Download{
+ Arch: getFcosArch(),
+ Artifact: artifact,
+ Format: Format,
+ ImageName: imageName,
+ LocalPath: filepath.Join(dataDir, imageName),
+ URL: downloadURL,
+ VMName: vmName,
+ Size: size,
+ },
+ }
+ f.Download.LocalUncompressedFile = f.getLocalUncompressedName()
+ return f, nil
+}
+
+func (f FedoraDownload) Get() *Download {
+ return &f.Download
+}
+
+func (f FedoraDownload) HasUsableCache() (bool, error) {
+ info, err := os.Stat(f.LocalPath)
+ if err != nil {
+ return false, nil
+ }
+ return info.Size() == f.Size, nil
+}
+
+func truncRead(url string) ([]byte, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logrus.Error(err)
+ }
+ }()
+
+ body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 10*1024*1024))
+ if err != nil {
+ return nil, err
+ }
+
+ _, _ = io.Copy(io.Discard, resp.Body)
+
+ return body, nil
+}
+
+func getFedoraDownload(releaseStream string) (string, *url.URL, int64, error) {
+ dirURL := githubURL + "tree/" + releaseStream + "/" + getFcosArch() + "/"
+ body, err := truncRead(dirURL)
+ if err != nil {
+ return "", nil, -1, err
+ }
+
+ rx, err := regexp.Compile(`fedora[^\"]+xz`)
+ if err != nil {
+ return "", nil, -1, err
+ }
+ file := rx.FindString(string(body))
+ if len(file) <= 0 {
+ return "", nil, -1, fmt.Errorf("could not locate Fedora download at %s", dirURL)
+ }
+
+ rawURL := githubURL + "raw/" + releaseStream + "/" + getFcosArch() + "/"
+ newLocation := rawURL + file
+ downloadURL, err := url.Parse(newLocation)
+ if err != nil {
+ return "", nil, -1, errors.Wrapf(err, "invalid URL generated from discovered Fedora file: %s", newLocation)
+ }
+
+ resp, err := http.Head(newLocation)
+ if err != nil {
+ return "", nil, -1, errors.Wrapf(err, "head request failed: %s", newLocation)
+ }
+ _ = resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return "", nil, -1, fmt.Errorf("head request failed [%d] on download: %s", resp.StatusCode, newLocation)
+ }
+
+ return file, downloadURL, resp.ContentLength, nil
+}
diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go
index 9368cc8ed..84d3be296 100644
--- a/pkg/machine/ignition.go
+++ b/pkg/machine/ignition.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
@@ -7,7 +7,10 @@ import (
"fmt"
"io/ioutil"
"net/url"
+ "os"
"path/filepath"
+
+ "github.com/sirupsen/logrus"
)
/*
@@ -355,6 +358,56 @@ machine_enabled=true
},
})
+ // get certs for current user
+ userHome, err := os.UserHomeDir()
+ if err != nil {
+ logrus.Warnf("Unable to copy certs via ignition %s", err.Error())
+ return files
+ }
+
+ certFiles := getCerts(filepath.Join(userHome, ".config/containers/certs.d"))
+ files = append(files, certFiles...)
+
+ certFiles = getCerts(filepath.Join(userHome, ".config/docker/certs.d"))
+ files = append(files, certFiles...)
+
+ return files
+}
+
+func getCerts(certsDir string) []File {
+ var (
+ files []File
+ )
+
+ certs, err := ioutil.ReadDir(certsDir)
+ if err == nil {
+ for _, cert := range certs {
+ b, err := ioutil.ReadFile(filepath.Join(certsDir, cert.Name()))
+ if err != nil {
+ logrus.Warnf("Unable to read cert file %s", err.Error())
+ continue
+ }
+ files = append(files, File{
+ Node: Node{
+ Group: getNodeGrp("root"),
+ Path: filepath.Join("/etc/containers/certs.d/", cert.Name()),
+ User: getNodeUsr("root"),
+ },
+ FileEmbedded1: FileEmbedded1{
+ Append: nil,
+ Contents: Resource{
+ Source: encodeDataURLPtr(string(b)),
+ },
+ Mode: intToPtr(0644),
+ },
+ })
+ }
+ } else {
+ if !os.IsNotExist(err) {
+ logrus.Warnf("Unable to copy certs via ignition, error while reading certs from %s: %s", certsDir, err.Error())
+ }
+ }
+
return files
}
diff --git a/pkg/machine/ignition_schema.go b/pkg/machine/ignition_schema.go
index aa4b8e060..8cfb0d04e 100644
--- a/pkg/machine/ignition_schema.go
+++ b/pkg/machine/ignition_schema.go
@@ -1,4 +1,4 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
diff --git a/pkg/machine/ignition_windows.go b/pkg/machine/ignition_windows.go
new file mode 100644
index 000000000..c0de48bd3
--- /dev/null
+++ b/pkg/machine/ignition_windows.go
@@ -0,0 +1,7 @@
+//+build windows
+
+package machine
+
+func getLocalTimeZone() (string, error) {
+ return "", nil
+}
diff --git a/pkg/machine/keys.go b/pkg/machine/keys.go
index 319fc2b4e..711b091f0 100644
--- a/pkg/machine/keys.go
+++ b/pkg/machine/keys.go
@@ -1,13 +1,21 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
import (
+ "errors"
+ "fmt"
"io/ioutil"
+ "os"
"os/exec"
+ "path/filepath"
"strings"
+
+ "github.com/sirupsen/logrus"
)
+var sshCommand = []string{"ssh-keygen", "-N", "", "-t", "ed25519", "-f"}
+
// CreateSSHKeys makes a priv and pub ssh key for interacting
// the a VM.
func CreateSSHKeys(writeLocation string) (string, error) {
@@ -21,7 +29,42 @@ func CreateSSHKeys(writeLocation string) (string, error) {
return strings.TrimSuffix(string(b), "\n"), nil
}
+func CreateSSHKeysPrefix(dir string, file string, passThru bool, skipExisting bool, prefix ...string) (string, error) {
+ location := filepath.Join(dir, file)
+
+ _, e := os.Stat(location)
+ if !skipExisting || errors.Is(e, os.ErrNotExist) {
+ if err := generatekeysPrefix(dir, file, passThru, prefix...); err != nil {
+ return "", err
+ }
+ } else {
+ fmt.Println("Keys already exist, reusing")
+ }
+ b, err := ioutil.ReadFile(filepath.Join(dir, file) + ".pub")
+ if err != nil {
+ return "", err
+ }
+ return strings.TrimSuffix(string(b), "\n"), nil
+}
+
// generatekeys creates an ed25519 set of keys
func generatekeys(writeLocation string) error {
- return exec.Command("ssh-keygen", "-N", "", "-t", "ed25519", "-f", writeLocation).Run()
+ args := append(append([]string{}, sshCommand[1:]...), writeLocation)
+ return exec.Command(sshCommand[0], args...).Run()
+}
+
+// generatekeys creates an ed25519 set of keys
+func generatekeysPrefix(dir string, file string, passThru bool, prefix ...string) error {
+ args := append([]string{}, prefix[1:]...)
+ args = append(args, sshCommand...)
+ args = append(args, file)
+ cmd := exec.Command(prefix[0], args...)
+ cmd.Dir = dir
+ if passThru {
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ }
+ logrus.Debugf("Running wsl cmd %v in dir: %s", args, dir)
+ return cmd.Run()
}
diff --git a/pkg/machine/machine_unsupported.go b/pkg/machine/machine_unsupported.go
index 9309d16bc..da1437984 100644
--- a/pkg/machine/machine_unsupported.go
+++ b/pkg/machine/machine_unsupported.go
@@ -1,3 +1,3 @@
-// +build !amd64 amd64,windows
+// +build !amd64,!arm64
package machine
diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go
index 3c8422a30..280b47f96 100644
--- a/pkg/machine/pull.go
+++ b/pkg/machine/pull.go
@@ -1,8 +1,9 @@
-// +build amd64,!windows arm64,!windows
+// +build amd64 arm64
package machine
import (
+ "bufio"
"fmt"
"io"
"io/ioutil"
@@ -17,6 +18,7 @@ import (
"github.com/containers/image/v5/pkg/compression"
"github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus"
+ "github.com/ulikunitz/xz"
"github.com/vbauerster/mpb/v6"
"github.com/vbauerster/mpb/v6/decor"
)
@@ -43,7 +45,7 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload
return nil, err
}
if len(getURL.Scheme) > 0 {
- urlSplit := strings.Split(pullPath, "/")
+ urlSplit := strings.Split(getURL.Path, "/")
imageName = urlSplit[len(urlSplit)-1]
dl.LocalUncompressedFile = filepath.Join(dataDir, imageName)
dl.URL = getURL
@@ -63,39 +65,48 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload
return gd, nil
}
-func (g GenericDownload) getLocalUncompressedName() string {
+func (d Download) getLocalUncompressedName() string {
var (
extension string
)
switch {
- case strings.HasSuffix(g.LocalPath, ".bz2"):
+ case strings.HasSuffix(d.LocalPath, ".bz2"):
extension = ".bz2"
- case strings.HasSuffix(g.LocalPath, ".gz"):
+ case strings.HasSuffix(d.LocalPath, ".gz"):
extension = ".gz"
- case strings.HasSuffix(g.LocalPath, ".xz"):
+ case strings.HasSuffix(d.LocalPath, ".xz"):
extension = ".xz"
}
- uncompressedFilename := filepath.Join(filepath.Dir(g.LocalUncompressedFile), g.VMName+"_"+g.ImageName)
+ uncompressedFilename := filepath.Join(filepath.Dir(d.LocalPath), d.VMName+"_"+d.ImageName)
return strings.TrimSuffix(uncompressedFilename, extension)
}
-func (g GenericDownload) DownloadImage() error {
+func (g GenericDownload) Get() *Download {
+ return &g.Download
+}
+
+func (g GenericDownload) HasUsableCache() (bool, error) {
// If we have a URL for this "downloader", we now pull it
- if g.URL != nil {
- if err := DownloadVMImage(g.URL, g.LocalPath); err != nil {
+ return g.URL == nil, nil
+}
+
+func DownloadImage(d DistributionDownload) error {
+ // check if the latest image is already present
+ ok, err := d.HasUsableCache()
+ if err != nil {
+ return err
+ }
+ if !ok {
+ if err := DownloadVMImage(d.Get().URL, d.Get().LocalPath); err != nil {
return err
}
}
- return Decompress(g.LocalPath, g.getLocalUncompressedName())
-}
-
-func (g GenericDownload) Get() *Download {
- return &g.Download
+ return Decompress(d.Get().LocalPath, d.Get().getLocalUncompressedName())
}
// DownloadVMImage downloads a VM image from url to given path
// with download status
-func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error {
+func DownloadVMImage(downloadURL *url2.URL, localImagePath string) error {
out, err := os.Create(localImagePath)
if err != nil {
return err
@@ -120,7 +131,7 @@ func DownloadVMImage(downloadURL fmt.Stringer, localImagePath string) error {
return fmt.Errorf("error downloading VM image %s: %s", downloadURL, resp.Status)
}
size := resp.ContentLength
- urlSplit := strings.Split(downloadURL.String(), "/")
+ urlSplit := strings.Split(downloadURL.Path, "/")
prefix := "Downloading VM image: " + urlSplit[len(urlSplit)-1]
onComplete := prefix + ": done"
@@ -177,24 +188,50 @@ func Decompress(localPath, uncompressedPath string) error {
// Will error out if file without .xz already exists
// Maybe extracting then renameing is a good idea here..
// depends on xz: not pre-installed on mac, so it becomes a brew dependency
-func decompressXZ(src string, output io.Writer) error {
- cmd := exec.Command("xzcat", "-k", src)
- //cmd := exec.Command("xz", "-d", "-k", "-v", src)
- stdOut, err := cmd.StdoutPipe()
- if err != nil {
- return err
+func decompressXZ(src string, output io.WriteCloser) error {
+ var read io.Reader
+ var cmd *exec.Cmd
+ // Prefer xz utils for fastest performance, fallback to go xi2 impl
+ if _, err := exec.LookPath("xzcat"); err == nil {
+ cmd = exec.Command("xzcat", "-k", src)
+ read, err = cmd.StdoutPipe()
+ if err != nil {
+ return err
+ }
+ cmd.Stderr = os.Stderr
+ } else {
+ file, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ // This XZ implementation is reliant on buffering. It is also 3x+ slower than XZ utils.
+ // Consider replacing with a faster implementation (e.g. xi2) if podman machine is
+ // updated with a larger image for the distribution base.
+ buf := bufio.NewReader(file)
+ read, err = xz.NewReader(buf)
+ if err != nil {
+ return err
+ }
}
- //cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
+
+ done := make(chan bool)
go func() {
- if _, err := io.Copy(output, stdOut); err != nil {
+ if _, err := io.Copy(output, read); err != nil {
logrus.Error(err)
}
+ output.Close()
+ done <- true
}()
- return cmd.Run()
+
+ if cmd != nil {
+ return cmd.Run()
+ }
+ <-done
+ return nil
}
-func decompressEverythingElse(src string, output io.Writer) error {
+func decompressEverythingElse(src string, output io.WriteCloser) error {
f, err := os.Open(src)
if err != nil {
return err
@@ -207,6 +244,9 @@ func decompressEverythingElse(src string, output io.Writer) error {
if err := uncompressStream.Close(); err != nil {
logrus.Error(err)
}
+ if err := output.Close(); err != nil {
+ logrus.Error(err)
+ }
}()
_, err = io.Copy(output, uncompressStream)
diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go
index c04773450..8404079a2 100644
--- a/pkg/machine/qemu/config.go
+++ b/pkg/machine/qemu/config.go
@@ -4,6 +4,8 @@ package qemu
import "time"
+type Provider struct{}
+
type MachineVM struct {
// CPUs to be assigned to the VM
CPUs uint64
@@ -44,6 +46,4 @@ var (
// defaultQMPTimeout is the timeout duration for the
// qmp monitor interactions
defaultQMPTimeout time.Duration = 2 * time.Second
- // defaultRemoteUser describes the ssh username default
- defaultRemoteUser = "core"
)
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 19cd131e1..a80a11573 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -21,18 +21,24 @@ import (
"github.com/containers/podman/v3/utils"
"github.com/containers/storage/pkg/homedir"
"github.com/digitalocean/go-qemu/qmp"
+ "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var (
+ qemuProvider = &Provider{}
// vmtype refers to qemu (vs libvirt, krun, etc)
vmtype = "qemu"
)
+func GetQemuProvider() machine.Provider {
+ return qemuProvider
+}
+
// NewMachine initializes an instance of a virtual machine based on the qemu
// virtualization.
-func NewMachine(opts machine.InitOptions) (machine.VM, error) {
+func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
vmConfigDir, err := machine.GetConfDir(vmtype)
if err != nil {
return nil, err
@@ -44,16 +50,8 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) {
ignitionFile := filepath.Join(vmConfigDir, vm.Name+".ign")
vm.IgnitionFilePath = ignitionFile
- // An image was specified
- if len(opts.ImagePath) > 0 {
- vm.ImagePath = opts.ImagePath
- }
-
- // Assign remote user name. if not provided, use default
+ vm.ImagePath = opts.ImagePath
vm.RemoteUsername = opts.Username
- if len(vm.RemoteUsername) < 1 {
- vm.RemoteUsername = defaultRemoteUser
- }
// Add a random port for ssh
port, err := utils.GetRandomPort()
@@ -106,7 +104,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) {
// LoadByName reads a json file that describes a known qemu vm
// and returns a vm instance
-func LoadVMByName(name string) (machine.VM, error) {
+func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
vm := new(MachineVM)
vmConfigDir, err := machine.GetConfDir(vmtype)
if err != nil {
@@ -126,7 +124,7 @@ func LoadVMByName(name string) (machine.VM, error) {
// Init writes the json configuration file to the filesystem for
// other verbs (start, stop)
-func (v *MachineVM) Init(opts machine.InitOptions) error {
+func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
var (
key string
)
@@ -135,7 +133,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
// its existence
vmConfigDir, err := machine.GetConfDir(vmtype)
if err != nil {
- return err
+ return false, err
}
jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json"
v.IdentityPath = filepath.Join(sshDir, v.Name)
@@ -147,11 +145,11 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
dd, err := machine.NewFcosDownloader(vmtype, v.Name, opts.ImagePath)
if err != nil {
- return err
+ return false, err
}
v.ImagePath = dd.Get().LocalUncompressedFile
- if err := dd.DownloadImage(); err != nil {
- return err
+ if err := machine.DownloadImage(dd); err != nil {
+ return false, err
}
default:
// The user has provided an alternate image which can be a file path
@@ -159,11 +157,11 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
v.ImageStream = "custom"
g, err := machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath)
if err != nil {
- return err
+ return false, err
}
v.ImagePath = g.Get().LocalUncompressedFile
- if err := g.DownloadImage(); err != nil {
- return err
+ if err := machine.DownloadImage(g); err != nil {
+ return false, err
}
}
// Add arch specific options including image location
@@ -175,12 +173,12 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
if len(opts.IgnitionPath) < 1 {
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/user/1000/podman/podman.sock", strconv.Itoa(v.Port), v.RemoteUsername)
if err := machine.AddConnection(&uri, v.Name, filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil {
- return err
+ return false, err
}
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root")
if err := machine.AddConnection(&uriRoot, v.Name+"-root", filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil {
- return err
+ return false, err
}
} else {
fmt.Println("An ignition path was provided. No SSH connection was added to Podman")
@@ -188,10 +186,10 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
// Write the JSON file
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
- return err
+ return false, err
}
if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil {
- return err
+ return false, err
}
// User has provided ignition file so keygen
@@ -199,17 +197,17 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
if len(opts.IgnitionPath) < 1 {
key, err = machine.CreateSSHKeys(v.IdentityPath)
if err != nil {
- return err
+ return false, err
}
}
// Run arch specific things that need to be done
if err := v.prepare(); err != nil {
- return err
+ return false, err
}
originalDiskSize, err := getDiskSize(v.ImagePath)
if err != nil {
- return err
+ return false, err
}
// Resize the disk image to input disk size
// only if the virtualdisk size is less than
@@ -219,7 +217,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
resize.Stdout = os.Stdout
resize.Stderr = os.Stderr
if err := resize.Run(); err != nil {
- return errors.Errorf("error resizing image: %q", err)
+ return false, errors.Errorf("error resizing image: %q", err)
}
}
// If the user provides an ignition file, we need to
@@ -227,9 +225,9 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
if len(opts.IgnitionPath) > 0 {
inputIgnition, err := ioutil.ReadFile(opts.IgnitionPath)
if err != nil {
- return err
+ return false, err
}
- return ioutil.WriteFile(v.IgnitionFilePath, inputIgnition, 0644)
+ return false, ioutil.WriteFile(v.IgnitionFilePath, inputIgnition, 0644)
}
// Write the ignition file
ign := machine.DynamicIgnition{
@@ -239,7 +237,8 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
TimeZone: opts.TimeZone,
WritePath: v.IgnitionFilePath,
}
- return machine.NewIgnitionFile(ign)
+ err = machine.NewIgnitionFile(ign)
+ return err == nil, err
}
// Start executes the qemu command line and forks it
@@ -571,7 +570,7 @@ func getDiskSize(path string) (uint64, error) {
}
// List lists all vm's that use qemu virtualization
-func List(_ machine.ListOptions) ([]*machine.ListResponse, error) {
+func (p *Provider) List(_ machine.ListOptions) ([]*machine.ListResponse, error) {
return GetVMInfos()
}
@@ -601,8 +600,8 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
listEntry.Stream = vm.ImageStream
listEntry.VMType = "qemu"
listEntry.CPUs = vm.CPUs
- listEntry.Memory = vm.Memory
- listEntry.DiskSize = vm.DiskSize
+ listEntry.Memory = vm.Memory * units.MiB
+ listEntry.DiskSize = vm.DiskSize * units.GiB
fi, err := os.Stat(fullPath)
if err != nil {
return err
@@ -627,7 +626,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
return listed, err
}
-func IsValidVMName(name string) (bool, error) {
+func (p *Provider) IsValidVMName(name string) (bool, error) {
infos, err := GetVMInfos()
if err != nil {
return false, err
@@ -640,8 +639,9 @@ func IsValidVMName(name string) (bool, error) {
return false, nil
}
-// CheckActiveVM checks if there is a VM already running
-func CheckActiveVM() (bool, string, error) {
+// CheckExclusiveActiveVM checks if there is a VM already running
+// that does not allow other VMs to be running
+func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) {
vms, err := GetVMInfos()
if err != nil {
return false, "", errors.Wrap(err, "error checking VM active")
diff --git a/pkg/machine/qemu/machine_unsupported.go b/pkg/machine/qemu/machine_unsupported.go
index da06ac324..e3ce05e3d 100644
--- a/pkg/machine/qemu/machine_unsupported.go
+++ b/pkg/machine/qemu/machine_unsupported.go
@@ -1,3 +1,3 @@
-// +build !amd64 amd64,windows
+// +build !amd64,!arm64 windows
package qemu
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
new file mode 100644
index 000000000..b4ee79acc
--- /dev/null
+++ b/pkg/machine/wsl/machine.go
@@ -0,0 +1,1119 @@
+//go:build windows
+// +build windows
+
+package wsl
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/containers/podman/v3/pkg/machine"
+ "github.com/containers/podman/v3/utils"
+ "github.com/containers/storage/pkg/homedir"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/text/encoding/unicode"
+ "golang.org/x/text/transform"
+)
+
+var (
+ wslProvider = &Provider{}
+ // vmtype refers to qemu (vs libvirt, krun, etc)
+ vmtype = "wsl"
+)
+
+const (
+ ErrorSuccessRebootInitiated = 1641
+ ErrorSuccessRebootRequired = 3010
+)
+
+// Usermode networking avoids potential nftables compatibility issues between the distro
+// and the WSL Kernel. Additionally it avoids fw rule conflicts between distros, since
+// all instances run under the same Kernel at runtime
+const containersConf = `[containers]
+
+[engine]
+cgroup_manager = "cgroupfs"
+events_logger = "file"
+`
+
+const appendPort = `grep -q Port\ %d /etc/ssh/sshd_config || echo Port %d >> /etc/ssh/sshd_config`
+
+const configServices = `ln -fs /usr/lib/systemd/system/sshd.service /etc/systemd/system/multi-user.target.wants/sshd.service
+ln -fs /usr/lib/systemd/system/podman.socket /etc/systemd/system/sockets.target.wants/podman.socket
+rm -f /etc/systemd/system/getty.target.wants/console-getty.service
+rm -f /etc/systemd/system/getty.target.wants/getty@tty1.service
+rm -f /etc/systemd/system/multi-user.target.wants/systemd-resolved.service
+rm -f /etc/systemd/system/dbus-org.freedesktop.resolve1.service
+ln -fs /dev/null /etc/systemd/system/console-getty.service
+mkdir -p /etc/systemd/system/systemd-sysusers.service.d/
+adduser -m [USER] -G wheel
+mkdir -p /home/[USER]/.config/systemd/[USER]/
+chown [USER]:[USER] /home/[USER]/.config
+`
+
+const sudoers = `%wheel ALL=(ALL) NOPASSWD: ALL
+`
+
+const bootstrap = `#!/bin/bash
+ps -ef | grep -v grep | grep -q systemd && exit 0
+nohup unshare --kill-child --fork --pid --mount --mount-proc --propagation shared /lib/systemd/systemd >/dev/null 2>&1 &
+sleep 0.1
+`
+
+const wslmotd = `
+You will be automatically entered into a nested process namespace where
+systemd is running. If you need to access the parent namespace, hit ctrl-d
+or type exit. This also means to log out you need to exit twice.
+
+`
+
+const sysdpid = "SYSDPID=`ps -eo cmd,pid | grep -m 1 ^/lib/systemd/systemd | awk '{print $2}'`"
+
+const profile = sysdpid + `
+if [ ! -z "$SYSDPID" ] && [ "$SYSDPID" != "1" ]; then
+ cat /etc/wslmotd
+ /usr/local/bin/enterns
+fi
+`
+
+const enterns = "#!/bin/bash\n" + sysdpid + `
+if [ ! -z "$SYSDPID" ] && [ "$SYSDPID" != "1" ]; then
+ nsenter -m -p -t $SYSDPID "$@"
+fi
+`
+
+const waitTerm = sysdpid + `
+if [ ! -z "$SYSDPID" ]; then
+ timeout 60 tail -f /dev/null --pid $SYSDPID
+fi
+`
+
+// WSL kernel does not have sg and crypto_user modules
+const overrideSysusers = `[Service]
+LoadCredential=
+`
+
+const lingerService = `[Unit]
+Description=A systemd user unit demo
+After=network-online.target
+Wants=network-online.target podman.socket
+[Service]
+ExecStart=/usr/bin/sleep infinity
+`
+
+const lingerSetup = `mkdir -p /home/[USER]/.config/systemd/[USER]/default.target.wants
+ln -fs /home/[USER]/.config/systemd/[USER]/linger-example.service \
+ /home/[USER]/.config/systemd/[USER]/default.target.wants/linger-example.service
+`
+
+const wslInstallError = `Could not %s. See previous output for any potential failure details.
+If you can not resolve the issue, and rerunning fails, try the "wsl --install" process
+outlined in the following article:
+
+http://docs.microsoft.com/en-us/windows/wsl/install
+
+`
+
+const wslKernelError = `Could not %s. See previous output for any potential failure details.
+If you can not resolve the issue, try rerunning the "podman machine init command". If that fails
+try the "wsl --update" command and then rerun "podman machine init". Finally, if all else fails,
+try following the steps outlined in the following article:
+
+http://docs.microsoft.com/en-us/windows/wsl/install
+
+`
+
+const wslInstallKernel = "install the WSL Kernel"
+
+const wslOldVersion = `Automatic installation of WSL can not be performed on this version of Windows
+Either update to Build 19041 (or later), or perform the manual installation steps
+outlined in the following article:
+
+http://docs.microsoft.com/en-us/windows/wsl/install\
+
+`
+
+type Provider struct{}
+
+type MachineVM struct {
+ // IdentityPath is the fq path to the ssh priv key
+ IdentityPath string
+ // IgnitionFilePath is the fq path to the .ign file
+ ImageStream string
+ // ImagePath is the fq path to
+ ImagePath string
+ // Name of the vm
+ Name string
+ // SSH port for user networking
+ Port int
+ // RemoteUsername of the vm user
+ RemoteUsername string
+}
+
+type ExitCodeError struct {
+ code uint
+}
+
+func (e *ExitCodeError) Error() string {
+ return fmt.Sprintf("Process failed with exit code: %d", e.code)
+}
+
+func GetWSLProvider() machine.Provider {
+ return wslProvider
+}
+
+// NewMachine initializes an instance of a virtual machine based on the qemu
+// virtualization.
+func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
+ vm := new(MachineVM)
+ if len(opts.Name) > 0 {
+ vm.Name = opts.Name
+ }
+
+ vm.ImagePath = opts.ImagePath
+ vm.RemoteUsername = opts.Username
+
+ // Add a random port for ssh
+ port, err := utils.GetRandomPort()
+ if err != nil {
+ return nil, err
+ }
+ vm.Port = port
+
+ return vm, nil
+}
+
+// LoadByName reads a json file that describes a known qemu vm
+// and returns a vm instance
+func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
+ vm := new(MachineVM)
+ vmConfigDir, err := machine.GetConfDir(vmtype)
+ if err != nil {
+ return nil, err
+ }
+ b, err := ioutil.ReadFile(filepath.Join(vmConfigDir, name+".json"))
+ if os.IsNotExist(err) {
+ return nil, errors.Wrap(machine.ErrNoSuchVM, name)
+ }
+ if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(b, vm)
+ return vm, err
+}
+
+// Init writes the json configuration file to the filesystem for
+// other verbs (start, stop)
+func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
+ if cont, err := checkAndInstallWSL(opts); !cont {
+ appendOutputIfError(opts.ReExec, err)
+ return cont, err
+ }
+
+ homeDir := homedir.Get()
+ sshDir := filepath.Join(homeDir, ".ssh")
+ v.IdentityPath = filepath.Join(sshDir, v.Name)
+
+ if err := downloadDistro(v, opts); err != nil {
+ return false, err
+ }
+
+ if err := writeJSON(v); err != nil {
+ return false, err
+ }
+
+ if err := setupConnections(v, opts, sshDir); err != nil {
+ return false, err
+ }
+
+ dist, err := provisionWSLDist(v)
+ if err != nil {
+ return false, err
+ }
+
+ fmt.Println("Configuring system...")
+ if err = configureSystem(v, dist); err != nil {
+ return false, err
+ }
+
+ if err = installScripts(dist); err != nil {
+ return false, err
+ }
+
+ if err = createKeys(v, dist, sshDir); err != nil {
+ return false, err
+ }
+
+ return true, nil
+}
+
+func downloadDistro(v *MachineVM, opts machine.InitOptions) error {
+ var (
+ dd machine.DistributionDownload
+ err error
+ )
+
+ if _, e := strconv.Atoi(opts.ImagePath); e == nil {
+ v.ImageStream = opts.ImagePath
+ dd, err = machine.NewFedoraDownloader(vmtype, v.Name, v.ImageStream)
+ } else {
+ v.ImageStream = "custom"
+ dd, err = machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath)
+ }
+ if err != nil {
+ return err
+ }
+
+ v.ImagePath = dd.Get().LocalUncompressedFile
+ return machine.DownloadImage(dd)
+}
+
+func writeJSON(v *MachineVM) error {
+ vmConfigDir, err := machine.GetConfDir(vmtype)
+ if err != nil {
+ return err
+ }
+
+ jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json"
+
+ b, err := json.MarshalIndent(v, "", " ")
+ if err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil {
+ return errors.Wrap(err, "could not write machine json config")
+ }
+
+ return nil
+}
+
+func setupConnections(v *MachineVM, opts machine.InitOptions, sshDir string) error {
+ uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(v.Port), "root")
+ if err := machine.AddConnection(&uriRoot, v.Name+"-root", filepath.Join(sshDir, v.Name), opts.IsDefault); err != nil {
+ return err
+ }
+
+ user := opts.Username
+ uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", withUser("/run/[USER]/1000/podman/podman.sock", user), strconv.Itoa(v.Port), v.RemoteUsername)
+ return machine.AddConnection(&uri, v.Name, filepath.Join(sshDir, v.Name), opts.IsDefault)
+}
+
+func provisionWSLDist(v *MachineVM) (string, error) {
+ vmDataDir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return "", err
+ }
+
+ distDir := filepath.Join(vmDataDir, "wsldist")
+ distTarget := filepath.Join(distDir, v.Name)
+ if err := os.MkdirAll(distDir, 0755); err != nil {
+ return "", errors.Wrap(err, "could not create wsldist directory")
+ }
+
+ dist := toDist(v.Name)
+ fmt.Println("Importing operating system into WSL (this may take 5+ minutes on a new WSL install)...")
+ if err = runCmdPassThrough("wsl", "--import", dist, distTarget, v.ImagePath); err != nil {
+ return "", errors.Wrap(err, "WSL import of guest OS failed")
+ }
+
+ fmt.Println("Installing packages (this will take awhile)...")
+ if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "upgrade", "-y"); err != nil {
+ return "", errors.Wrap(err, "package upgrade on guest OS failed")
+ }
+
+ if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "install",
+ "podman", "podman-docker", "openssh-server", "procps-ng", "-y"); err != nil {
+ return "", errors.Wrap(err, "package installation on guest OS failed")
+ }
+
+ // Fixes newuidmap
+ if err = runCmdPassThrough("wsl", "-d", dist, "dnf", "reinstall", "shadow-utils", "-y"); err != nil {
+ return "", errors.Wrap(err, "package reinstallation of shadow-utils on guest OS failed")
+ }
+
+ // Windows 11 (NT Version = 10, Build 22000) generates harmless but scary messages on every
+ // operation when mount was not present on the initial start. Force a cycle so that it won't
+ // repeatedly complain.
+ if winVersionAtLeast(10, 0, 22000) {
+ if err := runCmdPassThrough("wsl", "--terminate", dist); err != nil {
+ logrus.Warnf("could not cycle WSL dist: %s", err.Error())
+ }
+ }
+
+ return dist, nil
+}
+
+func createKeys(v *MachineVM, dist string, sshDir string) error {
+ user := v.RemoteUsername
+
+ if err := os.MkdirAll(sshDir, 0700); err != nil {
+ return errors.Wrap(err, "could not create ssh directory")
+ }
+
+ if err := runCmdPassThrough("wsl", "--terminate", dist); err != nil {
+ return errors.Wrap(err, "could not cycle WSL dist")
+ }
+
+ key, err := machine.CreateSSHKeysPrefix(sshDir, v.Name, true, true, "wsl", "-d", dist)
+ if err != nil {
+ return errors.Wrap(err, "could not create ssh keys")
+ }
+
+ if err := pipeCmdPassThrough("wsl", key+"\n", "-d", dist, "sh", "-c", "mkdir -p /root/.ssh;"+
+ "cat >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys"); err != nil {
+ return errors.Wrap(err, "could not create root authorized keys on guest OS")
+ }
+
+ userAuthCmd := withUser("mkdir -p /home/[USER]/.ssh;"+
+ "cat >> /home/[USER]/.ssh/authorized_keys; chown -R [USER]:[USER] /home/[USER]/.ssh;"+
+ "chmod 600 /home/[USER]/.ssh/authorized_keys", user)
+ if err := pipeCmdPassThrough("wsl", key+"\n", "-d", dist, "sh", "-c", userAuthCmd); err != nil {
+ return errors.Wrapf(err, "could not create '%s' authorized keys on guest OS", v.RemoteUsername)
+ }
+
+ return nil
+}
+
+func configureSystem(v *MachineVM, dist string) error {
+ user := v.RemoteUsername
+ if err := runCmdPassThrough("wsl", "-d", dist, "sh", "-c", fmt.Sprintf(appendPort, v.Port, v.Port)); err != nil {
+ return errors.Wrap(err, "could not configure SSH port for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", withUser(configServices, user), "-d", dist, "sh"); err != nil {
+ return errors.Wrap(err, "could not configure systemd settings for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", sudoers, "-d", dist, "sh", "-c", "cat >> /etc/sudoers"); err != nil {
+ return errors.Wrap(err, "could not add wheel to sudoers")
+ }
+
+ if err := pipeCmdPassThrough("wsl", overrideSysusers, "-d", dist, "sh", "-c",
+ "cat > /etc/systemd/system/systemd-sysusers.service.d/override.conf"); err != nil {
+ return errors.Wrap(err, "could not generate systemd-sysusers override for guest OS")
+ }
+
+ lingerCmd := withUser("cat > /home/[USER]/.config/systemd/[USER]/linger-example.service", user)
+ if err := pipeCmdPassThrough("wsl", lingerService, "-d", dist, "sh", "-c", lingerCmd); err != nil {
+ return errors.Wrap(err, "could not generate linger service for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", withUser(lingerSetup, user), "-d", dist, "sh"); err != nil {
+ return errors.Wrap(err, "could not configure systemd settomgs for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", containersConf, "-d", dist, "sh", "-c", "cat > /etc/containers/containers.conf"); err != nil {
+ return errors.Wrap(err, "could not create containers.conf for guest OS")
+ }
+
+ return nil
+}
+
+func installScripts(dist string) error {
+ if err := pipeCmdPassThrough("wsl", enterns, "-d", dist, "sh", "-c",
+ "cat > /usr/local/bin/enterns; chmod 755 /usr/local/bin/enterns"); err != nil {
+ return errors.Wrap(err, "could not create enterns script for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", profile, "-d", dist, "sh", "-c",
+ "cat > /etc/profile.d/enterns.sh"); err != nil {
+ return errors.Wrap(err, "could not create motd profile script for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", wslmotd, "-d", dist, "sh", "-c", "cat > /etc/wslmotd"); err != nil {
+ return errors.Wrap(err, "could not create a WSL MOTD for guest OS")
+ }
+
+ if err := pipeCmdPassThrough("wsl", bootstrap, "-d", dist, "sh", "-c",
+ "cat > /root/bootstrap; chmod 755 /root/bootstrap"); err != nil {
+ return errors.Wrap(err, "could not create bootstrap script for guest OS")
+ }
+
+ return nil
+}
+
+func checkAndInstallWSL(opts machine.InitOptions) (bool, error) {
+ if isWSLInstalled() {
+ return true, nil
+ }
+
+ admin := hasAdminRights()
+
+ if !isWSLFeatureEnabled() {
+ return false, attemptFeatureInstall(opts, admin)
+ }
+
+ skip := false
+ if !opts.ReExec && !admin {
+ fmt.Println("Launching WSL Kernel Install...")
+ if err := launchElevate(wslInstallKernel); err != nil {
+ return false, err
+ }
+
+ skip = true
+ }
+
+ if !skip {
+ if err := installWslKernel(); err != nil {
+ fmt.Fprintf(os.Stderr, wslKernelError, wslInstallKernel)
+ return false, err
+ }
+
+ if opts.ReExec {
+ return false, nil
+ }
+ }
+
+ return true, nil
+}
+
+func attemptFeatureInstall(opts machine.InitOptions, admin bool) error {
+ if !winVersionAtLeast(10, 0, 18362) {
+ return errors.Errorf("Your version of Windows does not support WSL. Update to Windows 10 Build 19041 or later")
+ } else if !winVersionAtLeast(10, 0, 19041) {
+ fmt.Fprint(os.Stderr, wslOldVersion)
+ return errors.Errorf("WSL can not be automatically installed")
+ }
+
+ message := "WSL is not installed on this system, installing it.\n\n"
+
+ if !admin {
+ message += "Since you are not running as admin, a new window will open and " +
+ "require you to approve administrator privileges.\n\n"
+ }
+
+ message += "NOTE: A system reboot will be required as part of this process. " +
+ "If you prefer, you may abort now, and perform a manual installation using the \"wsl --install\" command."
+
+ if !opts.ReExec && MessageBox(message, "Podman Machine", false) != 1 {
+ return errors.Errorf("WSL installation aborted")
+ }
+
+ if !opts.ReExec && !admin {
+ return launchElevate("install the Windows WSL Features")
+ }
+
+ return installWsl()
+}
+
+func launchElevate(operation string) error {
+ truncateElevatedOutputFile()
+ err := relaunchElevatedWait()
+ if err != nil {
+ if eerr, ok := err.(*ExitCodeError); ok {
+ if eerr.code == ErrorSuccessRebootRequired {
+ fmt.Println("Reboot is required to continue installation, please reboot at your convenience")
+ return nil
+ }
+ }
+
+ fmt.Fprintf(os.Stderr, "Elevated process failed with error: %v\n\n", err)
+ dumpOutputFile()
+ fmt.Fprintf(os.Stderr, wslInstallError, operation)
+ }
+ return err
+}
+
+func installWsl() error {
+ log, err := getElevatedOutputFileWrite()
+ if err != nil {
+ return err
+ }
+ defer log.Close()
+ if err := runCmdPassThroughTee(log, "dism", "/online", "/enable-feature",
+ "/featurename:Microsoft-Windows-Subsystem-Linux", "/all", "/norestart"); isMsiError(err) {
+ return errors.Wrap(err, "could not enable WSL Feature")
+ }
+
+ if err = runCmdPassThroughTee(log, "dism", "/online", "/enable-feature",
+ "/featurename:VirtualMachinePlatform", "/all", "/norestart"); isMsiError(err) {
+ return errors.Wrap(err, "could not enable Virtual Machine Feature")
+ }
+ log.Close()
+
+ return reboot()
+}
+
+func installWslKernel() error {
+ log, err := getElevatedOutputFileWrite()
+ if err != nil {
+ return err
+ }
+ defer log.Close()
+
+ message := "Installing WSL Kernel Update"
+ fmt.Println(message)
+ fmt.Fprintln(log, message)
+
+ backoff := 500 * time.Millisecond
+ for i := 0; i < 5; i++ {
+ err = runCmdPassThroughTee(log, "wsl", "--update")
+ if err == nil {
+ break
+ }
+ // In case of unusual circumstances (e.g. race with installer actions)
+ // retry a few times
+ message = "An error occured attempting the WSL Kernel update, retrying..."
+ fmt.Println(message)
+ fmt.Fprintln(log, message)
+ time.Sleep(backoff)
+ backoff *= 2
+ }
+
+ if err != nil {
+ return errors.Wrap(err, "could not install WSL Kernel")
+ }
+
+ return nil
+}
+
+func getElevatedOutputFileName() (string, error) {
+ dir, err := homedir.GetDataHome()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(dir, "podman-elevated-output.log"), nil
+}
+
+func dumpOutputFile() {
+ file, err := getElevatedOutputFileRead()
+ if err != nil {
+ logrus.Debug("could not find elevated child output file")
+ return
+ }
+ defer file.Close()
+ _, _ = io.Copy(os.Stdout, file)
+}
+
+func getElevatedOutputFileRead() (*os.File, error) {
+ return getElevatedOutputFile(os.O_RDONLY)
+}
+
+func getElevatedOutputFileWrite() (*os.File, error) {
+ return getElevatedOutputFile(os.O_WRONLY | os.O_CREATE | os.O_APPEND)
+}
+
+func appendOutputIfError(write bool, err error) {
+ if write && err == nil {
+ return
+ }
+
+ if file, check := getElevatedOutputFileWrite(); check == nil {
+ defer file.Close()
+ fmt.Fprintf(file, "Error: %v\n", err)
+ }
+}
+
+func truncateElevatedOutputFile() error {
+ name, err := getElevatedOutputFileName()
+ if err != nil {
+ return err
+ }
+
+ return os.Truncate(name, 0)
+}
+
+func getElevatedOutputFile(mode int) (*os.File, error) {
+ name, err := getElevatedOutputFileName()
+ if err != nil {
+ return nil, err
+ }
+
+ dir, err := homedir.GetDataHome()
+ if err != nil {
+ return nil, err
+ }
+
+ if err = os.MkdirAll(dir, 0755); err != nil {
+ return nil, err
+ }
+
+ return os.OpenFile(name, mode, 0644)
+}
+
+func isMsiError(err error) bool {
+ if err == nil {
+ return false
+ }
+
+ if eerr, ok := err.(*exec.ExitError); ok {
+ switch eerr.ExitCode() {
+ case 0:
+ fallthrough
+ case ErrorSuccessRebootInitiated:
+ fallthrough
+ case ErrorSuccessRebootRequired:
+ return false
+ }
+ }
+
+ return true
+}
+func toDist(name string) string {
+ if !strings.HasPrefix(name, "podman") {
+ name = "podman-" + name
+ }
+ return name
+}
+
+func withUser(s string, user string) string {
+ return strings.ReplaceAll(s, "[USER]", user)
+}
+
+func runCmdPassThrough(name string, arg ...string) error {
+ logrus.Debugf("Running command: %s %v", name, arg)
+ cmd := exec.Command(name, arg...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+}
+
+func runCmdPassThroughTee(out io.Writer, name string, arg ...string) error {
+ logrus.Debugf("Running command: %s %v", name, arg)
+
+ // TODO - Perhaps improve this with a conpty pseudo console so that
+ // dism installer text bars mirror console behavior (redraw)
+ cmd := exec.Command(name, arg...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = io.MultiWriter(os.Stdout, out)
+ cmd.Stderr = io.MultiWriter(os.Stderr, out)
+ return cmd.Run()
+}
+
+func pipeCmdPassThrough(name string, input string, arg ...string) error {
+ logrus.Debugf("Running command: %s %v", name, arg)
+ cmd := exec.Command(name, arg...)
+ cmd.Stdin = strings.NewReader(input)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd.Run()
+}
+
+func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
+ if v.isRunning() {
+ return errors.Errorf("%q is already running", name)
+ }
+
+ fmt.Println("Starting machine...")
+
+ dist := toDist(name)
+
+ err := runCmdPassThrough("wsl", "-d", dist, "/root/bootstrap")
+ if err != nil {
+ return errors.Wrap(err, "WSL bootstrap script failed")
+ }
+
+ return markStart(name)
+}
+
+func isWSLInstalled() bool {
+ cmd := exec.Command("wsl", "--status")
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return false
+ }
+ if err = cmd.Start(); err != nil {
+ return false
+ }
+ scanner := bufio.NewScanner(transform.NewReader(out, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
+ result := true
+ for scanner.Scan() {
+ line := scanner.Text()
+ // Windows 11 does not set an error exit code when a kernel is not avail
+ if strings.Contains(line, "kernel file is not found") {
+ result = false
+ break
+ }
+ }
+ if err := cmd.Wait(); !result || err != nil {
+ return false
+ }
+
+ return true
+}
+
+func isWSLFeatureEnabled() bool {
+ cmd := exec.Command("wsl", "--set-default-version", "2")
+ return cmd.Run() == nil
+}
+
+func isWSLRunning(dist string) (bool, error) {
+ cmd := exec.Command("wsl", "-l", "--running")
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return false, err
+ }
+ if err = cmd.Start(); err != nil {
+ return false, err
+ }
+ scanner := bufio.NewScanner(transform.NewReader(out, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
+ result := false
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ if len(fields) > 0 && dist == fields[0] {
+ result = true
+ break
+ }
+ }
+
+ _ = cmd.Wait()
+
+ return result, nil
+}
+
+func isSystemdRunning(dist string) (bool, error) {
+ cmd := exec.Command("wsl", "-d", dist, "sh")
+ cmd.Stdin = strings.NewReader(sysdpid + "\necho $SYSDPID\n")
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return false, err
+ }
+ if err = cmd.Start(); err != nil {
+ return false, err
+ }
+ scanner := bufio.NewScanner(out)
+ result := false
+ if scanner.Scan() {
+ text := scanner.Text()
+ i, err := strconv.Atoi(text)
+ if err == nil && i > 0 {
+ result = true
+ }
+ }
+
+ _ = cmd.Wait()
+
+ return result, nil
+}
+
+func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
+ dist := toDist(v.Name)
+
+ wsl, err := isWSLRunning(dist)
+ if err != nil {
+ return err
+ }
+
+ sysd := false
+ if wsl {
+ sysd, err = isSystemdRunning(dist)
+ if err != nil {
+ return err
+ }
+ }
+
+ if !wsl || !sysd {
+ return errors.Errorf("%q is not running", v.Name)
+ }
+
+ cmd := exec.Command("wsl", "-d", dist, "sh")
+ cmd.Stdin = strings.NewReader(waitTerm)
+ if err = cmd.Start(); err != nil {
+ return errors.Wrap(err, "Error executing wait command")
+ }
+
+ exitCmd := exec.Command("wsl", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
+ if err = exitCmd.Run(); err != nil {
+ return errors.Wrap(err, "Error stopping sysd")
+ }
+
+ if err = cmd.Wait(); err != nil {
+ return err
+ }
+
+ cmd = exec.Command("wsl", "--terminate", dist)
+ if err = cmd.Run(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+//nolint:cyclop
+func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, func() error, error) {
+ var files []string
+
+ if v.isRunning() {
+ return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name)
+ }
+
+ // Collect all the files that need to be destroyed
+ if !opts.SaveKeys {
+ files = append(files, v.IdentityPath, v.IdentityPath+".pub")
+ }
+ if !opts.SaveImage {
+ files = append(files, v.ImagePath)
+ }
+
+ vmConfigDir, err := machine.GetConfDir(vmtype)
+ if err != nil {
+ return "", nil, err
+ }
+ files = append(files, filepath.Join(vmConfigDir, v.Name+".json"))
+
+ vmDataDir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return "", nil, err
+ }
+ files = append(files, filepath.Join(vmDataDir, "wsldist", v.Name))
+
+ confirmationMessage := "\nThe following files will be deleted:\n\n"
+ for _, msg := range files {
+ confirmationMessage += msg + "\n"
+ }
+
+ confirmationMessage += "\n"
+ return confirmationMessage, func() error {
+ if err := machine.RemoveConnection(v.Name); err != nil {
+ logrus.Error(err)
+ }
+ if err := machine.RemoveConnection(v.Name + "-root"); err != nil {
+ logrus.Error(err)
+ }
+ if err := runCmdPassThrough("wsl", "--unregister", toDist(v.Name)); err != nil {
+ logrus.Error(err)
+ }
+ for _, f := range files {
+ if err := os.RemoveAll(f); err != nil {
+ logrus.Error(err)
+ }
+ }
+ return nil
+ }, nil
+}
+
+func (v *MachineVM) isRunning() bool {
+ dist := toDist(v.Name)
+
+ wsl, err := isWSLRunning(dist)
+ if err != nil {
+ return false
+ }
+
+ sysd := false
+ if wsl {
+ sysd, err = isSystemdRunning(dist)
+
+ if err != nil {
+ return false
+ }
+ }
+
+ return sysd
+}
+
+// SSH opens an interactive SSH session to the vm specified.
+// Added ssh function to VM interface: pkg/machine/config/go : line 58
+func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error {
+ if !v.isRunning() {
+ return errors.Errorf("vm %q is not running.", v.Name)
+ }
+
+ username := opts.Username
+ if username == "" {
+ username = v.RemoteUsername
+ }
+
+ sshDestination := username + "@localhost"
+ port := strconv.Itoa(v.Port)
+
+ args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile /dev/null", "-o", "StrictHostKeyChecking no"}
+ if len(opts.Args) > 0 {
+ args = append(args, opts.Args...)
+ } else {
+ fmt.Printf("Connecting to vm %s. To close connection, use `~.` or `exit`\n", v.Name)
+ }
+
+ cmd := exec.Command("ssh", args...)
+ logrus.Debugf("Executing: ssh %v\n", args)
+
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Stdin = os.Stdin
+
+ return cmd.Run()
+}
+
+// List lists all vm's that use qemu virtualization
+func (p *Provider) List(_ machine.ListOptions) ([]*machine.ListResponse, error) {
+ return GetVMInfos()
+}
+
+func GetVMInfos() ([]*machine.ListResponse, error) {
+ vmConfigDir, err := machine.GetConfDir(vmtype)
+ if err != nil {
+ return nil, err
+ }
+
+ var listed []*machine.ListResponse
+
+ if err = filepath.Walk(vmConfigDir, func(path string, info os.FileInfo, err error) error {
+ vm := new(MachineVM)
+ if strings.HasSuffix(info.Name(), ".json") {
+ fullPath := filepath.Join(vmConfigDir, info.Name())
+ b, err := ioutil.ReadFile(fullPath)
+ if err != nil {
+ return err
+ }
+ err = json.Unmarshal(b, vm)
+ if err != nil {
+ return err
+ }
+ listEntry := new(machine.ListResponse)
+
+ listEntry.Name = vm.Name
+ listEntry.Stream = vm.ImageStream
+ listEntry.VMType = "wsl"
+ listEntry.CPUs, _ = getCPUs(vm)
+ listEntry.Memory, _ = getMem(vm)
+ listEntry.DiskSize = getDiskSize(vm)
+ fi, err := os.Stat(fullPath)
+ if err != nil {
+ return err
+ }
+ listEntry.CreatedAt = fi.ModTime()
+ listEntry.LastUp = getLastStart(vm, fi.ModTime())
+ if vm.isRunning() {
+ listEntry.Running = true
+ }
+
+ listed = append(listed, listEntry)
+ }
+ return nil
+ }); err != nil {
+ return nil, err
+ }
+ return listed, err
+}
+
+func getDiskSize(vm *MachineVM) uint64 {
+ vmDataDir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return 0
+ }
+ distDir := filepath.Join(vmDataDir, "wsldist")
+ disk := filepath.Join(distDir, vm.Name, "ext4.vhdx")
+ info, err := os.Stat(disk)
+ if err != nil {
+ return 0
+ }
+ return uint64(info.Size())
+}
+
+func markStart(name string) error {
+ vmDataDir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return err
+ }
+ distDir := filepath.Join(vmDataDir, "wsldist")
+ start := filepath.Join(distDir, name, "laststart")
+ file, err := os.Create(start)
+ if err != nil {
+ return err
+ }
+ file.Close()
+
+ return nil
+}
+
+func getLastStart(vm *MachineVM, created time.Time) time.Time {
+ vmDataDir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return created
+ }
+ distDir := filepath.Join(vmDataDir, "wsldist")
+ start := filepath.Join(distDir, vm.Name, "laststart")
+ info, err := os.Stat(start)
+ if err != nil {
+ return created
+ }
+ return info.ModTime()
+}
+
+func getCPUs(vm *MachineVM) (uint64, error) {
+ dist := toDist(vm.Name)
+ if run, _ := isWSLRunning(dist); !run {
+ return 0, nil
+ }
+ cmd := exec.Command("wsl", "-d", dist, "nproc")
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return 0, err
+ }
+ if err = cmd.Start(); err != nil {
+ return 0, err
+ }
+ scanner := bufio.NewScanner(out)
+ var result string
+ for scanner.Scan() {
+ result = scanner.Text()
+ }
+ _ = cmd.Wait()
+
+ ret, err := strconv.Atoi(result)
+ return uint64(ret), err
+}
+
+func getMem(vm *MachineVM) (uint64, error) {
+ dist := toDist(vm.Name)
+ if run, _ := isWSLRunning(dist); !run {
+ return 0, nil
+ }
+ cmd := exec.Command("wsl", "-d", dist, "cat", "/proc/meminfo")
+ out, err := cmd.StdoutPipe()
+ if err != nil {
+ return 0, err
+ }
+ if err = cmd.Start(); err != nil {
+ return 0, err
+ }
+ scanner := bufio.NewScanner(out)
+ var (
+ total, available uint64
+ t, a int
+ )
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+ if strings.HasPrefix(fields[0], "MemTotal") && len(fields) >= 2 {
+ t, err = strconv.Atoi(fields[1])
+ total = uint64(t) * 1024
+ } else if strings.HasPrefix(fields[0], "MemAvailable") && len(fields) >= 2 {
+ a, err = strconv.Atoi(fields[1])
+ available = uint64(a) * 1024
+ }
+ if err != nil {
+ break
+ }
+ }
+ _ = cmd.Wait()
+
+ return total - available, err
+}
+
+func (p *Provider) IsValidVMName(name string) (bool, error) {
+ infos, err := GetVMInfos()
+ if err != nil {
+ return false, err
+ }
+ for _, vm := range infos {
+ if vm.Name == name {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) {
+ return false, "", nil
+}
diff --git a/pkg/machine/wsl/machine_unsupported.go b/pkg/machine/wsl/machine_unsupported.go
new file mode 100644
index 000000000..043c5d729
--- /dev/null
+++ b/pkg/machine/wsl/machine_unsupported.go
@@ -0,0 +1,3 @@
+// +build !windows
+
+package wsl
diff --git a/pkg/machine/wsl/util_windows.go b/pkg/machine/wsl/util_windows.go
new file mode 100644
index 000000000..95e4c9894
--- /dev/null
+++ b/pkg/machine/wsl/util_windows.go
@@ -0,0 +1,338 @@
+package wsl
+
+import (
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
+
+ "github.com/containers/storage/pkg/homedir"
+)
+
+//nolint
+type SHELLEXECUTEINFO struct {
+ cbSize uint32
+ fMask uint32
+ hwnd syscall.Handle
+ lpVerb uintptr
+ lpFile uintptr
+ lpParameters uintptr
+ lpDirectory uintptr
+ nShow int
+ hInstApp syscall.Handle
+ lpIDList uintptr
+ lpClass uintptr
+ hkeyClass syscall.Handle
+ dwHotKey uint32
+ hIconOrMonitor syscall.Handle
+ hProcess syscall.Handle
+}
+
+//nolint
+type Luid struct {
+ lowPart uint32
+ highPart int32
+}
+
+type LuidAndAttributes struct {
+ luid Luid
+ attributes uint32
+}
+
+type TokenPrivileges struct {
+ privilegeCount uint32
+ privileges [1]LuidAndAttributes
+}
+
+//nolint // Cleaner to refer to the official OS constant names, and consistent with syscall
+const (
+ SEE_MASK_NOCLOSEPROCESS = 0x40
+ EWX_FORCEIFHUNG = 0x10
+ EWX_REBOOT = 0x02
+ EWX_RESTARTAPPS = 0x40
+ SHTDN_REASON_MAJOR_APPLICATION = 0x00040000
+ SHTDN_REASON_MINOR_INSTALLATION = 0x00000002
+ SHTDN_REASON_FLAG_PLANNED = 0x80000000
+ TOKEN_ADJUST_PRIVILEGES = 0x0020
+ TOKEN_QUERY = 0x0008
+ SE_PRIVILEGE_ENABLED = 0x00000002
+ SE_ERR_ACCESSDENIED = 0x05
+)
+
+func winVersionAtLeast(major uint, minor uint, build uint) bool {
+ var out [3]uint32
+
+ in := []uint32{uint32(major), uint32(minor), uint32(build)}
+ out[0], out[1], out[2] = windows.RtlGetNtVersionNumbers()
+
+ for i, o := range out {
+ if in[i] > o {
+ return false
+ }
+ if in[i] < o {
+ return true
+ }
+ }
+
+ return true
+}
+
+func hasAdminRights() bool {
+ var sid *windows.SID
+
+ // See: https://coolaj86.com/articles/golang-and-windows-and-admins-oh-my/
+ if err := windows.AllocateAndInitializeSid(
+ &windows.SECURITY_NT_AUTHORITY,
+ 2,
+ windows.SECURITY_BUILTIN_DOMAIN_RID,
+ windows.DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &sid); err != nil {
+ logrus.Warnf("SID allocation error: %s", err)
+ return false
+ }
+ defer windows.FreeSid(sid)
+
+ // From MS docs:
+ // "If TokenHandle is NULL, CheckTokenMembership uses the impersonation
+ // token of the calling thread. If the thread is not impersonating,
+ // the function duplicates the thread's primary token to create an
+ // impersonation token."
+ token := windows.Token(0)
+
+ member, err := token.IsMember(sid)
+ if err != nil {
+ logrus.Warnf("Token Membership Error: %s", err)
+ return false
+ }
+
+ return member || token.IsElevated()
+}
+
+func relaunchElevatedWait() error {
+ e, _ := os.Executable()
+ d, _ := os.Getwd()
+ exe, _ := syscall.UTF16PtrFromString(e)
+ cwd, _ := syscall.UTF16PtrFromString(d)
+ arg, _ := syscall.UTF16PtrFromString(buildCommandArgs(true))
+ verb, _ := syscall.UTF16PtrFromString("runas")
+
+ shell32 := syscall.NewLazyDLL("shell32.dll")
+
+ info := &SHELLEXECUTEINFO{
+ fMask: SEE_MASK_NOCLOSEPROCESS,
+ hwnd: 0,
+ lpVerb: uintptr(unsafe.Pointer(verb)),
+ lpFile: uintptr(unsafe.Pointer(exe)),
+ lpParameters: uintptr(unsafe.Pointer(arg)),
+ lpDirectory: uintptr(unsafe.Pointer(cwd)),
+ nShow: 1,
+ }
+ info.cbSize = uint32(unsafe.Sizeof(*info))
+ procShellExecuteEx := shell32.NewProc("ShellExecuteExW")
+ if ret, _, _ := procShellExecuteEx.Call(uintptr(unsafe.Pointer(info))); ret == 0 { // 0 = False
+ err := syscall.GetLastError()
+ if info.hInstApp == SE_ERR_ACCESSDENIED {
+ return wrapMaybe(err, "request to elevate privileges was denied")
+ }
+ return wrapMaybef(err, "could not launch process, ShellEX Error = %d", info.hInstApp)
+ }
+
+ handle := syscall.Handle(info.hProcess)
+ defer syscall.CloseHandle(handle)
+
+ w, err := syscall.WaitForSingleObject(handle, syscall.INFINITE)
+ switch w {
+ case syscall.WAIT_OBJECT_0:
+ break
+ case syscall.WAIT_FAILED:
+ return errors.Wrap(err, "could not wait for process, failed")
+ default:
+ return errors.Errorf("could not wait for process, unknown error")
+ }
+ var code uint32
+ if err := syscall.GetExitCodeProcess(handle, &code); err != nil {
+ return err
+ }
+ if code != 0 {
+ return &ExitCodeError{uint(code)}
+ }
+
+ return nil
+}
+
+func wrapMaybe(err error, message string) error {
+ if err != nil {
+ return errors.Wrap(err, message)
+ }
+
+ return errors.New(message)
+}
+
+func wrapMaybef(err error, format string, args ...interface{}) error {
+ if err != nil {
+ return errors.Wrapf(err, format, args...)
+ }
+
+ return errors.Errorf(format, args...)
+}
+
+func reboot() error {
+ const (
+ wtLocation = `Microsoft\WindowsApps\wt.exe`
+ wtPrefix = `%LocalAppData%\Microsoft\WindowsApps\wt -p "Windows PowerShell" `
+ localAppData = "LocalAppData"
+ pShellLaunch = `powershell -noexit "powershell -EncodedCommand (Get-Content '%s')"`
+ )
+
+ exe, _ := os.Executable()
+ relaunch := fmt.Sprintf("& %s %s", syscall.EscapeArg(exe), buildCommandArgs(false))
+ encoded := base64.StdEncoding.EncodeToString(encodeUTF16Bytes(relaunch))
+
+ dataDir, err := homedir.GetDataHome()
+ if err != nil {
+ return errors.Wrap(err, "could not determine data directory")
+ }
+ if err := os.MkdirAll(dataDir, 0755); err != nil {
+ return errors.Wrap(err, "could not create data directory")
+ }
+ commFile := filepath.Join(dataDir, "podman-relaunch.dat")
+ if err := ioutil.WriteFile(commFile, []byte(encoded), 0600); err != nil {
+ return errors.Wrap(err, "could not serialize command state")
+ }
+
+ command := fmt.Sprintf(pShellLaunch, commFile)
+ if _, err := os.Lstat(filepath.Join(os.Getenv(localAppData), wtLocation)); err == nil {
+ wtCommand := wtPrefix + command
+ // RunOnce is limited to 260 chars (supposedly no longer in Builds >= 19489)
+ // For now fallbacak in cases of long usernames (>89 chars)
+ if len(wtCommand) < 260 {
+ command = wtCommand
+ }
+ }
+
+ if err := addRunOnceRegistryEntry(command); err != nil {
+ return err
+ }
+
+ if err := obtainShutdownPrivilege(); err != nil {
+ return err
+ }
+
+ message := "To continue the process of enabling WSL, the system needs to reboot. " +
+ "Alternatively, you can cancel and reboot manually\n\n" +
+ "After rebooting, please wait a minute or two for podman machine to relaunch and continue installing."
+
+ if MessageBox(message, "Podman Machine", false) != 1 {
+ fmt.Println("Reboot is required to continue installation, please reboot at your convenience")
+ os.Exit(ErrorSuccessRebootRequired)
+ return nil
+ }
+
+ user32 := syscall.NewLazyDLL("user32")
+ procExit := user32.NewProc("ExitWindowsEx")
+ if ret, _, err := procExit.Call(EWX_REBOOT|EWX_RESTARTAPPS|EWX_FORCEIFHUNG,
+ SHTDN_REASON_MAJOR_APPLICATION|SHTDN_REASON_MINOR_INSTALLATION|SHTDN_REASON_FLAG_PLANNED); ret != 1 {
+ return errors.Wrap(err, "reboot failed")
+ }
+
+ return nil
+}
+
+func obtainShutdownPrivilege() error {
+ const SeShutdownName = "SeShutdownPrivilege"
+
+ advapi32 := syscall.NewLazyDLL("advapi32")
+ OpenProcessToken := advapi32.NewProc("OpenProcessToken")
+ LookupPrivilegeValue := advapi32.NewProc("LookupPrivilegeValueW")
+ AdjustTokenPrivileges := advapi32.NewProc("AdjustTokenPrivileges")
+
+ proc, _ := syscall.GetCurrentProcess()
+
+ var hToken uintptr
+ if ret, _, err := OpenProcessToken.Call(uintptr(proc), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, uintptr(unsafe.Pointer(&hToken))); ret != 1 {
+ return errors.Wrap(err, "error opening process token")
+ }
+
+ var privs TokenPrivileges
+ if ret, _, err := LookupPrivilegeValue.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeShutdownName))), uintptr(unsafe.Pointer(&(privs.privileges[0].luid)))); ret != 1 {
+ return errors.Wrap(err, "error looking up shutdown privilege")
+ }
+
+ privs.privilegeCount = 1
+ privs.privileges[0].attributes = SE_PRIVILEGE_ENABLED
+
+ if ret, _, err := AdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&privs)), 0, uintptr(0), 0); ret != 1 {
+ return errors.Wrap(err, "error enabling shutdown privilege on token")
+ }
+
+ return nil
+}
+
+func addRunOnceRegistryEntry(command string) error {
+ k, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\RunOnce`, registry.WRITE)
+ if err != nil {
+ return errors.Wrap(err, "could not open RunOnce registry entry")
+ }
+
+ defer k.Close()
+
+ if err := k.SetExpandStringValue("podman-machine", command); err != nil {
+ return errors.Wrap(err, "could not open RunOnce registry entry")
+ }
+
+ return nil
+}
+
+func encodeUTF16Bytes(s string) []byte {
+ u16 := utf16.Encode([]rune(s))
+ u16le := make([]byte, len(u16)*2)
+ for i := 0; i < len(u16); i++ {
+ u16le[i<<1] = byte(u16[i])
+ u16le[(i<<1)+1] = byte(u16[i] >> 8)
+ }
+ return u16le
+}
+
+func MessageBox(caption, title string, fail bool) int {
+ var format int
+ if fail {
+ format = 0x10
+ } else {
+ format = 0x41
+ }
+
+ user32 := syscall.NewLazyDLL("user32.dll")
+ captionPtr, _ := syscall.UTF16PtrFromString(caption)
+ titlePtr, _ := syscall.UTF16PtrFromString(title)
+ ret, _, _ := user32.NewProc("MessageBoxW").Call(
+ uintptr(0),
+ uintptr(unsafe.Pointer(captionPtr)),
+ uintptr(unsafe.Pointer(titlePtr)),
+ uintptr(format))
+
+ return int(ret)
+}
+
+func buildCommandArgs(elevate bool) string {
+ var args []string
+ for _, arg := range os.Args[1:] {
+ if arg != "--reexec" {
+ args = append(args, syscall.EscapeArg(arg))
+ if elevate && arg == "init" {
+ args = append(args, "--reexec")
+ }
+ }
+ }
+ return strings.Join(args, " ")
+}
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 3e81d5c14..92725adc7 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -145,8 +145,8 @@ func tryMappingTool(uid bool, pid int, hostID int, mappings []idtools.IDMap) err
}
if output, err := cmd.CombinedOutput(); err != nil {
- logrus.Debugf("error from %s: %s", tool, output)
- return errors.Wrapf(err, "cannot setup namespace using %s", tool)
+ logrus.Errorf("error running `%s`: %s", strings.Join(args, " "), output)
+ return errors.Wrapf(err, "cannot setup namespace using %q", path)
}
return nil
}
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 40a18a6ac..57676db10 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -156,7 +156,9 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
// Add annotations from the image
for k, v := range inspectData.Annotations {
- annotations[k] = v
+ if !define.IsReservedAnnotation(k) {
+ annotations[k] = v
+ }
}
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 331c9393a..7ab9d1b29 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -9,6 +9,7 @@ import (
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg"
"github.com/containers/common/libimage"
"github.com/containers/podman/v3/libpod"
+ "github.com/containers/podman/v3/pkg/namespaces"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -96,6 +97,12 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, nil, nil, err
}
s.UserNS = defaultNS
+
+ mappings, err := util.ParseIDMapping(namespaces.UsernsMode(s.UserNS.NSMode), nil, nil, "", "")
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ s.IDMappings = mappings
}
if s.NetNS.IsDefault() {
defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod)
@@ -149,6 +156,10 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, nil, nil, err
}
+ if len(s.HostUsers) > 0 {
+ options = append(options, libpod.WithHostUsers(s.HostUsers))
+ }
+
command, err := makeCommand(ctx, s, imageData, rtc)
if err != nil {
return nil, nil, nil, err
@@ -482,5 +493,8 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.PidFile != "" {
options = append(options, libpod.WithPidFile(s.PidFile))
}
+
+ options = append(options, libpod.WithSelectedPasswordManagement(s.Passwd))
+
return options, nil
}
diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go
index a44bf9979..c74db7325 100644
--- a/pkg/specgen/generate/validate.go
+++ b/pkg/specgen/generate/validate.go
@@ -60,10 +60,6 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error
if memory.Limit != nil && memory.Reservation != nil && *memory.Limit < *memory.Reservation {
return warnings, errors.New("minimum memory limit cannot be less than memory reservation limit, see usage")
}
- if memory.Kernel != nil && !sysInfo.KernelMemory {
- warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
- memory.Kernel = nil
- }
if memory.DisableOOMKiller != nil && *memory.DisableOOMKiller && !sysInfo.OomKillDisable {
warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
memory.DisableOOMKiller = nil
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index e650c1966..5989456c9 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -152,6 +152,9 @@ type ContainerBasicConfig struct {
// Conflicts with UtsNS if UtsNS is not set to private.
// Optional.
Hostname string `json:"hostname,omitempty"`
+ // HostUses is a list of host usernames or UIDs to add to the container
+ // /etc/passwd file
+ HostUsers []string `json:"hostusers,omitempty"`
// Sysctl sets kernel parameters for the container
Sysctl map[string]string `json:"sysctl,omitempty"`
// Remove indicates if the container should be removed once it has been started
@@ -201,6 +204,8 @@ type ContainerBasicConfig struct {
// UnsetEnvAll unsets all default environment variables from the image or from buildin
// Optional.
UnsetEnvAll bool `json:"unsetenvall,omitempty"`
+ // Passwd is a container run option that determines if we are validating users/groups before running the container
+ Passwd *bool `json:"manage_password,omitempty"`
}
// ContainerStorageConfig contains information on the storage configuration of a
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 123c0073b..8e43cc50e 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -163,14 +163,6 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOption
hasLimits = true
}
}
- if m := c.KernelMemory; len(m) > 0 {
- mk, err := units.RAMInBytes(m)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid value for kernel-memory")
- }
- memory.Kernel = &mk
- hasLimits = true
- }
if c.MemorySwappiness >= 0 {
swappiness := uint64(c.MemorySwappiness)
memory.Swappiness = &swappiness
@@ -445,6 +437,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.NetworkOptions = c.Net.NetworkOptions
s.UseImageHosts = c.Net.NoHosts
}
+ s.HostUsers = c.HostUsers
s.ImageVolumeMode = c.ImageVolume
if s.ImageVolumeMode == "bind" {
s.ImageVolumeMode = "anonymous"
@@ -698,6 +691,9 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
// Initcontainers
s.InitContainerType = c.InitContainerType
+
+ t := true
+ s.Passwd = &t
return nil
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 208d815d9..11edf265f 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -665,8 +665,8 @@ func CreateCidFile(cidfile string, id string) error {
return nil
}
-// DefaultCPUPeriod is the default CPU period is 100us, which is the same default
-// as Kubernetes.
+// DefaultCPUPeriod is the default CPU period (100ms) in microseconds, which is
+// the same default as Kubernetes.
const DefaultCPUPeriod uint64 = 100000
// CoresToPeriodAndQuota converts a fraction of cores to the equivalent
@@ -723,3 +723,11 @@ func SocketPath() (string, error) {
// Glue the socket path together
return filepath.Join(xdg, "podman", "podman.sock"), nil
}
+
+func LookupUser(name string) (*user.User, error) {
+ // Assume UID look up first, if it fails lookup by username
+ if u, err := user.LookupId(name); err == nil {
+ return u, err
+ }
+ return user.Lookup(name)
+}
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index 07b63e566..36c2fc6aa 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -53,8 +53,8 @@ t POST "images/create?fromImage=alpine" 200 .error~null .status~".*Download comp
t POST "images/create?fromImage=alpine&tag=latest" 200
# 10977 - handle platform parameter correctly
-t POST "images/create?fromImage=alpine&platform=linux/arm64" 200
-t GET "images/alpine/json" 200 \
+t POST "images/create?fromImage=testimage:20210610&platform=linux/arm64" 200
+t GET "images/testimage:20210610/json" 200 \
.Architecture=arm64
# Make sure that new images are pulled
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index e931ceebe..5a02ca3cb 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -46,6 +46,10 @@ t GET /containers/json?all=true 200 \
.[0].Image=$IMAGE \
$network_expect
+# compat API imageid with sha256: prefix
+t GET containers/json?limit=1 200 \
+ .[0].ImageID~sha256:[0-9a-f]\\{64\\}
+
# Make sure `limit` works.
t GET libpod/containers/json?limit=1 200 \
length=1 \
diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go
index 5ed873f78..d4f0a2b04 100644
--- a/test/e2e/build_test.go
+++ b/test/e2e/build_test.go
@@ -238,19 +238,25 @@ var _ = Describe("Podman build", func() {
Expect("sha256:" + data[0].ID).To(Equal(string(id)))
})
- It("podman Test PATH in built image", func() {
+ It("podman Test PATH and reserved annotation in built image", func() {
path := "/tmp:/bin:/usr/bin:/usr/sbin"
session := podmanTest.Podman([]string{
- "build", "--pull-never", "-f", "build/basicalpine/Containerfile.path", "-t", "test-path",
+ "build", "--annotation", "io.podman.annotations.seccomp=foobar", "--pull-never", "-f", "build/basicalpine/Containerfile.path", "-t", "test-path",
})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- session = podmanTest.Podman([]string{"run", "test-path", "printenv", "PATH"})
+ session = podmanTest.Podman([]string{"run", "--name", "foobar", "test-path", "printenv", "PATH"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
stdoutLines := session.OutputToStringArray()
Expect(stdoutLines[0]).Should(Equal(path))
+
+ // Reserved annotation should not be applied from the image to the container.
+ session = podmanTest.Podman([]string{"inspect", "foobar"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).NotTo(ContainSubstring("io.podman.annotations.seccomp"))
})
It("podman build --http_proxy flag", func() {
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 064c82621..4771f8e58 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -91,25 +91,97 @@ var _ = Describe("Podman checkpoint", func() {
Expect(session).Should(Exit(0))
cid := session.OutputToString()
- result := podmanTest.Podman([]string{"container", "checkpoint", cid})
+ // Check if none of the checkpoint/restore specific information is displayed
+ // for newly started containers.
+ inspect := podmanTest.Podman([]string{"inspect", cid})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).Should(Exit(0))
+ inspectOut := inspect.InspectContainerToJSON()
+ Expect(inspectOut[0].State.Checkpointed).To(BeFalse(), ".State.Checkpointed")
+ Expect(inspectOut[0].State.Restored).To(BeFalse(), ".State.Restored")
+ Expect(inspectOut[0].State.CheckpointPath).To(Equal(""))
+ Expect(inspectOut[0].State.CheckpointLog).To(Equal(""))
+ Expect(inspectOut[0].State.RestoreLog).To(Equal(""))
+
+ result := podmanTest.Podman([]string{
+ "container",
+ "checkpoint",
+ "--keep",
+ cid,
+ })
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
- inspect := podmanTest.Podman([]string{"inspect", cid})
+ // For a checkpointed container we expect the checkpoint related information
+ // to be populated.
+ inspect = podmanTest.Podman([]string{"inspect", cid})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
- inspectOut := inspect.InspectContainerToJSON()
+ inspectOut = inspect.InspectContainerToJSON()
Expect(inspectOut[0].State.Checkpointed).To(BeTrue(), ".State.Checkpointed")
+ Expect(inspectOut[0].State.Restored).To(BeFalse(), ".State.Restored")
+ Expect(inspectOut[0].State.CheckpointPath).To(ContainSubstring("userdata/checkpoint"))
+ Expect(inspectOut[0].State.CheckpointLog).To(ContainSubstring("userdata/dump.log"))
+ Expect(inspectOut[0].State.RestoreLog).To(Equal(""))
- result = podmanTest.Podman([]string{"container", "restore", cid})
+ result = podmanTest.Podman([]string{
+ "container",
+ "restore",
+ "--keep",
+ cid,
+ })
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ inspect = podmanTest.Podman([]string{"inspect", cid})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).Should(Exit(0))
+ inspectOut = inspect.InspectContainerToJSON()
+ Expect(inspectOut[0].State.Restored).To(BeTrue(), ".State.Restored")
+ Expect(inspectOut[0].State.Checkpointed).To(BeFalse(), ".State.Checkpointed")
+ Expect(inspectOut[0].State.CheckpointPath).To(ContainSubstring("userdata/checkpoint"))
+ Expect(inspectOut[0].State.CheckpointLog).To(ContainSubstring("userdata/dump.log"))
+ Expect(inspectOut[0].State.RestoreLog).To(ContainSubstring("userdata/restore.log"))
+
+ result = podmanTest.Podman([]string{
+ "container",
+ "stop",
+ "--timeout",
+ "0",
+ cid,
+ })
+ result.WaitWithDefaultTimeout()
+
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ result = podmanTest.Podman([]string{
+ "container",
+ "start",
+ cid,
+ })
+ result.WaitWithDefaultTimeout()
+
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+
+ // Stopping and starting the container should remove all checkpoint
+ // related information from inspect again.
+ inspect = podmanTest.Podman([]string{"inspect", cid})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).Should(Exit(0))
+ inspectOut = inspect.InspectContainerToJSON()
+ Expect(inspectOut[0].State.Checkpointed).To(BeFalse(), ".State.Checkpointed")
+ Expect(inspectOut[0].State.Restored).To(BeFalse(), ".State.Restored")
+ Expect(inspectOut[0].State.CheckpointPath).To(Equal(""))
+ Expect(inspectOut[0].State.CheckpointLog).To(Equal(""))
+ Expect(inspectOut[0].State.RestoreLog).To(Equal(""))
})
It("podman checkpoint a running container by name", func() {
@@ -867,6 +939,9 @@ var _ = Describe("Podman checkpoint", func() {
})
It("podman checkpoint container with --pre-checkpoint", func() {
+ if !criu.MemTrack() {
+ Skip("system (architecture/kernel/CRIU) does not support memory tracking")
+ }
if !strings.Contains(podmanTest.OCIRuntime, "runc") {
Skip("Test only works on runc 1.0-rc3 or higher.")
}
@@ -900,6 +975,9 @@ var _ = Describe("Podman checkpoint", func() {
It("podman checkpoint container with --pre-checkpoint and export (migration)", func() {
SkipIfRemote("--import-previous is not yet supported on the remote client")
+ if !criu.MemTrack() {
+ Skip("system (architecture/kernel/CRIU) does not support memory tracking")
+ }
if !strings.Contains(podmanTest.OCIRuntime, "runc") {
Skip("Test only works on runc 1.0-rc3 or higher.")
}
@@ -1553,4 +1631,91 @@ var _ = Describe("Podman checkpoint", func() {
// Remove exported checkpoint
os.Remove(fileName)
})
+
+ It("podman checkpoint and restore dev/shm content with --export and --import", func() {
+ localRunString := getRunString([]string{"--rm", ALPINE, "top"})
+ session := podmanTest.Podman(localRunString)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ cid := session.OutputToString()
+
+ // Add test file in dev/shm
+ result := podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "echo test" + cid + "test > /dev/shm/test.output"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"inspect", "--format", "{{.OCIRuntime}}", cid})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ runtime := session.OutputToString()
+
+ checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz"
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", checkpointFileName})
+ result.WaitWithDefaultTimeout()
+
+ // As the container has been started with '--rm' it will be completely
+ // cleaned up after checkpointing.
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainers()).To(Equal(0))
+
+ result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ // The restored container should have the same runtime as the original container
+ result = podmanTest.Podman([]string{"inspect", "--format", "{{.OCIRuntime}}", cid})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(session.OutputToString()).To(Equal(runtime))
+
+ // Verify the test file content in dev/shm
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/dev/shm/test.output"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(result.OutputToString()).To(ContainSubstring("test" + cid + "test"))
+
+ // Remove exported checkpoint
+ os.Remove(checkpointFileName)
+ })
+
+ It("podman checkpoint and restore dev/shm content", func() {
+ localRunString := getRunString([]string{ALPINE, "top"})
+ session := podmanTest.Podman(localRunString)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ cid := session.OutputToString()
+
+ // Add test file in dev/shm
+ result := podmanTest.Podman([]string{"exec", cid, "/bin/sh", "-c", "echo test" + cid + "test > /dev/shm/test.output"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+
+ result = podmanTest.Podman([]string{"container", "checkpoint", cid})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ result = podmanTest.Podman([]string{"container", "restore", cid})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ // Verify the test file content in dev/shm
+ result = podmanTest.Podman([]string{"exec", cid, "cat", "/dev/shm/test.output"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(result.OutputToString()).To(ContainSubstring("test" + cid + "test"))
+
+ result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
})
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index cfa264de2..16f2c4272 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -1100,4 +1100,28 @@ USER test1`
Expect(pod.GetAnnotations()).To(HaveKeyWithValue("io.containers.autoupdate.authfile/"+ctr, "/some/authfile.json"))
}
})
+
+ It("podman generate kube can export env variables correctly", func() {
+ // Fixes https://github.com/containers/podman/issues/12647
+ // PR https://github.com/containers/podman/pull/12648
+
+ ctrName := "gen-kube-env-ctr"
+ podName := "gen-kube-env"
+ session1 := podmanTest.Podman([]string{"run", "-d", "--pod", "new:" + podName, "--name", ctrName,
+ "-e", "FOO=bar",
+ "-e", "HELLO=WORLD",
+ "alpine", "top"})
+ session1.WaitWithDefaultTimeout()
+ Expect(session1).Should(Exit(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", podName})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube).Should(Exit(0))
+
+ pod := new(v1.Pod)
+ err := yaml.Unmarshal(kube.Out.Contents(), pod)
+ Expect(err).To(BeNil())
+
+ Expect(pod.Spec.Containers[0].Env).To(HaveLen(2))
+ })
})
diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go
index 04952bb03..04fac6bfb 100644
--- a/test/e2e/run_memory_test.go
+++ b/test/e2e/run_memory_test.go
@@ -3,7 +3,6 @@ package integration
import (
"fmt"
"os"
- "strconv"
. "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo"
@@ -79,45 +78,4 @@ var _ = Describe("Podman run memory", func() {
Expect(session.OutputToString()).To(Equal(limit))
})
}
-
- It("podman run kernel-memory test", func() {
- if podmanTest.Host.Distribution == "ubuntu" {
- Skip("Unable to perform test on Ubuntu distributions due to memory management")
- }
-
- var session *PodmanSessionIntegration
-
- if CGROUPSV2 {
- session = podmanTest.Podman([]string{"run", "--net=none", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"})
- } else {
- session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"})
- }
-
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- Expect(session.OutputToString()).To(Equal("41943040"))
- })
-
- It("podman run kernel-memory test", func() {
- if podmanTest.Host.Distribution == "ubuntu" {
- Skip("Unable to perform test on Ubuntu distributions due to memory management")
- }
- var session *PodmanSessionIntegration
- if CGROUPSV2 {
- session = podmanTest.Podman([]string{"run", "--memory", "256m", "--memory-swap", "-1", ALPINE, "cat", "/sys/fs/cgroup/memory.swap.max"})
- } else {
- session = podmanTest.Podman([]string{"run", "--cgroupns=private", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"})
- }
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- output := session.OutputToString()
- Expect(err).To(BeNil())
- if CGROUPSV2 {
- Expect(output).To(Equal("max"))
- } else {
- crazyHighNumber, err := strconv.ParseInt(output, 10, 64)
- Expect(err).To(BeZero())
- Expect(crazyHighNumber).To(BeNumerically(">", 936854771712))
- }
- })
})
diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go
index 6d1d26914..2207a50a8 100644
--- a/test/e2e/run_passwd_test.go
+++ b/test/e2e/run_passwd_test.go
@@ -125,4 +125,16 @@ USER 1000`, ALPINE)
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group")))
})
+
+ It("podman run --no-manage-passwd flag", func() {
+ run := podmanTest.Podman([]string{"run", "--user", "1234:1234", ALPINE, "cat", "/etc/passwd"})
+ run.WaitWithDefaultTimeout()
+ Expect(run).Should(Exit(0))
+ Expect(run.OutputToString()).To(ContainSubstring("1234:1234"))
+
+ run = podmanTest.Podman([]string{"run", "--passwd=false", "--user", "1234:1234", ALPINE, "cat", "/etc/passwd"})
+ run.WaitWithDefaultTimeout()
+ Expect(run).Should(Exit(0))
+ Expect(run.OutputToString()).NotTo((ContainSubstring("1234:1234")))
+ })
})
diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go
index eb7dc9d11..2f3c3025a 100644
--- a/test/e2e/run_staticip_test.go
+++ b/test/e2e/run_staticip_test.go
@@ -7,6 +7,7 @@ import (
"time"
. "github.com/containers/podman/v3/test/utils"
+ "github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
@@ -65,6 +66,20 @@ var _ = Describe("Podman run with --ip flag", func() {
Expect(result.OutputToString()).To(ContainSubstring(ip + "/16"))
})
+ It("Podman run with specified static IPv6 has correct IP", func() {
+ netName := "ipv6-" + stringid.GenerateNonCryptoID()
+ ipv6 := "fd46:db93:aa76:ac37::10"
+ net := podmanTest.Podman([]string{"network", "create", "--subnet", "fd46:db93:aa76:ac37::/64", netName})
+ net.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(netName)
+ Expect(net).To(Exit(0))
+
+ result := podmanTest.Podman([]string{"run", "-ti", "--network", netName, "--ip6", ipv6, ALPINE, "ip", "addr"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(result.OutputToString()).To(ContainSubstring(ipv6 + "/64"))
+ })
+
It("Podman run with --network bridge:ip=", func() {
ip := GetRandomIPAddress()
result := podmanTest.Podman([]string{"run", "-ti", "--network", "bridge:ip=" + ip, ALPINE, "ip", "addr"})
diff --git a/test/python/docker/compat/test_images.py b/test/python/docker/compat/test_images.py
index 1e2b531b7..485a0e419 100644
--- a/test/python/docker/compat/test_images.py
+++ b/test/python/docker/compat/test_images.py
@@ -79,6 +79,7 @@ class TestImages(unittest.TestCase):
# Add more images
self.client.images.pull(constant.BB)
self.assertEqual(len(self.client.images.list()), 2)
+ self.assertEqual(len(self.client.images.list(all=True)), 2)
# List images with filter
self.assertEqual(len(self.client.images.list(filters={"reference": "alpine"})), 1)
diff --git a/test/system/010-images.bats b/test/system/010-images.bats
index 9de31f96c..201418620 100644
--- a/test/system/010-images.bats
+++ b/test/system/010-images.bats
@@ -240,4 +240,64 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z
run_podman rmi test:1.0
}
+
+@test "podman images - rmi -af removes all containers and pods" {
+ pname=$(random_string)
+ run_podman create --pod new:$pname $IMAGE
+
+ run_podman inspect --format '{{.ID}}' $IMAGE
+ imageID=$output
+
+ run_podman version --format "{{.Server.Version}}-{{.Server.Built}}"
+ pauseImage=localhost/podman-pause:$output
+ run_podman inspect --format '{{.ID}}' $pauseImage
+ pauseID=$output
+
+ run_podman 2 rmi -a
+ is "$output" "Error: 2 errors occurred:
+.** Image used by .*: image is in use by a container
+.** Image used by .*: image is in use by a container"
+
+ run_podman rmi -af
+ is "$output" "Untagged: $IMAGE
+Untagged: $pauseImage
+Deleted: $imageID
+Deleted: $pauseID" "infra images gets removed as well"
+
+ run_podman images --noheading
+ is "$output" ""
+ run_podman ps --all --noheading
+ is "$output" ""
+ run_podman pod ps --noheading
+ is "$output" ""
+
+ run_podman create --pod new:$pname $IMAGE
+}
+
+@test "podman images - rmi -f can remove infra images" {
+ pname=$(random_string)
+ run_podman create --pod new:$pname $IMAGE
+
+ run_podman version --format "{{.Server.Version}}-{{.Server.Built}}"
+ pauseImage=localhost/podman-pause:$output
+ run_podman inspect --format '{{.ID}}' $pauseImage
+ pauseID=$output
+
+ run_podman 2 rmi $pauseImage
+ is "$output" "Error: Image used by .* image is in use by a container"
+
+ run_podman rmi -f $pauseImage
+ is "$output" "Untagged: $pauseImage
+Deleted: $pauseID"
+
+ # Force-removing the infra container removes the pod and all its containers.
+ run_podman ps --all --noheading
+ is "$output" ""
+ run_podman pod ps --noheading
+ is "$output" ""
+
+ # Other images are still present.
+ run_podman image exists $IMAGE
+}
+
# vim: filetype=sh
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 6f1fa600a..d81a0758c 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -711,6 +711,18 @@ EOF
run_podman rmi nomtab
}
+@test "podman run --hostuser tests" {
+ skip_if_not_rootless "test whether hostuser is successfully added"
+ user=$(id -un)
+ run_podman 1 run --rm $IMAGE grep $user /etc/passwd
+ run_podman run --hostuser=$user --rm $IMAGE grep $user /etc/passwd
+ user=$(id -u)
+ run_podman run --hostuser=$user --rm $IMAGE grep $user /etc/passwd
+ run_podman run --hostuser=$user --user $user --rm $IMAGE grep $user /etc/passwd
+ user=bogus
+ run_podman 126 run --hostuser=$user --rm $IMAGE grep $user /etc/passwd
+}
+
@test "podman run --device-cgroup-rule tests" {
skip_if_rootless "cannot add devices in rootless mode"
@@ -756,4 +768,39 @@ EOF
is "$output" ".*TERM=abc" "missing TERM environment variable despite TERM being set on commandline"
}
+@test "podman run - no /etc/hosts" {
+ skip_if_rootless "cannot move /etc/hosts file as a rootless user"
+ tmpfile=$PODMAN_TMPDIR/hosts
+ mv /etc/hosts $tmpfile
+ run_podman '?' run --rm --add-host "foo.com:1.2.3.4" $IMAGE cat "/etc/hosts"
+ mv $tmpfile /etc/hosts
+ is "$status" 0 "podman run without /etc/hosts file should work"
+ is "$output" "1.2.3.4 foo.com.*" "users can add hosts even without /etc/hosts"
+}
+
+# rhbz#1854566 : $IMAGE has incorrect permission 555 on the root '/' filesystem
+@test "podman run image with filesystem permission" {
+ # make sure the IMAGE image have permissiong of 555 like filesystem RPM expects
+ run_podman run --rm $IMAGE stat -c %a /
+ is "$output" "555" "directory permissions on /"
+}
+
+# rhbz#1763007 : the --log-opt for podman run does not work as expected
+@test "podman run with log-opt option" {
+ # Pseudorandom size of the form N.NNN. The '| 1' handles '0.NNN' or 'N.NN0',
+ # which podman displays as 'NNN kB' or 'N.NN MB' respectively.
+ size=$(printf "%d.%03d" $(($RANDOM % 10 | 1)) $(($RANDOM % 100 | 1)))
+ run_podman run -d --rm --log-opt max-size=${size}m $IMAGE sleep 5
+ cid=$output
+ run_podman inspect --format "{{ .HostConfig.LogConfig.Size }}" $cid
+ is "$output" "${size}MB"
+ run_podman rm -t 0 -f $cid
+}
+
+@test "podman run --kernel-memory warning" {
+ # Not sure what situations this fails in, but want to make sure warning shows.
+ run_podman '?' run --rm --kernel-memory 100 $IMAGE false
+ is "$output" ".*The --kernel-memory flag is no longer supported. This flag is a noop." "warn on use of --kernel-memory"
+
+}
# vim: filetype=sh
diff --git a/test/system/170-run-userns.bats b/test/system/170-run-userns.bats
index eb6c4e259..a5be591ef 100644
--- a/test/system/170-run-userns.bats
+++ b/test/system/170-run-userns.bats
@@ -17,7 +17,7 @@ function _require_crun() {
skip_if_rootless "chroot is not allowed in rootless mode"
skip_if_remote "--group-add keep-groups not supported in remote mode"
_require_crun
- run chroot --groups 1234 / ${PODMAN} run --uidmap 0:200000:5000 --group-add keep-groups $IMAGE id
+ run chroot --groups 1234 / ${PODMAN} run --rm --uidmap 0:200000:5000 --group-add keep-groups $IMAGE id
is "$output" ".*65534(nobody)" "Check group leaked into user namespace"
}
@@ -25,30 +25,56 @@ function _require_crun() {
skip_if_rootless "chroot is not allowed in rootless mode"
skip_if_remote "--group-add keep-groups not supported in remote mode"
_require_crun
- run chroot --groups 1234,5678 / ${PODMAN} run --group-add keep-groups $IMAGE id
+ run chroot --groups 1234,5678 / ${PODMAN} run --rm --group-add keep-groups $IMAGE id
is "$output" ".*1234" "Check group leaked into container"
}
@test "podman --group-add without keep-groups while in a userns" {
skip_if_rootless "chroot is not allowed in rootless mode"
skip_if_remote "--group-add keep-groups not supported in remote mode"
- run chroot --groups 1234,5678 / ${PODMAN} run --uidmap 0:200000:5000 --group-add 457 $IMAGE id
+ run chroot --groups 1234,5678 / ${PODMAN} run --rm --uidmap 0:200000:5000 --group-add 457 $IMAGE id
is "$output" ".*457" "Check group leaked into container"
}
@test "podman --remote --group-add keep-groups " {
if is_remote; then
- run_podman 125 run --group-add keep-groups $IMAGE id
+ run_podman 125 run --rm --group-add keep-groups $IMAGE id
is "$output" ".*not supported in remote mode" "Remote check --group-add keep-groups"
fi
}
@test "podman --group-add without keep-groups " {
- run_podman run --group-add 457 $IMAGE id
+ run_podman run --rm --group-add 457 $IMAGE id
is "$output" ".*457" "Check group leaked into container"
}
@test "podman --group-add keep-groups plus added groups " {
- run_podman 125 run --group-add keep-groups --group-add 457 $IMAGE id
+ run_podman 125 run --rm --group-add keep-groups --group-add 457 $IMAGE id
is "$output" ".*the '--group-add keep-groups' option is not allowed with any other --group-add options" "Check group leaked into container"
}
+
+@test "podman userns=auto in config file" {
+ skip_if_remote "userns=auto is set on the server"
+
+ if is_rootless; then
+ egrep -q "^$(id -un):" /etc/subuid || skip "no IDs allocated for current user"
+ else
+ egrep -q "^containers:" /etc/subuid || skip "no IDs allocated for user 'containers'"
+ fi
+
+ cat > $PODMAN_TMPDIR/userns_auto.conf <<EOF
+[containers]
+userns="auto"
+EOF
+ # First make sure a user namespace is created
+ CONTAINERS_CONF=$PODMAN_TMPDIR/userns_auto.conf run_podman run -d $IMAGE sleep infinity
+ cid=$output
+
+ run_podman inspect --format '{{.HostConfig.UsernsMode}}' $cid
+ is "$output" "private" "Check that a user namespace was created for the container"
+
+ run_podman rm -t 0 -f $cid
+
+ # Then check that the main user is not mapped into the user namespace
+ CONTAINERS_CONF=$PODMAN_TMPDIR/userns_auto.conf run_podman 0 run --rm $IMAGE awk '{if($2 == "0"){exit 1}}' /proc/self/uid_map /proc/self/gid_map
+}
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index 4d36163d7..2b5ad44dc 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -139,10 +139,11 @@ load helpers
@test "podman run with slirp4ns assigns correct addresses to /etc/hosts" {
CIDR="$(random_rfc1918_subnet)"
+ IP=$(hostname -I | cut -f 1 -d " ")
local conname=con-$(random_string 10)
run_podman run --rm --network slirp4netns:cidr="${CIDR}.0/24" \
--name $conname --hostname $conname $IMAGE cat /etc/hosts
- is "$output" ".*${CIDR}.2 host.containers.internal" "host.containers.internal should be the cidr+2 address"
+ is "$output" ".*${IP} host.containers.internal" "host.containers.internal should be the cidr+2 address"
is "$output" ".*${CIDR}.100 $conname $conname" "$conname should be the cidr+100 address"
}
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 97b6db05c..415c9010e 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -398,6 +398,16 @@ function skip_if_rootless() {
fi
}
+######################
+# skip_if_not_rootless # ...with an optional message
+######################
+function skip_if_not_rootless() {
+ if ! is_rootless; then
+ local msg=$(_add_label_if_missing "$1" "rootfull")
+ skip "${msg:-not applicable under rootlfull podman}"
+ fi
+}
+
####################
# skip_if_remote # ...with an optional message
####################
diff --git a/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go b/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
index 7c59ed23f..712fd2d50 100644
--- a/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
+++ b/vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
@@ -52,6 +52,7 @@ const (
SpecDumpFile = "spec.dump"
NetworkStatusFile = "network.status"
CheckpointDirectory = "checkpoint"
+ DevShmCheckpointTar = "devshm-checkpoint.tar"
RootFsDiffTar = "rootfs-diff.tar"
DeletedFilesFile = "deleted.files"
// pod archive
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile b/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile
index 558e61453..67c43a05b 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile
@@ -2,6 +2,11 @@ SHELL = /bin/bash
GO ?= go
CC ?= gcc
COVERAGE_PATH ?= $(shell pwd)/.coverage
+CRIU_FEATURE_MEM_TRACK = $(shell if criu check --feature mem_dirty_track > /dev/null; then echo 1; else echo 0; fi)
+CRIU_FEATURE_LAZY_PAGES = $(shell if criu check --feature uffd-noncoop > /dev/null; then echo 1; else echo 0; fi)
+CRIU_FEATURE_PIDFD_STORE = $(shell if criu check --feature pidfd_store > /dev/null; then echo 1; else echo 0; fi)
+
+export CRIU_FEATURE_MEM_TRACK CRIU_FEATURE_LAZY_PAGES CRIU_FEATURE_PIDFD_STORE
all: build test phaul-test
@@ -70,6 +75,8 @@ coverage: $(COVERAGE_BINARIES) $(TEST_PAYLOAD)
test/phaul/phaul.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE $$PID; \
pkill -9 piggie; \
}
+ echo "mode: set" > .coverage/coverage.out && cat .coverage/coverprofile* | \
+ grep -v mode: | sort -r | awk '{if($$1 != last) {print $$0;last=$$1}}' >> .coverage/coverage.out
clean:
@rm -f $(TEST_BINARIES) $(COVERAGE_BINARIES) codecov
@@ -95,6 +102,6 @@ vendor:
codecov:
curl -Os https://uploader.codecov.io/latest/linux/codecov
chmod +x codecov
- ./codecov -f '.coverage/*'
+ ./codecov -f '.coverage/coverage.out'
.PHONY: build test phaul-test test-bin clean lint vendor coverage codecov
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/features.go b/vendor/github.com/checkpoint-restore/go-criu/v5/features.go
new file mode 100644
index 000000000..c7127f951
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/features.go
@@ -0,0 +1,45 @@
+package criu
+
+import (
+ "fmt"
+
+ "github.com/checkpoint-restore/go-criu/v5/rpc"
+)
+
+// Feature checking in go-criu is based on the libcriu feature checking function.
+
+// Feature checking allows the user to check if CRIU supports
+// certain features. There are CRIU features which do not depend
+// on the version of CRIU but on kernel features or architecture.
+//
+// One example is memory tracking. Memory tracking can be disabled
+// in the kernel or there are architectures which do not support
+// it (aarch64 for example). By using the feature check a libcriu
+// user can easily query CRIU if a certain feature is available.
+//
+// The features which should be checked can be marked in the
+// structure 'struct criu_feature_check'. Each structure member
+// that is set to true will result in CRIU checking for the
+// availability of that feature in the current combination of
+// CRIU/kernel/architecture.
+//
+// Available features will be set to true when the function
+// returns successfully. Missing features will be set to false.
+
+func (c *Criu) FeatureCheck(features *rpc.CriuFeatures) (*rpc.CriuFeatures, error) {
+ resp, err := c.doSwrkWithResp(
+ rpc.CriuReqType_FEATURE_CHECK,
+ nil,
+ nil,
+ features,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ if resp.GetType() != rpc.CriuReqType_FEATURE_CHECK {
+ return nil, fmt.Errorf("Unexpected CRIU RPC response")
+ }
+
+ return features, nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/main.go b/vendor/github.com/checkpoint-restore/go-criu/v5/main.go
index 78811c309..88b1b2458 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/main.go
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/main.go
@@ -87,19 +87,19 @@ func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
}
func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
- resp, err := c.doSwrkWithResp(reqType, opts, nfy)
+ resp, err := c.doSwrkWithResp(reqType, opts, nfy, nil)
if err != nil {
return err
}
respType := resp.GetType()
if respType != reqType {
- return errors.New("unexpected responce")
+ return errors.New("unexpected CRIU RPC response")
}
return nil
}
-func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) (*rpc.CriuResp, error) {
+func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify, features *rpc.CriuFeatures) (*rpc.CriuResp, error) {
var resp *rpc.CriuResp
req := rpc.CriuReq{
@@ -111,6 +111,10 @@ func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy N
opts.NotifyScripts = proto.Bool(true)
}
+ if features != nil {
+ req.Features = features
+ }
+
if c.swrkCmd == nil {
err := c.Prepare()
if err != nil {
@@ -209,7 +213,7 @@ func (c *Criu) StartPageServer(opts *rpc.CriuOpts) error {
// StartPageServerChld starts the page server and returns PID and port
func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
- resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil)
+ resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil, nil)
if err != nil {
return 0, 0, err
}
@@ -220,7 +224,7 @@ func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
// GetCriuVersion executes the VERSION RPC call and returns the version
// as an integer. Major * 10000 + Minor * 100 + SubLevel
func (c *Criu) GetCriuVersion() (int, error) {
- resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil)
+ resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil, nil)
if err != nil {
return 0, err
}
diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go
index 29c505e9c..f419601e9 100644
--- a/vendor/github.com/containers/common/pkg/config/config.go
+++ b/vendor/github.com/containers/common/pkg/config/config.go
@@ -512,6 +512,8 @@ type MachineConfig struct {
Image string `toml:"image,omitempty"`
// Memory in MB a machine is created with.
Memory uint64 `toml:"memory,omitempty,omitzero"`
+ // Username to use for rootless podman when init-ing a podman machine VM
+ User string `toml:"user,omitempty"`
}
// Destination represents destination for remote service
diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf
index 84b49b7e4..4e8ad21f8 100644
--- a/vendor/github.com/containers/common/pkg/config/containers.conf
+++ b/vendor/github.com/containers/common/pkg/config/containers.conf
@@ -587,6 +587,11 @@ default_sysctls = [
#
#memory=2048
+# The username to use and create on the podman machine OS for rootless
+# container access.
+#
+#user = "core"
+
# The [machine] table MUST be the last entry in this file.
# (Unless another table is added)
# TOML does not provide a way to end a table other than a further table being
diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go
index 8821aa91e..cd7fea4a1 100644
--- a/vendor/github.com/containers/common/pkg/config/default.go
+++ b/vendor/github.com/containers/common/pkg/config/default.go
@@ -227,8 +227,9 @@ func defaultMachineConfig() MachineConfig {
return MachineConfig{
CPUs: 1,
DiskSize: 100,
- Image: "testing",
+ Image: getDefaultMachineImage(),
Memory: 2048,
+ User: getDefaultMachineUser(),
}
}
diff --git a/vendor/github.com/containers/common/pkg/config/default_linux.go b/vendor/github.com/containers/common/pkg/config/default_linux.go
index c68c0b130..9446d3ff9 100644
--- a/vendor/github.com/containers/common/pkg/config/default_linux.go
+++ b/vendor/github.com/containers/common/pkg/config/default_linux.go
@@ -13,6 +13,17 @@ const (
oldMaxSize = uint64(1048576)
)
+// getDefaultMachineImage returns the default machine image stream
+// On Linux/Mac, this returns the FCOS stream
+func getDefaultMachineImage() string {
+ return "testing"
+}
+
+// getDefaultMachineUser returns the user to use for rootless podman
+func getDefaultMachineUser() string {
+ return "core"
+}
+
// getDefaultRootlessNetwork returns the default rootless network configuration.
// It is "slirp4netns" for Linux.
func getDefaultRootlessNetwork() string {
diff --git a/vendor/github.com/containers/common/pkg/config/default_unsupported.go b/vendor/github.com/containers/common/pkg/config/default_unsupported.go
index e38fb810d..b6ee286ec 100644
--- a/vendor/github.com/containers/common/pkg/config/default_unsupported.go
+++ b/vendor/github.com/containers/common/pkg/config/default_unsupported.go
@@ -1,7 +1,18 @@
-// +build !linux
+// +build !linux,!windows
package config
+// getDefaultMachineImage returns the default machine image stream
+// On Linux/Mac, this returns the FCOS stream
+func getDefaultMachineImage() string {
+ return "testing"
+}
+
+// getDefaultMachineUser returns the user to use for rootless podman
+func getDefaultMachineUser() string {
+ return "core"
+}
+
// getDefaultRootlessNetwork returns the default rootless network configuration.
// It is "cni" for non-Linux OSes (to better support `podman-machine` usecases).
func getDefaultRootlessNetwork() string {
diff --git a/vendor/github.com/containers/common/pkg/config/default_windows.go b/vendor/github.com/containers/common/pkg/config/default_windows.go
new file mode 100644
index 000000000..5f8dd1a28
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/config/default_windows.go
@@ -0,0 +1,28 @@
+package config
+
+// getDefaultImage returns the default machine image stream
+// On Windows this refers to the Fedora major release number
+func getDefaultMachineImage() string {
+ return "35"
+}
+
+// getDefaultMachineUser returns the user to use for rootless podman
+func getDefaultMachineUser() string {
+ return "user"
+}
+
+// getDefaultRootlessNetwork returns the default rootless network configuration.
+// It is "cni" for non-Linux OSes (to better support `podman-machine` usecases).
+func getDefaultRootlessNetwork() string {
+ return "cni"
+}
+
+// isCgroup2UnifiedMode returns whether we are running in cgroup2 mode.
+func isCgroup2UnifiedMode() (isUnified bool, isUnifiedErr error) {
+ return false, nil
+}
+
+// getDefaultProcessLimits returns the nofile and nproc for the current process in ulimits format
+func getDefaultProcessLimits() []string {
+ return []string{}
+}
diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod
index 57b634f17..96ca1f0b2 100644
--- a/vendor/github.com/containers/storage/go.mod
+++ b/vendor/github.com/containers/storage/go.mod
@@ -18,7 +18,7 @@ require (
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible
github.com/moby/sys/mountinfo v0.5.0
github.com/opencontainers/go-digest v1.0.0
- github.com/opencontainers/runc v1.0.2
+ github.com/opencontainers/runc v1.0.3
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/selinux v1.10.0
github.com/pkg/errors v0.9.1
diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum
index 94d46b21a..c7262fe7a 100644
--- a/vendor/github.com/containers/storage/go.sum
+++ b/vendor/github.com/containers/storage/go.sum
@@ -514,8 +514,9 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
-github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg=
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
+github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k=
+github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go
index fbf6ad362..e2357c540 100644
--- a/vendor/github.com/containers/storage/layers.go
+++ b/vendor/github.com/containers/storage/layers.go
@@ -23,6 +23,7 @@ import (
"github.com/containers/storage/pkg/system"
"github.com/containers/storage/pkg/tarlog"
"github.com/containers/storage/pkg/truncindex"
+ multierror "github.com/hashicorp/go-multierror"
"github.com/klauspost/pgzip"
digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/selinux/go-selinux/label"
@@ -1463,34 +1464,48 @@ func (r *layerStore) Diff(from, to string, options *DiffOptions) (io.ReadCloser,
}
return maybeCompressReadCloser(diff)
}
- defer tsfile.Close()
decompressor, err := pgzip.NewReader(tsfile)
if err != nil {
- return nil, err
- }
- defer decompressor.Close()
-
- tsbytes, err := ioutil.ReadAll(decompressor)
- if err != nil {
+ if e := tsfile.Close(); e != nil {
+ logrus.Debug(e)
+ }
return nil, err
}
- metadata = storage.NewJSONUnpacker(bytes.NewBuffer(tsbytes))
+ metadata = storage.NewJSONUnpacker(decompressor)
fgetter, err := r.newFileGetter(to)
if err != nil {
- return nil, err
+ errs := multierror.Append(nil, errors.Wrapf(err, "creating file-getter"))
+ if err := decompressor.Close(); err != nil {
+ errs = multierror.Append(errs, errors.Wrapf(err, "closing decompressor"))
+ }
+ if err := tsfile.Close(); err != nil {
+ errs = multierror.Append(errs, errors.Wrapf(err, "closing tarstream headers"))
+ }
+ return nil, errs.ErrorOrNil()
}
tarstream := asm.NewOutputTarStream(fgetter, metadata)
rc := ioutils.NewReadCloserWrapper(tarstream, func() error {
- err1 := tarstream.Close()
- err2 := fgetter.Close()
- if err2 == nil {
- return err1
+ var errs *multierror.Error
+ if err := decompressor.Close(); err != nil {
+ errs = multierror.Append(errs, errors.Wrapf(err, "closing decompressor"))
+ }
+ if err := tsfile.Close(); err != nil {
+ errs = multierror.Append(errs, errors.Wrapf(err, "closing tarstream headers"))
+ }
+ if err := tarstream.Close(); err != nil {
+ errs = multierror.Append(errs, errors.Wrapf(err, "closing reconstructed tarstream"))
+ }
+ if err := fgetter.Close(); err != nil {
+ errs = multierror.Append(errs, errors.Wrapf(err, "closing file-getter"))
+ }
+ if errs != nil {
+ return errs.ErrorOrNil()
}
- return err2
+ return nil
})
return maybeCompressReadCloser(rc)
}
diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir.go b/vendor/github.com/containers/storage/pkg/homedir/homedir.go
new file mode 100644
index 000000000..85c5e76c8
--- /dev/null
+++ b/vendor/github.com/containers/storage/pkg/homedir/homedir.go
@@ -0,0 +1,52 @@
+package homedir
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+)
+
+// GetConfigHome returns XDG_CONFIG_HOME.
+// GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set.
+//
+// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
+func GetConfigHome() (string, error) {
+ if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
+ return xdgConfigHome, nil
+ }
+ home := Get()
+ if home == "" {
+ return "", errors.New("could not get either XDG_CONFIG_HOME or HOME")
+ }
+ return filepath.Join(home, ".config"), nil
+}
+
+// GetDataHome returns XDG_DATA_HOME.
+// GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set.
+//
+// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
+func GetDataHome() (string, error) {
+ if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" {
+ return xdgDataHome, nil
+ }
+ home := Get()
+ if home == "" {
+ return "", errors.New("could not get either XDG_DATA_HOME or HOME")
+ }
+ return filepath.Join(home, ".local", "share"), nil
+}
+
+// GetCacheHome returns XDG_CACHE_HOME.
+// GetCacheHome returns $HOME/.cache and nil error if XDG_CACHE_HOME is not set.
+//
+// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
+func GetCacheHome() (string, error) {
+ if xdgCacheHome := os.Getenv("XDG_CACHE_HOME"); xdgCacheHome != "" {
+ return xdgCacheHome, nil
+ }
+ home := Get()
+ if home == "" {
+ return "", errors.New("could not get either XDG_CACHE_HOME or HOME")
+ }
+ return filepath.Join(home, ".cache"), nil
+}
diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go
index 06b53854b..027db259c 100644
--- a/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go
+++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_others.go
@@ -18,18 +18,3 @@ func GetRuntimeDir() (string, error) {
func StickRuntimeDirContents(files []string) ([]string, error) {
return nil, errors.New("homedir.StickRuntimeDirContents() is not supported on this system")
}
-
-// GetDataHome is unsupported on non-linux system.
-func GetDataHome() (string, error) {
- return "", errors.New("homedir.GetDataHome() is not supported on this system")
-}
-
-// GetConfigHome is unsupported on non-linux system.
-func GetConfigHome() (string, error) {
- return "", errors.New("homedir.GetConfigHome() is not supported on this system")
-}
-
-// GetCacheHome is unsupported on non-linux system.
-func GetCacheHome() (string, error) {
- return "", errors.New("homedir.GetCacheHome() is not supported on this system")
-}
diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go
index 2475e351b..33177bdf3 100644
--- a/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go
+++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_unix.go
@@ -93,48 +93,3 @@ func stick(f string) error {
m |= os.ModeSticky
return os.Chmod(f, m)
}
-
-// GetDataHome returns XDG_DATA_HOME.
-// GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set.
-//
-// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
-func GetDataHome() (string, error) {
- if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" {
- return xdgDataHome, nil
- }
- home := Get()
- if home == "" {
- return "", errors.New("could not get either XDG_DATA_HOME or HOME")
- }
- return filepath.Join(home, ".local", "share"), nil
-}
-
-// GetConfigHome returns XDG_CONFIG_HOME.
-// GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set.
-//
-// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
-func GetConfigHome() (string, error) {
- if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
- return xdgConfigHome, nil
- }
- home := Get()
- if home == "" {
- return "", errors.New("could not get either XDG_CONFIG_HOME or HOME")
- }
- return filepath.Join(home, ".config"), nil
-}
-
-// GetCacheHome returns XDG_CACHE_HOME.
-// GetCacheHome returns $HOME/.cache and nil error if XDG_CACHE_HOME is not set.
-//
-// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
-func GetCacheHome() (string, error) {
- if xdgCacheHome := os.Getenv("XDG_CACHE_HOME"); xdgCacheHome != "" {
- return xdgCacheHome, nil
- }
- home := Get()
- if home == "" {
- return "", errors.New("could not get either XDG_CACHE_HOME or HOME")
- }
- return filepath.Join(home, ".cache"), nil
-}
diff --git a/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go b/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go
index 4f2615ed3..af65f2c03 100644
--- a/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go
+++ b/vendor/github.com/containers/storage/pkg/homedir/homedir_windows.go
@@ -17,7 +17,12 @@ func Key() string {
// environment variables depending on the target operating system.
// Returned path should be used with "path/filepath" to form new paths.
func Get() string {
- return os.Getenv(Key())
+ home := os.Getenv(Key())
+ if home != "" {
+ return home
+ }
+ home, _ = os.UserHomeDir()
+ return home
}
// GetShortcutString returns the string that is shortcut to user's home directory
diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go b/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go
index db50a62e4..e444a1bcc 100644
--- a/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go
+++ b/vendor/github.com/containers/storage/pkg/idtools/idtools_supported.go
@@ -17,6 +17,12 @@ struct subid_range get_range(struct subid_range *ranges, int i)
{
return ranges[i];
}
+
+#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4)
+# define subid_get_uid_ranges get_subuid_ranges
+# define subid_get_gid_ranges get_subgid_ranges
+#endif
+
*/
import "C"
@@ -32,9 +38,9 @@ func readSubid(username string, isUser bool) (ranges, error) {
var nRanges C.int
var cRanges *C.struct_subid_range
if isUser {
- nRanges = C.get_subuid_ranges(cUsername, &cRanges)
+ nRanges = C.subid_get_uid_ranges(cUsername, &cRanges)
} else {
- nRanges = C.get_subgid_ranges(cUsername, &cRanges)
+ nRanges = C.subid_get_gid_ranges(cUsername, &cRanges)
}
if nRanges < 0 {
return nil, errors.New("cannot read subids")
diff --git a/vendor/github.com/containers/storage/types/options.go b/vendor/github.com/containers/storage/types/options.go
index fe4274efd..7586cd5ae 100644
--- a/vendor/github.com/containers/storage/types/options.go
+++ b/vendor/github.com/containers/storage/types/options.go
@@ -27,6 +27,13 @@ type tomlConfig struct {
} `toml:"storage"`
}
+const (
+ // these are default path for run and graph root for rootful users
+ // for rootless path is constructed via getRootlessStorageOpts
+ defaultRunRoot string = "/run/containers/storage"
+ defaultGraphRoot string = "/var/lib/containers/storage"
+)
+
// defaultConfigFile path to the system wide storage.conf file
var (
defaultConfigFile = "/usr/share/containers/storage.conf"
@@ -36,9 +43,14 @@ var (
defaultStoreOptions StoreOptions
)
+const (
+ overlayDriver = "overlay"
+ overlay2 = "overlay2"
+)
+
func init() {
- defaultStoreOptions.RunRoot = "/run/containers/storage"
- defaultStoreOptions.GraphRoot = "/var/lib/containers/storage"
+ defaultStoreOptions.RunRoot = defaultRunRoot
+ defaultStoreOptions.GraphRoot = defaultGraphRoot
defaultStoreOptions.GraphDriverName = ""
if _, err := os.Stat(defaultOverrideConfigFile); err == nil {
@@ -53,6 +65,13 @@ func init() {
}
ReloadConfigurationFileIfNeeded(defaultConfigFile, &defaultStoreOptions)
}
+ // reload could set values to empty for run and graph root if config does not contains anything
+ if defaultStoreOptions.RunRoot == "" {
+ defaultStoreOptions.RunRoot = defaultRunRoot
+ }
+ if defaultStoreOptions.GraphRoot == "" {
+ defaultStoreOptions.GraphRoot = defaultGraphRoot
+ }
}
// defaultStoreOptionsIsolated is an internal implementation detail of DefaultStoreOptions to allow testing.
@@ -180,7 +199,6 @@ func isRootlessDriver(driver string) bool {
// getRootlessStorageOpts returns the storage opts for containers running as non root
func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOptions, error) {
var opts StoreOptions
- const overlayDriver = "overlay"
dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUID)
if err != nil {
@@ -202,6 +220,11 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti
if driver := os.Getenv("STORAGE_DRIVER"); driver != "" {
opts.GraphDriverName = driver
}
+ if opts.GraphDriverName == overlay2 {
+ logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver.")
+ opts.GraphDriverName = overlayDriver
+ }
+
if opts.GraphDriverName == "" || opts.GraphDriverName == overlayDriver {
supported, err := overlay.SupportsNativeOverlay(opts.GraphRoot, rootlessRuntime)
if err != nil {
@@ -307,6 +330,10 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
config.Storage.Driver = os.Getenv("STORAGE_DRIVER")
storeOptions.GraphDriverName = config.Storage.Driver
}
+ if storeOptions.GraphDriverName == overlay2 {
+ logrus.Warnf("Switching default driver from overlay2 to the equivalent overlay driver.")
+ storeOptions.GraphDriverName = overlayDriver
+ }
if storeOptions.GraphDriverName == "" {
logrus.Errorf("The storage 'driver' option must be set in %s, guarantee proper operation.", configFile)
}
diff --git a/vendor/golang.org/x/sys/windows/registry/key.go b/vendor/golang.org/x/sys/windows/registry/key.go
new file mode 100644
index 000000000..906325e09
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/registry/key.go
@@ -0,0 +1,207 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build windows
+// +build windows
+
+// Package registry provides access to the Windows registry.
+//
+// Here is a simple example, opening a registry key and reading a string value from it.
+//
+// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
+// if err != nil {
+// log.Fatal(err)
+// }
+// defer k.Close()
+//
+// s, _, err := k.GetStringValue("SystemRoot")
+// if err != nil {
+// log.Fatal(err)
+// }
+// fmt.Printf("Windows system root is %q\n", s)
+//
+package registry
+
+import (
+ "io"
+ "runtime"
+ "syscall"
+ "time"
+)
+
+const (
+ // Registry key security and access rights.
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx
+ // for details.
+ ALL_ACCESS = 0xf003f
+ CREATE_LINK = 0x00020
+ CREATE_SUB_KEY = 0x00004
+ ENUMERATE_SUB_KEYS = 0x00008
+ EXECUTE = 0x20019
+ NOTIFY = 0x00010
+ QUERY_VALUE = 0x00001
+ READ = 0x20019
+ SET_VALUE = 0x00002
+ WOW64_32KEY = 0x00200
+ WOW64_64KEY = 0x00100
+ WRITE = 0x20006
+)
+
+// Key is a handle to an open Windows registry key.
+// Keys can be obtained by calling OpenKey; there are
+// also some predefined root keys such as CURRENT_USER.
+// Keys can be used directly in the Windows API.
+type Key syscall.Handle
+
+const (
+ // Windows defines some predefined root keys that are always open.
+ // An application can use these keys as entry points to the registry.
+ // Normally these keys are used in OpenKey to open new keys,
+ // but they can also be used anywhere a Key is required.
+ CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT)
+ CURRENT_USER = Key(syscall.HKEY_CURRENT_USER)
+ LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE)
+ USERS = Key(syscall.HKEY_USERS)
+ CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
+ PERFORMANCE_DATA = Key(syscall.HKEY_PERFORMANCE_DATA)
+)
+
+// Close closes open key k.
+func (k Key) Close() error {
+ return syscall.RegCloseKey(syscall.Handle(k))
+}
+
+// OpenKey opens a new key with path name relative to key k.
+// It accepts any open key, including CURRENT_USER and others,
+// and returns the new key and an error.
+// The access parameter specifies desired access rights to the
+// key to be opened.
+func OpenKey(k Key, path string, access uint32) (Key, error) {
+ p, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return 0, err
+ }
+ var subkey syscall.Handle
+ err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey)
+ if err != nil {
+ return 0, err
+ }
+ return Key(subkey), nil
+}
+
+// OpenRemoteKey opens a predefined registry key on another
+// computer pcname. The key to be opened is specified by k, but
+// can only be one of LOCAL_MACHINE, PERFORMANCE_DATA or USERS.
+// If pcname is "", OpenRemoteKey returns local computer key.
+func OpenRemoteKey(pcname string, k Key) (Key, error) {
+ var err error
+ var p *uint16
+ if pcname != "" {
+ p, err = syscall.UTF16PtrFromString(`\\` + pcname)
+ if err != nil {
+ return 0, err
+ }
+ }
+ var remoteKey syscall.Handle
+ err = regConnectRegistry(p, syscall.Handle(k), &remoteKey)
+ if err != nil {
+ return 0, err
+ }
+ return Key(remoteKey), nil
+}
+
+// ReadSubKeyNames returns the names of subkeys of key k.
+// The parameter n controls the number of returned names,
+// analogous to the way os.File.Readdirnames works.
+func (k Key) ReadSubKeyNames(n int) ([]string, error) {
+ // RegEnumKeyEx must be called repeatedly and to completion.
+ // During this time, this goroutine cannot migrate away from
+ // its current thread. See https://golang.org/issue/49320 and
+ // https://golang.org/issue/49466.
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ names := make([]string, 0)
+ // Registry key size limit is 255 bytes and described there:
+ // https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx
+ buf := make([]uint16, 256) //plus extra room for terminating zero byte
+loopItems:
+ for i := uint32(0); ; i++ {
+ if n > 0 {
+ if len(names) == n {
+ return names, nil
+ }
+ }
+ l := uint32(len(buf))
+ for {
+ err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
+ if err == nil {
+ break
+ }
+ if err == syscall.ERROR_MORE_DATA {
+ // Double buffer size and try again.
+ l = uint32(2 * len(buf))
+ buf = make([]uint16, l)
+ continue
+ }
+ if err == _ERROR_NO_MORE_ITEMS {
+ break loopItems
+ }
+ return names, err
+ }
+ names = append(names, syscall.UTF16ToString(buf[:l]))
+ }
+ if n > len(names) {
+ return names, io.EOF
+ }
+ return names, nil
+}
+
+// CreateKey creates a key named path under open key k.
+// CreateKey returns the new key and a boolean flag that reports
+// whether the key already existed.
+// The access parameter specifies the access rights for the key
+// to be created.
+func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
+ var h syscall.Handle
+ var d uint32
+ err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path),
+ 0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d)
+ if err != nil {
+ return 0, false, err
+ }
+ return Key(h), d == _REG_OPENED_EXISTING_KEY, nil
+}
+
+// DeleteKey deletes the subkey path of key k and its values.
+func DeleteKey(k Key, path string) error {
+ return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path))
+}
+
+// A KeyInfo describes the statistics of a key. It is returned by Stat.
+type KeyInfo struct {
+ SubKeyCount uint32
+ MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte
+ ValueCount uint32
+ MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte
+ MaxValueLen uint32 // longest data component among the key's values, in bytes
+ lastWriteTime syscall.Filetime
+}
+
+// ModTime returns the key's last write time.
+func (ki *KeyInfo) ModTime() time.Time {
+ return time.Unix(0, ki.lastWriteTime.Nanoseconds())
+}
+
+// Stat retrieves information about the open key k.
+func (k Key) Stat() (*KeyInfo, error) {
+ var ki KeyInfo
+ err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
+ &ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
+ &ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
+ if err != nil {
+ return nil, err
+ }
+ return &ki, nil
+}
diff --git a/vendor/golang.org/x/sys/windows/registry/mksyscall.go b/vendor/golang.org/x/sys/windows/registry/mksyscall.go
new file mode 100644
index 000000000..ee74927d3
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/registry/mksyscall.go
@@ -0,0 +1,10 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build generate
+// +build generate
+
+package registry
+
+//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall.go
diff --git a/vendor/golang.org/x/sys/windows/registry/syscall.go b/vendor/golang.org/x/sys/windows/registry/syscall.go
new file mode 100644
index 000000000..417335123
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/registry/syscall.go
@@ -0,0 +1,33 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build windows
+// +build windows
+
+package registry
+
+import "syscall"
+
+const (
+ _REG_OPTION_NON_VOLATILE = 0
+
+ _REG_CREATED_NEW_KEY = 1
+ _REG_OPENED_EXISTING_KEY = 2
+
+ _ERROR_NO_MORE_ITEMS syscall.Errno = 259
+)
+
+func LoadRegLoadMUIString() error {
+ return procRegLoadMUIStringW.Find()
+}
+
+//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW
+//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW
+//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW
+//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW
+//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW
+//sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW
+//sys regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) = advapi32.RegConnectRegistryW
+
+//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW
diff --git a/vendor/golang.org/x/sys/windows/registry/value.go b/vendor/golang.org/x/sys/windows/registry/value.go
new file mode 100644
index 000000000..2789f6f18
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/registry/value.go
@@ -0,0 +1,387 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build windows
+// +build windows
+
+package registry
+
+import (
+ "errors"
+ "io"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+const (
+ // Registry value types.
+ NONE = 0
+ SZ = 1
+ EXPAND_SZ = 2
+ BINARY = 3
+ DWORD = 4
+ DWORD_BIG_ENDIAN = 5
+ LINK = 6
+ MULTI_SZ = 7
+ RESOURCE_LIST = 8
+ FULL_RESOURCE_DESCRIPTOR = 9
+ RESOURCE_REQUIREMENTS_LIST = 10
+ QWORD = 11
+)
+
+var (
+ // ErrShortBuffer is returned when the buffer was too short for the operation.
+ ErrShortBuffer = syscall.ERROR_MORE_DATA
+
+ // ErrNotExist is returned when a registry key or value does not exist.
+ ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
+
+ // ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
+ ErrUnexpectedType = errors.New("unexpected key value type")
+)
+
+// GetValue retrieves the type and data for the specified value associated
+// with an open key k. It fills up buffer buf and returns the retrieved
+// byte count n. If buf is too small to fit the stored value it returns
+// ErrShortBuffer error along with the required buffer size n.
+// If no buffer is provided, it returns true and actual buffer size n.
+// If no buffer is provided, GetValue returns the value's type only.
+// If the value does not exist, the error returned is ErrNotExist.
+//
+// GetValue is a low level function. If value's type is known, use the appropriate
+// Get*Value function instead.
+func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
+ pname, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return 0, 0, err
+ }
+ var pbuf *byte
+ if len(buf) > 0 {
+ pbuf = (*byte)(unsafe.Pointer(&buf[0]))
+ }
+ l := uint32(len(buf))
+ err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
+ if err != nil {
+ return int(l), valtype, err
+ }
+ return int(l), valtype, nil
+}
+
+func (k Key) getValue(name string, buf []byte) (data []byte, valtype uint32, err error) {
+ p, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return nil, 0, err
+ }
+ var t uint32
+ n := uint32(len(buf))
+ for {
+ err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
+ if err == nil {
+ return buf[:n], t, nil
+ }
+ if err != syscall.ERROR_MORE_DATA {
+ return nil, 0, err
+ }
+ if n <= uint32(len(buf)) {
+ return nil, 0, err
+ }
+ buf = make([]byte, n)
+ }
+}
+
+// GetStringValue retrieves the string value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetStringValue returns ErrNotExist.
+// If value is not SZ or EXPAND_SZ, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 64))
+ if err2 != nil {
+ return "", typ, err2
+ }
+ switch typ {
+ case SZ, EXPAND_SZ:
+ default:
+ return "", typ, ErrUnexpectedType
+ }
+ if len(data) == 0 {
+ return "", typ, nil
+ }
+ u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
+ return syscall.UTF16ToString(u), typ, nil
+}
+
+// GetMUIStringValue retrieves the localized string value for
+// the specified value name associated with an open key k.
+// If the value name doesn't exist or the localized string value
+// can't be resolved, GetMUIStringValue returns ErrNotExist.
+// GetMUIStringValue panics if the system doesn't support
+// regLoadMUIString; use LoadRegLoadMUIString to check if
+// regLoadMUIString is supported before calling this function.
+func (k Key) GetMUIStringValue(name string) (string, error) {
+ pname, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return "", err
+ }
+
+ buf := make([]uint16, 1024)
+ var buflen uint32
+ var pdir *uint16
+
+ err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
+ if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path
+
+ // Try to resolve the string value using the system directory as
+ // a DLL search path; this assumes the string value is of the form
+ // @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.
+
+ // This approach works with tzres.dll but may have to be revised
+ // in the future to allow callers to provide custom search paths.
+
+ var s string
+ s, err = ExpandString("%SystemRoot%\\system32\\")
+ if err != nil {
+ return "", err
+ }
+ pdir, err = syscall.UTF16PtrFromString(s)
+ if err != nil {
+ return "", err
+ }
+
+ err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
+ }
+
+ for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
+ if buflen <= uint32(len(buf)) {
+ break // Buffer not growing, assume race; break
+ }
+ buf = make([]uint16, buflen)
+ err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
+ }
+
+ if err != nil {
+ return "", err
+ }
+
+ return syscall.UTF16ToString(buf), nil
+}
+
+// ExpandString expands environment-variable strings and replaces
+// them with the values defined for the current user.
+// Use ExpandString to expand EXPAND_SZ strings.
+func ExpandString(value string) (string, error) {
+ if value == "" {
+ return "", nil
+ }
+ p, err := syscall.UTF16PtrFromString(value)
+ if err != nil {
+ return "", err
+ }
+ r := make([]uint16, 100)
+ for {
+ n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
+ if err != nil {
+ return "", err
+ }
+ if n <= uint32(len(r)) {
+ return syscall.UTF16ToString(r[:n]), nil
+ }
+ r = make([]uint16, n)
+ }
+}
+
+// GetStringsValue retrieves the []string value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetStringsValue returns ErrNotExist.
+// If value is not MULTI_SZ, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 64))
+ if err2 != nil {
+ return nil, typ, err2
+ }
+ if typ != MULTI_SZ {
+ return nil, typ, ErrUnexpectedType
+ }
+ if len(data) == 0 {
+ return nil, typ, nil
+ }
+ p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
+ if len(p) == 0 {
+ return nil, typ, nil
+ }
+ if p[len(p)-1] == 0 {
+ p = p[:len(p)-1] // remove terminating null
+ }
+ val = make([]string, 0, 5)
+ from := 0
+ for i, c := range p {
+ if c == 0 {
+ val = append(val, string(utf16.Decode(p[from:i])))
+ from = i + 1
+ }
+ }
+ return val, typ, nil
+}
+
+// GetIntegerValue retrieves the integer value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetIntegerValue returns ErrNotExist.
+// If value is not DWORD or QWORD, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 8))
+ if err2 != nil {
+ return 0, typ, err2
+ }
+ switch typ {
+ case DWORD:
+ if len(data) != 4 {
+ return 0, typ, errors.New("DWORD value is not 4 bytes long")
+ }
+ var val32 uint32
+ copy((*[4]byte)(unsafe.Pointer(&val32))[:], data)
+ return uint64(val32), DWORD, nil
+ case QWORD:
+ if len(data) != 8 {
+ return 0, typ, errors.New("QWORD value is not 8 bytes long")
+ }
+ copy((*[8]byte)(unsafe.Pointer(&val))[:], data)
+ return val, QWORD, nil
+ default:
+ return 0, typ, ErrUnexpectedType
+ }
+}
+
+// GetBinaryValue retrieves the binary value for the specified
+// value name associated with an open key k. It also returns the value's type.
+// If value does not exist, GetBinaryValue returns ErrNotExist.
+// If value is not BINARY, it will return the correct value
+// type and ErrUnexpectedType.
+func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
+ data, typ, err2 := k.getValue(name, make([]byte, 64))
+ if err2 != nil {
+ return nil, typ, err2
+ }
+ if typ != BINARY {
+ return nil, typ, ErrUnexpectedType
+ }
+ return data, typ, nil
+}
+
+func (k Key) setValue(name string, valtype uint32, data []byte) error {
+ p, err := syscall.UTF16PtrFromString(name)
+ if err != nil {
+ return err
+ }
+ if len(data) == 0 {
+ return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
+ }
+ return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
+}
+
+// SetDWordValue sets the data and type of a name value
+// under key k to value and DWORD.
+func (k Key) SetDWordValue(name string, value uint32) error {
+ return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
+}
+
+// SetQWordValue sets the data and type of a name value
+// under key k to value and QWORD.
+func (k Key) SetQWordValue(name string, value uint64) error {
+ return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
+}
+
+func (k Key) setStringValue(name string, valtype uint32, value string) error {
+ v, err := syscall.UTF16FromString(value)
+ if err != nil {
+ return err
+ }
+ buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
+ return k.setValue(name, valtype, buf)
+}
+
+// SetStringValue sets the data and type of a name value
+// under key k to value and SZ. The value must not contain a zero byte.
+func (k Key) SetStringValue(name, value string) error {
+ return k.setStringValue(name, SZ, value)
+}
+
+// SetExpandStringValue sets the data and type of a name value
+// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
+func (k Key) SetExpandStringValue(name, value string) error {
+ return k.setStringValue(name, EXPAND_SZ, value)
+}
+
+// SetStringsValue sets the data and type of a name value
+// under key k to value and MULTI_SZ. The value strings
+// must not contain a zero byte.
+func (k Key) SetStringsValue(name string, value []string) error {
+ ss := ""
+ for _, s := range value {
+ for i := 0; i < len(s); i++ {
+ if s[i] == 0 {
+ return errors.New("string cannot have 0 inside")
+ }
+ }
+ ss += s + "\x00"
+ }
+ v := utf16.Encode([]rune(ss + "\x00"))
+ buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
+ return k.setValue(name, MULTI_SZ, buf)
+}
+
+// SetBinaryValue sets the data and type of a name value
+// under key k to value and BINARY.
+func (k Key) SetBinaryValue(name string, value []byte) error {
+ return k.setValue(name, BINARY, value)
+}
+
+// DeleteValue removes a named value from the key k.
+func (k Key) DeleteValue(name string) error {
+ return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
+}
+
+// ReadValueNames returns the value names of key k.
+// The parameter n controls the number of returned names,
+// analogous to the way os.File.Readdirnames works.
+func (k Key) ReadValueNames(n int) ([]string, error) {
+ ki, err := k.Stat()
+ if err != nil {
+ return nil, err
+ }
+ names := make([]string, 0, ki.ValueCount)
+ buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
+loopItems:
+ for i := uint32(0); ; i++ {
+ if n > 0 {
+ if len(names) == n {
+ return names, nil
+ }
+ }
+ l := uint32(len(buf))
+ for {
+ err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
+ if err == nil {
+ break
+ }
+ if err == syscall.ERROR_MORE_DATA {
+ // Double buffer size and try again.
+ l = uint32(2 * len(buf))
+ buf = make([]uint16, l)
+ continue
+ }
+ if err == _ERROR_NO_MORE_ITEMS {
+ break loopItems
+ }
+ return names, err
+ }
+ names = append(names, syscall.UTF16ToString(buf[:l]))
+ }
+ if n > len(names) {
+ return names, io.EOF
+ }
+ return names, nil
+}
diff --git a/vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go
new file mode 100644
index 000000000..fc1835d8a
--- /dev/null
+++ b/vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go
@@ -0,0 +1,117 @@
+// Code generated by 'go generate'; DO NOT EDIT.
+
+package registry
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var _ unsafe.Pointer
+
+// Do the interface allocations only once for common
+// Errno values.
+const (
+ errnoERROR_IO_PENDING = 997
+)
+
+var (
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+ errERROR_EINVAL error = syscall.EINVAL
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return errERROR_EINVAL
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ // TODO: add more here, after collecting data on the common
+ // error values see on Windows. (perhaps when running
+ // all.bat?)
+ return e
+}
+
+var (
+ modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
+ modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
+
+ procRegConnectRegistryW = modadvapi32.NewProc("RegConnectRegistryW")
+ procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
+ procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
+ procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW")
+ procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
+ procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW")
+ procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW")
+ procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
+)
+
+func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) {
+ r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result)))
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
+ r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)))
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
+ r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
+ r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
+ r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) {
+ r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0)
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
+ r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize))
+ if r0 != 0 {
+ regerrno = syscall.Errno(r0)
+ }
+ return
+}
+
+func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
+ r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
+ n = uint32(r0)
+ if n == 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 02c8315fa..68f9e5a99 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -46,10 +46,10 @@ github.com/blang/semver
github.com/buger/goterm
# github.com/cespare/xxhash/v2 v2.1.2
github.com/cespare/xxhash/v2
-# github.com/checkpoint-restore/checkpointctl v0.0.0-20210922093614-c31748bec9f2
+# github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681
## explicit
github.com/checkpoint-restore/checkpointctl/lib
-# github.com/checkpoint-restore/go-criu/v5 v5.2.0
+# github.com/checkpoint-restore/go-criu/v5 v5.3.0
## explicit
github.com/checkpoint-restore/go-criu/v5
github.com/checkpoint-restore/go-criu/v5/magic
@@ -106,7 +106,7 @@ github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/util
-# github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9
+# github.com/containers/common v0.46.1-0.20211209220542-24f363480347
## explicit
github.com/containers/common/libimage
github.com/containers/common/libimage/manifests
@@ -220,7 +220,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518
+# github.com/containers/storage v1.37.1-0.20211213220314-73a749e4fec5
## explicit
github.com/containers/storage
github.com/containers/storage/drivers
@@ -653,6 +653,7 @@ github.com/uber/jaeger-client-go/thrift-gen/jaeger
github.com/uber/jaeger-client-go/thrift-gen/zipkincore
github.com/uber/jaeger-client-go/utils
# github.com/ulikunitz/xz v0.5.10
+## explicit
github.com/ulikunitz/xz
github.com/ulikunitz/xz/internal/hash
github.com/ulikunitz/xz/internal/xlog
@@ -743,9 +744,11 @@ golang.org/x/sys/internal/unsafeheader
golang.org/x/sys/plan9
golang.org/x/sys/unix
golang.org/x/sys/windows
+golang.org/x/sys/windows/registry
# golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
golang.org/x/term
# golang.org/x/text v0.3.7
+## explicit
golang.org/x/text/encoding
golang.org/x/text/encoding/charmap
golang.org/x/text/encoding/htmlindex
@@ -817,6 +820,7 @@ google.golang.org/grpc/stats
google.golang.org/grpc/status
google.golang.org/grpc/tap
# google.golang.org/protobuf v1.27.1
+## explicit
google.golang.org/protobuf/encoding/prototext
google.golang.org/protobuf/encoding/protowire
google.golang.org/protobuf/internal/descfmt