@@ -22,15 +22,63 @@ import (
22
22
"fmt"
23
23
"os"
24
24
"os/exec"
25
+ "regexp"
25
26
"strconv"
26
27
"strings"
27
28
29
+ "github.com/Masterminds/semver/v3"
28
30
"github.com/containerd/containerd/v2/client"
29
31
"github.com/containerd/log"
30
32
31
33
"github.com/containerd/nerdctl/v2/pkg/api/types"
32
34
)
33
35
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
+
34
82
// setupSociCommand creates and sets up a SOCI command with common configuration
35
83
func setupSociCommand (gOpts types.GlobalCommandOptions ) (* exec.Cmd , error ) {
36
84
sociExecutable , err := exec .LookPath ("soci" )
@@ -56,6 +104,11 @@ func setupSociCommand(gOpts types.GlobalCommandOptions) (*exec.Cmd, error) {
56
104
57
105
// ConvertSociIndexV2 converts an image to SOCI format and returns the converted image reference with digest
58
106
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
+
59
112
sociCmd , err := setupSociCommand (gOpts )
60
113
if err != nil {
61
114
return "" , err
0 commit comments