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 all 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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ An example `finch.yaml` looks like this:
cpus: 4
# memory: the amount of memory to dedicate to the virtual machine. (required)
memory: 4GiB
# snapshotters: the snapshotters a user wants to use (the first snapshotter will be set as the default snapshotter)
# Supported Snapshotters List:
# - soci https://github.com/awslabs/soci-snapshotter/tree/main
# Once the option has been set the snapshotters will be installed on either finch vm init or finch vm start.
# The snapshotters 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 snapshotters value from finch.yaml or set snapshotters to `overlayfs`
# To completely remove the snapshotters' binaries, 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
snapshotters:
- soci
# 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
76 changes: 76 additions & 0 deletions e2e/vm/soci_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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/runfinch/common-tests/command"
"github.com/runfinch/common-tests/option"
)

const (
ffmpegSociImage = "public.ecr.aws/soci-workshop-examples/ffmpeg:latest"
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\nsnapshotters:\n "+
"- soci\nvmType: qemu\nrosetta: false"))
command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(600).Run()
command.New(o, "pull", ffmpegSociImage).WithTimeoutInSeconds(30).Run()
finchPullMounts := countMounts(limactlO)
command.Run(o, "rmi", "-f", ffmpegSociImage)
command.New(limactlO, "shell", "finch",
"sudo", "nerdctl", "--snapshotter=soci", "pull", ffmpegSociImage).WithTimeoutInSeconds(30).Run()
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\nsnapshotters:\n "+
"- soci\nvmType: qemu\nrosetta: false"))
command.New(o, virtualMachineRootCmd, "init").WithTimeoutInSeconds(600).Run()
command.New(o, "run", ffmpegSociImage).WithTimeoutInSeconds(30).Run()
finchPullMounts := countMounts(limactlO)
command.Run(o, "rmi", "-f", ffmpegSociImage)
command.New(limactlO, "shell", "finch",
"sudo", "nerdctl", "--snapshotter=soci", "run", ffmpegSociImage).WithTimeoutInSeconds(30).Run()
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/stretchr/testify v1.8.4
github.com/xorcare/pointer v1.2.2
golang.org/x/crypto v0.11.0
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad
golang.org/x/tools v0.11.1
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.27.4
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU=
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
3 changes: 3 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type AdditionalDirectory struct {
type Finch struct {
CPUs *int `yaml:"cpus"`
Memory *string `yaml:"memory"`
// Snapshotters: the snapshotters that will be installed and configured automatically on vm init or on vm start.
// Values: `soci` for SOCI snapshotter; `overlayfs` for default overlay snapshotter.
Snapshotters []string `yaml:"snapshotters,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
98 changes: 97 additions & 1 deletion pkg/config/lima_config_applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,40 @@ import (
"github.com/lima-vm/lima/pkg/limayaml"
"github.com/spf13/afero"
"github.com/xorcare/pointer"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"

"github.com/runfinch/finch/pkg/command"
"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
set -e
curl --retry 2 --retry-max-time 120 -OL "%s"
# 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 +123,24 @@ func (lca *limaConfigApplier) Apply(isInit bool) error {
limaCfg = *cfgAfterInit
}

supportedSnapshotters := []string{"overlayfs", "soci"}
snapshotters := make(map[string][2]bool)
for i, snapshotter := range lca.cfg.Snapshotters {
if !slices.Contains(supportedSnapshotters, snapshotter) {
return fmt.Errorf("invalid snapshotter config value: %s", snapshotter)
}

isDefaultSnapshotter := false
if i == 0 {
isDefaultSnapshotter = true
}

isEnabled := true
snapshotters[snapshotter] = [2]bool{isEnabled, isDefaultSnapshotter}
}

ginglis13 marked this conversation as resolved.
Show resolved Hide resolved
toggleSnaphotters(&limaCfg, snapshotters)

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 +233,54 @@ func hasUserModeEmulationInstallationScript(limaCfg *limayaml.LimaYAML) (int, bo

return scriptIdx, hasCrossArchToolInstallationScript
}

// toggles snapshotters and sets default snapshotter.
func toggleSnaphotters(limaCfg *limayaml.LimaYAML, snapshotters map[string][2]bool) {
toggleOverlayFs(limaCfg, snapshotters["overlayfs"][1])
toggleSoci(limaCfg, snapshotters["soci"][0], snapshotters["soci"][1], sociVersion)
}

// sets overlayfs as the default snapshotter.
func toggleOverlayFs(limaCfg *limayaml.LimaYAML, isDefault bool) {
if isDefault {
limaCfg.Env = map[string]string{"CONTAINERD_SNAPSHOTTER": ""}
}
}

func toggleSoci(limaCfg *limayaml.LimaYAML, enabled bool, isDefault bool, sociVersion string) {
idx, hasScript := findSociInstallationScript(limaCfg)
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.Provision = append(limaCfg.Provision, limayaml.Provision{
Mode: "system",
Script: sociInstallationScript,
})
} else if hasScript && !enabled {
if len(limaCfg.Provision) > 0 {
limaCfg.Provision = append(limaCfg.Provision[:idx], limaCfg.Provision[idx+1:]...)
}
}

if isDefault {
limaCfg.Env = map[string]string{"CONTAINERD_SNAPSHOTTER": "soci"}
} else {
limaCfg.Env = map[string]string{"CONTAINERD_SNAPSHOTTER": ""}
}
}

func findSociInstallationScript(limaCfg *limayaml.LimaYAML) (int, bool) {
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
}
Loading
Loading