diff --git a/cmd/nerdctl/image/image_convert_linux_test.go b/cmd/nerdctl/image/image_convert_linux_test.go index 90e7a556dc3..be24918fb31 100644 --- a/cmd/nerdctl/image/image_convert_linux_test.go +++ b/cmd/nerdctl/image/image_convert_linux_test.go @@ -102,7 +102,7 @@ func TestImageConvert(t *testing.T) { Command: func(data test.Data, helpers test.Helpers) test.TestableCommand { return helpers.Command("image", "convert", "--soci", "--soci-span-size", "2097152", - "--soci-min-layer-size", "20971520", + "--soci-min-layer-size", "0", testutil.CommonImage, data.Identifier("converted-image")) }, Expected: test.Expects(0, nil, nil), diff --git a/docs/soci.md b/docs/soci.md index d2dc84645df..209494566ff 100644 --- a/docs/soci.md +++ b/docs/soci.md @@ -4,6 +4,22 @@ SOCI Snapshotter is a containerd snapshotter plugin. It enables standard OCI ima See https://github.com/awslabs/soci-snapshotter to learn further information. +## SOCI Index Manifest Versions + +SOCI supports two index manifest versions: + +- **v1**: Original format using OCI Referrers API (disabled by default in SOCI v0.10.0+) +- **v2**: New format that packages SOCI index with the image (default in SOCI v0.10.0+) + +To enable v1 indices in SOCI v0.10.0+, add to `/etc/soci-snapshotter-grpc/config.toml`: +```toml +[pull_modes] + [pull_modes.soci_v1] + enable = true +``` + +For detailed information about the differences between v1 and v2, see the [SOCI Index Manifest v2 documentation](https://github.com/awslabs/soci-snapshotter/blob/main/docs/soci-index-manifest-v2.md). + ## Prerequisites - Install containerd remote snapshotter plugin (`soci-snapshotter-grpc`) from https://github.com/awslabs/soci-snapshotter/blob/main/docs/getting-started.md @@ -46,10 +62,12 @@ nerdctl push --snapshotter=soci --soci-span-size=2097152 --soci-min-layer-size=2 ``` --soci-span-size and --soci-min-layer-size are two properties to customize the SOCI index. See [Command Reference](https://github.com/containerd/nerdctl/blob/377b2077bb616194a8ef1e19ccde32aa1ffd6c84/docs/command-reference.md?plain=1#L773) for further details. +> **Note**: With SOCI v0.10.0+, `nerdctl push` creates and pushes v2 indices by default. For v1 indices, enable them in the snapshotter config as described in the section above. + ## Enable SOCI for `nerdctl image convert` -| :zap: Requirement | nerdctl >= 2.2.0 | +| :zap: Requirement | nerdctl >= 2.1.3 | | ----------------- | ---------------- | | :zap: Requirement | soci-snapshotter >= 0.10.0 | @@ -59,4 +77,6 @@ nerdctl push --snapshotter=soci --soci-span-size=2097152 --soci-min-layer-size=2 ```console nerdctl image convert --soci --soci-span-size=2097152 --soci-min-layer-size=20971520 public.ecr.aws/my-registry/my-repo:latest public.ecr.aws/my-registry/my-repo:soci ``` ---soci-span-size and --soci-min-layer-size are two properties to customize the SOCI index. See [Command Reference](https://github.com/containerd/nerdctl/blob/377b2077bb616194a8ef1e19ccde32aa1ffd6c84/docs/command-reference.md?plain=1#L773) for further details. \ No newline at end of file +--soci-span-size and --soci-min-layer-size are two properties to customize the SOCI index. See [Command Reference](https://github.com/containerd/nerdctl/blob/377b2077bb616194a8ef1e19ccde32aa1ffd6c84/docs/command-reference.md?plain=1#L773) for further details. + +The `image convert` command with `--soci` flag creates SOCI-enabled images using SOCI Index Manifest v2, which combines the SOCI index and the original image into a single artifact. diff --git a/pkg/snapshotterutil/sociutil.go b/pkg/snapshotterutil/sociutil.go index 2321b54fdf8..240ef54737e 100644 --- a/pkg/snapshotterutil/sociutil.go +++ b/pkg/snapshotterutil/sociutil.go @@ -27,14 +27,37 @@ import ( "strings" "github.com/Masterminds/semver/v3" + "github.com/containerd/containerd/v2/client" "github.com/containerd/log" "github.com/containerd/nerdctl/v2/pkg/api/types" ) +// setupSociCommand creates and sets up a SOCI command with common configuration +func setupSociCommand(gOpts types.GlobalCommandOptions) (*exec.Cmd, error) { + sociExecutable, err := exec.LookPath("soci") + if err != nil { + log.L.WithError(err).Error("soci executable not found in path $PATH") + log.L.Info("you might consider installing soci from: https://github.com/awslabs/soci-snapshotter/blob/main/docs/install.md") + return nil, err + } + + sociCmd := exec.Command(sociExecutable) + sociCmd.Env = os.Environ() + + // #region for global flags. + if gOpts.Address != "" { + sociCmd.Args = append(sociCmd.Args, "--address", gOpts.Address) + } + if gOpts.Namespace != "" { + sociCmd.Args = append(sociCmd.Args, "--namespace", gOpts.Namespace) + } + + return sociCmd, nil +} + // CheckSociVersion checks if the SOCI binary version is at least the required version -// This function can be used by both production code and tests func CheckSociVersion(requiredVersion string) error { sociExecutable, err := exec.LookPath("soci") if err != nil { @@ -59,49 +82,27 @@ func CheckSociVersion(requiredVersion string) error { } // Extract version number - installedVersion := matches[1] + installedVersionStr := matches[1] - // Compare versions using semver - v1, err := semver.NewVersion(installedVersion) + // Parse versions using semver package + installedVersion, err := semver.NewVersion(installedVersionStr) if err != nil { - return fmt.Errorf("failed to parse installed version %s: %v", installedVersion, err) + return fmt.Errorf("failed to parse installed SOCI version: %w", err) } - v2, err := semver.NewVersion(requiredVersion) + reqVersion, err := semver.NewVersion(requiredVersion) if err != nil { - return fmt.Errorf("failed to parse minimum required version %s: %v", requiredVersion, err) + return fmt.Errorf("failed to parse required SOCI version: %w", err) } - if v1.LessThan(v2) { - return fmt.Errorf("SOCI version %s is lower than the required version %s for the convert operation", installedVersion, requiredVersion) + // Compare versions + if installedVersion.LessThan(reqVersion) { + return fmt.Errorf("SOCI version %s is lower than the required version %s for the convert operation", installedVersion.String(), reqVersion.String()) } return nil } -// setupSociCommand creates and sets up a SOCI command with common configuration -func setupSociCommand(gOpts types.GlobalCommandOptions) (*exec.Cmd, error) { - sociExecutable, err := exec.LookPath("soci") - if err != nil { - log.L.WithError(err).Error("soci executable not found in path $PATH") - log.L.Info("you might consider installing soci from: https://github.com/awslabs/soci-snapshotter/blob/main/docs/install.md") - return nil, err - } - - sociCmd := exec.Command(sociExecutable) - sociCmd.Env = os.Environ() - - // #region for global flags. - if gOpts.Address != "" { - sociCmd.Args = append(sociCmd.Args, "--address", gOpts.Address) - } - if gOpts.Namespace != "" { - sociCmd.Args = append(sociCmd.Args, "--namespace", gOpts.Namespace) - } - - return sociCmd, nil -} - // ConvertSociIndexV2 converts an image to SOCI format and returns the converted image reference with digest func ConvertSociIndexV2(ctx context.Context, client *client.Client, srcRef string, destRef string, gOpts types.GlobalCommandOptions, platforms []string, sOpts types.SociOptions) (string, error) { // Check if SOCI version is at least 0.10.0 which is required for the convert operation