Compare commits

..

5 Commits

Author SHA1 Message Date
Jonas Kaninda
81fc83be09 chore: update operator image tag version 2024-11-27 21:34:08 +01:00
Jonas Kaninda
46d1851f08 feat: add tls 2024-11-27 20:54:41 +01:00
Jonas Kaninda
19d12f40c7 Merge branch 'redis' into develop 2024-11-27 20:08:55 +01:00
Jonas Kaninda
e4ca801d29 fix: fix HorizontalPodAutoscaler update at every reconcile 2024-11-27 20:08:26 +01:00
Jonas Kaninda
513b33fce2 chore: add redis config 2024-11-27 11:20:20 +01:00
13 changed files with 125 additions and 48 deletions

View File

@@ -1,5 +1,5 @@
# Image URL to use all building/pushing image targets
IMG ?= jkaninda/goma-operator:0.1.0-rc.1
IMG ?= jkaninda/goma-operator:0.1.0-rc.2
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.31.0

View File

@@ -36,6 +36,10 @@ type Server struct {
IdleTimeout int `json:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty"`
// LogLevel log level, info, debug, trace, off
LogLevel string `json:"logLevel,omitempty" yaml:"logLevel,omitempty"`
// tls secret name
TlsSecretName string `json:"tlsSecretName,omitempty" yaml:"tlsSecretName,omitempty"`
// Redis contains redis database details
Redis Redis `json:"redis,omitempty" yaml:"redis,omitempty"`
// Cors holds proxy global cors
Cors Cors `json:"cors,omitempty" yaml:"cors,omitempty,omitempty"`
// InterceptErrors holds the status codes to intercept the error from backend
@@ -85,3 +89,8 @@ type RouteHealthCheck struct {
Timeout string `json:"timeout,omitempty" yaml:"timeout"`
HealthyStatuses []int `json:"healthyStatuses,omitempty" yaml:"healthyStatuses"`
}
type Redis struct {
// Addr redis hostname and port number :
Addr string `json:"addr,omitempty" yaml:"addr,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty"`
}

View File

@@ -305,6 +305,21 @@ func (in *Middlewares) DeepCopy() *Middlewares {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Redis) DeepCopyInto(out *Redis) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Redis.
func (in *Redis) DeepCopy() *Redis {
if in == nil {
return nil
}
out := new(Redis)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route) DeepCopyInto(out *Route) {
*out = *in
@@ -488,6 +503,7 @@ func (in *RoutesConfig) DeepCopy() *RoutesConfig {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Server) DeepCopyInto(out *Server) {
*out = *in
out.Redis = in.Redis
in.Cors.DeepCopyInto(&out.Cors)
if in.InterceptErrors != nil {
in, out := &in.InterceptErrors, &out.InterceptErrors

View File

@@ -1082,6 +1082,18 @@ spec:
readTimeout:
description: ReadTimeout defines proxy read timeout
type: integer
redis:
description: Redis contains redis database details
properties:
addr:
description: 'Addr redis hostname and port number :'
type: string
password:
type: string
type: object
tlsSecretName:
description: tls secret name
type: string
writeTimeout:
description: WriteTimeout defines proxy write timeout
type: integer

View File

@@ -5,4 +5,4 @@ kind: Kustomization
images:
- name: controller
newName: jkaninda/goma-operator
newTag: 0.1.0-rc.1
newTag: 0.1.0-rc.2

14
dist/install.yaml vendored
View File

@@ -1090,6 +1090,18 @@ spec:
readTimeout:
description: ReadTimeout defines proxy read timeout
type: integer
redis:
description: Redis contains redis database details
properties:
addr:
description: 'Addr redis hostname and port number :'
type: string
password:
type: string
type: object
tlsSecretName:
description: tls secret name
type: string
writeTimeout:
description: WriteTimeout defines proxy write timeout
type: integer
@@ -1776,7 +1788,7 @@ spec:
- --health-probe-bind-address=:8081
command:
- /manager
image: jkaninda/goma-operator:0.1.0-rc.1
image: jkaninda/goma-operator:0.1.0-rc.2
livenessProbe:
httpGet:
path: /healthz

View File

@@ -18,9 +18,47 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)
// createDeployment creates Kubernetes deployment
func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway, imageName string) error {
// createUpdateDeployment creates Kubernetes deployment
func createUpdateDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway, imageName string) error {
logger := log.FromContext(ctx)
var volumes []corev1.Volume
var volumeMounts []corev1.VolumeMount
volumes = append(volumes, corev1.Volume{
Name: "config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: req.Name,
},
},
},
})
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "config",
MountPath: ConfigPath,
ReadOnly: true,
})
if len(gateway.Spec.Server.TlsSecretName) != 0 {
volumes = append(volumes, corev1.Volume{
Name: req.Name,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: gateway.Spec.Server.TlsSecretName,
},
},
})
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: req.Name,
ReadOnly: true,
MountPath: CertsPath,
})
}
// check if ReplicaCount is defined
if gateway.Spec.ReplicaCount != 0 {
ReplicaCount = gateway.Spec.ReplicaCount
}
// Define the desired Deployment
deployment := &v1.Deployment{
ObjectMeta: metav1.ObjectMeta{
@@ -29,7 +67,7 @@ func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request
Labels: gateway.Labels,
},
Spec: v1.DeploymentSpec{
Replicas: int32Ptr(gateway.Spec.ReplicaCount), // Set desired replicas
Replicas: int32Ptr(ReplicaCount), // Set desired replicas
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": req.Name,
@@ -81,28 +119,11 @@ func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request
},
},
},
Resources: gateway.Spec.Resources,
VolumeMounts: []corev1.VolumeMount{
{
Name: "config",
MountPath: "/etc/goma",
ReadOnly: true,
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: req.Name,
},
},
},
Resources: gateway.Spec.Resources,
VolumeMounts: volumeMounts,
},
},
Volumes: volumes,
},
},
},

View File

@@ -19,21 +19,19 @@ package controller
import (
"context"
"fmt"
v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
"strings"
gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1"
"gopkg.in/yaml.v3"
v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"strings"
)
// GatewayReconciler reconciles a Gateway object
@@ -157,7 +155,7 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
}
err = createDeployment(*r, ctx, req, *gateway, imageName)
err = createUpdateDeployment(*r, ctx, req, *gateway, imageName)
if err != nil {
addCondition(&gateway.Status, "DeploymentNotReady", metav1.ConditionFalse, "DeploymentNotReady", "Failed to created deployment for Gateway")
logger.Error(err, "Failed to create Deployment")
@@ -185,11 +183,10 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
gateway.Status.Routes = int32(len(gomaConfig.Gateway.Routes))
if err := r.updateStatus(ctx, gateway); err != nil {
if err = r.updateStatus(ctx, gateway); err != nil {
logger.Error(err, "Failed to update resource status")
return ctrl.Result{}, err
}
logger.Info("Successfully updated resource status")
return ctrl.Result{}, nil
}

View File

@@ -2,7 +2,6 @@ package controller
import (
"context"
"fmt"
"slices"
"strings"
@@ -22,6 +21,13 @@ func gatewayConfig(r GatewayReconciler, ctx context.Context, req ctrl.Request, g
gomaConfig := &GatewayConfig{}
gomaConfig.Version = GatewayConfigVersion
gomaConfig.Gateway = mapToGateway(gateway.Spec)
// attach cert files
if len(gateway.Spec.Server.TlsSecretName) != 0 {
gomaConfig.Gateway.SSLKeyFile = TLSKeyFile
gomaConfig.Gateway.SSLCertFile = TLSCertFile
}
labelSelector := client.MatchingLabels{}
var middlewareNames []string
// List ConfigMaps in the namespace with the matching label
@@ -35,8 +41,6 @@ func gatewayConfig(r GatewayReconciler, ctx context.Context, req ctrl.Request, g
logger.Error(err, "Failed to list Middlewares")
return *gomaConfig
}
logger.Info(fmt.Sprintf("Listing Routes: size: %d", len(routes.Items)))
for _, route := range routes.Items {
logger.Info("Found Route", "Name", route.Name)
if route.Spec.Gateway == gateway.Name {
@@ -63,6 +67,11 @@ func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Reques
gomaConfig := &GatewayConfig{}
gomaConfig.Version = GatewayConfigVersion
gomaConfig.Gateway = mapToGateway(gateway.Spec)
// attach cert files
if len(gateway.Spec.Server.TlsSecretName) != 0 {
gomaConfig.Gateway.SSLKeyFile = TLSKeyFile
gomaConfig.Gateway.SSLCertFile = TLSCertFile
}
labelSelector := client.MatchingLabels{}
var middlewareNames []string
// List ConfigMaps in the namespace with the matching label
@@ -76,8 +85,6 @@ func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Reques
logger.Error(err, "Failed to list Middlewares")
return err
}
logger.Info(fmt.Sprintf("Listing Routes: size: %d", len(routes.Items)))
for _, route := range routes.Items {
logger.Info("Found Route", "Name", route.Name)
if route.Spec.Gateway == gateway.Name {

View File

@@ -83,6 +83,7 @@ func createHpa(r GatewayReconciler, ctx context.Context, req ctrl.Request, gatew
}
logger.Info("Created HorizontalPodAutoscaler", "HorizontalPodAutoscaler.Name", hpa.Name)
} else {
logger.Info("HorizontalPodAutoscaler already exists", "HorizontalPodAutoscaler.Name", hpa.Name)
// Update the Deployment if the spec has changed
if !equalHpaSpec(existHpa, *hpa) {
existHpa.Spec = hpa.Spec
@@ -98,17 +99,16 @@ func createHpa(r GatewayReconciler, ctx context.Context, req ctrl.Request, gatew
// Helper function to compare Deployment specs
func equalHpaSpec(existing, desired autoscalingv2.HorizontalPodAutoscaler) bool {
// A deep equality check or field-by-field comparison would be more accurate
if existing.Spec.MinReplicas != desired.Spec.MinReplicas {
if *existing.Spec.MinReplicas != *desired.Spec.MinReplicas {
return false
}
if existing.Spec.MaxReplicas != desired.Spec.MaxReplicas {
return false
}
if existing.Spec.Metrics[0].Resource.Target.AverageUtilization != desired.Spec.Metrics[0].Resource.Target.AverageUtilization {
if *existing.Spec.Metrics[0].Resource.Target.AverageUtilization != *desired.Spec.Metrics[0].Resource.Target.AverageUtilization {
return false
}
if existing.Spec.Metrics[1].Resource.Target.AverageUtilization != desired.Spec.Metrics[1].Resource.Target.AverageUtilization {
if *existing.Spec.Metrics[1].Resource.Target.AverageUtilization != *desired.Spec.Metrics[1].Resource.Target.AverageUtilization {
return false
}
return true

View File

@@ -9,7 +9,7 @@ type Gateway struct {
// SSLKeyFile SSL Private key file
SSLKeyFile string `yaml:"sslKeyFile"`
// Redis contains redis database details
Redis Redis `yaml:"redis"`
Redis gomaprojv1beta1.Redis `yaml:"redis"`
// WriteTimeout defines proxy write timeout
WriteTimeout int `yaml:"writeTimeout"`
// ReadTimeout defines proxy read timeout

View File

@@ -6,7 +6,7 @@ func mapToGateway(g gomaprojv1beta1.GatewaySpec) Gateway {
return Gateway{
SSLKeyFile: "",
SSLCertFile: "",
Redis: Redis{},
Redis: g.Server.Redis,
WriteTimeout: g.Server.WriteTimeout,
ReadTimeout: g.Server.ReadTimeout,
IdleTimeout: g.Server.IdleTimeout,

View File

@@ -2,7 +2,8 @@ package controller
const (
AppImageName = "jkaninda/goma-gateway"
ExtraConfigPath = "/etc/goma/extra/"
ConfigPath = "/etc/goma"
CertsPath = "/etc/goma/certs"
BasicAuth = "basic" // basic authentication middlewares
JWTAuth = "jwt" // JWT authentication middlewares
OAuth = "oauth"
@@ -12,6 +13,8 @@ const (
GatewayConfigVersion = "1.0"
FinalizerName = "finalizer.gomaproj.jonaskaninda.com"
ConfigName = "goma.yml"
TLSCertFile = "/etc/goma/certs/tls.crt"
TLSKeyFile = "/etc/goma/certs/tls.key"
)
var (