Skip to content

Commit

Permalink
Server version validation regex (#271)
Browse files Browse the repository at this point in the history
* trying regex validation

* updating logic

* refactoring

* fallback templates added

* more test cases

* more test cases for ubi

* conditional cleanup

* regex fix

* added missing plus sign

* Default the cassandra fallback image name to new format

* doc update
  • Loading branch information
respringer authored Oct 13, 2020
1 parent 4006e56 commit 71806ea
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6042,15 +6042,7 @@ spec:
serverVersion:
description: Version string for config builder, used to generate Cassandra
server configuration
enum:
- 6.8.0
- 6.8.1
- 6.8.2
- 6.8.3
- 6.8.4
- 3.11.6
- 3.11.7
- 4.0.0
pattern: (6\.8\.\d+)|(3\.11\.\d+)|(4\.0\.\d+)
type: string
serviceAccount:
description: The k8s service account to use for the server pods
Expand Down
4 changes: 2 additions & 2 deletions docs/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,9 @@ parameter to `true`.

## The server image user

If the server image runs as the "cassandra" user, then a PodSecurityContext for that user will be defined by cass-operator. Otherwise the server image is assumed to be running as the "root" user and a PodSecurityContext is not defined.
If the server image runs as the "cassandra" or "dse" user, then a PodSecurityContext for that user will be defined by cass-operator. Otherwise the server image is assumed to be running as the "root" user and a PodSecurityContext is not defined.

For serverType="dse", the server images run as the "cassandra" user.
For serverType="dse", the server images run as the "dse" user.

For serverType="cassandra", the cass-operator follows these steps in order to determine which user the docker image is run as:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6032,15 +6032,7 @@ spec:
serverVersion:
description: Version string for config builder, used to generate Cassandra
server configuration
enum:
- 6.8.0
- 6.8.1
- 6.8.2
- 6.8.3
- 6.8.4
- 3.11.6
- 3.11.7
- 4.0.0
pattern: (6\.8\.\d+)|(3\.11\.\d+)|(4\.0\.\d+)
type: string
serviceAccount:
description: The k8s service account to use for the server pods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ type CassandraDatacenterSpec struct {

// Version string for config builder,
// used to generate Cassandra server configuration
// +kubebuilder:validation:Enum="6.8.0";"6.8.1";"6.8.2";"6.8.3";"6.8.4";"3.11.6";"3.11.7";"4.0.0"
// +kubebuilder:validation:Pattern=(6\.8\.\d+)|(3\.11\.\d+)|(4\.0\.\d+)
ServerVersion string `json:"serverVersion"`

// Cassandra server image name.
Expand Down
109 changes: 108 additions & 1 deletion operator/pkg/apis/cassandra/v1beta1/cassandradatacenter_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
package v1beta1

import (
"os"
"testing"

"github.com/datastax/cass-operator/operator/pkg/images"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -54,7 +56,7 @@ func Test_makeImage(t *testing.T) {
errString: "",
},
{
name: "test unknown version",
name: "test unknown dse version",
args: args{
serverImage: "",
serverType: "dse",
Expand All @@ -63,6 +65,36 @@ func Test_makeImage(t *testing.T) {
want: "",
errString: "server 'dse' and version '6.7.0' do not work together",
},
{
name: "test unknown cassandra version",
args: args{
serverImage: "",
serverType: "cassandra",
serverVersion: "3.10.0",
},
want: "",
errString: "server 'cassandra' and version '3.10.0' do not work together",
},
{
name: "test fallback",
args: args{
serverImage: "",
serverType: "dse",
serverVersion: "6.8.1234",
},
want: "datastax/dse-server:6.8.1234",
errString: "",
},
{
name: "test cassandra fallback",
args: args{
serverImage: "",
serverType: "cassandra",
serverVersion: "3.11.1234",
},
want: "datastax/cassandra-mgmtapi:3.11.1234",
errString: "",
},
{
name: "test 6.8.4",
args: args{
Expand Down Expand Up @@ -93,6 +125,81 @@ func Test_makeImage(t *testing.T) {
}
}

func Test_makeUbiImage(t *testing.T) {
type args struct {
serverType string
serverImage string
serverVersion string
}
tests := []struct {
name string
args args
want string
errString string
}{
{
name: "test fallback",
args: args{
serverImage: "",
serverType: "dse",
serverVersion: "6.8.1234",
},
want: "datastax/dse-server:6.8.1234-ubi7",
errString: "",
},
{
name: "test cassandra fallback",
args: args{
serverImage: "",
serverType: "cassandra",
serverVersion: "4.0.1234",
},
want: "datastax/cassandra-mgmtapi:4.0.1234-ubi7",
errString: "",
},
{
name: "test unknown dse version",
args: args{
serverImage: "",
serverType: "dse",
serverVersion: "6.7.0",
},
want: "",
errString: "server 'dse' and version '6.7.0' do not work together",
},
{
name: "test unknown cassandra version",
args: args{
serverImage: "",
serverType: "cassandra",
serverVersion: "3.10.0",
},
want: "",
errString: "server 'cassandra' and version '3.10.0' do not work together",
},
}
for _, tt := range tests {
os.Setenv(images.EnvBaseImageOS, "example")

t.Run(tt.name, func(t *testing.T) {
got, err := makeImage(tt.args.serverType, tt.args.serverVersion, tt.args.serverImage)
if got != tt.want {
t.Errorf("makeImage() = %v, want %v", got, tt.want)
}
if err == nil {
if tt.errString != "" {
t.Errorf("makeImage() err = %v, want %v", err, tt.errString)
}
} else {
if err.Error() != tt.errString {
t.Errorf("makeImage() err = %v, want %v", err, tt.errString)
}
}
})
os.Unsetenv(images.EnvBaseImageOS)
}
}

func TestCassandraDatacenter_GetServerImage(t *testing.T) {
type fields struct {
TypeMeta metav1.TypeMeta
Expand Down
34 changes: 5 additions & 29 deletions operator/pkg/apis/cassandra/v1beta1/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"reflect"
"strings"

"github.com/datastax/cass-operator/operator/pkg/images"
"k8s.io/apimachinery/pkg/runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
Expand All @@ -31,26 +32,11 @@ func attemptedTo(action string, actionStrArgs ...interface{}) error {
func ValidateSingleDatacenter(dc CassandraDatacenter) error {
// Ensure serverVersion and serverType are compatible

var err error
if dc.Spec.ServerType == "dse" {
switch dc.Spec.ServerVersion {
case "6.8.0":
err = nil
case "6.8.1":
err = nil
case "6.8.2":
err = nil
case "6.8.3":
err = nil
case "6.8.4":
err = nil
default:
err = attemptedTo("use unsupported DSE version '%s'", dc.Spec.ServerVersion)
if !images.IsDseVersionSupported(dc.Spec.ServerVersion) {
return attemptedTo("use unsupported DSE version '%s'", dc.Spec.ServerVersion)
}
}
if err != nil {
return err
}

if dc.Spec.ServerType == "cassandra" && dc.Spec.DseWorkloads != nil {
if dc.Spec.DseWorkloads.AnalyticsEnabled || dc.Spec.DseWorkloads.GraphEnabled || dc.Spec.DseWorkloads.SearchEnabled {
Expand All @@ -59,20 +45,10 @@ func ValidateSingleDatacenter(dc CassandraDatacenter) error {
}

if dc.Spec.ServerType == "cassandra" {
switch dc.Spec.ServerVersion {
case "3.11.6":
err = nil
case "3.11.7":
err = nil
case "4.0.0":
err = nil
default:
err = attemptedTo("use unsupported Cassandra version '%s'", dc.Spec.ServerVersion)
if !images.IsOssVersionSupported(dc.Spec.ServerVersion) {
return attemptedTo("use unsupported Cassandra version '%s'", dc.Spec.ServerVersion)
}
}
if err != nil {
return err
}

isDse := dc.Spec.ServerType == "dse"
isCassandra3 := dc.Spec.ServerType == "cassandra" && strings.HasPrefix(dc.Spec.ServerVersion, "3.")
Expand Down
39 changes: 38 additions & 1 deletion operator/pkg/images/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,23 @@ package images
import (
"fmt"
"os"
"regexp"
"strings"

corev1 "k8s.io/api/core/v1"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)

var ValidDsePrefixes = []string{"6.8"}
var ValidOssPrefixes = []string{"3.11", "4.0"}

const (
envDefaultRegistryOverride = "DEFAULT_CONTAINER_REGISTRY_OVERRIDE"
envDefaultRegistryOverridePullSecrets = "DEFAULT_CONTAINER_REGISTRY_OVERRIDE_PULL_SECRETS"
EnvBaseImageOS = "BASE_IMAGE_OS"
ValidDseVersionRegexp = "6\\.8\\.\\d+"
ValidOssVersionRegexp = "(3\\.11\\.\\d+)|(4\\.0\\.\\d+)"
UbiImageSuffix = "-ubi7"
)

// How to add new images:
Expand Down Expand Up @@ -126,6 +133,16 @@ var versionToUBIDSE map[string]Image = map[string]Image{

var log = logf.Log.WithName("images")

func IsDseVersionSupported(version string) bool {
validVersions := regexp.MustCompile(ValidDseVersionRegexp)
return validVersions.MatchString(version)
}

func IsOssVersionSupported(version string) bool {
validVersions := regexp.MustCompile(ValidOssVersionRegexp)
return validVersions.MatchString(version)
}

func stripRegistry(image string) string {
comps := strings.Split(image, "/")

Expand Down Expand Up @@ -212,7 +229,27 @@ func GetCassandraImage(serverType, version string) (string, error) {
}

if !found {
return "", fmt.Errorf("server '%s' and version '%s' do not work together", serverType, version)
// For fallback images, just return the image name directly
fallbackImageName := ""

if serverType == "dse" {
if !IsDseVersionSupported(version) {
return "", fmt.Errorf("server 'dse' and version '%s' do not work together", version)
}
fallbackImageName = fmt.Sprintf("datastax/dse-server:%s", version)
} else {
if !IsOssVersionSupported(version) {
return "", fmt.Errorf("server 'cassandra' and version '%s' do not work together", version)
}
// We will fall back to the "mutable" cassandra image without the explicit mgmt-api version
fallbackImageName = fmt.Sprintf("datastax/cassandra-mgmtapi:%s", version)
}

if shouldUseUBI() {
return fmt.Sprintf("%s%s", fallbackImageName, UbiImageSuffix), nil
}

return fallbackImageName, nil
}

return GetImage(imageKey), nil
Expand Down

0 comments on commit 71806ea

Please sign in to comment.