diff options
-rw-r--r-- | .cirrus.yml | 75 | ||||
-rw-r--r-- | cmd/podman/machine/set.go | 60 | ||||
-rw-r--r-- | contrib/cirrus/lib.sh | 53 | ||||
-rwxr-xr-x | contrib/cirrus/logcollector.sh | 43 | ||||
-rwxr-xr-x | contrib/cirrus/runner.sh | 9 | ||||
-rwxr-xr-x | contrib/cirrus/setup_environment.sh | 74 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-set.1.md | 20 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 10 | ||||
-rw-r--r-- | pkg/api/server/register_manifest.go | 157 | ||||
-rw-r--r-- | pkg/bindings/manifests/manifests.go | 91 | ||||
-rw-r--r-- | pkg/bindings/test/manifests_test.go | 2 | ||||
-rw-r--r-- | pkg/machine/config.go | 7 | ||||
-rw-r--r-- | pkg/machine/e2e/config_set.go | 43 | ||||
-rw-r--r-- | pkg/machine/e2e/set_test.go | 139 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 138 | ||||
-rw-r--r-- | pkg/machine/qemu/machine_test.go | 17 | ||||
-rw-r--r-- | pkg/machine/wsl/machine.go | 57 | ||||
-rw-r--r-- | test/apiv2/01-basic.at | 2 | ||||
-rw-r--r-- | test/e2e/run_networking_test.go | 2 | ||||
-rw-r--r-- | version/version.go | 4 |
20 files changed, 626 insertions, 377 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index cf97f4467..958c6b0c7 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,10 +7,6 @@ env: #### # Name of the ultimate destination branch for this CI run, PR or post-merge. DEST_BRANCH: "main" - # Netavark branch to use when TEST_ENVIRON=host-netavark - NETAVARK_BRANCH: "main" - # Aardvark branch to use - AARDVARK_BRANCH: "main" # Overrides default location (/tmp/cirrus) for repo clone GOPATH: &gopath "/var/tmp/go" GOBIN: "${GOPATH}/bin" @@ -24,21 +20,16 @@ env: # Runner statistics log file path/name STATS_LOGFILE_SFX: 'runner_stats.log' STATS_LOGFILE: '$GOSRC/${CIRRUS_TASK_NAME}-${STATS_LOGFILE_SFX}' - # Netavark/aardvark location/options when TEST_ENVIRON=host-netavark - NETAVARK_URL: "https://api.cirrus-ci.com/v1/artifact/github/containers/netavark/success/binary.zip?branch=${NETAVARK_BRANCH}" - NETAVARK_DEBUG: 0 # set non-zero to use the debug-mode binary - AARDVARK_URL: "https://api.cirrus-ci.com/v1/artifact/github/containers/aardvark-dns/success/binary.zip?branch=${AARDVARK_BRANCH}" - AARDVARK_DEBUG: 0 # set non-zero to use the debug-mode binary #### #### Cache-image names to test with (double-quotes around names are critical) #### - FEDORA_NAME: "fedora-35" - PRIOR_FEDORA_NAME: "fedora-34" + FEDORA_NAME: "fedora-36" + PRIOR_FEDORA_NAME: "fedora-35" UBUNTU_NAME: "ubuntu-2110" # Google-cloud VM Images - IMAGE_SUFFIX: "c4831699639992320" + IMAGE_SUFFIX: "c4955393725038592" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" @@ -53,7 +44,7 @@ env: #### N/B: Required ALL of these are set for every single task. #### TEST_FLAVOR: # int, sys, ext_svc, validate, automation, etc. - TEST_ENVIRON: host # 'host', 'host-netavark', or 'container' + TEST_ENVIRON: host # 'host', or 'container' PODBIN_NAME: podman # 'podman' or 'remote' PRIV_NAME: root # 'root' or 'rootless' DISTRO_NV: # any {PRIOR_,}{FEDORA,UBUNTU}_NAME value @@ -161,11 +152,11 @@ build_task: CTR_FQIN: ${FEDORA_CONTAINER_FQIN} # ID for re-use of build output _BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - #- env: &priorfedora_envvars - # DISTRO_NV: ${PRIOR_FEDORA_NAME} - # VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME} - # CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN} - # _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID} + - env: &priorfedora_envvars + DISTRO_NV: ${PRIOR_FEDORA_NAME} + VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME} + CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN} + _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - env: &ubuntu_envvars DISTRO_NV: ${UBUNTU_NAME} VM_IMAGE_NAME: ${UBUNTU_CACHE_IMAGE_NAME} @@ -394,7 +385,7 @@ unit_test_task: - validate matrix: - env: *stdenvars - #- env: *priorfedora_envvars + - env: *priorfedora_envvars - env: *ubuntu_envvars # Special-case: Rootless on latest Fedora (standard) VM - name: "Rootless unit on $DISTRO_NV" @@ -515,11 +506,11 @@ container_integration_test_task: _BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID} VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME} CTR_FQIN: ${FEDORA_CONTAINER_FQIN} - #- env: - # DISTRO_NV: ${PRIOR_FEDORA_NAME} - # _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - # VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME} - # CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN} + - env: + DISTRO_NV: ${PRIOR_FEDORA_NAME} + _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID} + VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME} + CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN} gce_instance: *standardvm timeout_in: 90m env: @@ -553,41 +544,6 @@ rootless_integration_test_task: always: *int_logs_artifacts -# Run various scenarios using upstream netavark/aardvark-dns binaries -netavark_task: - name: "Netavark $TEST_FLAVOR $PODBIN_NAME $PRIV_NAME" - alias: netavark - only_if: *not_build - skip: *branches_and_tags - depends_on: - - unit_test - gce_instance: *standardvm - matrix: - - env: &nenv - DISTRO_NV: ${FEDORA_NAME} - _BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME} - CTR_FQIN: ${FEDORA_CONTAINER_FQIN} - TEST_FLAVOR: int - TEST_ENVIRON: host-netavark - - env: - <<: *nenv - TEST_FLAVOR: int - PRIV_NAME: rootless - - env: - <<: *nenv - TEST_FLAVOR: sys - - env: - <<: *nenv - TEST_FLAVOR: sys - PRIV_NAME: rootless - clone_script: *noop # Comes from cache - gopath_cache: *ro_gopath_cache - setup_script: *setup - main_script: *main - always: *int_logs_artifacts - - # Always run subsequent to integration tests. While parallelism is lost # with runtime, debugging system-test failures can be more challenging # for some golang developers. Otherwise the following tasks run across @@ -841,7 +797,6 @@ success_task: - remote_integration_test - container_integration_test - rootless_integration_test - - netavark - local_system_test - remote_system_test - rootless_system_test diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go index 4c15f1de1..a994c981b 100644 --- a/cmd/podman/machine/set.go +++ b/cmd/podman/machine/set.go @@ -4,6 +4,9 @@ package machine import ( + "fmt" + "os" + "github.com/containers/common/pkg/completion" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/pkg/machine" @@ -23,9 +26,17 @@ var ( ) var ( - setOpts = machine.SetOptions{} + setFlags = SetFlags{} + setOpts = machine.SetOptions{} ) +type SetFlags struct { + CPUs uint64 + DiskSize uint64 + Memory uint64 + Rootful bool +} + func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: setCmd, @@ -34,7 +45,32 @@ func init() { flags := setCmd.Flags() rootfulFlagName := "rootful" - flags.BoolVar(&setOpts.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution") + flags.BoolVar(&setFlags.Rootful, rootfulFlagName, false, "Whether this machine should prefer rootful container execution") + + cpusFlagName := "cpus" + flags.Uint64Var( + &setFlags.CPUs, + cpusFlagName, 0, + "Number of CPUs", + ) + _ = setCmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + + diskSizeFlagName := "disk-size" + flags.Uint64Var( + &setFlags.DiskSize, + diskSizeFlagName, 0, + "Disk size in GB", + ) + + _ = setCmd.RegisterFlagCompletionFunc(diskSizeFlagName, completion.AutocompleteNone) + + memoryFlagName := "memory" + flags.Uint64VarP( + &setFlags.Memory, + memoryFlagName, "m", 0, + "Memory in MB", + ) + _ = setCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) } func setMachine(cmd *cobra.Command, args []string) error { @@ -53,5 +89,23 @@ func setMachine(cmd *cobra.Command, args []string) error { return err } - return vm.Set(vmName, setOpts) + if cmd.Flags().Changed("rootful") { + setOpts.Rootful = &setFlags.Rootful + } + if cmd.Flags().Changed("cpus") { + setOpts.CPUs = &setFlags.CPUs + } + if cmd.Flags().Changed("memory") { + setOpts.Memory = &setFlags.Memory + } + if cmd.Flags().Changed("disk-size") { + setOpts.DiskSize = &setFlags.DiskSize + } + + setErrs, lasterr := vm.Set(vmName, setOpts) + for _, err := range setErrs { + fmt.Fprintf(os.Stderr, "%v\n", err) + } + + return lasterr } diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index 09a255e6f..0f02c166f 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -97,7 +97,7 @@ EPOCH_TEST_COMMIT="$CIRRUS_BASE_SHA" # testing operations on all platforms and versions. This is necessary # to avoid needlessly passing through global/system values across # contexts, such as host->container or root->rootless user -PASSTHROUGH_ENV_RE='(^CI.*)|(^CIRRUS)|(^DISTRO_NV)|(^GOPATH)|(^GOCACHE)|(^GOSRC)|(^SCRIPT_BASE)|(CGROUP_MANAGER)|(OCI_RUNTIME)|(^TEST.*)|(^PODBIN_NAME)|(^PRIV_NAME)|(^ALT_NAME)|(^ROOTLESS_USER)|(SKIP_USERNS)|(.*_NAME)|(.*_FQIN)' +PASSTHROUGH_ENV_RE='(^CI.*)|(^CIRRUS)|(^DISTRO_NV)|(^GOPATH)|(^GOCACHE)|(^GOSRC)|(^SCRIPT_BASE)|(CGROUP_MANAGER)|(OCI_RUNTIME)|(^TEST.*)|(^PODBIN_NAME)|(^PRIV_NAME)|(^ALT_NAME)|(^ROOTLESS_USER)|(SKIP_USERNS)|(.*_NAME)|(.*_FQIN)|(NETWORK_BACKEND)' # Unsafe env. vars for display SECRET_ENV_RE='(ACCOUNT)|(GC[EP]..+)|(SSH)|(PASSWORD)|(TOKEN)' @@ -169,10 +169,6 @@ setup_rootless() { groupadd -g $rootless_gid $ROOTLESS_USER useradd -g $rootless_gid -u $rootless_uid --no-user-group --create-home $ROOTLESS_USER - # We also set up rootless user for image-scp tests (running as root) - if [[ $PRIV_NAME = "rootless" ]]; then - chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOPATH" "$GOSRC" - fi echo "$ROOTLESS_USER ALL=(root) NOPASSWD: ALL" > /etc/sudoers.d/ci-rootless mkdir -p "$HOME/.ssh" "/home/$ROOTLESS_USER/.ssh" @@ -216,20 +212,39 @@ setup_rootless() { install_test_configs() { msg "Installing ./test/registries.conf system-wide." install -v -D -m 644 ./test/registries.conf /etc/containers/ - if [[ "$TEST_ENVIRON" =~ netavark ]]; then - # belt-and-suspenders: any pre-existing CNI config. will spoil - # default use tof netavark (when both are installed). - rm -rf /etc/cni/net.d/* - else - echo "Installing cni config, policy and registry config" - req_env_vars GOSRC SCRIPT_BASE - cd $GOSRC || exit 1 - install -v -D -m 644 ./cni/87-podman-bridge.conflist /etc/cni/net.d/ - # This config must always sort last in the list of networks (podman picks first one - # as the default). This config prevents allocation of network address space used - # by default in google cloud. https://cloud.google.com/vpc/docs/vpc#ip-ranges - install -v -D -m 644 $SCRIPT_BASE/99-do-not-use-google-subnets.conflist /etc/cni/net.d/ - fi +} + +use_cni() { + msg "Unsetting NETWORK_BACKEND for all subsequent environments." + echo "export -n NETWORK_BACKEND" >> /etc/ci_environment + echo "unset NETWORK_BACKEND" >> /etc/ci_environment + export -n NETWORK_BACKEND + unset NETWORK_BACKEND + msg "Installing default CNI configuration" + cd $GOSRC || exit 1 + rm -rvf /etc/cni/net.d + mkdir -p /etc/cni/net.d + install -v -D -m 644 ./cni/87-podman-bridge.conflist \ + /etc/cni/net.d/ + # This config must always sort last in the list of networks (podman picks + # first one as the default). This config prevents allocation of network + # address space used by default in google cloud. + # https://cloud.google.com/vpc/docs/vpc#ip-ranges + install -v -D -m 644 $SCRIPT_BASE/99-do-not-use-google-subnets.conflist \ + /etc/cni/net.d/ +} + +use_netavark() { + msg "Forcing NETWORK_BACKEND=netavark for all subsequent environments." + echo "NETWORK_BACKEND=netavark" >> /etc/ci_environment + export NETWORK_BACKEND=netavark # needed for install_test_configs() + msg "Removing any/all CNI configuration" + rm -rvf /etc/cni/net.d/* + + # TODO: Remove this when netavark/aardvark-dns development slows down + warn "Updating netavark/aardvark-dns to avoid frequent VM image rebuilds" + # N/B: This is coming from updates-testing repo in F36 + lilto dnf update -y netavark aardvark-dns } # Remove all files provided by the distro version of podman. diff --git a/contrib/cirrus/logcollector.sh b/contrib/cirrus/logcollector.sh index 0cfbf7135..d712713b4 100755 --- a/contrib/cirrus/logcollector.sh +++ b/contrib/cirrus/logcollector.sh @@ -40,32 +40,34 @@ case $1 in packages) # These names are common to Fedora and Ubuntu PKG_NAMES=(\ - conmon \ - containernetworking-plugins \ - containers-common \ - criu \ - crun \ - golang \ - podman \ - runc \ - skopeo \ - slirp4netns \ + conmon + containernetworking-plugins + containers-common + criu + crun + golang + podman + runc + skopeo + slirp4netns ) case $OS_RELEASE_ID in fedora) cat /etc/fedora-release PKG_LST_CMD='rpm -q --qf=%{N}-%{V}-%{R}-%{ARCH}\n' PKG_NAMES+=(\ - container-selinux \ - libseccomp \ + aardvark + container-selinux + libseccomp + netavark ) ;; ubuntu) cat /etc/issue PKG_LST_CMD='dpkg-query --show --showformat=${Package}-${Version}-${Architecture}\n' PKG_NAMES+=(\ - cri-o-runc \ - libseccomp2 \ + cri-o-runc + libseccomp2 ) ;; *) bad_os_id_ver ;; @@ -74,19 +76,6 @@ case $1 in echo "Cgroups: " $(stat -f -c %T /sys/fs/cgroup) # Any not-present packages will be listed as such $PKG_LST_CMD "${PKG_NAMES[@]}" | sort -u - - # TODO: Remove this once netavark/aardvark-dns packages are used - if [[ "$TEST_ENVIRON" =~ netavark ]]; then - _npath=/usr/local/libexec/podman/ - for name in netavark aardvark-dns; do - echo "$name binary details:" - if [[ -r "$_npath/${name}.info" ]]; then - cat "$_npath/${name}.info" - else - echo "WARNING: $_npath/${name}.info not found." - fi - done - fi ;; time) # Assumed to be empty/undefined outside of Cirrus-CI (.cirrus.yml) diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index 5d6ba9921..101270703 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -12,7 +12,7 @@ set -eo pipefail # most notably: # # PODBIN_NAME : "podman" (i.e. local) or "remote" -# TEST_ENVIRON : 'host', 'host-netavark', or 'container'; desired environment in which to run +# TEST_ENVIRON : 'host', or 'container'; desired environment in which to run # CONTAINER : 1 if *currently* running inside a container, 0 if host # @@ -449,6 +449,13 @@ if [[ "$PRIV_NAME" == "rootless" ]] && [[ "$UID" -eq 0 ]]; then # https://github.com/containers/podman/issues/10857 rm -rf /var/lib/cni + # This must be done at the last second, otherwise `make` calls + # in setup_environment (as root) will balk about ownership. + msg "Recursively chowning \$GOPATH and \$GOSRC to $ROOTLESS_USER" + if [[ $PRIV_NAME = "rootless" ]]; then + chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOPATH" "$GOSRC" + fi + req_env_vars ROOTLESS_USER msg "Re-executing runner through ssh as user '$ROOTLESS_USER'" msg "************************************************************" diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 742289733..e3eb46783 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -38,13 +38,12 @@ done cp hack/podman-registry /bin -# Make sure cni network plugins directory exists -mkdir -p /etc/cni/net.d - # Some test operations & checks require a git "identity" _gc='git config --file /root/.gitconfig' $_gc user.email "TMcTestFace@example.com" $_gc user.name "Testy McTestface" +# Bypass git safety/security checks when operating in a throwaway environment +git config --system --add safe.directory $GOSRC # Ensure that all lower-level contexts and child-processes have # ready access to higher level orchestration (e.g Cirrus-CI) @@ -84,13 +83,6 @@ case "$CG_FS_TYPE" in else echo "OCI_RUNTIME=runc" >> /etc/ci_environment fi - - # As a general policy CGv1 + runc should coincide with the "older" - # VM Images in CI. Verify this is the case. - if [[ -n "$VM_IMAGE_NAME" ]] && [[ ! "$VM_IMAGE_NAME" =~ prior ]] - then - die "Most recent distro. version should never run with CGv1" - fi fi ;; cgroup2fs) @@ -99,13 +91,6 @@ case "$CG_FS_TYPE" in # which uses runc as the default. warn "Forcing testing with crun instead of runc" echo "OCI_RUNTIME=crun" >> /etc/ci_environment - - # As a general policy CGv2 + crun should coincide with the "newer" - # VM Images in CI. Verify this is the case. - if [[ -n "$VM_IMAGE_NAME" ]] && [[ "$VM_IMAGE_NAME" =~ prior ]] - then - die "Least recent distro. version should never run with CGv2" - fi fi ;; *) die_unknown CG_FS_TYPE @@ -130,6 +115,19 @@ case "$OS_RELEASE_ID" in msg "Enabling container_manage_cgroup" setsebool container_manage_cgroup true fi + + # For release 36 and later, netavark/aardvark is the default + # networking stack for podman. All previous releases only have + # CNI networking available. Upgrading from one to the other is + # not supported at this time. Support execution of the upgrade + # tests in F36 and later, by disabling Netavark and enabling CNI. + if [[ "$OS_RELEASE_VER" -ge 36 ]] && \ + [[ "$TEST_FLAVOR" != "upgrade_test" ]]; + then + use_netavark + else # Fedora < 36, or upgrade testing. + use_cni + fi ;; *) die_unknown OS_RELEASE_ID esac @@ -137,7 +135,7 @@ esac # Required to be defined by caller: The environment where primary testing happens # shellcheck disable=SC2154 case "$TEST_ENVIRON" in - host*) + host) # The e2e tests wrongly guess `--cgroup-manager` option # shellcheck disable=SC2154 if [[ "$CG_FS_TYPE" == "cgroup2fs" ]] || [[ "$PRIV_NAME" == "root" ]] @@ -148,43 +146,6 @@ case "$TEST_ENVIRON" in warn "Forcing CGROUP_MANAGER=cgroupfs" echo "CGROUP_MANAGER=cgroupfs" >> /etc/ci_environment fi - # TODO: For the foreseeable future, need to support running tests - # with and without the latest netavark/aardvark. Once they're more - # stable and widely supported in Fedora, they can be pre-installed - # from its RPM at VM image build-time. - if [[ "$TEST_ENVIRON" =~ netavark ]]; then - for info in "netavark $NETAVARK_BRANCH $NETAVARK_URL $NETAVARK_DEBUG" \ - "aardvark-dns $AARDVARK_BRANCH $AARDVARK_URL $AARDVARK_DEBUG"; do - - read _name _branch _url _debug <<<"$info" - req_env_vars _name _branch _url _debug - msg "Downloading latest $_name from upstream branch '$_branch'" - # Use identifiable archive filename in of a get_ci_env.sh environment - curl --fail --location -o /tmp/$_name.zip "$_url" - - # Needs to be in a specific location - # ref: https://github.com/containers/common/blob/main/pkg/config/config_linux.go#L39 - _pdir=/usr/local/libexec/podman - mkdir -p $_pdir - cd $_pdir - msg "$PWD" - unzip /tmp/$_name.zip - if ((_debug)); then - warn "Using debug $_name binary" - mv $_name.debug $_name - else - rm $_name.debug - fi - chmod 0755 $_pdir/$_name - cd - - done - - restorecon -F -v $_nvdir - # This is critical, it signals to all tests that netavark - # use is expected. - msg "Forcing NETWORK_BACKEND=netavark in all subsequent environments." - echo "NETWORK_BACKEND=netavark" >> /etc/ci_environment - fi ;; container) if ((CONTAINER==0)); then # not yet inside a container @@ -345,6 +306,9 @@ case "$TEST_FLAVOR" in # Guarantee the docker daemon can't be started, even by accident rm -vf $(type -P dockerd) + msg "Recursively chowning source to $ROOTLESS_USER" + chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOPATH" "$GOSRC" + msg "Obtaining necessary gitlab-runner testing bits" slug="gitlab.com/gitlab-org/gitlab-runner" helper_fqin="registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:x86_64-latest-pwsh" diff --git a/docs/source/markdown/podman-machine-set.1.md b/docs/source/markdown/podman-machine-set.1.md index a4918eacf..de90ee4b0 100644 --- a/docs/source/markdown/podman-machine-set.1.md +++ b/docs/source/markdown/podman-machine-set.1.md @@ -8,17 +8,29 @@ podman\-machine\-set - Sets a virtual machine setting ## DESCRIPTION -Sets an updatable virtual machine setting. - -Options mirror values passed to `podman machine init`. Only a limited -subset can be changed after machine initialization. +Change a machine setting. ## OPTIONS +#### **--cpus**=*number* + +Number of CPUs. +Only supported for QEMU machines. + +#### **--disk-size**=*number* + +Size of the disk for the guest VM in GB. +Can only be increased. Only supported for QEMU machines. + #### **--help** Print usage statement. +#### **--memory**, **-m**=*number* + +Memory (in MB). +Only supported for QEMU machines. + #### **--rootful**=*true|false* Whether this machine should prefer rootful (`true`) or rootless (`false`) diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 15d4b9f89..8dc7c57d5 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "net/url" + "strconv" "strings" "github.com/containers/image/v5/docker/reference" @@ -372,6 +373,15 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) { return } + if tlsVerify, ok := r.URL.Query()["tlsVerify"]; ok { + tls, err := strconv.ParseBool(tlsVerify[len(tlsVerify)-1]) + if err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("tlsVerify param is not a bool: %w", err)) + return + } + body.SkipTLSVerify = types.NewOptionalBool(!tls) + } + authconf, authfile, err := auth.GetCredentials(r) if err != nil { utils.Error(w, http.StatusBadRequest, err) diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go index 50a49bc1e..3e3a516f4 100644 --- a/pkg/api/server/register_manifest.go +++ b/pkg/api/server/register_manifest.go @@ -10,6 +10,82 @@ import ( func (s *APIServer) registerManifestHandlers(r *mux.Router) error { v3 := r.PathPrefix("/v{version:[0-3][0-9A-Za-z.-]*}/libpod/manifests").Subrouter() v4 := r.PathPrefix("/v{version:[4-9][0-9A-Za-z.-]*}/libpod/manifests").Subrouter() + // swagger:operation POST /libpod/manifests/{name}/push manifests ManifestPushV3Libpod + // --- + // summary: Push manifest to registry + // description: | + // Push a manifest list or image index to a registry + // + // Deprecated: As of 4.0.0 use ManifestPushLibpod instead + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or ID of the manifest + // - in: query + // name: destination + // type: string + // required: true + // description: the destination for the manifest + // - in: query + // name: all + // description: push all images + // type: boolean + // responses: + // 200: + // schema: + // $ref: "#/definitions/IDResponse" + // 400: + // $ref: "#/responses/BadParamError" + // 404: + // $ref: "#/responses/NoSuchManifest" + // 500: + // $ref: "#/responses/InternalError" + v3.Handle("/{name}/push", s.APIHandler(libpod.ManifestPushV3)).Methods(http.MethodPost) + // swagger:operation POST /libpod/manifests/{name}/registry/{destination} manifests ManifestPushLibpod + // --- + // summary: Push manifest list to registry + // description: | + // Push a manifest list or image index to the named registry + // + // As of v4.0.0 + // produces: + // - application/json + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or ID of the manifest list + // - in: path + // name: destination + // type: string + // required: true + // description: the registry for the manifest list + // - in: query + // name: all + // description: push all images + // type: boolean + // default: false + // - in: query + // name: tlsVerify + // type: boolean + // default: false + // description: skip TLS verification for registries + // responses: + // 200: + // schema: + // $ref: "#/definitions/IDResponse" + // 400: + // $ref: "#/responses/BadParamError" + // 404: + // $ref: "#/responses/NoSuchManifest" + // 500: + // $ref: "#/responses/InternalError" + v4.Handle("/{name:.*}/registry/{destination:.*}", s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost) // swagger:operation POST /libpod/manifests manifests ManifestCreateLibpod // --- // summary: Create @@ -116,6 +192,11 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // type: string // required: true // description: the name or ID of the manifest + // - in: query + // name: tlsVerify + // type: boolean + // default: false + // description: skip TLS verification for registries // - in: body // name: options // description: options for mutating a manifest @@ -221,81 +302,5 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" v4.Handle("/{name:.*}", s.APIHandler(libpod.ManifestDelete)).Methods(http.MethodDelete) - // swagger:operation POST /libpod/manifests/{name}/push manifests ManifestPushV3Libpod - // --- - // summary: Push manifest to registry - // description: | - // Push a manifest list or image index to a registry - // - // Deprecated: As of 4.0.0 use ManifestPushLibpod instead - // produces: - // - application/json - // parameters: - // - in: path - // name: name - // type: string - // required: true - // description: the name or ID of the manifest - // - in: query - // name: destination - // type: string - // required: true - // description: the destination for the manifest - // - in: query - // name: all - // description: push all images - // type: boolean - // responses: - // 200: - // schema: - // $ref: "#/definitions/IDResponse" - // 400: - // $ref: "#/responses/BadParamError" - // 404: - // $ref: "#/responses/NoSuchManifest" - // 500: - // $ref: "#/responses/InternalError" - v3.Handle("/{name}/push", s.APIHandler(libpod.ManifestPushV3)).Methods(http.MethodPost) - // swagger:operation POST /libpod/manifests/{name}/registry/{destination} manifests ManifestPushLibpod - // --- - // summary: Push manifest list to registry - // description: | - // Push a manifest list or image index to the named registry - // - // As of v4.0.0 - // produces: - // - application/json - // parameters: - // - in: path - // name: name - // type: string - // required: true - // description: the name or ID of the manifest list - // - in: path - // name: destination - // type: string - // required: true - // description: the registry for the manifest list - // - in: query - // name: all - // description: push all images - // type: boolean - // default: false - // - in: query - // name: tlsVerify - // type: boolean - // default: false - // description: skip TLS verification for registries - // responses: - // 200: - // schema: - // $ref: "#/definitions/IDResponse" - // 400: - // $ref: "#/responses/BadParamError" - // 404: - // $ref: "#/responses/NoSuchManifest" - // 500: - // $ref: "#/responses/InternalError" - v4.Handle("/{name:.*}/registry/{destination:.*}", s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost) return nil } diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 70b3819f5..828f4922c 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -2,13 +2,11 @@ package manifests import ( "context" - "fmt" "io/ioutil" "net/http" "strconv" "strings" - "github.com/blang/semver" "github.com/containers/image/v5/manifest" imageTypes "github.com/containers/image/v5/types" "github.com/containers/podman/v4/pkg/api/handlers" @@ -17,7 +15,6 @@ import ( "github.com/containers/podman/v4/pkg/bindings/images" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" - "github.com/containers/podman/v4/version" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" ) @@ -95,65 +92,23 @@ func Add(ctx context.Context, name string, options *AddOptions) (string, error) options = new(AddOptions) } - if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) { - optionsv4 := ModifyOptions{ - All: options.All, - Annotations: options.Annotation, - Arch: options.Arch, - Features: options.Features, - Images: options.Images, - OS: options.OS, - OSFeatures: nil, - OSVersion: options.OSVersion, - Variant: options.Variant, - Username: options.Username, - Password: options.Password, - Authfile: options.Authfile, - SkipTLSVerify: options.SkipTLSVerify, - } - optionsv4.WithOperation("update") - return Modify(ctx, name, options.Images, &optionsv4) - } - - // API Version < 4.0.0 - conn, err := bindings.GetClient(ctx) - if err != nil { - return "", err - } - opts, err := jsoniter.MarshalToString(options) - if err != nil { - return "", err - } - reader := strings.NewReader(opts) - - header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword()) - if err != nil { - return "", err - } - - params, err := options.ToParams() - if err != nil { - return "", err - } - // SkipTLSVerify is special. We need to delete the param added by - // ToParams() and change the key and flip the bool - if options.SkipTLSVerify != nil { - params.Del("SkipTLSVerify") - params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) - } - - v := version.APIVersion[version.Libpod][version.MinimalAPI] - header.Add("API-Version", - fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)) - - response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/manifests/%s/add", params, header, name) - if err != nil { - return "", err - } - defer response.Body.Close() - - var idr handlers.IDResponse - return idr.ID, response.Process(&idr) + optionsv4 := ModifyOptions{ + All: options.All, + Annotations: options.Annotation, + Arch: options.Arch, + Features: options.Features, + Images: options.Images, + OS: options.OS, + OSFeatures: nil, + OSVersion: options.OSVersion, + Variant: options.Variant, + Username: options.Username, + Password: options.Password, + Authfile: options.Authfile, + SkipTLSVerify: options.SkipTLSVerify, + } + optionsv4.WithOperation("update") + return Modify(ctx, name, options.Images, &optionsv4) } // Remove deletes a manifest entry from a manifest list. Both name and the digest to be @@ -185,9 +140,6 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt if err != nil { return "", err } - v := version.APIVersion[version.Libpod][version.MinimalAPI] - header.Add("API-Version", - fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)) params, err := options.ToParams() if err != nil { @@ -200,14 +152,7 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } - var response *bindings.APIResponse - if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) { - response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/registry/%s", params, header, name, destination) - } else { - params.Set("image", name) - params.Set("destination", destination) - response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/push", params, header, name) - } + response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/registry/%s", params, header, name, destination) if err != nil { return "", err } diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go index 64becda43..e6c93817d 100644 --- a/pkg/bindings/test/manifests_test.go +++ b/pkg/bindings/test/manifests_test.go @@ -96,7 +96,7 @@ var _ = Describe("podman manifest", func() { Expect(err).To(HaveOccurred()) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + Expect(code).To(BeNumerically("==", http.StatusBadRequest)) }) It("remove digest", func() { diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 5bbaf8c51..833f9cba8 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -95,7 +95,10 @@ type ListResponse struct { } type SetOptions struct { - Rootful bool + CPUs *uint64 + DiskSize *uint64 + Memory *uint64 + Rootful *bool } type SSHOptions struct { @@ -118,7 +121,7 @@ type InspectOptions struct{} type VM interface { Init(opts InitOptions) (bool, error) Remove(name string, opts RemoveOptions) (string, func() error, error) - Set(name string, opts SetOptions) error + Set(name string, opts SetOptions) ([]error, error) SSH(name string, opts SSHOptions) error Start(name string, opts StartOptions) error State(bypass bool) (Status, error) diff --git a/pkg/machine/e2e/config_set.go b/pkg/machine/e2e/config_set.go new file mode 100644 index 000000000..b310ab1b9 --- /dev/null +++ b/pkg/machine/e2e/config_set.go @@ -0,0 +1,43 @@ +package e2e + +import ( + "strconv" +) + +type setMachine struct { + cpus *uint + diskSize *uint + memory *uint + + cmd []string +} + +func (i *setMachine) buildCmd(m *machineTestBuilder) []string { + cmd := []string{"machine", "set"} + if i.cpus != nil { + cmd = append(cmd, "--cpus", strconv.Itoa(int(*i.cpus))) + } + if i.diskSize != nil { + cmd = append(cmd, "--disk-size", strconv.Itoa(int(*i.diskSize))) + } + if i.memory != nil { + cmd = append(cmd, "--memory", strconv.Itoa(int(*i.memory))) + } + cmd = append(cmd, m.name) + i.cmd = cmd + return cmd +} + +func (i *setMachine) withCPUs(num uint) *setMachine { + i.cpus = &num + return i +} +func (i *setMachine) withDiskSize(size uint) *setMachine { + i.diskSize = &size + return i +} + +func (i *setMachine) withMemory(num uint) *setMachine { + i.memory = &num + return i +} diff --git a/pkg/machine/e2e/set_test.go b/pkg/machine/e2e/set_test.go new file mode 100644 index 000000000..4b95bde8e --- /dev/null +++ b/pkg/machine/e2e/set_test.go @@ -0,0 +1,139 @@ +package e2e + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("podman machine set", func() { + var ( + mb *machineTestBuilder + testDir string + ) + + BeforeEach(func() { + testDir, mb = setup() + }) + AfterEach(func() { + teardown(originalHomeDir, testDir, mb) + }) + + It("set machine cpus", func() { + name := randomString(12) + i := new(initMachine) + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(session.ExitCode()).To(Equal(0)) + + set := setMachine{} + setSession, err := mb.setName(name).setCmd(set.withCPUs(2)).run() + Expect(err).To(BeNil()) + Expect(setSession.ExitCode()).To(Equal(0)) + + s := new(startMachine) + startSession, err := mb.setCmd(s).run() + Expect(err).To(BeNil()) + Expect(startSession.ExitCode()).To(Equal(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHComand([]string{"lscpu", "|", "grep", "\"CPU(s):\"", "|", "head", "-1"})).run() + Expect(err).To(BeNil()) + Expect(sshSession2.ExitCode()).To(Equal(0)) + Expect(sshSession2.outputToString()).To(ContainSubstring("2")) + + }) + + It("increase machine disk size", func() { + name := randomString(12) + i := new(initMachine) + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(session.ExitCode()).To(Equal(0)) + + set := setMachine{} + setSession, err := mb.setName(name).setCmd(set.withDiskSize(102)).run() + Expect(err).To(BeNil()) + Expect(setSession.ExitCode()).To(Equal(0)) + + s := new(startMachine) + startSession, err := mb.setCmd(s).run() + Expect(err).To(BeNil()) + Expect(startSession.ExitCode()).To(Equal(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHComand([]string{"sudo", "fdisk", "-l", "|", "grep", "Disk"})).run() + Expect(err).To(BeNil()) + Expect(sshSession2.ExitCode()).To(Equal(0)) + Expect(sshSession2.outputToString()).To(ContainSubstring("102 GiB")) + }) + + It("decrease machine disk size should fail", func() { + name := randomString(12) + i := new(initMachine) + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(session.ExitCode()).To(Equal(0)) + + set := setMachine{} + setSession, _ := mb.setName(name).setCmd(set.withDiskSize(50)).run() + // TODO seems like stderr is not being returned; re-enabled when fixed + // Expect(err).To(BeNil()) + Expect(setSession.ExitCode()).To(Not(Equal(0))) + }) + + It("set machine ram", func() { + + name := randomString(12) + i := new(initMachine) + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(session.ExitCode()).To(Equal(0)) + + set := setMachine{} + setSession, err := mb.setName(name).setCmd(set.withMemory(4000)).run() + Expect(err).To(BeNil()) + Expect(setSession.ExitCode()).To(Equal(0)) + + s := new(startMachine) + startSession, err := mb.setCmd(s).run() + Expect(err).To(BeNil()) + Expect(startSession.ExitCode()).To(Equal(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHComand([]string{"cat", "/proc/meminfo", "|", "numfmt", "--field", "2", "--from-unit=Ki", "--to-unit=Mi", "|", "sed", "'s/ kB/M/g'", "|", "grep", "MemTotal"})).run() + Expect(err).To(BeNil()) + Expect(sshSession2.ExitCode()).To(Equal(0)) + Expect(sshSession2.outputToString()).To(ContainSubstring("3824")) + }) + + It("no settings should change if no flags", func() { + name := randomString(12) + i := new(initMachine) + session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() + Expect(err).To(BeNil()) + Expect(session.ExitCode()).To(Equal(0)) + + set := setMachine{} + setSession, err := mb.setName(name).setCmd(&set).run() + Expect(err).To(BeNil()) + Expect(setSession.ExitCode()).To(Equal(0)) + + s := new(startMachine) + startSession, err := mb.setCmd(s).run() + Expect(err).To(BeNil()) + Expect(startSession.ExitCode()).To(Equal(0)) + + ssh2 := sshMachine{} + sshSession2, err := mb.setName(name).setCmd(ssh2.withSSHComand([]string{"lscpu", "|", "grep", "\"CPU(s):\"", "|", "head", "-1"})).run() + Expect(err).To(BeNil()) + Expect(sshSession2.ExitCode()).To(Equal(0)) + Expect(sshSession2.outputToString()).To(ContainSubstring("1")) + + ssh3 := sshMachine{} + sshSession3, err := mb.setName(name).setCmd(ssh3.withSSHComand([]string{"sudo", "fdisk", "-l", "|", "grep", "Disk"})).run() + Expect(err).To(BeNil()) + Expect(sshSession3.ExitCode()).To(Equal(0)) + Expect(sshSession3.outputToString()).To(ContainSubstring("100 GiB")) + }) + +}) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 5481bad29..091c9f8d1 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -390,25 +390,9 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { if err != nil { return false, err } - // Resize the disk image to input disk size - // only if the virtualdisk size is less than - // the given disk size - if opts.DiskSize<<(10*3) > originalDiskSize { - // Find the qemu executable - cfg, err := config.Default() - if err != nil { - return false, err - } - resizePath, err := cfg.FindHelperBinary("qemu-img", true) - if err != nil { - return false, err - } - resize := exec.Command(resizePath, []string{"resize", v.getImageFile(), strconv.Itoa(int(opts.DiskSize)) + "G"}...) - resize.Stdout = os.Stdout - resize.Stderr = os.Stderr - if err := resize.Run(); err != nil { - return false, errors.Errorf("resizing image: %q", err) - } + + if err := v.resizeDisk(opts.DiskSize, originalDiskSize>>(10*3)); err != nil { + return false, err } // If the user provides an ignition file, we need to // copy it into the conf dir @@ -432,14 +416,14 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { return err == nil, err } -func (v *MachineVM) Set(_ string, opts machine.SetOptions) error { - if v.Rootful == opts.Rootful { - return nil - } +func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) { + // If one setting fails to be applied, the others settings will not fail and still be applied. + // The setting(s) that failed to be applied will have its errors returned in setErrors + var setErrors []error state, err := v.State(false) if err != nil { - return err + return setErrors, err } if state == machine.Running { @@ -447,26 +431,45 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) error { if v.Name != machine.DefaultMachineName { suffix = " " + v.Name } - return errors.Errorf("cannot change setting while the vm is running, run 'podman machine stop%s' first", suffix) + return setErrors, errors.Errorf("cannot change settings while the vm is running, run 'podman machine stop%s' first", suffix) } - changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") - if err != nil { - return err + if opts.Rootful != nil && v.Rootful != *opts.Rootful { + if err := v.setRootful(*opts.Rootful); err != nil { + setErrors = append(setErrors, errors.Wrapf(err, "failed to set rootful option")) + } else { + v.Rootful = *opts.Rootful + } } - if changeCon { - newDefault := v.Name - if opts.Rootful { - newDefault += "-root" - } - if err := machine.ChangeDefault(newDefault); err != nil { - return err + if opts.CPUs != nil && v.CPUs != *opts.CPUs { + v.CPUs = *opts.CPUs + v.editCmdLine("-smp", strconv.Itoa(int(v.CPUs))) + } + + if opts.Memory != nil && v.Memory != *opts.Memory { + v.Memory = *opts.Memory + v.editCmdLine("-m", strconv.Itoa(int(v.Memory))) + } + + if opts.DiskSize != nil && v.DiskSize != *opts.DiskSize { + if err := v.resizeDisk(*opts.DiskSize, v.DiskSize); err != nil { + setErrors = append(setErrors, errors.Wrapf(err, "failed to resize disk")) + } else { + v.DiskSize = *opts.DiskSize } } - v.Rootful = opts.Rootful - return v.writeConfig() + err = v.writeConfig() + if err != nil { + setErrors = append(setErrors, err) + } + + if len(setErrors) > 0 { + return setErrors, setErrors[0] + } + + return setErrors, nil } // Start executes the qemu command line and forks it @@ -1462,3 +1465,64 @@ func (v *MachineVM) getImageFile() string { func (v *MachineVM) getIgnitionFile() string { return v.IgnitionFilePath.GetPath() } + +//resizeDisk increases the size of the machine's disk in GB. +func (v *MachineVM) resizeDisk(diskSize uint64, oldSize uint64) error { + // Resize the disk image to input disk size + // only if the virtualdisk size is less than + // the given disk size + if diskSize < oldSize { + return errors.Errorf("new disk size must be larger than current disk size: %vGB", oldSize) + } + + // Find the qemu executable + cfg, err := config.Default() + if err != nil { + return err + } + resizePath, err := cfg.FindHelperBinary("qemu-img", true) + if err != nil { + return err + } + resize := exec.Command(resizePath, []string{"resize", v.getImageFile(), strconv.Itoa(int(diskSize)) + "G"}...) + resize.Stdout = os.Stdout + resize.Stderr = os.Stderr + if err := resize.Run(); err != nil { + return errors.Errorf("resizing image: %q", err) + } + + return nil +} + +func (v *MachineVM) setRootful(rootful bool) error { + changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") + if err != nil { + return err + } + + if changeCon { + newDefault := v.Name + if rootful { + newDefault += "-root" + } + err := machine.ChangeDefault(newDefault) + if err != nil { + return err + } + } + return nil +} + +func (v *MachineVM) editCmdLine(flag string, value string) { + found := false + for i, val := range v.CmdLine { + if val == flag { + found = true + v.CmdLine[i+1] = value + break + } + } + if !found { + v.CmdLine = append(v.CmdLine, []string{flag, value}...) + } +} diff --git a/pkg/machine/qemu/machine_test.go b/pkg/machine/qemu/machine_test.go new file mode 100644 index 000000000..62ca6068a --- /dev/null +++ b/pkg/machine/qemu/machine_test.go @@ -0,0 +1,17 @@ +package qemu + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEditCmd(t *testing.T) { + vm := new(MachineVM) + vm.CmdLine = []string{"command", "-flag", "value"} + + vm.editCmdLine("-flag", "newvalue") + vm.editCmdLine("-anotherflag", "anothervalue") + + require.Equal(t, vm.CmdLine, []string{"command", "-flag", "newvalue", "-anotherflag", "anothervalue"}) +} diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index f57dbd299..1f1f2dcaf 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -736,28 +736,34 @@ func pipeCmdPassThrough(name string, input string, arg ...string) error { return cmd.Run() } -func (v *MachineVM) Set(name string, opts machine.SetOptions) error { - if v.Rootful == opts.Rootful { - return nil +func (v *MachineVM) Set(_ string, opts machine.SetOptions) ([]error, error) { + // If one setting fails to be applied, the others settings will not fail and still be applied. + // The setting(s) that failed to be applied will have its errors returned in setErrors + var setErrors []error + + if opts.Rootful != nil && v.Rootful != *opts.Rootful { + err := v.setRootful(*opts.Rootful) + if err != nil { + setErrors = append(setErrors, errors.Wrapf(err, "error setting rootful option")) + } else { + v.Rootful = *opts.Rootful + } } - changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") - if err != nil { - return err + if opts.CPUs != nil { + setErrors = append(setErrors, errors.Errorf("changing CPUs not suppored for WSL machines")) } - if changeCon { - newDefault := v.Name - if opts.Rootful { - newDefault += "-root" - } - if err := machine.ChangeDefault(newDefault); err != nil { - return err - } + if opts.Memory != nil { + setErrors = append(setErrors, errors.Errorf("changing memory not suppored for WSL machines")) + } - v.Rootful = opts.Rootful - return v.writeConfig() + if opts.DiskSize != nil { + setErrors = append(setErrors, errors.Errorf("changing Disk Size not suppored for WSL machines")) + } + + return setErrors, v.writeConfig() } func (v *MachineVM) Start(name string, _ machine.StartOptions) error { @@ -1362,3 +1368,22 @@ func (p *Provider) IsValidVMName(name string) (bool, error) { func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) { return false, "", nil } + +func (v *MachineVM) setRootful(rootful bool) error { + changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") + if err != nil { + return err + } + + if changeCon { + newDefault := v.Name + if rootful { + newDefault += "-root" + } + err := machine.ChangeDefault(newDefault) + if err != nil { + return err + } + } + return nil +} diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 2747ccbd4..e4348a9a7 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -19,7 +19,7 @@ for i in /version version; do t GET $i 200 \ .Components[0].Name="Podman Engine" \ .Components[0].Details.APIVersion~4[0-9.-]\\+ \ - .Components[0].Details.MinAPIVersion=3.1.0 \ + .Components[0].Details.MinAPIVersion=4.0.0 \ .Components[0].Details.Os=linux \ .ApiVersion=1.40 \ .MinAPIVersion=1.24 \ diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 49c387227..baec444e4 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -1133,6 +1133,8 @@ EXPOSE 2004-2005/tcp`, ALPINE) }) It("podman run with ipam none driver", func() { + // Test fails, issue #13931 + SkipIfNetavark(podmanTest) net := "ipam" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", "--ipam-driver=none", net}) session.WaitWithDefaultTimeout() diff --git a/version/version.go b/version/version.go index c6bd2c239..5b2ca0d74 100644 --- a/version/version.go +++ b/version/version.go @@ -27,7 +27,7 @@ const ( // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -var Version = semver.MustParse("4.0.0-dev") +var Version = semver.MustParse("4.1.0-dev") // See https://docs.docker.com/engine/api/v1.40/ // libpod compat handlers are expected to honor docker API versions @@ -38,7 +38,7 @@ var Version = semver.MustParse("4.0.0-dev") var APIVersion = map[Tree]map[Level]semver.Version{ Libpod: { CurrentAPI: Version, - MinimalAPI: semver.MustParse("3.1.0"), + MinimalAPI: semver.MustParse("4.0.0"), }, Compat: { CurrentAPI: semver.MustParse("1.40.0"), |