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
|
||||
- (Feature) Improve Gateway Config gen
|
||||
- (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)
|
||||
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries
|
||||
|
|
|
@ -7,7 +7,9 @@ FROM ${ENVOY_IMAGE} AS envoy
|
|||
|
||||
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
|
||||
|
||||
|
|
12
Makefile
12
Makefile
|
@ -121,6 +121,8 @@ ifndef LOCALONLY
|
|||
PUSHIMAGES := 1
|
||||
endif
|
||||
|
||||
BUILD_SKIP_UPDATE ?= false
|
||||
|
||||
ifdef IMAGETAG
|
||||
IMAGESUFFIX := :$(IMAGETAG)
|
||||
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 \
|
||||
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_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))
|
||||
|
@ -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)
|
||||
ifdef PUSHIMAGES
|
||||
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) .
|
||||
else
|
||||
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) .
|
||||
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]\+" \
|
||||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/integrations/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
|
@ -818,6 +821,7 @@ set-api-version/%:
|
|||
@grep -rHn "github.com/arangodb/kube-arangodb/pkg/apis/$*/v[A-Za-z0-9]\+" \
|
||||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/integrations/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
|
@ -831,6 +835,7 @@ set-api-version/%:
|
|||
@grep -rHn "DatabaseV[A-Za-z0-9]\+()" \
|
||||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/integrations/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
|
@ -844,6 +849,7 @@ set-api-version/%:
|
|||
@grep -rHn "ReplicationV[A-Za-z0-9]\+()" \
|
||||
"$(ROOT)/pkg/deployment/" \
|
||||
"$(ROOT)/pkg/replication/" \
|
||||
"$(ROOT)/pkg/integrations/" \
|
||||
"$(ROOT)/pkg/operator/" \
|
||||
"$(ROOT)/pkg/operatorV2/" \
|
||||
"$(ROOT)/pkg/server/" \
|
||||
|
|
|
@ -182,7 +182,7 @@ Flags:
|
|||
--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)
|
||||
--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)
|
||||
--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
|
||||
|
|
|
@ -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.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.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)
|
||||
--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
|
||||
|
|
|
@ -53,5 +53,10 @@ func (i *impl) Register(registrar *grpc.Server) {
|
|||
}
|
||||
|
||||
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/arangodb/kube-arangodb/pkg/util/svc"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/tests"
|
||||
"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, resp.Validate())
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -180,19 +180,23 @@ func ValidateRequiredInterfacePath[T ValidateInterface](path string, in T) error
|
|||
}
|
||||
|
||||
// ValidateList validates all elements on the list
|
||||
func ValidateList[T any](in []T, validator func(T) error) error {
|
||||
errors := make([]error, len(in))
|
||||
func ValidateList[T any](in []T, validator func(T) error, checks ...func(in []T) error) error {
|
||||
errors := make([]error, len(in)+len(checks))
|
||||
|
||||
for id := range in {
|
||||
errors[id] = PrefixResourceError(fmt.Sprintf("[%d]", id), validator(in[id]))
|
||||
}
|
||||
|
||||
for id, c := range checks {
|
||||
errors[len(in)+id] = c(in)
|
||||
}
|
||||
|
||||
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))
|
||||
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)+len(checks))
|
||||
|
||||
for id := range in {
|
||||
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...)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
package features
|
||||
|
||||
import api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
|
||||
func init() {
|
||||
registerFeature(gateway)
|
||||
}
|
||||
|
@ -35,3 +37,7 @@ var gateway = &feature{
|
|||
func Gateway() Feature {
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
@ -36,12 +35,18 @@ import (
|
|||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
|
||||
)
|
||||
|
||||
func GroupSNISupported(mode api.DeploymentMode, group api.ServerGroup) bool {
|
||||
switch mode {
|
||||
func GroupSNISupported(spec api.DeploymentSpec, group api.ServerGroup) bool {
|
||||
switch spec.Mode.Get() {
|
||||
case api.DeploymentModeCluster:
|
||||
if features.IsGatewayEnabled(spec) {
|
||||
return group == api.ServerGroupGateways
|
||||
}
|
||||
return group == api.ServerGroupCoordinators
|
||||
|
||||
case api.DeploymentModeSingle:
|
||||
if features.IsGatewayEnabled(spec) {
|
||||
return group == api.ServerGroupGateways
|
||||
}
|
||||
fallthrough
|
||||
case api.DeploymentModeActiveFailover:
|
||||
return group == api.ServerGroupSingle
|
||||
|
@ -70,7 +75,7 @@ func (s sni) isSupported(i Input) bool {
|
|||
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 {
|
||||
|
@ -102,7 +107,7 @@ func (s sni) Volumes(i Input) ([]core.Volume, []core.VolumeMount) {
|
|||
volumeMounts := make([]core.VolumeMount, 0, len(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])
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ func (r *Reconciler) createRebalancerV2GeneratePlan(spec api.DeploymentSpec, sta
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)...)
|
||||
}
|
||||
}
|
||||
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)...)
|
||||
} else {
|
||||
plan = append(plan, r.createScalePlan(status, status.Members.Gateways, api.ServerGroupGateways, 0, context)...)
|
||||
|
|
|
@ -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.
|
||||
|
@ -57,7 +57,11 @@ func (r *Reconciler) createRotateTLSServerSNIPlan(ctx context.Context, apiObject
|
|||
|
||||
var plan api.Plan
|
||||
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
|
||||
}
|
||||
for _, m := range status.Members.MembersOfGroup(group) {
|
||||
|
|
|
@ -23,6 +23,7 @@ package resources
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
@ -65,7 +66,8 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec
|
|||
Name: configMapName,
|
||||
},
|
||||
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()
|
||||
} 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 {
|
||||
if existingSha, existingChecksumSha := util.SHA256FromString(cm.Data[GatewayConfigFileName]), cm.Data[GatewayConfigChecksumFileName]; existingSha != gatewayCfgChecksum || existingChecksumSha != 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),
|
||||
GatewayConfigFileName: string(gatewayCfgYaml),
|
||||
GatewayConfigChecksumFileName: gatewayCfgChecksum,
|
||||
})); err != nil {
|
||||
log.Err(err).Debug("Failed to patch GatewayConfig ConfigMap")
|
||||
return errors.WithStack(err)
|
||||
|
@ -116,6 +119,11 @@ func (r *Resources) renderGatewayConfig(cachedStatus inspectorInterface.Inspecto
|
|||
|
||||
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{
|
||||
Targets: []gateway.ConfigDestinationTarget{
|
||||
{
|
||||
|
@ -133,6 +141,25 @@ func (r *Resources) renderGatewayConfig(cachedStatus inspectorInterface.Inspecto
|
|||
PrivateKeyPath: keyPath,
|
||||
}
|
||||
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
|
||||
|
|
|
@ -49,7 +49,7 @@ func (r *Resources) EnsureConfigMaps(ctx context.Context, cachedStatus inspector
|
|||
|
||||
reconcileRequired := k8sutil.NewReconcile(cachedStatus)
|
||||
|
||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
||||
if features.IsGatewayEnabled(spec) {
|
||||
counterMetric.Inc()
|
||||
if err := reconcileRequired.WithError(r.ensureGatewayConfig(ctx, cachedStatus, configMaps)); err != nil {
|
||||
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 (
|
||||
"fmt"
|
||||
"sort"
|
||||
"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"
|
||||
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"
|
||||
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"
|
||||
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/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
|
@ -46,12 +53,18 @@ type Config struct {
|
|||
Destinations ConfigDestinations `json:"destinations,omitempty"`
|
||||
|
||||
DefaultTLS *ConfigTLS `json:"defaultTLS,omitempty"`
|
||||
|
||||
IntegrationSidecar *ConfigDestinationTarget `json:"integrationSidecar,omitempty"`
|
||||
|
||||
SNI ConfigSNIList `json:"sni,omitempty"`
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
return errors.Errors(
|
||||
shared.PrefixResourceErrors("defaultDestination", c.DefaultDestination.Validate()),
|
||||
shared.PrefixResourceErrors("integrationSidecar", c.IntegrationSidecar.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,
|
||||
}
|
||||
|
||||
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 {
|
||||
name := fmt.Sprintf("cluster_%s", util.SHA256FromString(k))
|
||||
c, err := v.RenderCluster(name)
|
||||
|
@ -159,6 +207,33 @@ func (c Config) RenderRoutes() ([]*routeAPI.Route, error) {
|
|||
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) {
|
||||
httpFilterConfigType, err := anypb.New(&routerAPI.Router{})
|
||||
if err != nil {
|
||||
|
@ -170,6 +245,16 @@ func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
|||
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{
|
||||
StatPrefix: "ingress_http",
|
||||
CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO,
|
||||
|
@ -185,14 +270,13 @@ func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
|||
},
|
||||
},
|
||||
},
|
||||
HttpFilters: []*httpConnectionManagerAPI.HttpFilter{
|
||||
{
|
||||
Name: "envoy.filters.http.routerAPI",
|
||||
ConfigType: &httpConnectionManagerAPI.HttpFilter_TypedConfig{
|
||||
TypedConfig: httpFilterConfigType,
|
||||
},
|
||||
HttpFilters: append(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")
|
||||
|
@ -211,7 +295,7 @@ func (c Config) RenderFilters() ([]*listenerAPI.Filter, error) {
|
|||
func (c Config) RenderDefaultFilterChain() (*listenerAPI.FilterChain, error) {
|
||||
filters, err := c.RenderFilters()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to render filters")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &listenerAPI.FilterChain{
|
||||
|
@ -228,7 +312,16 @@ func (c Config) RenderDefaultFilterChain() (*listenerAPI.FilterChain, error) {
|
|||
}
|
||||
|
||||
func (c Config) RenderSecondaryFilterChains() ([]*listenerAPI.FilterChain, error) {
|
||||
return nil, nil
|
||||
if len(c.SNI) == 0 {
|
||||
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) {
|
||||
|
@ -242,6 +335,22 @@ func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
|||
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{
|
||||
Name: "default",
|
||||
Address: &coreAPI.Address{
|
||||
|
@ -252,8 +361,8 @@ func (c Config) RenderListener() (*listenerAPI.Listener, error) {
|
|||
},
|
||||
},
|
||||
},
|
||||
FilterChains: filterChains,
|
||||
|
||||
FilterChains: filterChains,
|
||||
ListenerFilters: listenerFilters,
|
||||
DefaultFilterChain: defaultFilterChain,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
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"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
|
@ -63,7 +64,10 @@ type ConfigDestination struct {
|
|||
Path *string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (c ConfigDestination) Validate() error {
|
||||
func (c *ConfigDestination) Validate() error {
|
||||
if c == nil {
|
||||
c = &ConfigDestination{}
|
||||
}
|
||||
return shared.WithErrors(
|
||||
shared.PrefixResourceError("targets", c.Targets.Validate()),
|
||||
shared.PrefixResourceError("type", c.Type.Validate()),
|
||||
|
@ -71,15 +75,15 @@ func (c ConfigDestination) Validate() error {
|
|||
)
|
||||
}
|
||||
|
||||
func (c ConfigDestination) GetPath() string {
|
||||
if c.Path == nil {
|
||||
func (c *ConfigDestination) GetPath() string {
|
||||
if c == nil || c.Path == nil {
|
||||
return "/"
|
||||
}
|
||||
|
||||
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{
|
||||
Match: &routeAPI.RouteMatch{
|
||||
PathSpecifier: &routeAPI.RouteMatch_Prefix{
|
||||
|
@ -94,10 +98,11 @@ func (c ConfigDestination) RenderRoute(name, prefix string) (*routeAPI.Route, er
|
|||
PrefixRewrite: c.GetPath(),
|
||||
},
|
||||
},
|
||||
TypedPerFilterConfig: map[string]*anypb.Any{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c ConfigDestination) RenderCluster(name string) (*clusterAPI.Cluster, error) {
|
||||
func (c *ConfigDestination) RenderCluster(name string) (*clusterAPI.Cluster, error) {
|
||||
cluster := &clusterAPI.Cluster{
|
||||
Name: name,
|
||||
ConnectTimeout: durationpb.New(time.Second),
|
||||
|
|
|
@ -54,7 +54,10 @@ type ConfigDestinationTarget struct {
|
|||
Port int32 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
func (c ConfigDestinationTarget) Validate() error {
|
||||
func (c *ConfigDestinationTarget) Validate() error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return shared.WithErrors(
|
||||
shared.ValidateRequiredPath("ip", &c.Host, func(t string) error {
|
||||
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{
|
||||
HostIdentifier: &endpointAPI.LbEndpoint_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
|
||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
||||
if features.IsGatewayEnabled(spec) {
|
||||
minGateways = spec.GetServerGroupSpec(api.ServerGroupGateways).New().GetCount() - 1
|
||||
currGateways = status.Members.Gateways.MembersReady()
|
||||
}
|
||||
|
|
|
@ -30,11 +30,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
ArangoGatewayExecutor string = "/usr/local/bin/envoy"
|
||||
GatewayVolumeMountDir = "/etc/gateway/"
|
||||
GatewayVolumeName = "gateway"
|
||||
GatewayConfigFileName = "gateway.yaml"
|
||||
GatewayConfigFilePath = GatewayVolumeMountDir + GatewayConfigFileName
|
||||
ArangoGatewayExecutor string = "/usr/local/bin/envoy"
|
||||
GatewayVolumeMountDir = "/etc/gateway/"
|
||||
GatewayVolumeName = "gateway"
|
||||
GatewayConfigFileName = "gateway.yaml"
|
||||
GatewayConfigChecksumFileName = "gateway.checksum"
|
||||
GatewayConfigChecksumENV = "GATEWAY_CONFIG_CHECKSUM"
|
||||
GatewayConfigFilePath = GatewayVolumeMountDir + GatewayConfigFileName
|
||||
)
|
||||
|
||||
func GetGatewayConfigMapName(name string) string {
|
||||
|
@ -50,6 +52,9 @@ func createGatewayVolumes(input pod.Input) pod.Volumes {
|
|||
// TLS
|
||||
volumes.Append(pod.TLS(), input)
|
||||
|
||||
// SNI
|
||||
volumes.Append(pod.SNIGateway(), input)
|
||||
|
||||
return volumes
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,19 @@ func (a *ArangoGatewayContainer) GetEnvs() ([]core.EnvVar, []core.EnvFromSource)
|
|||
|
||||
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 {
|
||||
for _, env := range a.groupSpec.Envs {
|
||||
// Do not override preset envs
|
||||
|
|
|
@ -192,7 +192,13 @@ func (m *MemberGatewayPod) Init(ctx context.Context, cachedStatus interfaces.Ins
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -232,7 +238,9 @@ func (m *MemberGatewayPod) Labels() map[string]string {
|
|||
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})
|
||||
}, m.spec.Gateway.GetSidecar(), []string{shared.ServerContainerName}, sidecar.IntegrationEnvoyV3{
|
||||
Spec: m.spec,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -531,7 +531,7 @@ func (r *Resources) InspectPods(ctx context.Context, cachedStatus inspectorInter
|
|||
}
|
||||
|
||||
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, "", "")
|
||||
|
||||
// Update conditions
|
||||
|
|
|
@ -82,7 +82,7 @@ func (r *Resources) EnsureServices(ctx context.Context, cachedStatus inspectorIn
|
|||
defer metrics.SetDuration(inspectServicesDurationGauges.WithLabelValues(deploymentName), start)
|
||||
counterMetric := inspectedServicesCounters.WithLabelValues(deploymentName)
|
||||
|
||||
if features.Gateway().Enabled() && spec.IsGatewayEnabled() {
|
||||
if features.IsGatewayEnabled(spec) {
|
||||
role = api.ServerGroupGateways.AsRole()
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"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 {
|
||||
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.%s.external", strings.ToLower(name), strings.ToLower(ver)), c.GetExternal())
|
||||
options.Add(fmt.Sprintf("--integration.%s.internal", cmd), c.GetInternal())
|
||||
options.Add(fmt.Sprintf("--integration.%s.external", cmd), c.GetExternal())
|
||||
|
||||
return options
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ type IntegrationAuthenticationV1 struct {
|
|||
Deployment *api.ArangoDeployment
|
||||
}
|
||||
|
||||
func (i IntegrationAuthenticationV1) Name() (string, string) {
|
||||
return "AUTHENTICATION", "V1"
|
||||
func (i IntegrationAuthenticationV1) Name() []string {
|
||||
return []string{"AUTHENTICATION", "V1"}
|
||||
}
|
||||
|
||||
func (i IntegrationAuthenticationV1) Validate() error {
|
||||
|
|
|
@ -28,8 +28,8 @@ type IntegrationAuthorizationV0 struct {
|
|||
Core *Core
|
||||
}
|
||||
|
||||
func (i IntegrationAuthorizationV0) Name() (string, string) {
|
||||
return "AUTHORIZATION", "V0"
|
||||
func (i IntegrationAuthorizationV0) Name() []string {
|
||||
return []string{"AUTHORIZATION", "V0"}
|
||||
}
|
||||
|
||||
func (i IntegrationAuthorizationV0) Validate() error {
|
||||
|
|
|
@ -22,34 +22,26 @@ 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
|
||||
Core *Core
|
||||
Spec api.DeploymentSpec
|
||||
}
|
||||
|
||||
func (i IntegrationEnvoyV3) Name() (string, string) {
|
||||
return "ENVOY", "V3"
|
||||
func (i IntegrationEnvoyV3) Name() []string {
|
||||
return []string{"ENVOY", "AUTH", "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.Add("--integration.envoy.auth.v3", true)
|
||||
|
||||
options.Merge(i.Core.Args(i))
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ type IntegrationVolumes interface {
|
|||
}
|
||||
|
||||
type Integration interface {
|
||||
Name() (string, string)
|
||||
Name() []string
|
||||
Args() (k8sutil.OptionPairs, 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) {
|
||||
for _, integration := range integrations {
|
||||
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 {
|
||||
name, version := i.Name()
|
||||
name := strings.Join(i.Name(), "/")
|
||||
|
||||
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 {
|
||||
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 {
|
||||
options.Merge(args)
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
return nil, errors.Wrapf(err, "Failure in envs %s", name)
|
||||
} 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)),
|
||||
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()),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ type IntegrationShutdownV1 struct {
|
|||
Core *Core
|
||||
}
|
||||
|
||||
func (i IntegrationShutdownV1) Name() (string, string) {
|
||||
return "SHUTDOWN", "V1"
|
||||
func (i IntegrationShutdownV1) Name() []string {
|
||||
return []string{"SHUTDOWN", "V1"}
|
||||
}
|
||||
|
||||
func (i IntegrationShutdownV1) Validate() error {
|
||||
|
|
|
@ -26,6 +26,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/configmap"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/secret"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/service"
|
||||
)
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
type Inspector interface {
|
||||
secret.Inspector
|
||||
service.Inspector
|
||||
configmap.Inspector
|
||||
}
|
||||
|
||||
type PodModifier interface {
|
||||
|
|
|
@ -101,3 +101,9 @@ func FormatListErr[A, B any](in []A, format func(A) (B, error)) ([]B, error) {
|
|||
|
||||
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