diff options
85 files changed, 1698 insertions, 131 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 84b483b57..5a9dbcb54 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -93,8 +93,8 @@ gating_task: timeout_in: 20m - networking_script: # Don't bother going further if something is down - - 'while read host port; do nc -zv -w 13 $host $port || exit 1; done < ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/required_host_ports.txt' + # Don't bother going further if something is down + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' gate_script: # N/B: entrypoint.sh resets $GOSRC (same as make clean) @@ -289,7 +289,7 @@ image_prune_task: - "meta" container: - image: "quay.io/cevich/imgprune:latest" # see contrib/imgprune + image: "quay.io/libpod/imgprune:latest" # see contrib/imgprune cpu: 1 memory: 1 @@ -331,6 +331,7 @@ testing_task: TEST_REMOTE_CLIENT: true TEST_REMOTE_CLIENT: false + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' unit_test_script: '$SCRIPT_BASE/unit_test.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' @@ -348,7 +349,7 @@ testing_task: audit_log_script: '$SCRIPT_BASE/logcollector.sh audit' journal_script: '$SCRIPT_BASE/logcollector.sh journal' -# Test crun on last Fedora +# Test crun only on latest Fedora testing_crun_task: depends_on: @@ -361,20 +362,15 @@ testing_crun_task: # Only test build cache-images, if that's what's requested only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' - gce_instance: - matrix: - # Images are generated separately, from build_images_task (below) - image_name: "${FEDORA_CACHE_IMAGE_NAME}" - timeout_in: 120m env: ADD_SECOND_PARTITION: true OCI_RUNTIME: "/usr/bin/crun" - matrix: - TEST_REMOTE_CLIENT: false + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' + install_crun_script: 'dnf install -y crun' unit_test_script: '$SCRIPT_BASE/unit_test.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}' @@ -385,11 +381,8 @@ testing_crun_task: on_failure: failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' - always: &crunstandardlogs - ginkgo_node_logs_script: '$SCRIPT_BASE/logcollector.sh ginkgo' - df_script: '$SCRIPT_BASE/logcollector.sh df' - audit_log_script: '$SCRIPT_BASE/logcollector.sh audit' - journal_script: '$SCRIPT_BASE/logcollector.sh journal' + always: + <<: *standardlogs # This task executes tests under unique environments/conditions @@ -413,6 +406,7 @@ special_testing_rootless_task: timeout_in: 60m + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}' @@ -441,6 +435,7 @@ special_testing_in_podman_task: timeout_in: 60m + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' @@ -467,6 +462,7 @@ special_testing_cross_task: timeout_in: 20m + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' cache_release_archive_script: '$SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP}' @@ -494,6 +490,7 @@ special_testing_cgroupv2_task: timeout_in: 20m + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' @@ -529,7 +526,8 @@ test_build_cache_images_task: - compute - devstorage.full_control - environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' + setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' build_vm_images_script: '$SCRIPT_BASE/build_vm_images.sh |& ${TIMESTAMP}' df_script: '${DFCMD}' journalctl_b_script: 'journalctl -b' @@ -568,6 +566,7 @@ verify_test_built_images_task: PACKER_BUILDER_NAME: "xfedora-30" PACKER_BUILDER_NAME: "ubuntu-18" + networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' # Verify expectations once per image check_image_script: >- @@ -1503,6 +1503,8 @@ publish [?[]string](#?[]string) publishAll [?bool](#?bool) +pull [?string](#?string) + quiet [?bool](#?bool) readonly [?bool](#?bool) @@ -2,7 +2,7 @@ export GO111MODULE=off GO ?= go DESTDIR ?= -EPOCH_TEST_COMMIT ?= bb80586e275fe0d3f47700ec54c9718a28b1e59c +EPOCH_TEST_COMMIT ?= b9a176bea94b8e3a97a70dd7cd599f1a057777b0 HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD @@ -20,6 +20,7 @@ SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers ETCDIR ?= /etc TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system +USERSYSTEMDDIR ?= ${PREFIX}/lib/systemd/user BUILDFLAGS ?= BUILDTAGS ?= \ $(shell hack/apparmor_tag.sh) \ @@ -335,6 +336,7 @@ brew-pkg: install-podman-remote-docs podman-remote-darwin @cp ./bin/podman-remote-darwin ./brew/podman @cp -r ./docs/remote ./brew/docs/ @cp docs/podman-remote.1 ./brew/docs/podman.1 + @cp docs/podman-remote.conf.5 ./brew/docs/podman-remote.conf.5 @sed -i 's/podman\\*-remote/podman/g' ./brew/docs/podman.1 @sed -i 's/Podman\\*-remote/Podman\ for\ Mac/g' ./brew/docs/podman.1 @sed -i 's/podman\.conf/podman\-remote\.conf/g' ./brew/docs/podman.1 @@ -395,9 +397,11 @@ install.docker: docker-docs install ${SELINUXOPT} -m 644 docs/docker*.1 -t $(DESTDIR)$(MANDIR)/man1 install.systemd: - install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${TMPFILESDIR} + install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} ${DESTDIR}${TMPFILESDIR} install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${SYSTEMDDIR}/io.podman.socket + install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${USERSYSTEMDDIR}/io.podman.socket install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.service ${DESTDIR}${SYSTEMDDIR}/io.podman.service + install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.service ${DESTDIR}${USERSYSTEMDDIR}/io.podman.service install ${SELINUXOPT} -m 644 contrib/varlink/podman.conf ${DESTDIR}${TMPFILESDIR}/podman.conf uninstall: diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f55fd9b18..3cfd8ed86 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,23 @@ # Release Notes +## 1.5.1 +### Features +- The hostname of pods is now set to the pod's name + +### Bugfixes +- Fixed a bug where `podman run` and `podman create` did not honor the `--authfile` option ([#3730](https://github.com/containers/libpod/issues/3730)) +- Fixed a bug where containers restored with `podman container restore --import` would incorrectly duplicate the Conmon PID file of the original container +- Fixed a bug where `podman build` ignored the default OCI runtime configured in `libpod.conf` +- Fixed a bug where `podman run --rm` (or force-removing any running container with `podman rm --force`) were not retrieving the correct exit code ([#3795](https://github.com/containers/libpod/issues/3795)) +- Fixed a bug where Podman would exit with an error if any configured hooks directory was not present +- Fixed a bug where `podman inspect` and `podman commit` would not use the correct `CMD` for containers run with `podman play kube` +- Fixed a bug created pods when using rootless Podman and CGroups V2 ([#3801](https://github.com/containers/libpod/issues/3801)) +- Fixed a bug where the `podman events` command with the `--since` or `--until` options could take a very long time to complete + +### Misc +- Rootless Podman will now inherit OCI runtime configuration from the root configuration ([#3781](https://github.com/containers/libpod/issues/3781)) +- Podman now properly sets a user agent while contacting registries ([#3788](https://github.com/containers/libpod/issues/3788)) + ## 1.5.0 ### Features - Podman containers can now join the user namespaces of other containers with `--userns=container:$ID`, or a user namespace at an arbitary path with `--userns=ns:$PATH` diff --git a/changelog.txt b/changelog.txt index beea8dd5c..b0a847aee 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,51 @@ +- Changelog for v1.5.1 (2019-08-15) + * Add release notes for v1.5.1 + * Set Pod hostname as Pod name + * tests for exit status on podman run --rm + * performance fix for podman events with large journalds + * pkg/cgroups: use DBUS session when rootless + * Fix play kube command in pod yaml + * removMergeDir from inspect result if not mounted + * Running Podman with a nonexistent hooks dir is nonfatal + * Cirrus: Install varlink on Ubuntu + * Cirrus: Install varlink on Fedora + * Add missing stage-packages in snapcraft.yaml. + * Add RHEL and SUSE to snap doc + * start groundwork for adding snap + * Add user systemd service and socket + * Small optimization - only store exit code when nonzero + * Fix container exit code with Journald backend + * Revert "Cirrus: Temp. workaround missing imgprune image" + * Homebrew installation in install.md + * varlink endpoint for containerstats requires root + * Adjust get_ci_vm.sh for substitution + * Cirrus: Add verification for cgroupv2 image + * Cirrus: Add experimental fedora VM image & test + * image: add user agent to Docker registry options + * Cirrus: Minor, use newer Ubuntu base image + * tests: disable some tests currently failing when not using runc + * containers: look also for 'file not found' in the error message + * cirrus: add tests with crun on Fedora 30 + * rootless: cherry-pick runtime from the system configuration + * cirrus: install crun + * cmd: drop check for euid==0 + * storage: drop unused geteuid check + * cmd, stats: fix check for rootless mode + * oci: drop check for euid==0 + * build: use the configured runtime + * Adjust read count so that a newline can be added afterwards + * Fix incorrect use of realloc() + * Bump gitvalidation epoch + * Bump to v1.5.1-dev + * Fix a couple of errors descovered by coverity + * Test that restored container does not depend on the original container + * Fix up ConmonPidFile after restore + * Cirrus: Enable updates-testing repo for Fedora + * enable windows remote client + * implement 'make remotesystem' + * Squish a few tpyo nits in container.go doc + * Cirrus: Add Second partition for storage testing + - Changelog for v1.5.0 (2019-08-09) * vendor github.com/containers/storage@v1.13.2 * Improve dns-search validation, empty domains now return an error diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index d5098ee51..e7ad921da 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -258,6 +258,20 @@ type MountValues struct { Latest bool } +type NetworkListValues struct { + PodmanCommand + Filter []string + Quiet bool +} + +type NetworkRmValues struct { + PodmanCommand +} + +type NetworkInspectValues struct { + PodmanCommand +} + type PauseValues struct { PodmanCommand All bool @@ -286,6 +300,7 @@ type PodCreateValues struct { LabelFile []string Labels []string Name string + Hostname string PodIDFile string Publish []string Share string diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 1e9092bd6..32478bb51 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -388,6 +388,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "publish-all", "P", false, "Publish all exposed ports to random ports on the host interface", ) + createFlags.String( + "pull", "missing", + `Pull image before creating ("always"|"missing"|"never") (default "missing")`, + ) createFlags.BoolP( "quiet", "q", false, "Suppress output information when pulling images", diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index ee9e57966..a133549ea 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -15,25 +15,30 @@ import ( // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers func GetRuntimeMigrate(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, true, false) + return getRuntime(ctx, c, false, true, false, true) +} + +// GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify +func GetRuntimeDisableFDs(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { + return getRuntime(ctx, c, false, false, false, false) } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber func GetRuntimeRenumber(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, true, false, false) + return getRuntime(ctx, c, true, false, false, true) } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, false) + return getRuntime(ctx, c, false, false, false, true) } // GetRuntimeNoStore generates a new libpod runtime configured by command line options func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, true) + return getRuntime(ctx, c, false, false, true, true) } -func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore bool) (*libpod.Runtime, error) { +func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore, withFDS bool) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false @@ -165,6 +170,10 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migra infraCommand, _ := c.Flags().GetString("infra-command") options = append(options, libpod.WithDefaultInfraCommand(infraCommand)) } + + if withFDS { + options = append(options, libpod.WithEnableSDNotify()) + } if c.Flags().Changed("config") { return libpod.NewRuntimeFromConfig(ctx, c.GlobalFlags.Config, options...) } diff --git a/cmd/podman/network.go b/cmd/podman/network.go new file mode 100644 index 000000000..83a5e71ab --- /dev/null +++ b/cmd/podman/network.go @@ -0,0 +1,31 @@ +//+build !remoteclient + +package main + +import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/spf13/cobra" +) + +var networkcheckDescription = "Manage networks" +var networkcheckCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "network", + Short: "Manage Networks", + Long: networkcheckDescription, + RunE: commandRunE(), + }, +} + +// Commands that are universally implemented +var networkcheckCommands = []*cobra.Command{ + _networkinspectCommand, + _networklistCommand, + _networkrmCommand, +} + +func init() { + networkcheckCommand.AddCommand(networkcheckCommands...) + networkcheckCommand.SetUsageTemplate(UsageTemplate()) + rootCmd.AddCommand(networkcheckCommand.Command) +} diff --git a/cmd/podman/network_inspect.go b/cmd/podman/network_inspect.go new file mode 100644 index 000000000..38aaf6ba4 --- /dev/null +++ b/cmd/podman/network_inspect.go @@ -0,0 +1,48 @@ +// +build !remoteclient + +package main + +import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + networkinspectCommand cliconfig.NetworkInspectValues + networkinspectDescription = `Inspect network` + _networkinspectCommand = &cobra.Command{ + Use: "inspect NETWORK [NETWORK...] [flags] ", + Short: "network inspect", + Long: networkinspectDescription, + RunE: func(cmd *cobra.Command, args []string) error { + networkinspectCommand.InputArgs = args + networkinspectCommand.GlobalFlags = MainGlobalOpts + networkinspectCommand.Remote = remoteclient + return networkinspectCmd(&networkinspectCommand) + }, + Example: `podman network inspect podman`, + } +) + +func init() { + networkinspectCommand.Command = _networkinspectCommand + networkinspectCommand.SetHelpTemplate(HelpTemplate()) + networkinspectCommand.SetUsageTemplate(UsageTemplate()) +} + +func networkinspectCmd(c *cliconfig.NetworkInspectValues) error { + if rootless.IsRootless() && !remoteclient { + return errors.New("network inspect is not supported for rootless mode") + } + if len(c.InputArgs) < 1 { + return errors.Errorf("at least one network name is required") + } + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) + if err != nil { + return err + } + return runtime.NetworkInspect(c) +} diff --git a/cmd/podman/network_list.go b/cmd/podman/network_list.go new file mode 100644 index 000000000..16edf743b --- /dev/null +++ b/cmd/podman/network_list.go @@ -0,0 +1,53 @@ +// +build !remoteclient + +package main + +import ( + "errors" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" + "github.com/spf13/cobra" +) + +var ( + networklistCommand cliconfig.NetworkListValues + networklistDescription = `List networks` + _networklistCommand = &cobra.Command{ + Use: "ls", + Args: noSubArgs, + Short: "network list", + Long: networklistDescription, + RunE: func(cmd *cobra.Command, args []string) error { + networklistCommand.InputArgs = args + networklistCommand.GlobalFlags = MainGlobalOpts + networklistCommand.Remote = remoteclient + return networklistCmd(&networklistCommand) + }, + Example: `podman network list`, + } +) + +func init() { + networklistCommand.Command = _networklistCommand + networklistCommand.SetHelpTemplate(HelpTemplate()) + networklistCommand.SetUsageTemplate(UsageTemplate()) + flags := networklistCommand.Flags() + // TODO enable filters based on something + //flags.StringSliceVarP(&networklistCommand.Filter, "filter", "f", []string{}, "Pause all running containers") + flags.BoolVarP(&networklistCommand.Quiet, "quiet", "q", false, "display only names") +} + +func networklistCmd(c *cliconfig.NetworkListValues) error { + if rootless.IsRootless() && !remoteclient { + return errors.New("network list is not supported for rootless mode") + } + if len(c.InputArgs) > 0 { + return errors.New("network list takes no arguments") + } + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) + if err != nil { + return err + } + return runtime.NetworkList(c) +} diff --git a/cmd/podman/network_rm.go b/cmd/podman/network_rm.go new file mode 100644 index 000000000..50bd48cea --- /dev/null +++ b/cmd/podman/network_rm.go @@ -0,0 +1,48 @@ +// +build !remoteclient + +package main + +import ( + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + networkrmCommand cliconfig.NetworkRmValues + networkrmDescription = `Remove networks` + _networkrmCommand = &cobra.Command{ + Use: "rm [flags] NETWORK [NETWORK...]", + Short: "network rm", + Long: networkrmDescription, + RunE: func(cmd *cobra.Command, args []string) error { + networkrmCommand.InputArgs = args + networkrmCommand.GlobalFlags = MainGlobalOpts + networkrmCommand.Remote = remoteclient + return networkrmCmd(&networkrmCommand) + }, + Example: `podman network rm podman`, + } +) + +func init() { + networkrmCommand.Command = _networkrmCommand + networkrmCommand.SetHelpTemplate(HelpTemplate()) + networkrmCommand.SetUsageTemplate(UsageTemplate()) +} + +func networkrmCmd(c *cliconfig.NetworkRmValues) error { + if rootless.IsRootless() && !remoteclient { + return errors.New("network rm is not supported for rootless mode") + } + if len(c.InputArgs) < 1 { + return errors.Errorf("at least one network name is required") + } + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) + if err != nil { + return err + } + return runtime.NetworkRemove(c) +} diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index d04c85dba..ad3c00aa8 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -52,6 +52,7 @@ func init() { flags.StringSliceVar(&podCreateCommand.LabelFile, "label-file", []string{}, "Read in a line delimited file of labels") flags.StringSliceVarP(&podCreateCommand.Labels, "label", "l", []string{}, "Set metadata on pod (default [])") flags.StringVarP(&podCreateCommand.Name, "name", "n", "", "Assign a name to the pod") + flags.StringVarP(&podCreateCommand.Hostname, "hostname", "", "", "Set a hostname to the pod") flags.StringVar(&podCreateCommand.PodIDFile, "pod-id-file", "", "Write the pod ID to the file") flags.StringSliceVarP(&podCreateCommand.Publish, "publish", "p", []string{}, "Publish a container's port, or a range of ports, to the host (default [])") flags.StringVar(&podCreateCommand.Share, "share", shared.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 0eee51e79..53f133929 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -150,7 +150,7 @@ func pullCmd(c *cliconfig.PullValues) (retError error) { // See https://bugzilla.redhat.com/show_bug.cgi?id=1701922 for background // information. if !c.Bool("all-tags") { - newImage, err := runtime.New(getContext(), imgArg, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, true, nil) + newImage, err := runtime.New(getContext(), imgArg, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways) if err != nil { return errors.Wrapf(err, "error pulling image %q", imgArg) } @@ -188,7 +188,7 @@ func pullCmd(c *cliconfig.PullValues) (retError error) { var foundIDs []string foundImage := true for _, name := range names { - newImage, err := runtime.New(getContext(), name, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, true, nil) + newImage, err := runtime.New(getContext(), name, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways) if err != nil { logrus.Errorf("error pulling image %q", name) foundImage = false diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index 1d35ac17b..5122d37d1 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -732,7 +732,7 @@ func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtim registryCreds = creds } dockerRegistryOptions.DockerRegistryCreds = registryCreds - newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, false, &label) + newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, &label, util.PullImageMissing) } else { newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage) } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index e29e6b28e..094330e24 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -83,7 +83,13 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. } else { return nil, nil, errors.Errorf("error, no input arguments were provided") } - newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, GetAuthFile(c.String("authfile")), writer, nil, image.SigningOptions{}, false, nil) + + pullType, err := util.ValidatePullType(c.String("pull")) + if err != nil { + return nil, nil, err + } + + newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, GetAuthFile(c.String("authfile")), writer, nil, image.SigningOptions{}, nil, pullType) if err != nil { return nil, nil, err } diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index 3479876b4..c6c32f8a9 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -436,6 +436,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["privileged"] = newCRBool(c, "privileged") m["publish"] = newCRStringSlice(c, "publish") m["publish-all"] = newCRBool(c, "publish-all") + m["pull"] = newCRString(c, "pull") m["quiet"] = newCRBool(c, "quiet") m["read-only"] = newCRBool(c, "read-only") m["read-only-tmpfs"] = newCRBool(c, "read-only-tmpfs") diff --git a/cmd/podman/shared/intermediate_varlink.go b/cmd/podman/shared/intermediate_varlink.go index 4742d4909..9dbf83950 100644 --- a/cmd/podman/shared/intermediate_varlink.go +++ b/cmd/podman/shared/intermediate_varlink.go @@ -137,6 +137,7 @@ func (g GenericCLIResults) MakeVarlink() iopodman.Create { Privileged: BoolToPtr(g.Find("privileged")), Publish: StringSliceToPtr(g.Find("publish")), PublishAll: BoolToPtr(g.Find("publish-all")), + Pull: StringToPtr(g.Find("pull")), Quiet: BoolToPtr(g.Find("quiet")), Readonly: BoolToPtr(g.Find("read-only")), Readonlytmpfs: BoolToPtr(g.Find("read-only-tmpfs")), @@ -393,6 +394,7 @@ func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults { m["privileged"] = boolFromVarlink(opts.Privileged, "privileged", false) m["publish"] = stringSliceFromVarlink(opts.Publish, "publish", nil) m["publish-all"] = boolFromVarlink(opts.PublishAll, "publish-all", false) + m["pull"] = stringFromVarlink(opts.Pull, "missing", nil) m["quiet"] = boolFromVarlink(opts.Quiet, "quiet", false) m["read-only"] = boolFromVarlink(opts.Readonly, "read-only", false) m["read-only-tmpfs"] = boolFromVarlink(opts.Readonlytmpfs, "read-only-tmpfs", true) diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go index 1333cf441..de289047a 100644 --- a/cmd/podman/sign.go +++ b/cmd/podman/sign.go @@ -15,6 +15,7 @@ import ( "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/trust" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -113,7 +114,7 @@ func signCmd(c *cliconfig.SignValues) error { if err != nil { return err } - newImage, err := runtime.ImageRuntime().New(getContext(), signimage, rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{SignBy: signby}, false, nil) + newImage, err := runtime.ImageRuntime().New(getContext(), signimage, rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{SignBy: signby}, nil, util.PullImageMissing) if err != nil { return errors.Wrapf(err, "error pulling image %s", signimage) } diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 92315cd6b..5f89534be 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -79,7 +79,7 @@ func varlinkCmd(c *cliconfig.VarlinkValues) error { timeout := time.Duration(c.Timeout) * time.Millisecond // Create a single runtime for varlink - runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := libpodruntime.GetRuntimeDisableFDs(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 4a4c97e99..2e7dee94d 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -347,6 +347,7 @@ type Create ( privileged: ?bool, publish: ?[]string, publishAll: ?bool, + pull: ?string, quiet: ?bool, readonly: ?bool, readonlytmpfs: ?bool, diff --git a/commands.md b/commands.md index 1b48d7862..4d3bea439 100644 --- a/commands.md +++ b/commands.md @@ -44,6 +44,10 @@ | [podman-logout(1)](/docs/podman-logout.1.md) | Logout of a container registry | | [podman-logs(1)](/docs/podman-logs.1.md) | Display the logs of a container | | [podman-mount(1)](/docs/podman-mount.1.md) | Mount a working container's root filesystem | +| [podman-network(1)](/docs/podman-network.1.md) | Manage Podman CNI networks | +| [podman-network-inspect(1)](/docs/podman-network-inspect.1.md) | Inspect one or more Podman networks | +| [podman-network-ls(1)](/docs/podman-network-ls.1.md) | Display a summary of Podman networks | +| [podman-network-rm(1)](/docs/podman-network-rm.1.md) | Remove one or more Podman networks | | [podman-pause(1)](/docs/podman-pause.1.md) | Pause one or more running containers | [![...](/docs/play.png)](https://podman.io/asciinema/podman/pause_unpause/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_pause_unpause.sh) | | [podman-play(1)](/docs/podman-play.1.md) | Play pods and containers based on a structured input file | | [podman-pod(1)](/docs/podman-pod.1.md) | Simple management tool for groups of containers, called pods | diff --git a/completions/bash/podman b/completions/bash/podman index d2eb5b570..962c15a95 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -946,6 +946,78 @@ _podman_healthcheck() { esac } +_podman_network() { + local boolean_options=" + --help + -h + " + subcommands=" + inspect + ls + rm + " + __podman_subcommands "$subcommands $aliases" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_podman_network_inspect() { + local options_with_args=" + " + local boolean_options=" + --help + -h + " + _complete_ "$options_with_args" "$boolean_options" + + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) + ;; + esac +} + +_podman_network_ls() { + local options_with_args=" + " + local boolean_options=" + --help + -h + --quiet + -q + " + _complete_ "$options_with_args" "$boolean_options" + + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) + ;; + esac +} + +_podman_network_ls() { + local options_with_args=" + " + local boolean_options=" + --help + -h + " + _complete_ "$options_with_args" "$boolean_options" + + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) + ;; + esac +} + _podman_generate() { local boolean_options=" --help @@ -1773,6 +1845,7 @@ _podman_container_run() { --pids-limit --pod --publish -p + --pull --runtime --rootfs --security-opt diff --git a/contrib/cirrus/check_image.sh b/contrib/cirrus/check_image.sh index c8e8c4c63..ad9a12f49 100755 --- a/contrib/cirrus/check_image.sh +++ b/contrib/cirrus/check_image.sh @@ -22,6 +22,9 @@ item_test 'Minimum available memory' $MEM_FREE -ge $MIN_MEM_MB || let "NFAILS+=1 # binary anywhere; that could potentially taint our results. item_test "remove_packaged_podman_files() did it's job" -z "$(type -P podman)" || let "NFAILS+=1" +# Integration Tests require varlink in Fedora +item_test "The varlink executable is present" -x "$(type -P varlink)" || let "NFAILS+=1" + MIN_ZIP_VER='3.0' VER_RE='.+([[:digit:]]+\.[[:digit:]]+).+' ACTUAL_VER=$(zip --version 2>&1 | egrep -m 1 "Zip$VER_RE" | sed -r -e "s/$VER_RE/\\1/") diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh index 8a43176e4..e5de518fa 100755 --- a/contrib/cirrus/integration_test.sh +++ b/contrib/cirrus/integration_test.sh @@ -37,11 +37,16 @@ case "$SPECIALMODE" in -o CheckHostIP=no $GOSRC/$SCRIPT_BASE/rootless_test.sh ${TESTSUITE} ;; cgroupv2) + # FIXME: use the package once all the fixes are in a release + # yum install -y crun + setenforce 0 + yum builddep -y crun + (git clone --depth=1 https://github.com/containers/crun && cd crun && ./autogen.sh && ./configure --prefix=/usr && make -j4 && make install) + export OCI_RUNTIME=/usr/bin/crun make make install PREFIX=/usr ETCDIR=/etc make test-binaries - echo "WARNING: Integration tests not yet ready for cgroups V2" - #TODO: make local${TESTSUITE} + make local${TESTSUITE} ;; none) make diff --git a/contrib/cirrus/networking.sh b/contrib/cirrus/networking.sh new file mode 100755 index 000000000..aeaf74035 --- /dev/null +++ b/contrib/cirrus/networking.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# This script attempts basic confirmation of functional networking +# by connecting to a set of essential external servers and failing +# if any cannot be reached. + +source $(dirname $0)/lib.sh + +while read host port +do + if [[ "$port" -eq "443" ]] + then + item_test "SSL/TLS to $host:$port" "$(echo -n '' | openssl s_client -quiet -no_ign_eof -connect $host:$port &> /dev/null; echo $?)" -eq "0" + else + item_test "Connect to $host:$port" "$(nc -zv -w 13 $host $port &> /dev/null; echo $?)" -eq 0 + fi +done < ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/required_host_ports.txt diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index f73df4182..0e1a82cc0 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -53,6 +53,7 @@ ooe.sh sudo dnf install -y \ libseccomp \ libseccomp-devel \ libselinux-devel \ + libvarlink-util \ lsof \ make \ nmap-ncat \ diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index 4b50d6dc3..00d92570f 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -65,6 +65,7 @@ $BIGTO $SUDOAPTGET install \ libnet1-dev \ libnl-3-dev \ libostree-dev \ + libvarlink \ libprotobuf-c0-dev \ libprotobuf-dev \ libseccomp-dev \ diff --git a/contrib/cirrus/required_host_ports.txt b/contrib/cirrus/required_host_ports.txt index 9248e497a..85a6c26be 100644 --- a/contrib/cirrus/required_host_ports.txt +++ b/contrib/cirrus/required_host_ports.txt @@ -2,3 +2,14 @@ github.com 22 docker.io 443 quay.io 443 registry.fedoraproject.org 443 +mirrors.fedoraproject.org 443 +dl.fedoraproject.org 443 +ewr.edge.kernel.org 443 +mirror.chpc.utah.edu 443 +mirror.clarkson.edu 443 +mirror.umd.edu 443 +mirror.vcu.edu 443 +mirrors.cat.pdx.edu 443 +pubmirror1.math.uh.edu 443 +pubmirror2.math.uh.edu 443 +sjc.edge.kernel.org 443 diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 03acaf1da..7b6765f8a 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -44,11 +44,6 @@ case "${OS_REL_VER}" in ;; fedora-30) ;& # continue to next item fedora-29) - # There is no crun package on Fedora29 - if test "${OS_REL_VER}" != "fedora-29"; then - yum install -y crun - fi - if [[ "$ADD_SECOND_PARTITION" == "true" ]]; then bash "$SCRIPT_BASE/add_second_partition.sh"; fi ;; diff --git a/contrib/imgprune/Dockerfile b/contrib/imgprune/Dockerfile index 26329e828..b0dc77da5 100644 --- a/contrib/imgprune/Dockerfile +++ b/contrib/imgprune/Dockerfile @@ -1,4 +1,4 @@ -FROM libpod/imgts:latest +FROM quay.io/libpod/imgts:latest RUN yum -y update && \ yum clean all diff --git a/contrib/perftest/main.go b/contrib/perftest/main.go index 237f4f6e6..f6c90914a 100644 --- a/contrib/perftest/main.go +++ b/contrib/perftest/main.go @@ -103,7 +103,7 @@ func main() { } fmt.Printf("image %s not found locally, fetching from remote registry..\n", *testImageName) - testImage, err = client.ImageRuntime().New(ctx, *testImageName, "", "", writer, &dockerRegistryOptions, image2.SigningOptions{}, false, nil) + testImage, err = client.ImageRuntime().New(ctx, *testImageName, "", "", writer, &dockerRegistryOptions, image2.SigningOptions{}, nil, util.PullImageMissing) if err != nil { logrus.Fatal(err) } diff --git a/contrib/snapcraft/.editorconfig b/contrib/snapcraft/.editorconfig new file mode 100644 index 000000000..1749b2d4c --- /dev/null +++ b/contrib/snapcraft/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false + +[*.yaml] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true
\ No newline at end of file diff --git a/contrib/snapcraft/.gitignore b/contrib/snapcraft/.gitignore new file mode 100644 index 000000000..59894f50f --- /dev/null +++ b/contrib/snapcraft/.gitignore @@ -0,0 +1,12 @@ +#snapcraft specifics +/parts/ +/stage/ +/prime/ + +*.snap + +.snapcraft +__pycache__ +*.pyc +*_source.tar.bz2 +snap/.snapcraft diff --git a/contrib/snapcraft/LICENSE b/contrib/snapcraft/LICENSE new file mode 100644 index 000000000..81802d619 --- /dev/null +++ b/contrib/snapcraft/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Snapcrafters + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contrib/snapcraft/README.md b/contrib/snapcraft/README.md new file mode 100644 index 000000000..f543c61a7 --- /dev/null +++ b/contrib/snapcraft/README.md @@ -0,0 +1,82 @@ +<h1 align="center"> + <img src="https://raw.githubusercontent.com/containers/podman.io/master/images/podman.svg?sanitize=true" alt="podman"> + <br /> +</h1> + +<p align="center"><b>This is the snap for <a href="https://podman.io/">podman</a></b>, <i>“Managing Pods, Containers, and Container Images in Multiple Formats (Docker, OCI, ...).”</i>. It works on Ubuntu, Fedora, Debian, RHEL, SUSE, and other major Linux +distributions.</p> + +<!-- Uncomment and modify this when you are provided a build status badge +<p align="center"> +<a href="https://build.snapcraft.io/user/snapcrafters/fork-and-rename-me"><img src="https://build.snapcraft.io/badge/snapcrafters/fork-and-rename-me.svg" alt="Snap Status"></a> +</p> +--> + +<!-- Uncomment and modify this when you have a screenshot +![podman](screenshot.png?raw=true "podman") +--> + +<p align="center">Published for <img src="https://raw.githubusercontent.com/anythingcodes/slack-emoji-for-techies/gh-pages/emoji/tux.png" align="top" width="24" /> with 💝 by Snapcrafters</p> + +## Install + + sudo snap install podman + +([Don't have snapd installed?](https://snapcraft.io/docs/core/install)) + +## Remaining tasks +<!-- Uncomment and modify this when you have a screenshot +![podman](screenshot.png?raw=true "podman") +--> + +Snapcrafters ([join us](https://forum.snapcraft.io/t/join-snapcrafters/1325)) +are working to land snap install documentation and +the [snapcraft.yaml](https://github.com/snapcrafters/podman/blob/master/snap/snapcraft.yaml) +upstream so `podman` can authoritatively publish future releases. + + - [x] Fork the [Snapcrafters template](https://github.com/snapcrafters/fork-and-rename-me) repository to your own GitHub account. + - If you have already forked the Snapcrafter template to your account and want to create another snap, you'll need to use GitHub's [Import repository](https://github.com/new/import) feature because you can only fork a repository once. + - [x] Rename the forked Snapcrafters template repository + - [x] Update the description of the repository + - [x] Update logos and references to `podman` and `[podman]` + - [ ] Create a snap that runs in `devmode` + - [x] Register the snap in the store, **using the preferred upstream name** + - [ ] Add a screenshot to this `README.md` + - [ ] Publish the `devmode` snap in the Snap store edge channel + - [ ] Add install instructions to this `README.md` + - [ ] Update snap store metadata, icons and screenshots + - [ ] Convert the snap to `strict` confinement, or `classic` confinement if it qualifies + - [ ] Publish the confined snap in the Snap store beta channel + - [ ] Update the install instructions in this `README.md` + - [ ] Post a call for testing on the [Snapcraft Forum](https://forum.snapcraft.io) - [link]() + - [ ] Make a post in the [Snapcraft Forum](https://forum.snapcraft.io) asking for a transfer of the snap name from you to snapcrafters - [link]() + - [ ] Ask a [Snapcrafters admin](https://github.com/orgs/snapcrafters/people?query=%20role%3Aowner) to fork your repo into github.com/snapcrafters, and configure the repo for automatic publishing into edge on commit + - [ ] Add the provided Snapcraft build badge to this `README.md` + - [ ] Publish the snap in the Snap store stable channel + - [ ] Update the install instructions in this `README.md` + - [ ] Post an announcement in the [Snapcraft Forum](https://forum.snapcraft.io) - [link]() + - [ ] Submit a pull request or patch upstream that adds snap install documentation - [link]() + - [ ] Submit a pull request or patch upstream that adds the `snapcraft.yaml` and any required assets/launchers - [link]() + - [ ] Add upstream contact information to the `README.md` + - If upstream accept the PR: + - [ ] Request upstream create a Snap store account + - [ ] Contact the Snap Advocacy team to request the snap be transferred to upstream + - [ ] Ask the Snap Advocacy team to celebrate the snap - [link]() + +If you have any questions, [post in the Snapcraft forum](https://forum.snapcraft.io). + +<!-- +## The Snapcrafters + +| [![Your Name](https://gravatar.com/avatar/bc0bced65e963eb5c3a16cab8b004431/?s=128)](https://github.com/yourname/) | +| :---: | +| [Your Name](https://github.com/yourname/) | +--> + +<!-- Uncomment and modify this when you have upstream contacts +## Upstream + +| [![Upstream Name](https://gravatar.com/avatar/bc0bced65e963eb5c3a16cab8b004431?s=128)](https://github.com/upstreamname) | +| :---: | +| [Upstream Name](https://github.com/upstreamname) | +--> diff --git a/contrib/snapcraft/snap/snapcraft.yaml b/contrib/snapcraft/snap/snapcraft.yaml new file mode 100644 index 000000000..7ff0df03b --- /dev/null +++ b/contrib/snapcraft/snap/snapcraft.yaml @@ -0,0 +1,45 @@ +name: podman # you probably want to 'snapcraft register <name>' +version: '0.11.1.1' # just for humans, typically '1.2+git' or '1.3.2' +summary: Manage pods, containers and container images +description: | + `podman` is a tool for managing Pods, Containers, and Container Images + in multiple formats including Docker/OSI images. It exposes the same + command line interface as Docker, but runs containers unprivileged by + default. + +confinement: devmode # use 'strict' once you have the right plugs and slots + +base: core18 + +parts: + podman: + plugin: go + source: https://github.com/containers/libpod/archive/v0.11.1.1.tar.gz + go-importpath: github.com/containers/libpod + build-packages: + # https://github.com/containers/libpod/blob/master/install.md#build-and-run-dependencies + - btrfs-tools + - git + - golang-go + - go-md2man + - iptables + - libassuan-dev + - libdevmapper-dev + - libglib2.0-dev + - libc6-dev + - libgpgme11-dev + - libgpg-error-dev + - libostree-dev + - libprotobuf-dev + - libprotobuf-c0-dev + - libseccomp-dev + - libselinux1-dev + - pkg-config + stage-packages: + - libarchive13 + - libassuan0 + - libgpgme11 + - libicu60 + - libostree-1-1 + - libsoup2.4-1 + - libxml2 diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 0de797f2b..934f785db 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -39,7 +39,7 @@ %global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7}) Name: podman -Version: 1.5.1 +Version: 1.5.2 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 @@ -389,6 +389,7 @@ popd %install install -dp %{buildroot}%{_unitdir} +install -dp %{buildroot}%{_usr}/lib/systemd/user PODMAN_VERSION=%{version} %{__make} PREFIX=%{buildroot}%{_prefix} ETCDIR=%{buildroot}%{_sysconfdir} \ install.bin \ install.remote \ @@ -487,6 +488,8 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %{_datadir}/containers/%{repo}.conf %{_unitdir}/io.podman.service %{_unitdir}/io.podman.socket +%{_usr}/lib/systemd/user/io.podman.service +%{_usr}/lib/systemd/user/io.podman.socket %{_usr}/lib/tmpfiles.d/%{name}.conf %if 0%{?with_devel} diff --git a/contrib/varlink/io.podman.service b/contrib/varlink/io.podman.service index c524ce815..725198e79 100644 --- a/contrib/varlink/io.podman.service +++ b/contrib/varlink/io.podman.service @@ -6,7 +6,8 @@ Documentation=man:podman-varlink(1) [Service] Type=simple -ExecStart=/usr/bin/podman varlink unix:/run/podman/io.podman +ExecStart=/usr/bin/podman varlink unix:%t/podman/io.podman +KillMode=none [Install] WantedBy=multi-user.target diff --git a/contrib/varlink/io.podman.socket b/contrib/varlink/io.podman.socket index 0f09fe3ef..f6a3ddc49 100644 --- a/contrib/varlink/io.podman.socket +++ b/contrib/varlink/io.podman.socket @@ -3,7 +3,7 @@ Description=Podman Remote API Socket Documentation=man:podman-varlink(1) [Socket] -ListenStream=/run/podman/io.podman +ListenStream=%t/podman/io.podman SocketMode=0600 [Install] diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 29ae2e1b7..7634408f5 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -582,6 +582,15 @@ port to a random port on the host within an *ephemeral port range* defined by `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host ports and the exposed ports, use `podman port`. +**--pull**=*missing* + +Pull image before creating ("always"|"missing"|"never") (default "missing"). + 'missing': default value, attempt to pull the latest image from the registries listed in registries.conf if a local image does not exist.Raise an error if the image is not in any listed registry and is not present locally. + 'always': Pull the image from the first registry it is found in as listed in registries.conf. Raise an error if not found in the registries, even if the image is present locally. + 'never': do not pull the image from the registry, use only the local version. Raise an error if the image is not present locally. + +Defaults to *missing*. + **--quiet**, **-q** Suppress output information when pulling images @@ -716,7 +725,7 @@ $ podman run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image This command mounts a `tmpfs` at `/tmp` within the container. The supported mount options are the same as the Linux default `mount` flags. If you do not specify any options, the systems uses the following options: -`rw,noexec,nosuid,nodev,size=65536k`. +`rw,noexec,nosuid,nodev`. **--tty**, **-t**=*true|false* diff --git a/docs/podman-network-inspect.1.md b/docs/podman-network-inspect.1.md new file mode 100644 index 000000000..576e61c79 --- /dev/null +++ b/docs/podman-network-inspect.1.md @@ -0,0 +1,50 @@ +% podman-network-inspect(1) + +## NAME +podman\-network-inspect- Inspect one or more Podman networks + +## SYNOPSIS +**podman network inspect** [*network* ...] + +## DESCRIPTION +Display the raw (JSON format) network configuration. This command is not available for rootless users. + +## EXAMPLE + +Inspect the default podman network + +``` +# podman network inspect podman +[{ + "cniVersion": "0.3.0", + "name": "podman", + "plugins": [ + { + "type": "bridge", + "bridge": "cni0", + "isGateway": true, + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "10.88.1.0/24", + "routes": [ + { "dst": "0.0.0.0/0" } + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] +} +] +``` + +## SEE ALSO +podman(1), podman-network(1), podman-network-ls(1) + +## HISTORY +August 2019, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/podman-network-ls.1.md b/docs/podman-network-ls.1.md new file mode 100644 index 000000000..725e07dbb --- /dev/null +++ b/docs/podman-network-ls.1.md @@ -0,0 +1,43 @@ +% podman-network-ls(1) + +## NAME +podman\-network-ls- Display a summary of CNI networks + +## SYNOPSIS +**podman network ls** [*options*] + +## DESCRIPTION +Displays a list of existing podman networks. This command is not available for rootless users. + +## OPTIONS +**--quiet**, **-q** + +The `quiet` options will restrict the output to only the network names + +## EXAMPLE + +Display networks + +``` +# podman network ls +NAME VERSION PLUGINS +podman 0.3.0 bridge,portmap +podman2 0.3.0 bridge,portmap +outside 0.3.0 bridge +podman9 0.3.0 bridge,portmap +``` + +Display only network names +``` +# podman network ls -q +podman +podman2 +outside +podman9 +``` + +## SEE ALSO +podman(1), podman-network(1), podman-network-inspect(1) + +## HISTORY +August 2019, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/podman-network-rm.1.md b/docs/podman-network-rm.1.md new file mode 100644 index 000000000..f72d6a694 --- /dev/null +++ b/docs/podman-network-rm.1.md @@ -0,0 +1,25 @@ +% podman-network-rm(1) + +## NAME +podman\-network-rm- Delete a Podman CNI network + +## SYNOPSIS +**podman network rm** [*network...*] + +## DESCRIPTION +Delete one or more Podman networks. + +## EXAMPLE + +Delete the `podman9` network + +``` +# podman network rm podman +Deleted: podman9 +``` + +## SEE ALSO +podman(1), podman-network(1), podman-network-inspect(1) + +## HISTORY +August 2019, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/podman-network.1.md b/docs/podman-network.1.md new file mode 100644 index 000000000..c01adc23e --- /dev/null +++ b/docs/podman-network.1.md @@ -0,0 +1,21 @@ +% podman-network(1) + +## NAME +podman\-network- Manage podman CNI networks + +## SYNOPSIS +**podman network** *subcommand* + +## DESCRIPTION +The network command manages CNI networks for Podman. It is not supported for rootless users. + +## COMMANDS + +| Command | Man Page | Description | +| ------- | --------------------------------------------------- | ---------------------------------------------------------------------------- | +| inspect | [podman-network-inspect(1)](podman-network-inspect.1.md)| Displays the raw CNI network configuration for one or more networks| +| ls | [podman-network-ls(1)](podman-network-ls.1.md)| Display a summary of CNI networks | +| rm | [podman-network-rm(1)](podman-network-rm.1.md)| Remove one or more CNI networks | + +## SEE ALSO +podman(1) diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index 8a211e284..33b5cbf9e 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -601,6 +601,15 @@ When using -P, podman will bind any exposed port to a random port on the host within an *ephemeral port range* defined by `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host ports and the exposed ports, use `podman port`. +**--pull**=*missing* + +Pull image before running ("always"|"missing"|"never") (default "missing"). + 'missing': default value, attempt to pull the latest image from the registries listed in registries.conf if a local image does not exist.Raise an error if the image is not in any listed registry and is not present locally. + 'always': Pull the image from the first registry it is found in as listed in registries.conf. Raise an error if not found in the registries, even if the image is present locally. + 'never': do not pull the image from the registry, use only the local version. Raise an error if the image is not present locally. + +Defaults to *missing*. + **--quiet**, **-q** Suppress output information when pulling images @@ -753,7 +762,7 @@ $ podman run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image This command mounts a `tmpfs` at `/tmp` within the container. The supported mount options are the same as the Linux default `mount` flags. If you do not specify any options, the systems uses the following options: -`rw,noexec,nosuid,nodev,size=65536k`. +`rw,noexec,nosuid,nodev`. **--tty**, **-t**=*true|false* diff --git a/docs/podman.1.md b/docs/podman.1.md index 12b7866ca..33ea81ef6 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -161,6 +161,7 @@ the exit codes follow the `chroot` standard, see below: | [podman-logout(1)](podman-logout.1.md) | Logout of a container registry. | | [podman-logs(1)](podman-logs.1.md) | Display the logs of a container. | | [podman-mount(1)](podman-mount.1.md) | Mount a working container's root filesystem. | +| [podman-network(1)](podman-network.1.md) | Manage Podman CNI networks. | | [podman-pause(1)](podman-pause.1.md) | Pause one or more containers. | | [podman-play(1)](podman-play.1.md) | Play pods and containers based on a structured input file. | | [podman-pod(1)](podman-pod.1.md) | Management tool for groups of containers, called pods. | diff --git a/install.md b/install.md index 49a67f984..d8d70a7b6 100644 --- a/install.md +++ b/install.md @@ -26,6 +26,14 @@ Built-in, no need to install sudo emerge app-emulation/libpod ``` +#### [MacOS](https://www.apple.com/macos) + +Using [Homebrew](https://brew.sh/): + +```bash +brew cask install podman +``` + #### [openSUSE](https://www.opensuse.org) ```bash diff --git a/libpod/driver/driver.go b/libpod/driver/driver.go index f9442fa21..85eda5a21 100644 --- a/libpod/driver/driver.go +++ b/libpod/driver/driver.go @@ -38,6 +38,10 @@ func GetDriverData(store cstorage.Store, layerID string) (*Data, error) { if err != nil { return nil, err } + if mountTimes, err := store.Mounted(layerID); mountTimes == 0 || err != nil { + delete(metaData, "MergedDir") + } + return &Data{ Name: name, Data: metaData, diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 3bc3f6de7..470c76959 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -73,6 +73,11 @@ func (e EventJournalD) Read(options ReadOptions) error { if err := j.SeekTail(); err != nil { return errors.Wrap(err, "failed to seek end of journal") } + } else { + podmanJournal := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} //nolint + if err := j.AddMatch(podmanJournal.String()); err != nil { + return errors.Wrap(err, "failed to add filter for event log") + } } // the api requires a next|prev before getting a cursor if _, err := j.Next(); err != nil { diff --git a/libpod/image/image.go b/libpod/image/image.go index 068491f28..cb7c390c6 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -135,7 +135,7 @@ func (ir *Runtime) NewFromLocal(name string) (*Image, error) { // New creates a new image object where the image could be local // or remote -func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, forcePull bool, label *string) (*Image, error) { +func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, label *string, pullType util.PullType) (*Image, error) { span, _ := opentracing.StartSpanFromContext(ctx, "newImage") span.SetTag("type", "runtime") defer span.Finish() @@ -145,11 +145,13 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile InputName: name, imageruntime: ir, } - if !forcePull { + if pullType != util.PullImageAlways { localImage, err := newImage.getLocalImage() if err == nil { newImage.image = localImage return &newImage, nil + } else if pullType == util.PullImageNever { + return nil, err } } diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index e93ebf797..5a6d095f6 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -3,12 +3,13 @@ package image import ( "context" "fmt" - "github.com/containers/libpod/libpod/events" "io" "io/ioutil" "os" "testing" + "github.com/containers/libpod/libpod/events" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" @@ -89,9 +90,9 @@ func TestImage_NewFromLocal(t *testing.T) { ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) ir.Eventer = events.NewNullEventer() - bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, false, nil) + bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) - bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, false, nil) + bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) tm, err := makeLocalMatrix(bb, bbglibc) @@ -139,7 +140,7 @@ func TestImage_New(t *testing.T) { // Iterate over the names and delete the image // after the pull for _, img := range names { - newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, false, nil) + newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) assert.NotEqual(t, newImage.ID(), "") err = newImage.Remove(context.Background(), false) @@ -168,7 +169,7 @@ func TestImage_MatchRepoTag(t *testing.T) { ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) ir.Eventer = events.NewNullEventer() - newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, false, nil) + newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, nil, util.PullImageMissing) assert.NoError(t, err) err = newImage.TagImage("foo:latest") assert.NoError(t, err) diff --git a/libpod/oci.go b/libpod/oci.go index 2eb004b84..4ba3114e3 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -60,6 +60,7 @@ type OCIRuntime struct { noPivot bool reservePorts bool supportsJSON bool + sdNotify bool } // ociError is used to parse the OCI runtime JSON log. It is not part of the @@ -87,6 +88,7 @@ func newOCIRuntime(name string, paths []string, conmonPath string, runtimeCfg *R runtime.logSizeMax = runtimeCfg.MaxLogSize runtime.noPivot = runtimeCfg.NoPivotRoot runtime.reservePorts = runtimeCfg.EnablePortReservation + runtime.sdNotify = runtimeCfg.SDNotify // TODO: probe OCI runtime for feature and enable automatically if // available. diff --git a/libpod/oci_internal_linux.go b/libpod/oci_internal_linux.go index e2c73f5ed..6e4ee2cf2 100644 --- a/libpod/oci_internal_linux.go +++ b/libpod/oci_internal_linux.go @@ -247,10 +247,14 @@ func (r *OCIRuntime) configureConmonEnv(runtimeDir string) ([]string, []*os.File if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify)) } - if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { - env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") - fds := activation.Files(false) - extraFiles = append(extraFiles, fds...) + if !r.sdNotify { + if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { + env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") + fds := activation.Files(false) + extraFiles = append(extraFiles, fds...) + } + } else { + logrus.Debug("disabling SD notify") } return env, extraFiles, nil } diff --git a/libpod/options.go b/libpod/options.go index 7fbd0016a..d2a67e38c 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -482,6 +482,15 @@ func WithEventsLogger(logger string) RuntimeOption { } } +// WithEnableSDNotify sets a runtime option so we know whether to disable socket/FD +// listening +func WithEnableSDNotify() RuntimeOption { + return func(rt *Runtime) error { + rt.config.SDNotify = true + return nil + } +} + // Container Creation Options // WithShmDir sets the directory that should be mounted on /dev/shm. @@ -1488,6 +1497,24 @@ func WithPodName(name string) PodCreateOption { } } +// WithPodHostname sets the hostname of the pod. +func WithPodHostname(hostname string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + // Check the hostname against a regex + if !nameRegex.MatchString(hostname) { + return regexError + } + + pod.config.Hostname = hostname + + return nil + } +} + // WithPodLabels sets the labels of a pod. func WithPodLabels(labels map[string]string) PodCreateOption { return func(pod *Pod) error { diff --git a/libpod/pod.go b/libpod/pod.go index 60626bfd7..3b9bb9c60 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -36,6 +36,8 @@ type PodConfig struct { // Namespace the pod is in Namespace string `json:"namespace,omitempty"` + Hostname string `json:"hostname,omitempty"` + // Labels contains labels applied to the pod Labels map[string]string `json:"labels"` // CgroupParent contains the pod's CGroup parent diff --git a/libpod/runtime.go b/libpod/runtime.go index 8a4eee081..2fa8dd424 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -252,6 +252,10 @@ type RuntimeConfig struct { EventsLogFilePath string `toml:"-events_logfile_path"` //DetachKeys is the sequence of keys used to detach a container DetachKeys string `toml:"detach_keys"` + + // SDNotify tells Libpod to allow containers to notify the host + // systemd of readiness using the SD_NOTIFY mechanism + SDNotify bool } // runtimeConfiguredFrom is a struct used during early runtime init to help diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index da35b7f93..ad6662f03 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -9,6 +9,7 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" "github.com/opencontainers/image-spec/specs-go/v1" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" @@ -30,6 +31,9 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID return nil, err } + // Set Pod hostname + g.Config.Hostname = p.config.Hostname + isRootless := rootless.IsRootless() entryCmd := []string{r.config.InfraCommand} @@ -108,7 +112,7 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, return nil, define.ErrRuntimeStopped } - newImage, err := r.ImageRuntime().New(ctx, r.config.InfraImage, "", "", nil, nil, image.SigningOptions{}, false, nil) + newImage, err := r.ImageRuntime().New(ctx, r.config.InfraImage, "", "", nil, nil, image.SigningOptions{}, nil, util.PullImageMissing) if err != nil { return nil, err } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index f38e6e7c1..073c5054d 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -52,6 +52,10 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Po pod.config.Name = name } + if pod.config.Hostname == "" { + pod.config.Hostname = pod.config.Name + } + // Allocate a lock for the pod lock, err := r.lockManager.AllocateLock() if err != nil { diff --git a/libpod/util_linux.go b/libpod/util_linux.go index 78cbc75a7..d5c113daf 100644 --- a/libpod/util_linux.go +++ b/libpod/util_linux.go @@ -48,6 +48,9 @@ func makeSystemdCgroup(path string) error { return err } + if rootless.IsRootless() { + return controller.CreateSystemdUserUnit(path, rootless.GetRootlessUID()) + } return controller.CreateSystemdUnit(path) } @@ -57,6 +60,14 @@ func deleteSystemdCgroup(path string) error { if err != nil { return err } + if rootless.IsRootless() { + conn, err := cgroups.GetUserConnection(rootless.GetRootlessUID()) + if err != nil { + return err + } + defer conn.Close() + return controller.DeleteByPathConn(path, conn) + } return controller.DeleteByPath(path) } diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/adapter/checkpoint_restore.go index 1cac86d12..15f9e8105 100644 --- a/pkg/adapter/checkpoint_restore.go +++ b/pkg/adapter/checkpoint_restore.go @@ -11,6 +11,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/errorhandling" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage/pkg/archive" jsoniter "github.com/json-iterator/go" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -112,7 +113,7 @@ func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri return nil, err } - _, err = runtime.ImageRuntime().New(ctx, config.RootfsImageName, rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false, nil) + _, err = runtime.ImageRuntime().New(ctx, config.RootfsImageName, rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, nil, util.PullImageMissing) if err != nil { return nil, err } diff --git a/pkg/adapter/network.go b/pkg/adapter/network.go new file mode 100644 index 000000000..cf3a1dfdd --- /dev/null +++ b/pkg/adapter/network.go @@ -0,0 +1,147 @@ +// +build !remoteclient + +package adapter + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "strings" + "text/tabwriter" + + "github.com/containernetworking/cni/libcni" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/network" + "github.com/pkg/errors" +) + +func getCNIConfDir(r *LocalRuntime) (string, error) { + config, err := r.GetConfig() + if err != nil { + return "", err + } + configPath := config.CNIConfigDir + + if len(config.CNIConfigDir) < 1 { + configPath = network.CNIConfigDir + } + return configPath, nil +} + +// NetworkList displays summary information about CNI networks +func (r *LocalRuntime) NetworkList(cli *cliconfig.NetworkListValues) error { + cniConfigPath, err := getCNIConfDir(r) + if err != nil { + return err + } + networks, err := network.LoadCNIConfsFromDir(cniConfigPath) + if err != nil { + return err + } + // quiet means we only print the network names + if cli.Quiet { + for _, cniNetwork := range networks { + fmt.Println(cniNetwork.Name) + } + return nil + } + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + if _, err := fmt.Fprintln(w, "NAME\tVERSION\tPLUGINS"); err != nil { + return err + } + for _, cniNetwork := range networks { + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", cniNetwork.Name, cniNetwork.CNIVersion, getCNIPlugins(cniNetwork)); err != nil { + return err + } + } + return w.Flush() +} + +// NetworkInspect displays the raw CNI configuration for one +// or more CNI networks +func (r *LocalRuntime) NetworkInspect(cli *cliconfig.NetworkInspectValues) error { + var ( + rawCNINetworks []map[string]interface{} + ) + cniConfigPath, err := getCNIConfDir(r) + if err != nil { + return err + } + for _, name := range cli.InputArgs { + b, err := readRawCNIConfByName(name, cniConfigPath) + if err != nil { + return err + } + rawList := make(map[string]interface{}) + if err := json.Unmarshal(b, &rawList); err != nil { + return fmt.Errorf("error parsing configuration list: %s", err) + } + rawCNINetworks = append(rawCNINetworks, rawList) + } + out, err := json.MarshalIndent(rawCNINetworks, "", "\t") + if err != nil { + return err + } + fmt.Printf("%s\n", out) + return nil +} + +// NetworkRemove deletes one or more CNI networks +func (r *LocalRuntime) NetworkRemove(cli *cliconfig.NetworkRmValues) error { + cniConfigPath, err := getCNIConfDir(r) + if err != nil { + return err + } + for _, name := range cli.InputArgs { + cniPath, err := getCNIConfigPathByName(name, cniConfigPath) + if err != nil { + return err + } + if err := os.Remove(cniPath); err != nil { + return err + } + fmt.Printf("Deleted: %s\n", name) + } + return nil +} + +// getCNIConfigPathByName finds a CNI network by name and +// returns its configuration file path +func getCNIConfigPathByName(name, cniConfigPath string) (string, error) { + files, err := libcni.ConfFiles(cniConfigPath, []string{".conflist"}) + if err != nil { + return "", err + } + for _, confFile := range files { + conf, err := libcni.ConfListFromFile(confFile) + if err != nil { + return "", err + } + if conf.Name == name { + return confFile, nil + } + } + return "", errors.Errorf("unable to find network configuration for %s", name) +} + +// readRawCNIConfByName reads the raw CNI configuration for a CNI +// network by name +func readRawCNIConfByName(name, cniConfigPath string) ([]byte, error) { + confFile, err := getCNIConfigPathByName(name, cniConfigPath) + if err != nil { + return nil, err + } + b, err := ioutil.ReadFile(confFile) + return b, err +} + +// getCNIPlugins returns a list of plugins that a given network +// has in the form of a string +func getCNIPlugins(list *libcni.NetworkConfigList) string { + var plugins []string + for _, plug := range list.Plugins { + plugins = append(plugins, plug.Network.Type) + } + return strings.Join(plugins, ",") +} diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index e25238956..ded805de2 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -19,6 +19,7 @@ import ( "github.com/containers/libpod/pkg/adapter/shortcuts" ns "github.com/containers/libpod/pkg/namespaces" createconfig "github.com/containers/libpod/pkg/spec" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/ghodss/yaml" @@ -255,6 +256,10 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa options = append(options, libpod.WithPodName(cli.Name)) } + if cli.Flag("hostname").Changed { + options = append(options, libpod.WithPodHostname(cli.Hostname)) + } + if cli.Infra { options = append(options, libpod.WithInfraContainer()) nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ",")) @@ -475,6 +480,12 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa podOptions = append(podOptions, libpod.WithPodName(podName)) // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml + hostname := podYAML.Spec.Hostname + if hostname == "" { + hostname = podName + } + podOptions = append(podOptions, libpod.WithPodHostname(hostname)) + nsOptions, err := shared.GetNamespaceOptions(strings.Split(shared.DefaultKernelNamespaces, ",")) if err != nil { return nil, err @@ -578,7 +589,7 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa } for _, container := range podYAML.Spec.Containers { - newImage, err := r.ImageRuntime().New(ctx, container.Image, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, false, nil) + newImage, err := r.ImageRuntime().New(ctx, container.Image, c.SignaturePolicy, c.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageMissing) if err != nil { return nil, err } @@ -707,6 +718,8 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container return nil, errors.Errorf("No command specified in container YAML or as CMD or ENTRYPOINT in this image for %s", containerConfig.Name) } + containerConfig.UserCommand = containerConfig.Command + containerConfig.StopSignal = 15 // If the user does not pass in ID mappings, just set to basics diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 03419c0bd..ba988aaf7 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -24,6 +24,7 @@ import ( "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" "k8s.io/api/core/v1" @@ -132,8 +133,8 @@ func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef type } // New calls into local storage to look for an image in local storage or to pull it -func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool, label *string) (*ContainerImage, error) { - img, err := r.Runtime.ImageRuntime().New(ctx, name, signaturePolicyPath, authfile, writer, dockeroptions, signingoptions, forcePull, label) +func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, label *string, pullType util.PullType) (*ContainerImage, error) { + img, err := r.Runtime.ImageRuntime().New(ctx, name, signaturePolicyPath, authfile, writer, dockeroptions, signingoptions, label, pullType) if err != nil { return nil, err } @@ -288,7 +289,11 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti options.CommonBuildOpts = commonOpts options.SystemContext = systemContext - options.Runtime = r.GetOCIRuntimePath() + if c.GlobalFlags.Runtime != "" { + options.Runtime = c.GlobalFlags.Runtime + } else { + options.Runtime = r.GetOCIRuntimePath() + } if c.Quiet { options.ReportWriter = ioutil.Discard diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 0cafbb2aa..420c9d0bb 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -26,6 +26,7 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/utils" "github.com/containers/storage/pkg/archive" "github.com/opencontainers/go-digest" @@ -272,7 +273,7 @@ func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef type } // New calls into local storage to look for an image in local storage or to pull it -func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool, label *string) (*ContainerImage, error) { +func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, label *string, pullType util.PullType) (*ContainerImage, error) { var iid string if label != nil { return nil, errors.New("the remote client function does not support checking a remote image for a label") diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index f2c6b548e..085718855 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -10,6 +10,8 @@ import ( "strconv" "strings" + systemdDbus "github.com/coreos/go-systemd/dbus" + "github.com/godbus/dbus" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -352,7 +354,56 @@ func (c *CgroupControl) CreateSystemdUnit(path string) error { if !c.systemd { return fmt.Errorf("the cgroup controller is not using systemd") } - return systemdCreate(path) + + conn, err := systemdDbus.New() + if err != nil { + return err + } + defer conn.Close() + + return systemdCreate(path, conn) +} + +// GetUserConnection returns an user connection to D-BUS +func GetUserConnection(uid int) (*systemdDbus.Conn, error) { + return systemdDbus.NewConnection(func() (*dbus.Conn, error) { + return dbusAuthConnection(uid, dbus.SessionBusPrivate) + }) +} + +// CreateSystemdUserUnit creates the systemd cgroup for the specified user +func (c *CgroupControl) CreateSystemdUserUnit(path string, uid int) error { + if !c.systemd { + return fmt.Errorf("the cgroup controller is not using systemd") + } + + conn, err := GetUserConnection(uid) + if err != nil { + return err + } + defer conn.Close() + + return systemdCreate(path, conn) +} + +func dbusAuthConnection(uid int, createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { + conn, err := createBus() + if err != nil { + return nil, err + } + + methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(uid))} + + err = conn.Auth(methods) + if err != nil { + conn.Close() + return nil, err + } + if err := conn.Hello(); err != nil { + return nil, err + } + + return conn, nil } // Delete cleans a cgroup @@ -386,10 +437,11 @@ func rmDirRecursively(path string) error { return nil } -// DeleteByPath deletes the specified cgroup path -func (c *CgroupControl) DeleteByPath(path string) error { +// DeleteByPathConn deletes the specified cgroup path using the specified +// dbus connection if needed. +func (c *CgroupControl) DeleteByPathConn(path string, conn *systemdDbus.Conn) error { if c.systemd { - return systemdDestroy(path) + return systemdDestroyConn(path, conn) } if c.cgroup2 { return rmDirRecursively(filepath.Join(cgroupRoot, c.path)) @@ -413,6 +465,19 @@ func (c *CgroupControl) DeleteByPath(path string) error { return lastError } +// DeleteByPath deletes the specified cgroup path +func (c *CgroupControl) DeleteByPath(path string) error { + if c.systemd { + conn, err := systemdDbus.New() + if err != nil { + return err + } + defer conn.Close() + return c.DeleteByPathConn(path, conn) + } + return c.DeleteByPathConn(path, nil) +} + // Update updates the cgroups func (c *CgroupControl) Update(resources *spec.LinuxResources) error { for _, h := range handlers { diff --git a/pkg/cgroups/systemd.go b/pkg/cgroups/systemd.go index e72e456bc..b8e6db156 100644 --- a/pkg/cgroups/systemd.go +++ b/pkg/cgroups/systemd.go @@ -9,13 +9,7 @@ import ( "github.com/godbus/dbus" ) -func systemdCreate(path string) error { - c, err := systemdDbus.New() - if err != nil { - return err - } - defer c.Close() - +func systemdCreate(path string, c *systemdDbus.Conn) error { slice, name := filepath.Split(path) slice = strings.TrimSuffix(slice, "/") @@ -43,7 +37,7 @@ func systemdCreate(path string) error { } ch := make(chan string) - _, err = c.StartTransientUnit(name, "replace", properties, ch) + _, err := c.StartTransientUnit(name, "replace", properties, ch) if err != nil { lastError = err continue @@ -55,7 +49,7 @@ func systemdCreate(path string) error { } /* - systemdDestroy is copied from containerd/cgroups/systemd.go file, that + systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that has the following license: Copyright The containerd Authors. @@ -72,18 +66,11 @@ func systemdCreate(path string) error { See the License for the specific language governing permissions and limitations under the License. */ - -func systemdDestroy(path string) error { - c, err := systemdDbus.New() - if err != nil { - return err - } - defer c.Close() - +func systemdDestroyConn(path string, c *systemdDbus.Conn) error { name := filepath.Base(path) ch := make(chan string) - _, err = c.StopUnit(name, "replace", ch) + _, err := c.StopUnit(name, "replace", ch) if err != nil { return err } diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index b962ffa5c..0d26bf4af 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -4,6 +4,7 @@ package hooks import ( "context" "fmt" + "os" "sort" "strings" "sync" @@ -56,7 +57,7 @@ func New(ctx context.Context, directories []string, extensionStages []string) (m for _, dir := range directories { err = ReadDir(dir, manager.extensionStages, manager.hooks) - if err != nil { + if err != nil && !os.IsNotExist(err) { return nil, err } } diff --git a/pkg/network/config.go b/pkg/network/config.go new file mode 100644 index 000000000..d282f66b6 --- /dev/null +++ b/pkg/network/config.go @@ -0,0 +1,4 @@ +package network + +// CNIConfigDir is the path where CNI config files exist +const CNIConfigDir = "/etc/cni/net.d" diff --git a/pkg/network/network.go b/pkg/network/network.go new file mode 100644 index 000000000..9d04340a3 --- /dev/null +++ b/pkg/network/network.go @@ -0,0 +1,26 @@ +package network + +import ( + "sort" + + "github.com/containernetworking/cni/libcni" +) + +// LoadCNIConfsFromDir loads all the CNI configurations from a dir +func LoadCNIConfsFromDir(dir string) ([]*libcni.NetworkConfigList, error) { + var configs []*libcni.NetworkConfigList + files, err := libcni.ConfFiles(dir, []string{".conflist"}) + if err != nil { + return nil, err + } + sort.Strings(files) + + for _, confFile := range files { + conf, err := libcni.ConfListFromFile(confFile) + if err != nil { + return nil, err + } + configs = append(configs, conf) + } + return configs, nil +} diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index a8dc7f4a8..b634f4cac 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -168,14 +168,14 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, "/run": false, } if config.ReadOnlyRootfs && config.ReadOnlyTmpfs { - options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup", "size=65536k"} + options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} for dest := range readonlyTmpfs { if _, ok := baseMounts[dest]; ok { continue } localOpts := options if dest == "/run" { - localOpts = append(localOpts, "noexec") + localOpts = append(localOpts, "noexec", "size=65536k") } baseMounts[dest] = spec.Mount{ Destination: dest, diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go index 40c99384d..9b2c734c0 100644 --- a/pkg/util/mountOpts.go +++ b/pkg/util/mountOpts.go @@ -92,9 +92,6 @@ func ProcessTmpfsOptions(options []string) ([]string, error) { if !foundWrite { baseOpts = append(baseOpts, "rw") } - if !foundSize { - baseOpts = append(baseOpts, "size=65536k") - } if !foundProp { baseOpts = append(baseOpts, "rprivate") } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 520e41438..3f73639e7 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -356,3 +356,32 @@ func OpenExclusiveFile(path string) (*os.File, error) { } return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) } + +// PullType whether to pull new image +type PullType int + +const ( + // PullImageAlways always try to pull new image when create or run + PullImageAlways PullType = iota + // PullImageMissing pulls image if it is not locally + PullImageMissing + // PullImageNever will never pull new image + PullImageNever +) + +// ValidatePullType check if the pullType from CLI is valid and returns the valid enum type +// if the value from CLI is invalid returns the error +func ValidatePullType(pullType string) (PullType, error) { + switch pullType { + case "always": + return PullImageAlways, nil + case "missing": + return PullImageMissing, nil + case "never": + return PullImageNever, nil + case "": + return PullImageMissing, nil + default: + return PullImageMissing, errors.Errorf("invalid pull type %q", pullType) + } +} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index b5a711dfd..fe7f11b4d 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -658,7 +658,7 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error { imageID = newImage[0].ID() } } else { - newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, "", "", output, &dockerRegistryOptions, so, false, nil) + newImage, err := i.Runtime.ImageRuntime().New(getContext(), name, "", "", output, &dockerRegistryOptions, so, nil, util.PullImageMissing) if err != nil { foundError = true c <- errors.Wrapf(err, "unable to pull %s", name) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index b6dd1ecd1..4e9881d59 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -538,7 +538,7 @@ func (p *PodmanTestIntegration) RunHealthCheck(cid string) error { return nil } // Restart container if it's not running - ps := p.Podman([]string{"ps", "--no-trunc", "--q", "--filter", fmt.Sprintf("id=%s", cid)}) + ps := p.Podman([]string{"ps", "--no-trunc", "--quiet", "--filter", fmt.Sprintf("id=%s", cid)}) ps.WaitWithDefaultTimeout() if ps.ExitCode() == 0 { if !strings.Contains(ps.OutputToString(), cid) { diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 25d0c3390..2918cce78 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -231,4 +231,14 @@ var _ = Describe("Podman create", func() { Expect(ctrJSON[0].Config.Cmd[0]).To(Equal("redis-server")) Expect(ctrJSON[0].Config.Entrypoint).To(Equal("docker-entrypoint.sh")) }) + + It("podman create --pull", func() { + session := podmanTest.PodmanNoCache([]string{"create", "--pull", "never", "--name=foo", "nginx"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + + session = podmanTest.PodmanNoCache([]string{"create", "--pull", "always", "--name=foo", "nginx"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To((Equal(0))) + }) }) diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go new file mode 100644 index 000000000..9aed5351a --- /dev/null +++ b/test/e2e/network_test.go @@ -0,0 +1,158 @@ +// +build !remoteclient + +package integration + +import ( + "fmt" + . "github.com/containers/libpod/test/utils" + "github.com/containers/storage/pkg/stringid" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "io/ioutil" + "os" + "path/filepath" +) + +func writeConf(conf []byte, confPath string) { + if err := ioutil.WriteFile(confPath, conf, 777); err != nil { + fmt.Println(err) + } +} +func removeConf(confPath string) { + if err := os.Remove(confPath); err != nil { + fmt.Println(err) + } +} + +var _ = Describe("Podman network", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + var ( + secondConf = `{ + "cniVersion": "0.3.0", + "name": "podman-integrationtest", + "plugins": [ + { + "type": "bridge", + "bridge": "cni1", + "isGateway": true, + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "10.99.0.0/16", + "routes": [ + { "dst": "0.0.0.0/0" } + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] +}` + cniPath = "/etc/cni/net.d" + ) + + It("podman network list", func() { + SkipIfRootless() + // Setup, use uuid to prevent conflict with other tests + uuid := stringid.GenerateNonCryptoID() + secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) + writeConf([]byte(secondConf), secondPath) + defer removeConf(secondPath) + + session := podmanTest.Podman([]string{"network", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue()) + }) + + It("podman network list -q", func() { + SkipIfRootless() + // Setup, use uuid to prevent conflict with other tests + uuid := stringid.GenerateNonCryptoID() + secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) + writeConf([]byte(secondConf), secondPath) + defer removeConf(secondPath) + + session := podmanTest.Podman([]string{"network", "ls", "--quiet"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue()) + }) + + It("podman network rm no args", func() { + SkipIfRootless() + session := podmanTest.Podman([]string{"network", "rm"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).ToNot(BeZero()) + }) + + It("podman network rm", func() { + SkipIfRootless() + // Setup, use uuid to prevent conflict with other tests + uuid := stringid.GenerateNonCryptoID() + secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) + writeConf([]byte(secondConf), secondPath) + defer removeConf(secondPath) + + session := podmanTest.Podman([]string{"network", "ls", "--quiet"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue()) + + rm := podmanTest.Podman([]string{"network", "rm", "podman-integrationtest"}) + rm.WaitWithDefaultTimeout() + Expect(rm.ExitCode()).To(BeZero()) + + results := podmanTest.Podman([]string{"network", "ls", "--quiet"}) + results.WaitWithDefaultTimeout() + Expect(results.ExitCode()).To(Equal(0)) + Expect(results.LineInOutputContains("podman-integrationtest")).To(BeFalse()) + }) + + It("podman network inspect no args", func() { + SkipIfRootless() + session := podmanTest.Podman([]string{"network", "inspect"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).ToNot(BeZero()) + }) + + It("podman network inspect", func() { + SkipIfRootless() + // Setup, use uuid to prevent conflict with other tests + uuid := stringid.GenerateNonCryptoID() + secondPath := filepath.Join(cniPath, fmt.Sprintf("%s.conflist", uuid)) + writeConf([]byte(secondConf), secondPath) + defer removeConf(secondPath) + + session := podmanTest.Podman([]string{"network", "inspect", "podman-integrationtest", "podman"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.IsJSONOutputValid()).To(BeTrue()) + }) + +}) diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go index 455f60937..c61131078 100644 --- a/test/e2e/pause_test.go +++ b/test/e2e/pause_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/containers/libpod/pkg/cgroups" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -25,6 +26,17 @@ var _ = Describe("Podman pause", func() { if err != nil { os.Exit(1) } + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if cgroupsv2 { + _, err := os.Stat("/sys/fs/cgroup/cgroup.freeze") + if err != nil { + Skip("freezer controller not available on the current kernel") + } + } + podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() podmanTest.SeedImages() diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 331412a39..af3cab379 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -21,6 +21,7 @@ metadata: app: {{ .Name }} name: {{ .Name }} spec: + hostname: {{ .Hostname }} containers: {{ with .Containers }} {{ range . }} @@ -66,6 +67,7 @@ status: {} type Pod struct { Name string + Hostname string Containers []Container } @@ -78,13 +80,13 @@ type Container struct { CapDrop []string } -func generateKubeYaml(ctrs []Container, fileName string) error { +func generateKubeYaml(name string, hostname string, ctrs []Container, fileName string) error { f, err := os.Create(fileName) if err != nil { return err } defer f.Close() - testPod := Pod{"test", ctrs} + testPod := Pod{name, hostname, ctrs} t, err := template.New("pod").Parse(yamlTemplate) if err != nil { @@ -127,7 +129,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) @@ -140,6 +142,70 @@ var _ = Describe("Podman generate kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring(ctrCmd[0])) }) + It("podman play kube test correct output", func() { + ctrName := "testCtr" + ctrCmd := []string{"echo", "hello"} + testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} + tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") + + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", tempFile}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + logs := podmanTest.Podman([]string{"logs", ctrName}) + logs.WaitWithDefaultTimeout() + Expect(logs.ExitCode()).To(Equal(0)) + Expect(logs.OutputToString()).To(ContainSubstring("hello")) + + inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("hello")) + }) + + It("podman play kube test hostname", func() { + podName := "test" + ctrName := "testCtr" + ctrCmd := []string{"top"} + testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} + tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") + + err := generateKubeYaml(podName, "", []Container{testContainer}, tempFile) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", tempFile}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "{{ .Config.Hostname }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(Equal(podName)) + }) + + It("podman play kube test with customized hostname", func() { + hostname := "myhostname" + ctrName := "testCtr" + ctrCmd := []string{"top"} + testContainer := Container{ctrCmd, ALPINE, ctrName, false, nil, nil} + tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") + + err := generateKubeYaml("test", hostname, []Container{testContainer}, tempFile) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", tempFile}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", ctrName, "--format", "{{ .Config.Hostname }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(Equal(hostname)) + }) + It("podman play kube cap add", func() { ctrName := "testCtr" ctrCmd := []string{"cat", "/proc/self/status"} @@ -147,7 +213,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, true, []string{capAdd}, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) @@ -167,7 +233,7 @@ var _ = Describe("Podman generate kube", func() { testContainer := Container{ctrCmd, ALPINE, ctrName, true, []string{capDrop}, nil} tempFile := filepath.Join(podmanTest.TempDir, "kube.yaml") - err := generateKubeYaml([]Container{testContainer}, tempFile) + err := generateKubeYaml("test", "", []Container{testContainer}, tempFile) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", tempFile}) diff --git a/test/e2e/run_cpu_test.go b/test/e2e/run_cpu_test.go index 87f89b1dd..4be9da3d2 100644 --- a/test/e2e/run_cpu_test.go +++ b/test/e2e/run_cpu_test.go @@ -3,8 +3,10 @@ package integration import ( + "io/ioutil" "os" + "github.com/containers/libpod/pkg/cgroups" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -22,6 +24,16 @@ var _ = Describe("Podman run cpu", func() { if err != nil { os.Exit(1) } + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if cgroupsv2 { + if err := ioutil.WriteFile("/sys/fs/cgroup/cgroup.subtree_control", []byte("+cpuset"), 0644); err != nil { + Skip("cpuset controller not available on the current kernel") + } + } + podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() podmanTest.SeedImages() @@ -36,44 +48,96 @@ var _ = Describe("Podman run cpu", func() { It("podman run cpu-period", func() { SkipIfRootless() - result := podmanTest.Podman([]string{"run", "--rm", "--cpu-period=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var result *PodmanSessionIntegration + if cgroupsv2 { + result = podmanTest.Podman([]string{"run", "--rm", "--cpu-period=5000", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpu.max"}) + } else { + result = podmanTest.Podman([]string{"run", "--rm", "--cpu-period=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"}) + } result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(Equal("5000")) + Expect(result.LineInOutputContains("5000")) }) It("podman run cpu-quota", func() { SkipIfRootless() - result := podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var result *PodmanSessionIntegration + + if cgroupsv2 { + result = podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpu.max"}) + } else { + result = podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + } result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(Equal("5000")) + Expect(result.LineInOutputContains("5000")) }) It("podman run cpus", func() { SkipIfRootless() - result := podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"}) - result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(Equal("100000")) - result = podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) - result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(Equal("50000")) + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if cgroupsv2 { + result := podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpu.max"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(Equal("5000 100000")) + } else { + result := podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(Equal("100000")) + + result = podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(Equal("50000")) + } }) It("podman run cpu-shares", func() { SkipIfRootless() - result := podmanTest.Podman([]string{"run", "--rm", "--cpu-shares=2", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.shares"}) - result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(Equal("2")) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if cgroupsv2 { + // [2-262144] is mapped to [1-10000] + result := podmanTest.Podman([]string{"run", "--rm", "--cpu-shares=262144", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpu.weight"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(Equal("10000")) + } else { + result := podmanTest.Podman([]string{"run", "--rm", "--cpu-shares=2", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.shares"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(Equal("2")) + } }) It("podman run cpuset-cpus", func() { SkipIfRootless() - result := podmanTest.Podman([]string{"run", "--rm", "--cpuset-cpus=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var result *PodmanSessionIntegration + + if cgroupsv2 { + result = podmanTest.Podman([]string{"run", "--rm", "--cpuset-cpus=0", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpuset.cpus.effective"}) + } else { + result = podmanTest.Podman([]string{"run", "--rm", "--cpuset-cpus=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"}) + } result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) Expect(result.OutputToString()).To(Equal("0")) @@ -81,7 +145,17 @@ var _ = Describe("Podman run cpu", func() { It("podman run cpuset-mems", func() { SkipIfRootless() - result := podmanTest.Podman([]string{"run", "--rm", "--cpuset-mems=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var result *PodmanSessionIntegration + + if cgroupsv2 { + result = podmanTest.Podman([]string{"run", "--rm", "--cpuset-mems=0", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpuset.mems.effective"}) + } else { + result = podmanTest.Podman([]string{"run", "--rm", "--cpuset-mems=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"}) + } result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) Expect(result.OutputToString()).To(Equal("0")) diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go index 8fe90c8d8..a45735a8a 100644 --- a/test/e2e/run_memory_test.go +++ b/test/e2e/run_memory_test.go @@ -5,6 +5,7 @@ package integration import ( "os" + "github.com/containers/libpod/pkg/cgroups" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -36,7 +37,16 @@ var _ = Describe("Podman run memory", func() { }) It("podman run memory test", func() { - session := podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"}) + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var session *PodmanSessionIntegration + + if cgroupsv2 { + session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.max"}) + } else { + session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"}) + } session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("41943040")) @@ -46,13 +56,31 @@ var _ = Describe("Podman run memory", func() { if podmanTest.Host.Distribution == "ubuntu" { Skip("Unable to perform test on Ubuntu distributions due to memory management") } - session := podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var session *PodmanSessionIntegration + + if cgroupsv2 { + session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.high"}) + } else { + session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"}) + } + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("41943040")) }) It("podman run memory-swappiness test", func() { + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if cgroupsv2 { + Skip("Memory swappiness not supported on cgroups v2") + } + session := podmanTest.Podman([]string{"run", "--memory-swappiness=15", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.swappiness"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -60,6 +88,12 @@ var _ = Describe("Podman run memory", func() { }) It("podman run kernel-memory test", func() { + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if cgroupsv2 { + Skip("Kernel memory not supported on cgroups v2") + } session := podmanTest.Podman([]string{"run", "--kernel-memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 1420a8403..ce2044a72 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -13,6 +13,7 @@ import ( "syscall" "time" + "github.com/containers/libpod/pkg/cgroups" . "github.com/containers/libpod/test/utils" "github.com/containers/storage/pkg/stringid" "github.com/mrunalp/fileutils" @@ -263,9 +264,15 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("1024")) - session = podmanTest.Podman([]string{"run", "--rm", "--oom-kill-disable=true", fedoraMinimal, "echo", "memory-hog"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if !cgroupsv2 { + // --oom-kill-disable not supported on cgroups v2. + session = podmanTest.Podman([]string{"run", "--rm", "--oom-kill-disable=true", fedoraMinimal, "echo", "memory-hog"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + } session = podmanTest.Podman([]string{"run", "--rm", "--oom-score-adj=100", fedoraMinimal, "cat", "/proc/self/oom_score_adj"}) session.WaitWithDefaultTimeout() @@ -310,18 +317,43 @@ var _ = Describe("Podman run", func() { It("podman run blkio-weight test", func() { SkipIfRootless() - if _, err := os.Stat("/sys/fs/cgroup/blkio/blkio.weight"); os.IsNotExist(err) { - Skip("Kernel does not support blkio.weight") + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + if !cgroupsv2 { + if _, err := os.Stat("/sys/fs/cgroup/blkio/blkio.weight"); os.IsNotExist(err) { + Skip("Kernel does not support blkio.weight") + } + } + + if cgroupsv2 { + // convert linearly from [10-1000] to [1-10000] + session := podmanTest.Podman([]string{"run", "--rm", "--blkio-weight=15", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.bfq.weight"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("51")) + } else { + session := podmanTest.Podman([]string{"run", "--rm", "--blkio-weight=15", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.weight"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("15")) } - session := podmanTest.Podman([]string{"run", "--rm", "--blkio-weight=15", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.weight"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("15")) }) It("podman run device-read-bps test", func() { SkipIfRootless() - session := podmanTest.Podman([]string{"run", "--rm", "--device-read-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_bps_device"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var session *PodmanSessionIntegration + + if cgroupsv2 { + session = podmanTest.Podman([]string{"run", "--rm", "--device-read-bps=/dev/zero:1mb", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"}) + } else { + session = podmanTest.Podman([]string{"run", "--rm", "--device-read-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_bps_device"}) + } + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("1048576")) @@ -329,7 +361,17 @@ var _ = Describe("Podman run", func() { It("podman run device-write-bps test", func() { SkipIfRootless() - session := podmanTest.Podman([]string{"run", "--rm", "--device-write-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_bps_device"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var session *PodmanSessionIntegration + + if cgroupsv2 { + session = podmanTest.Podman([]string{"run", "--rm", "--device-write-bps=/dev/zero:1mb", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"}) + } else { + session = podmanTest.Podman([]string{"run", "--rm", "--device-write-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_bps_device"}) + } session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("1048576")) @@ -337,7 +379,18 @@ var _ = Describe("Podman run", func() { It("podman run device-read-iops test", func() { SkipIfRootless() - session := podmanTest.Podman([]string{"run", "--rm", "--device-read-iops=/dev/zero:100", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_iops_device"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var session *PodmanSessionIntegration + + if cgroupsv2 { + session = podmanTest.Podman([]string{"run", "--rm", "--device-read-iops=/dev/zero:100", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"}) + } else { + session = podmanTest.Podman([]string{"run", "--rm", "--device-read-iops=/dev/zero:100", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_iops_device"}) + } + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("100")) @@ -345,7 +398,18 @@ var _ = Describe("Podman run", func() { It("podman run device-write-iops test", func() { SkipIfRootless() - session := podmanTest.Podman([]string{"run", "--rm", "--device-write-iops=/dev/zero:100", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_iops_device"}) + + cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() + Expect(err).To(BeNil()) + + var session *PodmanSessionIntegration + + if cgroupsv2 { + session = podmanTest.Podman([]string{"run", "--rm", "--device-write-iops=/dev/zero:100", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"}) + } else { + session = podmanTest.Podman([]string{"run", "--rm", "--device-write-iops=/dev/zero:100", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_iops_device"}) + } + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("100")) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index cefff0e2c..9e609b434 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -43,4 +43,17 @@ echo $rand | 0 | $rand is "$output" "" "unwanted /sys/kernel in 'mount' output (with --net=host)" } +# 'run --rm' goes through different code paths and may lose exit status. +# See https://github.com/containers/libpod/issues/3795 +@test "podman run --rm" { + skip_if_remote "podman-remote does not handle exit codes" + + run_podman 0 run --rm $IMAGE /bin/true + run_podman 1 run --rm $IMAGE /bin/false + + # Believe it or not, 'sh -c' resulted in different behavior + run_podman 0 run --rm $IMAGE sh -c /bin/true + run_podman 1 run --rm $IMAGE sh -c /bin/false +} + # vim: filetype=sh diff --git a/test/system/055-rm.bats b/test/system/055-rm.bats new file mode 100644 index 000000000..c13c8c52e --- /dev/null +++ b/test/system/055-rm.bats @@ -0,0 +1,42 @@ +#!/usr/bin/env bats -*- bats -*- +# +# tests for podman rm +# + +load helpers + +@test "podman rm" { + rand=$(random_string 30) + run_podman run --name $rand $IMAGE /bin/true + + # Don't care about output, just check exit status (it should exist) + run_podman 0 inspect $rand + + # container should be in output of 'ps -a' + run_podman ps -a + is "$output" ".* $IMAGE .*/true .* $rand" "Container present in 'ps -a'" + + # Remove container; now 'inspect' should fail + run_podman rm $rand + run_podman 125 inspect $rand +} + +# I'm sorry! This test takes 13 seconds. There's not much I can do about it, +# please know that I think it's justified: podman 1.5.0 had a strange bug +# in with exit status was not preserved on some code paths with 'rm -f' +# or 'podman run --rm' (see also 030-run.bats). The test below is a bit +# kludgy: what we care about is the exit status of the killed container, +# not 'podman rm', but BATS has no provision (that I know of) for forking, +# so what we do is start the 'rm' beforehand and monitor the exit status +# of the 'sleep' container. +# +# See https://github.com/containers/libpod/issues/3795 +@test "podman rm -f" { + skip_if_remote "podman-remote does not handle exit codes" + + rand=$(random_string 30) + ( sleep 3; run_podman rm -f $rand ) & + run_podman 137 run --name $rand $IMAGE sleep 30 +} + +# vim: filetype=sh diff --git a/version/version.go b/version/version.go index d5f91210e..f0823f260 100644 --- a/version/version.go +++ b/version/version.go @@ -4,7 +4,7 @@ package version // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -const Version = "1.5.1-dev" +const Version = "1.5.2-dev" // RemoteAPIVersion is the version for the remote // client API. It is used to determine compatibility |