mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
[Feature] Envoy Config Update (#1711)
This commit is contained in:
parent
2a62af3d23
commit
1095567432
75 changed files with 4887 additions and 937 deletions
|
@ -19,6 +19,7 @@
|
|||
- (Feature) PongV1 Integration Service
|
||||
- (Feature) Custom Gateway image
|
||||
- (Bugfix) Fix race condition in ArangoBackup
|
||||
- (Feature) Improve Gateway Config gen
|
||||
|
||||
## [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
|
||||
|
|
8
Makefile
8
Makefile
|
@ -803,10 +803,12 @@ set-typed-api-version/%:
|
|||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
"$(ROOT)/pkg/util/" \
|
||||
"$(ROOT)/pkg/handlers/" \
|
||||
"$(ROOT)/pkg/apis/backup/" \
|
||||
"$(ROOT)/pkg/apis/networking/" \
|
||||
"$(ROOT)/pkg/upgrade/" \
|
||||
| cut -d ':' -f 1 | sort | uniq \
|
||||
| xargs -n 1 $(SED) -i "s#github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/$*/v[A-Za-z0-9]\+#github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/$*/v$(API_VERSION)#g"
|
||||
|
@ -817,10 +819,12 @@ set-api-version/%:
|
|||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
"$(ROOT)/pkg/util/" \
|
||||
"$(ROOT)/pkg/handlers/" \
|
||||
"$(ROOT)/pkg/apis/backup/" \
|
||||
"$(ROOT)/pkg/apis/networking/" \
|
||||
"$(ROOT)/pkg/upgrade/" \
|
||||
| cut -d ':' -f 1 | sort | uniq \
|
||||
| xargs -n 1 $(SED) -i "s#github.com/arangodb/kube-arangodb/pkg/apis/$*/v[A-Za-z0-9]\+#github.com/arangodb/kube-arangodb/pkg/apis/$*/v$(API_VERSION)#g"
|
||||
|
@ -828,10 +832,12 @@ set-api-version/%:
|
|||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
"$(ROOT)/pkg/util/" \
|
||||
"$(ROOT)/pkg/handlers/" \
|
||||
"$(ROOT)/pkg/apis/backup/" \
|
||||
"$(ROOT)/pkg/apis/networking/" \
|
||||
"$(ROOT)/pkg/upgrade/" \
|
||||
| cut -d ':' -f 1 | sort | uniq \
|
||||
| xargs -n 1 $(SED) -i "s#DatabaseV[A-Za-z0-9]\+()\.#DatabaseV$(API_VERSION)().#g"
|
||||
|
@ -839,10 +845,12 @@ set-api-version/%:
|
|||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
"$(ROOT)/pkg/util/" \
|
||||
"$(ROOT)/pkg/handlers" \
|
||||
"$(ROOT)/pkg/apis/backup/" \
|
||||
"$(ROOT)/pkg/apis/networking/" \
|
||||
"$(ROOT)/pkg/upgrade/" \
|
||||
| cut -d ':' -f 1 | sort | uniq \
|
||||
| xargs -n 1 $(SED) -i "s#ReplicationV[A-Za-z0-9]\+()\.#ReplicationV$(API_VERSION)().#g"
|
||||
|
|
|
@ -13,19 +13,61 @@ metadata:
|
|||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
- "arangolocalstorages.storage.arangodb.com"
|
||||
# analytics.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "graphanalyticsengines.analytics.arangodb.com"
|
||||
# apps.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
# backup.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
# database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
# ml.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangomlbatchjobs.ml.arangodb.com"
|
||||
- "arangomlcronjobs.ml.arangodb.com"
|
||||
- "arangomlextensions.ml.arangodb.com"
|
||||
- "arangomlstorages.ml.arangodb.com"
|
||||
# networking.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoroutes.networking.arangodb.com"
|
||||
# replication.database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
# scheduler.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoprofiles.scheduler.arangodb.com"
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -13,19 +13,61 @@ metadata:
|
|||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
- "arangolocalstorages.storage.arangodb.com"
|
||||
# analytics.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "graphanalyticsengines.analytics.arangodb.com"
|
||||
# apps.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
# backup.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
# database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
# ml.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangomlbatchjobs.ml.arangodb.com"
|
||||
- "arangomlcronjobs.ml.arangodb.com"
|
||||
- "arangomlextensions.ml.arangodb.com"
|
||||
- "arangomlstorages.ml.arangodb.com"
|
||||
# networking.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoroutes.networking.arangodb.com"
|
||||
# replication.database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
# scheduler.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoprofiles.scheduler.arangodb.com"
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -13,19 +13,61 @@ metadata:
|
|||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
- "arangolocalstorages.storage.arangodb.com"
|
||||
# analytics.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "graphanalyticsengines.analytics.arangodb.com"
|
||||
# apps.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
# backup.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
# database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
# ml.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangomlbatchjobs.ml.arangodb.com"
|
||||
- "arangomlcronjobs.ml.arangodb.com"
|
||||
- "arangomlextensions.ml.arangodb.com"
|
||||
- "arangomlstorages.ml.arangodb.com"
|
||||
# networking.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoroutes.networking.arangodb.com"
|
||||
# replication.database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
# scheduler.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoprofiles.scheduler.arangodb.com"
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -13,19 +13,61 @@ metadata:
|
|||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
- "arangolocalstorages.storage.arangodb.com"
|
||||
# analytics.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "graphanalyticsengines.analytics.arangodb.com"
|
||||
# apps.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangojobs.apps.arangodb.com"
|
||||
# backup.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangobackuppolicies.backup.arangodb.com"
|
||||
- "arangobackups.backup.arangodb.com"
|
||||
# database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoclustersynchronizations.database.arangodb.com"
|
||||
- "arangodeployments.database.arangodb.com"
|
||||
- "arangomembers.database.arangodb.com"
|
||||
- "arangotasks.database.arangodb.com"
|
||||
# ml.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangomlbatchjobs.ml.arangodb.com"
|
||||
- "arangomlcronjobs.ml.arangodb.com"
|
||||
- "arangomlextensions.ml.arangodb.com"
|
||||
- "arangomlstorages.ml.arangodb.com"
|
||||
# networking.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoroutes.networking.arangodb.com"
|
||||
# replication.database.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangodeploymentreplications.replication.database.arangodb.com"
|
||||
# scheduler.arangodb.com
|
||||
- apiGroups: ["apiextensions.k8s.io"]
|
||||
resources: ["customresourcedefinitions"]
|
||||
verbs: ["get", "list", "watch", "update", "delete"]
|
||||
resourceNames:
|
||||
- "arangoprofiles.scheduler.arangodb.com"
|
||||
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
@ -3045,7 +3045,7 @@ Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.
|
|||
|
||||
### .spec.gateway.enabled
|
||||
|
||||
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec_gateway.go#L29)</sup>
|
||||
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec_gateway.go#L33)</sup>
|
||||
|
||||
Enabled setting enables/disables support for gateway in the cluster.
|
||||
When enabled, the cluster will contain a number of `gateway` servers.
|
||||
|
@ -3056,13 +3056,205 @@ Default Value: `false`
|
|||
|
||||
### .spec.gateway.image
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec_gateway.go#L33)</sup>
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/deployment/v1/deployment_spec_gateway.go#L37)</sup>
|
||||
|
||||
Image is the image to use for the gateway.
|
||||
By default, the image is determined by the operator.
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.args
|
||||
|
||||
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/core.go#L50)</sup>
|
||||
|
||||
Arguments to the entrypoint.
|
||||
The container image's CMD is used if this is not provided.
|
||||
Variable references $(VAR_NAME) are expanded using the container's environment. If a variable
|
||||
cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced
|
||||
to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will
|
||||
produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless
|
||||
of whether the variable exists or not. Cannot be updated.
|
||||
|
||||
Links:
|
||||
* [Kubernetes Docs](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.command
|
||||
|
||||
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/core.go#L40)</sup>
|
||||
|
||||
Entrypoint array. Not executed within a shell.
|
||||
The container image's ENTRYPOINT is used if this is not provided.
|
||||
Variable references $(VAR_NAME) are expanded using the container's environment. If a variable
|
||||
cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced
|
||||
to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will
|
||||
produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless
|
||||
of whether the variable exists or not. Cannot be updated.
|
||||
|
||||
Links:
|
||||
* [Kubernetes Docs](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.controllerListenPort
|
||||
|
||||
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/integration.go#L36)</sup>
|
||||
|
||||
ControllerListenPort defines on which port the sidecar container will be listening for controller requests
|
||||
|
||||
Default Value: `9202`
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.env
|
||||
|
||||
Type: `core.EnvVar` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/environments.go#L36)</sup>
|
||||
|
||||
Env keeps the information about environment variables provided to the container
|
||||
|
||||
Links:
|
||||
* [Kubernetes Docs](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#envvar-v1-core)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.envFrom
|
||||
|
||||
Type: `core.EnvFromSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/environments.go#L41)</sup>
|
||||
|
||||
EnvFrom keeps the information about environment variable sources provided to the container
|
||||
|
||||
Links:
|
||||
* [Kubernetes Docs](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#envfromsource-v1-core)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.image
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/image.go#L35)</sup>
|
||||
|
||||
Image define image details
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.imagePullPolicy
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/image.go#L39)</sup>
|
||||
|
||||
ImagePullPolicy define Image pull policy
|
||||
|
||||
Default Value: `IfNotPresent`
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.lifecycle
|
||||
|
||||
Type: `core.Lifecycle` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/lifecycle.go#L35)</sup>
|
||||
|
||||
Lifecycle keeps actions that the management system should take in response to container lifecycle events.
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.listenPort
|
||||
|
||||
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/integration.go#L32)</sup>
|
||||
|
||||
ListenPort defines on which port the sidecar container will be listening for connections
|
||||
|
||||
Default Value: `9201`
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.livenessProbe
|
||||
|
||||
Type: `core.Probe` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/probes.go#L37)</sup>
|
||||
|
||||
LivenessProbe keeps configuration of periodic probe of container liveness.
|
||||
Container will be restarted if the probe fails.
|
||||
|
||||
Links:
|
||||
* [Kubernetes docs](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.ports
|
||||
|
||||
Type: `[]core.ContainerPort` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/networking.go#L39)</sup>
|
||||
|
||||
Ports contains list of ports to expose from the container. Not specifying a port here
|
||||
DOES NOT prevent that port from being exposed. Any port which is
|
||||
listening on the default "0.0.0.0" address inside a container will be
|
||||
accessible from the network.
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.readinessProbe
|
||||
|
||||
Type: `core.Probe` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/probes.go#L42)</sup>
|
||||
|
||||
ReadinessProbe keeps configuration of periodic probe of container service readiness.
|
||||
Container will be removed from service endpoints if the probe fails.
|
||||
|
||||
Links:
|
||||
* [Kubernetes docs](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.resources
|
||||
|
||||
Type: `core.ResourceRequirements` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/resources.go#L37)</sup>
|
||||
|
||||
Resources holds resource requests & limits for container
|
||||
|
||||
Links:
|
||||
* [Documentation of core.ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#resourcerequirements-v1-core)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.securityContext
|
||||
|
||||
Type: `core.SecurityContext` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/security.go#L35)</sup>
|
||||
|
||||
SecurityContext holds container-level security attributes and common container settings.
|
||||
|
||||
Links:
|
||||
* [Kubernetes docs](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.startupProbe
|
||||
|
||||
Type: `core.Probe` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/probes.go#L50)</sup>
|
||||
|
||||
StartupProbe indicates that the Pod has successfully initialized.
|
||||
If specified, no other probes are executed until this completes successfully.
|
||||
If this probe fails, the Pod will be restarted, just as if the livenessProbe failed.
|
||||
This can be used to provide different probe parameters at the beginning of a Pod's lifecycle,
|
||||
when it might take a long time to load data or warm a cache, than during steady-state operation.
|
||||
|
||||
Links:
|
||||
* [Kubernetes docs](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes)
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.volumeMounts
|
||||
|
||||
Type: `[]core.VolumeMount` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/volume_mounts.go#L35)</sup>
|
||||
|
||||
VolumeMounts keeps list of pod volumes to mount into the container's filesystem.
|
||||
|
||||
***
|
||||
|
||||
### .spec.gateway.sidecar.workingDir
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/scheduler/v1beta1/container/resources/core.go#L55)</sup>
|
||||
|
||||
Container's working directory.
|
||||
If not specified, the container runtime's default will be used, which
|
||||
might be configured in the container image.
|
||||
|
||||
***
|
||||
|
||||
### .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#L156)</sup>
|
||||
|
|
|
@ -12,7 +12,15 @@ title: ArangoRoute V1Alpha1
|
|||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_spec.go#L27)</sup>
|
||||
|
||||
DeploymentName specifies the ArangoDeployment object name
|
||||
Deployment specifies the ArangoDeployment object name
|
||||
|
||||
***
|
||||
|
||||
### .spec.destination.path
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_spec_destination.go#L36)</sup>
|
||||
|
||||
Path defines service path used for overrides
|
||||
|
||||
***
|
||||
|
||||
|
@ -123,15 +131,29 @@ UID keeps the information about object UID
|
|||
|
||||
***
|
||||
|
||||
### .status.targets\[int\].tls.insecure
|
||||
### .status.target.destinations\[int\].host
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target_destination.go#L38)</sup>
|
||||
|
||||
***
|
||||
|
||||
### .status.target.destinations\[int\].port
|
||||
|
||||
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target_destination.go#L39)</sup>
|
||||
|
||||
***
|
||||
|
||||
### .status.target.path
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target.go#L37)</sup>
|
||||
|
||||
Path specifies request path override
|
||||
|
||||
***
|
||||
|
||||
### .status.target.TLS.insecure
|
||||
|
||||
Type: `boolean` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target_tls.go#L27)</sup>
|
||||
|
||||
Insecure allows Insecure traffic
|
||||
|
||||
***
|
||||
|
||||
### .status.targets\[int\].url
|
||||
|
||||
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target.go#L34)</sup>
|
||||
|
||||
|
|
28
examples/single-server-route.yaml
Normal file
28
examples/single-server-route.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
apiVersion: "database.arangodb.com/v1"
|
||||
kind: "ArangoDeployment"
|
||||
metadata:
|
||||
name: "example-simple-single"
|
||||
spec:
|
||||
mode: Single
|
||||
image: 'arangodb/arangodb:3.12.2'
|
||||
gateway:
|
||||
enabled: true
|
||||
gateways:
|
||||
count: 1
|
||||
---
|
||||
apiVersion: "networking.arangodb.com/v1alpha1"
|
||||
kind: "ArangoRoute"
|
||||
metadata:
|
||||
name: "example-simple-single-route"
|
||||
spec:
|
||||
deployment: example-simple-single
|
||||
destination:
|
||||
service:
|
||||
name: example-simple-single
|
||||
port: 8529
|
||||
schema: https
|
||||
tls:
|
||||
insecure: true
|
||||
path: "/_api/"
|
||||
route:
|
||||
path: "/secondary/"
|
|
@ -175,6 +175,14 @@ func Test_GenerateAPIDocs(t *testing.T) {
|
|||
"Spec": deploymentApi.ArangoMember{}.Spec,
|
||||
},
|
||||
},
|
||||
Shared: []string{
|
||||
"shared/v1",
|
||||
"scheduler/v1beta1",
|
||||
"scheduler/v1beta1/container",
|
||||
"scheduler/v1beta1/container/resources",
|
||||
"scheduler/v1beta1/pod",
|
||||
"scheduler/v1beta1/pod/resources",
|
||||
},
|
||||
},
|
||||
},
|
||||
"apps": map[string]inputPackage{
|
||||
|
|
|
@ -20,7 +20,11 @@
|
|||
|
||||
package v1
|
||||
|
||||
import "github.com/arangodb/kube-arangodb/pkg/util"
|
||||
import (
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
type DeploymentSpecGateway struct {
|
||||
// Enabled setting enables/disables support for gateway in the cluster.
|
||||
|
@ -31,6 +35,9 @@ type DeploymentSpecGateway struct {
|
|||
// Image is the image to use for the gateway.
|
||||
// By default, the image is determined by the operator.
|
||||
Image *string `json:"image"`
|
||||
|
||||
// Sidecar define the integration sidecar spec
|
||||
Sidecar *schedulerApi.IntegrationSidecar `json:"sidecar,omitempty"`
|
||||
}
|
||||
|
||||
// IsEnabled returns whether the gateway is enabled.
|
||||
|
@ -42,9 +49,22 @@ func (d *DeploymentSpecGateway) IsEnabled() bool {
|
|||
return *d.Enabled
|
||||
}
|
||||
|
||||
func (d *DeploymentSpecGateway) GetSidecar() *schedulerApi.IntegrationSidecar {
|
||||
if d == nil || d.Sidecar == nil {
|
||||
return nil
|
||||
}
|
||||
return d.Sidecar
|
||||
}
|
||||
|
||||
// Validate the given spec
|
||||
func (d *DeploymentSpecGateway) Validate() error {
|
||||
return nil
|
||||
if d == nil {
|
||||
d = &DeploymentSpecGateway{}
|
||||
}
|
||||
|
||||
return shared.WithErrors(
|
||||
shared.PrefixResourceErrors("integrationSidecar", d.GetSidecar().Validate()),
|
||||
)
|
||||
}
|
||||
|
||||
// GetImage returns the image to use for the gateway.
|
||||
|
|
|
@ -230,6 +230,8 @@ func (g ServerGroup) DefaultTerminationGracePeriod() time.Duration {
|
|||
return time.Hour
|
||||
case ServerGroupCoordinators:
|
||||
return time.Hour
|
||||
case ServerGroupGateways:
|
||||
return 15 * time.Minute
|
||||
default:
|
||||
return time.Second * 30
|
||||
}
|
||||
|
|
6
pkg/apis/deployment/v1/zz_generated.deepcopy.go
generated
6
pkg/apis/deployment/v1/zz_generated.deepcopy.go
generated
|
@ -28,6 +28,7 @@ package v1
|
|||
import (
|
||||
time "time"
|
||||
|
||||
v1beta1 "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -1184,6 +1185,11 @@ func (in *DeploymentSpecGateway) DeepCopyInto(out *DeploymentSpecGateway) {
|
|||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.Sidecar != nil {
|
||||
in, out := &in.Sidecar, &out.Sidecar
|
||||
*out = new(v1beta1.IntegrationSidecar)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,11 @@
|
|||
|
||||
package v2alpha1
|
||||
|
||||
import "github.com/arangodb/kube-arangodb/pkg/util"
|
||||
import (
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
type DeploymentSpecGateway struct {
|
||||
// Enabled setting enables/disables support for gateway in the cluster.
|
||||
|
@ -31,6 +35,9 @@ type DeploymentSpecGateway struct {
|
|||
// Image is the image to use for the gateway.
|
||||
// By default, the image is determined by the operator.
|
||||
Image *string `json:"image"`
|
||||
|
||||
// Sidecar define the integration sidecar spec
|
||||
Sidecar *schedulerApi.IntegrationSidecar `json:"sidecar,omitempty"`
|
||||
}
|
||||
|
||||
// IsEnabled returns whether the gateway is enabled.
|
||||
|
@ -42,9 +49,22 @@ func (d *DeploymentSpecGateway) IsEnabled() bool {
|
|||
return *d.Enabled
|
||||
}
|
||||
|
||||
func (d *DeploymentSpecGateway) GetSidecar() *schedulerApi.IntegrationSidecar {
|
||||
if d == nil || d.Sidecar == nil {
|
||||
return nil
|
||||
}
|
||||
return d.Sidecar
|
||||
}
|
||||
|
||||
// Validate the given spec
|
||||
func (d *DeploymentSpecGateway) Validate() error {
|
||||
return nil
|
||||
if d == nil {
|
||||
d = &DeploymentSpecGateway{}
|
||||
}
|
||||
|
||||
return shared.WithErrors(
|
||||
shared.PrefixResourceErrors("integrationSidecar", d.GetSidecar().Validate()),
|
||||
)
|
||||
}
|
||||
|
||||
// GetImage returns the image to use for the gateway.
|
||||
|
|
|
@ -230,6 +230,8 @@ func (g ServerGroup) DefaultTerminationGracePeriod() time.Duration {
|
|||
return time.Hour
|
||||
case ServerGroupCoordinators:
|
||||
return time.Hour
|
||||
case ServerGroupGateways:
|
||||
return 15 * time.Minute
|
||||
default:
|
||||
return time.Second * 30
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ package v2alpha1
|
|||
import (
|
||||
time "time"
|
||||
|
||||
v1beta1 "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -1184,6 +1185,11 @@ func (in *DeploymentSpecGateway) DeepCopyInto(out *DeploymentSpecGateway) {
|
|||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.Sidecar != nil {
|
||||
in, out := &in.Sidecar, &out.Sidecar
|
||||
*out = new(v1beta1.IntegrationSidecar)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ package v1alpha1
|
|||
import shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
|
||||
type ArangoRouteSpec struct {
|
||||
// DeploymentName specifies the ArangoDeployment object name
|
||||
DeploymentName *string `json:"deployment,omitempty"`
|
||||
// Deployment specifies the ArangoDeployment object name
|
||||
Deployment *string `json:"deployment,omitempty"`
|
||||
|
||||
// Destination defines the route destination
|
||||
Destination *ArangoRouteSpecDestination `json:"destination,omitempty"`
|
||||
|
@ -33,6 +33,14 @@ type ArangoRouteSpec struct {
|
|||
Route *ArangoRouteSpecRoute `json:"route,omitempty"`
|
||||
}
|
||||
|
||||
func (s *ArangoRouteSpec) GetDeployment() string {
|
||||
if s == nil || s.Destination == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *s.Deployment
|
||||
}
|
||||
|
||||
func (s *ArangoRouteSpec) GetDestination() *ArangoRouteSpecDestination {
|
||||
if s == nil || s.Destination == nil {
|
||||
return nil
|
||||
|
@ -54,7 +62,7 @@ func (s *ArangoRouteSpec) Validate() error {
|
|||
}
|
||||
|
||||
if err := shared.WithErrors(shared.PrefixResourceErrors("spec",
|
||||
shared.PrefixResourceErrors("deployment", shared.ValidateResourceNamePointer(s.DeploymentName)),
|
||||
shared.PrefixResourceErrors("deployment", shared.ValidateResourceNamePointer(s.Deployment)),
|
||||
shared.ValidateRequiredInterfacePath("destination", s.Destination),
|
||||
shared.ValidateOptionalInterfacePath("route", s.Route),
|
||||
)); err != nil {
|
||||
|
|
|
@ -31,30 +31,41 @@ type ArangoRouteSpecDestination struct {
|
|||
|
||||
// TLS defines TLS Configuration
|
||||
TLS *ArangoRouteSpecDestinationTLS `json:"tls,omitempty"`
|
||||
|
||||
// Path defines service path used for overrides
|
||||
Path *string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (s *ArangoRouteSpecDestination) GetService() *ArangoRouteSpecDestinationService {
|
||||
if s == nil || s.Service == nil {
|
||||
func (a *ArangoRouteSpecDestination) GetService() *ArangoRouteSpecDestinationService {
|
||||
if a == nil || a.Service == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.Service
|
||||
return a.Service
|
||||
}
|
||||
|
||||
func (s *ArangoRouteSpecDestination) GetSchema() *ArangoRouteSpecDestinationSchema {
|
||||
if s == nil || s.Schema == nil {
|
||||
func (a *ArangoRouteSpecDestination) GetSchema() *ArangoRouteSpecDestinationSchema {
|
||||
if a == nil || a.Schema == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.Schema
|
||||
return a.Schema
|
||||
}
|
||||
|
||||
func (s *ArangoRouteSpecDestination) GetTLS() *ArangoRouteSpecDestinationTLS {
|
||||
if s == nil || s.TLS == nil {
|
||||
func (a *ArangoRouteSpecDestination) GetPath() string {
|
||||
if a == nil || a.Path == nil {
|
||||
return "/"
|
||||
}
|
||||
|
||||
return *a.Path
|
||||
}
|
||||
|
||||
func (a *ArangoRouteSpecDestination) GetTLS() *ArangoRouteSpecDestinationTLS {
|
||||
if a == nil || a.TLS == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.TLS
|
||||
return a.TLS
|
||||
}
|
||||
|
||||
func (a *ArangoRouteSpecDestination) Validate() error {
|
||||
|
@ -66,6 +77,7 @@ func (a *ArangoRouteSpecDestination) Validate() error {
|
|||
shared.ValidateOptionalInterfacePath("service", a.Service),
|
||||
shared.ValidateOptionalInterfacePath("schema", a.Schema),
|
||||
shared.ValidateOptionalInterfacePath("tls", a.TLS),
|
||||
shared.PrefixResourceError("path", shared.ValidateAPIPath(a.GetPath())),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ type ArangoRouteSpecRoute struct {
|
|||
|
||||
func (a *ArangoRouteSpecRoute) GetPath() string {
|
||||
if a == nil || a.Path == nil {
|
||||
return "/"
|
||||
return ""
|
||||
}
|
||||
|
||||
return *a.Path
|
||||
|
@ -43,7 +43,7 @@ func (a *ArangoRouteSpecRoute) Validate() error {
|
|||
}
|
||||
|
||||
if err := shared.WithErrors(
|
||||
shared.PrefixResourceError("path", shared.ValidateAPIPath(a.GetPath())),
|
||||
shared.ValidateRequiredPath("path", a.Path, shared.ValidateAPIPath),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -33,6 +33,6 @@ type ArangoRouteStatus struct {
|
|||
// Deployment keeps the ArangoDeployment reference
|
||||
Deployment *sharedApi.Object `json:"deployment,omitempty"`
|
||||
|
||||
// Targets keeps the target details
|
||||
Targets ArangoRouteStatusTargets `json:"targets,omitempty"`
|
||||
// Target keeps the target details
|
||||
Target *ArangoRouteStatusTarget `json:"target,omitempty"`
|
||||
}
|
||||
|
|
|
@ -20,25 +20,46 @@
|
|||
|
||||
package v1alpha1
|
||||
|
||||
import "github.com/arangodb/kube-arangodb/pkg/util"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
type ArangoRouteStatusTargets []ArangoRouteStatusTarget
|
||||
|
||||
func (a ArangoRouteStatusTargets) Hash() string {
|
||||
return util.SHA256FromExtract(func(t ArangoRouteStatusTarget) string {
|
||||
return t.Hash()
|
||||
}, a...)
|
||||
}
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
type ArangoRouteStatusTarget struct {
|
||||
Url string `json:"url,omitempty"`
|
||||
// Destinations keeps target destinations
|
||||
Destinations ArangoRouteStatusTargetDestinations `json:"destinations,omitempty"`
|
||||
|
||||
TLS ArangoRouteStatusTargetTLS `json:"tls,omitempty"`
|
||||
// TLS Keeps target TLS Settings (if not nil, TLS is enabled)
|
||||
TLS *ArangoRouteStatusTargetTLS `json:"TLS,omitempty"`
|
||||
|
||||
// Path specifies request path override
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (a *ArangoRouteStatusTarget) RenderURLs() []string {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var urls = make([]string, len(a.Destinations))
|
||||
|
||||
proto := "http"
|
||||
|
||||
if a.TLS != nil {
|
||||
proto = "https"
|
||||
}
|
||||
|
||||
for id, dest := range a.Destinations {
|
||||
urls[id] = fmt.Sprintf("%s://%s:%d%s", proto, dest.Host, dest.Port, a.Path)
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
func (a *ArangoRouteStatusTarget) Hash() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
return util.SHA256FromStringArray(a.Url, a.TLS.Hash())
|
||||
return util.SHA256FromStringArray(a.Destinations.Hash(), a.TLS.Hash(), a.Path)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// 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 v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
type ArangoRouteStatusTargetDestinations []ArangoRouteStatusTargetDestination
|
||||
|
||||
func (a ArangoRouteStatusTargetDestinations) Hash() string {
|
||||
return util.SHA256FromExtract(func(t ArangoRouteStatusTargetDestination) string {
|
||||
return t.Hash()
|
||||
}, a...)
|
||||
}
|
||||
|
||||
type ArangoRouteStatusTargetDestination struct {
|
||||
Host string `json:"host,omitempty"`
|
||||
Port int32 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
func (a *ArangoRouteStatusTargetDestination) Hash() string {
|
||||
if a == nil {
|
||||
return ""
|
||||
}
|
||||
return util.SHA256FromStringArray(fmt.Sprintf("%s:%d", a.Host, a.Port))
|
||||
}
|
|
@ -24,7 +24,7 @@ import "github.com/arangodb/kube-arangodb/pkg/util"
|
|||
|
||||
type ArangoRouteStatusTargetTLS struct {
|
||||
// Insecure allows Insecure traffic
|
||||
Insecure bool `json:"insecure"`
|
||||
Insecure *bool `json:"insecure"`
|
||||
}
|
||||
|
||||
func (a *ArangoRouteStatusTargetTLS) Hash() string {
|
||||
|
@ -32,5 +32,13 @@ func (a *ArangoRouteStatusTargetTLS) Hash() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
return util.SHA256FromStringArray(util.BoolSwitch(a.Insecure, "true", "false"))
|
||||
return util.SHA256FromStringArray(util.BoolSwitch(a.IsInsecure(), "true", "false"))
|
||||
}
|
||||
|
||||
func (a *ArangoRouteStatusTargetTLS) IsInsecure() bool {
|
||||
if a == nil || a.Insecure == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return *a.Insecure
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
deploymentv1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
v2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
v1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
intstr "k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
@ -96,8 +96,8 @@ func (in *ArangoRouteList) DeepCopyObject() runtime.Object {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ArangoRouteSpec) DeepCopyInto(out *ArangoRouteSpec) {
|
||||
*out = *in
|
||||
if in.DeploymentName != nil {
|
||||
in, out := &in.DeploymentName, &out.DeploymentName
|
||||
if in.Deployment != nil {
|
||||
in, out := &in.Deployment, &out.Deployment
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
|
@ -142,6 +142,11 @@ func (in *ArangoRouteSpecDestination) DeepCopyInto(out *ArangoRouteSpecDestinati
|
|||
*out = new(ArangoRouteSpecDestinationTLS)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Path != nil {
|
||||
in, out := &in.Path, &out.Path
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -228,7 +233,7 @@ func (in *ArangoRouteStatus) DeepCopyInto(out *ArangoRouteStatus) {
|
|||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make(deploymentv1.ConditionList, len(*in))
|
||||
*out = make(v2alpha1.ConditionList, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
|
@ -238,10 +243,10 @@ func (in *ArangoRouteStatus) DeepCopyInto(out *ArangoRouteStatus) {
|
|||
*out = new(v1.Object)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Targets != nil {
|
||||
in, out := &in.Targets, &out.Targets
|
||||
*out = make(ArangoRouteStatusTargets, len(*in))
|
||||
copy(*out, *in)
|
||||
if in.Target != nil {
|
||||
in, out := &in.Target, &out.Target
|
||||
*out = new(ArangoRouteStatusTarget)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -259,7 +264,16 @@ func (in *ArangoRouteStatus) DeepCopy() *ArangoRouteStatus {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ArangoRouteStatusTarget) DeepCopyInto(out *ArangoRouteStatusTarget) {
|
||||
*out = *in
|
||||
out.TLS = in.TLS
|
||||
if in.Destinations != nil {
|
||||
in, out := &in.Destinations, &out.Destinations
|
||||
*out = make(ArangoRouteStatusTargetDestinations, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.TLS != nil {
|
||||
in, out := &in.TLS, &out.TLS
|
||||
*out = new(ArangoRouteStatusTargetTLS)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -273,9 +287,50 @@ func (in *ArangoRouteStatusTarget) DeepCopy() *ArangoRouteStatusTarget {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ArangoRouteStatusTargetDestination) DeepCopyInto(out *ArangoRouteStatusTargetDestination) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoRouteStatusTargetDestination.
|
||||
func (in *ArangoRouteStatusTargetDestination) DeepCopy() *ArangoRouteStatusTargetDestination {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ArangoRouteStatusTargetDestination)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ArangoRouteStatusTargetDestinations) DeepCopyInto(out *ArangoRouteStatusTargetDestinations) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ArangoRouteStatusTargetDestinations, len(*in))
|
||||
copy(*out, *in)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoRouteStatusTargetDestinations.
|
||||
func (in ArangoRouteStatusTargetDestinations) DeepCopy() ArangoRouteStatusTargetDestinations {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ArangoRouteStatusTargetDestinations)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ArangoRouteStatusTargetTLS) DeepCopyInto(out *ArangoRouteStatusTargetTLS) {
|
||||
*out = *in
|
||||
if in.Insecure != nil {
|
||||
in, out := &in.Insecure, &out.Insecure
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -288,23 +343,3 @@ func (in *ArangoRouteStatusTargetTLS) DeepCopy() *ArangoRouteStatusTargetTLS {
|
|||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ArangoRouteStatusTargets) DeepCopyInto(out *ArangoRouteStatusTargets) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ArangoRouteStatusTargets, len(*in))
|
||||
copy(*out, *in)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoRouteStatusTargets.
|
||||
func (in ArangoRouteStatusTargets) DeepCopy() ArangoRouteStatusTargets {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ArangoRouteStatusTargets)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
|
|
@ -59,9 +59,9 @@ func (m *Metadata) Apply(template *core.PodTemplateSpec) error {
|
|||
|
||||
z := m.DeepCopy()
|
||||
|
||||
template.Labels = z.Labels
|
||||
template.Annotations = z.Annotations
|
||||
template.OwnerReferences = z.OwnerReferences
|
||||
template.Labels = util.MergeMaps(true, template.Labels, z.Labels)
|
||||
template.Annotations = util.MergeMaps(true, template.Annotations, z.Annotations)
|
||||
template.OwnerReferences = append(template.OwnerReferences, z.OwnerReferences...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -131,6 +131,49 @@ func Test_Metadata(t *testing.T) {
|
|||
require.Contains(t, pod.Annotations, "B2")
|
||||
require.EqualValues(t, "4", pod.Annotations["B2"])
|
||||
|
||||
require.Len(t, pod.OwnerReferences, 2)
|
||||
require.EqualValues(t, "test", pod.OwnerReferences[0].UID)
|
||||
require.EqualValues(t, "test2", pod.OwnerReferences[1].UID)
|
||||
})
|
||||
})
|
||||
t.Run("Update Templat", func(t *testing.T) {
|
||||
applyMetadata(t, &core.PodTemplateSpec{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"A": "1",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"B": "2",
|
||||
},
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
{
|
||||
UID: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &Metadata{
|
||||
Labels: map[string]string{
|
||||
"A": "3",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"B2": "4",
|
||||
},
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
{
|
||||
UID: "test2",
|
||||
},
|
||||
},
|
||||
})(func(t *testing.T, pod *core.PodTemplateSpec) {
|
||||
require.Len(t, pod.Labels, 1)
|
||||
require.Contains(t, pod.Labels, "A")
|
||||
require.EqualValues(t, "3", pod.Labels["A"])
|
||||
|
||||
require.Len(t, pod.Annotations, 2)
|
||||
require.Contains(t, pod.Annotations, "B")
|
||||
require.EqualValues(t, "2", pod.Annotations["B"])
|
||||
require.Contains(t, pod.Annotations, "B2")
|
||||
require.EqualValues(t, "4", pod.Annotations["B2"])
|
||||
|
||||
require.Len(t, pod.OwnerReferences, 2)
|
||||
require.EqualValues(t, "test", pod.OwnerReferences[0].UID)
|
||||
require.EqualValues(t, "test2", pod.OwnerReferences[1].UID)
|
||||
|
|
|
@ -59,9 +59,9 @@ func (m *Metadata) Apply(template *core.PodTemplateSpec) error {
|
|||
|
||||
z := m.DeepCopy()
|
||||
|
||||
template.Labels = z.Labels
|
||||
template.Annotations = z.Annotations
|
||||
template.OwnerReferences = z.OwnerReferences
|
||||
template.Labels = util.MergeMaps(true, template.Labels, z.Labels)
|
||||
template.Annotations = util.MergeMaps(true, template.Annotations, z.Annotations)
|
||||
template.OwnerReferences = append(template.OwnerReferences, z.OwnerReferences...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -131,6 +131,49 @@ func Test_Metadata(t *testing.T) {
|
|||
require.Contains(t, pod.Annotations, "B2")
|
||||
require.EqualValues(t, "4", pod.Annotations["B2"])
|
||||
|
||||
require.Len(t, pod.OwnerReferences, 2)
|
||||
require.EqualValues(t, "test", pod.OwnerReferences[0].UID)
|
||||
require.EqualValues(t, "test2", pod.OwnerReferences[1].UID)
|
||||
})
|
||||
})
|
||||
t.Run("Update Template", func(t *testing.T) {
|
||||
applyMetadata(t, &core.PodTemplateSpec{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"A": "1",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"B": "2",
|
||||
},
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
{
|
||||
UID: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &Metadata{
|
||||
Labels: map[string]string{
|
||||
"A": "3",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"B2": "4",
|
||||
},
|
||||
OwnerReferences: []meta.OwnerReference{
|
||||
{
|
||||
UID: "test2",
|
||||
},
|
||||
},
|
||||
})(func(t *testing.T, pod *core.PodTemplateSpec) {
|
||||
require.Len(t, pod.Labels, 1)
|
||||
require.Contains(t, pod.Labels, "A")
|
||||
require.EqualValues(t, "3", pod.Labels["A"])
|
||||
|
||||
require.Len(t, pod.Annotations, 2)
|
||||
require.Contains(t, pod.Annotations, "B")
|
||||
require.EqualValues(t, "2", pod.Annotations["B"])
|
||||
require.Contains(t, pod.Annotations, "B2")
|
||||
require.EqualValues(t, "4", pod.Annotations["B2"])
|
||||
|
||||
require.Len(t, pod.OwnerReferences, 2)
|
||||
require.EqualValues(t, "test", pod.OwnerReferences[0].UID)
|
||||
require.EqualValues(t, "test2", pod.OwnerReferences[1].UID)
|
||||
|
|
|
@ -34,7 +34,7 @@ import (
|
|||
|
||||
var (
|
||||
resourceNameRE = regexp.MustCompile(`^([0-9\-\.a-z])+$`)
|
||||
apiPathRE = regexp.MustCompile(`^/([A-Za-z0-9\-]+/)*$`)
|
||||
apiPathRE = regexp.MustCompile(`^/([_A-Za-z0-9\-]+/)*$`)
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -190,6 +190,19 @@ func ValidateList[T any](in []T, validator func(T) error) error {
|
|||
return WithErrors(errors...)
|
||||
}
|
||||
|
||||
// ValidateMap validates all elements on the list
|
||||
func ValidateMap[T any](in map[string]T, validator func(string, T) error) error {
|
||||
errors := make([]error, 0, len(in))
|
||||
|
||||
for id := range in {
|
||||
if err := PrefixResourceError(fmt.Sprintf("`%s`", id), validator(id, in[id])); err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
return WithErrors(errors...)
|
||||
}
|
||||
|
||||
// ValidateImage Validates if provided image is valid
|
||||
func ValidateImage(image string) error {
|
||||
if image == "" {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,11 +4,14 @@ v1alpha1:
|
|||
spec:
|
||||
properties:
|
||||
deployment:
|
||||
description: DeploymentName specifies the ArangoDeployment object name
|
||||
description: Deployment specifies the ArangoDeployment object name
|
||||
type: string
|
||||
destination:
|
||||
description: Destination defines the route destination
|
||||
properties:
|
||||
path:
|
||||
description: Path defines service path used for overrides
|
||||
type: string
|
||||
schema:
|
||||
description: Schema defines HTTP/S schema used for connection
|
||||
type: string
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
|
||||
|
@ -283,7 +284,7 @@ func (i *ImageUpdatePod) GetRole() string {
|
|||
return "id"
|
||||
}
|
||||
|
||||
func (i *ImageUpdatePod) Init(_ context.Context, _ interfaces.Inspector, pod *core.Pod) error {
|
||||
func (i *ImageUpdatePod) Init(_ context.Context, _ interfaces.Inspector, pod *core.PodTemplateSpec) error {
|
||||
terminationGracePeriodSeconds := int64((time.Second * 30).Seconds())
|
||||
pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
|
||||
pod.Spec.PriorityClassName = i.spec.ID.Get().PriorityClassName
|
||||
|
@ -311,7 +312,7 @@ func (i *ImageUpdatePod) GetVolumes() []core.Volume {
|
|||
return getVolumes(i.AsInput()).Volumes()
|
||||
}
|
||||
|
||||
func (i *ImageUpdatePod) GetSidecars(*core.Pod) error {
|
||||
func (i *ImageUpdatePod) GetSidecars(spec *core.PodTemplateSpec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -503,6 +504,10 @@ func (a *ImageUpdatePod) AsInput() pod.Input {
|
|||
}
|
||||
}
|
||||
|
||||
func (i *ImageUpdatePod) Profiles() (schedulerApi.ProfileTemplates, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetExecutor returns the fixed path to the ArangoSync binary in the container.
|
||||
func (a *ArangoSyncIdentity) GetExecutor() string {
|
||||
return resources.ArangoSyncExecutor
|
||||
|
|
|
@ -132,6 +132,8 @@ func (s *stateInspector) RefreshState(ctx context.Context, members api.Deploymen
|
|||
if results[id].IsServing() {
|
||||
client = results[id].client
|
||||
}
|
||||
case api.ServerGroupTypeGateway:
|
||||
results[id] = s.fetchGatewayMemberState(ctxChild, members[id])
|
||||
default:
|
||||
assertion.InvalidGroupKey.Assert(true, "Unable to fetch Health for an unknown group: %s", members[id].Group.AsRole())
|
||||
results[id] = State{
|
||||
|
@ -178,6 +180,9 @@ func (s *stateInspector) RefreshState(ctx context.Context, members api.Deploymen
|
|||
case api.ServerGroupTypeArangoSync:
|
||||
// ArangoSync is considered as healthy when it is possible to fetch version.
|
||||
results[i].IsClusterHealthy = true
|
||||
case api.ServerGroupTypeGateway:
|
||||
// Gateway is considered as healthy when it is possible to fetch version.
|
||||
results[i].IsClusterHealthy = true
|
||||
default:
|
||||
assertion.InvalidGroupKey.Assert(true, "Unable to fetch Health for an unknown group: %s", members[i].Group.AsRole())
|
||||
results[i].IsClusterHealthy = false
|
||||
|
@ -228,6 +233,26 @@ func (s *stateInspector) fetchArangosyncMemberState(ctx context.Context, m api.D
|
|||
return state
|
||||
}
|
||||
|
||||
func (s *stateInspector) fetchGatewayMemberState(ctx context.Context, m api.DeploymentStatusMemberElement) State {
|
||||
// by default, it is not serving. It will be changed if it serves.
|
||||
var state State
|
||||
c, err := s.deployment.GetServerClient(ctx, m.Group, m.Member.ID)
|
||||
if err != nil {
|
||||
state.NotReachableErr = err
|
||||
return state
|
||||
}
|
||||
|
||||
if v, err := c.Version(ctx); err != nil {
|
||||
state.NotReachableErr = err
|
||||
return state
|
||||
} else {
|
||||
state.Version = v
|
||||
state.client = c
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *stateInspector) fetchServerMemberState(ctx context.Context, m api.DeploymentStatusMemberElement,
|
||||
servingGroup api.ServerGroup) State {
|
||||
// by default, it is not serving. It will be changed if it serves.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// 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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -81,4 +81,9 @@ var probeMap = map[api.ServerGroup]probes{
|
|||
liveness: newProbe(true, true),
|
||||
readiness: newProbe(false, false),
|
||||
},
|
||||
api.ServerGroupGateways: { // TODO: Enable Probes
|
||||
startup: newProbe(false, false),
|
||||
liveness: newProbe(false, false),
|
||||
readiness: newProbe(false, false),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/globals"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tolerations"
|
||||
|
@ -78,7 +79,7 @@ func (a actionRuntimeContainerSyncTolerations) Start(ctx context.Context) (bool,
|
|||
|
||||
expectedTolerations := member.Spec.Template.PodSpec.Spec.Tolerations
|
||||
|
||||
origTolerations := tolerations.CreatePodTolerations(a.actionCtx.GetMode(), a.action.Group)
|
||||
origTolerations := resources.CreatePodTolerations(a.actionCtx.GetMode(), a.action.Group)
|
||||
|
||||
calculatedTolerations := tolerations.MergeTolerationsIfNotFound(currentTolerations, origTolerations, expectedTolerations)
|
||||
|
||||
|
|
182
pkg/deployment/resources/config_map_gateway.go
Normal file
182
pkg/deployment/resources/config_map_gateway.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
//
|
||||
// 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"
|
||||
"path/filepath"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
networkingApi "github.com/arangodb/kube-arangodb/pkg/apis/networking/v1alpha1"
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/gateway"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/constants"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/globals"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
|
||||
configMapsV1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/configmap/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/patcher"
|
||||
)
|
||||
|
||||
func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspectorInterface.Inspector, configMaps configMapsV1.ModInterface) error {
|
||||
deploymentName := r.context.GetAPIObject().GetName()
|
||||
configMapName := GetGatewayConfigMapName(deploymentName)
|
||||
|
||||
log := r.log.Str("section", "gateway-config").Str("name", configMapName)
|
||||
|
||||
cfg, err := r.renderGatewayConfig(cachedStatus)
|
||||
if err != nil {
|
||||
return errors.WithStack(errors.Wrapf(err, "Failed to generate gateway config"))
|
||||
}
|
||||
|
||||
gatewayCfgYaml, gatewayCfgChecksum, _, err := cfg.RenderYAML()
|
||||
if err != nil {
|
||||
return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config"))
|
||||
}
|
||||
|
||||
if cm, exists := cachedStatus.ConfigMap().V1().GetSimple(configMapName); !exists {
|
||||
// Create
|
||||
cm = &core.ConfigMap{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: configMapName,
|
||||
},
|
||||
Data: map[string]string{
|
||||
GatewayConfigFileName: string(gatewayCfgYaml),
|
||||
},
|
||||
}
|
||||
|
||||
owner := r.context.GetAPIObject().AsOwner()
|
||||
|
||||
err = globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
|
||||
return k8sutil.CreateConfigMap(ctxChild, configMaps, cm, &owner)
|
||||
})
|
||||
if kerrors.IsAlreadyExists(err) {
|
||||
// CM added while we tried it also
|
||||
return nil
|
||||
} else if err != nil {
|
||||
// Failed to create
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return errors.Reconcile()
|
||||
} else {
|
||||
// CM Exists, checks checksum - if key is not in the map we return empty string
|
||||
if existingSha := util.SHA256FromString(cm.Data[GatewayConfigFileName]); existingSha != gatewayCfgChecksum {
|
||||
// We need to do the update
|
||||
if _, changed, err := patcher.Patcher[*core.ConfigMap](ctx, cachedStatus.ConfigMapsModInterface().V1(), cm, meta.PatchOptions{},
|
||||
patcher.PatchConfigMapData(map[string]string{
|
||||
GatewayConfigFileName: string(gatewayCfgYaml),
|
||||
})); err != nil {
|
||||
log.Err(err).Debug("Failed to patch GatewayConfig ConfigMap")
|
||||
return errors.WithStack(err)
|
||||
} else if changed {
|
||||
log.Str("service", cm.GetName()).Str("before", existingSha).Str("after", gatewayCfgChecksum).Info("Updated GatewayConfig")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resources) renderGatewayConfig(cachedStatus inspectorInterface.Inspector) (gateway.Config, error) {
|
||||
deploymentName := r.context.GetAPIObject().GetName()
|
||||
|
||||
log := r.log.Str("section", "gateway-config-render")
|
||||
|
||||
spec := r.context.GetSpec()
|
||||
svcServingName := fmt.Sprintf("%s-%s", deploymentName, spec.Mode.Get().ServingGroup().AsRole())
|
||||
|
||||
svc, svcExist := cachedStatus.Service().V1().GetSimple(svcServingName)
|
||||
if !svcExist {
|
||||
return gateway.Config{}, errors.Errorf("Service %s not found", svcServingName)
|
||||
}
|
||||
|
||||
var cfg gateway.Config
|
||||
|
||||
cfg.DefaultDestination = gateway.ConfigDestination{
|
||||
Targets: []gateway.ConfigDestinationTarget{
|
||||
{
|
||||
Host: svc.Spec.ClusterIP,
|
||||
Port: shared.ArangoPort,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if spec.TLS.IsSecure() {
|
||||
// Enabled TLS, add config
|
||||
keyPath := filepath.Join(shared.TLSKeyfileVolumeMountDir, constants.SecretTLSKeyfile)
|
||||
cfg.DefaultTLS = &gateway.ConfigTLS{
|
||||
CertificatePath: keyPath,
|
||||
PrivateKeyPath: keyPath,
|
||||
}
|
||||
cfg.DefaultDestination.Type = util.NewType(gateway.ConfigDestinationTypeHTTPS)
|
||||
}
|
||||
|
||||
// Check ArangoRoutes
|
||||
if c, err := cachedStatus.ArangoRoute().V1Alpha1(); err == nil {
|
||||
cfg.Destinations = gateway.ConfigDestinations{}
|
||||
if err := c.Iterate(func(at *networkingApi.ArangoRoute) error {
|
||||
log := log.Str("ArangoRoute", at.GetName())
|
||||
if !at.Status.Conditions.IsTrue(networkingApi.ReadyCondition) {
|
||||
l := log
|
||||
if c, ok := at.Status.Conditions.Get(networkingApi.ReadyCondition); ok {
|
||||
l.Str("message", c.Message)
|
||||
}
|
||||
l.Warn("ArangoRoute is not ready")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if target := at.Status.Target; target != nil {
|
||||
var dest gateway.ConfigDestination
|
||||
if destinations := target.Destinations; len(destinations) > 0 {
|
||||
for _, destination := range destinations {
|
||||
var t gateway.ConfigDestinationTarget
|
||||
|
||||
t.Host = destination.Host
|
||||
t.Port = destination.Port
|
||||
|
||||
dest.Targets = append(dest.Targets, t)
|
||||
}
|
||||
}
|
||||
if tls := target.TLS; tls != nil {
|
||||
dest.Type = util.NewType(gateway.ConfigDestinationTypeHTTPS)
|
||||
}
|
||||
dest.Path = util.NewType(target.Path)
|
||||
cfg.Destinations[at.Spec.GetRoute().GetPath()] = dest
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}, func(at *networkingApi.ArangoRoute) bool {
|
||||
return at.Spec.GetDeployment() == deploymentName
|
||||
}); err != nil {
|
||||
return gateway.Config{}, errors.Wrapf(err, "Unable to iterate over ArangoRoutes")
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
|
@ -22,21 +22,13 @@ package resources
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
|
||||
"github.com/arangodb/kube-arangodb/pkg/metrics"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/globals"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
|
||||
configMapsV1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/configmap/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -65,49 +57,3 @@ func (r *Resources) EnsureConfigMaps(ctx context.Context, cachedStatus inspector
|
|||
}
|
||||
return reconcileRequired.Reconcile(ctx)
|
||||
}
|
||||
|
||||
func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspectorInterface.Inspector, configMaps configMapsV1.ModInterface) error {
|
||||
deploymentName := r.context.GetAPIObject().GetName()
|
||||
configMapName := GetGatewayConfigMapName(deploymentName)
|
||||
|
||||
if _, exists := cachedStatus.ConfigMap().V1().GetSimple(configMapName); !exists {
|
||||
// Find serving service (single/crdn)
|
||||
spec := r.context.GetSpec()
|
||||
svcServingName := fmt.Sprintf("%s-%s", deploymentName, spec.Mode.Get().ServingGroup().AsRole())
|
||||
|
||||
svc, svcExist := cachedStatus.Service().V1().GetSimple(svcServingName)
|
||||
if !svcExist {
|
||||
return errors.Errorf("Service %s not found", svcServingName)
|
||||
}
|
||||
|
||||
gatewayCfgYaml, err := RenderGatewayConfigYAML(svc.Spec.ClusterIP)
|
||||
if err != nil {
|
||||
return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config"))
|
||||
}
|
||||
cm := &core.ConfigMap{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: configMapName,
|
||||
},
|
||||
Data: map[string]string{
|
||||
GatewayConfigFileName: string(gatewayCfgYaml),
|
||||
GatewayConfigChecksumName: util.SHA256(gatewayCfgYaml),
|
||||
},
|
||||
}
|
||||
|
||||
owner := r.context.GetAPIObject().AsOwner()
|
||||
|
||||
err = globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
|
||||
return k8sutil.CreateConfigMap(ctxChild, configMaps, cm, &owner)
|
||||
})
|
||||
if kerrors.IsAlreadyExists(err) {
|
||||
// CM added while we tried it also
|
||||
return nil
|
||||
} else if err != nil {
|
||||
// Failed to create
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return errors.Reconcile()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
259
pkg/deployment/resources/gateway/gateway_config.go
Normal file
259
pkg/deployment/resources/gateway/gateway_config.go
Normal file
|
@ -0,0 +1,259 @@
|
|||
//
|
||||
// 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 gateway
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
bootstrapAPI "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
|
||||
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
listenerAPI "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
routerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
|
||||
httpConnectionManagerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DefaultDestination ConfigDestination `json:"defaultDestination,omitempty"`
|
||||
|
||||
Destinations ConfigDestinations `json:"destinations,omitempty"`
|
||||
|
||||
DefaultTLS *ConfigTLS `json:"defaultTLS,omitempty"`
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
return errors.Errors(
|
||||
shared.PrefixResourceErrors("defaultDestination", c.DefaultDestination.Validate()),
|
||||
shared.PrefixResourceErrors("destinations", c.Destinations.Validate()),
|
||||
)
|
||||
}
|
||||
|
||||
func (c Config) RenderYAML() ([]byte, string, *bootstrapAPI.Bootstrap, error) {
|
||||
cfg, err := c.Render()
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
data, err := protojson.MarshalOptions{
|
||||
UseProtoNames: true,
|
||||
}.Marshal(cfg)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
data, err = yaml.JSONToYAML(data)
|
||||
return data, util.SHA256(data), cfg, err
|
||||
}
|
||||
|
||||
func (c Config) Render() (*bootstrapAPI.Bootstrap, error) {
|
||||
if err := c.Validate(); err != nil {
|
||||
return nil, errors.Wrapf(err, "Validation failed")
|
||||
}
|
||||
|
||||
clusters, err := c.RenderClusters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render clusters")
|
||||
}
|
||||
|
||||
listener, err := c.RenderListener()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render listener")
|
||||
}
|
||||
|
||||
return &bootstrapAPI.Bootstrap{
|
||||
Admin: &bootstrapAPI.Admin{
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Address: "127.0.0.1",
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{PortValue: 9901},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
StaticResources: &bootstrapAPI.Bootstrap_StaticResources{
|
||||
Listeners: []*listenerAPI.Listener{
|
||||
listener,
|
||||
},
|
||||
Clusters: clusters,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c Config) RenderClusters() ([]*clusterAPI.Cluster, error) {
|
||||
def, err := c.DefaultDestination.RenderCluster("default")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusters := []*clusterAPI.Cluster{
|
||||
def,
|
||||
}
|
||||
|
||||
for k, v := range c.Destinations {
|
||||
name := fmt.Sprintf("cluster_%s", util.SHA256FromString(k))
|
||||
c, err := v.RenderCluster(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusters = append(clusters, c)
|
||||
}
|
||||
|
||||
sort.Slice(clusters, func(i, j int) bool {
|
||||
return clusters[i].Name < clusters[j].Name
|
||||
})
|
||||
|
||||
return clusters, nil
|
||||
}
|
||||
|
||||
func (c Config) RenderRoutes() ([]*routeAPI.Route, error) {
|
||||
def, err := c.DefaultDestination.RenderRoute("default", "/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routes := []*routeAPI.Route{
|
||||
def,
|
||||
}
|
||||
|
||||
for k, v := range c.Destinations {
|
||||
name := fmt.Sprintf("cluster_%s", util.SHA256FromString(k))
|
||||
c, err := v.RenderRoute(name, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routes = append(routes, c)
|
||||
}
|
||||
|
||||
sort.Slice(routes, func(i, j int) bool {
|
||||
return routes[i].GetMatch().GetPrefix() > routes[j].GetMatch().GetPrefix()
|
||||
})
|
||||
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
||||
httpFilterConfigType, err := anypb.New(&routerAPI.Router{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render route config")
|
||||
}
|
||||
|
||||
routes, err := c.RenderRoutes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render routes")
|
||||
}
|
||||
|
||||
filterConfigType, err := anypb.New(&httpConnectionManagerAPI.HttpConnectionManager{
|
||||
StatPrefix: "ingress_http",
|
||||
CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO,
|
||||
RouteSpecifier: &httpConnectionManagerAPI.HttpConnectionManager_RouteConfig{
|
||||
RouteConfig: &routeAPI.RouteConfiguration{
|
||||
Name: "default",
|
||||
VirtualHosts: []*routeAPI.VirtualHost{
|
||||
{
|
||||
Name: "default",
|
||||
Domains: []string{"*"},
|
||||
Routes: routes,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HttpFilters: []*httpConnectionManagerAPI.HttpFilter{
|
||||
{
|
||||
Name: "envoy.filters.http.routerAPI",
|
||||
ConfigType: &httpConnectionManagerAPI.HttpFilter_TypedConfig{
|
||||
TypedConfig: httpFilterConfigType,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render http connection manager")
|
||||
}
|
||||
|
||||
return []*listenerAPI.Filter{
|
||||
{
|
||||
Name: "envoy.filters.network.httpConnectionManagerAPI",
|
||||
ConfigType: &listenerAPI.Filter_TypedConfig{
|
||||
TypedConfig: filterConfigType,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c Config) RenderDefaultFilterChain() (*listenerAPI.FilterChain, error) {
|
||||
filters, err := c.RenderFilters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render filters")
|
||||
}
|
||||
|
||||
ret := &listenerAPI.FilterChain{
|
||||
Filters: filters,
|
||||
}
|
||||
|
||||
if tls, err := c.DefaultTLS.RenderListenerTransportSocket(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ret.TransportSocket = tls
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (c Config) RenderSecondaryFilterChains() ([]*listenerAPI.FilterChain, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
||||
filterChains, err := c.RenderSecondaryFilterChains()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render secondary filter chains")
|
||||
}
|
||||
|
||||
defaultFilterChain, err := c.RenderDefaultFilterChain()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render default filter")
|
||||
}
|
||||
|
||||
return &listenerAPI.Listener{
|
||||
Name: "default",
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Address: "0.0.0.0",
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{PortValue: shared.ArangoPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
FilterChains: filterChains,
|
||||
|
||||
DefaultFilterChain: defaultFilterChain,
|
||||
}, nil
|
||||
}
|
122
pkg/deployment/resources/gateway/gateway_config_destination.go
Normal file
122
pkg/deployment/resources/gateway/gateway_config_destination.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// 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 gateway
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
endpointAPI "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
type ConfigDestinations map[string]ConfigDestination
|
||||
|
||||
func (c ConfigDestinations) Validate() error {
|
||||
if len(c) == 0 {
|
||||
return nil
|
||||
}
|
||||
return shared.WithErrors(
|
||||
shared.ValidateMap(c, func(k string, destination ConfigDestination) error {
|
||||
var errs []error
|
||||
if k == "/" {
|
||||
errs = append(errs, errors.Errorf("Route for `/` is reserved"))
|
||||
}
|
||||
if err := shared.ValidateAPIPath(k); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if err := destination.Validate(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return shared.WithErrors(errs...)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
type ConfigDestination struct {
|
||||
Targets ConfigDestinationTargets `json:"targets,omitempty"`
|
||||
|
||||
Type *ConfigDestinationType `json:"type,omitempty"`
|
||||
|
||||
Path *string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (c ConfigDestination) Validate() error {
|
||||
return shared.WithErrors(
|
||||
shared.PrefixResourceError("targets", c.Targets.Validate()),
|
||||
shared.PrefixResourceError("type", c.Type.Validate()),
|
||||
shared.PrefixResourceError("path", shared.ValidateAPIPath(c.GetPath())),
|
||||
)
|
||||
}
|
||||
|
||||
func (c ConfigDestination) GetPath() string {
|
||||
if c.Path == nil {
|
||||
return "/"
|
||||
}
|
||||
|
||||
return *c.Path
|
||||
}
|
||||
|
||||
func (c ConfigDestination) RenderRoute(name, prefix string) (*routeAPI.Route, error) {
|
||||
return &routeAPI.Route{
|
||||
Match: &routeAPI.RouteMatch{
|
||||
PathSpecifier: &routeAPI.RouteMatch_Prefix{
|
||||
Prefix: prefix,
|
||||
},
|
||||
},
|
||||
Action: &routeAPI.Route_Route{
|
||||
Route: &routeAPI.RouteAction{
|
||||
ClusterSpecifier: &routeAPI.RouteAction_Cluster{
|
||||
Cluster: name,
|
||||
},
|
||||
PrefixRewrite: c.GetPath(),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c ConfigDestination) RenderCluster(name string) (*clusterAPI.Cluster, error) {
|
||||
cluster := &clusterAPI.Cluster{
|
||||
Name: name,
|
||||
ConnectTimeout: durationpb.New(time.Second),
|
||||
LbPolicy: clusterAPI.Cluster_ROUND_ROBIN,
|
||||
LoadAssignment: &endpointAPI.ClusterLoadAssignment{
|
||||
ClusterName: name,
|
||||
Endpoints: []*endpointAPI.LocalityLbEndpoints{
|
||||
{
|
||||
LbEndpoints: c.Targets.RenderEndpoints(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if t, err := c.Type.RenderUpstreamTransportSocket(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
cluster.TransportSocket = t
|
||||
}
|
||||
|
||||
return cluster, nil
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// 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 gateway
|
||||
|
||||
import (
|
||||
coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
endpointAPI "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
type ConfigDestinationTargets []ConfigDestinationTarget
|
||||
|
||||
func (c ConfigDestinationTargets) RenderEndpoints() []*endpointAPI.LbEndpoint {
|
||||
var endpoints = make([]*endpointAPI.LbEndpoint, len(c))
|
||||
|
||||
for id := range c {
|
||||
endpoints[id] = c[id].RenderEndpoint()
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func (c ConfigDestinationTargets) Validate() error {
|
||||
if len(c) == 0 {
|
||||
return errors.Errorf("Empty Target not allowed")
|
||||
}
|
||||
return shared.ValidateList(c, func(target ConfigDestinationTarget) error {
|
||||
return target.Validate()
|
||||
})
|
||||
}
|
||||
|
||||
type ConfigDestinationTarget struct {
|
||||
Host string `json:"ip,omitempty"`
|
||||
Port int32 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
func (c ConfigDestinationTarget) Validate() error {
|
||||
return shared.WithErrors(
|
||||
shared.ValidateRequiredPath("ip", &c.Host, func(t string) error {
|
||||
if t == "" {
|
||||
return errors.Errorf("Empty string not allowed")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
shared.ValidateRequiredPath("ip", &c.Port, func(t int32) error {
|
||||
if t <= 0 {
|
||||
return errors.Errorf("Port needs to be greater than 0")
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (c ConfigDestinationTarget) RenderEndpoint() *endpointAPI.LbEndpoint {
|
||||
return &endpointAPI.LbEndpoint{
|
||||
HostIdentifier: &endpointAPI.LbEndpoint_Endpoint{
|
||||
Endpoint: &endpointAPI.Endpoint{
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Protocol: coreAPI.SocketAddress_TCP,
|
||||
Address: c.Host,
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{
|
||||
PortValue: uint32(c.Port),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// 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 gateway
|
||||
|
||||
import (
|
||||
coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
tlsApi "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
type ConfigDestinationType int
|
||||
|
||||
const (
|
||||
ConfigDestinationTypeHTTP ConfigDestinationType = iota
|
||||
ConfigDestinationTypeHTTPS
|
||||
)
|
||||
|
||||
func (c *ConfigDestinationType) Get() ConfigDestinationType {
|
||||
if c == nil {
|
||||
return ConfigDestinationTypeHTTP
|
||||
}
|
||||
|
||||
switch v := *c; v {
|
||||
case ConfigDestinationTypeHTTP, ConfigDestinationTypeHTTPS:
|
||||
return v
|
||||
default:
|
||||
return ConfigDestinationTypeHTTP
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfigDestinationType) RenderUpstreamTransportSocket() (*coreAPI.TransportSocket, error) {
|
||||
if c.Get() == ConfigDestinationTypeHTTPS {
|
||||
tlsConfig, err := anypb.New(&tlsApi.UpstreamTlsContext{
|
||||
CommonTlsContext: &tlsApi.CommonTlsContext{
|
||||
ValidationContextType: &tlsApi.CommonTlsContext_ValidationContext{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &coreAPI.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &coreAPI.TransportSocket_TypedConfig{
|
||||
TypedConfig: tlsConfig,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *ConfigDestinationType) Validate() error {
|
||||
switch c.Get() {
|
||||
case ConfigDestinationTypeHTTP, ConfigDestinationTypeHTTPS:
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("Invalid destination type")
|
||||
}
|
||||
}
|
150
pkg/deployment/resources/gateway/gateway_config_test.go
Normal file
150
pkg/deployment/resources/gateway/gateway_config_test.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// 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 gateway
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
bootstrapAPI "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
func renderAndPrintGatewayConfig(t *testing.T, cfg Config) *bootstrapAPI.Bootstrap {
|
||||
data, checksum, obj, err := cfg.RenderYAML()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Checksum: %s", checksum)
|
||||
t.Log(string(data))
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
func Test_GatewayConfig(t *testing.T) {
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
renderAndPrintGatewayConfig(t, Config{
|
||||
DefaultDestination: ConfigDestination{
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12345,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
renderAndPrintGatewayConfig(t, Config{
|
||||
DefaultDestination: ConfigDestination{
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12345,
|
||||
},
|
||||
},
|
||||
Type: util.NewType(ConfigDestinationTypeHTTPS),
|
||||
},
|
||||
DefaultTLS: &ConfigTLS{
|
||||
CertificatePath: "/test",
|
||||
PrivateKeyPath: "/test12",
|
||||
},
|
||||
})
|
||||
})
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
renderAndPrintGatewayConfig(t, Config{
|
||||
DefaultDestination: ConfigDestination{
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12345,
|
||||
},
|
||||
},
|
||||
Path: util.NewType("/test/path/"),
|
||||
Type: util.NewType(ConfigDestinationTypeHTTPS),
|
||||
},
|
||||
DefaultTLS: &ConfigTLS{
|
||||
CertificatePath: "/test",
|
||||
PrivateKeyPath: "/test12",
|
||||
},
|
||||
})
|
||||
})
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
renderAndPrintGatewayConfig(t, Config{
|
||||
DefaultDestination: ConfigDestination{
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12345,
|
||||
},
|
||||
},
|
||||
Path: util.NewType("/test/path/"),
|
||||
Type: util.NewType(ConfigDestinationTypeHTTPS),
|
||||
},
|
||||
DefaultTLS: &ConfigTLS{
|
||||
CertificatePath: "/test",
|
||||
PrivateKeyPath: "/test12",
|
||||
},
|
||||
Destinations: ConfigDestinations{
|
||||
"/test/": {
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12346,
|
||||
},
|
||||
},
|
||||
Path: util.NewType("/test/path/"),
|
||||
Type: util.NewType(ConfigDestinationTypeHTTPS),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
renderAndPrintGatewayConfig(t, Config{
|
||||
DefaultDestination: ConfigDestination{
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12345,
|
||||
},
|
||||
},
|
||||
Path: util.NewType("/test/path/"),
|
||||
Type: util.NewType(ConfigDestinationTypeHTTPS),
|
||||
},
|
||||
DefaultTLS: &ConfigTLS{
|
||||
CertificatePath: "/test",
|
||||
PrivateKeyPath: "/test12",
|
||||
},
|
||||
Destinations: ConfigDestinations{
|
||||
"/_test/": {
|
||||
Targets: []ConfigDestinationTarget{
|
||||
{
|
||||
Host: "127.0.0.1",
|
||||
Port: 12346,
|
||||
},
|
||||
},
|
||||
Path: util.NewType("/test/path/"),
|
||||
Type: util.NewType(ConfigDestinationTypeHTTP),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
69
pkg/deployment/resources/gateway/gateway_config_tls.go
Normal file
69
pkg/deployment/resources/gateway/gateway_config_tls.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// 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 gateway
|
||||
|
||||
import (
|
||||
coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
tlsApi "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
type ConfigTLS struct {
|
||||
CertificatePath string `json:"certificatePath,omitempty"`
|
||||
PrivateKeyPath string `json:"privateKeyPath,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ConfigTLS) RenderListenerTransportSocket() (*coreAPI.TransportSocket, error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tlsContext, err := anypb.New(&tlsApi.DownstreamTlsContext{
|
||||
CommonTlsContext: &tlsApi.CommonTlsContext{
|
||||
TlsCertificates: []*tlsApi.TlsCertificate{
|
||||
{
|
||||
CertificateChain: &coreAPI.DataSource{
|
||||
Specifier: &coreAPI.DataSource_Filename{
|
||||
Filename: c.CertificatePath,
|
||||
},
|
||||
},
|
||||
PrivateKey: &coreAPI.DataSource{
|
||||
Specifier: &coreAPI.DataSource_Filename{
|
||||
Filename: c.PrivateKeyPath,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render tls context")
|
||||
}
|
||||
|
||||
return &coreAPI.TransportSocket{
|
||||
Name: "envoy.transport_sockets.tls",
|
||||
ConfigType: &coreAPI.TransportSocket_TypedConfig{
|
||||
TypedConfig: tlsContext,
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -1,268 +0,0 @@
|
|||
//
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bootstrapAPI "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
|
||||
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
endpointAPI "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||
listenerAPI "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
routerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
|
||||
httpConnectionManagerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
type Redirect util.KV[string, []string]
|
||||
|
||||
func WithRedirect(prefix string, target ...string) Redirect {
|
||||
return Redirect{
|
||||
K: prefix,
|
||||
V: target,
|
||||
}
|
||||
}
|
||||
|
||||
func RenderGatewayConfigYAML(dbServiceAddress string, redirects ...Redirect) ([]byte, error) {
|
||||
cfg, err := RenderConfig(dbServiceAddress, redirects...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := protojson.MarshalOptions{
|
||||
UseProtoNames: true,
|
||||
}.Marshal(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = yaml.JSONToYAML(data)
|
||||
return data, err
|
||||
}
|
||||
|
||||
func RenderConfig(dbServiceAddress string, redirects ...Redirect) (*bootstrapAPI.Bootstrap, error) {
|
||||
clusters := []*clusterAPI.Cluster{
|
||||
{
|
||||
Name: "arangodb",
|
||||
ConnectTimeout: durationpb.New(250 * time.Millisecond),
|
||||
LbPolicy: clusterAPI.Cluster_ROUND_ROBIN,
|
||||
LoadAssignment: &endpointAPI.ClusterLoadAssignment{
|
||||
ClusterName: "arangodb",
|
||||
Endpoints: []*endpointAPI.LocalityLbEndpoints{
|
||||
{
|
||||
LbEndpoints: []*endpointAPI.LbEndpoint{
|
||||
{
|
||||
HostIdentifier: &endpointAPI.LbEndpoint_Endpoint{
|
||||
Endpoint: &endpointAPI.Endpoint{
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Address: dbServiceAddress,
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{
|
||||
PortValue: shared.ArangoPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
routes := []*routeAPI.Route{
|
||||
{
|
||||
Match: &routeAPI.RouteMatch{
|
||||
PathSpecifier: &routeAPI.RouteMatch_Prefix{
|
||||
Prefix: "/",
|
||||
},
|
||||
},
|
||||
Action: &routeAPI.Route_Route{
|
||||
Route: &routeAPI.RouteAction{
|
||||
ClusterSpecifier: &routeAPI.RouteAction_Cluster{
|
||||
Cluster: "arangodb",
|
||||
},
|
||||
PrefixRewrite: "/",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for id, redirect := range redirects {
|
||||
var endpoints []*endpointAPI.LbEndpoint
|
||||
|
||||
for _, target := range redirect.V {
|
||||
req, err := url.Parse(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(req.Port())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoints = append(endpoints, &endpointAPI.LbEndpoint{
|
||||
HostIdentifier: &endpointAPI.LbEndpoint_Endpoint{
|
||||
Endpoint: &endpointAPI.Endpoint{
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Address: req.Hostname(),
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{
|
||||
PortValue: uint32(port),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
cluster := &clusterAPI.Cluster{
|
||||
Name: fmt.Sprintf("cluster_%05d", id),
|
||||
ConnectTimeout: durationpb.New(250 * time.Millisecond),
|
||||
LbPolicy: clusterAPI.Cluster_ROUND_ROBIN,
|
||||
LoadAssignment: &endpointAPI.ClusterLoadAssignment{
|
||||
ClusterName: fmt.Sprintf("cluster_%05d", id),
|
||||
Endpoints: []*endpointAPI.LocalityLbEndpoints{
|
||||
{
|
||||
LbEndpoints: endpoints,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
route := &routeAPI.Route{
|
||||
Match: &routeAPI.RouteMatch{
|
||||
PathSpecifier: &routeAPI.RouteMatch_Prefix{
|
||||
Prefix: redirect.K,
|
||||
},
|
||||
},
|
||||
Action: &routeAPI.Route_Route{
|
||||
Route: &routeAPI.RouteAction{
|
||||
ClusterSpecifier: &routeAPI.RouteAction_Cluster{
|
||||
Cluster: fmt.Sprintf("cluster_%05d", id),
|
||||
},
|
||||
PrefixRewrite: "/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clusters = append(clusters, cluster)
|
||||
routes = append(routes, route)
|
||||
}
|
||||
|
||||
routes = util.Sort(routes, func(i, j *routeAPI.Route) bool {
|
||||
return i.Match.GetPrefix() > j.Match.GetPrefix()
|
||||
})
|
||||
|
||||
httpFilterConfigType, err := anypb.New(&routerAPI.Router{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filterConfigType, err := anypb.New(&httpConnectionManagerAPI.HttpConnectionManager{
|
||||
StatPrefix: "ingress_http",
|
||||
CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO,
|
||||
RouteSpecifier: &httpConnectionManagerAPI.HttpConnectionManager_RouteConfig{
|
||||
RouteConfig: &routeAPI.RouteConfiguration{
|
||||
Name: "local_route",
|
||||
VirtualHosts: []*routeAPI.VirtualHost{
|
||||
{
|
||||
Name: "local_service",
|
||||
Domains: []string{"*"},
|
||||
Routes: routes,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HttpFilters: []*httpConnectionManagerAPI.HttpFilter{
|
||||
{
|
||||
Name: "envoy.filters.http.routerAPI",
|
||||
ConfigType: &httpConnectionManagerAPI.HttpFilter_TypedConfig{
|
||||
TypedConfig: httpFilterConfigType,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &bootstrapAPI.Bootstrap{
|
||||
Admin: &bootstrapAPI.Admin{
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Address: "127.0.0.1",
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{PortValue: 9901},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
StaticResources: &bootstrapAPI.Bootstrap_StaticResources{
|
||||
Listeners: []*listenerAPI.Listener{
|
||||
{
|
||||
Name: "listener_0",
|
||||
Address: &coreAPI.Address{
|
||||
Address: &coreAPI.Address_SocketAddress{
|
||||
SocketAddress: &coreAPI.SocketAddress{
|
||||
Address: "0.0.0.0",
|
||||
PortSpecifier: &coreAPI.SocketAddress_PortValue{PortValue: shared.ArangoPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
FilterChains: []*listenerAPI.FilterChain{
|
||||
{
|
||||
Filters: []*listenerAPI.Filter{
|
||||
{
|
||||
Name: "envoy.filters.network.httpConnectionManagerAPI",
|
||||
ConfigType: &listenerAPI.Filter_TypedConfig{
|
||||
TypedConfig: filterConfigType,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clusters: clusters,
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -286,10 +286,15 @@ func createArangoSyncArgs(apiObject meta.Object, spec api.DeploymentSpec, group
|
|||
return args
|
||||
}
|
||||
|
||||
func createArangoGatewayArgs(groupSpec api.ServerGroupSpec) []string {
|
||||
args := []string{"--config-path", GatewayConfigFilePath}
|
||||
if len(groupSpec.Args) > 0 {
|
||||
args = append(args, groupSpec.Args...)
|
||||
func createArangoGatewayArgs(input pod.Input, additionalOptions ...k8sutil.OptionPair) []string {
|
||||
options := k8sutil.CreateOptionPairs(64)
|
||||
options.Add("--config-path", GatewayConfigFilePath)
|
||||
|
||||
options.Append(additionalOptions...)
|
||||
|
||||
args := options.Sort().AsSplittedArgs()
|
||||
if len(input.GroupSpec.Args) > 0 {
|
||||
args = append(args, input.GroupSpec.Args...)
|
||||
}
|
||||
|
||||
return args
|
||||
|
@ -297,7 +302,7 @@ func createArangoGatewayArgs(groupSpec api.ServerGroupSpec) []string {
|
|||
|
||||
// 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 {
|
||||
return tolerations.MergeTolerationsIfNotFound(tolerations.CreatePodTolerations(r.context.GetMode(), group), groupSpec.GetTolerations())
|
||||
return tolerations.MergeTolerationsIfNotFound(CreatePodTolerations(r.context.GetMode(), group), groupSpec.GetTolerations())
|
||||
}
|
||||
|
||||
func (r *Resources) RenderPodTemplateForMember(ctx context.Context, acs sutil.ACS, spec api.DeploymentSpec, status api.DeploymentStatus, memberID string, imageInfo api.ImageInfo) (*core.PodTemplateSpec, error) {
|
||||
|
@ -395,16 +400,17 @@ func (r *Resources) RenderPodForMember(ctx context.Context, acs sutil.ACS, spec
|
|||
}
|
||||
|
||||
podCreator = &MemberGatewayPod{
|
||||
podName: podName,
|
||||
groupSpec: groupSpec,
|
||||
spec: spec,
|
||||
group: group,
|
||||
resources: r,
|
||||
imageInfo: imageInfo,
|
||||
arangoMember: *member,
|
||||
apiObject: apiObject,
|
||||
memberStatus: m,
|
||||
cachedStatus: cache,
|
||||
podName: podName,
|
||||
status: m,
|
||||
groupSpec: groupSpec,
|
||||
spec: spec,
|
||||
group: group,
|
||||
resources: r,
|
||||
imageInfo: imageInfo,
|
||||
context: r.context,
|
||||
deploymentStatus: status,
|
||||
arangoMember: *member,
|
||||
cachedStatus: cache,
|
||||
}
|
||||
default:
|
||||
return nil, assertion.InvalidGroupKey.Assert(true, "Unable to render pod for an unknown group: %s", group.AsRole())
|
||||
|
@ -687,7 +693,18 @@ func RenderArangoPod(ctx context.Context, cachedStatus inspectorInterface.Inspec
|
|||
PodAffinity: podCreator.GetPodAffinity(),
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
if profiles, err := podCreator.Profiles(); err != nil {
|
||||
return nil, err
|
||||
} else if len(profiles) > 0 {
|
||||
if err := profiles.RenderOnTemplate(&p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &core.Pod{
|
||||
ObjectMeta: p.ObjectMeta,
|
||||
Spec: p.Spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateArangoPod creates a new Pod with container provided by parameter 'containerCreator'
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
core "k8s.io/api/core/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
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"
|
||||
|
@ -321,7 +322,7 @@ func (m *MemberArangoDPod) AsInput() pod.Input {
|
|||
}
|
||||
}
|
||||
|
||||
func (m *MemberArangoDPod) Init(_ context.Context, _ interfaces.Inspector, pod *core.Pod) error {
|
||||
func (m *MemberArangoDPod) Init(_ context.Context, _ interfaces.Inspector, pod *core.PodTemplateSpec) error {
|
||||
terminationGracePeriodSeconds := int64(math.Ceil(m.groupSpec.GetTerminationGracePeriod(m.group).Seconds()))
|
||||
pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
|
||||
pod.Spec.PriorityClassName = m.groupSpec.PriorityClassName
|
||||
|
@ -409,7 +410,7 @@ func (m *MemberArangoDPod) GetServiceAccountName() string {
|
|||
return m.groupSpec.GetServiceAccountName()
|
||||
}
|
||||
|
||||
func (m *MemberArangoDPod) GetSidecars(pod *core.Pod) error {
|
||||
func (m *MemberArangoDPod) GetSidecars(pod *core.PodTemplateSpec) error {
|
||||
//nolint:staticcheck
|
||||
if m.spec.Metrics.IsEnabled() && m.spec.Metrics.Mode.Get() != api.MetricsModeInternal {
|
||||
var c *core.Container
|
||||
|
@ -595,6 +596,10 @@ func (m *MemberArangoDPod) Annotations() map[string]string {
|
|||
return collection.MergeAnnotations(m.spec.Annotations, m.groupSpec.Annotations)
|
||||
}
|
||||
|
||||
func (m *MemberArangoDPod) Profiles() (schedulerApi.ProfileTemplates, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MemberArangoDPod) Labels() map[string]string {
|
||||
l := collection.ReservedLabels().Filter(collection.MergeAnnotations(m.spec.Labels, m.groupSpec.Labels))
|
||||
|
||||
|
|
|
@ -21,320 +21,35 @@
|
|||
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"
|
||||
GatewayConfigChecksumName = "gateway.yaml-checksum"
|
||||
GatewayConfigFilePath = GatewayVolumeMountDir + GatewayConfigFileName
|
||||
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 {
|
||||
func createGatewayVolumes(input pod.Input) pod.Volumes {
|
||||
volumes := pod.NewVolumes()
|
||||
|
||||
volumes.AddVolume(k8sutil.LifecycleVolume())
|
||||
volumes.AddVolumeMount(k8sutil.LifecycleVolumeMount())
|
||||
|
||||
volumes.AddVolume(k8sutil.CreateVolumeWithConfigMap(GatewayVolumeName, GetGatewayConfigMapName(memberName)))
|
||||
volumes.AddVolume(k8sutil.CreateVolumeWithConfigMap(GatewayVolumeName, GetGatewayConfigMapName(input.ApiObject.GetName())))
|
||||
volumes.AddVolumeMount(GatewayVolumeMount())
|
||||
|
||||
// TLS
|
||||
volumes.Append(pod.TLS(), input)
|
||||
|
||||
return volumes
|
||||
}
|
||||
|
||||
|
|
149
pkg/deployment/resources/pod_creator_gateway_container.go
Normal file
149
pkg/deployment/resources/pod_creator_gateway_container.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
//
|
||||
// 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 (
|
||||
core "k8s.io/api/core/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/pod"
|
||||
"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"
|
||||
)
|
||||
|
||||
var _ interfaces.ContainerCreator = &ArangoGatewayContainer{}
|
||||
|
||||
type ArangoGatewayContainer struct {
|
||||
member *MemberGatewayPod
|
||||
resources *Resources
|
||||
groupSpec api.ServerGroupSpec
|
||||
spec api.DeploymentSpec
|
||||
group api.ServerGroup
|
||||
arangoMember api.ArangoMember
|
||||
imageInfo api.ImageInfo
|
||||
cachedStatus interfaces.Inspector
|
||||
input pod.Input
|
||||
status api.MemberStatus
|
||||
}
|
||||
|
||||
func (a *ArangoGatewayContainer) GetCommand() ([]string, error) {
|
||||
cmd := make([]string, 0, 128)
|
||||
cmd = append(cmd, a.GetExecutor())
|
||||
cmd = append(cmd, createArangoGatewayArgs(a.input)...)
|
||||
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.input).VolumeMounts()
|
||||
}
|
242
pkg/deployment/resources/pod_creator_gateway_pod.go
Normal file
242
pkg/deployment/resources/pod_creator_gateway_pod.go
Normal file
|
@ -0,0 +1,242 @@
|
|||
//
|
||||
// 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"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
schedulerContainerResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container/resources"
|
||||
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/integrations/sidecar"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"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"
|
||||
)
|
||||
|
||||
var _ interfaces.PodCreator = &MemberGatewayPod{}
|
||||
|
||||
type MemberGatewayPod struct {
|
||||
podName string
|
||||
status api.MemberStatus
|
||||
groupSpec api.ServerGroupSpec
|
||||
spec api.DeploymentSpec
|
||||
deploymentStatus api.DeploymentStatus
|
||||
group api.ServerGroup
|
||||
arangoMember api.ArangoMember
|
||||
context Context
|
||||
resources *Resources
|
||||
imageInfo api.ImageInfo
|
||||
cachedStatus interfaces.Inspector
|
||||
}
|
||||
|
||||
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) AsInput() pod.Input {
|
||||
return pod.Input{
|
||||
ApiObject: m.context.GetAPIObject(),
|
||||
Deployment: m.spec,
|
||||
Status: m.deploymentStatus,
|
||||
Group: m.group,
|
||||
GroupSpec: m.groupSpec,
|
||||
Version: m.imageInfo.ArangoDBVersion,
|
||||
Enterprise: m.imageInfo.Enterprise,
|
||||
Member: m.status,
|
||||
ArangoMember: m.arangoMember,
|
||||
}
|
||||
}
|
||||
|
||||
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.status.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.PodTemplateSpec) 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.AsInput()).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{
|
||||
member: m,
|
||||
spec: m.spec,
|
||||
group: m.group,
|
||||
resources: m.resources,
|
||||
imageInfo: m.imageInfo,
|
||||
groupSpec: m.groupSpec,
|
||||
arangoMember: m.arangoMember,
|
||||
cachedStatus: m.cachedStatus,
|
||||
input: m.AsInput(),
|
||||
status: m.status,
|
||||
}
|
||||
}
|
||||
|
||||
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.PodTemplateSpec) 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 {
|
||||
l := collection.ReservedLabels().Filter(collection.MergeAnnotations(m.spec.Labels, m.groupSpec.Labels))
|
||||
|
||||
if m.status.Topology != nil && m.deploymentStatus.Topology.Enabled() && m.deploymentStatus.Topology.ID == m.status.Topology.ID {
|
||||
if l == nil {
|
||||
l = map[string]string{}
|
||||
}
|
||||
|
||||
l[k8sutil.LabelKeyArangoZone] = fmt.Sprintf("%d", m.status.Topology.Zone)
|
||||
l[k8sutil.LabelKeyArangoTopology] = string(m.status.Topology.ID)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (m *MemberGatewayPod) Profiles() (schedulerApi.ProfileTemplates, error) {
|
||||
integration, err := sidecar.NewIntegration(&schedulerContainerResourcesApi.Image{
|
||||
Image: util.NewType(m.resources.context.GetOperatorImage()),
|
||||
}, m.spec.Gateway.GetSidecar(), []string{shared.ServerContainerName})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []*schedulerApi.ProfileTemplate{integration}, nil
|
||||
}
|
|
@ -32,6 +32,7 @@ import (
|
|||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
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"
|
||||
|
@ -266,7 +267,7 @@ func (m *MemberSyncPod) GetServiceAccountName() string {
|
|||
return m.groupSpec.GetServiceAccountName()
|
||||
}
|
||||
|
||||
func (m *MemberSyncPod) GetSidecars(pod *core.Pod) error {
|
||||
func (m *MemberSyncPod) GetSidecars(pod *core.PodTemplateSpec) error {
|
||||
// A sidecar provided by the user
|
||||
sidecars := m.groupSpec.GetSidecars()
|
||||
if len(sidecars) > 0 {
|
||||
|
@ -350,7 +351,7 @@ func (m *MemberSyncPod) GetRestartPolicy() core.RestartPolicy {
|
|||
}
|
||||
|
||||
// Init initializes the arangosync pod.
|
||||
func (m *MemberSyncPod) Init(ctx context.Context, cachedStatus interfaces.Inspector, pod *core.Pod) error {
|
||||
func (m *MemberSyncPod) Init(ctx context.Context, cachedStatus interfaces.Inspector, pod *core.PodTemplateSpec) error {
|
||||
terminationGracePeriodSeconds := int64(math.Ceil(m.groupSpec.GetTerminationGracePeriod(m.group).Seconds()))
|
||||
pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
|
||||
pod.Spec.PriorityClassName = m.groupSpec.PriorityClassName
|
||||
|
@ -517,3 +518,7 @@ func (m *MemberSyncPod) syncHostAlias() *core.HostAlias {
|
|||
|
||||
return &alias
|
||||
}
|
||||
|
||||
func (m *MemberSyncPod) Profiles() (schedulerApi.ProfileTemplates, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
68
pkg/deployment/resources/pod_creator_tolerations.go
Normal file
68
pkg/deployment/resources/pod_creator_tolerations.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// 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 (
|
||||
"time"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/tolerations"
|
||||
)
|
||||
|
||||
// CreatePodTolerations creates a list of tolerations for a pod created for the given group.
|
||||
func CreatePodTolerations(mode api.DeploymentMode, group api.ServerGroup) []core.Toleration {
|
||||
notReadyDur := tolerations.TolerationDuration{Forever: false, TimeSpan: time.Minute}
|
||||
unreachableDur := tolerations.TolerationDuration{Forever: false, TimeSpan: time.Minute}
|
||||
switch group {
|
||||
case api.ServerGroupAgents:
|
||||
notReadyDur.Forever = true
|
||||
unreachableDur.Forever = true
|
||||
case api.ServerGroupCoordinators:
|
||||
notReadyDur.TimeSpan = 15 * time.Second
|
||||
unreachableDur.TimeSpan = 15 * time.Second
|
||||
case api.ServerGroupDBServers:
|
||||
notReadyDur.TimeSpan = 5 * time.Minute
|
||||
unreachableDur.TimeSpan = 5 * time.Minute
|
||||
case api.ServerGroupSingle:
|
||||
if mode == api.DeploymentModeSingle {
|
||||
notReadyDur.Forever = true
|
||||
unreachableDur.Forever = true
|
||||
} else {
|
||||
notReadyDur.TimeSpan = 5 * time.Minute
|
||||
unreachableDur.TimeSpan = 5 * time.Minute
|
||||
}
|
||||
case api.ServerGroupSyncMasters:
|
||||
notReadyDur.TimeSpan = 15 * time.Second
|
||||
unreachableDur.TimeSpan = 15 * time.Second
|
||||
case api.ServerGroupSyncWorkers:
|
||||
notReadyDur.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{tolerations.NewNoExecuteToleration(tolerations.TolerationKeyNodeNotReady, notReadyDur),
|
||||
tolerations.NewNoExecuteToleration(tolerations.TolerationKeyNodeUnreachable, unreachableDur),
|
||||
tolerations.NewNoExecuteToleration(tolerations.TolerationKeyNodeAlphaUnreachable, unreachableDur),
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ func (r *Resources) EnsureLeader(ctx context.Context, cachedStatus inspectorInte
|
|||
selector := k8sutil.LabelsForLeaderMember(deploymentName, group.AsRole(), leaderID)
|
||||
|
||||
if s, ok := cachedStatus.Service().V1().GetSimple(leaderAgentSvcName); ok {
|
||||
if c, err := patcher.ServicePatcher(ctx, cachedStatus.ServicesModInterface().V1(), s, meta.PatchOptions{}, patcher.PatchServiceSelector(selector), patcher.PatchServicePorts(ports)); err != nil {
|
||||
if _, c, err := patcher.Patcher[*core.Service](ctx, cachedStatus.ServicesModInterface().V1(), s, meta.PatchOptions{}, patcher.PatchServiceSelector(selector), patcher.PatchServicePorts(ports)); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if !c {
|
||||
|
|
|
@ -131,7 +131,7 @@ func (r *Resources) EnsureSecrets(ctx context.Context, cachedStatus inspectorInt
|
|||
|
||||
if err := reconcileRequired.ParallelAll(len(members), func(id int) error {
|
||||
switch members[id].Group.Type() {
|
||||
case api.ServerGroupTypeArangoD:
|
||||
case api.ServerGroupTypeArangoD, api.ServerGroupTypeGateway:
|
||||
memberName := members[id].Member.ArangoMemberName(r.context.GetAPIObject().GetName(), members[id].Group)
|
||||
|
||||
member, ok := cachedStatus.ArangoMember().V1().GetSimple(memberName)
|
||||
|
|
|
@ -120,7 +120,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
|
|||
continue
|
||||
} else {
|
||||
|
||||
if changed, err := patcher.ServicePatcher(ctx, svcs, s, meta.PatchOptions{},
|
||||
if _, changed, err := patcher.Patcher[*core.Service](ctx, svcs, s, meta.PatchOptions{},
|
||||
patcher.PatchServicePorts(ports),
|
||||
patcher.PatchServiceSelector(selector),
|
||||
patcher.PatchServicePublishNotReadyAddresses(true),
|
||||
|
@ -176,7 +176,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
|
|||
reconcileRequired.Required()
|
||||
continue
|
||||
} else {
|
||||
if changed, err := patcher.ServicePatcher(ctx, svcs, s, meta.PatchOptions{},
|
||||
if _, changed, err := patcher.Patcher[*core.Service](ctx, svcs, s, meta.PatchOptions{},
|
||||
patcher.PatchServicePorts(ports),
|
||||
patcher.PatchServiceSelector(selector),
|
||||
patcher.PatchServicePublishNotReadyAddresses(false),
|
||||
|
@ -205,7 +205,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
|
|||
log.Str("service", svcName).Debug("Created headless service")
|
||||
}
|
||||
} else {
|
||||
if changed, err := patcher.ServicePatcher(ctx, svcs, s, meta.PatchOptions{}, patcher.PatchServicePorts(headlessPorts), patcher.PatchServiceSelector(headlessSelector)); err != nil {
|
||||
if _, changed, err := patcher.Patcher[*core.Service](ctx, svcs, s, meta.PatchOptions{}, patcher.PatchServicePorts(headlessPorts), patcher.PatchServiceSelector(headlessSelector)); err != nil {
|
||||
log.Err(err).Debug("Failed to patch headless service")
|
||||
return errors.WithStack(err)
|
||||
} else if changed {
|
||||
|
@ -245,7 +245,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if changed, err := patcher.ServicePatcher(ctx, svcs, s, meta.PatchOptions{}, patcher.PatchServiceOnlyPorts(clientServicePorts...), patcher.PatchServiceSelector(clientServiceSelectors)); err != nil {
|
||||
if _, changed, err := patcher.Patcher[*core.Service](ctx, svcs, s, meta.PatchOptions{}, patcher.PatchServiceOnlyPorts(clientServicePorts...), patcher.PatchServiceSelector(clientServiceSelectors)); err != nil {
|
||||
log.Err(err).Debug("Failed to patch database client service")
|
||||
return errors.WithStack(err)
|
||||
} else if changed {
|
||||
|
@ -380,7 +380,7 @@ func (r *Resources) ensureExternalAccessServices(ctx context.Context, cachedStat
|
|||
}
|
||||
}
|
||||
if !createExternalAccessService && !deleteExternalAccessService {
|
||||
if changed, err := patcher.ServicePatcher(ctx, svcs, existing, meta.PatchOptions{},
|
||||
if _, changed, err := patcher.Patcher[*core.Service](ctx, svcs, existing, meta.PatchOptions{},
|
||||
patcher.PatchServiceSelector(eaSelector),
|
||||
patcher.Optional(patcher.PatchServiceOnlyPorts(eaPorts...), owned)); err != nil {
|
||||
log.Err(err).Debug("Failed to patch database client service")
|
||||
|
@ -434,8 +434,8 @@ func (r *Resources) ensureExternalAccessManagedServices(ctx context.Context, cac
|
|||
log := r.log.Str("section", "service-ea").Str("service", eaServiceName)
|
||||
managedServiceNames := spec.GetManagedServiceNames()
|
||||
|
||||
apply := func(svc *core.Service) (bool, error) {
|
||||
return patcher.ServicePatcher(ctx, cachedStatus.ServicesModInterface().V1(), svc, meta.PatchOptions{},
|
||||
apply := func(svc *core.Service) (*core.Service, bool, error) {
|
||||
return patcher.Patcher[*core.Service](ctx, cachedStatus.ServicesModInterface().V1(), svc, meta.PatchOptions{},
|
||||
patcher.PatchServiceSelector(selectors))
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,7 @@ func (r *Resources) ensureExternalAccessManagedServices(ctx context.Context, cac
|
|||
log.Warn("the field \"spec.externalAccess.managedServiceNames\" should be provided for \"managed\" service type")
|
||||
return nil
|
||||
}
|
||||
} else if changed, err := apply(svc); err != nil {
|
||||
} else if _, changed, err := apply(svc); err != nil {
|
||||
return errors.WithMessage(err, "failed to ensure service selector")
|
||||
} else if changed {
|
||||
log.Info("selector applied to the managed service \"%s\"", svc.GetName())
|
||||
|
@ -464,7 +464,7 @@ func (r *Resources) ensureExternalAccessManagedServices(ctx context.Context, cac
|
|||
continue
|
||||
}
|
||||
|
||||
if changed, err := apply(svc); err != nil {
|
||||
if _, changed, err := apply(svc); err != nil {
|
||||
return errors.WithMessage(err, "failed to ensure service selector")
|
||||
} else if changed {
|
||||
log.Info("selector applied to the managed service \"%s\"", svcName)
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
)
|
||||
|
||||
func (h *handler) HandleArangoDeployment(ctx context.Context, item operation.Item, extension *networkingApi.ArangoRoute, status *networkingApi.ArangoRouteStatus) (bool, error) {
|
||||
var name = util.WithDefault(extension.Spec.DeploymentName)
|
||||
var name = util.WithDefault(extension.Spec.Deployment)
|
||||
|
||||
if status.Deployment != nil {
|
||||
name = status.Deployment.GetName()
|
||||
|
|
|
@ -41,7 +41,7 @@ func Test_Handler_Deployment(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -73,7 +73,7 @@ func Test_Handler_MissingDeployment(t *testing.T) {
|
|||
|
||||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test", func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment-missing")
|
||||
obj.Spec.Deployment = util.NewType("deployment-missing")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -105,7 +105,7 @@ func Test_Handler_Deployment_Changed(t *testing.T) {
|
|||
|
||||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test", func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
func (h *handler) HandleArangoDestination(ctx context.Context, item operation.Item, extension *networkingApi.ArangoRoute, status *networkingApi.ArangoRouteStatus, _ *api.ArangoDeployment) (*operator.Condition, bool, error) {
|
||||
func (h *handler) HandleArangoDestination(ctx context.Context, item operation.Item, extension *networkingApi.ArangoRoute, status *networkingApi.ArangoRouteStatus, deployment *api.ArangoDeployment) (*operator.Condition, bool, error) {
|
||||
if dest := extension.Spec.GetDestination(); dest != nil {
|
||||
if svc := dest.GetService(); svc != nil {
|
||||
port := svc.Port
|
||||
|
@ -117,30 +117,56 @@ func (h *handler) HandleArangoDestination(ctx context.Context, item operation.It
|
|||
}, false, nil
|
||||
}
|
||||
|
||||
var targets = networkingApi.ArangoRouteStatusTargets{
|
||||
networkingApi.ArangoRouteStatusTarget{
|
||||
Url: fmt.Sprintf("%s://%s.%s.svc:%d%s", dest.GetSchema().String(), s.GetName(), s.GetNamespace(), destPort, extension.Spec.GetRoute().GetPath()),
|
||||
TLS: networkingApi.ArangoRouteStatusTargetTLS{
|
||||
Insecure: extension.Spec.Destination.GetTLS().GetInsecure(),
|
||||
},
|
||||
},
|
||||
var target networkingApi.ArangoRouteStatusTarget
|
||||
|
||||
target.Path = dest.GetPath()
|
||||
|
||||
if dest.Schema.Get() == networkingApi.ArangoRouteSpecDestinationSchemaHTTPS {
|
||||
target.TLS = &networkingApi.ArangoRouteStatusTargetTLS{
|
||||
Insecure: util.NewType(extension.Spec.Destination.GetTLS().GetInsecure()),
|
||||
}
|
||||
}
|
||||
|
||||
if status.Targets.Hash() == targets.Hash() {
|
||||
if ip := s.Spec.ClusterIP; ip != "" {
|
||||
target.Destinations = networkingApi.ArangoRouteStatusTargetDestinations{
|
||||
networkingApi.ArangoRouteStatusTargetDestination{
|
||||
Host: ip,
|
||||
Port: destPort,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if domain := deployment.Spec.ClusterDomain; domain != nil {
|
||||
target.Destinations = networkingApi.ArangoRouteStatusTargetDestinations{
|
||||
networkingApi.ArangoRouteStatusTargetDestination{
|
||||
Host: fmt.Sprintf("%s.%s.svc.%s", s.GetName(), s.GetNamespace(), *domain),
|
||||
Port: destPort,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
target.Destinations = networkingApi.ArangoRouteStatusTargetDestinations{
|
||||
networkingApi.ArangoRouteStatusTargetDestination{
|
||||
Host: fmt.Sprintf("%s.%s.svc", s.GetName(), s.GetNamespace()),
|
||||
Port: destPort,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if status.Target.Hash() == target.Hash() {
|
||||
return &operator.Condition{
|
||||
Status: true,
|
||||
Reason: "Destination Found",
|
||||
Message: "Destination Found",
|
||||
Hash: targets.Hash(),
|
||||
Hash: target.Hash(),
|
||||
}, false, nil
|
||||
}
|
||||
|
||||
status.Targets = targets
|
||||
status.Target = &target
|
||||
return &operator.Condition{
|
||||
Status: true,
|
||||
Reason: "Destination Found",
|
||||
Message: "Destination Found",
|
||||
Hash: targets.Hash(),
|
||||
Hash: target.Hash(),
|
||||
}, true, nil
|
||||
}
|
||||
}
|
||||
|
@ -154,8 +180,8 @@ func (h *handler) HandleArangoDestination(ctx context.Context, item operation.It
|
|||
|
||||
func (h *handler) HandleArangoDestinationWithTargets(ctx context.Context, item operation.Item, extension *networkingApi.ArangoRoute, status *networkingApi.ArangoRouteStatus, depl *api.ArangoDeployment) (*operator.Condition, bool, error) {
|
||||
c, changed, err := h.HandleArangoDestination(ctx, item, extension, status, depl)
|
||||
if c == nil && !c.Status && status.Targets != nil {
|
||||
status.Targets = nil
|
||||
if c == nil && !c.Status && status.Target != nil {
|
||||
status.Target = nil
|
||||
changed = true
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ func Test_Handler_Destination_Service_Missing(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -81,7 +81,7 @@ func Test_Handler_Destination_Service_Valid(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -114,11 +114,117 @@ func Test_Handler_Destination_Service_Valid(t *testing.T) {
|
|||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition))
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition))
|
||||
|
||||
require.Len(t, extension.Status.Target.RenderURLs(), 1)
|
||||
require.EqualValues(t, "http://deployment.fake.svc:10244/", extension.Status.Target.RenderURLs()[0])
|
||||
|
||||
c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition)
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Targets.Hash())
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_Valid_WithIP(t *testing.T) {
|
||||
// Setup
|
||||
handler := newFakeHandler()
|
||||
|
||||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
Service: &networkingApi.ArangoRouteSpecDestinationService{
|
||||
Object: &sharedApi.Object{
|
||||
Name: "deployment",
|
||||
},
|
||||
Port: util.NewType(intstr.FromInt32(10244)),
|
||||
},
|
||||
}
|
||||
})
|
||||
deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment")
|
||||
svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) {
|
||||
obj.Spec.Ports = []core.ServicePort{
|
||||
{
|
||||
Port: 10244,
|
||||
},
|
||||
}
|
||||
obj.Spec.ClusterIP = "127.0.0.2"
|
||||
})
|
||||
|
||||
refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc)
|
||||
|
||||
// Test
|
||||
require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension)))
|
||||
|
||||
// Refresh
|
||||
refresh(t)
|
||||
|
||||
// Assert
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition))
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition))
|
||||
|
||||
require.Len(t, extension.Status.Target.RenderURLs(), 1)
|
||||
require.EqualValues(t, "http://127.0.0.2:10244/", extension.Status.Target.RenderURLs()[0])
|
||||
|
||||
c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition)
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_Valid_WithPath(t *testing.T) {
|
||||
// Setup
|
||||
handler := newFakeHandler()
|
||||
|
||||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
Service: &networkingApi.ArangoRouteSpecDestinationService{
|
||||
Object: &sharedApi.Object{
|
||||
Name: "deployment",
|
||||
},
|
||||
Port: util.NewType(intstr.FromInt32(10244)),
|
||||
},
|
||||
Path: util.NewType("/test/path/"),
|
||||
}
|
||||
})
|
||||
deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment")
|
||||
svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) {
|
||||
obj.Spec.Ports = []core.ServicePort{
|
||||
{
|
||||
Port: 10244,
|
||||
},
|
||||
}
|
||||
obj.Spec.ClusterIP = "127.0.0.2"
|
||||
})
|
||||
|
||||
refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc)
|
||||
|
||||
// Test
|
||||
require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension)))
|
||||
|
||||
// Refresh
|
||||
refresh(t)
|
||||
|
||||
// Assert
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition))
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition))
|
||||
|
||||
require.Len(t, extension.Status.Target.RenderURLs(), 1)
|
||||
require.EqualValues(t, "http://127.0.0.2:10244/test/path/", extension.Status.Target.RenderURLs()[0])
|
||||
|
||||
c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition)
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_ValidName(t *testing.T) {
|
||||
|
@ -128,7 +234,7 @@ func Test_Handler_Destination_Service_ValidName(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -170,7 +276,7 @@ func Test_Handler_Destination_Service_ValidName(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Targets.Hash())
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_WrongPort(t *testing.T) {
|
||||
|
@ -180,7 +286,7 @@ func Test_Handler_Destination_Service_WrongPort(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -226,7 +332,7 @@ func Test_Handler_Destination_Service_WrongPortName(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -272,7 +378,7 @@ func Test_Handler_Destination_Service_Insecure_Default(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -309,10 +415,9 @@ func Test_Handler_Destination_Service_Insecure_Default(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Targets.Hash())
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
|
||||
require.Len(t, extension.Status.Targets, 1)
|
||||
require.False(t, extension.Status.Targets[0].TLS.Insecure)
|
||||
require.False(t, extension.Status.Target.TLS.IsInsecure())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_Insecure_Nil(t *testing.T) {
|
||||
|
@ -322,7 +427,7 @@ func Test_Handler_Destination_Service_Insecure_Nil(t *testing.T) {
|
|||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -362,20 +467,19 @@ func Test_Handler_Destination_Service_Insecure_Nil(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Targets.Hash())
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
|
||||
require.Len(t, extension.Status.Targets, 1)
|
||||
require.False(t, extension.Status.Targets[0].TLS.Insecure)
|
||||
require.False(t, extension.Status.Target.TLS.IsInsecure())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_Insecure_Override(t *testing.T) {
|
||||
func Test_Handler_Destination_Service_Insecure_HTTPS_Override(t *testing.T) {
|
||||
// Setup
|
||||
handler := newFakeHandler()
|
||||
|
||||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.DeploymentName = util.NewType("deployment")
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
|
@ -388,6 +492,7 @@ func Test_Handler_Destination_Service_Insecure_Override(t *testing.T) {
|
|||
TLS: &networkingApi.ArangoRouteSpecDestinationTLS{
|
||||
Insecure: util.NewType(true),
|
||||
},
|
||||
Schema: util.NewType(networkingApi.ArangoRouteSpecDestinationSchemaHTTPS),
|
||||
}
|
||||
})
|
||||
deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment")
|
||||
|
@ -415,8 +520,60 @@ func Test_Handler_Destination_Service_Insecure_Override(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Targets.Hash())
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
|
||||
require.Len(t, extension.Status.Targets, 1)
|
||||
require.True(t, extension.Status.Targets[0].TLS.Insecure)
|
||||
require.True(t, extension.Status.Target.TLS.IsInsecure())
|
||||
}
|
||||
|
||||
func Test_Handler_Destination_Service_Insecure_HTTP_Override(t *testing.T) {
|
||||
// Setup
|
||||
handler := newFakeHandler()
|
||||
|
||||
// Arrange
|
||||
extension := tests.NewMetaObject[*networkingApi.ArangoRoute](t, tests.FakeNamespace, "test",
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Deployment = util.NewType("deployment")
|
||||
},
|
||||
func(t *testing.T, obj *networkingApi.ArangoRoute) {
|
||||
obj.Spec.Destination = &networkingApi.ArangoRouteSpecDestination{
|
||||
Service: &networkingApi.ArangoRouteSpecDestinationService{
|
||||
Object: &sharedApi.Object{
|
||||
Name: "deployment",
|
||||
},
|
||||
Port: util.NewType(intstr.FromInt32(10244)),
|
||||
},
|
||||
TLS: &networkingApi.ArangoRouteSpecDestinationTLS{
|
||||
Insecure: util.NewType(true),
|
||||
},
|
||||
Schema: util.NewType(networkingApi.ArangoRouteSpecDestinationSchemaHTTP),
|
||||
}
|
||||
})
|
||||
deployment := tests.NewMetaObject[*api.ArangoDeployment](t, tests.FakeNamespace, "deployment")
|
||||
svc := tests.NewMetaObject[*core.Service](t, tests.FakeNamespace, "deployment", func(t *testing.T, obj *core.Service) {
|
||||
obj.Spec.Ports = []core.ServicePort{
|
||||
{
|
||||
Port: 10244,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
refresh := tests.CreateObjects(t, handler.kubeClient, handler.client, &deployment, &extension, &svc)
|
||||
|
||||
// Test
|
||||
require.NoError(t, tests.Handle(handler, tests.NewItem(t, operation.Update, extension)))
|
||||
|
||||
// Refresh
|
||||
refresh(t)
|
||||
|
||||
// Assert
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.DestinationValidCondition))
|
||||
require.True(t, extension.Status.Conditions.IsTrue(networkingApi.ReadyCondition))
|
||||
|
||||
c, ok := extension.Status.Conditions.Get(networkingApi.DestinationValidCondition)
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Reason, "Destination Found")
|
||||
require.EqualValues(t, c.Hash, extension.Status.Target.Hash())
|
||||
|
||||
require.False(t, extension.Status.Target.TLS.IsInsecure())
|
||||
}
|
||||
|
|
59
pkg/integrations/sidecar/core.go
Normal file
59
pkg/integrations/sidecar/core.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// 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 sidecar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
type Core struct {
|
||||
Internal *bool
|
||||
External *bool
|
||||
}
|
||||
|
||||
func (c *Core) GetInternal() bool {
|
||||
if c == nil || c.Internal == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return *c.Internal
|
||||
}
|
||||
|
||||
func (c *Core) GetExternal() bool {
|
||||
if c == nil || c.External == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return *c.External
|
||||
}
|
||||
|
||||
func (c *Core) Args(int Integration) k8sutil.OptionPairs {
|
||||
var options k8sutil.OptionPairs
|
||||
name, ver := int.Name()
|
||||
|
||||
options.Add(fmt.Sprintf("--integration.%s.%s.internal", strings.ToLower(name), strings.ToLower(ver)), c.GetInternal())
|
||||
options.Add(fmt.Sprintf("--integration.%s.%s.external", strings.ToLower(name), strings.ToLower(ver)), c.GetExternal())
|
||||
|
||||
return options
|
||||
}
|
85
pkg/integrations/sidecar/integration.authentication.v1.go
Normal file
85
pkg/integrations/sidecar/integration.authentication.v1.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
//
|
||||
// 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 sidecar
|
||||
|
||||
import (
|
||||
core "k8s.io/api/core/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/pod"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
var _ IntegrationVolumes = IntegrationAuthenticationV1{}
|
||||
|
||||
type IntegrationAuthenticationV1 struct {
|
||||
Core *Core
|
||||
Deployment *api.ArangoDeployment
|
||||
}
|
||||
|
||||
func (i IntegrationAuthenticationV1) Name() (string, string) {
|
||||
return "AUTHENTICATION", "V1"
|
||||
}
|
||||
|
||||
func (i IntegrationAuthenticationV1) Validate() error {
|
||||
if i.Deployment == nil {
|
||||
return errors.Errorf("Deployment is nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i IntegrationAuthenticationV1) Args() (k8sutil.OptionPairs, error) {
|
||||
options := k8sutil.CreateOptionPairs()
|
||||
|
||||
options.Add("--integration.authentication.v1", true)
|
||||
options.Add("--integration.authentication.v1.enabled", i.Deployment.GetAcceptedSpec().IsAuthenticated())
|
||||
options.Add("--integration.authentication.v1.path", shared.ClusterJWTSecretVolumeMountDir)
|
||||
|
||||
options.Merge(i.Core.Args(i))
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func (i IntegrationAuthenticationV1) Volumes() ([]core.Volume, []core.VolumeMount, error) {
|
||||
if i.Deployment.GetAcceptedSpec().IsAuthenticated() {
|
||||
return []core.Volume{
|
||||
{
|
||||
Name: shared.ClusterJWTSecretVolumeName,
|
||||
VolumeSource: core.VolumeSource{
|
||||
Secret: &core.SecretVolumeSource{
|
||||
SecretName: pod.JWTSecretFolder(i.Deployment.GetName()),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, []core.VolumeMount{
|
||||
{
|
||||
Name: shared.ClusterJWTSecretVolumeName,
|
||||
ReadOnly: true,
|
||||
MountPath: shared.ClusterJWTSecretVolumeMountDir,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
}
|
47
pkg/integrations/sidecar/integration.authorization.v1.go
Normal file
47
pkg/integrations/sidecar/integration.authorization.v1.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// 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 sidecar
|
||||
|
||||
import (
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
type IntegrationAuthorizationV0 struct {
|
||||
Core *Core
|
||||
}
|
||||
|
||||
func (i IntegrationAuthorizationV0) Name() (string, string) {
|
||||
return "AUTHORIZATION", "V0"
|
||||
}
|
||||
|
||||
func (i IntegrationAuthorizationV0) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i IntegrationAuthorizationV0) Args() (k8sutil.OptionPairs, error) {
|
||||
options := k8sutil.CreateOptionPairs()
|
||||
|
||||
options.Add("--integration.authorization.v0", true)
|
||||
|
||||
options.Merge(i.Core.Args(i))
|
||||
|
||||
return options, nil
|
||||
}
|
57
pkg/integrations/sidecar/integration.envoy.v3.go
Normal file
57
pkg/integrations/sidecar/integration.envoy.v3.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// 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 sidecar
|
||||
|
||||
import (
|
||||
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/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
type IntegrationEnvoyV3 struct {
|
||||
Core *Core
|
||||
Deployment *api.ArangoDeployment
|
||||
}
|
||||
|
||||
func (i IntegrationEnvoyV3) Name() (string, string) {
|
||||
return "ENVOY", "V3"
|
||||
}
|
||||
|
||||
func (i IntegrationEnvoyV3) Validate() error {
|
||||
if i.Deployment == nil {
|
||||
return errors.Errorf("Deployment is nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i IntegrationEnvoyV3) Args() (k8sutil.OptionPairs, error) {
|
||||
options := k8sutil.CreateOptionPairs()
|
||||
|
||||
options.Add("--integration.authentication.v1", true)
|
||||
options.Add("--integration.authentication.v1.enabled", i.Deployment.GetAcceptedSpec().IsAuthenticated())
|
||||
options.Add("--integration.authentication.v1.path", shared.ClusterJWTSecretVolumeMountDir)
|
||||
|
||||
options.Merge(i.Core.Args(i))
|
||||
|
||||
return options, nil
|
||||
}
|
211
pkg/integrations/sidecar/integration.go
Normal file
211
pkg/integrations/sidecar/integration.go
Normal file
|
@ -0,0 +1,211 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany
|
||||
|
||||
package sidecar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
schedulerContainerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container"
|
||||
schedulerContainerResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/container/resources"
|
||||
schedulerPodApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod"
|
||||
schedulerPodResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1/pod/resources"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/constants"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
const (
|
||||
ContainerName = "integration"
|
||||
ListenPortName = "integration"
|
||||
ListenPortHealthName = "health"
|
||||
)
|
||||
|
||||
func WithIntegrationEnvs(in Integration) ([]core.EnvVar, error) {
|
||||
if v, ok := in.(IntegrationEnvs); ok {
|
||||
return v.Envs()
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type IntegrationEnvs interface {
|
||||
Integration
|
||||
Envs() ([]core.EnvVar, error)
|
||||
}
|
||||
|
||||
func WithIntegrationVolumes(in Integration) ([]core.Volume, []core.VolumeMount, error) {
|
||||
if v, ok := in.(IntegrationVolumes); ok {
|
||||
return v.Volumes()
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
type IntegrationVolumes interface {
|
||||
Integration
|
||||
Volumes() ([]core.Volume, []core.VolumeMount, error)
|
||||
}
|
||||
|
||||
type Integration interface {
|
||||
Name() (string, string)
|
||||
Args() (k8sutil.OptionPairs, error)
|
||||
Validate() error
|
||||
}
|
||||
|
||||
func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *schedulerApi.IntegrationSidecar, coreContainers []string, integrations ...Integration) (*schedulerApi.ProfileTemplate, error) {
|
||||
for _, integration := range integrations {
|
||||
if err := integration.Validate(); err != nil {
|
||||
name, version := integration.Name()
|
||||
|
||||
return nil, errors.Wrapf(err, "Failure in %s/%s", name, version)
|
||||
}
|
||||
}
|
||||
|
||||
// Arguments
|
||||
|
||||
exePath := k8sutil.BinaryPath()
|
||||
lifecycle, err := k8sutil.NewLifecycleFinalizersWithBinary(exePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "NewLifecycleFinalizers failed")
|
||||
}
|
||||
|
||||
options := k8sutil.CreateOptionPairs(64)
|
||||
|
||||
options.Addf("--services.address", "127.0.0.1:%d", integration.GetListenPort())
|
||||
options.Addf("--health.address", "0.0.0.0:%d", integration.GetControllerListenPort())
|
||||
|
||||
// Volumes
|
||||
var volumes []core.Volume
|
||||
var volumeMounts []core.VolumeMount
|
||||
|
||||
// Envs
|
||||
|
||||
var envs = []core.EnvVar{
|
||||
{
|
||||
Name: "INTEGRATION_API_ADDRESS",
|
||||
Value: fmt.Sprintf("127.0.0.1:%d", integration.GetListenPort()),
|
||||
},
|
||||
{
|
||||
Name: "INTEGRATION_SERVICE_ADDRESS",
|
||||
Value: fmt.Sprintf("127.0.0.1:%d", integration.GetListenPort()),
|
||||
},
|
||||
}
|
||||
|
||||
for _, i := range integrations {
|
||||
name, version := i.Name()
|
||||
|
||||
if err := i.Validate(); err != nil {
|
||||
return nil, errors.Wrapf(err, "Failure in %s/%s", name, version)
|
||||
}
|
||||
|
||||
if args, err := i.Args(); err != nil {
|
||||
return nil, errors.Wrapf(err, "Failure in arguments %s/%s", name, version)
|
||||
} else if len(args) > 0 {
|
||||
options.Merge(args)
|
||||
}
|
||||
|
||||
if lvolumes, lvolumeMounts, err := WithIntegrationVolumes(i); err != nil {
|
||||
return nil, errors.Wrapf(err, "Failure in volumes %s/%s", name, version)
|
||||
} else if len(lvolumes) > 0 || len(lvolumeMounts) > 0 {
|
||||
volumes = append(volumes, lvolumes...)
|
||||
volumeMounts = append(volumeMounts, lvolumeMounts...)
|
||||
}
|
||||
|
||||
if lenvs, err := WithIntegrationEnvs(i); err != nil {
|
||||
return nil, errors.Wrapf(err, "Failure in envs %s/%s", name, version)
|
||||
} else if len(lenvs) > 0 {
|
||||
envs = append(envs, lenvs...)
|
||||
}
|
||||
|
||||
envs = append(envs, core.EnvVar{
|
||||
Name: fmt.Sprintf("INTEGRATION_SERVICE_%s_%s", strings.ToUpper(name), strings.ToUpper(version)),
|
||||
Value: fmt.Sprintf("127.0.0.1:%d", integration.GetListenPort()),
|
||||
})
|
||||
}
|
||||
|
||||
c := schedulerContainerApi.Container{
|
||||
Core: &schedulerContainerResourcesApi.Core{
|
||||
Command: append([]string{exePath, "integration"}, options.Sort().AsArgs()...),
|
||||
},
|
||||
Environments: &schedulerContainerResourcesApi.Environments{
|
||||
Env: k8sutil.GetLifecycleEnv(),
|
||||
},
|
||||
Networking: &schedulerContainerResourcesApi.Networking{
|
||||
Ports: []core.ContainerPort{
|
||||
{
|
||||
Name: ListenPortName,
|
||||
ContainerPort: int32(integration.GetListenPort()),
|
||||
Protocol: core.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: ListenPortHealthName,
|
||||
ContainerPort: int32(integration.GetControllerListenPort()),
|
||||
Protocol: core.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: image,
|
||||
|
||||
Lifecycle: &schedulerContainerResourcesApi.Lifecycle{
|
||||
Lifecycle: lifecycle,
|
||||
},
|
||||
|
||||
Probes: &schedulerContainerResourcesApi.Probes{
|
||||
ReadinessProbe: &core.Probe{
|
||||
ProbeHandler: core.ProbeHandler{
|
||||
GRPC: &core.GRPCAction{
|
||||
Port: int32(integration.GetControllerListenPort()),
|
||||
},
|
||||
},
|
||||
InitialDelaySeconds: 1, // Wait 1s before first probe
|
||||
TimeoutSeconds: 2, // Timeout of each probe is 2s
|
||||
PeriodSeconds: 30, // Interval between probes is 30s
|
||||
SuccessThreshold: 1, // Single probe is enough to indicate success
|
||||
FailureThreshold: 2, // Need 2 failed probes to consider a failed state
|
||||
},
|
||||
},
|
||||
|
||||
VolumeMounts: &schedulerContainerResourcesApi.VolumeMounts{
|
||||
VolumeMounts: volumeMounts,
|
||||
},
|
||||
}
|
||||
|
||||
pt := schedulerApi.ProfileTemplate{
|
||||
Container: &schedulerApi.ProfileContainerTemplate{
|
||||
Containers: map[string]schedulerContainerApi.Container{
|
||||
ContainerName: util.TypeOrDefault(k8sutil.CreateDefaultContainerTemplate(image).With(&c).With(integration.GetContainer())),
|
||||
},
|
||||
},
|
||||
Pod: &schedulerPodApi.Pod{
|
||||
Metadata: &schedulerPodResourcesApi.Metadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Volumes: &schedulerPodResourcesApi.Volumes{
|
||||
Volumes: volumes,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, container := range coreContainers {
|
||||
pt.Pod.Metadata.Annotations[fmt.Sprintf("%s/%s", constants.AnnotationShutdownCoreContainer, container)] = constants.AnnotationShutdownCoreContainerModeWait
|
||||
}
|
||||
|
||||
pt.Pod.Metadata.Annotations[fmt.Sprintf("%s/%s", constants.AnnotationShutdownContainer, ContainerName)] = ListenPortHealthName
|
||||
pt.Pod.Metadata.Annotations[constants.AnnotationShutdownManagedContainer] = "true"
|
||||
|
||||
pt.Container.Containers.ExtendContainers(&schedulerContainerApi.Container{
|
||||
Environments: &schedulerContainerResourcesApi.Environments{
|
||||
Env: envs,
|
||||
},
|
||||
}, coreContainers...)
|
||||
|
||||
return &pt, nil
|
||||
}
|
47
pkg/integrations/sidecar/integration.shutdown.v1.go
Normal file
47
pkg/integrations/sidecar/integration.shutdown.v1.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// 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 sidecar
|
||||
|
||||
import (
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
type IntegrationShutdownV1 struct {
|
||||
Core *Core
|
||||
}
|
||||
|
||||
func (i IntegrationShutdownV1) Name() (string, string) {
|
||||
return "SHUTDOWN", "V1"
|
||||
}
|
||||
|
||||
func (i IntegrationShutdownV1) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i IntegrationShutdownV1) Args() (k8sutil.OptionPairs, error) {
|
||||
options := k8sutil.CreateOptionPairs()
|
||||
|
||||
options.Add("--integration.shutdown.v1", true)
|
||||
|
||||
options.Merge(i.Core.Args(i))
|
||||
|
||||
return options, nil
|
||||
}
|
50
pkg/util/dict_test.go
Normal file
50
pkg/util/dict_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// 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 util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testDictDefaultValue[T any](t *testing.T, expected T) {
|
||||
t.Run(reflect.TypeOf(expected).String(), func(t *testing.T) {
|
||||
m := map[string]T{}
|
||||
|
||||
ev, ok := m["missing"]
|
||||
require.False(t, ok)
|
||||
|
||||
require.Equal(t, expected, ev)
|
||||
|
||||
evs := m["missing"]
|
||||
|
||||
require.Equal(t, expected, evs)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Dict_Types(t *testing.T) {
|
||||
testDictDefaultValue[string](t, "")
|
||||
testDictDefaultValue[int](t, 0)
|
||||
testDictDefaultValue[*string](t, nil)
|
||||
testDictDefaultValue[*int](t, nil)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// 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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service"
|
||||
)
|
||||
|
@ -39,11 +40,11 @@ type PodModifier interface {
|
|||
}
|
||||
|
||||
type PodCreator interface {
|
||||
Init(context.Context, Inspector, *core.Pod) error
|
||||
Init(context.Context, Inspector, *core.PodTemplateSpec) error
|
||||
GetName() string
|
||||
GetRole() string
|
||||
GetVolumes() []core.Volume
|
||||
GetSidecars(*core.Pod) error
|
||||
GetSidecars(*core.PodTemplateSpec) error
|
||||
GetInitContainers(cachedStatus Inspector) ([]core.Container, error)
|
||||
GetFinalizers() []string
|
||||
GetTolerations() []core.Toleration
|
||||
|
@ -61,6 +62,8 @@ type PodCreator interface {
|
|||
Annotations() map[string]string
|
||||
Labels() map[string]string
|
||||
|
||||
Profiles() (schedulerApi.ProfileTemplates, error)
|
||||
|
||||
PodModifier
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// 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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -148,6 +148,17 @@ func (o OptionPairs) AsArgs() []string {
|
|||
return s
|
||||
}
|
||||
|
||||
func (o OptionPairs) AsSplittedArgs() []string {
|
||||
s := make([]string, len(o)*2)
|
||||
|
||||
for id, pair := range o {
|
||||
s[id*2] = pair.Key
|
||||
s[id*2+1] = pair.Value
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// OptionPair key value pair builder
|
||||
type OptionPair struct {
|
||||
Key string
|
||||
|
|
40
pkg/util/k8sutil/patcher/config_map.go
Normal file
40
pkg/util/k8sutil/patcher/config_map.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// 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 patcher
|
||||
|
||||
import (
|
||||
core "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
|
||||
)
|
||||
|
||||
func PatchConfigMapData(data map[string]string) Patch[*core.ConfigMap] {
|
||||
return func(in *core.ConfigMap) []patch.Item {
|
||||
if len(data) == len(in.Data) && equality.Semantic.DeepDerivative(data, in.Data) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []patch.Item{
|
||||
patch.ItemReplace(patch.NewPath("data"), data),
|
||||
}
|
||||
}
|
||||
}
|
105
pkg/util/k8sutil/patcher/config_map_test.go
Normal file
105
pkg/util/k8sutil/patcher/config_map_test.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// 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 patcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/tests"
|
||||
)
|
||||
|
||||
func Test_ConfigMap(t *testing.T) {
|
||||
c := tests.NewEmptyInspector(t)
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
require.NoError(t, c.Refresh(context.Background()))
|
||||
|
||||
_, err := c.ConfigMapsModInterface().V1().Create(context.Background(), &core.ConfigMap{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: c.Namespace(),
|
||||
},
|
||||
}, meta.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
require.NoError(t, c.Refresh(context.Background()))
|
||||
|
||||
t.Run("Check", func(t *testing.T) {
|
||||
cm, ok := c.ConfigMap().V1().GetSimple("test")
|
||||
require.True(t, ok)
|
||||
require.Len(t, cm.Data, 0)
|
||||
})
|
||||
|
||||
require.NoError(t, c.Refresh(context.Background()))
|
||||
|
||||
t.Run("Update", func(t *testing.T) {
|
||||
cm, ok := c.ConfigMap().V1().GetSimple("test")
|
||||
require.True(t, ok)
|
||||
uCm, ok, err := Patcher[*core.ConfigMap](context.Background(), c.ConfigMapsModInterface().V1(), cm, meta.PatchOptions{}, PatchConfigMapData(map[string]string{
|
||||
"A": "B",
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
require.NoError(t, c.Refresh(context.Background()))
|
||||
|
||||
cm, ok = c.ConfigMap().V1().GetSimple("test")
|
||||
require.True(t, ok)
|
||||
|
||||
require.Equal(t, map[string]string{
|
||||
"A": "B",
|
||||
}, uCm.Data)
|
||||
|
||||
require.Equal(t, map[string]string{
|
||||
"A": "B",
|
||||
}, cm.Data)
|
||||
})
|
||||
|
||||
t.Run("Reupdate", func(t *testing.T) {
|
||||
cm, ok := c.ConfigMap().V1().GetSimple("test")
|
||||
require.True(t, ok)
|
||||
|
||||
uCm, ok, err := Patcher[*core.ConfigMap](context.Background(), c.ConfigMapsModInterface().V1(), cm, meta.PatchOptions{}, PatchConfigMapData(map[string]string{
|
||||
"A": "B",
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
|
||||
require.NoError(t, c.Refresh(context.Background()))
|
||||
|
||||
cm, ok = c.ConfigMap().V1().GetSimple("test")
|
||||
require.True(t, ok)
|
||||
|
||||
require.Equal(t, map[string]string{
|
||||
"A": "B",
|
||||
}, uCm.Data)
|
||||
|
||||
require.Equal(t, map[string]string{
|
||||
"A": "B",
|
||||
}, cm.Data)
|
||||
})
|
||||
}
|
89
pkg/util/k8sutil/patcher/patcher.go
Normal file
89
pkg/util/k8sutil/patcher/patcher.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// 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 patcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/globals"
|
||||
)
|
||||
|
||||
type Patch[T meta.Object] func(in T) []patch.Item
|
||||
|
||||
type Client[T meta.Object] interface {
|
||||
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts meta.PatchOptions, subresources ...string) (result T, err error)
|
||||
}
|
||||
|
||||
func Patcher[T meta.Object](ctx context.Context, client Client[T], in T, opts meta.PatchOptions, functions ...Patch[T]) (T, bool, error) {
|
||||
if v := reflect.ValueOf(in); !v.IsValid() || v.IsZero() {
|
||||
return util.Default[T](), false, nil
|
||||
}
|
||||
|
||||
if in.GetName() == "" {
|
||||
return util.Default[T](), false, nil
|
||||
}
|
||||
|
||||
var items []patch.Item
|
||||
|
||||
for id := range functions {
|
||||
if f := functions[id]; f != nil {
|
||||
items = append(items, f(in)...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
return in, false, nil
|
||||
}
|
||||
|
||||
data, err := patch.NewPatch(items...).Marshal()
|
||||
if err != nil {
|
||||
return util.Default[T](), false, err
|
||||
}
|
||||
|
||||
nctx, c := globals.GetGlobals().Timeouts().Kubernetes().WithTimeout(ctx)
|
||||
defer c()
|
||||
|
||||
if obj, err := client.Patch(nctx, in.GetName(), types.JSONPatchType, data, opts); err != nil {
|
||||
return util.Default[T](), false, err
|
||||
} else {
|
||||
return obj, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
func Optional[T meta.Object](p Patch[T], enabled bool) Patch[T] {
|
||||
return func(in T) []patch.Item {
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
return p(in)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// 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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -21,57 +21,13 @@
|
|||
package patcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/globals"
|
||||
v1 "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service/v1"
|
||||
)
|
||||
|
||||
type ServicePatch func(in *core.Service) []patch.Item
|
||||
|
||||
func ServicePatcher(ctx context.Context, client v1.ModInterface, in *core.Service, opts meta.PatchOptions, functions ...ServicePatch) (bool, error) {
|
||||
if in == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if in.GetName() == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var items []patch.Item
|
||||
|
||||
for id := range functions {
|
||||
if f := functions[id]; f != nil {
|
||||
items = append(items, f(in)...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
data, err := patch.NewPatch(items...).Marshal()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
nctx, c := globals.GetGlobals().Timeouts().Kubernetes().WithTimeout(ctx)
|
||||
defer c()
|
||||
|
||||
if _, err := client.Patch(nctx, in.GetName(), types.JSONPatchType, data, opts); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func PatchServicePorts(ports []core.ServicePort) ServicePatch {
|
||||
func PatchServicePorts(ports []core.ServicePort) Patch[*core.Service] {
|
||||
return func(in *core.Service) []patch.Item {
|
||||
if len(ports) == len(in.Spec.Ports) && equality.Semantic.DeepDerivative(ports, in.Spec.Ports) {
|
||||
return nil
|
||||
|
@ -83,21 +39,7 @@ func PatchServicePorts(ports []core.ServicePort) ServicePatch {
|
|||
}
|
||||
}
|
||||
|
||||
func Optional(p ServicePatch, enabled bool) ServicePatch {
|
||||
return func(in *core.Service) []patch.Item {
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
return p(in)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func PatchServiceOnlyPorts(ports ...core.ServicePort) ServicePatch {
|
||||
func PatchServiceOnlyPorts(ports ...core.ServicePort) Patch[*core.Service] {
|
||||
return func(in *core.Service) []patch.Item {
|
||||
psvc := in.Spec.DeepCopy()
|
||||
cp := psvc.Ports
|
||||
|
@ -149,7 +91,7 @@ func PatchServiceOnlyPorts(ports ...core.ServicePort) ServicePatch {
|
|||
}
|
||||
}
|
||||
|
||||
func PatchServiceSelector(selector map[string]string) ServicePatch {
|
||||
func PatchServiceSelector(selector map[string]string) Patch[*core.Service] {
|
||||
return func(in *core.Service) []patch.Item {
|
||||
if in.Spec.Selector != nil && equality.Semantic.DeepEqual(in.Spec.Selector, selector) {
|
||||
return nil
|
||||
|
@ -161,7 +103,7 @@ func PatchServiceSelector(selector map[string]string) ServicePatch {
|
|||
}
|
||||
}
|
||||
|
||||
func PatchServiceType(t core.ServiceType) ServicePatch {
|
||||
func PatchServiceType(t core.ServiceType) Patch[*core.Service] {
|
||||
return func(in *core.Service) []patch.Item {
|
||||
if in.Spec.Type == t {
|
||||
return nil
|
||||
|
@ -173,7 +115,7 @@ func PatchServiceType(t core.ServiceType) ServicePatch {
|
|||
}
|
||||
}
|
||||
|
||||
func PatchServicePublishNotReadyAddresses(publishNotReadyAddresses bool) ServicePatch {
|
||||
func PatchServicePublishNotReadyAddresses(publishNotReadyAddresses bool) Patch[*core.Service] {
|
||||
return func(in *core.Service) []patch.Item {
|
||||
if in.Spec.PublishNotReadyAddresses == publishNotReadyAddresses {
|
||||
return nil
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// 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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -53,7 +53,7 @@ func Test_Service(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.False(t, svc.Spec.PublishNotReadyAddresses)
|
||||
|
||||
changed, err := ServicePatcher(context.Background(), c.ServicesModInterface().V1(), svc, meta.PatchOptions{}, PatchServicePublishNotReadyAddresses(true))
|
||||
_, changed, err := Patcher[*core.Service](context.Background(), c.ServicesModInterface().V1(), svc, meta.PatchOptions{}, PatchServicePublishNotReadyAddresses(true))
|
||||
require.NoError(t, err)
|
||||
require.True(t, changed)
|
||||
|
||||
|
@ -69,7 +69,7 @@ func Test_Service(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.True(t, svc.Spec.PublishNotReadyAddresses)
|
||||
|
||||
changed, err := ServicePatcher(context.Background(), c.ServicesModInterface().V1(), svc, meta.PatchOptions{}, PatchServicePublishNotReadyAddresses(true))
|
||||
_, changed, err := Patcher[*core.Service](context.Background(), c.ServicesModInterface().V1(), svc, meta.PatchOptions{}, PatchServicePublishNotReadyAddresses(true))
|
||||
require.NoError(t, err)
|
||||
require.False(t, changed)
|
||||
|
||||
|
@ -85,7 +85,7 @@ func Test_Service(t *testing.T) {
|
|||
require.True(t, ok)
|
||||
require.True(t, svc.Spec.PublishNotReadyAddresses)
|
||||
|
||||
changed, err := ServicePatcher(context.Background(), c.ServicesModInterface().V1(), svc, meta.PatchOptions{}, PatchServicePublishNotReadyAddresses(false))
|
||||
_, changed, err := Patcher[*core.Service](context.Background(), c.ServicesModInterface().V1(), svc, meta.PatchOptions{}, PatchServicePublishNotReadyAddresses(false))
|
||||
require.NoError(t, err)
|
||||
require.True(t, changed)
|
||||
|
||||
|
|
|
@ -560,10 +560,10 @@ func NewContainer(containerCreator interfaces.ContainerCreator) (core.Container,
|
|||
}
|
||||
|
||||
// NewPod creates a basic Pod for given settings.
|
||||
func NewPod(deploymentName, role, id, podName string, podCreator interfaces.PodCreator) core.Pod {
|
||||
func NewPod(deploymentName, role, id, podName string, podCreator interfaces.PodCreator) core.PodTemplateSpec {
|
||||
|
||||
hostname := shared.CreatePodHostName(deploymentName, role, id)
|
||||
p := core.Pod{
|
||||
p := core.PodTemplateSpec{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: podName,
|
||||
Labels: LabelsForMember(deploymentName, role, id),
|
||||
|
|
|
@ -82,7 +82,7 @@ func CreateExporterService(ctx context.Context, cachedStatus inspector.Inspector
|
|||
svcName := CreateExporterClientServiceName(deploymentName)
|
||||
|
||||
if svc, exists := cachedStatus.Service().V1().GetSimple(svcName); exists {
|
||||
if changed, err := patcher.ServicePatcher(ctx, cachedStatus.ServicesModInterface().V1(), svc, meta.PatchOptions{}, patcher.PatchServiceSelector(selectors), patcher.PatchServicePorts(ports)); err != nil {
|
||||
if _, changed, err := patcher.Patcher[*core.Service](ctx, cachedStatus.ServicesModInterface().V1(), svc, meta.PatchOptions{}, patcher.PatchServiceSelector(selectors), patcher.PatchServicePorts(ports)); err != nil {
|
||||
return "", false, err
|
||||
} else {
|
||||
return svcName, changed, nil
|
||||
|
|
|
@ -24,8 +24,6 @@ import (
|
|||
"time"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -105,41 +103,3 @@ func AddTolerationIfNotFound(source []core.Toleration, toAdd core.Toleration) []
|
|||
|
||||
return append(source, toAdd)
|
||||
}
|
||||
|
||||
// CreatePodTolerations creates a list of tolerations for a pod created for the given group.
|
||||
func CreatePodTolerations(mode api.DeploymentMode, group api.ServerGroup) []core.Toleration {
|
||||
notReadyDur := TolerationDuration{Forever: false, TimeSpan: time.Minute}
|
||||
unreachableDur := TolerationDuration{Forever: false, TimeSpan: time.Minute}
|
||||
switch group {
|
||||
case api.ServerGroupAgents:
|
||||
notReadyDur.Forever = true
|
||||
unreachableDur.Forever = true
|
||||
case api.ServerGroupCoordinators:
|
||||
notReadyDur.TimeSpan = 15 * time.Second
|
||||
unreachableDur.TimeSpan = 15 * time.Second
|
||||
case api.ServerGroupDBServers:
|
||||
notReadyDur.TimeSpan = 5 * time.Minute
|
||||
unreachableDur.TimeSpan = 5 * time.Minute
|
||||
case api.ServerGroupSingle:
|
||||
if mode == api.DeploymentModeSingle {
|
||||
notReadyDur.Forever = true
|
||||
unreachableDur.Forever = true
|
||||
} else {
|
||||
notReadyDur.TimeSpan = 5 * time.Minute
|
||||
unreachableDur.TimeSpan = 5 * time.Minute
|
||||
}
|
||||
case api.ServerGroupSyncMasters:
|
||||
notReadyDur.TimeSpan = 15 * time.Second
|
||||
unreachableDur.TimeSpan = 15 * time.Second
|
||||
case api.ServerGroupSyncWorkers:
|
||||
notReadyDur.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),
|
||||
NewNoExecuteToleration(TolerationKeyNodeUnreachable, unreachableDur),
|
||||
NewNoExecuteToleration(TolerationKeyNodeAlphaUnreachable, unreachableDur),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue