Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: configurable image via spec #93

Closed
wants to merge 1 commit into from
Closed
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
39 changes: 32 additions & 7 deletions api/v1alpha1/limitador_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@ import (

"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
"github.com/kuadrant/limitador-operator/pkg/helpers"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/kuadrant/limitador-operator/pkg/helpers"
"k8s.io/utils/env"
)

const (
DefaultServiceHTTPPort int32 = 8080
DefaultServiceGRPCPort int32 = 8081
DefaultServiceHTTPPort int32 = 8080
DefaultServiceGRPCPort int32 = 8081
DefaultLimitadorImage string = "quay.io/kuadrant/limitador:latest"

// Status conditions
StatusConditionReady string = "Ready"

ImageEnvVarKey = "RELATED_IMAGE_LIMITADOR"
)

var (
Expand Down Expand Up @@ -65,9 +68,6 @@ type LimitadorSpec struct {
// +optional
Replicas *int `json:"replicas,omitempty"`

// +optional
Version *string `json:"version,omitempty"`

// +optional
Listener *Listener `json:"listener,omitempty"`

Expand All @@ -85,6 +85,12 @@ type LimitadorSpec struct {

// +optional
ResourceRequirements *corev1.ResourceRequirements `json:"resourceRequirements,omitempty"`

// +optional
Image *string `json:"image"`

// +optional
ImagePullSecret *corev1.LocalObjectReference `json:"imagePullSecret"`
}

//+kubebuilder:object:root=true
Expand Down Expand Up @@ -135,6 +141,25 @@ func (l *Limitador) GetResourceRequirements() *corev1.ResourceRequirements {
return l.Spec.ResourceRequirements
}

func (l *Limitador) GetImage() string {
if l.Spec.Image == nil {
return env.GetString(ImageEnvVarKey, DefaultLimitadorImage)
}

return *l.Spec.Image
}

func (l *Limitador) GetImagePullSecrets() []corev1.LocalObjectReference {
var imagePullSecrets []corev1.LocalObjectReference
if l.Spec.ImagePullSecret == nil {
return nil
}

imagePullSecrets = append(imagePullSecrets, *l.Spec.ImagePullSecret)

return imagePullSecrets
}

//+kubebuilder:object:root=true

// LimitadorList contains a list of Limitador
Expand Down
51 changes: 51 additions & 0 deletions api/v1alpha1/limitador_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1alpha1

import (
"fmt"
"os"
"strconv"
"testing"

Expand Down Expand Up @@ -210,3 +211,53 @@ func TestLimitadorStatusEquals(t *testing.T) {
assert.Equal(subT, l.Equals(status, logr.Logger{}), true)
})
}

func TestLimitadorGetImage(t *testing.T) {
var (
testImage = "quay.io/test/limitador:nightly"
)
const envVarImage = "quay.io/env/limitador:custom"

setEnvVarImage := func(t *testing.T) {
if err := os.Setenv(ImageEnvVarKey, envVarImage); err != nil {
t.Fatal(err)
}
}
unSetEnvVarImage := func(t *testing.T) {
if err := os.Unsetenv(ImageEnvVarKey); err != nil {
t.Fatal(err)
}
}

t.Run("test default image if not specified in spec", func(subT *testing.T) {
l := &Limitador{}
assert.Equal(subT, l.GetImage(), DefaultLimitadorImage)
})

t.Run("test image override via env var", func(subT *testing.T) {
defer unSetEnvVarImage(subT)
setEnvVarImage(subT)
l := &Limitador{}
assert.Equal(subT, l.GetImage(), envVarImage)
})

t.Run("test image preference via spec over env var and default", func(subT *testing.T) {
defer unSetEnvVarImage(subT)
setEnvVarImage(subT)
l := &Limitador{Spec: LimitadorSpec{Image: &testImage}}
assert.Equal(subT, l.GetImage(), testImage)
})
}

func TestLimitadorGetImagePullSecrets(t *testing.T) {
t.Run("test nil returned if image not specified in spec", func(subT *testing.T) {
l := &Limitador{}
assert.DeepEqual(subT, l.GetImagePullSecrets(), []corev1.LocalObjectReference(nil))
})

t.Run("test pull secret returned if specified in spec", func(subT *testing.T) {
pullSecret := corev1.LocalObjectReference{Name: "pullSecret"}
l := &Limitador{Spec: LimitadorSpec{ImagePullSecret: &pullSecret}}
assert.DeepEqual(subT, l.GetImagePullSecrets(), []corev1.LocalObjectReference{pullSecret})
})
}
15 changes: 10 additions & 5 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions bundle/manifests/limitador.kuadrant.io_limitadors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,18 @@ spec:
type: array
type: object
type: object
image:
type: string
imagePullSecret:
description: LocalObjectReference contains enough information to let
you locate the referenced object inside the same namespace.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
x-kubernetes-map-type: atomic
limits:
items:
description: RateLimit defines the desired Limitador limit
Expand Down Expand Up @@ -1138,8 +1150,6 @@ spec:
type: object
type: object
type: object
version:
type: string
type: object
status:
description: LimitadorStatus defines the observed state of Limitador
Expand Down
14 changes: 12 additions & 2 deletions config/crd/bases/limitador.kuadrant.io_limitadors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,18 @@ spec:
type: array
type: object
type: object
image:
type: string
imagePullSecret:
description: LocalObjectReference contains enough information to let
you locate the referenced object inside the same namespace.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
x-kubernetes-map-type: atomic
limits:
items:
description: RateLimit defines the desired Limitador limit
Expand Down Expand Up @@ -1139,8 +1151,6 @@ spec:
type: object
type: object
type: object
version:
type: string
type: object
status:
description: LimitadorStatus defines the observed state of Limitador
Expand Down
1 change: 1 addition & 0 deletions controllers/limitador_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ func (r *LimitadorReconciler) reconcileDeployment(ctx context.Context, limitador
reconcilers.DeploymentCommandMutator,
reconcilers.DeploymentAffinityMutator,
reconcilers.DeploymentResourcesMutator,
reconcilers.DeploymentPullSecretMutator,
)

deployment := limitador.Deployment(limitadorObj, storageConfigSecret)
Expand Down
23 changes: 12 additions & 11 deletions controllers/limitador_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ var _ = Describe("Limitador controller", func() {
const (
LimitadorNamespace = "default"
LimitadorReplicas = 2
LimitadorImage = "quay.io/kuadrant/limitador"
LimitadorVersion = "0.3.0"
LimitadorImage = "quay.io/kuadrant/limitador:0.3.0"
LimitadorHTTPPort = 8000
LimitadorGRPCPort = 8001
LimitadorMaxUnavailable = 1
LimitdaorUpdatedMaxUnavailable = 3
LimitadorUpdatedMaxUnavailable = 3

timeout = time.Second * 10
interval = time.Millisecond * 250
Expand All @@ -47,11 +46,11 @@ var _ = Describe("Limitador controller", func() {
}
updatedMaxUnavailable := &intstr.IntOrString{
Type: 0,
IntVal: LimitdaorUpdatedMaxUnavailable,
IntVal: LimitadorUpdatedMaxUnavailable,
}

replicas := LimitadorReplicas
version := LimitadorVersion
image := LimitadorImage
httpPort := &limitadorv1alpha1.TransportProtocol{Port: &httpPortNumber}
grpcPort := &limitadorv1alpha1.TransportProtocol{Port: &grpcPortNumber}
affinity := &v1.Affinity{
Expand Down Expand Up @@ -104,7 +103,7 @@ var _ = Describe("Limitador controller", func() {
},
Spec: limitadorv1alpha1.LimitadorSpec{
Replicas: &replicas,
Version: &version,
Image: &image,
Affinity: affinity,
Listener: &limitadorv1alpha1.Listener{
HTTP: httpPort,
Expand Down Expand Up @@ -186,7 +185,7 @@ var _ = Describe("Limitador controller", func() {
Equal((int32)(LimitadorReplicas)),
)
Expect(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Image).Should(
Equal(LimitadorImage + ":" + LimitadorVersion),
Equal(LimitadorImage),
)
// It should contain at least the limits file
Expect(len(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Command) > 1).Should(BeTrue())
Expand Down Expand Up @@ -308,7 +307,7 @@ var _ = Describe("Limitador controller", func() {
It("Should modify the limitador deployment", func() {
updatedLimitador := limitadorv1alpha1.Limitador{}
replicas = LimitadorReplicas + 1
version = "latest"
image := "quay.io/test/limitador:nightly"
resourceRequirements := &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("200m"),
Expand All @@ -335,7 +334,8 @@ var _ = Describe("Limitador controller", func() {
}

updatedLimitador.Spec.Replicas = &replicas
updatedLimitador.Spec.Version = &version
updatedLimitador.Spec.Image = &image
updatedLimitador.Spec.ImagePullSecret = &v1.LocalObjectReference{Name: "pullSecret"}
updatedLimitador.Spec.ResourceRequirements = resourceRequirements
affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight = 99
updatedLimitador.Spec.Affinity = affinity
Expand All @@ -358,11 +358,12 @@ var _ = Describe("Limitador controller", func() {
}

correctReplicas := *updatedLimitadorDeployment.Spec.Replicas == LimitadorReplicas+1
correctImage := updatedLimitadorDeployment.Spec.Template.Spec.Containers[0].Image == LimitadorImage+":latest"
correctImage := updatedLimitadorDeployment.Spec.Template.Spec.Containers[0].Image == image
correctResources := reflect.DeepEqual(updatedLimitadorDeployment.Spec.Template.Spec.Containers[0].Resources, *resourceRequirements)
correctAffinity := updatedLimitadorDeployment.Spec.Template.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight == 99
correctImagePullSecret := len(updatedLimitadorDeployment.Spec.Template.Spec.ImagePullSecrets) == 1 && updatedLimitadorDeployment.Spec.Template.Spec.ImagePullSecrets[0].Name == updatedLimitador.Spec.ImagePullSecret.Name

return correctReplicas && correctImage && correctResources && correctAffinity
return correctReplicas && correctImage && correctResources && correctAffinity && correctImagePullSecret
}, timeout, interval).Should(BeTrue())
})

Expand Down
15 changes: 0 additions & 15 deletions pkg/limitador/image.go

This file was deleted.

11 changes: 0 additions & 11 deletions pkg/limitador/image_test.go

This file was deleted.

8 changes: 2 additions & 6 deletions pkg/limitador/k8s_objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,6 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
replicas = int32(*limitador.Spec.Replicas)
}

image := GetLimitadorImageVersion()
if limitador.Spec.Version != nil {
image = fmt.Sprintf("%s:%s", LimitadorRepository, *limitador.Spec.Version)
}

return &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
Expand All @@ -90,7 +85,7 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
Containers: []v1.Container{
{
Name: "limitador",
Image: image,
Image: limitador.GetImage(),
Command: deploymentContainerCommand(limitador.Spec.Storage, storageConfigSecret, limitador.Spec.RateLimitHeaders),
Ports: []v1.ContainerPort{
{
Expand Down Expand Up @@ -142,6 +137,7 @@ func Deployment(limitador *limitadorv1alpha1.Limitador, storageConfigSecret *v1.
ImagePullPolicy: v1.PullIfNotPresent,
},
},
ImagePullSecrets: limitador.GetImagePullSecrets(),
Volumes: []v1.Volume{
{
Name: "config-file",
Expand Down
4 changes: 2 additions & 2 deletions pkg/limitador/k8s_objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestConstants(t *testing.T) {
func newTestLimitadorObj(name, namespace string, limits []limitadorv1alpha1.RateLimit) *limitadorv1alpha1.Limitador {
var (
replicas = 1
version = "1.0"
image = "quay.io/kuadrant/limitador:1.0"
httpPort = int32(8000)
grpcPort = int32(8001)
)
Expand All @@ -43,7 +43,7 @@ func newTestLimitadorObj(name, namespace string, limits []limitadorv1alpha1.Rate
},
Spec: limitadorv1alpha1.LimitadorSpec{
Replicas: &replicas,
Version: &version,
Image: &image,
Listener: &limitadorv1alpha1.Listener{
HTTP: &limitadorv1alpha1.TransportProtocol{Port: &httpPort},
GRPC: &limitadorv1alpha1.TransportProtocol{Port: &grpcPort},
Expand Down
Loading
Loading