From 0f11984982a15a9a8f5d8e0e987ed4c5413d0928 Mon Sep 17 00:00:00 2001 From: Vishwas Siravara Date: Sun, 19 Mar 2023 16:36:19 -0700 Subject: [PATCH] feat: Shape feat for test (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add workflow to sync finch submodules Signed-off-by: Vishwas Siravara * fix: print debug logs when lima disk command fails (#270) ## Before ```sh ➜ finch git:(main) ✗ ./_output/bin/finch vm init FATA[0000] exit status 1 ``` ## After ```sh ➜ finch git:(disk-combined-output) ✗ ./_output/bin/finch vm init FATA[0000] failed to create disk, debug logs: time="2023-03-07T13:35:02-08:00" level=fatal msg="disk \"finch\" already exists (\"/Users/davidhyc/dev/runfinch/finch/_output/lima/data/_disks/finch\")" ``` ## Notes The concept behind this PR is from: /~https://github.com/runfinch/finch/blob/674b3794fe5f5902c264d5327024fb92e147e60f/cmd/finch/virtual_machine_init.go#L97-L101 ## License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Hsing-Yu (David) Chen * fix: print debug logs after newline (#273) ## Why It increases readability because debug logs can be multi-line. ## Before ```sh ➜ finch git:(main) ./_output/bin/finch vm init INFO[0000] Initializing and starting Finch virtual machine... ERRO[0000] Finch virtual machine failed to start, debug logs: time="2023-03-07T14:50:40-08:00" level=info msg="Terminal is not available, proceeding without opening an editor" time="2023-03-07T14:50:40-08:00" level=fatal msg="field `images[0].digest` is invalid: sha256:156de3fd8a0c7e80dea9054aa9a0873e111efc16e5d8519929f913a1ca5ae9: invalid checksum digest length" FATA[0000] exit status 1 ``` ## After ```sh ➜ finch git:(debug-logs-newline) ✗ ./_output/bin/finch vm init INFO[0000] Initializing and starting Finch virtual machine... ERRO[0000] Finch virtual machine failed to start, debug logs: time="2023-03-07T14:49:45-08:00" level=info msg="Terminal is not available, proceeding without opening an editor" time="2023-03-07T14:49:45-08:00" level=fatal msg="field `images[0].digest` is invalid: sha256:156de3fd8a0c7e80dea9054aa9a0873e111efc16e5d8519929f913a1ca5ae9: invalid checksum digest length" FATA[0000] exit status 1 ``` ## License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Hsing-Yu (David) Chen * ci: use the Go version in go.mod instead of a hard-coded one (#276) ## Summary PR is a follow-up of #257. ## License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Hsing-Yu (David) Chen * docs: make 'git pull' automatically update submodules (#274) ## Why Before this PR, if the pinned commit of any submodule is updated in the remote tracking branch, folks need to explicitly add `--recurse` when running `git pull` to also update the submodule to the updated commit. Regarding our usage of submodules, since we only consume upstream updates (i.e., not modifying them), and there're not many submodules, which means that recursively updating them won't take a lot of time, it's likely that we always want to recursively update all the submodules when running `git pull`. ## Steps to Reproduce Setup: ```sh git clone --recurse-submodules --branch v0.4.0 /~https://github.com/runfinch/finch.git cd finch git checkout -b demo git branch --set-upstream-to origin/main git pull ``` Submodules are not updated: ```sh ➜ finch git:(demo) ✗ git --no-pager diff diff --git a/deps/finch-core b/deps/finch-core index eef2102..01e6162 160000 --- a/deps/finch-core +++ b/deps/finch-core @@ -1 +1 @@ -Subproject commit eef21029b89d7db00bddc6e426e4405c920b13ed +Subproject commit 01e6162d6fd76fddb9d8ef59845c782a0b6ebafd ``` Fix it: ```sh ➜ finch git:(demo) ✗ git config submodule.recurse true ➜ finch git:(demo) ✗ git pull Fetching submodule deps/finch-core Fetching submodule deps/finch-core/src/lima Fetching submodule deps/finch-core/src/socket_vmnet Already up to date. Submodule path 'deps/finch-core': checked out 'eef21029b89d7db00bddc6e426e4405c920b13ed' Submodule path 'deps/finch-core/src/socket_vmnet': checked out 'ee27d206872fc861c2993264be93d2ccc2740f9c' ➜ finch git:(demo) git --no-pager diff ➜ finch git:(demo) echo $? 0 ``` ## Notes If `submodule.stickyRecursiveClone` is set, and the repository is cloned with `--recurse-submodules`, it'd also work, but it does not account for the case when the repository is already cloned without `--recurse-submodules`, so setting `git config submodule.recurse true` seems to be the simplest way that works for all scenarios. ## License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Hsing-Yu (David) Chen * ci: Add workflow to sync finch submodules (#271) Issue #, if available: N/A *Description of changes:* Add workflow to update submodules in `finch`. #### Details 1. Workflow runs daily at 9am UTC. 2. Has a `workflow_dispatch` trigger for manually running the workflow in addition to the regular daily cadence. *Testing done:* Yes. See /~https://github.com/vsiravar/finch-public/pull/2 Test with updating `FINCH_OS_BASENAME` : /~https://github.com/vsiravar/finch-public/pull/4/files#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52 - [X] I've reviewed the guidance in CONTRIBUTING.md #### License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Signed-off-by: Vishwas Siravara Signed-off-by: Vishwas Siravara * PR 1 with a new interface for testing reverting a PR * PR 2 which uses previoulsy created interface in PR1 * build(deps): Bump submodules (#281) Automated changes by [create-pull-request](/~https://github.com/peter-evans/create-pull-request) GitHub action Signed-off-by: GitHub Co-authored-by: vsiravar * test: Improve test coverage for additional disk feature (#280) Issue #, if available: Improving the e2e test coverage for additional disk. *Description of changes:* Adding test to retaining volume, network, and restart the container after the VM is removed *Testing done:* - [x] I've reviewed the guidance in CONTRIBUTING.md #### License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Signed-off-by: Ang Zhou * ci: add sanity test for running multiple VM instances in release test (#278) Issue #, if available: *Description of changes:* *Testing done:* /~https://github.com/runfinch/finch/actions/runs/4368693657/workflow /~https://github.com/runfinch/finch/actions/runs/4367945800 - [x] I've reviewed the guidance in CONTRIBUTING.md #### License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Signed-off-by: Anqi Pang * ci: Add schedule to update-deps workflow (#292) Issue #, if available: *Description of changes:* Schedule [update dependencies](/~https://github.com/runfinch/finch/blob/main/.github/workflows/update-deps.yaml) workflow which is currently triggered only by `workflow_dispatch`. This workflow will run after dependencies are built in `finch-core` by /~https://github.com/runfinch/finch-core/blob/main/.github/workflows/release.yaml(runs at 9 am UTC on Tuesday) at 11 am UTC on Tuesday. *Testing done:* Yes. /~https://github.com/runfinch/finch/pull/291 - [X] I've reviewed the guidance in CONTRIBUTING.md #### License Acceptance By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. Signed-off-by: Vishwas Siravara * build(deps): bump github.com/onsi/ginkgo/v2 from 2.9.0 to 2.9.1 (#285) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump github.com/runfinch/common-tests from 0.6.1 to 0.6.2 (#300) Bumps [github.com/runfinch/common-tests](/~https://github.com/runfinch/common-tests) from 0.6.1 to 0.6.2.
Release notes

Sourced from github.com/runfinch/common-tests's releases.

v0.6.2

0.6.2 (2023-03-16)

Bug Fixes

  • Fix tests to match nerdctl 1.2.1 outputs (#50) (3d9b4f4)

Build System or External Dependencies

  • deps: bump github.com/onsi/ginkgo/v2 from 2.8.3 to 2.8.4 (#41) (a9476c1)
  • deps: bump github.com/onsi/gomega from 1.27.1 to 1.27.2 (#40) (e8fc71a)
Changelog

Sourced from github.com/runfinch/common-tests's changelog.

0.6.2 (2023-03-16)

Bug Fixes

  • Fix tests to match nerdctl 1.2.1 outputs (#50) (3d9b4f4)

Build System or External Dependencies

  • deps: bump github.com/onsi/ginkgo/v2 from 2.8.3 to 2.8.4 (#41) (a9476c1)
  • deps: bump github.com/onsi/gomega from 1.27.1 to 1.27.2 (#40) (e8fc71a)
Commits
  • da6f643 chore(main): release 0.6.2 (#44)
  • 3d9b4f4 fix: Fix tests to match nerdctl 1.2.1 outputs (#50)
  • e8fc71a build(deps): bump github.com/onsi/gomega from 1.27.1 to 1.27.2 (#40)
  • a9476c1 build(deps): bump github.com/onsi/ginkgo/v2 from 2.8.3 to 2.8.4 (#41)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/runfinch/common-tests&package-manager=go_modules&previous-version=0.6.1&new-version=0.6.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): Bump lima version (#302) Automated changes by [create-pull-request](/~https://github.com/peter-evans/create-pull-request) GitHub action Signed-off-by: GitHub Co-authored-by: vsiravar * build(deps): Bump submodules (#304) Automated changes by [create-pull-request](/~https://github.com/peter-evans/create-pull-request) GitHub action Signed-off-by: GitHub Co-authored-by: ahsan-z-khan --------- Signed-off-by: Vishwas Siravara Signed-off-by: Hsing-Yu (David) Chen Signed-off-by: Vishwas Siravara Signed-off-by: GitHub Signed-off-by: Ang Zhou Signed-off-by: Anqi Pang Signed-off-by: dependabot[bot] Co-authored-by: Hsing-Yu (David) Chen Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: vsiravar Co-authored-by: Ang Zhou <40868185+azhouwd@users.noreply.github.com> Co-authored-by: Anqi Pang <55934700+AnqiPang@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ahsan-z-khan --- .github/bin/update-os-image.sh | 24 +++++++ .github/workflows/release-homebrew.yaml | 61 +++++++++++++++- .github/workflows/release-installer.yaml | 91 ++++++++++++++++++++++-- .github/workflows/sync-submodules.yaml | 27 +++++++ .github/workflows/update-deps.yaml | 2 + CONTRIBUTING.md | 8 ++- Makefile | 8 +-- cmd/finch/main.go | 32 +++++++++ cmd/finch/virtual_machine_init.go | 2 +- cmd/finch/virtual_machine_init_test.go | 2 +- cmd/finch/virtual_machine_remove.go | 2 +- cmd/finch/virtual_machine_remove_test.go | 2 +- cmd/finch/virtual_machine_start.go | 2 +- cmd/finch/virtual_machine_start_test.go | 2 +- cmd/finch/virtual_machine_stop.go | 2 +- cmd/finch/virtual_machine_stop_test.go | 2 +- deps/finch-core | 2 +- e2e/vm/additional_disk_test.go | 42 +++++++---- go.mod | 8 +-- go.sum | 15 ++-- pkg/disk/disk.go | 10 ++- pkg/disk/disk_test.go | 6 +- 22 files changed, 303 insertions(+), 49 deletions(-) create mode 100755 .github/bin/update-os-image.sh create mode 100644 .github/workflows/sync-submodules.yaml diff --git a/.github/bin/update-os-image.sh b/.github/bin/update-os-image.sh new file mode 100755 index 000000000..295d64efe --- /dev/null +++ b/.github/bin/update-os-image.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -euxo pipefail +# Set OS hash directory in finch-core as base directory for searching latest OS images. +OS_BASE_IMAGE_HASH_DIR="./deps/finch-core/hashes/" + +OS_AARCH64_FILENAME_PATTERN="Fedora-Cloud-Base-.*-.*.aarch64-.*.qcow2" + +OS_X86_64_FILENAME_PATTERN="Fedora-Cloud-Base-.*-.*.x86_64-.*.qcow2" + + +# Use wildcard patterns to search for the two files and assign their paths to variables +AARCH64_FILEPATH=$(find "$OS_BASE_IMAGE_HASH_DIR" -name "Fedora-Cloud-Base-*-*.aarch64-*.qcow2.sha512" -print -quit) +X86_64_FILEPATH=$(find "$OS_BASE_IMAGE_HASH_DIR" -name "Fedora-Cloud-Base-*-*.x86_64-*.qcow2.sha512" -print -quit) + +# Extract the file names without the path and remove the ".sha512" extension +AARCH64_OS_BASE_IMAGE=$(basename "$AARCH64_FILEPATH" .sha512) +X86_64_OS_BASE_IMAGE=$(basename "$X86_64_FILEPATH" .sha512) + +echo "AARCH64 base image: ${AARCH64_OS_BASE_IMAGE}" +echo "X86_64 base image: ${X86_64_OS_BASE_IMAGE}" + +# Replace occurrences of FINCH_OS_BASENAME in the Makefile with the file names +sed -E -i.bak 's|^([[:blank:]]*FINCH_OS_BASENAME[[:blank:]]*\?=[[:blank:]]*)('"${OS_AARCH64_FILENAME_PATTERN}"')|\1'"$AARCH64_OS_BASE_IMAGE"'|' Makefile +sed -E -i.bak 's|^([[:blank:]]*FINCH_OS_BASENAME[[:blank:]]*\?=[[:blank:]]*)('"${OS_X86_64_FILENAME_PATTERN}"')|\1'"$X86_64_OS_BASE_IMAGE"'|' Makefile diff --git a/.github/workflows/release-homebrew.yaml b/.github/workflows/release-homebrew.yaml index 5ef2df351..9b72de138 100644 --- a/.github/workflows/release-homebrew.yaml +++ b/.github/workflows/release-homebrew.yaml @@ -59,6 +59,9 @@ jobs: - uses: actions/checkout@v3 with: ref: ${{ env.FINCH_TAG }} + fetch-depth: 0 + persist-credentials: false + submodules: true - name: Clean up previous files run: | sudo rm -rf /opt/finch @@ -85,8 +88,36 @@ jobs: brew bump-cask-pr --write-only -f --version=$FINCH_VERSION finch - name: Silently install run: | + export HOMEBREW_NO_INSTALL_FROM_API=1 cd $(brew --repo homebrew/cask) brew install --cask ./Casks/finch.rb + - name: Build project + run: | + brew install go lz4 automake autoconf libtool + export PATH="/opt/homebrew/opt/libtool/libexec/gnubin:$PATH" + make + - name: Multiple instances of Finch test + run: | + # start two Finch VM instances + ./_output/bin/finch vm init + finch vm init + # start a container in each VM instance + ./_output/bin/finch pull alpine + finch pull alpine + ./_output/bin/finch run --name test-ctr1 alpine + finch run --name test-ctr2 alpine + # clean up the VMs + ./_output/bin/finch vm stop && ./_output/bin/finch vm remove + finch vm stop && finch vm remove + - name: Clean up multiple instance test + run: | + sudo rm -rf ./_output + export HOMEBREW_NO_INSTALL_FROM_API=1 + cd $(brew --repo homebrew/cask) + # Need to reinstall because there were errors on arm64 11.7 and arm64 12.6 hosts after running multiple instances tests, + # that caused the VM initialization failure in the e2e test. + # Example workflow run /~https://github.com/runfinch/finch/actions/runs/4367457552/jobs/7638794529 + brew reinstall --cask ./Casks/finch.rb - name: Run e2e tests run: INSTALLED=true make test-e2e - name: Silently uninstall @@ -124,6 +155,9 @@ jobs: - uses: actions/checkout@v3 with: ref: ${{ env.FINCH_TAG }} + fetch-depth: 0 + persist-credentials: false + submodules: true - name: Clean up previous files run: | sudo rm -rf /opt/finch @@ -148,8 +182,33 @@ jobs: brew bump-cask-pr --write-only -f --version=$FINCH_VERSION finch - name: Silently install run: | + export HOMEBREW_NO_INSTALL_FROM_API=1 cd $(brew --repo homebrew/cask) brew install --cask ./Casks/finch.rb + - name: Build project + run: | + brew install go lz4 automake autoconf libtool + export PATH="/opt/homebrew/opt/libtool/libexec/gnubin:$PATH" + make + - name: Multiple instances of Finch test + run: | + # start two Finch VM instances + ./_output/bin/finch vm init + finch vm init + # start a container in each VM instance + ./_output/bin/finch pull alpine + finch pull alpine + ./_output/bin/finch run --name test-ctr1 alpine + finch run --name test-ctr2 alpine + # clean up the VMs + ./_output/bin/finch vm stop && ./_output/bin/finch vm remove + finch vm stop && finch vm remove + - name: Clean up multiple instance test + run: | + sudo rm -rf ./_output + export HOMEBREW_NO_INSTALL_FROM_API=1 + cd $(brew --repo homebrew/cask) + brew reinstall --cask ./Casks/finch.rb - name: Run e2e tests run: INSTALLED=true make test-e2e - name: Silently uninstall @@ -161,7 +220,7 @@ jobs: echo ERROR: Finch is not uninstalled exit 1 fi - + pr-to-homebrew: needs: [get-latest-tag, macos-arm64-test-installer, macos-amd64-test-installer] runs-on: macos-latest diff --git a/.github/workflows/release-installer.yaml b/.github/workflows/release-installer.yaml index 8a81606fa..2703500f5 100644 --- a/.github/workflows/release-installer.yaml +++ b/.github/workflows/release-installer.yaml @@ -41,12 +41,20 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.19.x - - uses: actions/checkout@v3 + go-version-file: go.mod + cache: true + - name: Checkout the tag + uses: actions/checkout@v3 + with: + ref: ${{ github.ref_name }} + fetch-depth: 0 + persist-credentials: false + submodules: true - name: Clean up previous files run: | sudo rm -rf /opt/finch sudo rm -rf ~/.finch + sudo rm -rf ./_output if pgrep '^qemu-system'; then sudo pkill '^qemu-system' fi @@ -64,6 +72,41 @@ jobs: aws s3 cp s3://${{ secrets.INSTALLER_PRIVATE_BUCKET_NAME }}/Finch-${GITHUB_REF_NAME}-aarch64.pkg Finch-${GITHUB_REF_NAME}-aarch64.pkg - name: Silently install run: sudo installer -pkg Finch-${GITHUB_REF_NAME}-aarch64.pkg -target / + - name: Build project + run: | + brew install go lz4 automake autoconf libtool + export PATH="/opt/homebrew/opt/libtool/libexec/gnubin:$PATH" + make + - name: Multiple instances of Finch test + run: | + # start two Finch VM instances + ./_output/bin/finch vm init + finch vm init + # start a container in each VM instance + ./_output/bin/finch pull alpine + finch pull alpine + ./_output/bin/finch run --name test-ctr1 alpine + finch run --name test-ctr2 alpine + # check whether containers exist + if ! ./_output/bin/finch ps -a | grep 'test-ctr1'; then + echo "ERROR: The container test-ctr1 doesn't exist" + exit 1 + fi + if ! finch ps -a | grep 'test-ctr2'; then + echo "ERROR: The container test-ctr2 doesn't exist" + exit 1 + fi + # clean up the VMs + ./_output/bin/finch vm stop && ./_output/bin/finch vm remove + finch vm stop && finch vm remove + - name: Clean up multiple instance test + run: | + sudo rm -rf ./_output + echo 'y' | sudo bash /Applications/Finch/uninstall.sh + # Need to reinstall because there were errors on arm64 11.7 and arm64 12.6 hosts after running multiple instances tests, + # that caused the VM initialization failure in the e2e test. + # Example workflow run /~https://github.com/runfinch/finch/actions/runs/4367457552/jobs/7638794529 + sudo installer -pkg Finch-${GITHUB_REF_NAME}-aarch64.pkg -target / - name: Run e2e tests run: INSTALLED=true make test-e2e - name: Silently uninstall @@ -88,12 +131,20 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.19.x - - uses: actions/checkout@v3 + go-version-file: go.mod + cache: true + - name: Checkout the tag + uses: actions/checkout@v3 + with: + ref: ${{ github.ref_name }} + fetch-depth: 0 + persist-credentials: false + submodules: true - name: Clean up previous files run: | sudo rm -rf /opt/finch sudo rm -rf ~/.finch + sudo rm -rf ./_output if pgrep '^qemu-system'; then sudo pkill '^qemu-system' fi @@ -112,6 +163,38 @@ jobs: - name: Silently install run: | sudo installer -pkg Finch-${GITHUB_REF_NAME}-x86_64.pkg -target / + - name: Build project + run: | + brew install go lz4 automake autoconf libtool + export PATH="/opt/homebrew/opt/libtool/libexec/gnubin:$PATH" + make + - name: Multiple instances of Finch test + run: | + # start two Finch VM instances + ./_output/bin/finch vm init + finch vm init + # start a container in each VM instance + ./_output/bin/finch pull alpine + finch pull alpine + ./_output/bin/finch run --name test-ctr1 alpine + finch run --name test-ctr2 alpine + # check whether containers exist + if ! ./_output/bin/finch ps -a | grep 'test-ctr1'; then + echo "ERROR: The container test-ctr1 doesn't exist" + exit 1 + fi + if ! finch ps -a | grep 'test-ctr2'; then + echo "ERROR: The container test-ctr2 doesn't exist" + exit 1 + fi + # clean up the VMs + ./_output/bin/finch vm stop && ./_output/bin/finch vm remove + finch vm stop && finch vm remove + - name: Clean up multiple instance test + run: | + sudo rm -rf ./_output + echo 'y' | sudo bash /Applications/Finch/uninstall.sh + sudo installer -pkg Finch-${GITHUB_REF_NAME}-x86_64.pkg -target / - name: Run e2e tests run: INSTALLED=true make test-e2e - name: Silently uninstall diff --git a/.github/workflows/sync-submodules.yaml b/.github/workflows/sync-submodules.yaml new file mode 100644 index 000000000..44b0c37c5 --- /dev/null +++ b/.github/workflows/sync-submodules.yaml @@ -0,0 +1,27 @@ +name: Sync Submodules + +# Pulls changes from the main branch of submodules daily at 9:00 UTC and opens a PR. +on: + schedule: + - cron: '0 9 * * *' + workflow_dispatch: + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + submodules: recursive + token: ${{ secrets.GITHUB_TOKEN }} + - name: Update sub modules + run: | + git submodule update --remote + ./.github/bin/update-os-image.sh + - name: Create PR + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + signoff: true + title: 'build(deps): Bump submodules' diff --git a/.github/workflows/update-deps.yaml b/.github/workflows/update-deps.yaml index 27e0aed6e..90aa7bb0b 100644 --- a/.github/workflows/update-deps.yaml +++ b/.github/workflows/update-deps.yaml @@ -1,5 +1,7 @@ name: Update dependencies on: + schedule: + - cron: '0 11 * * 2' workflow_dispatch: permissions: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 50e451daa..9a727ada5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -139,9 +139,13 @@ If the repo is already cloned, but the submodules are not pulled yet, the follow git submodule update --init --recursive ``` -After cloning the repo, run `make` to build the binary. +After cloning the repo, run the following command to make subsequent `git pull` to also update submodules to the versions specified in the upstream branch. -The binary in `_output` can be directly used. E.g. initializing the vm and display the version +```shell +git config submodule.recurse true +``` + +Then run `make` to build the binary. The binary in `_output` can be directly used. E.g. initializing the vm and display the version ```sh ./_output/bin/finch vm init diff --git a/Makefile b/Makefile index ad1302560..fe5b5320b 100644 --- a/Makefile +++ b/Makefile @@ -34,14 +34,14 @@ ifneq (,$(findstring arm64,$(ARCH))) SUPPORTED_ARCH = true LIMA_ARCH = aarch64 # From https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/aarch64/images/ - FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.aarch64-20230302174539.qcow2 - LIMA_URL ?= https://deps.runfinch.com/aarch64/lima-and-qemu.macos-aarch64.1673290784.tar.gz + FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.aarch64-20230317205128.qcow2 + LIMA_URL ?= https://deps.runfinch.com/aarch64/lima-and-qemu.macos-aarch64.1678826933.tar.gz else ifneq (,$(findstring x86_64,$(ARCH))) SUPPORTED_ARCH = true LIMA_ARCH = x86_64 # From https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Cloud/x86_64/images/ - FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.x86_64-20230302173518.qcow2 - LIMA_URL ?= https://deps.runfinch.com/x86-64/lima-and-qemu.macos-x86_64.1673290501.tar.gz + FINCH_OS_BASENAME ?= Fedora-Cloud-Base-37-1.7.x86_64-20230317204632.qcow2 + LIMA_URL ?= https://deps.runfinch.com/x86-64/lima-and-qemu.macos-x86_64.1678817277.tar.gz endif FINCH_OS_HASH := `shasum -a 256 $(OUTDIR)/os/$(FINCH_OS_BASENAME) | cut -d ' ' -f 1` diff --git a/cmd/finch/main.go b/cmd/finch/main.go index a93cf811b..3905c5e17 100644 --- a/cmd/finch/main.go +++ b/cmd/finch/main.go @@ -28,7 +28,39 @@ import ( const finchRootCmd = "finch" +type Shape interface { + Area() float64 +} + +type Rectangle struct { + Width float64 + Height float64 +} + +func (r Rectangle) Area() float64 { + return 0 +} + +type Square struct { + Width float64 +} + +func (r Square) Area() float64 { + return 1 +} + +func computeArea(shape Shape) float64 { + return shape.Area() +} + func main() { + println(computeArea(&Rectangle{ + Width: 0, + Height: 0, + })) + println(computeArea(&Square{ + Width: 1, + })) logger := flog.NewLogrus() stdLib := system.NewStdLib() fs := afero.NewOsFs() diff --git a/cmd/finch/virtual_machine_init.go b/cmd/finch/virtual_machine_init.go index 4a83f2d5d..0c691b864 100644 --- a/cmd/finch/virtual_machine_init.go +++ b/cmd/finch/virtual_machine_init.go @@ -96,7 +96,7 @@ func (iva *initVMAction) run() error { iva.logger.Info("Initializing and starting Finch virtual machine...") logs, err := limaCmd.CombinedOutput() if err != nil { - iva.logger.Errorf("Finch virtual machine failed to start, debug logs: %s", logs) + iva.logger.Errorf("Finch virtual machine failed to start, debug logs:\n%s", logs) return err } iva.logger.Info("Finch virtual machine started successfully") diff --git a/cmd/finch/virtual_machine_init_test.go b/cmd/finch/virtual_machine_init_test.go index 9777d46be..cf73bf1f5 100644 --- a/cmd/finch/virtual_machine_init_test.go +++ b/cmd/finch/virtual_machine_init_test.go @@ -289,7 +289,7 @@ func TestInitVMAction_run(t *testing.T) { mockBaseYamlFilePath, "--tty=false").Return(command) logger.EXPECT().Info("Initializing and starting Finch virtual machine...") - logger.EXPECT().Errorf("Finch virtual machine failed to start, debug logs: %s", logs) + logger.EXPECT().Errorf("Finch virtual machine failed to start, debug logs:\n%s", logs) }, }, } diff --git a/cmd/finch/virtual_machine_remove.go b/cmd/finch/virtual_machine_remove.go index bca27aad2..16e47f4d5 100644 --- a/cmd/finch/virtual_machine_remove.go +++ b/cmd/finch/virtual_machine_remove.go @@ -81,7 +81,7 @@ func (rva *removeVMAction) removeVM(force bool) error { } logs, err := limaCmd.CombinedOutput() if err != nil { - rva.logger.Errorf("Finch virtual machine failed to remove, debug logs: %s", logs) + rva.logger.Errorf("Finch virtual machine failed to remove, debug logs:\n%s", logs) return err } rva.logger.Info("Finch virtual machine removed successfully") diff --git a/cmd/finch/virtual_machine_remove_test.go b/cmd/finch/virtual_machine_remove_test.go index e77fdfbbe..8b43bd5f2 100644 --- a/cmd/finch/virtual_machine_remove_test.go +++ b/cmd/finch/virtual_machine_remove_test.go @@ -159,7 +159,7 @@ func TestRemoveVMAction_run(t *testing.T) { command.EXPECT().CombinedOutput().Return(logs, errors.New("failed to remove instance")) creator.EXPECT().CreateWithoutStdio("remove", limaInstanceName).Return(command) logger.EXPECT().Info("Removing existing Finch virtual machine...") - logger.EXPECT().Errorf("Finch virtual machine failed to remove, debug logs: %s", logs) + logger.EXPECT().Errorf("Finch virtual machine failed to remove, debug logs:\n%s", logs) }, force: false, }, diff --git a/cmd/finch/virtual_machine_start.go b/cmd/finch/virtual_machine_start.go index d46c7635b..0edb893f6 100644 --- a/cmd/finch/virtual_machine_start.go +++ b/cmd/finch/virtual_machine_start.go @@ -88,7 +88,7 @@ func (sva *startVMAction) run() error { sva.logger.Info("Starting existing Finch virtual machine...") logs, err := limaCmd.CombinedOutput() if err != nil { - sva.logger.Errorf("Finch virtual machine failed to start, debug logs: %s", logs) + sva.logger.Errorf("Finch virtual machine failed to start, debug logs:\n%s", logs) return err } sva.logger.Info("Finch virtual machine started successfully") diff --git a/cmd/finch/virtual_machine_start_test.go b/cmd/finch/virtual_machine_start_test.go index 6b4458aa6..1b65543c2 100644 --- a/cmd/finch/virtual_machine_start_test.go +++ b/cmd/finch/virtual_machine_start_test.go @@ -305,7 +305,7 @@ func TestStartVMAction_run(t *testing.T) { lcc.EXPECT().CreateWithoutStdio("start", limaInstanceName).Return(command) logger.EXPECT().Info("Starting existing Finch virtual machine...") - logger.EXPECT().Errorf("Finch virtual machine failed to start, debug logs: %s", logs) + logger.EXPECT().Errorf("Finch virtual machine failed to start, debug logs:\n%s", logs) }, }, } diff --git a/cmd/finch/virtual_machine_stop.go b/cmd/finch/virtual_machine_stop.go index 4bece4c89..7b5c75f53 100644 --- a/cmd/finch/virtual_machine_stop.go +++ b/cmd/finch/virtual_machine_stop.go @@ -79,7 +79,7 @@ func (sva *stopVMAction) stopVM(force bool) error { } logs, err := limaCmd.CombinedOutput() if err != nil { - sva.logger.Errorf("Finch virtual machine failed to stop, debug logs: %s", logs) + sva.logger.Errorf("Finch virtual machine failed to stop, debug logs:\n%s", logs) return err } sva.logger.Info("Finch virtual machine stopped successfully") diff --git a/cmd/finch/virtual_machine_stop_test.go b/cmd/finch/virtual_machine_stop_test.go index 8134e7b0b..5eec80209 100644 --- a/cmd/finch/virtual_machine_stop_test.go +++ b/cmd/finch/virtual_machine_stop_test.go @@ -158,7 +158,7 @@ func TestStopVMAction_run(t *testing.T) { command.EXPECT().CombinedOutput().Return(logs, errors.New("error")) creator.EXPECT().CreateWithoutStdio("stop", limaInstanceName).Return(command) logger.EXPECT().Info("Stopping existing Finch virtual machine...") - logger.EXPECT().Errorf("Finch virtual machine failed to stop, debug logs: %s", logs) + logger.EXPECT().Errorf("Finch virtual machine failed to stop, debug logs:\n%s", logs) }, force: false, }, diff --git a/deps/finch-core b/deps/finch-core index eef21029b..31067f9e3 160000 --- a/deps/finch-core +++ b/deps/finch-core @@ -1 +1 @@ -Subproject commit eef21029b89d7db00bddc6e426e4405c920b13ed +Subproject commit 31067f9e328bb05543c547b3aad728a31db236f9 diff --git a/e2e/vm/additional_disk_test.go b/e2e/vm/additional_disk_test.go index ecb3fd48e..56dc777b5 100644 --- a/e2e/vm/additional_disk_test.go +++ b/e2e/vm/additional_disk_test.go @@ -4,6 +4,8 @@ package vm import ( + "fmt" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/runfinch/common-tests/command" @@ -12,32 +14,46 @@ import ( const ( savedImage = "public.ecr.aws/docker/library/alpine:latest" - containerName = "userDataTest" + containerName = "test-ctr" + volumeName = "test-volume" + networkName = "test-network" ) var testAdditionalDisk = func(o *option.Option) { ginkgo.Describe("Additional disk", ginkgo.Serial, func() { ginkgo.It("Retains container user data after the VM is deleted", func() { - command.Run(o, "pull", savedImage) + command.Run(o, "volume", "create", volumeName) + ginkgo.DeferCleanup(command.Run, o, "volume", "rm", volumeName) + command.Run(o, "network", "create", networkName) + ginkgo.DeferCleanup(command.Run, o, "network", "rm", networkName) + + command.Run(o, "run", "-d", "--name", containerName, "-v", fmt.Sprintf("%s:/tmp", volumeName), + savedImage, "sh", "-c", "sleep infinity") + command.Run(o, "exec", containerName, "sh", "-c", "echo foo > /tmp/test.txt") ginkgo.DeferCleanup(command.Run, o, "rmi", savedImage) - oldImagesOutput := command.StdoutStr(o, "images", "--format", "{{.Name}}") - gomega.Expect(oldImagesOutput).Should(gomega.ContainSubstring(savedImage)) + ginkgo.DeferCleanup(command.Run, o, "rm", "-f", containerName) - command.Run(o, "run", "--name", containerName, savedImage) - ginkgo.DeferCleanup(command.Run, o, "rm", containerName) - oldPsOutput := command.StdoutStr(o, "ps", "--all", "--format", "{{.Names}}") - gomega.Expect(oldPsOutput).Should(gomega.ContainSubstring(containerName)) + command.Run(o, "kill", containerName) command.New(o, virtualMachineRootCmd, "stop").WithoutCheckingExitCode().WithTimeoutInSeconds(90).Run() command.Run(o, virtualMachineRootCmd, "remove") - command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(240).Run() - newImagesOutput := command.StdoutStr(o, "images", "--format", "{{.Name}}") - gomega.Expect(newImagesOutput).Should(gomega.Equal(oldImagesOutput)) + imageOutput := command.StdoutAsLines(o, "images", "--format", "{{.Name}}") + gomega.Expect(imageOutput).Should(gomega.ContainElement(savedImage)) + + psOutput := command.StdoutAsLines(o, "ps", "--all", "--format", "{{.Names}}") + gomega.Expect(psOutput).Should(gomega.ContainElement(containerName)) + + volumeOutput := command.StdoutAsLines(o, "volume", "ls", "--format", "{{.Name}}") + gomega.Expect(volumeOutput).Should(gomega.ContainElement(volumeName)) + + networkOutput := command.StdoutAsLines(o, "network", "ls", "--format", "{{.Name}}") + gomega.Expect(networkOutput).Should(gomega.ContainElement(networkName)) - newPsOutput := command.StdoutStr(o, "ps", "--all", "--format", "{{.Names}}") - gomega.Expect(newPsOutput).Should(gomega.Equal(oldPsOutput)) + command.Run(o, "start", containerName) + gomega.Expect(command.StdoutStr(o, "exec", containerName, "cat", "/tmp/test.txt")). + Should(gomega.Equal("foo")) }) }) } diff --git a/go.mod b/go.mod index 34965cecc..20e4af197 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,11 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-licenses v1.6.0 github.com/lima-vm/lima v0.15.0 - github.com/onsi/ginkgo/v2 v2.9.0 - github.com/onsi/gomega v1.27.2 + github.com/onsi/ginkgo/v2 v2.9.1 + github.com/onsi/gomega v1.27.3 github.com/pelletier/go-toml v1.9.5 github.com/pkg/sftp v1.13.5 - github.com/runfinch/common-tests v0.6.1 + github.com/runfinch/common-tests v0.6.2 github.com/sirupsen/logrus v1.9.0 github.com/spf13/afero v1.9.5 github.com/spf13/cobra v1.6.1 @@ -31,7 +31,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect diff --git a/go.sum b/go.sum index 18c6a4939..636233095 100644 --- a/go.sum +++ b/go.sum @@ -178,8 +178,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -307,10 +308,10 @@ github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= -github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= +github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= +github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= +github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= @@ -338,8 +339,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/runfinch/common-tests v0.6.1 h1:seHaiuhpsbty7eDI1HU503xIMu29Tc0vuGrtgQfTL7A= -github.com/runfinch/common-tests v0.6.1/go.mod h1:kEouVzij+txCzHvD1Cue8DqSQT06ZOhoBPaSCyQyQdE= +github.com/runfinch/common-tests v0.6.2 h1:dTuWhwvh4angzIM+Dci+gE4MiWrgOf7H68lmlP8P5Aw= +github.com/runfinch/common-tests v0.6.2/go.mod h1:cqid4oyin7anOS0mR8MYt3fzUL14YnIaDgcM9wBMwro= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= diff --git a/pkg/disk/disk.go b/pkg/disk/disk.go index 3ee51dbeb..d7a2d6f31 100644 --- a/pkg/disk/disk.go +++ b/pkg/disk/disk.go @@ -116,7 +116,10 @@ func (m *userDataDiskManager) limaDiskExists() bool { func (m *userDataDiskManager) createLimaDisk() error { cmd := m.lcc.CreateWithoutStdio("disk", "create", diskName, "--size", diskSize) - return cmd.Run() + if logs, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to create disk, debug logs:\n%s", logs) + } + return nil } func (m *userDataDiskManager) attachPersistentDiskToLimaDisk() error { @@ -165,5 +168,8 @@ func (m *userDataDiskManager) limaDiskIsLocked() bool { func (m *userDataDiskManager) unlockLimaDisk() error { cmd := m.lcc.CreateWithoutStdio("disk", "unlock", diskName) - return cmd.Run() + if logs, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to unlock disk, debug logs:\n%s", logs) + } + return nil } diff --git a/pkg/disk/disk_test.go b/pkg/disk/disk_test.go index 4dcf2a501..fbf4cdf68 100644 --- a/pkg/disk/disk_test.go +++ b/pkg/disk/disk_test.go @@ -55,7 +55,7 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { cmd.EXPECT().Output().Return([]byte(""), nil) lcc.EXPECT().CreateWithoutStdio(mockCreateArgs).Return(cmd) - cmd.EXPECT().Run().Return(nil) + cmd.EXPECT().CombinedOutput().Return(nil, nil) dfs.EXPECT().Stat(finch.UserDataDiskPath(homeDir)).Return(nil, fs.ErrNotExist) dfs.EXPECT().Stat(path.Dir(finch.UserDataDiskPath(homeDir))).Return(nil, nil) @@ -107,7 +107,7 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { cmd.EXPECT().Output().Return([]byte(""), nil) lcc.EXPECT().CreateWithoutStdio(mockCreateArgs).Return(cmd) - cmd.EXPECT().Run().Return(nil) + cmd.EXPECT().CombinedOutput().Return(nil, nil) dfs.EXPECT().Stat(finch.UserDataDiskPath(homeDir)).Return(nil, nil) @@ -130,7 +130,7 @@ func TestUserDataDiskManager_InitializeUserDataDisk(t *testing.T) { dfs.EXPECT().Stat(lockPath).Return(nil, nil) lcc.EXPECT().CreateWithoutStdio(mockUnlockArgs).Return(cmd) - cmd.EXPECT().Run().Return(nil) + cmd.EXPECT().CombinedOutput().Return(nil, nil) }, }, }