From 134f401071872b55054646a83305e1688c85bef4 Mon Sep 17 00:00:00 2001 From: Arjun Raja Yogidas Date: Tue, 1 Jul 2025 04:43:06 +0000 Subject: [PATCH 1/2] fix golangci-lint Signed-off-by: Arjun Raja Yogidas --- cmd/nerdctl/image/image_convert_linux_test.go | 2 +- docs/soci.md | 24 ++++++- go.mod | 2 +- pkg/snapshotterutil/sociutil.go | 72 +++++++++---------- 4 files changed, 56 insertions(+), 44 deletions(-) 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/go.mod b/go.mod index fb857204047..a3050d1ca52 100644 --- a/go.mod +++ b/go.mod @@ -136,7 +136,7 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/mod v0.25.0 // indirect + golang.org/x/mod v0.25.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect //gomodjail:unconfined google.golang.org/grpc v1.72.2 // indirect diff --git a/pkg/snapshotterutil/sociutil.go b/pkg/snapshotterutil/sociutil.go index 2321b54fdf8..1744b3ab777 100644 --- a/pkg/snapshotterutil/sociutil.go +++ b/pkg/snapshotterutil/sociutil.go @@ -26,15 +26,38 @@ import ( "strconv" "strings" - "github.com/Masterminds/semver/v3" + "golang.org/x/mod/semver" + "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 { @@ -58,50 +81,19 @@ func CheckSociVersion(requiredVersion string) error { return fmt.Errorf("failed to parse SOCI version from output: %s", versionStr) } - // Extract version number - installedVersion := matches[1] + // Extract version number and add 'v' prefix required by semver package + // The regex captures only the numeric part, so we always need to add the 'v' + installedVersion := "v" + matches[1] + requiredSemver := "v" + requiredVersion - // Compare versions using semver - v1, err := semver.NewVersion(installedVersion) - if err != nil { - return fmt.Errorf("failed to parse installed version %s: %v", installedVersion, err) - } - - v2, err := semver.NewVersion(requiredVersion) - if err != nil { - return fmt.Errorf("failed to parse minimum required version %s: %v", requiredVersion, err) - } - - if v1.LessThan(v2) { - return fmt.Errorf("SOCI version %s is lower than the required version %s for the convert operation", installedVersion, requiredVersion) + // Use semver package to compare versions + if semver.Compare(installedVersion, requiredSemver) < 0 { + return fmt.Errorf("SOCI version %s is lower than the required version %s for the convert operation", strings.TrimPrefix(installedVersion, "v"), requiredVersion) } 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 From 6f509d13a1483c1f09d2ad5f8f09136c62b7f0eb Mon Sep 17 00:00:00 2001 From: Arjun Raja Yogidas Date: Wed, 2 Jul 2025 18:30:08 +0000 Subject: [PATCH 2/2] use masterMindsSemver Signed-off-by: Arjun Raja Yogidas --- go.mod | 2 +- pkg/snapshotterutil/sociutil.go | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index a3050d1ca52..fb857204047 100644 --- a/go.mod +++ b/go.mod @@ -136,7 +136,7 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/mod v0.25.0 + golang.org/x/mod v0.25.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect //gomodjail:unconfined google.golang.org/grpc v1.72.2 // indirect diff --git a/pkg/snapshotterutil/sociutil.go b/pkg/snapshotterutil/sociutil.go index 1744b3ab777..240ef54737e 100644 --- a/pkg/snapshotterutil/sociutil.go +++ b/pkg/snapshotterutil/sociutil.go @@ -26,7 +26,7 @@ import ( "strconv" "strings" - "golang.org/x/mod/semver" + "github.com/Masterminds/semver/v3" "github.com/containerd/containerd/v2/client" "github.com/containerd/log" @@ -81,14 +81,23 @@ func CheckSociVersion(requiredVersion string) error { return fmt.Errorf("failed to parse SOCI version from output: %s", versionStr) } - // Extract version number and add 'v' prefix required by semver package - // The regex captures only the numeric part, so we always need to add the 'v' - installedVersion := "v" + matches[1] - requiredSemver := "v" + requiredVersion + // Extract version number + installedVersionStr := matches[1] - // Use semver package to compare versions - if semver.Compare(installedVersion, requiredSemver) < 0 { - return fmt.Errorf("SOCI version %s is lower than the required version %s for the convert operation", strings.TrimPrefix(installedVersion, "v"), requiredVersion) + // Parse versions using semver package + installedVersion, err := semver.NewVersion(installedVersionStr) + if err != nil { + return fmt.Errorf("failed to parse installed SOCI version: %w", err) + } + + reqVersion, err := semver.NewVersion(requiredVersion) + if err != nil { + return fmt.Errorf("failed to parse required SOCI version: %w", err) + } + + // 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