Skip to content

Commit d8d7ecb

Browse files
committed
Add support of names and digest ScyllaDB image reference
1 parent f514347 commit d8d7ecb

File tree

4 files changed

+179
-21
lines changed

4 files changed

+179
-21
lines changed

assets/config/config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
operator:
2-
scyllaDBVersion: "6.2.0"
2+
scyllaDBVersion: "sha256:5b53a7c60d9f9555bb87791ff29b2e633c6f472aec00de7afaf4db1addc6d594"
33
# scyllaDBEnterpriseVersionNeedingConsistentClusterManagementOverride sets enterprise version
44
# that requires consistent_cluster_management workaround for restore.
55
# In the future, enterprise versions should be run as a different config instance in its own run.
@@ -13,6 +13,6 @@ operator:
1313
prometheusVersion: "v2.54.1"
1414
operatorTests:
1515
scyllaDBVersions:
16-
updateFrom: "6.2.0-rc2"
17-
upgradeFrom: "6.1.2"
16+
updateFrom: "sha256:cf00b8bfcce4d27e8527cad913e83db89af842e6cfb64ff4736bce83594b33d1"
17+
upgradeFrom: "sha256:0c1c6b49916230f62bdb0c2539b811b1a99d2c43d06ed810fad6f51015dea3ae"
1818
nodeSetupImage: "quay.io/scylladb/scylla-operator-images:node-setup-v0.0.3@sha256:c6b3de240cc5c884d5c617485bae35c51572cdfd39b6431d2e1f759c7d7feea1"

pkg/cmd/tests/options.go

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77
"regexp"
88
"strings"
99

10+
"github.com/blang/semver"
1011
"github.com/onsi/ginkgo/v2"
1112
configassets "github.com/scylladb/scylla-operator/assets/config"
1213
scyllav1 "github.com/scylladb/scylla-operator/pkg/api/scylla/v1"
1314
"github.com/scylladb/scylla-operator/pkg/genericclioptions"
1415
"github.com/scylladb/scylla-operator/pkg/helpers/slices"
16+
scyllasemver "github.com/scylladb/scylla-operator/pkg/semver"
1517
"github.com/scylladb/scylla-operator/test/e2e/framework"
1618
"github.com/spf13/cobra"
1719
apierrors "k8s.io/apimachinery/pkg/util/errors"
@@ -27,7 +29,7 @@ const (
2729
digestRegexp = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`
2830
)
2931

30-
var tagWithOptionalDigestRegexp = regexp.MustCompile("^" + referenceTagRegexp + "(?:@" + digestRegexp + ")?$")
32+
var tagOrDigestRegexp = regexp.MustCompile("^(?:" + referenceTagRegexp + "(?:@" + digestRegexp + ")?|" + digestRegexp + ")$")
3133

3234
type IngressControllerOptions struct {
3335
Address string
@@ -191,37 +193,64 @@ func (o *TestFrameworkOptions) Validate(args []string) error {
191193
errors = append(errors, fmt.Errorf("gcs-service-account-key-path and s3-credentials-file-path can't be set simultanously"))
192194
}
193195

194-
if !tagWithOptionalDigestRegexp.MatchString(o.ScyllaDBVersion) {
196+
if !tagOrDigestRegexp.MatchString(o.ScyllaDBVersion) {
195197
errors = append(errors, fmt.Errorf(
196-
"invalid scylladb-version format: %q. Expected format: <tag>[@<digest>]",
198+
"invalid scylladb-version format: %q. Expected format: <tag>, <tag>@<digest>, or <digest>",
197199
o.ScyllaDBVersion,
198200
))
199201
}
200202

201-
if !tagWithOptionalDigestRegexp.MatchString(o.ScyllaDBUpdateFrom) {
203+
if _, err := semver.Parse(o.ScyllaDBVersion); err != nil {
204+
semScyllaDBVersion, fullScyllaDBVersion, err := scyllasemver.GetImageVersionAndDigest("scylla", o.ScyllaDBVersion)
205+
if err != nil {
206+
return fmt.Errorf("failed to resolve ScyllaDB version: %w", err)
207+
}
208+
fmt.Println("Extracted ScyllaDB version:", fullScyllaDBVersion)
209+
o.ScyllaDBVersion = semScyllaDBVersion
210+
}
211+
212+
if !tagOrDigestRegexp.MatchString(o.ScyllaDBUpdateFrom) {
202213
errors = append(errors, fmt.Errorf(
203-
"invalid scylladb-update-from-version format: %q. Expected format: <tag>[@<digest>]",
214+
"invalid scylladb-update-from-version format: %q. Expected format: <tag>, <tag>@<digest>, or <digest>",
204215
o.ScyllaDBUpdateFrom,
205216
))
206217
}
207218

208-
if !tagWithOptionalDigestRegexp.MatchString(o.ScyllaDBUpgradeFrom) {
219+
if _, err := semver.Parse(o.ScyllaDBUpdateFrom); err != nil {
220+
semScyllaDBUpdateFromVersion, fullScyllaDBUpdateFromVersion, err := scyllasemver.GetImageVersionAndDigest("scylla", o.ScyllaDBUpdateFrom)
221+
if err != nil {
222+
return fmt.Errorf("failed to resolve ScyllaDB update from version: %w", err)
223+
}
224+
fmt.Println("Extracted ScyllaDB update from version:", fullScyllaDBUpdateFromVersion)
225+
o.ScyllaDBUpdateFrom = semScyllaDBUpdateFromVersion
226+
}
227+
228+
if !tagOrDigestRegexp.MatchString(o.ScyllaDBUpgradeFrom) {
209229
errors = append(errors, fmt.Errorf(
210-
"invalid scylladb-upgrade-from-version format: %q. Expected format: <tag>[@<digest>]",
230+
"invalid scylladb-upgrade-from-version format: %q. Expected format: <tag>, <tag>@<digest>, or <digest>",
211231
o.ScyllaDBUpgradeFrom,
212232
))
213233
}
214234

215-
if !tagWithOptionalDigestRegexp.MatchString(o.ScyllaDBManagerVersion) {
235+
if _, err := semver.Parse(o.ScyllaDBUpgradeFrom); err != nil {
236+
semScyllaDBUpgradeFromVersion, fullScyllaDBUpgradeFromVersion, err := scyllasemver.GetImageVersionAndDigest("scylla", o.ScyllaDBUpgradeFrom)
237+
if err != nil {
238+
return fmt.Errorf("failed to resolve ScyllaDB upgrade from version: %w", err)
239+
}
240+
fmt.Println("Extracted ScyllaDB upgrade from version:", fullScyllaDBUpgradeFromVersion)
241+
o.ScyllaDBUpgradeFrom = semScyllaDBUpgradeFromVersion
242+
}
243+
244+
if !tagOrDigestRegexp.MatchString(o.ScyllaDBManagerVersion) {
216245
errors = append(errors, fmt.Errorf(
217-
"invalid scylladb-manager-version format: %q. Expected format: <tag>[@<digest>]",
246+
"invalid scylladb-manager-version format: %q. Expected format: <tag>, <tag>@<digest>, or <digest>",
218247
o.ScyllaDBManagerVersion,
219248
))
220249
}
221250

222-
if !tagWithOptionalDigestRegexp.MatchString(o.ScyllaDBManagerAgentVersion) {
251+
if !tagOrDigestRegexp.MatchString(o.ScyllaDBManagerAgentVersion) {
223252
errors = append(errors, fmt.Errorf(
224-
"invalid scylladb-manager-agent-version format: %q. Expected format: <tag>[@<digest>]",
253+
"invalid scylladb-manager-agent-version format: %q. Expected format: <tag>, <tag>@<digest>, or <digest>",
225254
o.ScyllaDBManagerAgentVersion,
226255
))
227256
}

pkg/controller/scylladbdatacenter/sync_statefulsets.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/scylladb/scylla-operator/pkg/pointer"
1818
"github.com/scylladb/scylla-operator/pkg/resourceapply"
1919
"github.com/scylladb/scylla-operator/pkg/scyllaclient"
20+
scyllasemver "github.com/scylladb/scylla-operator/pkg/semver"
2021
"github.com/scylladb/scylla-operator/pkg/util/hash"
2122
"github.com/scylladb/scylla-operator/pkg/util/parallel"
2223
appsv1 "k8s.io/api/apps/v1"
@@ -976,15 +977,36 @@ func (sdcc *Controller) syncStatefulSets(
976977
existingVersionString, existingVersionLabelPresent := existing.Labels[naming.ScyllaVersionLabel]
977978

978979
if requiredVersionLabelPresent && existingVersionLabelPresent {
979-
requiredVersion, err := semver.Parse(requiredVersionString)
980-
if err != nil {
981-
return progressingConditions, err
980+
parsedSemRequiredVersion, parseErr := semver.Parse(requiredVersionString)
981+
if parseErr != nil {
982+
semRequiredVersion, _, getVersionErr := scyllasemver.GetImageVersionAndDigest("scylla", requiredVersionString)
983+
if getVersionErr != nil {
984+
return progressingConditions, getVersionErr
985+
}
986+
987+
parsedSemRequiredVersion, err = semver.Parse(semRequiredVersion)
988+
if err != nil {
989+
return progressingConditions, err
990+
}
982991
}
983-
existingVersion, err := semver.Parse(existingVersionString)
984-
if err != nil {
985-
return progressingConditions, err
992+
993+
requiredVersion := parsedSemRequiredVersion
994+
995+
parsedSemExistingVersion, parseErr := semver.Parse(existingVersionString)
996+
if parseErr != nil {
997+
semExistingVersion, _, getVersionErr := scyllasemver.GetImageVersionAndDigest("scylla", existingVersionString)
998+
if getVersionErr != nil {
999+
return progressingConditions, getVersionErr
1000+
}
1001+
1002+
parsedSemExistingVersion, err = semver.Parse(semExistingVersion)
1003+
if err != nil {
1004+
return progressingConditions, err
1005+
}
9861006
}
9871007

1008+
existingVersion := parsedSemExistingVersion
1009+
9881010
if requiredVersion.Major != existingVersion.Major ||
9891011
requiredVersion.Minor != existingVersion.Minor {
9901012
// We need to run hooks for version upgrades.

pkg/semver/version.go

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,33 @@ limitations under the License.
1717
package semver
1818

1919
import (
20+
"fmt"
21+
"regexp"
22+
"strings"
23+
"sync"
24+
2025
"github.com/blang/semver"
26+
"github.com/google/go-containerregistry/pkg/authn"
27+
"github.com/google/go-containerregistry/pkg/name"
28+
"github.com/google/go-containerregistry/pkg/v1/remote"
29+
"k8s.io/klog/v2"
30+
)
31+
32+
type ImageVersionInfo struct {
33+
SemVersion string
34+
fullVersion string
35+
}
36+
37+
var (
38+
cacheLockScyllaDBImageVersionAndDigest sync.RWMutex
39+
cacheScyllaDBImageVersionAndDigest = make(map[string]ImageVersionInfo)
2140
)
2241

2342
var (
2443
ScyllaVersionThatSupportsDisablingWritebackCache = semver.MustParse("2021.0.0")
2544
)
2645

27-
// ScyllaVersion contains the version of a cluster with unkown version support
46+
// ScyllaVersion contains the version of a cluster with unknown version support
2847
type ScyllaVersion struct {
2948
version semver.Version
3049
unknown bool
@@ -47,3 +66,91 @@ func (sv ScyllaVersion) SupportFeatureUnsafe(featureVersion semver.Version) bool
4766
func (sv ScyllaVersion) SupportFeatureSafe(featureVersion semver.Version) bool {
4867
return !sv.unknown && sv.version.GTE(featureVersion)
4968
}
69+
70+
func GetImageVersionAndDigest(imageName, version string) (string, string, error) {
71+
// If the version is already in <tag>@<digest> format, extract the semantic version
72+
if strings.Contains(version, "@") {
73+
semVersion, err := computeSemVersion(version)
74+
if err != nil {
75+
return "", "", err
76+
}
77+
return semVersion, version, nil
78+
}
79+
80+
imageReference := fmt.Sprintf("scylladb/%s", imageName)
81+
if strings.Contains(version, ":") {
82+
imageReference = fmt.Sprintf("%s@%s", imageReference, version)
83+
} else {
84+
imageReference = fmt.Sprintf("%s:%s", imageReference, version)
85+
}
86+
87+
cacheLockScyllaDBImageVersionAndDigest.RLock()
88+
cached, ok := cacheScyllaDBImageVersionAndDigest[imageReference]
89+
cacheLockScyllaDBImageVersionAndDigest.RUnlock()
90+
if ok {
91+
return cached.SemVersion, cached.fullVersion, nil
92+
}
93+
94+
fullVersion, err := computeFullImageVersion(imageReference)
95+
if err != nil {
96+
return "", "", err
97+
}
98+
99+
semVersion, err := computeSemVersion(fullVersion)
100+
if err != nil {
101+
return "", "", err
102+
}
103+
104+
cacheLockScyllaDBImageVersionAndDigest.Lock()
105+
cacheScyllaDBImageVersionAndDigest[imageReference] = ImageVersionInfo{
106+
SemVersion: semVersion,
107+
fullVersion: fullVersion,
108+
}
109+
cacheLockScyllaDBImageVersionAndDigest.Unlock()
110+
return semVersion, fullVersion, nil
111+
}
112+
113+
func computeFullImageVersion(imageReference string) (string, error) {
114+
ref, err := name.ParseReference(imageReference)
115+
if err != nil {
116+
return "", fmt.Errorf("can't parse image reference: %w", err)
117+
}
118+
119+
img, err := remote.Image(ref, remote.WithAuth(authn.Anonymous))
120+
if err != nil {
121+
return "", fmt.Errorf("can't fetch image: %w", err)
122+
}
123+
124+
configFile, err := img.ConfigFile()
125+
if err != nil {
126+
return "", fmt.Errorf("can't get image config: %w", err)
127+
}
128+
129+
versionLabel, ok := configFile.Config.Labels["org.opencontainers.image.version"]
130+
if !ok {
131+
klog.Warningf("no org.opencontainers.image.version label found for image: %s", imageReference)
132+
return "", fmt.Errorf("no version label found")
133+
}
134+
135+
digest, err := img.Digest()
136+
if err != nil {
137+
return "", fmt.Errorf("can't get image digest: %w", err)
138+
}
139+
return fmt.Sprintf("%s@%s", versionLabel, digest), nil
140+
}
141+
142+
func computeSemVersion(fullVersion string) (string, error) {
143+
reSemVersion := regexp.MustCompile(`\d+\.\d+\.\d+`)
144+
semVersion := reSemVersion.FindString(fullVersion)
145+
146+
if semVersion == "" {
147+
return "", fmt.Errorf("could not extract semantic version from: %s", fullVersion)
148+
}
149+
150+
reSuffix := regexp.MustCompile(`[~-]([a-zA-Z]+\d*)`)
151+
suffixMatch := reSuffix.FindStringSubmatch(fullVersion)
152+
if len(suffixMatch) > 1 {
153+
semVersion = fmt.Sprintf("%s-%s", semVersion, suffixMatch[1])
154+
}
155+
return semVersion, nil
156+
}

0 commit comments

Comments
 (0)