1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

OAS-9904 Introduce ArangoDeployment Gateway Group (#1694)

Co-authored-by: ajanikow <12255597+ajanikow@users.noreply.github.com>
This commit is contained in:
jwierzbo 2024-08-26 09:08:33 +02:00 committed by GitHub
parent 08831bf349
commit 1facdeabdb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 8259 additions and 51 deletions

View file

@ -11,6 +11,7 @@
- (Feature) ServerGroup Pointer - (Feature) ServerGroup Pointer
- (Feature) Envoy AuthV3 Integration - (Feature) Envoy AuthV3 Integration
- (Maintenance) Switch to ubuntu:24.04 base image - (Maintenance) Switch to ubuntu:24.04 base image
- (Feature) Gateway Group for ArangoDeployment
## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23) ## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23)
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries - (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries

View file

@ -475,7 +475,7 @@ $(BIN): $(VBIN_LINUX_AMD64) $(VBIN_OPS_LINUX_AMD64) $(VBIN_INT_LINUX_AMD64)
@cp "$(VBIN_OPS_LINUX_AMD64)" "$(BIN_OPS)" @cp "$(VBIN_OPS_LINUX_AMD64)" "$(BIN_OPS)"
.PHONY: docker .PHONY: docker
docker: check-vars $(VBIN_LINUX_AMD64) $(VBIN_LINUX_ARM64) docker: clean check-vars $(VBIN_LINUX_AMD64) $(VBIN_LINUX_ARM64)
ifdef PUSHIMAGES ifdef PUSHIMAGES
docker buildx build --no-cache -f $(DOCKERFILE) --build-arg GOVERSION=$(GOVERSION) --build-arg DISTRIBUTION=$(DISTRIBUTION) \ docker buildx build --no-cache -f $(DOCKERFILE) --build-arg GOVERSION=$(GOVERSION) --build-arg DISTRIBUTION=$(DISTRIBUTION) \
--build-arg "VERSION=${VERSION_MAJOR_MINOR_PATCH}" --build-arg "RELEASE_MODE=$(RELEASE_MODE)" \ --build-arg "VERSION=${VERSION_MAJOR_MINOR_PATCH}" --build-arg "RELEASE_MODE=$(RELEASE_MODE)" \

View file

@ -74,6 +74,7 @@ covers individual newer features separately.
| Feature | Operator Version | Introduced | ArangoDB Version | ArangoDB Edition | State | Enabled | Flag | Remarks | | Feature | Operator Version | Introduced | ArangoDB Version | ArangoDB Edition | State | Enabled | Flag | Remarks |
|:------------------------------------------------------------------------------|:-----------------|:-----------|:-----------------|:----------------------|:-------------|:--------|:-------------------------------------------------------|:------------------------------------------------------------------------------------------| |:------------------------------------------------------------------------------|:-----------------|:-----------|:-----------------|:----------------------|:-------------|:--------|:-------------------------------------------------------|:------------------------------------------------------------------------------------------|
| Gateway | 1.2.43 | 1.2.43 | >= 3.8.0 | Community, Enterprise | Alpha | True | N/A | Support for ArangoDeployment Gateway Group |
| Cleanup Imported Backups | 1.2.41 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | False | --deployment.feature.backup-cleanup | Cleanup backups created outside of the Operator and imported into Kubernetes ArangoBackup | | Cleanup Imported Backups | 1.2.41 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | False | --deployment.feature.backup-cleanup | Cleanup backups created outside of the Operator and imported into Kubernetes ArangoBackup |
| Upscale resources spec in init containers | 1.2.36 | 1.2.36 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.init-containers-upscale-resources | Upscale resources spec to built-in init containers if they are not specified or lower | | Upscale resources spec in init containers | 1.2.36 | 1.2.36 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.init-containers-upscale-resources | Upscale resources spec to built-in init containers if they are not specified or lower |
| Create backups asynchronously | 1.2.35 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.async-backup-creation | Create backups asynchronously to avoid blocking the operator and reaching the timeout | | Create backups asynchronously | 1.2.35 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.async-backup-creation | Create backups asynchronously to avoid blocking the operator and reaching the timeout |

View file

@ -973,7 +973,7 @@ Possible Values:
### .spec.architecture ### .spec.architecture
Type: `[]string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L254)</sup> Type: `[]string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L257)</sup>
Architecture defines the list of supported architectures. Architecture defines the list of supported architectures.
First element on the list is marked as default architecture. First element on the list is marked as default architecture.
@ -1050,7 +1050,7 @@ KillPodProbability is the chance of a pod being killed during an event
### .spec.ClusterDomain ### .spec.ClusterDomain
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L226)</sup> Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L229)</sup>
ClusterDomain define domain used in the kubernetes cluster. ClusterDomain define domain used in the kubernetes cluster.
Required only of domain is not set to default (cluster.local) Required only of domain is not set to default (cluster.local)
@ -1061,7 +1061,7 @@ Default Value: `cluster.local`
### .spec.communicationMethod ### .spec.communicationMethod
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L234)</sup> Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L237)</sup>
CommunicationMethod define communication method used in deployment CommunicationMethod define communication method used in deployment
@ -3043,6 +3043,932 @@ Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.
*** ***
### .spec.gateways.affinity
Type: `core.PodAffinity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L185)</sup>
Affinity specified additional affinity settings in ArangoDB Pod definitions
Links:
* [Documentation of core.PodAffinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#podaffinity-v1-core)
***
### .spec.gateways.allowMemberRecreation
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L227)</sup>
AllowMemberRecreation allows to recreate member.
This setting changes the member recreation logic based on group:
- For Sync Masters, Sync Workers, Coordinator and DB-Servers it determines if a member can be recreated in case of failure (default `true`)
- For Agents and Single this value is hardcoded to `false` and the value provided in spec is ignored.
***
### .spec.gateways.annotations
Type: `object` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L128)</sup>
Annotations specified the annotations added to Pods in this group.
Annotations are merged with `spec.annotations`.
***
### .spec.gateways.annotationsIgnoreList
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L130)</sup>
AnnotationsIgnoreList list regexp or plain definitions which annotations should be ignored
***
### .spec.gateways.annotationsMode
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L132)</sup>
AnnotationsMode Define annotations mode which should be use while overriding annotations
***
### .spec.gateways.antiAffinity
Type: `core.PodAntiAffinity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L181)</sup>
AntiAffinity specified additional antiAffinity settings in ArangoDB Pod definitions
Links:
* [Documentation of core.Pod.AntiAffinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#podantiaffinity-v1-core)
***
### .spec.gateways.args
Type: `[]string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L86)</sup>
Args setting specifies additional command-line arguments passed to all servers of this group.
Default Value: `[]`
***
### .spec.gateways.count
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L78)</sup>
Count setting specifies the number of servers to start for the given group.
For the Agent group, this value must be a positive, odd number.
The default value is `3` for all groups except `single` (there the default is `1`
for `spec.mode: Single` and `2` for `spec.mode: ActiveFailover`).
For the `syncworkers` group, it is highly recommended to use the same number
as for the `dbservers` group.
***
### .spec.gateways.entrypoint
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L88)</sup>
Entrypoint overrides container executable
***
### .spec.gateways.envs\[int\].name
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_env_var.go#L26)</sup>
***
### .spec.gateways.envs\[int\].value
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_env_var.go#L27)</sup>
***
### .spec.gateways.ephemeralVolumes.apps.size
Type: `resource.Quantity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_ephemeral_volumes.go#L64)</sup>
Size define size of the ephemeral volume
Links:
* [Documentation of resource.Quantity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#quantity-resource-core)
***
### .spec.gateways.ephemeralVolumes.temp.size
Type: `resource.Quantity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_ephemeral_volumes.go#L64)</sup>
Size define size of the ephemeral volume
Links:
* [Documentation of resource.Quantity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#quantity-resource-core)
***
### .spec.gateways.exporterPort
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L240)</sup>
ExporterPort define Port used by exporter
***
### .spec.gateways.extendedRotationCheck
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L210)</sup>
ExtendedRotationCheck extend checks for rotation
***
### .spec.gateways.externalPortEnabled
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L222)</sup>
ExternalPortEnabled if external port should be enabled. If is set to false, ports needs to be exposed via sidecar. Only for ArangoD members
***
### .spec.gateways.indexMethod
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L233)</sup>
IndexMethod define group Indexing method
Possible Values:
* `"random"` (default) - Pick random ID for member. Enforced on the Community Operator.
* `"ordered"` - Use sequential number as Member ID, starting from 0. Enterprise Operator required.
***
### .spec.gateways.initContainers.containers
Type: `[]core.Container` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_init_containers.go#L91)</sup>
Containers contains list of containers
Links:
* [Documentation of core.Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#container-v1-core)
***
### .spec.gateways.initContainers.mode
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_init_containers.go#L94)</sup>
Mode keep container replace mode
***
### .spec.gateways.internalPort
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L218)</sup>
InternalPort define port used in internal communication, can be accessed over localhost via sidecar. Only for ArangoD members
***
### .spec.gateways.internalPortProtocol
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L220)</sup>
InternalPortProtocol define protocol of port used in internal communication, can be accessed over localhost via sidecar. Only for ArangoD members
***
### .spec.gateways.labels
Type: `object` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L134)</sup>
Labels specified the labels added to Pods in this group.
***
### .spec.gateways.labelsIgnoreList
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L136)</sup>
LabelsIgnoreList list regexp or plain definitions which labels should be ignored
***
### .spec.gateways.labelsMode
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L138)</sup>
LabelsMode Define labels mode which should be use while overriding labels
***
### .spec.gateways.maxCount
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L82)</sup>
MaxCount specifies a maximum for the count of servers. If set, a specification is invalid if `count > maxCount`.
***
### .spec.gateways.memoryReservation
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L110)</sup>
MemoryReservation determines the system reservation of memory while calculating `ARANGODB_OVERRIDE_DETECTED_TOTAL_MEMORY` value.
If this field is set, `ARANGODB_OVERRIDE_DETECTED_TOTAL_MEMORY` is reduced by a specified value in percent.
Accepted Range <0, 50>. If the value is outside the accepted range, it is adjusted to the closest value.
Links:
* [Documentation of the ArangoDB Envs](https://docs.arangodb.com/devel/components/arangodb-server/environment-variables/)
Default Value: `0`
***
### .spec.gateways.minCount
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L80)</sup>
MinCount specifies a minimum for the count of servers. If set, a specification is invalid if `count < minCount`.
***
### .spec.gateways.nodeAffinity
Type: `core.NodeAffinity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L189)</sup>
NodeAffinity specified additional nodeAffinity settings in ArangoDB Pod definitions
Links:
* [Documentation of code.NodeAffinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#nodeaffinity-v1-core)
***
### .spec.gateways.nodeSelector
Type: `map[string]string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L153)</sup>
NodeSelector setting specifies a set of labels to be used as `nodeSelector` for Pods of this node.
Links:
* [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/)
***
### .spec.gateways.numactl.args
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_numactl_spec.go#L38)</sup>
Args define list of the numactl process
Default Value: `[]`
***
### .spec.gateways.numactl.enabled
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_numactl_spec.go#L30)</sup>
Enabled define if numactl should be enabled
Default Value: `false`
***
### .spec.gateways.numactl.path
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_numactl_spec.go#L34)</sup>
Path define numactl path within the container
Default Value: `/usr/bin/numactl`
***
### .spec.gateways.overrideDetectedNumberOfCores
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L116)</sup>
> [!IMPORTANT]
> **Values set by this feature override user-provided `ARANGODB_OVERRIDE_DETECTED_NUMBER_OF_CORES` Container Environment Variable**
OverrideDetectedNumberOfCores determines if number of cores should be overridden based on values in resources.
If is set to true and Container CPU Limits are set, it sets Container Environment Variable `ARANGODB_OVERRIDE_DETECTED_NUMBER_OF_CORES` to the value from the Container CPU Limits.
Links:
* [Documentation of the ArangoDB Envs](https://docs.arangodb.com/devel/components/arangodb-server/environment-variables/)
Default Value: `true`
***
### .spec.gateways.overrideDetectedTotalMemory
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L104)</sup>
> [!IMPORTANT]
> **Values set by this feature override user-provided `ARANGODB_OVERRIDE_DETECTED_TOTAL_MEMORY` Container Environment Variable**
OverrideDetectedTotalMemory determines if memory should be overridden based on values in resources.
If is set to true and Container Memory Limits are set, it sets Container Environment Variable `ARANGODB_OVERRIDE_DETECTED_TOTAL_MEMORY` to the value from the Container Memory Limits.
Links:
* [Documentation of the ArangoDB Envs](https://docs.arangodb.com/devel/components/arangodb-server/environment-variables/)
Default Value: `true`
***
### .spec.gateways.podModes.network
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec_pod_modes.go#L31)</sup>
***
### .spec.gateways.podModes.pid
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec_pod_modes.go#L32)</sup>
***
### .spec.gateways.port
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L238)</sup>
Port define Port used by member
***
### .spec.gateways.priorityClassName
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L159)</sup>
PriorityClassName specifies a priority class name
Will be forwarded to the pod spec.
Links:
* [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/)
***
### .spec.gateways.probes.livenessProbeDisabled
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L250)</sup>
LivenessProbeDisabled if set to true, the operator does not generate a liveness probe for new pods belonging to this group
Default Value: `false`
***
### .spec.gateways.probes.livenessProbeSpec.failureThreshold
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L300)</sup>
FailureThreshold when a Pod starts and the probe fails, Kubernetes will try failureThreshold times before giving up.
Giving up means restarting the container.
Minimum value is 1.
Default Value: `3`
***
### .spec.gateways.probes.livenessProbeSpec.initialDelaySeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L283)</sup>
InitialDelaySeconds specifies number of seconds after the container has started before liveness or readiness probes are initiated.
Minimum value is 0.
Default Value: `2`
***
### .spec.gateways.probes.livenessProbeSpec.periodSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L287)</sup>
PeriodSeconds How often (in seconds) to perform the probe.
Minimum value is 1.
Default Value: `10`
***
### .spec.gateways.probes.livenessProbeSpec.successThreshold
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L295)</sup>
SuccessThreshold Minimum consecutive successes for the probe to be considered successful after having failed.
Minimum value is 1.
Default Value: `1`
***
### .spec.gateways.probes.livenessProbeSpec.timeoutSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L291)</sup>
TimeoutSeconds specifies number of seconds after which the probe times out
Minimum value is 1.
Default Value: `2`
***
### .spec.gateways.probes.ReadinessProbeDisabled
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L257)</sup>
> [!WARNING]
> ***DEPRECATED***
>
> **This field is deprecated, kept only for backward compatibility.**
OldReadinessProbeDisabled if true readinessProbes are disabled
***
### .spec.gateways.probes.readinessProbeDisabled
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L259)</sup>
ReadinessProbeDisabled override flag for probe disabled in good manner (lowercase) with backward compatibility
***
### .spec.gateways.probes.readinessProbeSpec.failureThreshold
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L300)</sup>
FailureThreshold when a Pod starts and the probe fails, Kubernetes will try failureThreshold times before giving up.
Giving up means restarting the container.
Minimum value is 1.
Default Value: `3`
***
### .spec.gateways.probes.readinessProbeSpec.initialDelaySeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L283)</sup>
InitialDelaySeconds specifies number of seconds after the container has started before liveness or readiness probes are initiated.
Minimum value is 0.
Default Value: `2`
***
### .spec.gateways.probes.readinessProbeSpec.periodSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L287)</sup>
PeriodSeconds How often (in seconds) to perform the probe.
Minimum value is 1.
Default Value: `10`
***
### .spec.gateways.probes.readinessProbeSpec.successThreshold
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L295)</sup>
SuccessThreshold Minimum consecutive successes for the probe to be considered successful after having failed.
Minimum value is 1.
Default Value: `1`
***
### .spec.gateways.probes.readinessProbeSpec.timeoutSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L291)</sup>
TimeoutSeconds specifies number of seconds after which the probe times out
Minimum value is 1.
Default Value: `2`
***
### .spec.gateways.probes.startupProbeDisabled
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L264)</sup>
StartupProbeDisabled if true startupProbes are disabled
***
### .spec.gateways.probes.startupProbeSpec.failureThreshold
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L300)</sup>
FailureThreshold when a Pod starts and the probe fails, Kubernetes will try failureThreshold times before giving up.
Giving up means restarting the container.
Minimum value is 1.
Default Value: `3`
***
### .spec.gateways.probes.startupProbeSpec.initialDelaySeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L283)</sup>
InitialDelaySeconds specifies number of seconds after the container has started before liveness or readiness probes are initiated.
Minimum value is 0.
Default Value: `2`
***
### .spec.gateways.probes.startupProbeSpec.periodSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L287)</sup>
PeriodSeconds How often (in seconds) to perform the probe.
Minimum value is 1.
Default Value: `10`
***
### .spec.gateways.probes.startupProbeSpec.successThreshold
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L295)</sup>
SuccessThreshold Minimum consecutive successes for the probe to be considered successful after having failed.
Minimum value is 1.
Default Value: `1`
***
### .spec.gateways.probes.startupProbeSpec.timeoutSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L291)</sup>
TimeoutSeconds specifies number of seconds after which the probe times out
Minimum value is 1.
Default Value: `2`
***
### .spec.gateways.pvcResizeMode
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L173)</sup>
VolumeResizeMode specified resize mode for PVCs and PVs
Possible Values:
* `"runtime"` (default) - PVC will be resized in Pod runtime (EKS, GKE)
* `"rotate"` - Pod will be shutdown and PVC will be resized (AKS)
***
### .spec.gateways.resources
Type: `core.ResourceRequirements` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L98)</sup>
Resources holds resource requests & limits
Links:
* [Documentation of core.ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#resourcerequirements-v1-core)
***
### .spec.gateways.schedulerName
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L90)</sup>
SchedulerName define scheduler name used for group
***
### .spec.gateways.securityContext.addCapabilities
Type: `[]core.Capability` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L41)</sup>
AddCapabilities add new capabilities to containers
***
### .spec.gateways.securityContext.allowPrivilegeEscalation
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L44)</sup>
AllowPrivilegeEscalation Controls whether a process can gain more privileges than its parent process.
***
### .spec.gateways.securityContext.dropAllCapabilities
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L38)</sup>
> [!WARNING]
> ***DEPRECATED***
>
> **This field is added for backward compatibility. Will be removed in 1.1.0.**
DropAllCapabilities specifies if capabilities should be dropped for this pod containers
***
### .spec.gateways.securityContext.fsGroup
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L61)</sup>
FSGroup is a special supplemental group that applies to all containers in a pod.
***
### .spec.gateways.securityContext.privileged
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L47)</sup>
Privileged If true, runs container in privileged mode. Processes in privileged containers are
essentially equivalent to root on the host.
***
### .spec.gateways.securityContext.readOnlyRootFilesystem
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L49)</sup>
ReadOnlyRootFilesystem if true, mounts the container's root filesystem as read-only.
***
### .spec.gateways.securityContext.runAsGroup
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L55)</sup>
RunAsGroup is the GID to run the entrypoint of the container process.
***
### .spec.gateways.securityContext.runAsNonRoot
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L51)</sup>
RunAsNonRoot if true, indicates that the container must run as a non-root user.
***
### .spec.gateways.securityContext.runAsUser
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L53)</sup>
RunAsUser is the UID to run the entrypoint of the container process.
***
### .spec.gateways.securityContext.seccompProfile
Type: `core.SeccompProfile` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L77)</sup>
SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.
Links:
* [Documentation of core.SeccompProfile](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#seccompprofile-v1-core)
***
### .spec.gateways.securityContext.seLinuxOptions
Type: `core.SELinuxOptions` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L82)</sup>
SELinuxOptions are the labels to be applied to the container
Links:
* [Documentation of core.SELinuxOptions](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#selinuxoptions-v1-core)
***
### .spec.gateways.securityContext.supplementalGroups
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L59)</sup>
SupplementalGroups is a list of groups applied to the first process run in each container, in addition to the container's primary GID,
the fsGroup (if specified), and group memberships defined in the container image for the uid of the container process.
***
### .spec.gateways.securityContext.sysctls
Type: `map[string]intstr.IntOrString` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_security_context_spec.go#L72)</sup>
Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported
sysctls (by the container runtime) might fail to launch.
Map Value can be String or Int
Links:
* [Documentation](https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/)
Example:
```yaml
sysctls:
"kernel.shm_rmid_forced": "0"
"net.core.somaxconn": 1024
"kernel.msgmax": "65536"
```
***
### .spec.gateways.serviceAccountName
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L149)</sup>
ServiceAccountName setting specifies the `serviceAccountName` for the `Pods` created
for each server of this group. If empty, it defaults to using the
`default` service account.
Using an alternative `ServiceAccount` is typically used to separate access rights.
The ArangoDB deployments need some very minimal access rights. With the
deployment of the operator, we grant the rights to 'get' all 'pod' resources.
If you are using a different service account, please grant these rights
to that service account.
***
### .spec.gateways.shutdownDelay
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L216)</sup>
ShutdownDelay define how long operator should delay finalizer removal after shutdown
***
### .spec.gateways.shutdownMethod
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L214)</sup>
ShutdownMethod describe procedure of member shutdown taken by Operator
***
### .spec.gateways.sidecarCoreNames
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L192)</sup>
SidecarCoreNames is a list of sidecar containers which must run in the pod.
Some names (e.g.: "server", "worker") are reserved, and they don't have any impact.
***
### .spec.gateways.sidecars
Type: `[]core.Container` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L196)</sup>
Sidecars specifies a list of additional containers to be started
Links:
* [Documentation of core.Container](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#container-v1-core)
***
### .spec.gateways.storageClassName
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L94)</sup>
> [!WARNING]
> ***DEPRECATED***
>
> **Use VolumeClaimTemplate instead.**
StorageClassName specifies the classname for storage of the servers.
***
### .spec.gateways.terminationGracePeriodSeconds
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L229)</sup>
TerminationGracePeriodSeconds override default TerminationGracePeriodSeconds for pods - via silent rotation
***
### .spec.gateways.tolerations
Type: `[]core.Toleration` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L125)</sup>
Tolerations specifies the tolerations added to Pods in this group.
By default, suitable tolerations are set for the following keys with the `NoExecute` effect:
- `node.kubernetes.io/not-ready`
- `node.kubernetes.io/unreachable`
- `node.alpha.kubernetes.io/unreachable` (will be removed in future version)
For more information on tolerations, consult the https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
Links:
* [Documentation of core.Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#toleration-v1-core)
***
### .spec.gateways.volumeAllowShrink
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L177)</sup>
> [!WARNING]
> ***DEPRECATED***
>
> **Not used anymore**
VolumeAllowShrink allows shrinking of the volume
***
### .spec.gateways.volumeClaimTemplate
Type: `core.PersistentVolumeClaim` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L168)</sup>
VolumeClaimTemplate specifies a volumeClaimTemplate used by operator to create to volume claims for pods of this group.
This setting is not available for group `coordinators`, `syncmasters` & `syncworkers`.
The default value describes a volume with `8Gi` storage, `ReadWriteOnce` access mode and volume mode set to `PersistentVolumeFilesystem`.
If this field is not set and `spec.<group>.resources.requests.storage` is set, then a default volume claim
with size as specified by `spec.<group>.resources.requests.storage` will be created. In that case `storage`
and `iops` is not forwarded to the pods resource requirements.
Links:
* [Documentation of core.PersistentVolumeClaim](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#persistentvolumeclaim-v1-core)
***
### .spec.gateways.volumeMounts
Type: `[]ServerGroupSpecVolumeMount` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_spec.go#L206)</sup>
VolumeMounts define list of volume mounts mounted into server container
Links:
* [Documentation of ServerGroupSpecVolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#volumemount-v1-core)
***
### .spec.gateways.volumes\[int\].configMap
Type: `core.ConfigMapVolumeSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_volume.go#L138)</sup>
ConfigMap which should be mounted into pod
Links:
* [Documentation of core.ConfigMapVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#configmapvolumesource-v1-core)
***
### .spec.gateways.volumes\[int\].emptyDir
Type: `core.EmptyDirVolumeSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_volume.go#L143)</sup>
EmptyDir
Links:
* [Documentation of core.EmptyDirVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#emptydirvolumesource-v1-core)
***
### .spec.gateways.volumes\[int\].hostPath
Type: `core.HostPathVolumeSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_volume.go#L148)</sup>
HostPath
Links:
* [Documentation of core.HostPathVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#hostpathvolumesource-v1-core)
***
### .spec.gateways.volumes\[int\].name
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_volume.go#L128)</sup>
Name of volume
***
### .spec.gateways.volumes\[int\].persistentVolumeClaim
Type: `core.PersistentVolumeClaimVolumeSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_volume.go#L153)</sup>
PersistentVolumeClaim
Links:
* [Documentation of core.PersistentVolumeClaimVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#persistentvolumeclaimvolumesource-v1-core)
***
### .spec.gateways.volumes\[int\].secret
Type: `core.SecretVolumeSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_group_volume.go#L133)</sup>
Secret which should be mounted into pod
Links:
* [Documentation of core.SecretVolumeSource](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#secretvolumesource-v1-core)
***
### .spec.id.affinity ### .spec.id.affinity
Type: `core.PodAffinity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_id_group_spec.go#L48)</sup> Type: `core.PodAffinity` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/server_id_group_spec.go#L48)</sup>
@ -3369,7 +4295,7 @@ Links:
### .spec.memberPropagationMode ### .spec.memberPropagationMode
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L209)</sup> Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L212)</sup>
MemberPropagationMode defines how changes to pod spec should be propogated. MemberPropagationMode defines how changes to pod spec should be propogated.
Changes to a pods configuration require a restart of that pod in almost all cases. Changes to a pods configuration require a restart of that pod in almost all cases.
@ -6605,7 +7531,7 @@ MaintenanceGracePeriod action timeout
### .spec.timezone ### .spec.timezone
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L258)</sup> Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec.go#L261)</sup>
Timezone if specified, will set a timezone for deployment. Timezone if specified, will set a timezone for deployment.
Must be in format accepted by "tzdata", e.g. `America/New_York` or `Europe/London` Must be in format accepted by "tzdata", e.g. `America/New_York` or `Europe/London`

View file

@ -8,6 +8,7 @@ title: List of all features
| Feature | Operator Version | Introduced | ArangoDB Version | ArangoDB Edition | State | Enabled | Flag | Remarks | | Feature | Operator Version | Introduced | ArangoDB Version | ArangoDB Edition | State | Enabled | Flag | Remarks |
|:----------------------------------------------------------------|:-----------------|:-----------|:-----------------|:----------------------|:-------------|:--------|:-------------------------------------------------------|:------------------------------------------------------------------------------------------| |:----------------------------------------------------------------|:-----------------|:-----------|:-----------------|:----------------------|:-------------|:--------|:-------------------------------------------------------|:------------------------------------------------------------------------------------------|
| Gateway | 1.2.43 | 1.2.43 | >= 3.8.0 | Community, Enterprise | Alpha | True | N/A | Support for ArangoDeployment Gateway Group |
| Cleanup Imported Backups | 1.2.41 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | False | --deployment.feature.backup-cleanup | Cleanup backups created outside of the Operator and imported into Kubernetes ArangoBackup | | Cleanup Imported Backups | 1.2.41 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | False | --deployment.feature.backup-cleanup | Cleanup backups created outside of the Operator and imported into Kubernetes ArangoBackup |
| Upscale resources spec in init containers | 1.2.36 | 1.2.36 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.init-containers-upscale-resources | Upscale resources spec to built-in init containers if they are not specified or lower | | Upscale resources spec in init containers | 1.2.36 | 1.2.36 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.init-containers-upscale-resources | Upscale resources spec to built-in init containers if they are not specified or lower |
| Create backups asynchronously | 1.2.35 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.async-backup-creation | Create backups asynchronously to avoid blocking the operator and reaching the timeout | | Create backups asynchronously | 1.2.35 | 1.2.41 | >= 3.8.0 | Community, Enterprise | Production | True | --deployment.feature.async-backup-creation | Create backups asynchronously to avoid blocking the operator and reaching the timeout |

View file

@ -257,3 +257,8 @@ features:
releases: releases:
- operatorVersion: 1.2.36 - operatorVersion: 1.2.36
state: Alpha state: Alpha
- name: Gateway
remarks: Support for ArangoDeployment Gateway Group
releases:
- operatorVersion: 1.2.43
state: Alpha

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -73,7 +73,7 @@ func (d *ArangoDeployment) AsOwner() meta.OwnerReference {
// ForeachServerGroup calls the given callback for all server groups. // ForeachServerGroup calls the given callback for all server groups.
// If the callback returns an error, this error is returned and no other server // If the callback returns an error, this error is returned and no other server
// groups are processed. // groups are processed.
// Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers // Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers, gateways
func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *DeploymentStatus) error { func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *DeploymentStatus) error {
if status == nil { if status == nil {
status = &d.Status status = &d.Status
@ -84,7 +84,7 @@ func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *Deploy
// ForeachServerGroupAccepted calls the given callback for all accepted server groups. // ForeachServerGroupAccepted calls the given callback for all accepted server groups.
// If the callback returns an error, this error is returned and no other server // If the callback returns an error, this error is returned and no other server
// groups are processed. // groups are processed.
// Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers // Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers, gateways
func (d *ArangoDeployment) ForeachServerGroupAccepted(cb ServerGroupFunc, status *DeploymentStatus) error { func (d *ArangoDeployment) ForeachServerGroupAccepted(cb ServerGroupFunc, status *DeploymentStatus) error {
if status == nil { if status == nil {
status = &d.Status status = &d.Status
@ -115,6 +115,9 @@ func (d *ArangoDeployment) foreachServerGroup(cb ServerGroupFunc, spec Deploymen
if err := cb(ServerGroupSyncWorkers, spec.SyncWorkers, &status.Members.SyncWorkers); err != nil { if err := cb(ServerGroupSyncWorkers, spec.SyncWorkers, &status.Members.SyncWorkers); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
if err := cb(ServerGroupGateways, spec.Gateways.Get(), &status.Members.Gateways); err != nil {
return errors.WithStack(err)
}
return nil return nil
} }

View file

@ -199,6 +199,9 @@ type DeploymentSpec struct {
// SyncWorkers contains specification for Syncworker pods running in deployment mode `Cluster`. // SyncWorkers contains specification for Syncworker pods running in deployment mode `Cluster`.
SyncWorkers ServerGroupSpec `json:"syncworkers"` SyncWorkers ServerGroupSpec `json:"syncworkers"`
// Gateways contain specification for Gateway pods running in deployment mode `Single` or `Cluster`.
Gateways *ServerGroupSpec `json:"gateways,omitempty"`
// MemberPropagationMode defines how changes to pod spec should be propogated. // MemberPropagationMode defines how changes to pod spec should be propogated.
// Changes to a pods configuration require a restart of that pod in almost all cases. // Changes to a pods configuration require a restart of that pod in almost all cases.
// Pods are restarted eagerly by default, which can cause more restarts than desired, especially when updating arangod as well as the operator. // Pods are restarted eagerly by default, which can cause more restarts than desired, especially when updating arangod as well as the operator.
@ -256,6 +259,9 @@ type DeploymentSpec struct {
// Timezone if specified, will set a timezone for deployment. // Timezone if specified, will set a timezone for deployment.
// Must be in format accepted by "tzdata", e.g. `America/New_York` or `Europe/London` // Must be in format accepted by "tzdata", e.g. `America/New_York` or `Europe/London`
Timezone *string `json:"timezone,omitempty"` Timezone *string `json:"timezone,omitempty"`
// Gateway defined main Gateway configuration.
Gateway *DeploymentSpecGateway `json:"gateway,omitempty"`
} }
// GetAllowMemberRecreation returns member recreation policy based on group and settings // GetAllowMemberRecreation returns member recreation policy based on group and settings
@ -267,6 +273,8 @@ func (s *DeploymentSpec) GetAllowMemberRecreation(group ServerGroup) bool {
groupSpec := s.GetServerGroupSpec(group) groupSpec := s.GetServerGroupSpec(group)
switch group { switch group {
case ServerGroupGateways:
return true
case ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers:
if v := groupSpec.AllowMemberRecreation; v == nil { if v := groupSpec.AllowMemberRecreation; v == nil {
return true return true
@ -326,6 +334,11 @@ func (s DeploymentSpec) GetSyncImage() string {
return s.GetImage() return s.GetImage()
} }
// IsGatewayEnabled returns true when the deployment has gateways enabled.
func (s DeploymentSpec) IsGatewayEnabled() bool {
return s.Gateway.IsEnabled()
}
// GetImagePullPolicy returns the value of imagePullPolicy. // GetImagePullPolicy returns the value of imagePullPolicy.
func (s DeploymentSpec) GetImagePullPolicy() core.PullPolicy { func (s DeploymentSpec) GetImagePullPolicy() core.PullPolicy {
return util.TypeOrDefault[core.PullPolicy](s.ImagePullPolicy) return util.TypeOrDefault[core.PullPolicy](s.ImagePullPolicy)
@ -364,8 +377,7 @@ func (s DeploymentSpec) IsSecure() bool {
return s.TLS.IsSecure() return s.TLS.IsSecure()
} }
// GetServerGroupSpec returns the server group spec (from this // GetServerGroupSpec returns the server group spec (from this deployment spec) for the given group.
// deployment spec) for the given group.
func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec { func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec {
switch group { switch group {
case ServerGroupSingle: case ServerGroupSingle:
@ -380,13 +392,14 @@ func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec {
return s.SyncMasters.WithGroup(group) return s.SyncMasters.WithGroup(group)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return s.SyncWorkers.WithGroup(group) return s.SyncWorkers.WithGroup(group)
case ServerGroupGateways:
return s.Gateways.WithGroup(group)
default: default:
return ServerGroupSpec{} return ServerGroupSpec{}
} }
} }
// UpdateServerGroupSpec returns the server group spec (from this // UpdateServerGroupSpec returns the server group spec (from this deployment spec) for the given group.
// deployment spec) for the given group.
func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGroupSpec) { func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGroupSpec) {
switch group { switch group {
case ServerGroupSingle: case ServerGroupSingle:
@ -401,6 +414,8 @@ func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGr
s.SyncMasters = gspec s.SyncMasters = gspec
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
s.SyncWorkers = gspec s.SyncWorkers = gspec
case ServerGroupGateways:
s.Gateways = gspec.DeepCopy()
} }
} }
@ -421,6 +436,11 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
if s.GetImagePullPolicy() == "" { if s.GetImagePullPolicy() == "" {
s.ImagePullPolicy = util.NewType[core.PullPolicy](core.PullIfNotPresent) s.ImagePullPolicy = util.NewType[core.PullPolicy](core.PullIfNotPresent)
} }
if s.Gateway.IsEnabled() {
if s.Gateways == nil {
s.Gateways = &ServerGroupSpec{}
}
}
s.ExternalAccess.SetDefaults() s.ExternalAccess.SetDefaults()
s.RocksDB.SetDefaults() s.RocksDB.SetDefaults()
s.Authentication.SetDefaults(deploymentName + "-jwt") s.Authentication.SetDefaults(deploymentName + "-jwt")
@ -432,6 +452,7 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
s.Coordinators.SetDefaults(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode()) s.Coordinators.SetDefaults(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode())
s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode()) s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode())
s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode()) s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode())
s.Gateways.SetDefaults(ServerGroupGateways, s.IsGatewayEnabled(), s.GetMode())
s.Metrics.SetDefaults(deploymentName+"-exporter-jwt-token", s.Authentication.IsAuthenticated()) s.Metrics.SetDefaults(deploymentName+"-exporter-jwt-token", s.Authentication.IsAuthenticated())
s.Chaos.SetDefaults() s.Chaos.SetDefaults()
s.Bootstrap.SetDefaults(deploymentName) s.Bootstrap.SetDefaults(deploymentName)
@ -480,6 +501,7 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
s.Coordinators.SetDefaultsFrom(source.Coordinators) s.Coordinators.SetDefaultsFrom(source.Coordinators)
s.SyncMasters.SetDefaultsFrom(source.SyncMasters) s.SyncMasters.SetDefaultsFrom(source.SyncMasters)
s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers) s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers)
s.Gateways.SetDefaultsFrom(source.Gateways.Get())
s.Metrics.SetDefaultsFrom(source.Metrics) s.Metrics.SetDefaultsFrom(source.Metrics)
s.Lifecycle.SetDefaultsFrom(source.Lifecycle) s.Lifecycle.SetDefaultsFrom(source.Lifecycle)
s.Chaos.SetDefaultsFrom(source.Chaos) s.Chaos.SetDefaultsFrom(source.Chaos)
@ -539,6 +561,11 @@ func (s *DeploymentSpec) Validate() error {
if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil { if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
if s.IsGatewayEnabled() {
if err := s.Gateways.Validate(ServerGroupGateways, s.IsGatewayEnabled(), s.GetMode(), s.GetEnvironment()); err != nil {
return errors.WithStack(err)
}
}
if err := s.Metrics.Validate(); err != nil { if err := s.Metrics.Validate(); err != nil {
return errors.WithStack(errors.Wrap(err, "spec.metrics")) return errors.WithStack(errors.Wrap(err, "spec.metrics"))
} }
@ -554,6 +581,9 @@ func (s *DeploymentSpec) Validate() error {
if err := s.Architecture.Validate(); err != nil { if err := s.Architecture.Validate(); err != nil {
return errors.WithStack(errors.Wrap(err, "spec.architecture")) return errors.WithStack(errors.Wrap(err, "spec.architecture"))
} }
if err := s.Gateway.Validate(); err != nil {
return errors.WithStack(errors.Wrap(err, "spec.architecture"))
}
return nil return nil
} }
@ -614,6 +644,14 @@ func (s DeploymentSpec) ResetImmutableFields(target *DeploymentSpec) []string {
if l := s.SyncWorkers.ResetImmutableFields(ServerGroupSyncWorkers, "syncworkers", &target.SyncWorkers); l != nil { if l := s.SyncWorkers.ResetImmutableFields(ServerGroupSyncWorkers, "syncworkers", &target.SyncWorkers); l != nil {
resetFields = append(resetFields, l...) resetFields = append(resetFields, l...)
} }
if s.Gateways != nil {
if target.Gateways == nil {
target.Gateways = &ServerGroupSpec{}
}
if l := s.Gateways.ResetImmutableFields(ServerGroupGateways, "gateways", target.Gateways); l != nil {
resetFields = append(resetFields, l...)
}
}
if l := s.Metrics.ResetImmutableFields("metrics", &target.Metrics); l != nil { if l := s.Metrics.ResetImmutableFields("metrics", &target.Metrics); l != nil {
resetFields = append(resetFields, l...) resetFields = append(resetFields, l...)
} }

View file

@ -0,0 +1,37 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package v1
type DeploymentSpecGateway struct {
Enabled *bool `json:"enabled,omitempty"`
}
func (d *DeploymentSpecGateway) IsEnabled() bool {
if d == nil || d.Enabled == nil {
return false
}
return *d.Enabled
}
func (d *DeploymentSpecGateway) Validate() error {
return nil
}

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -103,6 +103,7 @@ type DeploymentStatus struct {
Coordinators *ServerGroupStatus `json:"coordinators,omitempty"` Coordinators *ServerGroupStatus `json:"coordinators,omitempty"`
SyncMasters *ServerGroupStatus `json:"syncmasters,omitempty"` SyncMasters *ServerGroupStatus `json:"syncmasters,omitempty"`
SyncWorkers *ServerGroupStatus `json:"syncworkers,omitempty"` SyncWorkers *ServerGroupStatus `json:"syncworkers,omitempty"`
Gateways *ServerGroupStatus `json:"gateways,omitempty"`
} }
// Equal checks for equality // Equal checks for equality
@ -174,6 +175,8 @@ func (ds DeploymentStatus) getServerGroupStatus(group ServerGroup) *ServerGroupS
return ds.SyncMasters.DeepCopy() return ds.SyncMasters.DeepCopy()
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ds.SyncWorkers.DeepCopy() return ds.SyncWorkers.DeepCopy()
case ServerGroupGateways:
return ds.Gateways.DeepCopy()
default: default:
return nil return nil
} }
@ -195,5 +198,7 @@ func (ds *DeploymentStatus) UpdateServerGroupStatus(group ServerGroup, gspec Ser
ds.SyncMasters = &gspec ds.SyncMasters = &gspec
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
ds.SyncWorkers = &gspec ds.SyncWorkers = &gspec
case ServerGroupGateways:
ds.Gateways = &gspec
} }
} }

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -35,6 +35,7 @@ type DeploymentStatusMembers struct {
Coordinators MemberStatusList `json:"coordinators,omitempty"` Coordinators MemberStatusList `json:"coordinators,omitempty"`
SyncMasters MemberStatusList `json:"syncmasters,omitempty"` SyncMasters MemberStatusList `json:"syncmasters,omitempty"`
SyncWorkers MemberStatusList `json:"syncworkers,omitempty"` SyncWorkers MemberStatusList `json:"syncworkers,omitempty"`
Gateways MemberStatusList `json:"gateways,omitempty"`
} }
// Equal checks for equality // Equal checks for equality
@ -44,7 +45,8 @@ func (ds DeploymentStatusMembers) Equal(other DeploymentStatusMembers) bool {
ds.DBServers.Equal(other.DBServers) && ds.DBServers.Equal(other.DBServers) &&
ds.Coordinators.Equal(other.Coordinators) && ds.Coordinators.Equal(other.Coordinators) &&
ds.SyncMasters.Equal(other.SyncMasters) && ds.SyncMasters.Equal(other.SyncMasters) &&
ds.SyncWorkers.Equal(other.SyncWorkers) ds.SyncWorkers.Equal(other.SyncWorkers) &&
ds.Gateways.Equal(other.Gateways)
} }
// ContainsID returns true if the given set of members contains a member with given ID. // ContainsID returns true if the given set of members contains a member with given ID.
@ -54,7 +56,8 @@ func (ds DeploymentStatusMembers) ContainsID(id string) bool {
ds.DBServers.ContainsID(id) || ds.DBServers.ContainsID(id) ||
ds.Coordinators.ContainsID(id) || ds.Coordinators.ContainsID(id) ||
ds.SyncMasters.ContainsID(id) || ds.SyncMasters.ContainsID(id) ||
ds.SyncWorkers.ContainsID(id) ds.SyncWorkers.ContainsID(id) ||
ds.Gateways.ContainsID(id)
} }
// ElementByID returns the element in the given list that has the given ID and true. // ElementByID returns the element in the given list that has the given ID and true.
@ -78,6 +81,9 @@ func (ds DeploymentStatusMembers) ElementByID(id string) (MemberStatus, ServerGr
if result, found := ds.SyncWorkers.ElementByID(id); found { if result, found := ds.SyncWorkers.ElementByID(id); found {
return result, ServerGroupSyncWorkers, true return result, ServerGroupSyncWorkers, true
} }
if result, found := ds.Gateways.ElementByID(id); found {
return result, ServerGroupGateways, true
}
return MemberStatus{}, 0, false return MemberStatus{}, 0, false
} }
@ -129,6 +135,10 @@ func (ds DeploymentStatusMembers) ForServerGroup(cb MemberStatusFunc, group Serv
if err := cb(ServerGroupSyncWorkers, ds.SyncWorkers); err != nil { if err := cb(ServerGroupSyncWorkers, ds.SyncWorkers); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
case ServerGroupGateways:
if err := cb(ServerGroupGateways, ds.Gateways); err != nil {
return errors.WithStack(err)
}
} }
return nil return nil
} }
@ -155,6 +165,9 @@ func (ds DeploymentStatusMembers) MemberStatusByPodName(podName string) (MemberS
if result, found := ds.SyncWorkers.ElementByPodName(podName); found { if result, found := ds.SyncWorkers.ElementByPodName(podName); found {
return result, ServerGroupSyncWorkers, true return result, ServerGroupSyncWorkers, true
} }
if result, found := ds.Gateways.ElementByPodName(podName); found {
return result, ServerGroupGateways, true
}
return MemberStatus{}, 0, false return MemberStatus{}, 0, false
} }
@ -190,6 +203,8 @@ func (ds *DeploymentStatusMembers) Add(status MemberStatus, group ServerGroup) e
err = ds.SyncMasters.add(status) err = ds.SyncMasters.add(status)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
err = ds.SyncWorkers.add(status) err = ds.SyncWorkers.add(status)
case ServerGroupGateways:
err = ds.Gateways.add(status)
default: default:
return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group))
} }
@ -215,6 +230,8 @@ func (ds *DeploymentStatusMembers) Update(status MemberStatus, group ServerGroup
err = ds.SyncMasters.update(status) err = ds.SyncMasters.update(status)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
err = ds.SyncWorkers.update(status) err = ds.SyncWorkers.update(status)
case ServerGroupGateways:
err = ds.Gateways.update(status)
default: default:
return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group))
} }
@ -241,6 +258,8 @@ func (ds *DeploymentStatusMembers) RemoveByID(id string, group ServerGroup) erro
err = ds.SyncMasters.removeByID(id) err = ds.SyncMasters.removeByID(id)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
err = ds.SyncWorkers.removeByID(id) err = ds.SyncWorkers.removeByID(id)
case ServerGroupGateways:
err = ds.Gateways.removeByID(id)
default: default:
return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group))
} }
@ -250,23 +269,30 @@ func (ds *DeploymentStatusMembers) RemoveByID(id string, group ServerGroup) erro
return nil return nil
} }
// AllMembersReady returns true when all members, that must be ready for the given mode, are in the Ready state. // AllMembersReady returns true when all members, that must be ready for the given mode are in the Ready state.
func (ds DeploymentStatusMembers) AllMembersReady(mode DeploymentMode, syncEnabled bool) bool { func (ds DeploymentStatusMembers) AllMembersReady(mode DeploymentMode, syncEnabled, gatewayEnabled bool) bool {
syncReady := func() bool { syncReady := func() bool {
if syncEnabled { if syncEnabled {
return ds.SyncMasters.AllMembersReady() && ds.SyncWorkers.AllMembersReady() return ds.SyncMasters.AllMembersReady() && ds.SyncWorkers.AllMembersReady()
} }
return true return true
} }
gatewayReady := func() bool {
if gatewayEnabled {
return ds.Gateways.AllMembersReady()
}
return true
}
switch mode { switch mode {
case DeploymentModeSingle: case DeploymentModeSingle:
return ds.Single.MembersReady() > 0 return ds.Single.MembersReady() > 0 && gatewayReady()
case DeploymentModeActiveFailover: case DeploymentModeActiveFailover:
return ds.Agents.AllMembersReady() && ds.Single.MembersReady() > 0 return ds.Agents.AllMembersReady() && ds.Single.MembersReady() > 0
case DeploymentModeCluster: case DeploymentModeCluster:
return ds.Agents.AllMembersReady() && return ds.Agents.AllMembersReady() &&
ds.DBServers.AllMembersReady() && ds.DBServers.AllMembersReady() &&
ds.Coordinators.AllMembersReady() && ds.Coordinators.AllMembersReady() &&
gatewayReady() &&
syncReady() syncReady()
default: default:
return false return false
@ -288,6 +314,8 @@ func (ds DeploymentStatusMembers) MembersOfGroup(group ServerGroup) MemberStatus
return ds.SyncMasters return ds.SyncMasters
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ds.SyncWorkers return ds.SyncWorkers
case ServerGroupGateways:
return ds.Gateways
default: default:
return MemberStatusList{} return MemberStatusList{}
} }

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -34,6 +34,7 @@ func newMemberList() DeploymentStatusMembers {
Coordinators: MemberStatusList{{ID: ServerGroupCoordinators.AsRole()}}, Coordinators: MemberStatusList{{ID: ServerGroupCoordinators.AsRole()}},
SyncMasters: MemberStatusList{{ID: ServerGroupSyncMasters.AsRole()}}, SyncMasters: MemberStatusList{{ID: ServerGroupSyncMasters.AsRole()}},
SyncWorkers: MemberStatusList{{ID: ServerGroupSyncWorkers.AsRole()}}, SyncWorkers: MemberStatusList{{ID: ServerGroupSyncWorkers.AsRole()}},
Gateways: MemberStatusList{{ID: ServerGroupGateways.AsRole()}},
} }
} }

View file

@ -84,6 +84,7 @@ const (
ServerGroupCoordinators ServerGroup = 4 ServerGroupCoordinators ServerGroup = 4
ServerGroupSyncMasters ServerGroup = 5 ServerGroupSyncMasters ServerGroup = 5
ServerGroupSyncWorkers ServerGroup = 6 ServerGroupSyncWorkers ServerGroup = 6
ServerGroupGateways ServerGroup = 7
ServerGroupImageDiscovery ServerGroup = -1 ServerGroupImageDiscovery ServerGroup = -1
ServerGroupSingleString = "single" ServerGroupSingleString = "single"
@ -92,6 +93,7 @@ const (
ServerGroupCoordinatorsString = "coordinator" ServerGroupCoordinatorsString = "coordinator"
ServerGroupSyncMastersString = "syncmaster" ServerGroupSyncMastersString = "syncmaster"
ServerGroupSyncWorkersString = "syncworker" ServerGroupSyncWorkersString = "syncworker"
ServerGroupGatewaysString = "gateways"
ServerGroupImageDiscoveryString = "id" ServerGroupImageDiscoveryString = "id"
ServerGroupSingleAbbreviatedString = "sngl" ServerGroupSingleAbbreviatedString = "sngl"
@ -100,6 +102,7 @@ const (
ServerGroupCoordinatorsAbbreviatedString = "crdn" ServerGroupCoordinatorsAbbreviatedString = "crdn"
ServerGroupSyncMastersAbbreviatedString = "syma" ServerGroupSyncMastersAbbreviatedString = "syma"
ServerGroupSyncWorkersAbbreviatedString = "sywo" ServerGroupSyncWorkersAbbreviatedString = "sywo"
ServerGroupGatewaysAbbreviatedString = "gway"
ServerGroupImageDiscoveryAbbreviatedString = "id" ServerGroupImageDiscoveryAbbreviatedString = "id"
) )
@ -112,6 +115,7 @@ var (
ServerGroupCoordinators, ServerGroupCoordinators,
ServerGroupSyncMasters, ServerGroupSyncMasters,
ServerGroupSyncWorkers, ServerGroupSyncWorkers,
ServerGroupGateways,
} }
// AllArangoDServerGroups contains a constant list of all ArangoD server groups // AllArangoDServerGroups contains a constant list of all ArangoD server groups
AllArangoDServerGroups = []ServerGroup{ AllArangoDServerGroups = []ServerGroup{
@ -131,6 +135,8 @@ func (g ServerGroup) Type() ServerGroupType {
return ServerGroupTypeID return ServerGroupTypeID
case ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupSyncMasters, ServerGroupSyncWorkers:
return ServerGroupTypeArangoSync return ServerGroupTypeArangoSync
case ServerGroupGateways:
return ServerGroupTypeGateway
default: default:
return ServerGroupTypeUnknown return ServerGroupTypeUnknown
} }
@ -151,6 +157,8 @@ func (g ServerGroup) AsRole() string {
return ServerGroupSyncMastersString return ServerGroupSyncMastersString
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ServerGroupSyncWorkersString return ServerGroupSyncWorkersString
case ServerGroupGateways:
return ServerGroupGatewaysString
case ServerGroupImageDiscovery: case ServerGroupImageDiscovery:
return ServerGroupImageDiscoveryString return ServerGroupImageDiscoveryString
default: default:
@ -163,14 +171,14 @@ func (g ServerGroup) Enabled(mode DeploymentMode) bool {
switch mode { switch mode {
case DeploymentModeSingle: case DeploymentModeSingle:
switch g { switch g {
case ServerGroupSingle: case ServerGroupSingle, ServerGroupGateways:
return true return true
default: default:
return false return false
} }
case DeploymentModeCluster: case DeploymentModeCluster:
switch g { switch g {
case ServerGroupAgents, ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupAgents, ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers, ServerGroupGateways:
return true return true
default: default:
return false return false
@ -202,6 +210,8 @@ func (g ServerGroup) AsRoleAbbreviated() string {
return ServerGroupSyncMastersAbbreviatedString return ServerGroupSyncMastersAbbreviatedString
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ServerGroupSyncWorkersAbbreviatedString return ServerGroupSyncWorkersAbbreviatedString
case ServerGroupGateways:
return ServerGroupGatewaysAbbreviatedString
case ServerGroupImageDiscovery: case ServerGroupImageDiscovery:
return ServerGroupImageDiscoveryAbbreviatedString return ServerGroupImageDiscoveryAbbreviatedString
default: default:
@ -228,7 +238,7 @@ func (g ServerGroup) DefaultTerminationGracePeriod() time.Duration {
// IsStateless returns true when the groups runs servers without a persistent volume. // IsStateless returns true when the groups runs servers without a persistent volume.
func (g ServerGroup) IsStateless() bool { func (g ServerGroup) IsStateless() bool {
switch g { switch g {
case ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers, ServerGroupGateways:
return true return true
default: default:
return false return false
@ -255,6 +265,16 @@ func (g ServerGroup) IsArangosync() bool {
} }
} }
// IsGateway returns true when the group is a gateway group
func (g ServerGroup) IsGateway() bool {
switch g {
case ServerGroupGateways:
return true
default:
return false
}
}
// IsExportMetrics return true when the group can be used with the arangodbexporter // IsExportMetrics return true when the group can be used with the arangodbexporter
func (g ServerGroup) IsExportMetrics() bool { func (g ServerGroup) IsExportMetrics() bool {
switch g { switch g {
@ -280,6 +300,8 @@ func ServerGroupFromAbbreviatedRole(label string) ServerGroup {
return ServerGroupSyncMasters return ServerGroupSyncMasters
case ServerGroupSyncWorkersAbbreviatedString: case ServerGroupSyncWorkersAbbreviatedString:
return ServerGroupSyncWorkers return ServerGroupSyncWorkers
case ServerGroupGatewaysAbbreviatedString:
return ServerGroupGateways
case ServerGroupImageDiscoveryAbbreviatedString: case ServerGroupImageDiscoveryAbbreviatedString:
return ServerGroupImageDiscovery return ServerGroupImageDiscovery
default: default:
@ -287,7 +309,7 @@ func ServerGroupFromAbbreviatedRole(label string) ServerGroup {
} }
} }
// ServerGroupFromAbbreviatedRole returns ServerGroup from role // ServerGroupFromRole returns ServerGroup from role
func ServerGroupFromRole(label string) ServerGroup { func ServerGroupFromRole(label string) ServerGroup {
switch label { switch label {
case ServerGroupSingleString: case ServerGroupSingleString:
@ -302,6 +324,8 @@ func ServerGroupFromRole(label string) ServerGroup {
return ServerGroupSyncMasters return ServerGroupSyncMasters
case ServerGroupSyncWorkersString: case ServerGroupSyncWorkersString:
return ServerGroupSyncWorkers return ServerGroupSyncWorkers
case ServerGroupGatewaysString:
return ServerGroupGateways
case ServerGroupImageDiscoveryString: case ServerGroupImageDiscoveryString:
return ServerGroupImageDiscovery return ServerGroupImageDiscovery
default: default:

View file

@ -46,6 +46,7 @@ func TestServerGroupSpecValidateCount(t *testing.T) {
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncMasters).New().Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncMasters).New().Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncWorkers).New().Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncWorkers).New().Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupGateways).New().Validate(ServerGroupGateways, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2), MinCount: util.NewType[int](2), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2), MinCount: util.NewType[int](2), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](1), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](1), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment))
@ -112,6 +113,10 @@ func TestServerGroupSpecDefault(t *testing.T) {
assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeActiveFailover).New().GetCount()) assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeActiveFailover).New().GetCount())
assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).New().GetCount()) assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).New().GetCount())
assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupGateways, false, DeploymentModeSingle).New().GetCount())
assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupGateways, false, DeploymentModeActiveFailover).New().GetCount())
assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupGateways, true, DeploymentModeCluster).New().GetCount())
for _, g := range AllServerGroups { for _, g := range AllServerGroups {
assert.Equal(t, 0, len(def(ServerGroupSpec{}, g, true, DeploymentModeSingle).Args)) assert.Equal(t, 0, len(def(ServerGroupSpec{}, g, true, DeploymentModeSingle).Args))
assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).New().GetStorageClassName()) assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).New().GetStorageClassName())

View file

@ -33,6 +33,7 @@ func TestServerGroupAsRole(t *testing.T) {
assert.Equal(t, "coordinator", ServerGroupCoordinators.AsRole()) assert.Equal(t, "coordinator", ServerGroupCoordinators.AsRole())
assert.Equal(t, "syncmaster", ServerGroupSyncMasters.AsRole()) assert.Equal(t, "syncmaster", ServerGroupSyncMasters.AsRole())
assert.Equal(t, "syncworker", ServerGroupSyncWorkers.AsRole()) assert.Equal(t, "syncworker", ServerGroupSyncWorkers.AsRole())
assert.Equal(t, "gateways", ServerGroupGateways.AsRole())
} }
func TestServerGroupAsRoleAbbreviated(t *testing.T) { func TestServerGroupAsRoleAbbreviated(t *testing.T) {
@ -42,6 +43,7 @@ func TestServerGroupAsRoleAbbreviated(t *testing.T) {
assert.Equal(t, "crdn", ServerGroupCoordinators.AsRoleAbbreviated()) assert.Equal(t, "crdn", ServerGroupCoordinators.AsRoleAbbreviated())
assert.Equal(t, "syma", ServerGroupSyncMasters.AsRoleAbbreviated()) assert.Equal(t, "syma", ServerGroupSyncMasters.AsRoleAbbreviated())
assert.Equal(t, "sywo", ServerGroupSyncWorkers.AsRoleAbbreviated()) assert.Equal(t, "sywo", ServerGroupSyncWorkers.AsRoleAbbreviated())
assert.Equal(t, "gway", ServerGroupGateways.AsRoleAbbreviated())
} }
func TestServerGroupIsArangod(t *testing.T) { func TestServerGroupIsArangod(t *testing.T) {
@ -51,6 +53,7 @@ func TestServerGroupIsArangod(t *testing.T) {
assert.True(t, ServerGroupCoordinators.IsArangod()) assert.True(t, ServerGroupCoordinators.IsArangod())
assert.False(t, ServerGroupSyncMasters.IsArangod()) assert.False(t, ServerGroupSyncMasters.IsArangod())
assert.False(t, ServerGroupSyncWorkers.IsArangod()) assert.False(t, ServerGroupSyncWorkers.IsArangod())
assert.False(t, ServerGroupGateways.IsArangod())
} }
func TestServerGroupIsArangosync(t *testing.T) { func TestServerGroupIsArangosync(t *testing.T) {
@ -60,6 +63,7 @@ func TestServerGroupIsArangosync(t *testing.T) {
assert.False(t, ServerGroupCoordinators.IsArangosync()) assert.False(t, ServerGroupCoordinators.IsArangosync())
assert.True(t, ServerGroupSyncMasters.IsArangosync()) assert.True(t, ServerGroupSyncMasters.IsArangosync())
assert.True(t, ServerGroupSyncWorkers.IsArangosync()) assert.True(t, ServerGroupSyncWorkers.IsArangosync())
assert.False(t, ServerGroupGateways.IsArangosync())
} }
func TestServerGroupType(t *testing.T) { func TestServerGroupType(t *testing.T) {
@ -71,4 +75,6 @@ func TestServerGroupType(t *testing.T) {
assert.Equal(t, ServerGroupTypeArangoD, ServerGroupCoordinators.Type()) assert.Equal(t, ServerGroupTypeArangoD, ServerGroupCoordinators.Type())
assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncMasters.Type()) assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncMasters.Type())
assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncWorkers.Type()) assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncWorkers.Type())
assert.Equal(t, ServerGroupTypeGateway, ServerGroupGateways.Type())
} }

View file

@ -27,4 +27,5 @@ const (
ServerGroupTypeArangoD ServerGroupTypeArangoD
ServerGroupTypeArangoSync ServerGroupTypeArangoSync
ServerGroupTypeID ServerGroupTypeID
ServerGroupTypeGateway
) )

View file

@ -1101,6 +1101,11 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
in.Coordinators.DeepCopyInto(&out.Coordinators) in.Coordinators.DeepCopyInto(&out.Coordinators)
in.SyncMasters.DeepCopyInto(&out.SyncMasters) in.SyncMasters.DeepCopyInto(&out.SyncMasters)
in.SyncWorkers.DeepCopyInto(&out.SyncWorkers) in.SyncWorkers.DeepCopyInto(&out.SyncWorkers)
if in.Gateways != nil {
in, out := &in.Gateways, &out.Gateways
*out = new(ServerGroupSpec)
(*in).DeepCopyInto(*out)
}
if in.MemberPropagationMode != nil { if in.MemberPropagationMode != nil {
in, out := &in.MemberPropagationMode, &out.MemberPropagationMode in, out := &in.MemberPropagationMode, &out.MemberPropagationMode
*out = new(DeploymentMemberPropagationMode) *out = new(DeploymentMemberPropagationMode)
@ -1148,6 +1153,11 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
*out = new(string) *out = new(string)
**out = **in **out = **in
} }
if in.Gateway != nil {
in, out := &in.Gateway, &out.Gateway
*out = new(DeploymentSpecGateway)
(*in).DeepCopyInto(*out)
}
return return
} }
@ -1161,6 +1171,27 @@ func (in *DeploymentSpec) DeepCopy() *DeploymentSpec {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeploymentSpecGateway) DeepCopyInto(out *DeploymentSpecGateway) {
*out = *in
if in.Enabled != nil {
in, out := &in.Enabled, &out.Enabled
*out = new(bool)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentSpecGateway.
func (in *DeploymentSpecGateway) DeepCopy() *DeploymentSpecGateway {
if in == nil {
return nil
}
out := new(DeploymentSpecGateway)
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 *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
*out = *in *out = *in
@ -1291,6 +1322,11 @@ func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
*out = new(ServerGroupStatus) *out = new(ServerGroupStatus)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.Gateways != nil {
in, out := &in.Gateways, &out.Gateways
*out = new(ServerGroupStatus)
(*in).DeepCopyInto(*out)
}
return return
} }
@ -1521,6 +1557,13 @@ func (in *DeploymentStatusMembers) DeepCopyInto(out *DeploymentStatusMembers) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.Gateways != nil {
in, out := &in.Gateways, &out.Gateways
*out = make(MemberStatusList, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return return
} }

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -73,7 +73,7 @@ func (d *ArangoDeployment) AsOwner() meta.OwnerReference {
// ForeachServerGroup calls the given callback for all server groups. // ForeachServerGroup calls the given callback for all server groups.
// If the callback returns an error, this error is returned and no other server // If the callback returns an error, this error is returned and no other server
// groups are processed. // groups are processed.
// Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers // Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers, gateways
func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *DeploymentStatus) error { func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *DeploymentStatus) error {
if status == nil { if status == nil {
status = &d.Status status = &d.Status
@ -84,7 +84,7 @@ func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *Deploy
// ForeachServerGroupAccepted calls the given callback for all accepted server groups. // ForeachServerGroupAccepted calls the given callback for all accepted server groups.
// If the callback returns an error, this error is returned and no other server // If the callback returns an error, this error is returned and no other server
// groups are processed. // groups are processed.
// Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers // Groups are processed in this order: agents, single, dbservers, coordinators, syncmasters, syncworkers, gateways
func (d *ArangoDeployment) ForeachServerGroupAccepted(cb ServerGroupFunc, status *DeploymentStatus) error { func (d *ArangoDeployment) ForeachServerGroupAccepted(cb ServerGroupFunc, status *DeploymentStatus) error {
if status == nil { if status == nil {
status = &d.Status status = &d.Status
@ -115,6 +115,9 @@ func (d *ArangoDeployment) foreachServerGroup(cb ServerGroupFunc, spec Deploymen
if err := cb(ServerGroupSyncWorkers, spec.SyncWorkers, &status.Members.SyncWorkers); err != nil { if err := cb(ServerGroupSyncWorkers, spec.SyncWorkers, &status.Members.SyncWorkers); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
if err := cb(ServerGroupGateways, spec.Gateways.Get(), &status.Members.Gateways); err != nil {
return errors.WithStack(err)
}
return nil return nil
} }

View file

@ -199,6 +199,9 @@ type DeploymentSpec struct {
// SyncWorkers contains specification for Syncworker pods running in deployment mode `Cluster`. // SyncWorkers contains specification for Syncworker pods running in deployment mode `Cluster`.
SyncWorkers ServerGroupSpec `json:"syncworkers"` SyncWorkers ServerGroupSpec `json:"syncworkers"`
// Gateways contain specification for Gateway pods running in deployment mode `Single` or `Cluster`.
Gateways *ServerGroupSpec `json:"gateways,omitempty"`
// MemberPropagationMode defines how changes to pod spec should be propogated. // MemberPropagationMode defines how changes to pod spec should be propogated.
// Changes to a pods configuration require a restart of that pod in almost all cases. // Changes to a pods configuration require a restart of that pod in almost all cases.
// Pods are restarted eagerly by default, which can cause more restarts than desired, especially when updating arangod as well as the operator. // Pods are restarted eagerly by default, which can cause more restarts than desired, especially when updating arangod as well as the operator.
@ -256,6 +259,9 @@ type DeploymentSpec struct {
// Timezone if specified, will set a timezone for deployment. // Timezone if specified, will set a timezone for deployment.
// Must be in format accepted by "tzdata", e.g. `America/New_York` or `Europe/London` // Must be in format accepted by "tzdata", e.g. `America/New_York` or `Europe/London`
Timezone *string `json:"timezone,omitempty"` Timezone *string `json:"timezone,omitempty"`
// Gateway defined main Gateway configuration.
Gateway *DeploymentSpecGateway `json:"gateway,omitempty"`
} }
// GetAllowMemberRecreation returns member recreation policy based on group and settings // GetAllowMemberRecreation returns member recreation policy based on group and settings
@ -267,6 +273,8 @@ func (s *DeploymentSpec) GetAllowMemberRecreation(group ServerGroup) bool {
groupSpec := s.GetServerGroupSpec(group) groupSpec := s.GetServerGroupSpec(group)
switch group { switch group {
case ServerGroupGateways:
return true
case ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers:
if v := groupSpec.AllowMemberRecreation; v == nil { if v := groupSpec.AllowMemberRecreation; v == nil {
return true return true
@ -326,6 +334,11 @@ func (s DeploymentSpec) GetSyncImage() string {
return s.GetImage() return s.GetImage()
} }
// IsGatewayEnabled returns true when the deployment has gateways enabled.
func (s DeploymentSpec) IsGatewayEnabled() bool {
return s.Gateway.IsEnabled()
}
// GetImagePullPolicy returns the value of imagePullPolicy. // GetImagePullPolicy returns the value of imagePullPolicy.
func (s DeploymentSpec) GetImagePullPolicy() core.PullPolicy { func (s DeploymentSpec) GetImagePullPolicy() core.PullPolicy {
return util.TypeOrDefault[core.PullPolicy](s.ImagePullPolicy) return util.TypeOrDefault[core.PullPolicy](s.ImagePullPolicy)
@ -364,8 +377,7 @@ func (s DeploymentSpec) IsSecure() bool {
return s.TLS.IsSecure() return s.TLS.IsSecure()
} }
// GetServerGroupSpec returns the server group spec (from this // GetServerGroupSpec returns the server group spec (from this deployment spec) for the given group.
// deployment spec) for the given group.
func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec { func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec {
switch group { switch group {
case ServerGroupSingle: case ServerGroupSingle:
@ -380,13 +392,14 @@ func (s DeploymentSpec) GetServerGroupSpec(group ServerGroup) ServerGroupSpec {
return s.SyncMasters.WithGroup(group) return s.SyncMasters.WithGroup(group)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return s.SyncWorkers.WithGroup(group) return s.SyncWorkers.WithGroup(group)
case ServerGroupGateways:
return s.Gateways.WithGroup(group)
default: default:
return ServerGroupSpec{} return ServerGroupSpec{}
} }
} }
// UpdateServerGroupSpec returns the server group spec (from this // UpdateServerGroupSpec returns the server group spec (from this deployment spec) for the given group.
// deployment spec) for the given group.
func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGroupSpec) { func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGroupSpec) {
switch group { switch group {
case ServerGroupSingle: case ServerGroupSingle:
@ -401,6 +414,8 @@ func (s *DeploymentSpec) UpdateServerGroupSpec(group ServerGroup, gspec ServerGr
s.SyncMasters = gspec s.SyncMasters = gspec
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
s.SyncWorkers = gspec s.SyncWorkers = gspec
case ServerGroupGateways:
s.Gateways = gspec.DeepCopy()
} }
} }
@ -421,6 +436,11 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
if s.GetImagePullPolicy() == "" { if s.GetImagePullPolicy() == "" {
s.ImagePullPolicy = util.NewType[core.PullPolicy](core.PullIfNotPresent) s.ImagePullPolicy = util.NewType[core.PullPolicy](core.PullIfNotPresent)
} }
if s.Gateway.IsEnabled() {
if s.Gateways == nil {
s.Gateways = &ServerGroupSpec{}
}
}
s.ExternalAccess.SetDefaults() s.ExternalAccess.SetDefaults()
s.RocksDB.SetDefaults() s.RocksDB.SetDefaults()
s.Authentication.SetDefaults(deploymentName + "-jwt") s.Authentication.SetDefaults(deploymentName + "-jwt")
@ -432,6 +452,7 @@ func (s *DeploymentSpec) SetDefaults(deploymentName string) {
s.Coordinators.SetDefaults(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode()) s.Coordinators.SetDefaults(ServerGroupCoordinators, s.GetMode().HasCoordinators(), s.GetMode())
s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode()) s.SyncMasters.SetDefaults(ServerGroupSyncMasters, s.Sync.IsEnabled(), s.GetMode())
s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode()) s.SyncWorkers.SetDefaults(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode())
s.Gateways.SetDefaults(ServerGroupGateways, s.IsGatewayEnabled(), s.GetMode())
s.Metrics.SetDefaults(deploymentName+"-exporter-jwt-token", s.Authentication.IsAuthenticated()) s.Metrics.SetDefaults(deploymentName+"-exporter-jwt-token", s.Authentication.IsAuthenticated())
s.Chaos.SetDefaults() s.Chaos.SetDefaults()
s.Bootstrap.SetDefaults(deploymentName) s.Bootstrap.SetDefaults(deploymentName)
@ -480,6 +501,7 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
s.Coordinators.SetDefaultsFrom(source.Coordinators) s.Coordinators.SetDefaultsFrom(source.Coordinators)
s.SyncMasters.SetDefaultsFrom(source.SyncMasters) s.SyncMasters.SetDefaultsFrom(source.SyncMasters)
s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers) s.SyncWorkers.SetDefaultsFrom(source.SyncWorkers)
s.Gateways.SetDefaultsFrom(source.Gateways.Get())
s.Metrics.SetDefaultsFrom(source.Metrics) s.Metrics.SetDefaultsFrom(source.Metrics)
s.Lifecycle.SetDefaultsFrom(source.Lifecycle) s.Lifecycle.SetDefaultsFrom(source.Lifecycle)
s.Chaos.SetDefaultsFrom(source.Chaos) s.Chaos.SetDefaultsFrom(source.Chaos)
@ -539,6 +561,11 @@ func (s *DeploymentSpec) Validate() error {
if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil { if err := s.SyncWorkers.Validate(ServerGroupSyncWorkers, s.Sync.IsEnabled(), s.GetMode(), s.GetEnvironment()); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
if s.IsGatewayEnabled() {
if err := s.Gateways.Validate(ServerGroupGateways, s.IsGatewayEnabled(), s.GetMode(), s.GetEnvironment()); err != nil {
return errors.WithStack(err)
}
}
if err := s.Metrics.Validate(); err != nil { if err := s.Metrics.Validate(); err != nil {
return errors.WithStack(errors.Wrap(err, "spec.metrics")) return errors.WithStack(errors.Wrap(err, "spec.metrics"))
} }
@ -554,6 +581,9 @@ func (s *DeploymentSpec) Validate() error {
if err := s.Architecture.Validate(); err != nil { if err := s.Architecture.Validate(); err != nil {
return errors.WithStack(errors.Wrap(err, "spec.architecture")) return errors.WithStack(errors.Wrap(err, "spec.architecture"))
} }
if err := s.Gateway.Validate(); err != nil {
return errors.WithStack(errors.Wrap(err, "spec.architecture"))
}
return nil return nil
} }
@ -614,6 +644,14 @@ func (s DeploymentSpec) ResetImmutableFields(target *DeploymentSpec) []string {
if l := s.SyncWorkers.ResetImmutableFields(ServerGroupSyncWorkers, "syncworkers", &target.SyncWorkers); l != nil { if l := s.SyncWorkers.ResetImmutableFields(ServerGroupSyncWorkers, "syncworkers", &target.SyncWorkers); l != nil {
resetFields = append(resetFields, l...) resetFields = append(resetFields, l...)
} }
if s.Gateways != nil {
if target.Gateways == nil {
target.Gateways = &ServerGroupSpec{}
}
if l := s.Gateways.ResetImmutableFields(ServerGroupGateways, "gateways", target.Gateways); l != nil {
resetFields = append(resetFields, l...)
}
}
if l := s.Metrics.ResetImmutableFields("metrics", &target.Metrics); l != nil { if l := s.Metrics.ResetImmutableFields("metrics", &target.Metrics); l != nil {
resetFields = append(resetFields, l...) resetFields = append(resetFields, l...)
} }

View file

@ -0,0 +1,37 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package v2alpha1
type DeploymentSpecGateway struct {
Enabled *bool `json:"enabled,omitempty"`
}
func (d *DeploymentSpecGateway) IsEnabled() bool {
if d == nil || d.Enabled == nil {
return false
}
return *d.Enabled
}
func (d *DeploymentSpecGateway) Validate() error {
return nil
}

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -103,6 +103,7 @@ type DeploymentStatus struct {
Coordinators *ServerGroupStatus `json:"coordinators,omitempty"` Coordinators *ServerGroupStatus `json:"coordinators,omitempty"`
SyncMasters *ServerGroupStatus `json:"syncmasters,omitempty"` SyncMasters *ServerGroupStatus `json:"syncmasters,omitempty"`
SyncWorkers *ServerGroupStatus `json:"syncworkers,omitempty"` SyncWorkers *ServerGroupStatus `json:"syncworkers,omitempty"`
Gateways *ServerGroupStatus `json:"gateways,omitempty"`
} }
// Equal checks for equality // Equal checks for equality
@ -174,6 +175,8 @@ func (ds DeploymentStatus) getServerGroupStatus(group ServerGroup) *ServerGroupS
return ds.SyncMasters.DeepCopy() return ds.SyncMasters.DeepCopy()
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ds.SyncWorkers.DeepCopy() return ds.SyncWorkers.DeepCopy()
case ServerGroupGateways:
return ds.Gateways.DeepCopy()
default: default:
return nil return nil
} }
@ -195,5 +198,7 @@ func (ds *DeploymentStatus) UpdateServerGroupStatus(group ServerGroup, gspec Ser
ds.SyncMasters = &gspec ds.SyncMasters = &gspec
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
ds.SyncWorkers = &gspec ds.SyncWorkers = &gspec
case ServerGroupGateways:
ds.Gateways = &gspec
} }
} }

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -35,6 +35,7 @@ type DeploymentStatusMembers struct {
Coordinators MemberStatusList `json:"coordinators,omitempty"` Coordinators MemberStatusList `json:"coordinators,omitempty"`
SyncMasters MemberStatusList `json:"syncmasters,omitempty"` SyncMasters MemberStatusList `json:"syncmasters,omitempty"`
SyncWorkers MemberStatusList `json:"syncworkers,omitempty"` SyncWorkers MemberStatusList `json:"syncworkers,omitempty"`
Gateways MemberStatusList `json:"gateways,omitempty"`
} }
// Equal checks for equality // Equal checks for equality
@ -44,7 +45,8 @@ func (ds DeploymentStatusMembers) Equal(other DeploymentStatusMembers) bool {
ds.DBServers.Equal(other.DBServers) && ds.DBServers.Equal(other.DBServers) &&
ds.Coordinators.Equal(other.Coordinators) && ds.Coordinators.Equal(other.Coordinators) &&
ds.SyncMasters.Equal(other.SyncMasters) && ds.SyncMasters.Equal(other.SyncMasters) &&
ds.SyncWorkers.Equal(other.SyncWorkers) ds.SyncWorkers.Equal(other.SyncWorkers) &&
ds.Gateways.Equal(other.Gateways)
} }
// ContainsID returns true if the given set of members contains a member with given ID. // ContainsID returns true if the given set of members contains a member with given ID.
@ -54,7 +56,8 @@ func (ds DeploymentStatusMembers) ContainsID(id string) bool {
ds.DBServers.ContainsID(id) || ds.DBServers.ContainsID(id) ||
ds.Coordinators.ContainsID(id) || ds.Coordinators.ContainsID(id) ||
ds.SyncMasters.ContainsID(id) || ds.SyncMasters.ContainsID(id) ||
ds.SyncWorkers.ContainsID(id) ds.SyncWorkers.ContainsID(id) ||
ds.Gateways.ContainsID(id)
} }
// ElementByID returns the element in the given list that has the given ID and true. // ElementByID returns the element in the given list that has the given ID and true.
@ -78,6 +81,9 @@ func (ds DeploymentStatusMembers) ElementByID(id string) (MemberStatus, ServerGr
if result, found := ds.SyncWorkers.ElementByID(id); found { if result, found := ds.SyncWorkers.ElementByID(id); found {
return result, ServerGroupSyncWorkers, true return result, ServerGroupSyncWorkers, true
} }
if result, found := ds.Gateways.ElementByID(id); found {
return result, ServerGroupGateways, true
}
return MemberStatus{}, 0, false return MemberStatus{}, 0, false
} }
@ -129,6 +135,10 @@ func (ds DeploymentStatusMembers) ForServerGroup(cb MemberStatusFunc, group Serv
if err := cb(ServerGroupSyncWorkers, ds.SyncWorkers); err != nil { if err := cb(ServerGroupSyncWorkers, ds.SyncWorkers); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
case ServerGroupGateways:
if err := cb(ServerGroupGateways, ds.Gateways); err != nil {
return errors.WithStack(err)
}
} }
return nil return nil
} }
@ -155,6 +165,9 @@ func (ds DeploymentStatusMembers) MemberStatusByPodName(podName string) (MemberS
if result, found := ds.SyncWorkers.ElementByPodName(podName); found { if result, found := ds.SyncWorkers.ElementByPodName(podName); found {
return result, ServerGroupSyncWorkers, true return result, ServerGroupSyncWorkers, true
} }
if result, found := ds.Gateways.ElementByPodName(podName); found {
return result, ServerGroupGateways, true
}
return MemberStatus{}, 0, false return MemberStatus{}, 0, false
} }
@ -190,6 +203,8 @@ func (ds *DeploymentStatusMembers) Add(status MemberStatus, group ServerGroup) e
err = ds.SyncMasters.add(status) err = ds.SyncMasters.add(status)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
err = ds.SyncWorkers.add(status) err = ds.SyncWorkers.add(status)
case ServerGroupGateways:
err = ds.Gateways.add(status)
default: default:
return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group))
} }
@ -215,6 +230,8 @@ func (ds *DeploymentStatusMembers) Update(status MemberStatus, group ServerGroup
err = ds.SyncMasters.update(status) err = ds.SyncMasters.update(status)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
err = ds.SyncWorkers.update(status) err = ds.SyncWorkers.update(status)
case ServerGroupGateways:
err = ds.Gateways.update(status)
default: default:
return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group))
} }
@ -241,6 +258,8 @@ func (ds *DeploymentStatusMembers) RemoveByID(id string, group ServerGroup) erro
err = ds.SyncMasters.removeByID(id) err = ds.SyncMasters.removeByID(id)
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
err = ds.SyncWorkers.removeByID(id) err = ds.SyncWorkers.removeByID(id)
case ServerGroupGateways:
err = ds.Gateways.removeByID(id)
default: default:
return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group)) return errors.WithStack(errors.Wrapf(NotFoundError, "ServerGroup %d is not known", group))
} }
@ -250,23 +269,30 @@ func (ds *DeploymentStatusMembers) RemoveByID(id string, group ServerGroup) erro
return nil return nil
} }
// AllMembersReady returns true when all members, that must be ready for the given mode, are in the Ready state. // AllMembersReady returns true when all members, that must be ready for the given mode are in the Ready state.
func (ds DeploymentStatusMembers) AllMembersReady(mode DeploymentMode, syncEnabled bool) bool { func (ds DeploymentStatusMembers) AllMembersReady(mode DeploymentMode, syncEnabled, gatewayEnabled bool) bool {
syncReady := func() bool { syncReady := func() bool {
if syncEnabled { if syncEnabled {
return ds.SyncMasters.AllMembersReady() && ds.SyncWorkers.AllMembersReady() return ds.SyncMasters.AllMembersReady() && ds.SyncWorkers.AllMembersReady()
} }
return true return true
} }
gatewayReady := func() bool {
if gatewayEnabled {
return ds.Gateways.AllMembersReady()
}
return true
}
switch mode { switch mode {
case DeploymentModeSingle: case DeploymentModeSingle:
return ds.Single.MembersReady() > 0 return ds.Single.MembersReady() > 0 && gatewayReady()
case DeploymentModeActiveFailover: case DeploymentModeActiveFailover:
return ds.Agents.AllMembersReady() && ds.Single.MembersReady() > 0 return ds.Agents.AllMembersReady() && ds.Single.MembersReady() > 0
case DeploymentModeCluster: case DeploymentModeCluster:
return ds.Agents.AllMembersReady() && return ds.Agents.AllMembersReady() &&
ds.DBServers.AllMembersReady() && ds.DBServers.AllMembersReady() &&
ds.Coordinators.AllMembersReady() && ds.Coordinators.AllMembersReady() &&
gatewayReady() &&
syncReady() syncReady()
default: default:
return false return false
@ -288,6 +314,8 @@ func (ds DeploymentStatusMembers) MembersOfGroup(group ServerGroup) MemberStatus
return ds.SyncMasters return ds.SyncMasters
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ds.SyncWorkers return ds.SyncWorkers
case ServerGroupGateways:
return ds.Gateways
default: default:
return MemberStatusList{} return MemberStatusList{}
} }

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -34,6 +34,7 @@ func newMemberList() DeploymentStatusMembers {
Coordinators: MemberStatusList{{ID: ServerGroupCoordinators.AsRole()}}, Coordinators: MemberStatusList{{ID: ServerGroupCoordinators.AsRole()}},
SyncMasters: MemberStatusList{{ID: ServerGroupSyncMasters.AsRole()}}, SyncMasters: MemberStatusList{{ID: ServerGroupSyncMasters.AsRole()}},
SyncWorkers: MemberStatusList{{ID: ServerGroupSyncWorkers.AsRole()}}, SyncWorkers: MemberStatusList{{ID: ServerGroupSyncWorkers.AsRole()}},
Gateways: MemberStatusList{{ID: ServerGroupGateways.AsRole()}},
} }
} }

View file

@ -84,6 +84,7 @@ const (
ServerGroupCoordinators ServerGroup = 4 ServerGroupCoordinators ServerGroup = 4
ServerGroupSyncMasters ServerGroup = 5 ServerGroupSyncMasters ServerGroup = 5
ServerGroupSyncWorkers ServerGroup = 6 ServerGroupSyncWorkers ServerGroup = 6
ServerGroupGateways ServerGroup = 7
ServerGroupImageDiscovery ServerGroup = -1 ServerGroupImageDiscovery ServerGroup = -1
ServerGroupSingleString = "single" ServerGroupSingleString = "single"
@ -92,6 +93,7 @@ const (
ServerGroupCoordinatorsString = "coordinator" ServerGroupCoordinatorsString = "coordinator"
ServerGroupSyncMastersString = "syncmaster" ServerGroupSyncMastersString = "syncmaster"
ServerGroupSyncWorkersString = "syncworker" ServerGroupSyncWorkersString = "syncworker"
ServerGroupGatewaysString = "gateways"
ServerGroupImageDiscoveryString = "id" ServerGroupImageDiscoveryString = "id"
ServerGroupSingleAbbreviatedString = "sngl" ServerGroupSingleAbbreviatedString = "sngl"
@ -100,6 +102,7 @@ const (
ServerGroupCoordinatorsAbbreviatedString = "crdn" ServerGroupCoordinatorsAbbreviatedString = "crdn"
ServerGroupSyncMastersAbbreviatedString = "syma" ServerGroupSyncMastersAbbreviatedString = "syma"
ServerGroupSyncWorkersAbbreviatedString = "sywo" ServerGroupSyncWorkersAbbreviatedString = "sywo"
ServerGroupGatewaysAbbreviatedString = "gway"
ServerGroupImageDiscoveryAbbreviatedString = "id" ServerGroupImageDiscoveryAbbreviatedString = "id"
) )
@ -112,6 +115,7 @@ var (
ServerGroupCoordinators, ServerGroupCoordinators,
ServerGroupSyncMasters, ServerGroupSyncMasters,
ServerGroupSyncWorkers, ServerGroupSyncWorkers,
ServerGroupGateways,
} }
// AllArangoDServerGroups contains a constant list of all ArangoD server groups // AllArangoDServerGroups contains a constant list of all ArangoD server groups
AllArangoDServerGroups = []ServerGroup{ AllArangoDServerGroups = []ServerGroup{
@ -131,6 +135,8 @@ func (g ServerGroup) Type() ServerGroupType {
return ServerGroupTypeID return ServerGroupTypeID
case ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupSyncMasters, ServerGroupSyncWorkers:
return ServerGroupTypeArangoSync return ServerGroupTypeArangoSync
case ServerGroupGateways:
return ServerGroupTypeGateway
default: default:
return ServerGroupTypeUnknown return ServerGroupTypeUnknown
} }
@ -151,6 +157,8 @@ func (g ServerGroup) AsRole() string {
return ServerGroupSyncMastersString return ServerGroupSyncMastersString
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ServerGroupSyncWorkersString return ServerGroupSyncWorkersString
case ServerGroupGateways:
return ServerGroupGatewaysString
case ServerGroupImageDiscovery: case ServerGroupImageDiscovery:
return ServerGroupImageDiscoveryString return ServerGroupImageDiscoveryString
default: default:
@ -163,14 +171,14 @@ func (g ServerGroup) Enabled(mode DeploymentMode) bool {
switch mode { switch mode {
case DeploymentModeSingle: case DeploymentModeSingle:
switch g { switch g {
case ServerGroupSingle: case ServerGroupSingle, ServerGroupGateways:
return true return true
default: default:
return false return false
} }
case DeploymentModeCluster: case DeploymentModeCluster:
switch g { switch g {
case ServerGroupAgents, ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupAgents, ServerGroupDBServers, ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers, ServerGroupGateways:
return true return true
default: default:
return false return false
@ -202,6 +210,8 @@ func (g ServerGroup) AsRoleAbbreviated() string {
return ServerGroupSyncMastersAbbreviatedString return ServerGroupSyncMastersAbbreviatedString
case ServerGroupSyncWorkers: case ServerGroupSyncWorkers:
return ServerGroupSyncWorkersAbbreviatedString return ServerGroupSyncWorkersAbbreviatedString
case ServerGroupGateways:
return ServerGroupGatewaysAbbreviatedString
case ServerGroupImageDiscovery: case ServerGroupImageDiscovery:
return ServerGroupImageDiscoveryAbbreviatedString return ServerGroupImageDiscoveryAbbreviatedString
default: default:
@ -228,7 +238,7 @@ func (g ServerGroup) DefaultTerminationGracePeriod() time.Duration {
// IsStateless returns true when the groups runs servers without a persistent volume. // IsStateless returns true when the groups runs servers without a persistent volume.
func (g ServerGroup) IsStateless() bool { func (g ServerGroup) IsStateless() bool {
switch g { switch g {
case ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers: case ServerGroupCoordinators, ServerGroupSyncMasters, ServerGroupSyncWorkers, ServerGroupGateways:
return true return true
default: default:
return false return false
@ -255,6 +265,16 @@ func (g ServerGroup) IsArangosync() bool {
} }
} }
// IsGateway returns true when the group is a gateway group
func (g ServerGroup) IsGateway() bool {
switch g {
case ServerGroupGateways:
return true
default:
return false
}
}
// IsExportMetrics return true when the group can be used with the arangodbexporter // IsExportMetrics return true when the group can be used with the arangodbexporter
func (g ServerGroup) IsExportMetrics() bool { func (g ServerGroup) IsExportMetrics() bool {
switch g { switch g {
@ -280,6 +300,8 @@ func ServerGroupFromAbbreviatedRole(label string) ServerGroup {
return ServerGroupSyncMasters return ServerGroupSyncMasters
case ServerGroupSyncWorkersAbbreviatedString: case ServerGroupSyncWorkersAbbreviatedString:
return ServerGroupSyncWorkers return ServerGroupSyncWorkers
case ServerGroupGatewaysAbbreviatedString:
return ServerGroupGateways
case ServerGroupImageDiscoveryAbbreviatedString: case ServerGroupImageDiscoveryAbbreviatedString:
return ServerGroupImageDiscovery return ServerGroupImageDiscovery
default: default:
@ -287,7 +309,7 @@ func ServerGroupFromAbbreviatedRole(label string) ServerGroup {
} }
} }
// ServerGroupFromAbbreviatedRole returns ServerGroup from role // ServerGroupFromRole returns ServerGroup from role
func ServerGroupFromRole(label string) ServerGroup { func ServerGroupFromRole(label string) ServerGroup {
switch label { switch label {
case ServerGroupSingleString: case ServerGroupSingleString:
@ -302,6 +324,8 @@ func ServerGroupFromRole(label string) ServerGroup {
return ServerGroupSyncMasters return ServerGroupSyncMasters
case ServerGroupSyncWorkersString: case ServerGroupSyncWorkersString:
return ServerGroupSyncWorkers return ServerGroupSyncWorkers
case ServerGroupGatewaysString:
return ServerGroupGateways
case ServerGroupImageDiscoveryString: case ServerGroupImageDiscoveryString:
return ServerGroupImageDiscovery return ServerGroupImageDiscovery
default: default:

View file

@ -46,6 +46,7 @@ func TestServerGroupSpecValidateCount(t *testing.T) {
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncMasters).New().Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncMasters).New().Validate(ServerGroupSyncMasters, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncWorkers).New().Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupSyncWorkers).New().Validate(ServerGroupSyncWorkers, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2)}.New().WithGroup(ServerGroupGateways).New().Validate(ServerGroupGateways, true, DeploymentModeCluster, EnvironmentProduction))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2), MinCount: util.NewType[int](2), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](2), MinCount: util.NewType[int](2), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment))
assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](1), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment)) assert.Nil(t, ServerGroupSpec{Count: util.NewType[int](1), MaxCount: util.NewType[int](5)}.New().WithGroup(ServerGroupCoordinators).New().Validate(ServerGroupCoordinators, true, DeploymentModeCluster, EnvironmentDevelopment))
@ -112,6 +113,10 @@ func TestServerGroupSpecDefault(t *testing.T) {
assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeActiveFailover).New().GetCount()) assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupSyncWorkers, false, DeploymentModeActiveFailover).New().GetCount())
assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).New().GetCount()) assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupSyncWorkers, true, DeploymentModeCluster).New().GetCount())
assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupGateways, false, DeploymentModeSingle).New().GetCount())
assert.Equal(t, 0, def(ServerGroupSpec{}, ServerGroupGateways, false, DeploymentModeActiveFailover).New().GetCount())
assert.Equal(t, 3, def(ServerGroupSpec{}, ServerGroupGateways, true, DeploymentModeCluster).New().GetCount())
for _, g := range AllServerGroups { for _, g := range AllServerGroups {
assert.Equal(t, 0, len(def(ServerGroupSpec{}, g, true, DeploymentModeSingle).Args)) assert.Equal(t, 0, len(def(ServerGroupSpec{}, g, true, DeploymentModeSingle).Args))
assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).New().GetStorageClassName()) assert.Equal(t, "", def(ServerGroupSpec{}, g, true, DeploymentModeSingle).New().GetStorageClassName())

View file

@ -33,6 +33,7 @@ func TestServerGroupAsRole(t *testing.T) {
assert.Equal(t, "coordinator", ServerGroupCoordinators.AsRole()) assert.Equal(t, "coordinator", ServerGroupCoordinators.AsRole())
assert.Equal(t, "syncmaster", ServerGroupSyncMasters.AsRole()) assert.Equal(t, "syncmaster", ServerGroupSyncMasters.AsRole())
assert.Equal(t, "syncworker", ServerGroupSyncWorkers.AsRole()) assert.Equal(t, "syncworker", ServerGroupSyncWorkers.AsRole())
assert.Equal(t, "gateways", ServerGroupGateways.AsRole())
} }
func TestServerGroupAsRoleAbbreviated(t *testing.T) { func TestServerGroupAsRoleAbbreviated(t *testing.T) {
@ -42,6 +43,7 @@ func TestServerGroupAsRoleAbbreviated(t *testing.T) {
assert.Equal(t, "crdn", ServerGroupCoordinators.AsRoleAbbreviated()) assert.Equal(t, "crdn", ServerGroupCoordinators.AsRoleAbbreviated())
assert.Equal(t, "syma", ServerGroupSyncMasters.AsRoleAbbreviated()) assert.Equal(t, "syma", ServerGroupSyncMasters.AsRoleAbbreviated())
assert.Equal(t, "sywo", ServerGroupSyncWorkers.AsRoleAbbreviated()) assert.Equal(t, "sywo", ServerGroupSyncWorkers.AsRoleAbbreviated())
assert.Equal(t, "gway", ServerGroupGateways.AsRoleAbbreviated())
} }
func TestServerGroupIsArangod(t *testing.T) { func TestServerGroupIsArangod(t *testing.T) {
@ -51,6 +53,7 @@ func TestServerGroupIsArangod(t *testing.T) {
assert.True(t, ServerGroupCoordinators.IsArangod()) assert.True(t, ServerGroupCoordinators.IsArangod())
assert.False(t, ServerGroupSyncMasters.IsArangod()) assert.False(t, ServerGroupSyncMasters.IsArangod())
assert.False(t, ServerGroupSyncWorkers.IsArangod()) assert.False(t, ServerGroupSyncWorkers.IsArangod())
assert.False(t, ServerGroupGateways.IsArangod())
} }
func TestServerGroupIsArangosync(t *testing.T) { func TestServerGroupIsArangosync(t *testing.T) {
@ -60,6 +63,7 @@ func TestServerGroupIsArangosync(t *testing.T) {
assert.False(t, ServerGroupCoordinators.IsArangosync()) assert.False(t, ServerGroupCoordinators.IsArangosync())
assert.True(t, ServerGroupSyncMasters.IsArangosync()) assert.True(t, ServerGroupSyncMasters.IsArangosync())
assert.True(t, ServerGroupSyncWorkers.IsArangosync()) assert.True(t, ServerGroupSyncWorkers.IsArangosync())
assert.False(t, ServerGroupGateways.IsArangosync())
} }
func TestServerGroupType(t *testing.T) { func TestServerGroupType(t *testing.T) {
@ -71,4 +75,6 @@ func TestServerGroupType(t *testing.T) {
assert.Equal(t, ServerGroupTypeArangoD, ServerGroupCoordinators.Type()) assert.Equal(t, ServerGroupTypeArangoD, ServerGroupCoordinators.Type())
assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncMasters.Type()) assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncMasters.Type())
assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncWorkers.Type()) assert.Equal(t, ServerGroupTypeArangoSync, ServerGroupSyncWorkers.Type())
assert.Equal(t, ServerGroupTypeGateway, ServerGroupGateways.Type())
} }

View file

@ -27,4 +27,5 @@ const (
ServerGroupTypeArangoD ServerGroupTypeArangoD
ServerGroupTypeArangoSync ServerGroupTypeArangoSync
ServerGroupTypeID ServerGroupTypeID
ServerGroupTypeGateway
) )

View file

@ -1101,6 +1101,11 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
in.Coordinators.DeepCopyInto(&out.Coordinators) in.Coordinators.DeepCopyInto(&out.Coordinators)
in.SyncMasters.DeepCopyInto(&out.SyncMasters) in.SyncMasters.DeepCopyInto(&out.SyncMasters)
in.SyncWorkers.DeepCopyInto(&out.SyncWorkers) in.SyncWorkers.DeepCopyInto(&out.SyncWorkers)
if in.Gateways != nil {
in, out := &in.Gateways, &out.Gateways
*out = new(ServerGroupSpec)
(*in).DeepCopyInto(*out)
}
if in.MemberPropagationMode != nil { if in.MemberPropagationMode != nil {
in, out := &in.MemberPropagationMode, &out.MemberPropagationMode in, out := &in.MemberPropagationMode, &out.MemberPropagationMode
*out = new(DeploymentMemberPropagationMode) *out = new(DeploymentMemberPropagationMode)
@ -1148,6 +1153,11 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
*out = new(string) *out = new(string)
**out = **in **out = **in
} }
if in.Gateway != nil {
in, out := &in.Gateway, &out.Gateway
*out = new(DeploymentSpecGateway)
(*in).DeepCopyInto(*out)
}
return return
} }
@ -1161,6 +1171,27 @@ func (in *DeploymentSpec) DeepCopy() *DeploymentSpec {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeploymentSpecGateway) DeepCopyInto(out *DeploymentSpecGateway) {
*out = *in
if in.Enabled != nil {
in, out := &in.Enabled, &out.Enabled
*out = new(bool)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentSpecGateway.
func (in *DeploymentSpecGateway) DeepCopy() *DeploymentSpecGateway {
if in == nil {
return nil
}
out := new(DeploymentSpecGateway)
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 *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
*out = *in *out = *in
@ -1291,6 +1322,11 @@ func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) {
*out = new(ServerGroupStatus) *out = new(ServerGroupStatus)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.Gateways != nil {
in, out := &in.Gateways, &out.Gateways
*out = new(ServerGroupStatus)
(*in).DeepCopyInto(*out)
}
return return
} }
@ -1521,6 +1557,13 @@ func (in *DeploymentStatusMembers) DeepCopyInto(out *DeploymentStatusMembers) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.Gateways != nil {
in, out := &in.Gateways, &out.Gateways
*out = make(MemberStatusList, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return return
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
//
// DISCLAIMER
//
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package features
func init() {
registerFeature(gateway)
}
var gateway = &feature{
name: "gateway",
description: "Defines if gateway extension is enabled",
enterpriseRequired: false,
enabledByDefault: false,
hidden: true,
}
func Gateway() Feature {
return gateway
}

View file

@ -81,6 +81,11 @@ type ArangoSyncIdentity struct {
interfaces.ContainerCreator interfaces.ContainerCreator
} }
// GatewayIdentity helps to resolve the Gateway identity, e.g.: image ID, version of the entrypoint.
type GatewayIdentity struct {
interfaces.ContainerCreator
}
type imagesBuilder struct { type imagesBuilder struct {
Log logging.Logger Log logging.Logger
Context resources.Context Context resources.Context

View file

@ -210,6 +210,16 @@ func (d *Deployment) renderMember(spec api.DeploymentSpec, status *api.Deploymen
Image: apiObject.Status.CurrentImage, Image: apiObject.Status.CurrentImage,
Architecture: &arch, Architecture: &arch,
}, nil }, nil
case api.ServerGroupGateways:
d.log.Str("id", id).Debug("Adding gateway")
return &api.MemberStatus{
ID: id,
UID: uuid.NewUUID(),
CreatedAt: meta.Now(),
Phase: api.MemberPhaseNone,
Image: apiObject.Status.CurrentImage,
Architecture: &arch,
}, nil
default: default:
return nil, errors.WithStack(errors.Errorf("Unknown server group %d", group)) return nil, errors.WithStack(errors.Errorf("Unknown server group %d", group))
} }

View file

@ -43,7 +43,7 @@ func GenerateMemberEndpoint(services service.Inspector, apiObject meta.Object, s
func GenerateMemberEndpointFromService(svc *core.Service, apiObject meta.Object, spec api.DeploymentSpec, group api.ServerGroup, member api.MemberStatus) (string, error) { func GenerateMemberEndpointFromService(svc *core.Service, apiObject meta.Object, spec api.DeploymentSpec, group api.ServerGroup, member api.MemberStatus) (string, error) {
switch group.Type() { switch group.Type() {
case api.ServerGroupTypeArangoD: case api.ServerGroupTypeArangoD, api.ServerGroupTypeGateway:
switch method := spec.CommunicationMethod.Get(); method { switch method := spec.CommunicationMethod.Get(); method {
case api.DeploymentCommunicationMethodDNS, api.DeploymentCommunicationMethodHeadlessDNS: case api.DeploymentCommunicationMethodDNS, api.DeploymentCommunicationMethodHeadlessDNS:
return k8sutil.CreateServiceDNSNameWithDomain(svc, spec.ClusterDomain), nil return k8sutil.CreateServiceDNSNameWithDomain(svc, spec.ClusterDomain), nil

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ import (
"time" "time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
) )
func (r *Reconciler) createRebalancerV2GeneratePlan(spec api.DeploymentSpec, status api.DeploymentStatus) api.Plan { func (r *Reconciler) createRebalancerV2GeneratePlan(spec api.DeploymentSpec, status api.DeploymentStatus) api.Plan {
@ -44,7 +45,7 @@ func (r *Reconciler) createRebalancerV2GeneratePlan(spec api.DeploymentSpec, sta
r.metrics.Rebalancer.SetEnabled(true) r.metrics.Rebalancer.SetEnabled(true)
if !status.Members.AllMembersReady(spec.Mode.Get(), spec.Sync.IsEnabled()) { if !status.Members.AllMembersReady(spec.Mode.Get(), spec.Sync.IsEnabled(), features.Gateway().Enabled() && spec.IsGatewayEnabled()) {
return nil return nil
} }

View file

@ -49,6 +49,7 @@ var (
api.ServerGroupDBServers, api.ServerGroupDBServers,
api.ServerGroupSyncMasters, api.ServerGroupSyncMasters,
api.ServerGroupSyncWorkers, api.ServerGroupSyncWorkers,
api.ServerGroupGateways,
} }
) )

View file

@ -25,6 +25,7 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/actions" "github.com/arangodb/kube-arangodb/pkg/deployment/actions"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
sharedReconcile "github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared" sharedReconcile "github.com/arangodb/kube-arangodb/pkg/deployment/reconcile/shared"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
) )
@ -69,6 +70,11 @@ func (r *Reconciler) createScaleMemberPlan(ctx context.Context, apiObject k8suti
plan = append(plan, r.createScalePlan(status, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, 0, context)...) plan = append(plan, r.createScalePlan(status, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, 0, context)...)
} }
} }
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, spec.Gateways.GetCount(), context)...)
} else {
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, 0, context)...)
}
return plan return plan
} }

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -132,6 +132,9 @@ func (r *Resilience) isMemberFailureAcceptable(group api.ServerGroup, m api.Memb
case api.ServerGroupSyncMasters, api.ServerGroupSyncWorkers: case api.ServerGroupSyncMasters, api.ServerGroupSyncWorkers:
// Sync masters & workers can be replaced at will // Sync masters & workers can be replaced at will
return true, "" return true, ""
case api.ServerGroupGateways:
// Gateways can be replaced at will
return true, ""
case api.ServerGroupSingle: case api.ServerGroupSingle:
return false, "ServerGroupSingle can not marked as a failed" return false, "ServerGroupSingle can not marked as a failed"
default: default:

View file

@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/util/errors" "github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/globals" "github.com/arangodb/kube-arangodb/pkg/util/globals"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
@ -72,6 +73,12 @@ func (r *Resources) EnsurePDBs(ctx context.Context) error {
currSyncWorker = status.Members.SyncWorkers.MembersReady() currSyncWorker = status.Members.SyncWorkers.MembersReady()
} }
minGateways, currGateways := 0, 0
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
minGateways = spec.GetServerGroupSpec(api.ServerGroupGateways).New().GetCount() - 1
currGateways = status.Members.Gateways.MembersReady()
}
// Ensure all PDBs as calculated // Ensure all PDBs as calculated
if err := r.ensurePDBForGroup(ctx, api.ServerGroupAgents, minAgents, currAgents); err != nil { if err := r.ensurePDBForGroup(ctx, api.ServerGroupAgents, minAgents, currAgents); err != nil {
return err return err
@ -88,6 +95,9 @@ func (r *Resources) EnsurePDBs(ctx context.Context) error {
if err := r.ensurePDBForGroup(ctx, api.ServerGroupSyncWorkers, minSyncWorker, currSyncWorker); err != nil { if err := r.ensurePDBForGroup(ctx, api.ServerGroupSyncWorkers, minSyncWorker, currSyncWorker); err != nil {
return err return err
} }
if err := r.ensurePDBForGroup(ctx, api.ServerGroupGateways, minGateways, currGateways); err != nil {
return err
}
} }
return nil return nil

View file

@ -286,6 +286,15 @@ func createArangoSyncArgs(apiObject meta.Object, spec api.DeploymentSpec, group
return args return args
} }
func createArangoGatewayArgs(groupSpec api.ServerGroupSpec) []string {
args := []string{"--config-path", GatewayConfigFilePath}
if len(groupSpec.Args) > 0 {
args = append(args, groupSpec.Args...)
}
return args
}
// CreatePodTolerations creates a list of tolerations for a pod created for the given group. // CreatePodTolerations creates a list of tolerations for a pod created for the given group.
func (r *Resources) CreatePodTolerations(group api.ServerGroup, groupSpec api.ServerGroupSpec) []core.Toleration { func (r *Resources) CreatePodTolerations(group api.ServerGroup, groupSpec api.ServerGroupSpec) []core.Toleration {
return tolerations.MergeTolerationsIfNotFound(tolerations.CreatePodTolerations(r.context.GetMode(), group), groupSpec.GetTolerations()) return tolerations.MergeTolerationsIfNotFound(tolerations.CreatePodTolerations(r.context.GetMode(), group), groupSpec.GetTolerations())
@ -379,6 +388,21 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec
memberStatus: m, memberStatus: m,
cachedStatus: cache, cachedStatus: cache,
} }
case api.ServerGroupTypeGateway:
imageInfo.Image = r.context.GetOperatorImage()
podCreator = &MemberGatewayPod{
podName: podName,
groupSpec: groupSpec,
spec: spec,
group: group,
resources: r,
imageInfo: imageInfo,
arangoMember: *member,
apiObject: apiObject,
memberStatus: m,
cachedStatus: cache,
}
default: default:
return nil, assertion.InvalidGroupKey.Assert(true, "Unable to render pod for an unknown group: %s", group.AsRole()) return nil, assertion.InvalidGroupKey.Assert(true, "Unable to render pod for an unknown group: %s", group.AsRole())
} }
@ -548,6 +572,27 @@ func (r *Resources) createPodForMember(ctx context.Context, cachedStatus inspect
m.Pod.Propagate(&m) m.Pod.Propagate(&m)
log.Str("pod-name", pod.Name).Debug("Created pod") log.Str("pod-name", pod.Name).Debug("Created pod")
case api.ServerGroupTypeGateway:
ctxChild, cancel := globals.GetGlobalTimeouts().Kubernetes().WithTimeout(ctx)
defer cancel()
podName, uid, err := CreateArangoPod(ctxChild, cachedStatus.PodsModInterface().V1(), apiObject, spec, group, CreatePodFromTemplate(template.PodSpec))
if err != nil {
if uErr := r.context.WithMemberStatusUpdateErr(ctx, m.ID, group, updateMemberPhase(api.MemberPhaseCreationFailed)); uErr != nil {
return errors.WithStack(uErr)
}
return errors.WithStack(err)
}
var pod api.MemberPodStatus
pod.Name = podName
pod.UID = uid
pod.SpecVersion = template.PodSpecChecksum
m.Pod = &pod
m.Pod.Propagate(&m)
log.Str("pod-name", pod.Name).Debug("Created Gateway pod")
default: default:
return assertion.InvalidGroupKey.Assert(true, "Unable to create pod for an unknown group: %s", group.AsRole()) return assertion.InvalidGroupKey.Assert(true, "Unable to create pod for an unknown group: %s", group.AsRole())
} }

View file

@ -0,0 +1,346 @@
//
// DISCLAIMER
//
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package resources
import (
"context"
"fmt"
"math"
core "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
"github.com/arangodb/kube-arangodb/pkg/util/collection"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
kresources "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/resources"
)
const (
ArangoGatewayExecutor string = "/usr/local/bin/envoy"
GatewayVolumeMountDir = "/etc/gateway/"
GatewayVolumeName = "gateway"
GatewayConfigFileName = "gateway.yaml"
GatewayConfigFilePath = GatewayVolumeMountDir + GatewayConfigFileName
)
type ArangoGatewayContainer struct {
groupSpec api.ServerGroupSpec
spec api.DeploymentSpec
group api.ServerGroup
resources *Resources
imageInfo api.ImageInfo
apiObject meta.Object
memberStatus api.MemberStatus
arangoMember api.ArangoMember
}
var _ interfaces.PodCreator = &MemberGatewayPod{}
var _ interfaces.ContainerCreator = &ArangoGatewayContainer{}
type MemberGatewayPod struct {
podName string
groupSpec api.ServerGroupSpec
spec api.DeploymentSpec
group api.ServerGroup
arangoMember api.ArangoMember
resources *Resources
imageInfo api.ImageInfo
apiObject meta.Object
memberStatus api.MemberStatus
cachedStatus interfaces.Inspector
}
func GetGatewayConfigMapName(name string) string {
return fmt.Sprintf("%s-gateway", name)
}
func (a *ArangoGatewayContainer) GetCommand() ([]string, error) {
cmd := make([]string, 0, 128)
cmd = append(cmd, a.GetExecutor())
cmd = append(cmd, createArangoGatewayArgs(a.groupSpec)...)
return cmd, nil
}
func (a *ArangoGatewayContainer) GetName() string {
return shared.ServerContainerName
}
func (a *ArangoGatewayContainer) GetPorts() []core.ContainerPort {
port := shared.ArangoPort
return []core.ContainerPort{
{
Name: shared.ServerContainerName,
ContainerPort: int32(port),
Protocol: core.ProtocolTCP,
},
}
}
func (a *ArangoGatewayContainer) GetExecutor() string {
return a.groupSpec.GetEntrypoint(ArangoGatewayExecutor)
}
func (a *ArangoGatewayContainer) GetSecurityContext() *core.SecurityContext {
return k8sutil.CreateSecurityContext(a.groupSpec.SecurityContext)
}
func (a *ArangoGatewayContainer) GetProbes() (*core.Probe, *core.Probe, *core.Probe, error) {
var liveness, readiness, startup *core.Probe
probeLivenessConfig, err := a.resources.getLivenessProbe(a.spec, a.group, a.imageInfo)
if err != nil {
return nil, nil, nil, err
}
probeReadinessConfig, err := a.resources.getReadinessProbe(a.spec, a.group, a.imageInfo)
if err != nil {
return nil, nil, nil, err
}
probeStartupConfig, err := a.resources.getReadinessProbe(a.spec, a.group, a.imageInfo)
if err != nil {
return nil, nil, nil, err
}
if probeLivenessConfig != nil {
liveness = probeLivenessConfig.Create()
}
if probeReadinessConfig != nil {
readiness = probeReadinessConfig.Create()
}
if probeStartupConfig != nil {
startup = probeStartupConfig.Create()
}
return liveness, readiness, startup, nil
}
func (a *ArangoGatewayContainer) GetResourceRequirements() core.ResourceRequirements {
return kresources.ExtractPodAcceptedResourceRequirement(a.arangoMember.Spec.Overrides.GetResources(&a.groupSpec))
}
func (a *ArangoGatewayContainer) GetLifecycle() (*core.Lifecycle, error) {
return k8sutil.NewLifecycleFinalizers()
}
func (a *ArangoGatewayContainer) GetImagePullPolicy() core.PullPolicy {
return a.spec.GetImagePullPolicy()
}
func (a *ArangoGatewayContainer) GetImage() string {
return a.imageInfo.Image
}
func (a *ArangoGatewayContainer) GetEnvs() ([]core.EnvVar, []core.EnvFromSource) {
envs := NewEnvBuilder()
envs.Add(true, k8sutil.GetLifecycleEnv()...)
if len(a.groupSpec.Envs) > 0 {
for _, env := range a.groupSpec.Envs {
// Do not override preset envs
envs.Add(false, core.EnvVar{
Name: env.Name,
Value: env.Value,
})
}
}
return envs.GetEnvList(), nil
}
func (a *ArangoGatewayContainer) GetVolumeMounts() []core.VolumeMount {
return createGatewayVolumes(a.apiObject.GetName()).VolumeMounts()
}
func (m *MemberGatewayPod) GetName() string {
return m.resources.context.GetAPIObject().GetName()
}
func (m *MemberGatewayPod) GetRole() string {
return m.group.AsRole()
}
func (m *MemberGatewayPod) GetImagePullSecrets() []string {
return m.spec.ImagePullSecrets
}
func (m *MemberGatewayPod) GetPodAntiAffinity() *core.PodAntiAffinity {
a := &core.PodAntiAffinity{}
pod.AppendPodAntiAffinityDefault(m, a)
a = kresources.MergePodAntiAffinity(a, m.groupSpec.AntiAffinity)
return kresources.OptionalPodAntiAffinity(a)
}
func (m *MemberGatewayPod) GetPodAffinity() *core.PodAffinity {
a := &core.PodAffinity{}
pod.AppendAffinityWithRole(m, a, api.ServerGroupDBServers.AsRole())
a = kresources.MergePodAffinity(a, m.groupSpec.Affinity)
return kresources.OptionalPodAffinity(a)
}
func (m *MemberGatewayPod) GetNodeAffinity() *core.NodeAffinity {
a := &core.NodeAffinity{}
pod.AppendArchSelector(a, m.memberStatus.Architecture.Default(m.spec.Architecture.GetDefault()).AsNodeSelectorRequirement())
a = kresources.MergeNodeAffinity(a, m.groupSpec.NodeAffinity)
return kresources.OptionalNodeAffinity(a)
}
func (m *MemberGatewayPod) GetNodeSelector() map[string]string {
return m.groupSpec.GetNodeSelector()
}
func (m *MemberGatewayPod) GetServiceAccountName() string {
return m.groupSpec.GetServiceAccountName()
}
func (m *MemberGatewayPod) GetSidecars(pod *core.Pod) error {
// A sidecar provided by the user
sidecars := m.groupSpec.GetSidecars()
if len(sidecars) > 0 {
addLifecycleSidecar(m.groupSpec.SidecarCoreNames, sidecars)
pod.Spec.Containers = append(pod.Spec.Containers, sidecars...)
}
return nil
}
func (m *MemberGatewayPod) GetVolumes() []core.Volume {
return createGatewayVolumes(m.apiObject.GetName()).Volumes()
}
func (m *MemberGatewayPod) IsDeploymentMode() bool {
return m.spec.IsDevelopment()
}
func (m *MemberGatewayPod) GetInitContainers(cachedStatus interfaces.Inspector) ([]core.Container, error) {
var initContainers []core.Container
if c := m.groupSpec.InitContainers.GetContainers(); len(c) > 0 {
initContainers = append(initContainers, c...)
}
res := kresources.ExtractPodInitContainerAcceptedResourceRequirement(m.GetContainerCreator().GetResourceRequirements())
initContainers = applyInitContainersResourceResources(initContainers, res)
initContainers = upscaleInitContainersResourceResources(initContainers, res)
return initContainers, nil
}
func (m *MemberGatewayPod) GetFinalizers() []string {
return nil
}
func (m *MemberGatewayPod) GetTolerations() []core.Toleration {
return m.resources.CreatePodTolerations(m.group, m.groupSpec)
}
func (m *MemberGatewayPod) GetContainerCreator() interfaces.ContainerCreator {
return &ArangoGatewayContainer{
groupSpec: m.groupSpec,
spec: m.spec,
group: m.group,
resources: m.resources,
imageInfo: m.imageInfo,
apiObject: m.apiObject,
memberStatus: m.memberStatus,
arangoMember: m.arangoMember,
}
}
func (m *MemberGatewayPod) GetRestartPolicy() core.RestartPolicy {
if features.RestartPolicyAlways().Enabled() {
return core.RestartPolicyAlways
}
return core.RestartPolicyNever
}
func (m *MemberGatewayPod) Init(ctx context.Context, cachedStatus interfaces.Inspector, pod *core.Pod) error {
terminationGracePeriodSeconds := int64(math.Ceil(m.groupSpec.GetTerminationGracePeriod(m.group).Seconds()))
pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
pod.Spec.PriorityClassName = m.groupSpec.PriorityClassName
return nil
}
func (m *MemberGatewayPod) Validate(_ interfaces.Inspector) error {
if err := validateSidecars(m.groupSpec.SidecarCoreNames, m.groupSpec.GetSidecars()); err != nil {
return err
}
return nil
}
func (m *MemberGatewayPod) ApplyPodSpec(spec *core.PodSpec) error {
if s := m.groupSpec.SchedulerName; s != nil {
spec.SchedulerName = *s
}
m.groupSpec.PodModes.Apply(spec)
return nil
}
func (m *MemberGatewayPod) Annotations() map[string]string {
return collection.MergeAnnotations(m.spec.Annotations, m.groupSpec.Annotations)
}
func (m *MemberGatewayPod) Labels() map[string]string {
return collection.ReservedLabels().Filter(collection.MergeAnnotations(m.spec.Labels, m.groupSpec.Labels))
}
func createGatewayVolumes(memberName string) pod.Volumes {
volumes := pod.NewVolumes()
volumes.AddVolume(k8sutil.LifecycleVolume())
volumes.AddVolumeMount(k8sutil.LifecycleVolumeMount())
volumes.AddVolume(k8sutil.CreateVolumeWithConfigMap(GatewayVolumeName, GetGatewayConfigMapName(memberName)))
volumes.AddVolumeMount(GatewayVolumeMount())
return volumes
}
func GatewayVolumeMount() core.VolumeMount {
return core.VolumeMount{
Name: GatewayVolumeName,
MountPath: GatewayVolumeMountDir,
ReadOnly: true,
}
}

View file

@ -33,6 +33,7 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared" shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
"github.com/arangodb/kube-arangodb/pkg/deployment/agency/state" "github.com/arangodb/kube-arangodb/pkg/deployment/agency/state"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/patch" "github.com/arangodb/kube-arangodb/pkg/deployment/patch"
"github.com/arangodb/kube-arangodb/pkg/metrics" "github.com/arangodb/kube-arangodb/pkg/metrics"
"github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util"
@ -530,7 +531,7 @@ func (r *Resources) InspectPods(ctx context.Context, cachedStatus inspectorInter
} }
spec := r.context.GetSpec() spec := r.context.GetSpec()
allMembersReady := status.Members.AllMembersReady(spec.GetMode(), r.context.IsSyncEnabled()) allMembersReady := status.Members.AllMembersReady(spec.GetMode(), r.context.IsSyncEnabled(), features.Gateway().Enabled() && spec.IsGatewayEnabled())
status.Conditions.Update(api.ConditionTypeReady, allMembersReady, "", "") status.Conditions.Update(api.ConditionTypeReady, allMembersReady, "", "")
// Update conditions // Update conditions

View file

@ -82,6 +82,10 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
defer metrics.SetDuration(inspectServicesDurationGauges.WithLabelValues(deploymentName), start) defer metrics.SetDuration(inspectServicesDurationGauges.WithLabelValues(deploymentName), start)
counterMetric := inspectedServicesCounters.WithLabelValues(deploymentName) counterMetric := inspectedServicesCounters.WithLabelValues(deploymentName)
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
role = api.ServerGroupGateways.AsRole()
}
// Fetch existing services // Fetch existing services
svcs := cachedStatus.ServicesModInterface().V1() svcs := cachedStatus.ServicesModInterface().V1()
amInspector := cachedStatus.ArangoMember().V1() amInspector := cachedStatus.ArangoMember().V1()

View file

@ -641,6 +641,18 @@ func CreateVolumeWithSecret(name, secretName string) core.Volume {
}, },
} }
} }
func CreateVolumeWithConfigMap(name, configMapName string) core.Volume {
return core.Volume{
Name: name,
VolumeSource: core.VolumeSource{
ConfigMap: &core.ConfigMapVolumeSource{
LocalObjectReference: core.LocalObjectReference{
Name: configMapName,
},
},
},
}
}
func CreateVolumeWithPersitantVolumeClaim(name, claimName string) core.Volume { func CreateVolumeWithPersitantVolumeClaim(name, claimName string) core.Volume {
return core.Volume{ return core.Volume{

View file

@ -1,7 +1,7 @@
// //
// DISCLAIMER // DISCLAIMER
// //
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany // Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -134,6 +134,9 @@ func CreatePodTolerations(mode api.DeploymentMode, group api.ServerGroup) []core
case api.ServerGroupSyncWorkers: case api.ServerGroupSyncWorkers:
notReadyDur.TimeSpan = 1 * time.Minute notReadyDur.TimeSpan = 1 * time.Minute
unreachableDur.TimeSpan = 1 * time.Minute unreachableDur.TimeSpan = 1 * time.Minute
case api.ServerGroupGateways:
notReadyDur.TimeSpan = 15 * time.Second
unreachableDur.TimeSpan = 15 * time.Second
} }
return []core.Toleration{NewNoExecuteToleration(TolerationKeyNodeNotReady, notReadyDur), return []core.Toleration{NewNoExecuteToleration(TolerationKeyNodeNotReady, notReadyDur),
NewNoExecuteToleration(TolerationKeyNodeUnreachable, unreachableDur), NewNoExecuteToleration(TolerationKeyNodeUnreachable, unreachableDur),

View file

@ -44,7 +44,7 @@ const (
LabelKeyArangoScheduled = "deployment.arangodb.com/scheduled" LabelKeyArangoScheduled = "deployment.arangodb.com/scheduled"
// LabelKeyArangoTopology is the key of the label used to store the ArangoDeployment topology ID in // LabelKeyArangoTopology is the key of the label used to store the ArangoDeployment topology ID in
LabelKeyArangoTopology = "deployment.arangodb.com/topology" LabelKeyArangoTopology = "deployment.arangodb.com/topology"
// LabelKeyArangoLeader is the key of the label used to store the current leader of a group instances. // LabelKeyArangoLeader is the key of the label used to store the current leader of a group instances. AF only.
LabelKeyArangoLeader = "deployment.arangodb.com/leader" LabelKeyArangoLeader = "deployment.arangodb.com/leader"
// LabelKeyArangoActive is the key of the label used to mark members as active. // LabelKeyArangoActive is the key of the label used to mark members as active.
LabelKeyArangoActive = "deployment.arangodb.com/active" LabelKeyArangoActive = "deployment.arangodb.com/active"