Skip to content

Commit b4bcc30

Browse files
committed
check soci version
Signed-off-by: Arjun Raja Yogidas <[email protected]>
1 parent 445fb7b commit b4bcc30

File tree

2 files changed

+58
-36
lines changed

2 files changed

+58
-36
lines changed

pkg/snapshotterutil/sociutil.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,63 @@ import (
2222
"fmt"
2323
"os"
2424
"os/exec"
25+
"regexp"
2526
"strconv"
2627
"strings"
2728

29+
"github.com/Masterminds/semver/v3"
2830
"github.com/containerd/containerd/v2/client"
2931
"github.com/containerd/log"
3032

3133
"github.com/containerd/nerdctl/v2/pkg/api/types"
3234
)
3335

36+
// CheckSociVersion checks if the SOCI binary version is at least the required version
37+
// This function can be used by both production code and tests
38+
func CheckSociVersion(requiredVersion string) error {
39+
sociExecutable, err := exec.LookPath("soci")
40+
if err != nil {
41+
log.L.WithError(err).Error("soci executable not found in path $PATH")
42+
log.L.Info("you might consider installing soci from: https://github.com/awslabs/soci-snapshotter/blob/main/docs/install.md")
43+
return err
44+
}
45+
46+
cmd := exec.Command(sociExecutable, "--version")
47+
output, err := cmd.CombinedOutput()
48+
if err != nil {
49+
return fmt.Errorf("failed to get SOCI version: %w", err)
50+
}
51+
52+
// Parse the version string
53+
versionStr := string(output)
54+
// Handle format like "soci version v0.10.0 8bbfe951bbb411798ee85dbd908544df4a1619a8.m"
55+
re := regexp.MustCompile(`v?(\d+\.\d+\.\d+)`)
56+
matches := re.FindStringSubmatch(versionStr)
57+
if len(matches) < 2 {
58+
return fmt.Errorf("failed to parse SOCI version from output: %s", versionStr)
59+
}
60+
61+
// Extract version number
62+
installedVersion := matches[1]
63+
64+
// Compare versions using semver
65+
v1, err := semver.NewVersion(installedVersion)
66+
if err != nil {
67+
return fmt.Errorf("failed to parse installed version %s: %v", installedVersion, err)
68+
}
69+
70+
v2, err := semver.NewVersion(requiredVersion)
71+
if err != nil {
72+
return fmt.Errorf("failed to parse minimum required version %s: %v", requiredVersion, err)
73+
}
74+
75+
if v1.LessThan(v2) {
76+
return fmt.Errorf("SOCI version %s is lower than the required version %s for the convert operation", installedVersion, requiredVersion)
77+
}
78+
79+
return nil
80+
}
81+
3482
// setupSociCommand creates and sets up a SOCI command with common configuration
3583
func setupSociCommand(gOpts types.GlobalCommandOptions) (*exec.Cmd, error) {
3684
sociExecutable, err := exec.LookPath("soci")
@@ -56,6 +104,11 @@ func setupSociCommand(gOpts types.GlobalCommandOptions) (*exec.Cmd, error) {
56104

57105
// ConvertSociIndexV2 converts an image to SOCI format and returns the converted image reference with digest
58106
func ConvertSociIndexV2(ctx context.Context, client *client.Client, srcRef string, destRef string, gOpts types.GlobalCommandOptions, platforms []string, sOpts types.SociOptions) (string, error) {
107+
// Check if SOCI version is at least 0.10.0 which is required for the convert operation
108+
if err := CheckSociVersion("0.10.0"); err != nil {
109+
return "", err
110+
}
111+
59112
sociCmd, err := setupSociCommand(gOpts)
60113
if err != nil {
61114
return "", err

pkg/testutil/nerdtest/requirements.go

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/containerd/nerdctl/v2/pkg/infoutil"
3636
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
3737
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
38+
"github.com/containerd/nerdctl/v2/pkg/snapshotterutil"
3839
"github.com/containerd/nerdctl/v2/pkg/testutil"
3940
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest/platform"
4041
)
@@ -416,44 +417,12 @@ var RemapIDs = &test.Requirement{
416417
func SociVersion(minVersion string) *test.Requirement {
417418
return &test.Requirement{
418419
Check: func(data test.Data, helpers test.Helpers) (bool, string) {
419-
sociExecutable, err := exec.LookPath("soci")
420+
// Use the common CheckSociVersion function from snapshotterutil
421+
err := snapshotterutil.CheckSociVersion(minVersion)
420422
if err != nil {
421-
return false, fmt.Sprintf("soci executable not found in path $PATH: %v", err)
422-
}
423-
424-
cmd := exec.Command(sociExecutable, "--version")
425-
output, err := cmd.Output()
426-
if err != nil {
427-
return false, fmt.Sprintf("failed to get soci version: %v", err)
428-
}
429-
430-
// Parse version from output
431-
// Example output format: "soci version v0.9.0 737f61a3db40c386f997c1f126344158aa3ad43c"
432-
versionStr := strings.TrimSpace(string(output))
433-
parts := strings.Fields(versionStr)
434-
if len(parts) < 3 {
435-
return false, fmt.Sprintf("unexpected soci version output format: %s", versionStr)
436-
}
437-
438-
// Extract version number without 'v' prefix
439-
installedVersion := strings.TrimPrefix(parts[2], "v")
440-
441-
// Compare versions
442-
v1, err := semver.NewVersion(installedVersion)
443-
if err != nil {
444-
return false, fmt.Sprintf("failed to parse installed version %s: %v", installedVersion, err)
445-
}
446-
447-
v2, err := semver.NewVersion(minVersion)
448-
if err != nil {
449-
return false, fmt.Sprintf("failed to parse minimum required version %s: %v", minVersion, err)
450-
}
451-
452-
if v1.LessThan(v2) {
453-
return false, fmt.Sprintf("installed soci version %s is older than required version %s", installedVersion, minVersion)
423+
return false, err.Error()
454424
}
455-
456-
return true, fmt.Sprintf("soci version %s meets minimum requirement %s", installedVersion, minVersion)
425+
return true, fmt.Sprintf("soci version meets minimum requirement %s", minVersion)
457426
},
458427
}
459428
}

0 commit comments

Comments
 (0)