mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
[Feature] [Gateway] SNI and Authz support (#1714)
This commit is contained in:
parent
efbbc79439
commit
db5406e868
47 changed files with 623 additions and 280 deletions
|
@ -21,6 +21,7 @@
|
||||||
- (Bugfix) Fix race condition in ArangoBackup
|
- (Bugfix) Fix race condition in ArangoBackup
|
||||||
- (Feature) Improve Gateway Config gen
|
- (Feature) Improve Gateway Config gen
|
||||||
- (Feature) Integration Service TLS
|
- (Feature) Integration Service TLS
|
||||||
|
- (Feature) (Gateway) SNI and Authz support
|
||||||
|
|
||||||
## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23)
|
## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23)
|
||||||
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries
|
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries
|
||||||
|
|
|
@ -7,7 +7,9 @@ FROM ${ENVOY_IMAGE} AS envoy
|
||||||
|
|
||||||
FROM ${IMAGE} AS base
|
FROM ${IMAGE} AS base
|
||||||
|
|
||||||
RUN apt-get update && apt-get upgrade -y && apt-get clean
|
ARG BUILD_SKIP_UPDATE=false
|
||||||
|
ENV BUILD_SKIP_UPDATE=${BUILD_SKIP_UPDATE}
|
||||||
|
RUN if [ X"${BUILD_SKIP_UPDATE}" = X"true" ]; then echo "Update skipped!"; else apt-get update && apt-get upgrade -y && apt-get clean; fi
|
||||||
|
|
||||||
FROM base
|
FROM base
|
||||||
|
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -121,6 +121,8 @@ ifndef LOCALONLY
|
||||||
PUSHIMAGES := 1
|
PUSHIMAGES := 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
BUILD_SKIP_UPDATE ?= false
|
||||||
|
|
||||||
ifdef IMAGETAG
|
ifdef IMAGETAG
|
||||||
IMAGESUFFIX := :$(IMAGETAG)
|
IMAGESUFFIX := :$(IMAGETAG)
|
||||||
else
|
else
|
||||||
|
@ -272,7 +274,7 @@ NON_EE_SOURCES := $(shell $(NON_EE_SOURCES_QUERY))
|
||||||
|
|
||||||
YAML_EXCLUDE_DIRS := vendor .gobuild deps tools pkg/generated/clientset pkg/generated/informers pkg/generated/listers \
|
YAML_EXCLUDE_DIRS := vendor .gobuild deps tools pkg/generated/clientset pkg/generated/informers pkg/generated/listers \
|
||||||
chart/kube-arangodb/templates chart/kube-arangodb-arm64/templates chart/kube-arangodb-enterprise/templates chart/kube-arangodb-enterprise-arm64/templates \
|
chart/kube-arangodb/templates chart/kube-arangodb-arm64/templates chart/kube-arangodb-enterprise/templates chart/kube-arangodb-enterprise-arm64/templates \
|
||||||
chart/kube-arangodb-crd/templates chart/arangodb-ingress-proxy/templates
|
chart/kube-arangodb-crd/templates
|
||||||
YAML_EXCLUDE_FILES :=
|
YAML_EXCLUDE_FILES :=
|
||||||
YAML_QUERY := find ./ -type f -name '*.yaml' $(foreach EXCLUDE_DIR,$(YAML_EXCLUDE_DIRS), ! -path "*/$(EXCLUDE_DIR)/*") $(foreach EXCLUDE_FILE,$(YAML_EXCLUDE_FILES), ! -path "*/$(EXCLUDE_FILE)")
|
YAML_QUERY := find ./ -type f -name '*.yaml' $(foreach EXCLUDE_DIR,$(YAML_EXCLUDE_DIRS), ! -path "*/$(EXCLUDE_DIR)/*") $(foreach EXCLUDE_FILE,$(YAML_EXCLUDE_FILES), ! -path "*/$(EXCLUDE_FILE)")
|
||||||
YAMLS := $(shell $(YAML_QUERY))
|
YAMLS := $(shell $(YAML_QUERY))
|
||||||
|
@ -478,11 +480,11 @@ $(BIN): $(VBIN_LINUX_AMD64) $(VBIN_OPS_LINUX_AMD64) $(VBIN_INT_LINUX_AMD64)
|
||||||
docker: clean check-vars $(VBIN_LINUX_AMD64) $(VBIN_LINUX_ARM64)
|
docker: clean check-vars $(VBIN_LINUX_AMD64) $(VBIN_LINUX_ARM64)
|
||||||
ifdef PUSHIMAGES
|
ifdef PUSHIMAGES
|
||||||
docker buildx build --no-cache -f $(DOCKERFILE) --build-arg GOVERSION=$(GOVERSION) --build-arg DISTRIBUTION=$(DISTRIBUTION) \
|
docker buildx build --no-cache -f $(DOCKERFILE) --build-arg GOVERSION=$(GOVERSION) --build-arg DISTRIBUTION=$(DISTRIBUTION) \
|
||||||
--build-arg "VERSION=${VERSION_MAJOR_MINOR_PATCH}" --build-arg "RELEASE_MODE=$(RELEASE_MODE)" \
|
--build-arg "VERSION=${VERSION_MAJOR_MINOR_PATCH}" --build-arg "RELEASE_MODE=$(RELEASE_MODE)" --build-arg "BUILD_SKIP_UPDATE=${BUILD_SKIP_UPDATE}" \
|
||||||
--platform linux/amd64,linux/arm64 --push -t $(OPERATORIMAGE) .
|
--platform linux/amd64,linux/arm64 --push -t $(OPERATORIMAGE) .
|
||||||
else
|
else
|
||||||
docker buildx build --no-cache -f $(DOCKERFILE) --build-arg GOVERSION=$(GOVERSION) --build-arg DISTRIBUTION=$(DISTRIBUTION) \
|
docker buildx build --no-cache -f $(DOCKERFILE) --build-arg GOVERSION=$(GOVERSION) --build-arg DISTRIBUTION=$(DISTRIBUTION) \
|
||||||
--build-arg "VERSION=${VERSION_MAJOR_MINOR_PATCH}" --build-arg "RELEASE_MODE=$(RELEASE_MODE)" \
|
--build-arg "VERSION=${VERSION_MAJOR_MINOR_PATCH}" --build-arg "RELEASE_MODE=$(RELEASE_MODE)" --build-arg "BUILD_SKIP_UPDATE=${BUILD_SKIP_UPDATE}" \
|
||||||
--platform linux/amd64,linux/arm64 -t $(OPERATORIMAGE) .
|
--platform linux/amd64,linux/arm64 -t $(OPERATORIMAGE) .
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -802,6 +804,7 @@ set-typed-api-version/%:
|
||||||
@grep -rHn "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/$*/v[A-Za-z0-9]\+" \
|
@grep -rHn "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned/typed/$*/v[A-Za-z0-9]\+" \
|
||||||
"$(ROOT)/pkg/deployment/" \
|
"$(ROOT)/pkg/deployment/" \
|
||||||
"$(ROOT)/pkg/replication/" \
|
"$(ROOT)/pkg/replication/" \
|
||||||
|
"$(ROOT)/pkg/integrations/" \
|
||||||
"$(ROOT)/pkg/operator/" \
|
"$(ROOT)/pkg/operator/" \
|
||||||
"$(ROOT)/pkg/operatorV2/" \
|
"$(ROOT)/pkg/operatorV2/" \
|
||||||
"$(ROOT)/pkg/server/" \
|
"$(ROOT)/pkg/server/" \
|
||||||
|
@ -818,6 +821,7 @@ set-api-version/%:
|
||||||
@grep -rHn "github.com/arangodb/kube-arangodb/pkg/apis/$*/v[A-Za-z0-9]\+" \
|
@grep -rHn "github.com/arangodb/kube-arangodb/pkg/apis/$*/v[A-Za-z0-9]\+" \
|
||||||
"$(ROOT)/pkg/deployment/" \
|
"$(ROOT)/pkg/deployment/" \
|
||||||
"$(ROOT)/pkg/replication/" \
|
"$(ROOT)/pkg/replication/" \
|
||||||
|
"$(ROOT)/pkg/integrations/" \
|
||||||
"$(ROOT)/pkg/operator/" \
|
"$(ROOT)/pkg/operator/" \
|
||||||
"$(ROOT)/pkg/operatorV2/" \
|
"$(ROOT)/pkg/operatorV2/" \
|
||||||
"$(ROOT)/pkg/server/" \
|
"$(ROOT)/pkg/server/" \
|
||||||
|
@ -831,6 +835,7 @@ set-api-version/%:
|
||||||
@grep -rHn "DatabaseV[A-Za-z0-9]\+()" \
|
@grep -rHn "DatabaseV[A-Za-z0-9]\+()" \
|
||||||
"$(ROOT)/pkg/deployment/" \
|
"$(ROOT)/pkg/deployment/" \
|
||||||
"$(ROOT)/pkg/replication/" \
|
"$(ROOT)/pkg/replication/" \
|
||||||
|
"$(ROOT)/pkg/integrations/" \
|
||||||
"$(ROOT)/pkg/operator/" \
|
"$(ROOT)/pkg/operator/" \
|
||||||
"$(ROOT)/pkg/operatorV2/" \
|
"$(ROOT)/pkg/operatorV2/" \
|
||||||
"$(ROOT)/pkg/server/" \
|
"$(ROOT)/pkg/server/" \
|
||||||
|
@ -844,6 +849,7 @@ set-api-version/%:
|
||||||
@grep -rHn "ReplicationV[A-Za-z0-9]\+()" \
|
@grep -rHn "ReplicationV[A-Za-z0-9]\+()" \
|
||||||
"$(ROOT)/pkg/deployment/" \
|
"$(ROOT)/pkg/deployment/" \
|
||||||
"$(ROOT)/pkg/replication/" \
|
"$(ROOT)/pkg/replication/" \
|
||||||
|
"$(ROOT)/pkg/integrations/" \
|
||||||
"$(ROOT)/pkg/operator/" \
|
"$(ROOT)/pkg/operator/" \
|
||||||
"$(ROOT)/pkg/operatorV2/" \
|
"$(ROOT)/pkg/operatorV2/" \
|
||||||
"$(ROOT)/pkg/server/" \
|
"$(ROOT)/pkg/server/" \
|
||||||
|
|
|
@ -182,7 +182,7 @@ Flags:
|
||||||
--kubernetes.max-batch-size int Size of batch during objects read (default 256)
|
--kubernetes.max-batch-size int Size of batch during objects read (default 256)
|
||||||
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
||||||
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
||||||
--log.level stringArray Set log levels in format <level> or <logger>=<level>. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, http, inspector, integration-config-v1, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, pod_compare, root, root-event-recorder, server, server-authentication (default [info])
|
--log.level stringArray Set log levels in format <level> or <logger>=<level>. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, http, inspector, integration-config-v1, integration-envoy-auth-v3, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, pod_compare, root, root-event-recorder, server, server-authentication (default [info])
|
||||||
--log.sampling If true, operator will try to minimize duplication of logging events (default true)
|
--log.sampling If true, operator will try to minimize duplication of logging events (default true)
|
||||||
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
||||||
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
description: ArangoDB Ingress Proxy
|
|
||||||
name: arangodb-ingress-proxy
|
|
||||||
tillerVersion: '>2.7'
|
|
||||||
version: 1.0.0
|
|
|
@ -1,15 +0,0 @@
|
||||||
Copyright 2016-2022 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
|
|
|
@ -1,20 +0,0 @@
|
||||||
# Introduction
|
|
||||||
|
|
||||||
Kubernetes ArangoDB Ingress for custom certificates.
|
|
||||||
|
|
||||||
ArangoDB supports more than only HTTP protocol, so simple Ingress is not enough.
|
|
||||||
|
|
||||||
## Before
|
|
||||||
|
|
||||||
Before Ingress proxy will be installed certificate secret needs to be created:
|
|
||||||
|
|
||||||
```
|
|
||||||
kubectl -n <deployment namespace> create secret tls <secret name> --cert <path to cert> --key <path to key>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To install Ingress:
|
|
||||||
```
|
|
||||||
helm install --name <my ingress name> --namespace <deployment namespace> <path to kube-arangodb repository>/chart/arangodb-ingress-proxy --set replicas=2 --set tls=TLS Secret name> --set deployment=<ArangoDeployment name>
|
|
||||||
```
|
|
|
@ -1,3 +0,0 @@
|
||||||
Your LB is ready!
|
|
||||||
|
|
||||||
Get LoadBalancer IP using `kubectl --namespace "{{ .Release.Namespace }}" get svc "{{ template "arangodb-ingress-proxy.name" . }}" -o jsonpath="{.status.loadBalancer.ingress[0].ip}"`
|
|
|
@ -1,15 +0,0 @@
|
||||||
{{/* vim: set filetype=mustache: */}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Expand the name of the chart.
|
|
||||||
*/}}
|
|
||||||
{{- define "arangodb-ingress-proxy.name" -}}
|
|
||||||
{{- printf "%s" .Chart.Name | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Expand the name of the release.
|
|
||||||
*/}}
|
|
||||||
{{- define "arangodb-ingress-proxy.releaseName" -}}
|
|
||||||
{{- printf "%s" .Release.Name | trunc 63 | trimSuffix "-" -}}
|
|
||||||
{{- end -}}
|
|
|
@ -1,46 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
data:
|
|
||||||
config: |
|
|
||||||
user nginx;
|
|
||||||
worker_processes 1;
|
|
||||||
|
|
||||||
error_log /dev/stdout info;
|
|
||||||
|
|
||||||
pid /var/run/nginx.pid;
|
|
||||||
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream {
|
|
||||||
log_format basic '$remote_addr [$time_local] '
|
|
||||||
'$protocol $status $bytes_sent $bytes_received '
|
|
||||||
'$session_time';
|
|
||||||
access_log /dev/stdout basic;
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 8529 ssl;
|
|
||||||
proxy_pass {{ required "Arango Deployment name needs to be provided!" .Values.deployment }}:8529;
|
|
||||||
|
|
||||||
proxy_ssl on;
|
|
||||||
|
|
||||||
ssl_certificate /etc/nginx/local-tls/tls.crt;
|
|
||||||
ssl_certificate_key /etc/nginx/local-tls/tls.key;
|
|
||||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
|
||||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
||||||
ssl_session_timeout 4h;
|
|
||||||
ssl_handshake_timeout 30s;
|
|
||||||
proxy_timeout 6h;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
spec:
|
|
||||||
replicas: {{ .Values.replicas }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
spec:
|
|
||||||
affinity:
|
|
||||||
nodeAffinity:
|
|
||||||
requiredDuringSchedulingIgnoredDuringExecution:
|
|
||||||
nodeSelectorTerms:
|
|
||||||
- matchExpressions:
|
|
||||||
- key: kubernetes.io/arch
|
|
||||||
operator: In
|
|
||||||
values:
|
|
||||||
- amd64
|
|
||||||
podAntiAffinity:
|
|
||||||
preferredDuringSchedulingIgnoredDuringExecution:
|
|
||||||
- weight: 100
|
|
||||||
podAffinityTerm:
|
|
||||||
topologyKey: "kubernetes.io/hostname"
|
|
||||||
labelSelector:
|
|
||||||
matchExpressions:
|
|
||||||
- key: app.kubernetes.io/name
|
|
||||||
operator: In
|
|
||||||
values:
|
|
||||||
- {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
imagePullPolicy: {{ .Values.imagePullPolicy }}
|
|
||||||
image: {{ .Values.image }}
|
|
||||||
ports:
|
|
||||||
- name: nginx
|
|
||||||
containerPort: 8529
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /etc/nginx/nginx.conf
|
|
||||||
name: config
|
|
||||||
subPath: config
|
|
||||||
- mountPath: /etc/nginx/local-tls
|
|
||||||
name: tls
|
|
||||||
volumes:
|
|
||||||
- name: config
|
|
||||||
configMap:
|
|
||||||
name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
- name: tls
|
|
||||||
secret:
|
|
||||||
secretName: {{ required "TLS certificate need to be provided!" .Values.tls }}
|
|
|
@ -1,23 +0,0 @@
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
- name: server
|
|
||||||
port: 8529
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: 8529
|
|
||||||
selector:
|
|
||||||
app.kubernetes.io/name: {{ template "arangodb-ingress-proxy.name" . }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
release: {{ .Release.Name }}
|
|
||||||
type: LoadBalancer
|
|
|
@ -1,3 +0,0 @@
|
||||||
replicas: 2
|
|
||||||
imagePullPolicy: Always
|
|
||||||
image: nginx:1.16.1-alpine
|
|
|
@ -80,7 +80,7 @@ Flags:
|
||||||
--kubernetes.max-batch-size int Size of batch during objects read (default 256)
|
--kubernetes.max-batch-size int Size of batch during objects read (default 256)
|
||||||
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
||||||
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
||||||
--log.level stringArray Set log levels in format <level> or <logger>=<level>. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, http, inspector, integration-config-v1, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, pod_compare, root, root-event-recorder, server, server-authentication (default [info])
|
--log.level stringArray Set log levels in format <level> or <logger>=<level>. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, http, inspector, integration-config-v1, integration-envoy-auth-v3, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, pod_compare, root, root-event-recorder, server, server-authentication (default [info])
|
||||||
--log.sampling If true, operator will try to minimize duplication of logging events (default true)
|
--log.sampling If true, operator will try to minimize duplication of logging events (default true)
|
||||||
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
||||||
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
||||||
|
|
|
@ -53,5 +53,10 @@ func (i *impl) Register(registrar *grpc.Server) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *impl) Check(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest) (*pbEnvoyAuthV3.CheckResponse, error) {
|
func (i *impl) Check(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest) (*pbEnvoyAuthV3.CheckResponse, error) {
|
||||||
return &pbEnvoyAuthV3.CheckResponse{}, nil
|
logger.Info("Request Received")
|
||||||
|
return &pbEnvoyAuthV3.CheckResponse{
|
||||||
|
HttpResponse: &pbEnvoyAuthV3.CheckResponse_OkResponse{
|
||||||
|
OkResponse: &pbEnvoyAuthV3.OkHttpResponse{},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
25
integrations/envoy/auth/v3/logger.go
Normal file
25
integrations/envoy/auth/v3/logger.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// 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 v3
|
||||||
|
|
||||||
|
import "github.com/arangodb/kube-arangodb/pkg/logging"
|
||||||
|
|
||||||
|
var logger = logging.Global().RegisterAndGetLogger("integration-envoy-auth-v3", logging.Info)
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/svc"
|
"github.com/arangodb/kube-arangodb/pkg/util/svc"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util/tests"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/tests/tgrpc"
|
"github.com/arangodb/kube-arangodb/pkg/util/tests/tgrpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ func Test_AllowAll(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, resp.Validate())
|
require.NoError(t, resp.Validate())
|
||||||
require.Nil(t, resp.Status)
|
require.Nil(t, resp.Status)
|
||||||
require.Nil(t, resp.HttpResponse)
|
require.NotNil(t, resp.HttpResponse)
|
||||||
|
require.NotNil(t, tests.CastAs[*pbEnvoyAuthV3.CheckResponse_OkResponse](t, resp.GetHttpResponse()).OkResponse)
|
||||||
require.Nil(t, resp.DynamicMetadata)
|
require.Nil(t, resp.DynamicMetadata)
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,19 +180,23 @@ func ValidateRequiredInterfacePath[T ValidateInterface](path string, in T) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateList validates all elements on the list
|
// ValidateList validates all elements on the list
|
||||||
func ValidateList[T any](in []T, validator func(T) error) error {
|
func ValidateList[T any](in []T, validator func(T) error, checks ...func(in []T) error) error {
|
||||||
errors := make([]error, len(in))
|
errors := make([]error, len(in)+len(checks))
|
||||||
|
|
||||||
for id := range in {
|
for id := range in {
|
||||||
errors[id] = PrefixResourceError(fmt.Sprintf("[%d]", id), validator(in[id]))
|
errors[id] = PrefixResourceError(fmt.Sprintf("[%d]", id), validator(in[id]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for id, c := range checks {
|
||||||
|
errors[len(in)+id] = c(in)
|
||||||
|
}
|
||||||
|
|
||||||
return WithErrors(errors...)
|
return WithErrors(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateMap validates all elements on the list
|
// ValidateMap validates all elements on the list
|
||||||
func ValidateMap[T any](in map[string]T, validator func(string, T) error) error {
|
func ValidateMap[T any](in map[string]T, validator func(string, T) error, checks ...func(in map[string]T) error) error {
|
||||||
errors := make([]error, 0, len(in))
|
errors := make([]error, 0, len(in)+len(checks))
|
||||||
|
|
||||||
for id := range in {
|
for id := range in {
|
||||||
if err := PrefixResourceError(fmt.Sprintf("`%s`", id), validator(id, in[id])); err != nil {
|
if err := PrefixResourceError(fmt.Sprintf("`%s`", id), validator(id, in[id])); err != nil {
|
||||||
|
@ -200,6 +204,10 @@ func ValidateMap[T any](in map[string]T, validator func(string, T) error) error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for id, c := range checks {
|
||||||
|
errors[len(in)+id] = c(in)
|
||||||
|
}
|
||||||
|
|
||||||
return WithErrors(errors...)
|
return WithErrors(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
package features
|
package features
|
||||||
|
|
||||||
|
import api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerFeature(gateway)
|
registerFeature(gateway)
|
||||||
}
|
}
|
||||||
|
@ -35,3 +37,7 @@ var gateway = &feature{
|
||||||
func Gateway() Feature {
|
func Gateway() Feature {
|
||||||
return gateway
|
return gateway
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsGatewayEnabled(spec api.DeploymentSpec) bool {
|
||||||
|
return Gateway().Enabled() && spec.IsGatewayEnabled()
|
||||||
|
}
|
||||||
|
|
117
pkg/deployment/pod/sni-gateway.go
Normal file
117
pkg/deployment/pod/sni-gateway.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// DISCLAIMER
|
||||||
|
//
|
||||||
|
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||||
|
//
|
||||||
|
|
||||||
|
package pod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
core "k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
|
||||||
|
"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"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SNIGateway() Builder {
|
||||||
|
return sniGateway{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sniGateway struct{}
|
||||||
|
|
||||||
|
func (s sniGateway) Envs(i Input) []core.EnvVar {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sniGateway) isSupported(i Input) bool {
|
||||||
|
if !i.Deployment.TLS.IsSecure() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !features.Gateway().Supported(i.Version, i.Enterprise) {
|
||||||
|
// We need 3.7.0+ and Enterprise to support this
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return GroupSNISupported(i.Deployment, i.Group)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sniGateway) Verify(i Input, cachedStatus interfaces.Inspector) error {
|
||||||
|
if !s.isSupported(i) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secret := range util.SortKeys(i.Deployment.TLS.GetSNI().Mapping) {
|
||||||
|
kubeSecret, exists := cachedStatus.Secret().V1().GetSimple(secret)
|
||||||
|
if !exists {
|
||||||
|
return errors.Errorf("SNI Secret not found %s", secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := kubeSecret.Data[constants.SecretTLSKeyfile]
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("Unable to find secret key %s/%s for SNI", secret, constants.SecretTLSKeyfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sniGateway) Volumes(i Input) ([]core.Volume, []core.VolumeMount) {
|
||||||
|
if !s.isSupported(i) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sni := i.Deployment.TLS.GetSNI()
|
||||||
|
volumes := make([]core.Volume, 0, len(sni.Mapping))
|
||||||
|
volumeMounts := make([]core.VolumeMount, 0, len(sni.Mapping))
|
||||||
|
|
||||||
|
for _, secret := range util.SortKeys(sni.Mapping) {
|
||||||
|
secretNameSha := util.SHA256FromString(secret)
|
||||||
|
|
||||||
|
secretNameSha = fmt.Sprintf("sni-%s", secretNameSha[:48])
|
||||||
|
|
||||||
|
vol := core.Volume{
|
||||||
|
Name: secretNameSha,
|
||||||
|
VolumeSource: core.VolumeSource{
|
||||||
|
Secret: &core.SecretVolumeSource{
|
||||||
|
SecretName: secret,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
volMount := core.VolumeMount{
|
||||||
|
Name: secretNameSha,
|
||||||
|
MountPath: fmt.Sprintf("%s/%s", shared.TLSSNIKeyfileVolumeMountDir, secret),
|
||||||
|
ReadOnly: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
volumes = append(volumes, vol)
|
||||||
|
volumeMounts = append(volumeMounts, volMount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return volumes, volumeMounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sniGateway) Args(i Input) k8sutil.OptionPairs {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -21,7 +21,6 @@
|
||||||
package pod
|
package pod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
core "k8s.io/api/core/v1"
|
core "k8s.io/api/core/v1"
|
||||||
|
@ -36,12 +35,18 @@ import (
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GroupSNISupported(mode api.DeploymentMode, group api.ServerGroup) bool {
|
func GroupSNISupported(spec api.DeploymentSpec, group api.ServerGroup) bool {
|
||||||
switch mode {
|
switch spec.Mode.Get() {
|
||||||
case api.DeploymentModeCluster:
|
case api.DeploymentModeCluster:
|
||||||
|
if features.IsGatewayEnabled(spec) {
|
||||||
|
return group == api.ServerGroupGateways
|
||||||
|
}
|
||||||
return group == api.ServerGroupCoordinators
|
return group == api.ServerGroupCoordinators
|
||||||
|
|
||||||
case api.DeploymentModeSingle:
|
case api.DeploymentModeSingle:
|
||||||
|
if features.IsGatewayEnabled(spec) {
|
||||||
|
return group == api.ServerGroupGateways
|
||||||
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case api.DeploymentModeActiveFailover:
|
case api.DeploymentModeActiveFailover:
|
||||||
return group == api.ServerGroupSingle
|
return group == api.ServerGroupSingle
|
||||||
|
@ -70,7 +75,7 @@ func (s sni) isSupported(i Input) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return GroupSNISupported(i.Deployment.Mode.Get(), i.Group)
|
return GroupSNISupported(i.Deployment, i.Group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s sni) Verify(i Input, cachedStatus interfaces.Inspector) error {
|
func (s sni) Verify(i Input, cachedStatus interfaces.Inspector) error {
|
||||||
|
@ -102,7 +107,7 @@ func (s sni) Volumes(i Input) ([]core.Volume, []core.VolumeMount) {
|
||||||
volumeMounts := make([]core.VolumeMount, 0, len(sni.Mapping))
|
volumeMounts := make([]core.VolumeMount, 0, len(sni.Mapping))
|
||||||
|
|
||||||
for _, secret := range util.SortKeys(sni.Mapping) {
|
for _, secret := range util.SortKeys(sni.Mapping) {
|
||||||
secretNameSha := fmt.Sprintf("%0x", sha256.Sum256([]byte(secret)))
|
secretNameSha := util.SHA256FromString(secret)
|
||||||
|
|
||||||
secretNameSha = fmt.Sprintf("sni-%s", secretNameSha[:48])
|
secretNameSha = fmt.Sprintf("sni-%s", secretNameSha[:48])
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (r *Reconciler) createRebalancerV2GeneratePlan(spec api.DeploymentSpec, sta
|
||||||
|
|
||||||
r.metrics.Rebalancer.SetEnabled(true)
|
r.metrics.Rebalancer.SetEnabled(true)
|
||||||
|
|
||||||
if !status.Members.AllMembersReady(spec.Mode.Get(), spec.Sync.IsEnabled(), features.Gateway().Enabled() && spec.IsGatewayEnabled()) {
|
if !status.Members.AllMembersReady(spec.Mode.Get(), spec.Sync.IsEnabled(), features.IsGatewayEnabled(spec)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (r *Reconciler) createScaleMemberPlan(ctx context.Context, apiObject k8suti
|
||||||
plan = append(plan, r.createScalePlan(status, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, 0, context)...)
|
plan = append(plan, r.createScalePlan(status, status.Members.SyncWorkers, api.ServerGroupSyncWorkers, 0, context)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
if features.IsGatewayEnabled(spec) {
|
||||||
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, spec.Gateways.GetCount(), context)...)
|
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, spec.Gateways.GetCount(), context)...)
|
||||||
} else {
|
} else {
|
||||||
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, 0, context)...)
|
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, 0, context)...)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// DISCLAIMER
|
// DISCLAIMER
|
||||||
//
|
//
|
||||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -57,7 +57,11 @@ func (r *Reconciler) createRotateTLSServerSNIPlan(ctx context.Context, apiObject
|
||||||
|
|
||||||
var plan api.Plan
|
var plan api.Plan
|
||||||
for _, group := range api.AllServerGroups {
|
for _, group := range api.AllServerGroups {
|
||||||
if !pod.GroupSNISupported(spec.Mode.Get(), group) {
|
if group == api.ServerGroupGateways {
|
||||||
|
// Gateways are managed differently
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !pod.GroupSNISupported(spec, group) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, m := range status.Members.MembersOfGroup(group) {
|
for _, m := range status.Members.MembersOfGroup(group) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ package resources
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
core "k8s.io/api/core/v1"
|
core "k8s.io/api/core/v1"
|
||||||
|
@ -66,6 +67,7 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec
|
||||||
},
|
},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
GatewayConfigFileName: string(gatewayCfgYaml),
|
GatewayConfigFileName: string(gatewayCfgYaml),
|
||||||
|
GatewayConfigChecksumFileName: gatewayCfgChecksum,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,11 +87,12 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec
|
||||||
return errors.Reconcile()
|
return errors.Reconcile()
|
||||||
} else {
|
} else {
|
||||||
// CM Exists, checks checksum - if key is not in the map we return empty string
|
// CM Exists, checks checksum - if key is not in the map we return empty string
|
||||||
if existingSha := util.SHA256FromString(cm.Data[GatewayConfigFileName]); existingSha != gatewayCfgChecksum {
|
if existingSha, existingChecksumSha := util.SHA256FromString(cm.Data[GatewayConfigFileName]), cm.Data[GatewayConfigChecksumFileName]; existingSha != gatewayCfgChecksum || existingChecksumSha != gatewayCfgChecksum {
|
||||||
// We need to do the update
|
// We need to do the update
|
||||||
if _, changed, err := patcher.Patcher[*core.ConfigMap](ctx, cachedStatus.ConfigMapsModInterface().V1(), cm, meta.PatchOptions{},
|
if _, changed, err := patcher.Patcher[*core.ConfigMap](ctx, cachedStatus.ConfigMapsModInterface().V1(), cm, meta.PatchOptions{},
|
||||||
patcher.PatchConfigMapData(map[string]string{
|
patcher.PatchConfigMapData(map[string]string{
|
||||||
GatewayConfigFileName: string(gatewayCfgYaml),
|
GatewayConfigFileName: string(gatewayCfgYaml),
|
||||||
|
GatewayConfigChecksumFileName: gatewayCfgChecksum,
|
||||||
})); err != nil {
|
})); err != nil {
|
||||||
log.Err(err).Debug("Failed to patch GatewayConfig ConfigMap")
|
log.Err(err).Debug("Failed to patch GatewayConfig ConfigMap")
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
@ -116,6 +119,11 @@ func (r *Resources) renderGatewayConfig(cachedStatus inspectorInterface.Inspecto
|
||||||
|
|
||||||
var cfg gateway.Config
|
var cfg gateway.Config
|
||||||
|
|
||||||
|
cfg.IntegrationSidecar = &gateway.ConfigDestinationTarget{
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: int32(r.context.GetSpec().Gateway.GetSidecar().GetListenPort()),
|
||||||
|
}
|
||||||
|
|
||||||
cfg.DefaultDestination = gateway.ConfigDestination{
|
cfg.DefaultDestination = gateway.ConfigDestination{
|
||||||
Targets: []gateway.ConfigDestinationTarget{
|
Targets: []gateway.ConfigDestinationTarget{
|
||||||
{
|
{
|
||||||
|
@ -133,6 +141,25 @@ func (r *Resources) renderGatewayConfig(cachedStatus inspectorInterface.Inspecto
|
||||||
PrivateKeyPath: keyPath,
|
PrivateKeyPath: keyPath,
|
||||||
}
|
}
|
||||||
cfg.DefaultDestination.Type = util.NewType(gateway.ConfigDestinationTypeHTTPS)
|
cfg.DefaultDestination.Type = util.NewType(gateway.ConfigDestinationTypeHTTPS)
|
||||||
|
|
||||||
|
// Check SNI
|
||||||
|
if sni := spec.TLS.GetSNI().Mapping; len(sni) > 0 {
|
||||||
|
for _, volume := range util.SortKeys(sni) {
|
||||||
|
servers, ok := sni[volume]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var s gateway.ConfigSNI
|
||||||
|
f := path.Join(shared.TLSSNIKeyfileVolumeMountDir, volume, constants.SecretTLSKeyfile)
|
||||||
|
s.ConfigTLS = gateway.ConfigTLS{
|
||||||
|
CertificatePath: f,
|
||||||
|
PrivateKeyPath: f,
|
||||||
|
}
|
||||||
|
s.ServerNames = servers
|
||||||
|
cfg.SNI = append(cfg.SNI, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ArangoRoutes
|
// Check ArangoRoutes
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (r *Resources) EnsureConfigMaps(ctx context.Context, cachedStatus inspector
|
||||||
|
|
||||||
reconcileRequired := k8sutil.NewReconcile(cachedStatus)
|
reconcileRequired := k8sutil.NewReconcile(cachedStatus)
|
||||||
|
|
||||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
if features.IsGatewayEnabled(spec) {
|
||||||
counterMetric.Inc()
|
counterMetric.Inc()
|
||||||
if err := reconcileRequired.WithError(r.ensureGatewayConfig(ctx, cachedStatus, configMaps)); err != nil {
|
if err := reconcileRequired.WithError(r.ensureGatewayConfig(ctx, cachedStatus, configMaps)); err != nil {
|
||||||
return errors.Section(err, "Gateway ConfigMap")
|
return errors.Section(err, "Gateway ConfigMap")
|
||||||
|
|
25
pkg/deployment/resources/gateway/consts.go
Normal file
25
pkg/deployment/resources/gateway/consts.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
|
||||||
|
const (
|
||||||
|
IntegrationSidecarFilterName = "envoy.filters.http.ext_authz"
|
||||||
|
)
|
|
@ -23,16 +23,23 @@ package gateway
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
bootstrapAPI "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
|
bootstrapAPI "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
|
||||||
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||||
coreAPI "github.com/envoyproxy/go-control-plane/envoy/config/core/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"
|
listenerAPI "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||||
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||||
|
httpFilterAuthzApi "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
|
||||||
routerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
|
routerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
|
||||||
|
tlsInspectorApi "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3"
|
||||||
httpConnectionManagerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
httpConnectionManagerAPI "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||||
|
upstreamHttpApi "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
|
||||||
|
"github.com/golang/protobuf/ptypes/any"
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
"google.golang.org/protobuf/types/known/anypb"
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||||
|
@ -46,12 +53,18 @@ type Config struct {
|
||||||
Destinations ConfigDestinations `json:"destinations,omitempty"`
|
Destinations ConfigDestinations `json:"destinations,omitempty"`
|
||||||
|
|
||||||
DefaultTLS *ConfigTLS `json:"defaultTLS,omitempty"`
|
DefaultTLS *ConfigTLS `json:"defaultTLS,omitempty"`
|
||||||
|
|
||||||
|
IntegrationSidecar *ConfigDestinationTarget `json:"integrationSidecar,omitempty"`
|
||||||
|
|
||||||
|
SNI ConfigSNIList `json:"sni,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) Validate() error {
|
func (c Config) Validate() error {
|
||||||
return errors.Errors(
|
return errors.Errors(
|
||||||
shared.PrefixResourceErrors("defaultDestination", c.DefaultDestination.Validate()),
|
shared.PrefixResourceErrors("defaultDestination", c.DefaultDestination.Validate()),
|
||||||
|
shared.PrefixResourceErrors("integrationSidecar", c.IntegrationSidecar.Validate()),
|
||||||
shared.PrefixResourceErrors("destinations", c.Destinations.Validate()),
|
shared.PrefixResourceErrors("destinations", c.Destinations.Validate()),
|
||||||
|
shared.PrefixResourceErrors("sni", c.SNI.Validate()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +129,41 @@ func (c Config) RenderClusters() ([]*clusterAPI.Cluster, error) {
|
||||||
def,
|
def,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i := c.IntegrationSidecar; i != nil {
|
||||||
|
hpo, err := anypb.New(&upstreamHttpApi.HttpProtocolOptions{
|
||||||
|
UpstreamProtocolOptions: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_{
|
||||||
|
ExplicitHttpConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig{
|
||||||
|
ProtocolConfig: &upstreamHttpApi.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{
|
||||||
|
Http2ProtocolOptions: &coreAPI.Http2ProtocolOptions{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cluster := &clusterAPI.Cluster{
|
||||||
|
Name: "integration_sidecar",
|
||||||
|
ConnectTimeout: durationpb.New(time.Second),
|
||||||
|
LbPolicy: clusterAPI.Cluster_ROUND_ROBIN,
|
||||||
|
LoadAssignment: &endpointAPI.ClusterLoadAssignment{
|
||||||
|
ClusterName: "integration_sidecar",
|
||||||
|
Endpoints: []*endpointAPI.LocalityLbEndpoints{
|
||||||
|
{
|
||||||
|
LbEndpoints: []*endpointAPI.LbEndpoint{
|
||||||
|
i.RenderEndpoint(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TypedExtensionProtocolOptions: map[string]*any.Any{
|
||||||
|
"envoy.extensions.upstreams.http.v3.HttpProtocolOptions": hpo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clusters = append(clusters, cluster)
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range c.Destinations {
|
for k, v := range c.Destinations {
|
||||||
name := fmt.Sprintf("cluster_%s", util.SHA256FromString(k))
|
name := fmt.Sprintf("cluster_%s", util.SHA256FromString(k))
|
||||||
c, err := v.RenderCluster(name)
|
c, err := v.RenderCluster(name)
|
||||||
|
@ -159,6 +207,33 @@ func (c Config) RenderRoutes() ([]*routeAPI.Route, error) {
|
||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Config) RenderIntegrationSidecarFilter() (*httpConnectionManagerAPI.HttpFilter, error) {
|
||||||
|
e, err := anypb.New(&httpFilterAuthzApi.ExtAuthz{
|
||||||
|
Services: &httpFilterAuthzApi.ExtAuthz_GrpcService{
|
||||||
|
GrpcService: &coreAPI.GrpcService{
|
||||||
|
TargetSpecifier: &coreAPI.GrpcService_EnvoyGrpc_{
|
||||||
|
EnvoyGrpc: &coreAPI.GrpcService_EnvoyGrpc{
|
||||||
|
ClusterName: "integration_sidecar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Timeout: durationpb.New(500 * time.Millisecond),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IncludePeerCertificate: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpConnectionManagerAPI.HttpFilter{
|
||||||
|
Name: IntegrationSidecarFilterName,
|
||||||
|
ConfigType: &httpConnectionManagerAPI.HttpFilter_TypedConfig{
|
||||||
|
TypedConfig: e,
|
||||||
|
},
|
||||||
|
IsOptional: false,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
||||||
httpFilterConfigType, err := anypb.New(&routerAPI.Router{})
|
httpFilterConfigType, err := anypb.New(&routerAPI.Router{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -170,6 +245,16 @@ func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
||||||
return nil, errors.Wrapf(err, "Unable to render routes")
|
return nil, errors.Wrapf(err, "Unable to render routes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var httpFilters []*httpConnectionManagerAPI.HttpFilter
|
||||||
|
|
||||||
|
if i := c.IntegrationSidecar; i != nil {
|
||||||
|
q, err := c.RenderIntegrationSidecarFilter()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
httpFilters = append(httpFilters, q)
|
||||||
|
}
|
||||||
|
|
||||||
filterConfigType, err := anypb.New(&httpConnectionManagerAPI.HttpConnectionManager{
|
filterConfigType, err := anypb.New(&httpConnectionManagerAPI.HttpConnectionManager{
|
||||||
StatPrefix: "ingress_http",
|
StatPrefix: "ingress_http",
|
||||||
CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO,
|
CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO,
|
||||||
|
@ -185,14 +270,13 @@ func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
HttpFilters: []*httpConnectionManagerAPI.HttpFilter{
|
HttpFilters: append(httpFilters, &httpConnectionManagerAPI.HttpFilter{
|
||||||
{
|
|
||||||
Name: "envoy.filters.http.routerAPI",
|
Name: "envoy.filters.http.routerAPI",
|
||||||
ConfigType: &httpConnectionManagerAPI.HttpFilter_TypedConfig{
|
ConfigType: &httpConnectionManagerAPI.HttpFilter_TypedConfig{
|
||||||
TypedConfig: httpFilterConfigType,
|
TypedConfig: httpFilterConfigType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "Unable to render http connection manager")
|
return nil, errors.Wrapf(err, "Unable to render http connection manager")
|
||||||
|
@ -211,7 +295,7 @@ func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
||||||
func (c Config) RenderDefaultFilterChain() (*listenerAPI.FilterChain, error) {
|
func (c Config) RenderDefaultFilterChain() (*listenerAPI.FilterChain, error) {
|
||||||
filters, err := c.RenderFilters()
|
filters, err := c.RenderFilters()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "Unable to render filters")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := &listenerAPI.FilterChain{
|
ret := &listenerAPI.FilterChain{
|
||||||
|
@ -228,9 +312,18 @@ func (c Config) RenderDefaultFilterChain() (*listenerAPI.FilterChain, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) RenderSecondaryFilterChains() ([]*listenerAPI.FilterChain, error) {
|
func (c Config) RenderSecondaryFilterChains() ([]*listenerAPI.FilterChain, error) {
|
||||||
|
if len(c.SNI) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filters, err := c.RenderFilters()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SNI.RenderFilterChain(filters)
|
||||||
|
}
|
||||||
|
|
||||||
func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
||||||
filterChains, err := c.RenderSecondaryFilterChains()
|
filterChains, err := c.RenderSecondaryFilterChains()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -242,6 +335,22 @@ func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
||||||
return nil, errors.Wrapf(err, "Unable to render default filter")
|
return nil, errors.Wrapf(err, "Unable to render default filter")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var listenerFilters []*listenerAPI.ListenerFilter
|
||||||
|
|
||||||
|
if c.DefaultTLS != nil {
|
||||||
|
w, err := anypb.New(&tlsInspectorApi.TlsInspector{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "Unable to render TLS Inspector")
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerFilters = append(listenerFilters, &listenerAPI.ListenerFilter{
|
||||||
|
Name: "envoy.filters.listener.tls_inspector",
|
||||||
|
ConfigType: &listenerAPI.ListenerFilter_TypedConfig{
|
||||||
|
TypedConfig: w,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return &listenerAPI.Listener{
|
return &listenerAPI.Listener{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
Address: &coreAPI.Address{
|
Address: &coreAPI.Address{
|
||||||
|
@ -253,7 +362,7 @@ func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FilterChains: filterChains,
|
FilterChains: filterChains,
|
||||||
|
ListenerFilters: listenerFilters,
|
||||||
DefaultFilterChain: defaultFilterChain,
|
DefaultFilterChain: defaultFilterChain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
clusterAPI "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||||
endpointAPI "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
endpointAPI "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||||
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
routeAPI "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||||
|
anypb "github.com/golang/protobuf/ptypes/any"
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
|
||||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||||
|
@ -63,7 +64,10 @@ type ConfigDestination struct {
|
||||||
Path *string `json:"path,omitempty"`
|
Path *string `json:"path,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDestination) Validate() error {
|
func (c *ConfigDestination) Validate() error {
|
||||||
|
if c == nil {
|
||||||
|
c = &ConfigDestination{}
|
||||||
|
}
|
||||||
return shared.WithErrors(
|
return shared.WithErrors(
|
||||||
shared.PrefixResourceError("targets", c.Targets.Validate()),
|
shared.PrefixResourceError("targets", c.Targets.Validate()),
|
||||||
shared.PrefixResourceError("type", c.Type.Validate()),
|
shared.PrefixResourceError("type", c.Type.Validate()),
|
||||||
|
@ -71,15 +75,15 @@ func (c ConfigDestination) Validate() error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDestination) GetPath() string {
|
func (c *ConfigDestination) GetPath() string {
|
||||||
if c.Path == nil {
|
if c == nil || c.Path == nil {
|
||||||
return "/"
|
return "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
return *c.Path
|
return *c.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDestination) RenderRoute(name, prefix string) (*routeAPI.Route, error) {
|
func (c *ConfigDestination) RenderRoute(name, prefix string) (*routeAPI.Route, error) {
|
||||||
return &routeAPI.Route{
|
return &routeAPI.Route{
|
||||||
Match: &routeAPI.RouteMatch{
|
Match: &routeAPI.RouteMatch{
|
||||||
PathSpecifier: &routeAPI.RouteMatch_Prefix{
|
PathSpecifier: &routeAPI.RouteMatch_Prefix{
|
||||||
|
@ -94,10 +98,11 @@ func (c ConfigDestination) RenderRoute(name, prefix string) (*routeAPI.Route, er
|
||||||
PrefixRewrite: c.GetPath(),
|
PrefixRewrite: c.GetPath(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TypedPerFilterConfig: map[string]*anypb.Any{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDestination) RenderCluster(name string) (*clusterAPI.Cluster, error) {
|
func (c *ConfigDestination) RenderCluster(name string) (*clusterAPI.Cluster, error) {
|
||||||
cluster := &clusterAPI.Cluster{
|
cluster := &clusterAPI.Cluster{
|
||||||
Name: name,
|
Name: name,
|
||||||
ConnectTimeout: durationpb.New(time.Second),
|
ConnectTimeout: durationpb.New(time.Second),
|
||||||
|
|
|
@ -54,7 +54,10 @@ type ConfigDestinationTarget struct {
|
||||||
Port int32 `json:"port,omitempty"`
|
Port int32 `json:"port,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDestinationTarget) Validate() error {
|
func (c *ConfigDestinationTarget) Validate() error {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return shared.WithErrors(
|
return shared.WithErrors(
|
||||||
shared.ValidateRequiredPath("ip", &c.Host, func(t string) error {
|
shared.ValidateRequiredPath("ip", &c.Host, func(t string) error {
|
||||||
if t == "" {
|
if t == "" {
|
||||||
|
@ -71,7 +74,10 @@ func (c ConfigDestinationTarget) Validate() error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConfigDestinationTarget) RenderEndpoint() *endpointAPI.LbEndpoint {
|
func (c *ConfigDestinationTarget) RenderEndpoint() *endpointAPI.LbEndpoint {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return &endpointAPI.LbEndpoint{
|
return &endpointAPI.LbEndpoint{
|
||||||
HostIdentifier: &endpointAPI.LbEndpoint_Endpoint{
|
HostIdentifier: &endpointAPI.LbEndpoint_Endpoint{
|
||||||
Endpoint: &endpointAPI.Endpoint{
|
Endpoint: &endpointAPI.Endpoint{
|
||||||
|
|
82
pkg/deployment/resources/gateway/gateway_config_sni.go
Normal file
82
pkg/deployment/resources/gateway/gateway_config_sni.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// 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 (
|
||||||
|
listenerAPI "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||||
|
|
||||||
|
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||||
|
sharedApi "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigSNIList []ConfigSNI
|
||||||
|
|
||||||
|
func (c ConfigSNIList) RenderFilterChain(filters []*listenerAPI.Filter) ([]*listenerAPI.FilterChain, error) {
|
||||||
|
var r = make([]*listenerAPI.FilterChain, len(filters))
|
||||||
|
for id := range filters {
|
||||||
|
if f, err := c[id].RenderFilterChain(filters); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
r[id] = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ConfigSNIList) Validate() error {
|
||||||
|
return shared.ValidateList(c, func(sni ConfigSNI) error {
|
||||||
|
return sni.Validate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigSNI struct {
|
||||||
|
ConfigTLS `json:",inline"`
|
||||||
|
|
||||||
|
ServerNames []string `json:"serverNames,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ConfigSNI) RenderFilterChain(filters []*listenerAPI.Filter) (*listenerAPI.FilterChain, error) {
|
||||||
|
transport, err := c.RenderListenerTransportSocket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &listenerAPI.FilterChain{
|
||||||
|
TransportSocket: transport,
|
||||||
|
FilterChainMatch: &listenerAPI.FilterChainMatch{
|
||||||
|
ServerNames: util.CopyList(c.ServerNames),
|
||||||
|
},
|
||||||
|
Filters: filters,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c ConfigSNI) Validate() error {
|
||||||
|
return shared.WithErrors(
|
||||||
|
shared.ValidateList(c.ServerNames, sharedApi.IsValidDomain, func(in []string) error {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return errors.Errorf("AtLeast one element required on list")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
|
@ -147,4 +147,45 @@ 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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Path: util.NewType("/test/path/"),
|
||||||
|
Type: util.NewType(ConfigDestinationTypeHTTPS),
|
||||||
|
},
|
||||||
|
DefaultTLS: &ConfigTLS{
|
||||||
|
CertificatePath: "/test",
|
||||||
|
PrivateKeyPath: "/test12",
|
||||||
|
},
|
||||||
|
SNI: []ConfigSNI{
|
||||||
|
{
|
||||||
|
ConfigTLS: ConfigTLS{
|
||||||
|
CertificatePath: "/cp",
|
||||||
|
PrivateKeyPath: "/pp",
|
||||||
|
},
|
||||||
|
ServerNames: []string{
|
||||||
|
"example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Destinations: ConfigDestinations{
|
||||||
|
"/_test/": {
|
||||||
|
Targets: []ConfigDestinationTarget{
|
||||||
|
{
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: 12346,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Path: util.NewType("/test/path/"),
|
||||||
|
Type: util.NewType(ConfigDestinationTypeHTTP),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (r *Resources) EnsurePDBs(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
minGateways, currGateways := 0, 0
|
minGateways, currGateways := 0, 0
|
||||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
if features.IsGatewayEnabled(spec) {
|
||||||
minGateways = spec.GetServerGroupSpec(api.ServerGroupGateways).New().GetCount() - 1
|
minGateways = spec.GetServerGroupSpec(api.ServerGroupGateways).New().GetCount() - 1
|
||||||
currGateways = status.Members.Gateways.MembersReady()
|
currGateways = status.Members.Gateways.MembersReady()
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ const (
|
||||||
GatewayVolumeMountDir = "/etc/gateway/"
|
GatewayVolumeMountDir = "/etc/gateway/"
|
||||||
GatewayVolumeName = "gateway"
|
GatewayVolumeName = "gateway"
|
||||||
GatewayConfigFileName = "gateway.yaml"
|
GatewayConfigFileName = "gateway.yaml"
|
||||||
|
GatewayConfigChecksumFileName = "gateway.checksum"
|
||||||
|
GatewayConfigChecksumENV = "GATEWAY_CONFIG_CHECKSUM"
|
||||||
GatewayConfigFilePath = GatewayVolumeMountDir + GatewayConfigFileName
|
GatewayConfigFilePath = GatewayVolumeMountDir + GatewayConfigFileName
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,6 +52,9 @@ func createGatewayVolumes(input pod.Input) pod.Volumes {
|
||||||
// TLS
|
// TLS
|
||||||
volumes.Append(pod.TLS(), input)
|
volumes.Append(pod.TLS(), input)
|
||||||
|
|
||||||
|
// SNI
|
||||||
|
volumes.Append(pod.SNIGateway(), input)
|
||||||
|
|
||||||
return volumes
|
return volumes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,19 @@ func (a *ArangoGatewayContainer) GetEnvs() ([]core.EnvVar, []core.EnvFromSource)
|
||||||
|
|
||||||
envs.Add(true, k8sutil.GetLifecycleEnv()...)
|
envs.Add(true, k8sutil.GetLifecycleEnv()...)
|
||||||
|
|
||||||
|
var cmChecksum = ""
|
||||||
|
|
||||||
|
if cm, ok := a.cachedStatus.ConfigMap().V1().GetSimple(GetGatewayConfigMapName(a.input.ApiObject.GetName())); ok {
|
||||||
|
if v, ok := cm.Data[GatewayConfigChecksumFileName]; ok {
|
||||||
|
cmChecksum = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
envs.Add(true, core.EnvVar{
|
||||||
|
Name: GatewayConfigChecksumENV,
|
||||||
|
Value: cmChecksum,
|
||||||
|
})
|
||||||
|
|
||||||
if len(a.groupSpec.Envs) > 0 {
|
if len(a.groupSpec.Envs) > 0 {
|
||||||
for _, env := range a.groupSpec.Envs {
|
for _, env := range a.groupSpec.Envs {
|
||||||
// Do not override preset envs
|
// Do not override preset envs
|
||||||
|
|
|
@ -192,7 +192,13 @@ func (m *MemberGatewayPod) Init(ctx context.Context, cachedStatus interfaces.Ins
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MemberGatewayPod) Validate(_ interfaces.Inspector) error {
|
func (m *MemberGatewayPod) Validate(cachedStatus interfaces.Inspector) error {
|
||||||
|
i := m.AsInput()
|
||||||
|
|
||||||
|
if err := pod.SNI().Verify(i, cachedStatus); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := validateSidecars(m.groupSpec.SidecarCoreNames, m.groupSpec.GetSidecars()); err != nil {
|
if err := validateSidecars(m.groupSpec.SidecarCoreNames, m.groupSpec.GetSidecars()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -232,7 +238,9 @@ func (m *MemberGatewayPod) Labels() map[string]string {
|
||||||
func (m *MemberGatewayPod) Profiles() (schedulerApi.ProfileTemplates, error) {
|
func (m *MemberGatewayPod) Profiles() (schedulerApi.ProfileTemplates, error) {
|
||||||
integration, err := sidecar.NewIntegration(&schedulerContainerResourcesApi.Image{
|
integration, err := sidecar.NewIntegration(&schedulerContainerResourcesApi.Image{
|
||||||
Image: util.NewType(m.resources.context.GetOperatorImage()),
|
Image: util.NewType(m.resources.context.GetOperatorImage()),
|
||||||
}, m.spec.Gateway.GetSidecar(), []string{shared.ServerContainerName})
|
}, m.spec.Gateway.GetSidecar(), []string{shared.ServerContainerName}, sidecar.IntegrationEnvoyV3{
|
||||||
|
Spec: m.spec,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -531,7 +531,7 @@ func (r *Resources) InspectPods(ctx context.Context, cachedStatus inspectorInter
|
||||||
}
|
}
|
||||||
|
|
||||||
spec := r.context.GetSpec()
|
spec := r.context.GetSpec()
|
||||||
allMembersReady := status.Members.AllMembersReady(spec.GetMode(), r.context.IsSyncEnabled(), features.Gateway().Enabled() && spec.IsGatewayEnabled())
|
allMembersReady := status.Members.AllMembersReady(spec.GetMode(), r.context.IsSyncEnabled(), features.IsGatewayEnabled(spec))
|
||||||
status.Conditions.Update(api.ConditionTypeReady, allMembersReady, "", "")
|
status.Conditions.Update(api.ConditionTypeReady, allMembersReady, "", "")
|
||||||
|
|
||||||
// Update conditions
|
// Update conditions
|
||||||
|
|
|
@ -82,7 +82,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
|
||||||
defer metrics.SetDuration(inspectServicesDurationGauges.WithLabelValues(deploymentName), start)
|
defer metrics.SetDuration(inspectServicesDurationGauges.WithLabelValues(deploymentName), start)
|
||||||
counterMetric := inspectedServicesCounters.WithLabelValues(deploymentName)
|
counterMetric := inspectedServicesCounters.WithLabelValues(deploymentName)
|
||||||
|
|
||||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
if features.IsGatewayEnabled(spec) {
|
||||||
role = api.ServerGroupGateways.AsRole()
|
role = api.ServerGroupGateways.AsRole()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,10 +51,12 @@ func (c *Core) GetExternal() bool {
|
||||||
|
|
||||||
func (c *Core) Args(int Integration) k8sutil.OptionPairs {
|
func (c *Core) Args(int Integration) k8sutil.OptionPairs {
|
||||||
var options k8sutil.OptionPairs
|
var options k8sutil.OptionPairs
|
||||||
name, ver := int.Name()
|
cmd := strings.Join(util.FormatList(int.Name(), func(a string) string {
|
||||||
|
return strings.ToLower(a)
|
||||||
|
}), ".")
|
||||||
|
|
||||||
options.Add(fmt.Sprintf("--integration.%s.%s.internal", strings.ToLower(name), strings.ToLower(ver)), c.GetInternal())
|
options.Add(fmt.Sprintf("--integration.%s.internal", cmd), c.GetInternal())
|
||||||
options.Add(fmt.Sprintf("--integration.%s.%s.external", strings.ToLower(name), strings.ToLower(ver)), c.GetExternal())
|
options.Add(fmt.Sprintf("--integration.%s.external", cmd), c.GetExternal())
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ type IntegrationAuthenticationV1 struct {
|
||||||
Deployment *api.ArangoDeployment
|
Deployment *api.ArangoDeployment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationAuthenticationV1) Name() (string, string) {
|
func (i IntegrationAuthenticationV1) Name() []string {
|
||||||
return "AUTHENTICATION", "V1"
|
return []string{"AUTHENTICATION", "V1"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationAuthenticationV1) Validate() error {
|
func (i IntegrationAuthenticationV1) Validate() error {
|
||||||
|
|
|
@ -28,8 +28,8 @@ type IntegrationAuthorizationV0 struct {
|
||||||
Core *Core
|
Core *Core
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationAuthorizationV0) Name() (string, string) {
|
func (i IntegrationAuthorizationV0) Name() []string {
|
||||||
return "AUTHORIZATION", "V0"
|
return []string{"AUTHORIZATION", "V0"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationAuthorizationV0) Validate() error {
|
func (i IntegrationAuthorizationV0) Validate() error {
|
||||||
|
|
|
@ -22,34 +22,26 @@ package sidecar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IntegrationEnvoyV3 struct {
|
type IntegrationEnvoyV3 struct {
|
||||||
Core *Core
|
Core *Core
|
||||||
Deployment *api.ArangoDeployment
|
Spec api.DeploymentSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationEnvoyV3) Name() (string, string) {
|
func (i IntegrationEnvoyV3) Name() []string {
|
||||||
return "ENVOY", "V3"
|
return []string{"ENVOY", "AUTH", "V3"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationEnvoyV3) Validate() error {
|
func (i IntegrationEnvoyV3) Validate() error {
|
||||||
if i.Deployment == nil {
|
|
||||||
return errors.Errorf("Deployment is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationEnvoyV3) Args() (k8sutil.OptionPairs, error) {
|
func (i IntegrationEnvoyV3) Args() (k8sutil.OptionPairs, error) {
|
||||||
options := k8sutil.CreateOptionPairs()
|
options := k8sutil.CreateOptionPairs()
|
||||||
|
|
||||||
options.Add("--integration.authentication.v1", true)
|
options.Add("--integration.envoy.auth.v3", 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))
|
options.Merge(i.Core.Args(i))
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ type IntegrationVolumes interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Integration interface {
|
type Integration interface {
|
||||||
Name() (string, string)
|
Name() []string
|
||||||
Args() (k8sutil.OptionPairs, error)
|
Args() (k8sutil.OptionPairs, error)
|
||||||
Validate() error
|
Validate() error
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,9 @@ type Integration interface {
|
||||||
func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *schedulerApi.IntegrationSidecar, coreContainers []string, integrations ...Integration) (*schedulerApi.ProfileTemplate, error) {
|
func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *schedulerApi.IntegrationSidecar, coreContainers []string, integrations ...Integration) (*schedulerApi.ProfileTemplate, error) {
|
||||||
for _, integration := range integrations {
|
for _, integration := range integrations {
|
||||||
if err := integration.Validate(); err != nil {
|
if err := integration.Validate(); err != nil {
|
||||||
name, version := integration.Name()
|
name := strings.Join(integration.Name(), "/")
|
||||||
|
|
||||||
return nil, errors.Wrapf(err, "Failure in %s/%s", name, version)
|
return nil, errors.Wrapf(err, "Failure in %s", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,33 +100,35 @@ func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *sc
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, i := range integrations {
|
for _, i := range integrations {
|
||||||
name, version := i.Name()
|
name := strings.Join(i.Name(), "/")
|
||||||
|
|
||||||
if err := i.Validate(); err != nil {
|
if err := i.Validate(); err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failure in %s/%s", name, version)
|
return nil, errors.Wrapf(err, "Failure in %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if args, err := i.Args(); err != nil {
|
if args, err := i.Args(); err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failure in arguments %s/%s", name, version)
|
return nil, errors.Wrapf(err, "Failure in arguments %s", name)
|
||||||
} else if len(args) > 0 {
|
} else if len(args) > 0 {
|
||||||
options.Merge(args)
|
options.Merge(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lvolumes, lvolumeMounts, err := WithIntegrationVolumes(i); err != nil {
|
if lvolumes, lvolumeMounts, err := WithIntegrationVolumes(i); err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failure in volumes %s/%s", name, version)
|
return nil, errors.Wrapf(err, "Failure in volumes %s", name)
|
||||||
} else if len(lvolumes) > 0 || len(lvolumeMounts) > 0 {
|
} else if len(lvolumes) > 0 || len(lvolumeMounts) > 0 {
|
||||||
volumes = append(volumes, lvolumes...)
|
volumes = append(volumes, lvolumes...)
|
||||||
volumeMounts = append(volumeMounts, lvolumeMounts...)
|
volumeMounts = append(volumeMounts, lvolumeMounts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lenvs, err := WithIntegrationEnvs(i); err != nil {
|
if lenvs, err := WithIntegrationEnvs(i); err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failure in envs %s/%s", name, version)
|
return nil, errors.Wrapf(err, "Failure in envs %s", name)
|
||||||
} else if len(lenvs) > 0 {
|
} else if len(lenvs) > 0 {
|
||||||
envs = append(envs, lenvs...)
|
envs = append(envs, lenvs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
envs = append(envs, core.EnvVar{
|
envs = append(envs, core.EnvVar{
|
||||||
Name: fmt.Sprintf("INTEGRATION_SERVICE_%s_%s", strings.ToUpper(name), strings.ToUpper(version)),
|
Name: fmt.Sprintf("INTEGRATION_SERVICE_%s", strings.Join(util.FormatList(i.Name(), func(a string) string {
|
||||||
|
return strings.ToUpper(a)
|
||||||
|
}), "_")),
|
||||||
Value: fmt.Sprintf("127.0.0.1:%d", integration.GetListenPort()),
|
Value: fmt.Sprintf("127.0.0.1:%d", integration.GetListenPort()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ type IntegrationShutdownV1 struct {
|
||||||
Core *Core
|
Core *Core
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationShutdownV1) Name() (string, string) {
|
func (i IntegrationShutdownV1) Name() []string {
|
||||||
return "SHUTDOWN", "V1"
|
return []string{"SHUTDOWN", "V1"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i IntegrationShutdownV1) Validate() error {
|
func (i IntegrationShutdownV1) Validate() error {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
core "k8s.io/api/core/v1"
|
core "k8s.io/api/core/v1"
|
||||||
|
|
||||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/configmap"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service"
|
||||||
)
|
)
|
||||||
|
@ -33,6 +34,7 @@ import (
|
||||||
type Inspector interface {
|
type Inspector interface {
|
||||||
secret.Inspector
|
secret.Inspector
|
||||||
service.Inspector
|
service.Inspector
|
||||||
|
configmap.Inspector
|
||||||
}
|
}
|
||||||
|
|
||||||
type PodModifier interface {
|
type PodModifier interface {
|
||||||
|
|
|
@ -101,3 +101,9 @@ func FormatListErr[A, B any](in []A, format func(A) (B, error)) ([]B, error) {
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CopyList[A any](in []A) []A {
|
||||||
|
ret := make([]A, len(in))
|
||||||
|
copy(ret, in)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
33
pkg/util/tests/cast.go
Normal file
33
pkg/util/tests/cast.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// 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 tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CastAs[A any](t *testing.T, in any) A {
|
||||||
|
v, ok := in.(A)
|
||||||
|
require.True(t, ok)
|
||||||
|
return v
|
||||||
|
}
|
Loading…
Reference in a new issue