Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding config option for SOCI installation on VM #506

Merged
merged 23 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
db75013
soci-comments
CodeChanning Jul 19, 2023
e491649
initial commit for soci packaging
CodeChanning Jul 27, 2023
a5ab536
making toggle for soci configurable
CodeChanning Jul 27, 2023
1d14f80
removed soci dependency group
CodeChanning Jul 27, 2023
eaf316c
removed finch support binary
CodeChanning Jul 27, 2023
4500b15
making soci version configurable and incorporating some comments
CodeChanning Jul 28, 2023
7fb0f86
updating soci version + adding tests
CodeChanning Jul 31, 2023
e81e15c
making soci installation script a constant
CodeChanning Aug 1, 2023
016b0c6
fixing containerd config bug when stopping and starting vm
CodeChanning Aug 4, 2023
cc766d1
adding e2e tests for pulling and running with soci
CodeChanning Aug 5, 2023
47bc078
fixing testSoci method signature
CodeChanning Aug 5, 2023
49680dc
fixing testSoci method signature in vm_test.go
CodeChanning Aug 5, 2023
41a1ae5
taking out CreateLimaOption() + making config option a str instead of…
CodeChanning Aug 8, 2023
877eb38
fixing lint errors + adding error checking
CodeChanning Aug 8, 2023
f49625f
fixing lint and unit test issues
CodeChanning Aug 8, 2023
3d32a8e
removing nerdctl archive in VM
CodeChanning Aug 9, 2023
3635d25
replacing .New with .Run when possible
CodeChanning Aug 10, 2023
f2d03ad
fixing godoc and not exporting socified ffmpeg image
CodeChanning Aug 10, 2023
8074323
removing redundant .Expect
CodeChanning Aug 10, 2023
7747083
retrying cURL and exiting when it fails
CodeChanning Aug 10, 2023
c395020
fixing spacing for comments and typos
CodeChanning Aug 10, 2023
d4d8d19
making config option a slice instead of string + adding as conifg va…
CodeChanning Aug 11, 2023
bb21ba3
using map for enabled snapshotter bool values
CodeChanning Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ An example `finch.yaml` looks like this:
cpus: 4
# memory: the amount of memory to dedicate to the virtual machine. (required)
memory: 4GiB
# snapshotter: the snapshotter a user want to use as there default snapshotter
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
# Supported Snapshotters List:
# - soci https://github.com/awslabs/soci-snapshotter/tree/main
# Once the option has been set the snapshotter will be installed on either finch vm init or finch vm start.
# The snapshotter binary will be downloaded on the virtual machine and will be configured and ready for use.
# To change your default snpahotter back to overlayfs, simply remove the snapshotter value from finch.yaml
# To completely remove the snapshotter binary, shell into your VM and remove /usr/local/bin/{snapshotter binary}
# and remove the snapshotter configuration in the containerd config file found at /etc/containerd/config.toml
snapshotter: soci
pendo324 marked this conversation as resolved.
Show resolved Hide resolved
# creds_helpers: a list of credential helpers that will be installed and configured automatically.
# Supported Credential Helpers List:
# - ecr-login https://github.com/awslabs/amazon-ecr-credential-helper
Expand Down
79 changes: 79 additions & 0 deletions e2e/vm/soci_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package vm

import (
"os"
"path/filepath"
"strings"

"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
"github.com/runfinch/common-tests/command"
"github.com/runfinch/common-tests/option"
)

const (
FfmpegSociImage = "public.ecr.aws/soci-workshop-examples/ffmpeg:latest"
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
sociMountString = "fuse.rawBridge"
)

var testSoci = func(o *option.Option, installed bool) {
ginkgo.Describe("SOCI", func() {
var limactlO *option.Option
var limaHomePathEnv string
var wd string
var err error

ginkgo.BeforeEach(func() {
wd, err = os.Getwd()
gomega.Expect(err).Should(gomega.BeNil())
limaHomePathEnv = "LIMA_HOME=" + filepath.Join(wd, "../../_output/lima/data")
limactlO, err = option.New([]string{filepath.Join(wd, "../../_output/lima/bin/limactl")},
option.Env([]string{limaHomePathEnv}))
gomega.Expect(err).Should(gomega.BeNil())
})

ginkgo.It("finch pull should have same mounts as nerdctl pull with SOCI", func() {
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
resetVM(o, installed)
resetDisks(o, installed)
writeFile(finchConfigFilePath, []byte("cpus: 6\nmemory: 4GiB\nsnapshotter: soci\n"+
"vmType: qemu\nrosetta: false"))
initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(600).Run()
gomega.Expect(initCmdSession).Should(gexec.Exit(0))
command.New(o, "pull", FfmpegSociImage).WithTimeoutInSeconds(30).Run()
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
finchPullMounts := countMounts(limactlO)
command.Run(o, "rmi", "-f", FfmpegSociImage)
command.New(limactlO, "shell", "finch",
"sudo", "nerdctl", "--snapshotter=soci", "pull", FfmpegSociImage).WithTimeoutInSeconds(30).Run()
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
nerdctlPullMounts := countMounts(limactlO)
command.Run(o, "rmi", "-f", FfmpegSociImage)
gomega.Expect(finchPullMounts).Should(gomega.Equal(nerdctlPullMounts))
})

ginkgo.It("finch run should have same mounts as nerdctl run with SOCI", func() {
resetVM(o, installed)
resetDisks(o, installed)
writeFile(finchConfigFilePath, []byte("cpus: 6\nmemory: 4GiB\nsnapshotter: soci\n"+
"vmType: qemu\nrosetta: false"))
initCmdSession := command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(600).Run()
gomega.Expect(initCmdSession).Should(gexec.Exit(0))
command.New(o, "run", FfmpegSociImage).WithTimeoutInSeconds(30).Run()
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
finchPullMounts := countMounts(limactlO)
command.Run(o, "rmi", "-f", FfmpegSociImage)
command.New(limactlO, "shell", "finch",
"sudo", "nerdctl", "--snapshotter=soci", "run", FfmpegSociImage).WithTimeoutInSeconds(30).Run()
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
nerdctlPullMounts := countMounts(limactlO)
command.Run(o, "rmi", "-f", FfmpegSociImage)
gomega.Expect(finchPullMounts).Should(gomega.Equal(nerdctlPullMounts))
})
})
}

// counts the mounts present in the VM after pulling an image.
func countMounts(limactlO *option.Option) int {
mountOutput := command.StdoutStr(limactlO, "shell", "finch", "mount")
ningziwen marked this conversation as resolved.
Show resolved Hide resolved
return strings.Count(mountOutput, sociMountString)
}
1 change: 1 addition & 0 deletions e2e/vm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func TestVM(t *testing.T) {
testVirtualizationFrameworkAndRosetta(o, *e2e.Installed)
testSupportBundle(o)
testCredHelper(o, *e2e.Installed, *e2e.Registry)
testSoci(o, *e2e.Installed)
})

gomega.RegisterFailHandler(ginkgo.Fail)
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type AdditionalDirectory struct {
type Finch struct {
CPUs *int `yaml:"cpus"`
Memory *string `yaml:"memory"`
// Soci: the snapshotter that will be installed and configured automatically on vm init or on vm start
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
Snapshotter *string `yaml:"snapshotter,omitempty"`
// CredsHelper: the list of credential helpers that will be installed and configured automatically on vm init or on vm start
CredsHelpers []string `yaml:"creds_helpers,omitempty"`
// AdditionalDirectories are the work directories that are not supported by default. In macOS, only home directory is supported by default.
Expand Down
70 changes: 69 additions & 1 deletion pkg/config/lima_config_applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,32 @@ import (
"github.com/runfinch/finch/pkg/system"
)

const userModeEmulationProvisioningScriptHeader = "# cross-arch tools"
const (
sociVersion = "0.3.0"
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
sociInstallationProvisioningScriptHeader = "# soci installation and configuring"
sociFileNameFormat = "soci-snapshotter-%s-linux-%s.tar.gz"
sociDownloadURLFormat = "https://github.com/awslabs/soci-snapshotter/releases/download/v%s/%s"
sociInstallationScriptFormat = `%s
if [ ! -f /usr/local/bin/soci ]; then
#download soci
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
curl -OL "%s"
pendo324 marked this conversation as resolved.
Show resolved Hide resolved
#move to usr/local/bin
tar -C /usr/local/bin -xvf %s soci soci-snapshotter-grpc
fi

#changing containerd config
export config=etc/containerd/config.toml
echo " [proxy_plugins.soci]
type = \"snapshot\"
address = \"/run/soci-snapshotter-grpc/soci-snapshotter-grpc.sock\" " >> $config

sudo systemctl restart containerd.service
sudo soci-snapshotter-grpc &> ~/soci-snapshotter-logs &

`

userModeEmulationProvisioningScriptHeader = "# cross-arch tools"
)

// LimaConfigApplierSystemDeps contains the system dependencies for LimaConfigApplier.
//
Expand Down Expand Up @@ -96,6 +121,15 @@ func (lca *limaConfigApplier) Apply(isInit bool) error {
limaCfg = *cfgAfterInit
}

var sociEnabled bool
if lca.cfg.Snapshotter == nil {
sociEnabled = false
} else {
sociEnabled = (*lca.cfg.Snapshotter == "soci")
}

ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
toggleSoci(&limaCfg, sociEnabled, sociVersion)

limaCfgBytes, err := yaml.Marshal(limaCfg)
if err != nil {
return fmt.Errorf("failed to marshal the lima config file: %w", err)
Expand Down Expand Up @@ -188,3 +222,37 @@ func hasUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML) (int, bo

return scriptIdx, hasCrossArchToolInstallationScript
}

func toggleSoci(limaCfg *limayaml.LimaYAML, enabled bool, sociVersion string) {
idx, hasScript := hasSociInstallationScript(limaCfg)
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
sociFileName := fmt.Sprintf(sociFileNameFormat, sociVersion, system.NewStdLib().Arch())
sociDownloadURL := fmt.Sprintf(sociDownloadURLFormat, sociVersion, sociFileName)
sociInstallationScript := fmt.Sprintf(sociInstallationScriptFormat, sociInstallationProvisioningScriptHeader, sociDownloadURL, sociFileName)
if !hasScript && enabled {
limaCfg.Env = map[string]string{"CONTAINERD_SNAPSHOTTER": "soci"}
limaCfg.Provision = append(limaCfg.Provision, limayaml.Provision{
Mode: "system",
Script: sociInstallationScript,
})
} else if hasScript && !enabled {
limaCfg.Env = map[string]string{"CONTAINERD_SNAPSHOTTER": ""}
if len(limaCfg.Provision) > 0 {
limaCfg.Provision = append(limaCfg.Provision[:idx], limaCfg.Provision[idx+1:]...)
}
}
}

func hasSociInstallationScript(limaCfg *limayaml.LimaYAML) (int, bool) {
ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
hasSociInstallationScript := false
var scriptIdx int
for idx, prov := range limaCfg.Provision {
trimmed := strings.Trim(prov.Script, " ")
if !hasSociInstallationScript && strings.HasPrefix(trimmed, sociInstallationProvisioningScriptHeader) {
hasSociInstallationScript = true
scriptIdx = idx
break
}
}

return scriptIdx, hasSociInstallationScript
}
117 changes: 117 additions & 0 deletions pkg/config/lima_config_applier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"gopkg.in/yaml.v3"

"github.com/runfinch/finch/pkg/mocks"
"github.com/runfinch/finch/pkg/system"
)

func TestDiskLimaConfigApplier_Apply(t *testing.T) {
Expand Down Expand Up @@ -79,6 +80,122 @@ elif [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-x86"
fi

if [[ $qemu_pkgs ]]; then
dnf install -y --setopt=install_weak_deps=False ${qemu_pkgs}
fi
`, limaCfg.Provision[0].Script)
},
want: nil,
},
{
name: "adds soci script when snapshotter is set to soci in config",
config: &Finch{
Memory: pointer.String("2GiB"),
CPUs: pointer.Int(4),
VMType: pointer.String("qemu"),
Rosetta: pointer.Bool(false),
Snapshotter: pointer.String("soci"),
},
path: "/lima.yaml",
isInit: true,
mockSvc: func(
fs afero.Fs,
l *mocks.Logger,
cmd *mocks.Command,
creator *mocks.CommandCreator,
deps *mocks.LimaConfigApplierSystemDeps,
) {
err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600)
require.NoError(t, err)
cmd.EXPECT().Output().Return([]byte("13.0.0"), nil)
creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd)
},
postRunCheck: func(t *testing.T, fs afero.Fs) {
buf, err := afero.ReadFile(fs, "/lima.yaml")
require.NoError(t, err)

sociFileName := fmt.Sprintf(sociFileNameFormat, sociVersion, system.NewStdLib().Arch())
sociDownloadURL := fmt.Sprintf(sociDownloadURLFormat, sociVersion, sociFileName)
sociInstallationScript := fmt.Sprintf(sociInstallationScriptFormat,
sociInstallationProvisioningScriptHeader,
sociDownloadURL,
sociFileName)

var limaCfg limayaml.LimaYAML
err = yaml.Unmarshal(buf, &limaCfg)
require.NoError(t, err)
require.Equal(t, 4, *limaCfg.CPUs)
require.Equal(t, "2GiB", *limaCfg.Memory)
require.Equal(t, "reverse-sshfs", *limaCfg.MountType)
require.Equal(t, "system", limaCfg.Provision[1].Mode)
require.Equal(t, "soci", limaCfg.Env["CONTAINERD_SNAPSHOTTER"])
require.Equal(t, sociInstallationScript, limaCfg.Provision[1].Script)
require.Equal(t, "system", limaCfg.Provision[0].Mode)
require.Equal(t, `# cross-arch tools
#!/bin/bash
qemu_pkgs=""
if [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-aarch64"
elif [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-arm"
elif [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-x86"
fi

if [[ $qemu_pkgs ]]; then
dnf install -y --setopt=install_weak_deps=False ${qemu_pkgs}
fi
`, limaCfg.Provision[0].Script)
},
want: nil,
},
{
name: "doesn't add soci script when snapshotter is not set in config",
config: &Finch{
Memory: pointer.String("2GiB"),
CPUs: pointer.Int(4),
VMType: pointer.String("qemu"),
Rosetta: pointer.Bool(false),
Snapshotter: pointer.String(""),
},
path: "/lima.yaml",
isInit: true,
mockSvc: func(
fs afero.Fs,
l *mocks.Logger,
cmd *mocks.Command,
creator *mocks.CommandCreator,
deps *mocks.LimaConfigApplierSystemDeps,
) {
err := afero.WriteFile(fs, "/lima.yaml", []byte("memory: 4GiB\ncpus: 8"), 0o600)
require.NoError(t, err)
cmd.EXPECT().Output().Return([]byte("13.0.0"), nil)
creator.EXPECT().Create("sw_vers", "-productVersion").Return(cmd)
},
postRunCheck: func(t *testing.T, fs afero.Fs) {
buf, err := afero.ReadFile(fs, "/lima.yaml")
require.NoError(t, err)

var limaCfg limayaml.LimaYAML
err = yaml.Unmarshal(buf, &limaCfg)
require.NoError(t, err)
require.Equal(t, 4, *limaCfg.CPUs)
require.Equal(t, "2GiB", *limaCfg.Memory)
require.Equal(t, "reverse-sshfs", *limaCfg.MountType)
require.Equal(t, "system", limaCfg.Provision[0].Mode)
require.Equal(t, "", limaCfg.Env["CONTAINERD_SNAPSHOTTER"])
require.Equal(t, "system", limaCfg.Provision[0].Mode)
require.Equal(t, `# cross-arch tools
#!/bin/bash
qemu_pkgs=""
if [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-aarch64"
elif [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-arm"
elif [ ! -f /usr/bin/qemu-aarch64-static ]; then
qemu_pkgs="$qemu_pkgs qemu-user-static-x86"
fi

if [[ $qemu_pkgs ]]; then
dnf install -y --setopt=install_weak_deps=False ${qemu_pkgs}
fi
Expand Down