Compare commits

15 Commits

Author SHA1 Message Date
Jonas Kaninda
2794a5777d chore: update operator image tag version
Some checks failed
Lint / Run on Ubuntu (push) Successful in 24m20s
Tests / Run on Ubuntu (push) Successful in 15m46s
E2E Tests / Run on Ubuntu (push) Failing after 15m10s
2024-12-03 09:04:51 +01:00
Jonas Kaninda
09211ce7a3 chore: add finalizer to Route 2024-12-03 08:41:42 +01:00
Jonas Kaninda
bfb287ab01 Update readme 2024-12-02 07:55:42 +01:00
Jonas Kaninda
46c5ff9770 refactor: refactoring of code 2024-12-02 07:54:51 +01:00
Jonas Kaninda
00284095b0 refator: rename ssl to tls 2024-11-29 14:34:37 +01:00
Jonas Kaninda
9db20f4577 refator: rename ssl to tls 2024-11-29 14:05:16 +01:00
Jonas Kaninda
3bd4b68925 chore: change Route crd structure 2024-11-29 08:04:30 +01:00
Jonas Kaninda
614c05b283 chore: increase ReadinessProbe 2024-11-28 09:32:33 +01:00
Jonas Kaninda
c0400d7c75 chore: update operator image tag version 2024-11-28 07:53:09 +01:00
Jonas Kaninda
a7162fa72f chore: use predicte to stop reconcile on status subupdate 2024-11-28 07:39:43 +01:00
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
20 changed files with 463 additions and 394 deletions

View File

@@ -1,5 +1,5 @@
# Image URL to use all building/pushing image targets # 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.5
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.31.0 ENVTEST_K8S_VERSION = 1.31.0

View File

@@ -12,7 +12,7 @@ A Kubernetes operator for managing Goma Gateway.
- [Docker Hub](https://hub.docker.com/r/jkaninda/goma-operator) - [Docker Hub](https://hub.docker.com/r/jkaninda/goma-operator)
- [Github](https://github.com/jkaninda/goma-operator) - [Github](https://github.com/jkaninda/goma-operator)
### Documentation is found at <https://jkaninda.github.io/goma-gateway/install/kuberntes-advanced.html> ### Documentation is found at <https://jkaninda.github.io/goma-gateway/operator-manual/installation.html>
### 1. Kubernetes installation ### 1. Kubernetes installation

View File

@@ -25,8 +25,33 @@ import (
// RouteSpec defines the desired state of Route. // RouteSpec defines the desired state of Route.
type RouteSpec struct { type RouteSpec struct {
Gateway string `json:"gateway"` // Gateway defines the name of the Gateway resource
Routes []RouteConfig `json:"routes"` Gateway string `json:"gateway"`
// Path defines route path
Path string `json:"path" yaml:"path"`
// Hosts Domains/hosts based request routing
Hosts []string `json:"hosts,omitempty" yaml:"hosts"`
// Rewrite rewrites route path to desired path
Rewrite string `json:"rewrite,omitempty" yaml:"rewrite"`
// Methods allowed method
Methods []string `json:"methods,omitempty" yaml:"methods"`
// Destination Defines backend URL
Destination string `json:"destination,omitempty" yaml:"destination"`
Backends []string `json:"backends,omitempty" yaml:"backends"`
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify"`
// HealthCheck Defines the backend is health
HealthCheck RouteHealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
// Cors contains the route cors headers
Cors Cors `json:"cors,omitempty" yaml:"cors"`
RateLimit int `json:"rateLimit,omitempty" yaml:"rateLimit"`
// DisableHostFording Disables host forwarding.
DisableHostFording bool `json:"disableHostFording,omitempty" yaml:"disableHostFording"`
// InterceptErrors intercepts backend errors based on the status codes
InterceptErrors []int `json:"interceptErrors,omitempty" yaml:"interceptErrors"`
// BlockCommonExploits enable, disable block common exploits
BlockCommonExploits bool `json:"blockCommonExploits,omitempty" yaml:"blockCommonExploits"`
// Middlewares Defines route middleware
Middlewares []string `json:"middlewares,omitempty" yaml:"middlewares"`
} }
// RouteStatus defines the observed state of Route. // RouteStatus defines the observed state of Route.

View File

@@ -36,6 +36,10 @@ type Server struct {
IdleTimeout int `json:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty"` IdleTimeout int `json:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty"`
// LogLevel log level, info, debug, trace, off // LogLevel log level, info, debug, trace, off
LogLevel string `json:"logLevel,omitempty" yaml:"logLevel,omitempty"` 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 holds proxy global cors
Cors Cors `json:"cors,omitempty" yaml:"cors,omitempty,omitempty"` Cors Cors `json:"cors,omitempty" yaml:"cors,omitempty,omitempty"`
// InterceptErrors holds the status codes to intercept the error from backend // InterceptErrors holds the status codes to intercept the error from backend
@@ -47,41 +51,15 @@ type Server struct {
// EnableMetrics enable and disable server metrics // EnableMetrics enable and disable server metrics
EnableMetrics bool `json:"enableMetrics,omitempty" yaml:"enableMetrics"` EnableMetrics bool `json:"enableMetrics,omitempty" yaml:"enableMetrics"`
} }
type RouteConfig struct {
// Path defines route path
Path string `json:"path" yaml:"path"`
// Name defines route name
Name string `json:"name" yaml:"name"`
// Hosts Domains/hosts based request routing
Hosts []string `json:"hosts,omitempty" yaml:"hosts"`
// Rewrite rewrites route path to desired path
Rewrite string `json:"rewrite,omitempty" yaml:"rewrite"`
// Methods allowed method
Methods []string `json:"methods,omitempty" yaml:"methods"`
// Destination Defines backend URL
Destination string `json:"destination,omitempty" yaml:"destination"`
Backends []string `json:"backends,omitempty" yaml:"backends"`
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify"`
// HealthCheck Defines the backend is health
HealthCheck RouteHealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
// Cors contains the route cors headers
Cors Cors `json:"cors,omitempty" yaml:"cors"`
RateLimit int `json:"rateLimit,omitempty" yaml:"rateLimit"`
// DisableHostFording Disable host forwarding.
DisableHostFording bool `json:"disableHostFording,omitempty" yaml:"disableHostFording"`
// InterceptErrors intercepts backend errors based on the status codes
InterceptErrors []int `json:"interceptErrors,omitempty" yaml:"interceptErrors"`
// BlockCommonExploits enable, disable block common exploits
BlockCommonExploits bool `json:"blockCommonExploits,omitempty" yaml:"blockCommonExploits"`
// Middlewares Defines route middleware
Middlewares []string `json:"middlewares,omitempty" yaml:"middlewares"`
}
type RoutesConfig struct {
Routes []RouteConfig `json:"routes" yaml:"routes"`
}
type RouteHealthCheck struct { type RouteHealthCheck struct {
Path string `json:"path,omitempty" yaml:"path"` Path string `json:"path,omitempty" yaml:"path"`
Interval string `json:"interval,omitempty" yaml:"interval"` Interval string `json:"interval,omitempty" yaml:"interval"`
Timeout string `json:"timeout,omitempty" yaml:"timeout"` Timeout string `json:"timeout,omitempty" yaml:"timeout"`
HealthyStatuses []int `json:"healthyStatuses,omitempty" yaml:"healthyStatuses"` 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 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route) DeepCopyInto(out *Route) { func (in *Route) DeepCopyInto(out *Route) {
*out = *in *out = *in
@@ -332,48 +347,6 @@ func (in *Route) DeepCopyObject() runtime.Object {
return nil return nil
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RouteConfig) DeepCopyInto(out *RouteConfig) {
*out = *in
if in.Hosts != nil {
in, out := &in.Hosts, &out.Hosts
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Methods != nil {
in, out := &in.Methods, &out.Methods
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Backends != nil {
in, out := &in.Backends, &out.Backends
*out = make([]string, len(*in))
copy(*out, *in)
}
in.HealthCheck.DeepCopyInto(&out.HealthCheck)
in.Cors.DeepCopyInto(&out.Cors)
if in.InterceptErrors != nil {
in, out := &in.InterceptErrors, &out.InterceptErrors
*out = make([]int, len(*in))
copy(*out, *in)
}
if in.Middlewares != nil {
in, out := &in.Middlewares, &out.Middlewares
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteConfig.
func (in *RouteConfig) DeepCopy() *RouteConfig {
if in == nil {
return nil
}
out := new(RouteConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RouteHealthCheck) DeepCopyInto(out *RouteHealthCheck) { func (in *RouteHealthCheck) DeepCopyInto(out *RouteHealthCheck) {
*out = *in *out = *in
@@ -429,12 +402,32 @@ func (in *RouteList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RouteSpec) DeepCopyInto(out *RouteSpec) { func (in *RouteSpec) DeepCopyInto(out *RouteSpec) {
*out = *in *out = *in
if in.Routes != nil { if in.Hosts != nil {
in, out := &in.Routes, &out.Routes in, out := &in.Hosts, &out.Hosts
*out = make([]RouteConfig, len(*in)) *out = make([]string, len(*in))
for i := range *in { copy(*out, *in)
(*in)[i].DeepCopyInto(&(*out)[i]) }
} if in.Methods != nil {
in, out := &in.Methods, &out.Methods
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Backends != nil {
in, out := &in.Backends, &out.Backends
*out = make([]string, len(*in))
copy(*out, *in)
}
in.HealthCheck.DeepCopyInto(&out.HealthCheck)
in.Cors.DeepCopyInto(&out.Cors)
if in.InterceptErrors != nil {
in, out := &in.InterceptErrors, &out.InterceptErrors
*out = make([]int, len(*in))
copy(*out, *in)
}
if in.Middlewares != nil {
in, out := &in.Middlewares, &out.Middlewares
*out = make([]string, len(*in))
copy(*out, *in)
} }
} }
@@ -463,31 +456,10 @@ func (in *RouteStatus) DeepCopy() *RouteStatus {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RoutesConfig) DeepCopyInto(out *RoutesConfig) {
*out = *in
if in.Routes != nil {
in, out := &in.Routes, &out.Routes
*out = make([]RouteConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoutesConfig.
func (in *RoutesConfig) DeepCopy() *RoutesConfig {
if in == nil {
return nil
}
out := new(RoutesConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Server) DeepCopyInto(out *Server) { func (in *Server) DeepCopyInto(out *Server) {
*out = *in *out = *in
out.Redis = in.Redis
in.Cors.DeepCopyInto(&out.Cors) in.Cors.DeepCopyInto(&out.Cors)
if in.InterceptErrors != nil { if in.InterceptErrors != nil {
in, out := &in.InterceptErrors, &out.InterceptErrors in, out := &in.InterceptErrors, &out.InterceptErrors

View File

@@ -1082,6 +1082,18 @@ spec:
readTimeout: readTimeout:
description: ReadTimeout defines proxy read timeout description: ReadTimeout defines proxy read timeout
type: integer 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: writeTimeout:
description: WriteTimeout defines proxy write timeout description: WriteTimeout defines proxy write timeout
type: integer type: integer

View File

@@ -39,95 +39,84 @@ spec:
spec: spec:
description: RouteSpec defines the desired state of Route. description: RouteSpec defines the desired state of Route.
properties: properties:
gateway: backends:
type: string
routes:
items: items:
properties: type: string
backends:
items:
type: string
type: array
blockCommonExploits:
description: BlockCommonExploits enable, disable block common
exploits
type: boolean
cors:
description: Cors contains the route cors headers
properties:
headers:
additionalProperties:
type: string
description: Headers contains custom headers
type: object
origins:
description: Cors contains Allowed origins,
items:
type: string
type: array
type: object
destination:
description: Destination Defines backend URL
type: string
disableHostFording:
description: DisableHostFording Disable host forwarding.
type: boolean
healthCheck:
description: HealthCheck Defines the backend is health
properties:
healthyStatuses:
items:
type: integer
type: array
interval:
type: string
path:
type: string
timeout:
type: string
type: object
hosts:
description: Hosts Domains/hosts based request routing
items:
type: string
type: array
insecureSkipVerify:
type: boolean
interceptErrors:
description: InterceptErrors intercepts backend errors based
on the status codes
items:
type: integer
type: array
methods:
description: Methods allowed method
items:
type: string
type: array
middlewares:
description: Middlewares Defines route middleware
items:
type: string
type: array
name:
description: Name defines route name
type: string
path:
description: Path defines route path
type: string
rateLimit:
type: integer
rewrite:
description: Rewrite rewrites route path to desired path
type: string
required:
- name
- path
type: object
type: array type: array
blockCommonExploits:
description: BlockCommonExploits enable, disable block common exploits
type: boolean
cors:
description: Cors contains the route cors headers
properties:
headers:
additionalProperties:
type: string
description: Headers contains custom headers
type: object
origins:
description: Cors contains Allowed origins,
items:
type: string
type: array
type: object
destination:
description: Destination Defines backend URL
type: string
disableHostFording:
description: DisableHostFording Disables host forwarding.
type: boolean
gateway:
description: Gateway defines the name of the Gateway resource
type: string
healthCheck:
description: HealthCheck Defines the backend is health
properties:
healthyStatuses:
items:
type: integer
type: array
interval:
type: string
path:
type: string
timeout:
type: string
type: object
hosts:
description: Hosts Domains/hosts based request routing
items:
type: string
type: array
insecureSkipVerify:
type: boolean
interceptErrors:
description: InterceptErrors intercepts backend errors based on the
status codes
items:
type: integer
type: array
methods:
description: Methods allowed method
items:
type: string
type: array
middlewares:
description: Middlewares Defines route middleware
items:
type: string
type: array
path:
description: Path defines route path
type: string
rateLimit:
type: integer
rewrite:
description: Rewrite rewrites route path to desired path
type: string
required: required:
- gateway - gateway
- routes - path
type: object type: object
status: status:
description: RouteStatus defines the observed state of Route. description: RouteStatus defines the observed state of Route.

View File

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

173
dist/install.yaml vendored
View File

@@ -1090,6 +1090,18 @@ spec:
readTimeout: readTimeout:
description: ReadTimeout defines proxy read timeout description: ReadTimeout defines proxy read timeout
type: integer 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: writeTimeout:
description: WriteTimeout defines proxy write timeout description: WriteTimeout defines proxy write timeout
type: integer type: integer
@@ -1274,95 +1286,84 @@ spec:
spec: spec:
description: RouteSpec defines the desired state of Route. description: RouteSpec defines the desired state of Route.
properties: properties:
gateway: backends:
type: string
routes:
items: items:
properties: type: string
backends:
items:
type: string
type: array
blockCommonExploits:
description: BlockCommonExploits enable, disable block common
exploits
type: boolean
cors:
description: Cors contains the route cors headers
properties:
headers:
additionalProperties:
type: string
description: Headers contains custom headers
type: object
origins:
description: Cors contains Allowed origins,
items:
type: string
type: array
type: object
destination:
description: Destination Defines backend URL
type: string
disableHostFording:
description: DisableHostFording Disable host forwarding.
type: boolean
healthCheck:
description: HealthCheck Defines the backend is health
properties:
healthyStatuses:
items:
type: integer
type: array
interval:
type: string
path:
type: string
timeout:
type: string
type: object
hosts:
description: Hosts Domains/hosts based request routing
items:
type: string
type: array
insecureSkipVerify:
type: boolean
interceptErrors:
description: InterceptErrors intercepts backend errors based
on the status codes
items:
type: integer
type: array
methods:
description: Methods allowed method
items:
type: string
type: array
middlewares:
description: Middlewares Defines route middleware
items:
type: string
type: array
name:
description: Name defines route name
type: string
path:
description: Path defines route path
type: string
rateLimit:
type: integer
rewrite:
description: Rewrite rewrites route path to desired path
type: string
required:
- name
- path
type: object
type: array type: array
blockCommonExploits:
description: BlockCommonExploits enable, disable block common exploits
type: boolean
cors:
description: Cors contains the route cors headers
properties:
headers:
additionalProperties:
type: string
description: Headers contains custom headers
type: object
origins:
description: Cors contains Allowed origins,
items:
type: string
type: array
type: object
destination:
description: Destination Defines backend URL
type: string
disableHostFording:
description: DisableHostFording Disables host forwarding.
type: boolean
gateway:
description: Gateway defines the name of the Gateway resource
type: string
healthCheck:
description: HealthCheck Defines the backend is health
properties:
healthyStatuses:
items:
type: integer
type: array
interval:
type: string
path:
type: string
timeout:
type: string
type: object
hosts:
description: Hosts Domains/hosts based request routing
items:
type: string
type: array
insecureSkipVerify:
type: boolean
interceptErrors:
description: InterceptErrors intercepts backend errors based on the
status codes
items:
type: integer
type: array
methods:
description: Methods allowed method
items:
type: string
type: array
middlewares:
description: Middlewares Defines route middleware
items:
type: string
type: array
path:
description: Path defines route path
type: string
rateLimit:
type: integer
rewrite:
description: Rewrite rewrites route path to desired path
type: string
required: required:
- gateway - gateway
- routes - path
type: object type: object
status: status:
description: RouteStatus defines the observed state of Route. description: RouteStatus defines the observed state of Route.
@@ -1776,7 +1777,7 @@ spec:
- --health-probe-bind-address=:8081 - --health-probe-bind-address=:8081
command: command:
- /manager - /manager
image: jkaninda/goma-operator:0.1.0-rc.1 image: jkaninda/goma-operator:0.1.0-rc.5
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /healthz

1
go.mod
View File

@@ -44,6 +44,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect

2
go.sum
View File

@@ -70,6 +70,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=

View File

@@ -18,9 +18,47 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log"
) )
// createDeployment creates Kubernetes deployment // createUpdateDeployment creates Kubernetes deployment
func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway, imageName string) error { func createUpdateDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway, imageName string) error {
logger := log.FromContext(ctx) 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: "certs",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: gateway.Spec.Server.TlsSecretName,
},
},
})
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "certs",
ReadOnly: true,
MountPath: CertsPath,
})
}
// check if ReplicaCount is defined
if gateway.Spec.ReplicaCount != 0 {
ReplicaCount = gateway.Spec.ReplicaCount
}
// Define the desired Deployment // Define the desired Deployment
deployment := &v1.Deployment{ deployment := &v1.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@@ -29,7 +67,7 @@ func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request
Labels: gateway.Labels, Labels: gateway.Labels,
}, },
Spec: v1.DeploymentSpec{ Spec: v1.DeploymentSpec{
Replicas: int32Ptr(gateway.Spec.ReplicaCount), // Set desired replicas Replicas: int32Ptr(ReplicaCount), // Set desired replicas
Selector: &metav1.LabelSelector{ Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{ MatchLabels: map[string]string{
"app": req.Name, "app": req.Name,
@@ -62,7 +100,7 @@ func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request
}, },
}, },
ReadinessProbe: &corev1.Probe{ ReadinessProbe: &corev1.Probe{
InitialDelaySeconds: 5, InitialDelaySeconds: 15,
PeriodSeconds: 10, PeriodSeconds: 10,
ProbeHandler: corev1.ProbeHandler{ ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{ HTTPGet: &corev1.HTTPGetAction{
@@ -72,7 +110,7 @@ func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request
}, },
}, },
LivenessProbe: &corev1.Probe{ LivenessProbe: &corev1.Probe{
InitialDelaySeconds: 15, InitialDelaySeconds: 30,
PeriodSeconds: 30, PeriodSeconds: 30,
ProbeHandler: corev1.ProbeHandler{ ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{ HTTPGet: &corev1.HTTPGetAction{
@@ -81,28 +119,11 @@ func createDeployment(r GatewayReconciler, ctx context.Context, req ctrl.Request
}, },
}, },
}, },
Resources: gateway.Spec.Resources, Resources: gateway.Spec.Resources,
VolumeMounts: []corev1.VolumeMount{ VolumeMounts: volumeMounts,
{
Name: "config",
MountPath: "/etc/goma",
ReadOnly: true,
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: req.Name,
},
},
},
}, },
}, },
Volumes: volumes,
}, },
}, },
}, },
@@ -182,7 +203,7 @@ func equalDeploymentSpec(existing, desired v1.DeploymentSpec, autoScalingEnabled
} }
return true return true
} }
func (r *RouteReconciler) RestartDeployment(ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway) error { func restartDeployment(r client.Client, ctx context.Context, req ctrl.Request, gateway *gomaprojv1beta1.Gateway) error {
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
// Fetch the Deployment // Fetch the Deployment
var deployment v1.Deployment var deployment v1.Deployment

View File

@@ -19,21 +19,20 @@ package controller
import ( import (
"context" "context"
"fmt" "fmt"
v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
"strings"
gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1" gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime" ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"strings"
) )
// GatewayReconciler reconciles a Gateway object // GatewayReconciler reconciles a Gateway object
@@ -153,11 +152,16 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
addCondition(&gateway.Status, "ConfigMapReady", metav1.ConditionFalse, "ConfigMapReady", "Failed to update ConfigMap for Gateway") addCondition(&gateway.Status, "ConfigMapReady", metav1.ConditionFalse, "ConfigMapReady", "Failed to update ConfigMap for Gateway")
return ctrl.Result{}, err return ctrl.Result{}, err
} }
if err = restartDeployment(r.Client, ctx, req, gateway); err != nil {
logger.Error(err, "Failed to restart Deployment")
return ctrl.Result{}, err
}
logger.Info("Updated ConfigMap", "ConfigMap.Name", configMap.Name) logger.Info("Updated ConfigMap", "ConfigMap.Name", configMap.Name)
} }
} }
err = createDeployment(*r, ctx, req, *gateway, imageName) err = createUpdateDeployment(*r, ctx, req, *gateway, imageName)
if err != nil { if err != nil {
addCondition(&gateway.Status, "DeploymentNotReady", metav1.ConditionFalse, "DeploymentNotReady", "Failed to created deployment for Gateway") addCondition(&gateway.Status, "DeploymentNotReady", metav1.ConditionFalse, "DeploymentNotReady", "Failed to created deployment for Gateway")
logger.Error(err, "Failed to create Deployment") logger.Error(err, "Failed to create Deployment")
@@ -185,11 +189,10 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
} }
gateway.Status.Routes = int32(len(gomaConfig.Gateway.Routes)) 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") logger.Error(err, "Failed to update resource status")
return ctrl.Result{}, err return ctrl.Result{}, err
} }
logger.Info("Successfully updated resource status") logger.Info("Successfully updated resource status")
return ctrl.Result{}, nil return ctrl.Result{}, nil
} }
@@ -197,11 +200,6 @@ func (r *GatewayReconciler) updateStatus(ctx context.Context, gateway *gomaprojv
return r.Client.Status().Update(ctx, gateway) return r.Client.Status().Update(ctx, gateway)
} }
// Helper function to return a pointer to an int32
func int32Ptr(i int32) *int32 {
return &i
}
func addCondition(status *gomaprojv1beta1.GatewayStatus, condType string, statusType metav1.ConditionStatus, reason, message string) { func addCondition(status *gomaprojv1beta1.GatewayStatus, condType string, statusType metav1.ConditionStatus, reason, message string) {
for i, existingCondition := range status.Conditions { for i, existingCondition := range status.Conditions {
if existingCondition.Type == condType { if existingCondition.Type == condType {
@@ -294,8 +292,10 @@ func (r *GatewayReconciler) finalize(ctx context.Context, gateway *gomaprojv1bet
// SetupWithManager sets up the controller with the Manager. // SetupWithManager sets up the controller with the Manager.
func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error {
pred := predicate.GenerationChangedPredicate{}
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
For(&gomaprojv1beta1.Gateway{}). For(&gomaprojv1beta1.Gateway{}).
WithEventFilter(pred).
Owns(&corev1.ConfigMap{}). // Watch ConfigMaps created by the controller Owns(&corev1.ConfigMap{}). // Watch ConfigMaps created by the controller
Owns(&v1.Deployment{}). // Watch Deployments created by the controller Owns(&v1.Deployment{}). // Watch Deployments created by the controller
Owns(&corev1.Service{}). // Watch Services created by the controller Owns(&corev1.Service{}). // Watch Services created by the controller

View File

@@ -2,7 +2,7 @@ package controller
import ( import (
"context" "context"
"fmt" "github.com/jinzhu/copier"
"slices" "slices"
"strings" "strings"
@@ -21,7 +21,16 @@ func gatewayConfig(r GatewayReconciler, ctx context.Context, req ctrl.Request, g
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
gomaConfig := &GatewayConfig{} gomaConfig := &GatewayConfig{}
gomaConfig.Version = GatewayConfigVersion gomaConfig.Version = GatewayConfigVersion
gomaConfig.Gateway = mapToGateway(gateway.Spec) err := copier.Copy(&gomaConfig.Gateway, &gateway.Spec.Server)
if err != nil {
logger.Error(err, "failed to copy gateway spec")
}
// attach cert files
if len(gateway.Spec.Server.TlsSecretName) != 0 {
gomaConfig.Gateway.TlsCertFile = TLSCertFile
gomaConfig.Gateway.TlsKeyFile = TLSKeyFile
}
labelSelector := client.MatchingLabels{} labelSelector := client.MatchingLabels{}
var middlewareNames []string var middlewareNames []string
// List ConfigMaps in the namespace with the matching label // List ConfigMaps in the namespace with the matching label
@@ -35,17 +44,19 @@ func gatewayConfig(r GatewayReconciler, ctx context.Context, req ctrl.Request, g
logger.Error(err, "Failed to list Middlewares") logger.Error(err, "Failed to list Middlewares")
return *gomaConfig return *gomaConfig
} }
logger.Info(fmt.Sprintf("Listing Routes: size: %d", len(routes.Items)))
for _, route := range routes.Items { for _, route := range routes.Items {
logger.Info("Found Route", "Name", route.Name) logger.Info("Found Route", "Name", route.Name)
if route.Spec.Gateway == gateway.Name { if route.Spec.Gateway == gateway.Name {
gomaConfig.Gateway.Routes = append(gomaConfig.Gateway.Routes, route.Spec.Routes...) logger.Info("Found Route", "Name", route.Name)
for _, rt := range route.Spec.Routes { rt := Route{}
middlewareNames = append(middlewareNames, rt.Middlewares...) err := copier.Copy(&rt, &route.Spec)
if err != nil {
logger.Error(err, "Failed to deep copy Route", "Name", route.Name)
return *gomaConfig
} }
rt.Name = route.Name
gomaConfig.Gateway.Routes = append(gomaConfig.Gateway.Routes, rt)
middlewareNames = append(middlewareNames, rt.Middlewares...)
} }
} }
for _, mid := range middlewares.Items { for _, mid := range middlewares.Items {
@@ -58,35 +69,47 @@ func gatewayConfig(r GatewayReconciler, ctx context.Context, req ctrl.Request, g
} }
return *gomaConfig return *gomaConfig
} }
func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway) error { func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Request, gateway gomaprojv1beta1.Gateway) (bool, error) {
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
gomaConfig := &GatewayConfig{} gomaConfig := &GatewayConfig{}
gomaConfig.Version = GatewayConfigVersion gomaConfig.Version = GatewayConfigVersion
gomaConfig.Gateway = mapToGateway(gateway.Spec) err := copier.Copy(&gomaConfig.Gateway, &gateway.Spec.Server)
if err != nil {
logger.Error(err, "failed to copy gateway spec")
}
// attach cert files
if len(gateway.Spec.Server.TlsSecretName) != 0 {
gomaConfig.Gateway.TlsCertFile = TLSCertFile
gomaConfig.Gateway.TlsKeyFile = TLSKeyFile
}
labelSelector := client.MatchingLabels{} labelSelector := client.MatchingLabels{}
var middlewareNames []string var middlewareNames []string
// List ConfigMaps in the namespace with the matching label // List ConfigMaps in the namespace with the matching label
var routes gomaprojv1beta1.RouteList var routes gomaprojv1beta1.RouteList
if err := r.List(ctx, &routes, labelSelector, client.InNamespace(req.Namespace)); err != nil { if err := r.List(ctx, &routes, labelSelector, client.InNamespace(req.Namespace)); err != nil {
logger.Error(err, "Failed to list Routes") logger.Error(err, "Failed to list Routes")
return err return false, err
} }
var middlewares gomaprojv1beta1.MiddlewareList var middlewares gomaprojv1beta1.MiddlewareList
if err := r.List(ctx, &middlewares, labelSelector, client.InNamespace(req.Namespace)); err != nil { if err := r.List(ctx, &middlewares, labelSelector, client.InNamespace(req.Namespace)); err != nil {
logger.Error(err, "Failed to list Middlewares") logger.Error(err, "Failed to list Middlewares")
return err return false, err
} }
logger.Info(fmt.Sprintf("Listing Routes: size: %d", len(routes.Items)))
for _, route := range routes.Items { for _, route := range routes.Items {
logger.Info("Found Route", "Name", route.Name) logger.Info("Found Route", "Name", route.Name)
if route.Spec.Gateway == gateway.Name { if route.Spec.Gateway == gateway.Name {
gomaConfig.Gateway.Routes = append(gomaConfig.Gateway.Routes, route.Spec.Routes...) if route.ObjectMeta.DeletionTimestamp.IsZero() {
for _, rt := range route.Spec.Routes { rt := Route{}
err := copier.Copy(&rt, &route.Spec)
if err != nil {
logger.Error(err, "Failed to deep copy Route", "Name", route.Name)
return false, err
}
rt.Name = route.Name
gomaConfig.Gateway.Routes = append(gomaConfig.Gateway.Routes, rt)
middlewareNames = append(middlewareNames, rt.Middlewares...) middlewareNames = append(middlewareNames, rt.Middlewares...)
} }
} }
} }
for _, mid := range middlewares.Items { for _, mid := range middlewares.Items {
@@ -101,7 +124,7 @@ func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Reques
yamlContent, err := yaml.Marshal(&gomaConfig) yamlContent, err := yaml.Marshal(&gomaConfig)
if err != nil { if err != nil {
logger.Error(err, "Unable to marshal YAML") logger.Error(err, "Unable to marshal YAML")
return err return false, err
} }
// Define the desired ConfigMap // Define the desired ConfigMap
configMap := &corev1.ConfigMap{ configMap := &corev1.ConfigMap{
@@ -122,18 +145,18 @@ func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Reques
err = r.Get(ctx, types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}, &existingConfigMap) err = r.Get(ctx, types.NamespacedName{Name: configMap.Name, Namespace: configMap.Namespace}, &existingConfigMap)
if err != nil && client.IgnoreNotFound(err) != nil { if err != nil && client.IgnoreNotFound(err) != nil {
logger.Error(err, "Failed to get ConfigMap") logger.Error(err, "Failed to get ConfigMap")
return err return false, err
} }
if err != nil && client.IgnoreNotFound(err) == nil { if err != nil && client.IgnoreNotFound(err) == nil {
// Create the ConfigMap if it doesn't exist // Create the ConfigMap if it doesn't exist
if err = controllerutil.SetControllerReference(&gateway, configMap, r.Scheme); err != nil { if err = controllerutil.SetControllerReference(&gateway, configMap, r.Scheme); err != nil {
logger.Error(err, "Failed to set controller reference") logger.Error(err, "Failed to set controller reference")
return err return false, err
} }
if err = r.Create(ctx, configMap); err != nil { if err = r.Create(ctx, configMap); err != nil {
logger.Error(err, "Failed to create ConfigMap") logger.Error(err, "Failed to create ConfigMap")
return err return false, err
} }
logger.Info("Created ConfigMap", "ConfigMap.Name", configMap.Name) logger.Info("Created ConfigMap", "ConfigMap.Name", configMap.Name)
} else { } else {
@@ -142,13 +165,13 @@ func updateGatewayConfig(r RouteReconciler, ctx context.Context, req ctrl.Reques
existingConfigMap.Data = configMap.Data existingConfigMap.Data = configMap.Data
if err = r.Update(ctx, &existingConfigMap); err != nil { if err = r.Update(ctx, &existingConfigMap); err != nil {
logger.Error(err, "Failed to update ConfigMap") logger.Error(err, "Failed to update ConfigMap")
return err return false, err
} }
logger.Info("Updated ConfigMap", "ConfigMap.Name", configMap.Name) logger.Info("Updated ConfigMap", "ConfigMap.Name", configMap.Name)
} }
} }
return nil return true, nil
} }
@@ -208,3 +231,8 @@ func mapMid(middleware gomaprojv1beta1.Middleware) *Middleware {
} }
return mid return mid
} }
// Helper function to return a pointer to an int32
func int32Ptr(i int32) *int32 {
return &i
}

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) logger.Info("Created HorizontalPodAutoscaler", "HorizontalPodAutoscaler.Name", hpa.Name)
} else { } else {
logger.Info("HorizontalPodAutoscaler already exists", "HorizontalPodAutoscaler.Name", hpa.Name)
// Update the Deployment if the spec has changed // Update the Deployment if the spec has changed
if !equalHpaSpec(existHpa, *hpa) { if !equalHpaSpec(existHpa, *hpa) {
existHpa.Spec = hpa.Spec 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 // Helper function to compare Deployment specs
func equalHpaSpec(existing, desired autoscalingv2.HorizontalPodAutoscaler) bool { 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 return false
} }
if existing.Spec.MaxReplicas != desired.Spec.MaxReplicas { if existing.Spec.MaxReplicas != desired.Spec.MaxReplicas {
return false 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 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 false
} }
return true return true

View File

@@ -18,6 +18,7 @@ package controller
import ( import (
"context" "context"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1" gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@@ -55,8 +56,8 @@ type RouteReconciler struct {
func (r *RouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { func (r *RouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
// Fetch the custom resource // Fetch the custom resource
var route gomaprojv1beta1.Route route := &gomaprojv1beta1.Route{}
if err := r.Get(ctx, req.NamespacedName, &route); err != nil { if err := r.Get(ctx, req.NamespacedName, route); err != nil {
logger.Error(err, "Unable to fetch CustomResource") logger.Error(err, "Unable to fetch CustomResource")
return ctrl.Result{}, client.IgnoreNotFound(err) return ctrl.Result{}, client.IgnoreNotFound(err)
} }
@@ -65,15 +66,48 @@ func (r *RouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
logger.Error(err, "Failed to fetch Gateway") logger.Error(err, "Failed to fetch Gateway")
return ctrl.Result{}, err return ctrl.Result{}, err
} }
err := updateGatewayConfig(*r, ctx, req, gateway)
// Check if the object is being deleted and if so, handle it
if route.ObjectMeta.DeletionTimestamp.IsZero() {
if !controllerutil.ContainsFinalizer(route, FinalizerName) {
controllerutil.AddFinalizer(route, FinalizerName)
err := r.Update(ctx, route)
if err != nil {
return ctrl.Result{}, err
}
}
} else {
if controllerutil.ContainsFinalizer(route, FinalizerName) {
// Once finalization is done, remove the finalizer
ok, err := updateGatewayConfig(*r, ctx, req, gateway)
if err != nil {
return ctrl.Result{}, err
}
if ok {
if err = restartDeployment(r.Client, ctx, req, &gateway); err != nil {
return ctrl.Result{}, err
}
}
// Remove the finalizer
controllerutil.RemoveFinalizer(route, FinalizerName)
err = r.Update(ctx, route)
if err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
ok, err := updateGatewayConfig(*r, ctx, req, gateway)
if err != nil { if err != nil {
return ctrl.Result{}, err return ctrl.Result{}, err
} }
// if ok {
if err = r.RestartDeployment(ctx, req, gateway); err != nil { if err = restartDeployment(r.Client, ctx, req, &gateway); err != nil {
logger.Error(err, "Failed to restart Deployment") logger.Error(err, "Failed to restart Deployment")
return ctrl.Result{}, err return ctrl.Result{}, err
}
} }
return ctrl.Result{}, nil return ctrl.Result{}, nil
} }

View File

@@ -77,15 +77,11 @@ var _ = Describe("Route Controller", func() {
}, },
Spec: gomaprojv1beta1.RouteSpec{ Spec: gomaprojv1beta1.RouteSpec{
Gateway: resourceName, Gateway: resourceName,
Routes: []gomaprojv1beta1.RouteConfig{
{ Path: "/",
Path: "/", Rewrite: "/",
Name: resourceName, Destination: "https://example.com",
Rewrite: "/", Methods: []string{"GET", "POST"},
Destination: "https://example.com",
Methods: []string{"GET", "POST"},
},
},
}, },
} }
Expect(k8sClient.Create(ctx, resource)).To(Succeed()) Expect(k8sClient.Create(ctx, resource)).To(Succeed())

View File

@@ -4,12 +4,12 @@ import gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1"
// Gateway contains Goma Proxy Gateway's configs // Gateway contains Goma Proxy Gateway's configs
type Gateway struct { type Gateway struct {
// SSLCertFile SSL Certificate file // TlsCertFile SSL Certificate file
SSLCertFile string `yaml:"sslCertFile"` TlsCertFile string `yaml:"tlsCertFile"`
// SSLKeyFile SSL Private key file // TlsKeyFile SSL Private key file
SSLKeyFile string `yaml:"sslKeyFile"` TlsKeyFile string `yaml:"tlsKeyFile"`
// Redis contains redis database details // Redis contains redis database details
Redis Redis `yaml:"redis"` Redis gomaprojv1beta1.Redis `yaml:"redis"`
// WriteTimeout defines proxy write timeout // WriteTimeout defines proxy write timeout
WriteTimeout int `yaml:"writeTimeout"` WriteTimeout int `yaml:"writeTimeout"`
// ReadTimeout defines proxy read timeout // ReadTimeout defines proxy read timeout
@@ -28,10 +28,38 @@ type Gateway struct {
DisableKeepAlive bool `yaml:"disableKeepAlive"` DisableKeepAlive bool `yaml:"disableKeepAlive"`
EnableMetrics bool `yaml:"enableMetrics"` EnableMetrics bool `yaml:"enableMetrics"`
// InterceptErrors holds the status codes to intercept the error from backend // InterceptErrors holds the status codes to intercept the error from backend
InterceptErrors []int `yaml:"interceptErrors,omitempty"` InterceptErrors []int `yaml:"interceptErrors,omitempty"`
Routes []gomaprojv1beta1.RouteConfig `json:"routes,omitempty" yaml:"routes,omitempty"` Routes []Route `json:"routes,omitempty" yaml:"routes,omitempty"`
}
type Route struct {
// Path defines route path
Path string `json:"path" yaml:"path"`
// Name defines route name
Name string `json:"name" yaml:"name"`
// Hosts Domains/hosts based request routing
Hosts []string `json:"hosts,omitempty" yaml:"hosts"`
// Rewrite rewrites route path to desired path
Rewrite string `json:"rewrite,omitempty" yaml:"rewrite"`
// Methods allowed method
Methods []string `json:"methods,omitempty" yaml:"methods"`
// Destination Defines backend URL
Destination string `json:"destination,omitempty" yaml:"destination"`
Backends []string `json:"backends,omitempty" yaml:"backends"`
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify"`
// HealthCheck Defines the backend is health
HealthCheck gomaprojv1beta1.RouteHealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"`
// Cors contains the route cors headers
Cors gomaprojv1beta1.Cors `json:"cors,omitempty" yaml:"cors"`
RateLimit int `json:"rateLimit,omitempty" yaml:"rateLimit"`
// DisableHostFording Disable host forwarding.
DisableHostFording bool `json:"disableHostFording,omitempty" yaml:"disableHostFording"`
// InterceptErrors intercepts backend errors based on the status codes
InterceptErrors []int `json:"interceptErrors,omitempty" yaml:"interceptErrors"`
// BlockCommonExploits enable, disable block common exploits
BlockCommonExploits bool `json:"blockCommonExploits,omitempty" yaml:"blockCommonExploits"`
// Middlewares Defines route middleware
Middlewares []string `json:"middlewares,omitempty" yaml:"middlewares"`
} }
type Redis struct { type Redis struct {
// Addr redis hostname and port number : // Addr redis hostname and port number :
Addr string `yaml:"addr"` Addr string `yaml:"addr"`

View File

@@ -1,22 +1 @@
package controller package controller
import gomaprojv1beta1 "github.com/jkaninda/goma-operator/api/v1beta1"
func mapToGateway(g gomaprojv1beta1.GatewaySpec) Gateway {
return Gateway{
SSLKeyFile: "",
SSLCertFile: "",
Redis: Redis{},
WriteTimeout: g.Server.WriteTimeout,
ReadTimeout: g.Server.ReadTimeout,
IdleTimeout: g.Server.IdleTimeout,
LogLevel: g.Server.LogLevel,
Cors: g.Server.Cors,
DisableHealthCheckStatus: g.Server.DisableHealthCheckStatus,
DisableRouteHealthCheckError: g.Server.DisableHealthCheckStatus,
DisableKeepAlive: g.Server.DisableKeepAlive,
InterceptErrors: g.Server.InterceptErrors,
EnableMetrics: g.Server.EnableMetrics,
}
}

View File

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