Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion cmd/nerdctl/image/image_convert_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
24 changes: 22 additions & 2 deletions docs/soci.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 |
Expand All @@ -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.
--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.
65 changes: 33 additions & 32 deletions pkg/snapshotterutil/sociutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down
Loading