mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
[Feature] Webhooks (#1781)
This commit is contained in:
parent
3993a0c40f
commit
58154dbf5b
66 changed files with 4372 additions and 463 deletions
|
@ -129,6 +129,8 @@ linters-settings:
|
|||
pkg: k8s.io/api/batch/v1
|
||||
- alias: core
|
||||
pkg: k8s.io/api/core/v1
|
||||
- alias: admission
|
||||
pkg: k8s.io/api/admission/v1
|
||||
- alias: policy
|
||||
pkg: k8s.io/api/policy/v1
|
||||
- alias: storage
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
- (Feature) (Scheduler) Shutdown Integration
|
||||
- (Feature) CertManager Integration
|
||||
- (Feature) (Networking) Gateway Options sync
|
||||
- (Feature) Webhooks
|
||||
|
||||
## [1.2.43](https://github.com/arangodb/kube-arangodb/tree/1.2.43) (2024-10-14)
|
||||
- (Feature) ArangoRoute CRD
|
||||
|
|
|
@ -195,7 +195,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, generic-parent-operator, helm, http, inspector, integration-config-v1, integration-envoy-auth-v3, integration-scheduler-v2, integration-storage-v2, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, platform-chart-operator, platform-pod-shutdown, platform-storage-operator, pod_compare, root, root-event-recorder, scheduler-batchjob-operator, scheduler-cronjob-operator, scheduler-deployment-operator, scheduler-pod-operator, scheduler-profile-operator, 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, generic-parent-operator, helm, http, inspector, integration-config-v1, integration-envoy-auth-v3, integration-scheduler-v2, integration-storage-v2, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, platform-chart-operator, platform-pod-shutdown, platform-storage-operator, pod_compare, root, root-event-recorder, scheduler-batchjob-operator, scheduler-cronjob-operator, scheduler-deployment-operator, scheduler-pod-operator, scheduler-profile-operator, server, server-authentication, webhook (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
|
||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
|||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{ if .Values.webhooks.enabled }}
|
||||
- name: webhooks
|
||||
imagePullPolicy: {{ .Values.operator.imagePullPolicy }}
|
||||
image: {{ .Values.operator.image }}
|
||||
args:
|
||||
- webhook
|
||||
{{- if .Values.certificate.enabled }}
|
||||
- --ssl.secret.name={{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
- --ssl.secret.namespace={{ .Release.Namespace }}
|
||||
{{- end -}}
|
||||
{{- if .Values.webhooks.args }}
|
||||
{{- range .Values.webhooks.args }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: MY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: MY_CONTAINER_NAME
|
||||
value: "webhooks"
|
||||
- name: MY_POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
ports:
|
||||
- name: webhooks
|
||||
containerPort: 8828
|
||||
securityContext:
|
||||
privileged: false
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- 'ALL'
|
||||
{{- if .Values.webhooks.resources }}
|
||||
resources:
|
||||
{{ toYaml .Values.webhooks.resources | indent 22 }}
|
||||
{{- end }}
|
||||
{{- if not .Values.webhooks.debug }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
tolerations:
|
||||
- key: "node.kubernetes.io/unreachable"
|
||||
|
|
31
chart/kube-arangodb-arm64/templates/service-webhooks.yaml
Normal file
31
chart/kube-arangodb-arm64/templates/service-webhooks.yaml
Normal file
|
@ -0,0 +1,31 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if .Values.operator.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.operator.annotations | indent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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: webhooks
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: webhooks
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
type: ClusterIP
|
||||
|
||||
{{- end }}
|
24
chart/kube-arangodb-arm64/templates/webhook/certificate.yaml
Normal file
24
chart/kube-arangodb-arm64/templates/webhook/certificate.yaml
Normal file
|
@ -0,0 +1,24 @@
|
|||
{{ if .Values.certificate.enabled -}}
|
||||
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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:
|
||||
secretName: {{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
duration: {{ .Values.certificate.cert.duration }}
|
||||
issuerRef:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}
|
||||
dnsNames:
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}.svc
|
||||
|
||||
{{- end }}
|
42
chart/kube-arangodb-arm64/templates/webhook/mutation.yaml
Normal file
42
chart/kube-arangodb-arm64/templates/webhook/mutation.yaml
Normal file
|
@ -0,0 +1,42 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks:
|
||||
- name: "pods.policies.scheduler.arangodb.com"
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- {{ .Release.Namespace }}
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: profiles.arangodb.com/deployment
|
||||
operator: Exists
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE"]
|
||||
resources: ["pods"]
|
||||
scope: "Namespaced"
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
path: /webhook/core/v1/pods/policies/mutate
|
||||
admissionReviewVersions: ["v1"]
|
||||
sideEffects: None
|
||||
timeoutSeconds: 5
|
||||
|
||||
{{- end }}
|
17
chart/kube-arangodb-arm64/templates/webhook/validation.yaml
Normal file
17
chart/kube-arangodb-arm64/templates/webhook/validation.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks: []
|
||||
|
||||
{{- end }}
|
|
@ -44,6 +44,16 @@ rbac:
|
|||
acs: true
|
||||
at: true
|
||||
debug: false
|
||||
webhooks:
|
||||
enabled: false
|
||||
args: []
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
certificate:
|
||||
enabled: false
|
||||
ca:
|
||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
|||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{ if .Values.webhooks.enabled }}
|
||||
- name: webhooks
|
||||
imagePullPolicy: {{ .Values.operator.imagePullPolicy }}
|
||||
image: {{ .Values.operator.image }}
|
||||
args:
|
||||
- webhook
|
||||
{{- if .Values.certificate.enabled }}
|
||||
- --ssl.secret.name={{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
- --ssl.secret.namespace={{ .Release.Namespace }}
|
||||
{{- end -}}
|
||||
{{- if .Values.webhooks.args }}
|
||||
{{- range .Values.webhooks.args }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: MY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: MY_CONTAINER_NAME
|
||||
value: "webhooks"
|
||||
- name: MY_POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
ports:
|
||||
- name: webhooks
|
||||
containerPort: 8828
|
||||
securityContext:
|
||||
privileged: false
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- 'ALL'
|
||||
{{- if .Values.webhooks.resources }}
|
||||
resources:
|
||||
{{ toYaml .Values.webhooks.resources | indent 22 }}
|
||||
{{- end }}
|
||||
{{- if not .Values.webhooks.debug }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
tolerations:
|
||||
- key: "node.kubernetes.io/unreachable"
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if .Values.operator.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.operator.annotations | indent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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: webhooks
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: webhooks
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
type: ClusterIP
|
||||
|
||||
{{- end }}
|
|
@ -0,0 +1,24 @@
|
|||
{{ if .Values.certificate.enabled -}}
|
||||
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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:
|
||||
secretName: {{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
duration: {{ .Values.certificate.cert.duration }}
|
||||
issuerRef:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}
|
||||
dnsNames:
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}.svc
|
||||
|
||||
{{- end }}
|
|
@ -0,0 +1,42 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks:
|
||||
- name: "pods.policies.scheduler.arangodb.com"
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- {{ .Release.Namespace }}
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: profiles.arangodb.com/deployment
|
||||
operator: Exists
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE"]
|
||||
resources: ["pods"]
|
||||
scope: "Namespaced"
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
path: /webhook/core/v1/pods/policies/mutate
|
||||
admissionReviewVersions: ["v1"]
|
||||
sideEffects: None
|
||||
timeoutSeconds: 5
|
||||
|
||||
{{- end }}
|
|
@ -0,0 +1,17 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks: []
|
||||
|
||||
{{- end }}
|
|
@ -44,6 +44,16 @@ rbac:
|
|||
acs: true
|
||||
at: true
|
||||
debug: false
|
||||
webhooks:
|
||||
enabled: false
|
||||
args: []
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
certificate:
|
||||
enabled: false
|
||||
ca:
|
||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
|||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{ if .Values.webhooks.enabled }}
|
||||
- name: webhooks
|
||||
imagePullPolicy: {{ .Values.operator.imagePullPolicy }}
|
||||
image: {{ .Values.operator.image }}
|
||||
args:
|
||||
- webhook
|
||||
{{- if .Values.certificate.enabled }}
|
||||
- --ssl.secret.name={{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
- --ssl.secret.namespace={{ .Release.Namespace }}
|
||||
{{- end -}}
|
||||
{{- if .Values.webhooks.args }}
|
||||
{{- range .Values.webhooks.args }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: MY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: MY_CONTAINER_NAME
|
||||
value: "webhooks"
|
||||
- name: MY_POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
ports:
|
||||
- name: webhooks
|
||||
containerPort: 8828
|
||||
securityContext:
|
||||
privileged: false
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- 'ALL'
|
||||
{{- if .Values.webhooks.resources }}
|
||||
resources:
|
||||
{{ toYaml .Values.webhooks.resources | indent 22 }}
|
||||
{{- end }}
|
||||
{{- if not .Values.webhooks.debug }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
tolerations:
|
||||
- key: "node.kubernetes.io/unreachable"
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if .Values.operator.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.operator.annotations | indent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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: webhooks
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: webhooks
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
type: ClusterIP
|
||||
|
||||
{{- end }}
|
|
@ -0,0 +1,24 @@
|
|||
{{ if .Values.certificate.enabled -}}
|
||||
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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:
|
||||
secretName: {{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
duration: {{ .Values.certificate.cert.duration }}
|
||||
issuerRef:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}
|
||||
dnsNames:
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}.svc
|
||||
|
||||
{{- end }}
|
|
@ -0,0 +1,42 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks:
|
||||
- name: "pods.policies.scheduler.arangodb.com"
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- {{ .Release.Namespace }}
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: profiles.arangodb.com/deployment
|
||||
operator: Exists
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE"]
|
||||
resources: ["pods"]
|
||||
scope: "Namespaced"
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
path: /webhook/core/v1/pods/policies/mutate
|
||||
admissionReviewVersions: ["v1"]
|
||||
sideEffects: None
|
||||
timeoutSeconds: 5
|
||||
|
||||
{{- end }}
|
|
@ -0,0 +1,17 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks: []
|
||||
|
||||
{{- end }}
|
|
@ -44,6 +44,16 @@ rbac:
|
|||
acs: true
|
||||
at: true
|
||||
debug: false
|
||||
webhooks:
|
||||
enabled: false
|
||||
args: []
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
certificate:
|
||||
enabled: false
|
||||
ca:
|
||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
|||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{ if .Values.webhooks.enabled }}
|
||||
- name: webhooks
|
||||
imagePullPolicy: {{ .Values.operator.imagePullPolicy }}
|
||||
image: {{ .Values.operator.image }}
|
||||
args:
|
||||
- webhook
|
||||
{{- if .Values.certificate.enabled }}
|
||||
- --ssl.secret.name={{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
- --ssl.secret.namespace={{ .Release.Namespace }}
|
||||
{{- end -}}
|
||||
{{- if .Values.webhooks.args }}
|
||||
{{- range .Values.webhooks.args }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: MY_POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: MY_CONTAINER_NAME
|
||||
value: "webhooks"
|
||||
- name: MY_POD_IP
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
ports:
|
||||
- name: webhooks
|
||||
containerPort: 8828
|
||||
securityContext:
|
||||
privileged: false
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- 'ALL'
|
||||
{{- if .Values.webhooks.resources }}
|
||||
resources:
|
||||
{{ toYaml .Values.webhooks.resources | indent 22 }}
|
||||
{{- end }}
|
||||
{{- if not .Values.webhooks.debug }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8828
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
tolerations:
|
||||
- key: "node.kubernetes.io/unreachable"
|
||||
|
|
31
chart/kube-arangodb/templates/service-webhooks.yaml
Normal file
31
chart/kube-arangodb/templates/service-webhooks.yaml
Normal file
|
@ -0,0 +1,31 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if .Values.operator.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.operator.annotations | indent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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: webhooks
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: webhooks
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
type: ClusterIP
|
||||
|
||||
{{- end }}
|
24
chart/kube-arangodb/templates/webhook/certificate.yaml
Normal file
24
chart/kube-arangodb/templates/webhook/certificate.yaml
Normal file
|
@ -0,0 +1,24 @@
|
|||
{{ if .Values.certificate.enabled -}}
|
||||
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.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:
|
||||
secretName: {{ template "kube-arangodb.operatorName" . }}-webhook-cert
|
||||
duration: {{ .Values.certificate.cert.duration }}
|
||||
issuerRef:
|
||||
name: {{ template "kube-arangodb.operatorName" . }}
|
||||
dnsNames:
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}
|
||||
- {{ template "kube-arangodb.operatorName" . }}-webhook.{{ .Release.Namespace }}.svc
|
||||
|
||||
{{- end }}
|
42
chart/kube-arangodb/templates/webhook/mutation.yaml
Normal file
42
chart/kube-arangodb/templates/webhook/mutation.yaml
Normal file
|
@ -0,0 +1,42 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks:
|
||||
- name: "pods.policies.scheduler.arangodb.com"
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: In
|
||||
values:
|
||||
- {{ .Release.Namespace }}
|
||||
objectSelector:
|
||||
matchExpressions:
|
||||
- key: profiles.arangodb.com/deployment
|
||||
operator: Exists
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE"]
|
||||
resources: ["pods"]
|
||||
scope: "Namespaced"
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
name: {{ template "kube-arangodb.operatorName" . }}-webhook
|
||||
path: /webhook/core/v1/pods/policies/mutate
|
||||
admissionReviewVersions: ["v1"]
|
||||
sideEffects: None
|
||||
timeoutSeconds: 5
|
||||
|
||||
{{- end }}
|
17
chart/kube-arangodb/templates/webhook/validation.yaml
Normal file
17
chart/kube-arangodb/templates/webhook/validation.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
{{ if .Values.webhooks.enabled }}
|
||||
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
name: "{{ template "kube-arangodb.operatorName" . }}.{{ .Release.Namespace }}.operator.arangodb.com"
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kube-arangodb.operatorName" . }}-ca"
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ template "kube-arangodb.name" . }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
release: {{ .Release.Name }}
|
||||
webhooks: []
|
||||
|
||||
{{- end }}
|
|
@ -45,6 +45,16 @@ rbac:
|
|||
acs: true
|
||||
at: true
|
||||
debug: false
|
||||
webhooks:
|
||||
enabled: false
|
||||
args: []
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
certificate:
|
||||
enabled: false
|
||||
ca:
|
||||
|
|
|
@ -84,9 +84,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
logger = logging.Global().RegisterAndGetLogger("root", logging.Info)
|
||||
eventRecorder = logging.Global().RegisterAndGetLogger("root-event-recorder", logging.Info)
|
||||
|
||||
cmdMain = cobra.Command{
|
||||
Use: "arangodb_operator",
|
||||
Run: executeMain,
|
||||
|
|
|
@ -22,17 +22,16 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/exporter"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -67,23 +66,14 @@ func init() {
|
|||
|
||||
func cmdExporterCheck(cmd *cobra.Command, args []string) {
|
||||
if err := cmdExporterCheckE(); err != nil {
|
||||
log.Error().Err(err).Msgf("Fatal")
|
||||
logger.Err(err).Error("Fatal")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func onSigterm(f func()) {
|
||||
sigs := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
defer f()
|
||||
<-sigs
|
||||
}()
|
||||
}
|
||||
|
||||
func cmdExporterCheckE() error {
|
||||
ctx := util.CreateSignalContext(context.Background())
|
||||
|
||||
if len(exporterInput.endpoints) < 1 {
|
||||
return errors.Errorf("Requires at least one ArangoDB Endpoint to be present")
|
||||
}
|
||||
|
@ -117,26 +107,34 @@ func cmdExporterCheckE() error {
|
|||
return string(data), nil
|
||||
}, false, 15*time.Second)
|
||||
|
||||
go mon.UpdateMonitorStatus(util.CreateSignalContext(context.Background()))
|
||||
go mon.UpdateMonitorStatus(ctx)
|
||||
|
||||
exporter := exporter.NewExporter(exporterInput.listenAddress, "/metrics", p)
|
||||
server, err := operatorHTTP.NewServer(ctx,
|
||||
operatorHTTP.DefaultHTTPServerSettings,
|
||||
operatorHTTP.WithServeMux(func(in *http.ServeMux) {
|
||||
in.Handle("/metrics", p)
|
||||
}, func(in *http.ServeMux) {
|
||||
in.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`<html>
|
||||
<head><title>ArangoDB Exporter</title></head>
|
||||
<body>
|
||||
<h1>ArangoDB Exporter</h1>
|
||||
<p><a href='/metrics'>Metrics</a></p>
|
||||
</body>
|
||||
</html>`))
|
||||
})
|
||||
}),
|
||||
operatorHTTP.WithTLSConfigFetcherGen(func() util.TLSConfigFetcher {
|
||||
if exporterInput.keyfile != "" {
|
||||
if e, err := exporter.WithKeyfile(exporterInput.keyfile); err != nil {
|
||||
return util.NewKeyfileTLSConfig(exporterInput.keyfile)
|
||||
}
|
||||
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if r, err := e.Start(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
onSigterm(r.Stop)
|
||||
return r.Wait()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if r, err := exporter.Start(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
onSigterm(r.Stop)
|
||||
return r.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
return server.StartAddr(ctx, exporterInput.listenAddress)
|
||||
}
|
||||
|
|
28
cmd/logger.go
Normal file
28
cmd/logger.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// 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 cmd
|
||||
|
||||
import "github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
|
||||
var (
|
||||
logger = logging.Global().RegisterAndGetLogger("root", logging.Info)
|
||||
eventRecorder = logging.Global().RegisterAndGetLogger("root-event-recorder", logging.Info)
|
||||
)
|
113
cmd/webhook.go
Normal file
113
cmd/webhook.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// 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 cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
goHttp "net/http"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/handlers/scheduler"
|
||||
"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/http"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
"github.com/arangodb/kube-arangodb/pkg/webhook"
|
||||
)
|
||||
|
||||
var (
|
||||
cmdWebhook = &cobra.Command{
|
||||
Use: "webhook",
|
||||
Run: cmdWebhookCheck,
|
||||
}
|
||||
|
||||
webhookInput struct {
|
||||
listenAddress string
|
||||
|
||||
secretName, secretNamespace string
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
f := cmdWebhook.PersistentFlags()
|
||||
|
||||
f.StringVar(&webhookInput.listenAddress, "server.address", "0.0.0.0:8828", "Address the webhook will listen on (IP:port)")
|
||||
f.StringVar(&webhookInput.secretName, "ssl.secret.name", "", "Secret Name containing TLS certificate used for the metrics server")
|
||||
f.StringVar(&webhookInput.secretNamespace, "ssl.secret.namespace", os.Getenv(constants.EnvOperatorPodNamespace), "Secret Name containing TLS certificate used for the metrics server")
|
||||
|
||||
cmdMain.AddCommand(cmdWebhook)
|
||||
}
|
||||
|
||||
func cmdWebhookCheck(cmd *cobra.Command, args []string) {
|
||||
if err := cmdWebhookCheckE(); err != nil {
|
||||
logger.Err(err).Error("Fatal")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdWebhookCheckE() error {
|
||||
ctx := util.CreateSignalContext(context.Background())
|
||||
|
||||
client, ok := kclient.GetDefaultFactory().Client()
|
||||
if !ok {
|
||||
return errors.Errorf("Unable to get client")
|
||||
}
|
||||
|
||||
var admissions webhook.Admissions
|
||||
|
||||
admissions = append(admissions, scheduler.WebhookAdmissions(client)...)
|
||||
|
||||
server, err := webhookServer(ctx, client, admissions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Str("addr", webhookInput.listenAddress).Info("Starting Webhook Server")
|
||||
|
||||
return server.StartAddr(ctx, webhookInput.listenAddress)
|
||||
}
|
||||
|
||||
func webhookServer(ctx context.Context, client kclient.Client, admissions ...webhook.Admission) (http.Server, error) {
|
||||
return http.NewServer(ctx,
|
||||
http.DefaultHTTPServerSettings,
|
||||
http.WithTLSConfigFetcherGen(func() util.TLSConfigFetcher {
|
||||
if webhookInput.secretName != "" && webhookInput.secretNamespace != "" {
|
||||
return util.NewSecretTLSConfig(client.Kubernetes().CoreV1().Secrets(webhookInput.secretNamespace), webhookInput.secretName)
|
||||
}
|
||||
|
||||
return util.NewSelfSignedTLSConfig("operator")
|
||||
}),
|
||||
http.WithServeMux(
|
||||
func(in *goHttp.ServeMux) {
|
||||
in.HandleFunc("/ready", func(writer goHttp.ResponseWriter, request *goHttp.Request) {
|
||||
writer.WriteHeader(goHttp.StatusOK)
|
||||
})
|
||||
in.HandleFunc("/health", func(writer goHttp.ResponseWriter, request *goHttp.Request) {
|
||||
writer.WriteHeader(goHttp.StatusOK)
|
||||
})
|
||||
},
|
||||
webhook.Admissions(admissions).Register(),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -406,5 +406,8 @@ Links:
|
|||
|
||||
### .spec.template.priority
|
||||
|
||||
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/scheduler/v1beta1/profile_template.go#L32)</sup>
|
||||
Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.43/pkg/apis/scheduler/v1beta1/profile_template.go#L34)</sup>
|
||||
|
||||
Priority defines Priority of the Profile. Higher value means Profile will get applied first.
|
||||
If Priority across Profiles is same, Profiles are also sorted by name.
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Available Commands:
|
|||
storage
|
||||
task
|
||||
version
|
||||
webhook
|
||||
|
||||
Flags:
|
||||
--action.PVCResize.concurrency int Define limit of concurrent PVC Resizes on the cluster (default 32)
|
||||
|
@ -80,7 +81,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, generic-parent-operator, helm, http, inspector, integration-config-v1, integration-envoy-auth-v3, integration-scheduler-v2, integration-storage-v2, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, platform-chart-operator, platform-pod-shutdown, platform-storage-operator, pod_compare, root, root-event-recorder, scheduler-batchjob-operator, scheduler-cronjob-operator, scheduler-deployment-operator, scheduler-pod-operator, scheduler-profile-operator, 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, generic-parent-operator, helm, http, inspector, integration-config-v1, integration-envoy-auth-v3, integration-scheduler-v2, integration-storage-v2, integrations, k8s-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, platform-chart-operator, platform-pod-shutdown, platform-storage-operator, pod_compare, root, root-event-recorder, scheduler-batchjob-operator, scheduler-cronjob-operator, scheduler-deployment-operator, scheduler-pod-operator, scheduler-profile-operator, server, server-authentication, webhook (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
|
||||
|
|
|
@ -8,7 +8,7 @@ parent: How to ...
|
|||
|
||||
1) Create a kubernetes [Secret](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/) with root password:
|
||||
```bash
|
||||
kubectl create secret generic arango-root-pwd --from-literal=password=<paste_your_password_here>
|
||||
kubectl create secret generic arango-root-pwd --from-literal=password=<paste_your_password_here> --from-literal=username=root
|
||||
```
|
||||
|
||||
1) Then specify the newly created secret in the ArangoDeploymentSpec:
|
||||
|
|
|
@ -29,6 +29,20 @@ metadata:
|
|||
profiles.arangodb.com/deployment: << deployment name >>
|
||||
```
|
||||
|
||||
### Webhooks
|
||||
|
||||
When Webhook support is enabled Integration Sidecar is supported in Kubernetes Pod resources.
|
||||
|
||||
To inject integration sidecar for specific deployment label needs to be defined:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
profiles.arangodb.com/deployment: << deployment name >>
|
||||
```
|
||||
|
||||
### Integrations
|
||||
|
||||
To enable integration in specific version, labels needs to be added:
|
||||
|
|
|
@ -798,117 +798,115 @@ var file_integrations_storage_v2_definition_storage_proto_rawDesc = []byte{
|
|||
0x0a, 0x30, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x08, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x1a, 0x1f, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x23, 0x0a,
|
||||
0x0d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61,
|
||||
0x74, 0x68, 0x22, 0x71, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f,
|
||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61,
|
||||
0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x1d, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x68, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
|
||||
0x12, 0x3d, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x22,
|
||||
0x3e, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x22,
|
||||
0x17, 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x49, 0x0a, 0x1a, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70,
|
||||
0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
||||
0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x60, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e,
|
||||
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04,
|
||||
0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x50, 0x0a, 0x1c, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79,
|
||||
0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73,
|
||||
0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x22, 0x49, 0x0a, 0x1a,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61,
|
||||
0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64,
|
||||
0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74,
|
||||
0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x50, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x1a, 0x1f, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x22, 0x6f, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||
0x12, 0x30, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e,
|
||||
0x66, 0x6f, 0x22, 0x68, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f,
|
||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x3d, 0x0a,
|
||||
0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
||||
0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x22, 0x3e, 0x0a, 0x14,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x88, 0x01,
|
||||
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x22, 0x17, 0x0a, 0x15,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x1a, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22,
|
||||
0x33, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64,
|
||||
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63,
|
||||
0x68, 0x75, 0x6e, 0x6b, 0x22, 0x5f, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56,
|
||||
0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
|
||||
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x50, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63,
|
||||
0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
|
||||
0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x22, 0x48, 0x0a, 0x1a, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x4b, 0x0a, 0x1c, 0x53, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68,
|
||||
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70,
|
||||
0x61, 0x74, 0x68, 0x22, 0x4f, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x05, 0x66,
|
||||
0x69, 0x6c, 0x65, 0x73, 0x32, 0xad, 0x04, 0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x56, 0x32, 0x12, 0x47, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x1e, 0x2e, 0x73, 0x68, 0x75,
|
||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49,
|
||||
0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x68, 0x75,
|
||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49,
|
||||
0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x0a, 0x52,
|
||||
0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x68, 0x75, 0x74,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65,
|
||||
0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x25, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5e, 0x0a, 0x0b, 0x57, 0x72, 0x69, 0x74,
|
||||
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74,
|
||||
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26,
|
||||
0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x22, 0x4f, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65,
|
||||
0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x30, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e,
|
||||
0x66, 0x6f, 0x22, 0x4a, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44,
|
||||
0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f,
|
||||
0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x49, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a,
|
||||
0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
||||
0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x4e, 0x0a, 0x1c, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||
0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69,
|
||||
0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0xa1, 0x04, 0x0a, 0x09, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x12, 0x45, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74,
|
||||
0x12, 0x1d, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1e, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x59, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x23, 0x2e,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56,
|
||||
0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x57, 0x72,
|
||||
0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69,
|
||||
0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x25, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x59, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64,
|
||||
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f,
|
||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73,
|
||||
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56,
|
||||
0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x12, 0x26, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x68,
|
||||
0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
||||
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x68, 0x75,
|
||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x30, 0x01, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x6f, 0x64, 0x62, 0x2f, 0x6b, 0x75, 0x62, 0x65,
|
||||
0x2d, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x6f, 0x64, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x76,
|
||||
0x32, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64,
|
||||
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48,
|
||||
0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||
0x74, 0x12, 0x25, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x5c, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12,
|
||||
0x24, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e,
|
||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x46,
|
||||
0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x61,
|
||||
0x6e, 0x67, 0x6f, 0x64, 0x62, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x2d, 0x61, 0x72, 0x61, 0x6e, 0x67,
|
||||
0x6f, 0x64, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x66, 0x69,
|
||||
0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -925,46 +923,46 @@ func file_integrations_storage_v2_definition_storage_proto_rawDescGZIP() []byte
|
|||
|
||||
var file_integrations_storage_v2_definition_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_integrations_storage_v2_definition_storage_proto_goTypes = []interface{}{
|
||||
(*StorageV2Path)(nil), // 0: shutdown.StorageV2Path
|
||||
(*StorageV2Object)(nil), // 1: shutdown.StorageV2Object
|
||||
(*StorageV2ObjectInfo)(nil), // 2: shutdown.StorageV2ObjectInfo
|
||||
(*StorageV2InitRequest)(nil), // 3: shutdown.StorageV2InitRequest
|
||||
(*StorageV2InitResponse)(nil), // 4: shutdown.StorageV2InitResponse
|
||||
(*StorageV2ReadObjectRequest)(nil), // 5: shutdown.StorageV2ReadObjectRequest
|
||||
(*StorageV2ReadObjectResponse)(nil), // 6: shutdown.StorageV2ReadObjectResponse
|
||||
(*StorageV2WriteObjectRequest)(nil), // 7: shutdown.StorageV2WriteObjectRequest
|
||||
(*StorageV2WriteObjectResponse)(nil), // 8: shutdown.StorageV2WriteObjectResponse
|
||||
(*StorageV2HeadObjectRequest)(nil), // 9: shutdown.StorageV2HeadObjectRequest
|
||||
(*StorageV2HeadObjectResponse)(nil), // 10: shutdown.StorageV2HeadObjectResponse
|
||||
(*StorageV2DeleteObjectRequest)(nil), // 11: shutdown.StorageV2DeleteObjectRequest
|
||||
(*StorageV2DeleteObjectResponse)(nil), // 12: shutdown.StorageV2DeleteObjectResponse
|
||||
(*StorageV2ListObjectsRequest)(nil), // 13: shutdown.StorageV2ListObjectsRequest
|
||||
(*StorageV2ListObjectsResponse)(nil), // 14: shutdown.StorageV2ListObjectsResponse
|
||||
(*StorageV2Path)(nil), // 0: storage.StorageV2Path
|
||||
(*StorageV2Object)(nil), // 1: storage.StorageV2Object
|
||||
(*StorageV2ObjectInfo)(nil), // 2: storage.StorageV2ObjectInfo
|
||||
(*StorageV2InitRequest)(nil), // 3: storage.StorageV2InitRequest
|
||||
(*StorageV2InitResponse)(nil), // 4: storage.StorageV2InitResponse
|
||||
(*StorageV2ReadObjectRequest)(nil), // 5: storage.StorageV2ReadObjectRequest
|
||||
(*StorageV2ReadObjectResponse)(nil), // 6: storage.StorageV2ReadObjectResponse
|
||||
(*StorageV2WriteObjectRequest)(nil), // 7: storage.StorageV2WriteObjectRequest
|
||||
(*StorageV2WriteObjectResponse)(nil), // 8: storage.StorageV2WriteObjectResponse
|
||||
(*StorageV2HeadObjectRequest)(nil), // 9: storage.StorageV2HeadObjectRequest
|
||||
(*StorageV2HeadObjectResponse)(nil), // 10: storage.StorageV2HeadObjectResponse
|
||||
(*StorageV2DeleteObjectRequest)(nil), // 11: storage.StorageV2DeleteObjectRequest
|
||||
(*StorageV2DeleteObjectResponse)(nil), // 12: storage.StorageV2DeleteObjectResponse
|
||||
(*StorageV2ListObjectsRequest)(nil), // 13: storage.StorageV2ListObjectsRequest
|
||||
(*StorageV2ListObjectsResponse)(nil), // 14: storage.StorageV2ListObjectsResponse
|
||||
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
|
||||
}
|
||||
var file_integrations_storage_v2_definition_storage_proto_depIdxs = []int32{
|
||||
0, // 0: shutdown.StorageV2Object.path:type_name -> shutdown.StorageV2Path
|
||||
2, // 1: shutdown.StorageV2Object.info:type_name -> shutdown.StorageV2ObjectInfo
|
||||
15, // 2: shutdown.StorageV2ObjectInfo.last_updated:type_name -> google.protobuf.Timestamp
|
||||
0, // 3: shutdown.StorageV2ReadObjectRequest.path:type_name -> shutdown.StorageV2Path
|
||||
0, // 4: shutdown.StorageV2WriteObjectRequest.path:type_name -> shutdown.StorageV2Path
|
||||
0, // 5: shutdown.StorageV2HeadObjectRequest.path:type_name -> shutdown.StorageV2Path
|
||||
2, // 6: shutdown.StorageV2HeadObjectResponse.info:type_name -> shutdown.StorageV2ObjectInfo
|
||||
0, // 7: shutdown.StorageV2DeleteObjectRequest.path:type_name -> shutdown.StorageV2Path
|
||||
0, // 8: shutdown.StorageV2ListObjectsRequest.path:type_name -> shutdown.StorageV2Path
|
||||
1, // 9: shutdown.StorageV2ListObjectsResponse.files:type_name -> shutdown.StorageV2Object
|
||||
3, // 10: shutdown.StorageV2.Init:input_type -> shutdown.StorageV2InitRequest
|
||||
5, // 11: shutdown.StorageV2.ReadObject:input_type -> shutdown.StorageV2ReadObjectRequest
|
||||
7, // 12: shutdown.StorageV2.WriteObject:input_type -> shutdown.StorageV2WriteObjectRequest
|
||||
9, // 13: shutdown.StorageV2.HeadObject:input_type -> shutdown.StorageV2HeadObjectRequest
|
||||
11, // 14: shutdown.StorageV2.DeleteObject:input_type -> shutdown.StorageV2DeleteObjectRequest
|
||||
13, // 15: shutdown.StorageV2.ListObjects:input_type -> shutdown.StorageV2ListObjectsRequest
|
||||
4, // 16: shutdown.StorageV2.Init:output_type -> shutdown.StorageV2InitResponse
|
||||
6, // 17: shutdown.StorageV2.ReadObject:output_type -> shutdown.StorageV2ReadObjectResponse
|
||||
8, // 18: shutdown.StorageV2.WriteObject:output_type -> shutdown.StorageV2WriteObjectResponse
|
||||
10, // 19: shutdown.StorageV2.HeadObject:output_type -> shutdown.StorageV2HeadObjectResponse
|
||||
12, // 20: shutdown.StorageV2.DeleteObject:output_type -> shutdown.StorageV2DeleteObjectResponse
|
||||
14, // 21: shutdown.StorageV2.ListObjects:output_type -> shutdown.StorageV2ListObjectsResponse
|
||||
0, // 0: storage.StorageV2Object.path:type_name -> storage.StorageV2Path
|
||||
2, // 1: storage.StorageV2Object.info:type_name -> storage.StorageV2ObjectInfo
|
||||
15, // 2: storage.StorageV2ObjectInfo.last_updated:type_name -> google.protobuf.Timestamp
|
||||
0, // 3: storage.StorageV2ReadObjectRequest.path:type_name -> storage.StorageV2Path
|
||||
0, // 4: storage.StorageV2WriteObjectRequest.path:type_name -> storage.StorageV2Path
|
||||
0, // 5: storage.StorageV2HeadObjectRequest.path:type_name -> storage.StorageV2Path
|
||||
2, // 6: storage.StorageV2HeadObjectResponse.info:type_name -> storage.StorageV2ObjectInfo
|
||||
0, // 7: storage.StorageV2DeleteObjectRequest.path:type_name -> storage.StorageV2Path
|
||||
0, // 8: storage.StorageV2ListObjectsRequest.path:type_name -> storage.StorageV2Path
|
||||
1, // 9: storage.StorageV2ListObjectsResponse.files:type_name -> storage.StorageV2Object
|
||||
3, // 10: storage.StorageV2.Init:input_type -> storage.StorageV2InitRequest
|
||||
5, // 11: storage.StorageV2.ReadObject:input_type -> storage.StorageV2ReadObjectRequest
|
||||
7, // 12: storage.StorageV2.WriteObject:input_type -> storage.StorageV2WriteObjectRequest
|
||||
9, // 13: storage.StorageV2.HeadObject:input_type -> storage.StorageV2HeadObjectRequest
|
||||
11, // 14: storage.StorageV2.DeleteObject:input_type -> storage.StorageV2DeleteObjectRequest
|
||||
13, // 15: storage.StorageV2.ListObjects:input_type -> storage.StorageV2ListObjectsRequest
|
||||
4, // 16: storage.StorageV2.Init:output_type -> storage.StorageV2InitResponse
|
||||
6, // 17: storage.StorageV2.ReadObject:output_type -> storage.StorageV2ReadObjectResponse
|
||||
8, // 18: storage.StorageV2.WriteObject:output_type -> storage.StorageV2WriteObjectResponse
|
||||
10, // 19: storage.StorageV2.HeadObject:output_type -> storage.StorageV2HeadObjectResponse
|
||||
12, // 20: storage.StorageV2.DeleteObject:output_type -> storage.StorageV2DeleteObjectResponse
|
||||
14, // 21: storage.StorageV2.ListObjects:output_type -> storage.StorageV2ListObjectsResponse
|
||||
16, // [16:22] is the sub-list for method output_type
|
||||
10, // [10:16] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
syntax = "proto3";
|
||||
|
||||
package shutdown;
|
||||
package storage;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ func NewStorageV2Client(cc grpc.ClientConnInterface) StorageV2Client {
|
|||
|
||||
func (c *storageV2Client) Init(ctx context.Context, in *StorageV2InitRequest, opts ...grpc.CallOption) (*StorageV2InitResponse, error) {
|
||||
out := new(StorageV2InitResponse)
|
||||
err := c.cc.Invoke(ctx, "/shutdown.StorageV2/Init", in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/storage.StorageV2/Init", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func (c *storageV2Client) Init(ctx context.Context, in *StorageV2InitRequest, op
|
|||
}
|
||||
|
||||
func (c *storageV2Client) ReadObject(ctx context.Context, in *StorageV2ReadObjectRequest, opts ...grpc.CallOption) (StorageV2_ReadObjectClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &StorageV2_ServiceDesc.Streams[0], "/shutdown.StorageV2/ReadObject", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StorageV2_ServiceDesc.Streams[0], "/storage.StorageV2/ReadObject", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func (x *storageV2ReadObjectClient) Recv() (*StorageV2ReadObjectResponse, error)
|
|||
}
|
||||
|
||||
func (c *storageV2Client) WriteObject(ctx context.Context, opts ...grpc.CallOption) (StorageV2_WriteObjectClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &StorageV2_ServiceDesc.Streams[1], "/shutdown.StorageV2/WriteObject", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StorageV2_ServiceDesc.Streams[1], "/storage.StorageV2/WriteObject", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ func (x *storageV2WriteObjectClient) CloseAndRecv() (*StorageV2WriteObjectRespon
|
|||
|
||||
func (c *storageV2Client) HeadObject(ctx context.Context, in *StorageV2HeadObjectRequest, opts ...grpc.CallOption) (*StorageV2HeadObjectResponse, error) {
|
||||
out := new(StorageV2HeadObjectResponse)
|
||||
err := c.cc.Invoke(ctx, "/shutdown.StorageV2/HeadObject", in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/storage.StorageV2/HeadObject", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func (c *storageV2Client) HeadObject(ctx context.Context, in *StorageV2HeadObjec
|
|||
|
||||
func (c *storageV2Client) DeleteObject(ctx context.Context, in *StorageV2DeleteObjectRequest, opts ...grpc.CallOption) (*StorageV2DeleteObjectResponse, error) {
|
||||
out := new(StorageV2DeleteObjectResponse)
|
||||
err := c.cc.Invoke(ctx, "/shutdown.StorageV2/DeleteObject", in, out, opts...)
|
||||
err := c.cc.Invoke(ctx, "/storage.StorageV2/DeleteObject", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func (c *storageV2Client) DeleteObject(ctx context.Context, in *StorageV2DeleteO
|
|||
}
|
||||
|
||||
func (c *storageV2Client) ListObjects(ctx context.Context, in *StorageV2ListObjectsRequest, opts ...grpc.CallOption) (StorageV2_ListObjectsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &StorageV2_ServiceDesc.Streams[2], "/shutdown.StorageV2/ListObjects", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StorageV2_ServiceDesc.Streams[2], "/storage.StorageV2/ListObjects", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ func _StorageV2_Init_Handler(srv interface{}, ctx context.Context, dec func(inte
|
|||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/shutdown.StorageV2/Init",
|
||||
FullMethod: "/storage.StorageV2/Init",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageV2Server).Init(ctx, req.(*StorageV2InitRequest))
|
||||
|
@ -298,7 +298,7 @@ func _StorageV2_HeadObject_Handler(srv interface{}, ctx context.Context, dec fun
|
|||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/shutdown.StorageV2/HeadObject",
|
||||
FullMethod: "/storage.StorageV2/HeadObject",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageV2Server).HeadObject(ctx, req.(*StorageV2HeadObjectRequest))
|
||||
|
@ -316,7 +316,7 @@ func _StorageV2_DeleteObject_Handler(srv interface{}, ctx context.Context, dec f
|
|||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/shutdown.StorageV2/DeleteObject",
|
||||
FullMethod: "/storage.StorageV2/DeleteObject",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageV2Server).DeleteObject(ctx, req.(*StorageV2DeleteObjectRequest))
|
||||
|
@ -349,7 +349,7 @@ func (x *storageV2ListObjectsServer) Send(m *StorageV2ListObjectsResponse) error
|
|||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var StorageV2_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "shutdown.StorageV2",
|
||||
ServiceName: "storage.StorageV2",
|
||||
HandlerType: (*StorageV2Server)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
|
|
5
pkg/api/api.go
generated
5
pkg/api/api.go
generated
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -21,6 +21,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -73,7 +74,7 @@ func NewServer(cli typedCore.CoreV1Interface, cfg ServerConfig) (*Server, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig, err := prepareTLSConfig(cli, cfg)
|
||||
tlsConfig, err := prepareTLSConfig(cli, cfg).Eval(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
63
pkg/api/tls.go
generated
63
pkg/api/tls.go
generated
|
@ -21,70 +21,15 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
"github.com/arangodb-helper/go-certificates"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
func prepareTLSConfig(cli typedCore.CoreV1Interface, cfg ServerConfig) (*tls.Config, error) {
|
||||
cert, key, err := loadOrSelfSignCertificate(cli, cfg)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
tlsConfig, err := createTLSConfig(cert, key)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// loadOrSelfSignCertificate loads TLS certificate from secret or creates a new one
|
||||
func loadOrSelfSignCertificate(cli typedCore.CoreV1Interface, cfg ServerConfig) (string, string, error) {
|
||||
func prepareTLSConfig(cli typedCore.CoreV1Interface, cfg ServerConfig) util.TLSConfigFetcher {
|
||||
if cfg.TLSSecretName != "" {
|
||||
// Load TLS certificate from secret
|
||||
s, err := cli.Secrets(cfg.Namespace).Get(context.Background(), cfg.TLSSecretName, meta.GetOptions{})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return util.NewSecretTLSConfig(cli.Secrets(cfg.Namespace), cfg.TLSSecretName)
|
||||
}
|
||||
certBytes, found := s.Data[core.TLSCertKey]
|
||||
if !found {
|
||||
return "", "", errors.Errorf("No %s found in secret %s", core.TLSCertKey, cfg.TLSSecretName)
|
||||
}
|
||||
keyBytes, found := s.Data[core.TLSPrivateKeyKey]
|
||||
if !found {
|
||||
return "", "", errors.Errorf("No %s found in secret %s", core.TLSPrivateKeyKey, cfg.TLSSecretName)
|
||||
}
|
||||
return string(certBytes), string(keyBytes), nil
|
||||
}
|
||||
// Secret not specified, create our own TLS certificate
|
||||
options := certificates.CreateCertificateOptions{
|
||||
CommonName: cfg.ServerName,
|
||||
Hosts: append([]string{cfg.ServerName}, cfg.ServerAltNames...),
|
||||
ValidFrom: time.Now(),
|
||||
ValidFor: time.Hour * 24 * 365 * 10,
|
||||
IsCA: false,
|
||||
ECDSACurve: "P256",
|
||||
}
|
||||
return certificates.CreateCertificate(options, nil)
|
||||
}
|
||||
|
||||
// createTLSConfig creates a TLS config based on given config
|
||||
func createTLSConfig(cert, key string) (*tls.Config, error) {
|
||||
var result *tls.Config
|
||||
c, err := tls.X509KeyPair([]byte(cert), []byte(key))
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
result = &tls.Config{
|
||||
Certificates: []tls.Certificate{c},
|
||||
}
|
||||
return result, nil
|
||||
return util.NewSelfSignedTLSConfig(cfg.ServerName, cfg.ServerAltNames...)
|
||||
}
|
||||
|
|
|
@ -28,8 +28,10 @@ import (
|
|||
)
|
||||
|
||||
type ProfileContainerTemplate struct {
|
||||
// Containers applies values per container
|
||||
Containers schedulerContainerApi.Containers `json:"containers,omitempty"`
|
||||
|
||||
// All applies generic values to all Containers
|
||||
All *schedulerContainerApi.Generic `json:"all,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -29,10 +29,14 @@ import (
|
|||
)
|
||||
|
||||
type ProfileTemplate struct {
|
||||
// Priority defines Priority of the Profile. Higher value means Profile will get applied first.
|
||||
// If Priority across Profiles is same, Profiles are also sorted by name.
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
|
||||
// Pod Template
|
||||
Pod *schedulerPodApi.Pod `json:"pod,omitempty"`
|
||||
|
||||
// Container Template
|
||||
Container *ProfileContainerTemplate `json:"container,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -60,6 +60,8 @@ func (p Path) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
type Items []Item
|
||||
|
||||
type Item struct {
|
||||
Op Operation `json:"op"`
|
||||
Path Path `json:"path"`
|
||||
|
|
|
@ -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.
|
||||
|
@ -20,13 +20,6 @@
|
|||
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||
)
|
||||
|
||||
type Authentication func() (string, error)
|
||||
|
||||
// CreateArangodJwtAuthorizationHeader calculates a JWT authorization header, for authorization
|
||||
|
@ -35,27 +28,3 @@ type Authentication func() (string, error)
|
|||
func CreateArangodJwtAuthorizationHeader(jwt string) (string, error) {
|
||||
return "bearer " + jwt, nil
|
||||
}
|
||||
|
||||
func NewExporter(endpoint string, url string, handler http.Handler) operatorHTTP.PlainServer {
|
||||
s := http.NewServeMux()
|
||||
|
||||
s.Handle(url, handler)
|
||||
|
||||
s.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`<html>
|
||||
<head><title>ArangoDB Exporter</title></head>
|
||||
<body>
|
||||
<h1>ArangoDB Exporter</h1>
|
||||
<p><a href='/metrics'>Metrics</a></p>
|
||||
</body>
|
||||
</html>`))
|
||||
})
|
||||
|
||||
return operatorHTTP.NewServer(&http.Server{
|
||||
Addr: endpoint,
|
||||
ReadTimeout: time.Second * 30,
|
||||
ReadHeaderTimeout: time.Second * 15,
|
||||
WriteTimeout: time.Second * 30,
|
||||
Handler: s,
|
||||
})
|
||||
}
|
||||
|
|
43
pkg/handlers/scheduler/admissions.go
Normal file
43
pkg/handlers/scheduler/admissions.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// 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 scheduler
|
||||
|
||||
import (
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/handlers/scheduler/webhooks/policies"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/constants"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
"github.com/arangodb/kube-arangodb/pkg/webhook"
|
||||
)
|
||||
|
||||
func WebhookAdmissions(client kclient.Client) webhook.Admissions {
|
||||
return webhook.Admissions{
|
||||
webhook.NewAdmissionHandler[*core.Pod](
|
||||
"policies",
|
||||
constants.PodGroup,
|
||||
constants.PodVersionV1,
|
||||
constants.PodKind,
|
||||
constants.PodResource,
|
||||
policies.NewPoliciesPodHandler(client),
|
||||
),
|
||||
}
|
||||
}
|
137
pkg/handlers/scheduler/webhooks/policies/handler.go
Normal file
137
pkg/handlers/scheduler/webhooks/policies/handler.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// 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 policies
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
admission "k8s.io/api/admission/v1"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
|
||||
"github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
"github.com/arangodb/kube-arangodb/pkg/scheduler"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/constants"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
kerrors "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
"github.com/arangodb/kube-arangodb/pkg/webhook"
|
||||
)
|
||||
|
||||
func NewPoliciesPodHandler(client kclient.Client) webhook.Handler[*core.Pod] {
|
||||
return handler{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
var _ webhook.MutationHandler[*core.Pod] = handler{}
|
||||
|
||||
type handler struct {
|
||||
client kclient.Client
|
||||
}
|
||||
|
||||
func (h handler) CanHandle(ctx context.Context, log logging.Logger, t webhook.AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) bool {
|
||||
if request == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if request.Operation != admission.Create {
|
||||
return false
|
||||
}
|
||||
|
||||
if new == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := new.GetLabels()[constants.ProfilesDeployment]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (h handler) Mutate(ctx context.Context, log logging.Logger, t webhook.AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) (webhook.MutationResponse, error) {
|
||||
if !h.CanHandle(ctx, log, t, request, old, new) {
|
||||
return webhook.MutationResponse{}, errors.Errorf("Object cannot be handled")
|
||||
}
|
||||
|
||||
labels := new.GetLabels()
|
||||
|
||||
v := labels[constants.ProfilesDeployment]
|
||||
depl, err := h.client.Arango().DatabaseV1().ArangoDeployments(request.Namespace).Get(ctx, v, meta.GetOptions{})
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return webhook.MutationResponse{
|
||||
ValidationResponse: webhook.NewValidationResponse(false, "ArangoDeployment %s/%s not found", request.Namespace, v),
|
||||
}, nil
|
||||
}
|
||||
return webhook.MutationResponse{
|
||||
ValidationResponse: webhook.NewValidationResponse(false, "Unable to get ArangoDeployment %s/%s: %s", request.Namespace, v, err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
profiles := util.FilterList(util.FormatList(strings.Split(labels[constants.ProfilesList], ","), func(s string) string {
|
||||
return strings.TrimSpace(s)
|
||||
}), func(s string) bool {
|
||||
return s != ""
|
||||
})
|
||||
|
||||
calculatedProfiles, profilesChecksum, err := scheduler.Profiles(ctx, h.client.Arango().SchedulerV1beta1().ArangoProfiles(depl.GetNamespace()), labels, profiles...)
|
||||
if err != nil {
|
||||
return webhook.MutationResponse{
|
||||
ValidationResponse: webhook.NewValidationResponse(false, "Unable to get ArangoProfiles: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var template core.PodTemplateSpec
|
||||
|
||||
template.Labels = new.GetLabels()
|
||||
template.Annotations = new.GetAnnotations()
|
||||
new.Spec.DeepCopyInto(&template.Spec)
|
||||
|
||||
if template.Annotations == nil {
|
||||
template.Annotations = map[string]string{}
|
||||
}
|
||||
|
||||
template.Annotations[constants.ProfilesAnnotationApplied] = "true"
|
||||
template.Annotations[constants.ProfilesAnnotationChecksum] = profilesChecksum
|
||||
template.Annotations[constants.ProfilesAnnotationProfiles] = strings.Join(util.FormatList(calculatedProfiles, func(a util.KV[string, schedulerApi.ProfileAcceptedTemplate]) string {
|
||||
return a.K
|
||||
}), ",")
|
||||
|
||||
if err := schedulerApi.ProfileTemplates(util.FormatList(calculatedProfiles, func(a util.KV[string, schedulerApi.ProfileAcceptedTemplate]) *schedulerApi.ProfileTemplate {
|
||||
return a.V.Template
|
||||
})).RenderOnTemplate(&template); err != nil {
|
||||
return webhook.MutationResponse{
|
||||
ValidationResponse: webhook.NewValidationResponse(false, "Unable to get apply ArangoProfiles: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return webhook.MutationResponse{
|
||||
ValidationResponse: webhook.ValidationResponse{Allowed: true},
|
||||
Patch: []patch.Item{
|
||||
patch.ItemReplace(patch.NewPath("metadata", "labels"), template.Labels),
|
||||
patch.ItemReplace(patch.NewPath("metadata", "annotations"), template.Annotations),
|
||||
patch.ItemReplace(patch.NewPath("spec"), template.Spec),
|
||||
},
|
||||
}, nil
|
||||
}
|
40
pkg/logging/http.go
Normal file
40
pkg/logging/http.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func HTTPRequestWrap(request *http.Request) Wrap {
|
||||
return func(in *zerolog.Event) *zerolog.Event {
|
||||
if request == nil {
|
||||
return in
|
||||
}
|
||||
|
||||
in = in.Str("method", request.Method)
|
||||
in = in.Str("url", request.RequestURI)
|
||||
|
||||
return in
|
||||
}
|
||||
}
|
|
@ -29,13 +29,10 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jessevdk/go-assets"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
"github.com/arangodb-helper/go-certificates"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/dashboard"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/metrics"
|
||||
|
@ -108,40 +105,16 @@ func NewServer(cli typedCore.CoreV1Interface, cfg Config, deps Dependencies) (*S
|
|||
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
||||
}
|
||||
|
||||
var cert, key string
|
||||
var fetcher util.TLSConfigFetcher
|
||||
if cfg.TLSSecretName != "" && cfg.TLSSecretNamespace != "" {
|
||||
serverLogger.Str("addr", cfg.Address).Str("secret", cfg.TLSSecretName).Str("secret-namespace", cfg.TLSSecretNamespace).Info("Using existing TLS Certificate")
|
||||
s, err := cli.Secrets(cfg.TLSSecretNamespace).Get(context.Background(), cfg.TLSSecretName, meta.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
certBytes, found := s.Data[core.TLSCertKey]
|
||||
if !found {
|
||||
return nil, errors.WithStack(errors.Errorf("No %s found in secret %s", core.TLSCertKey, cfg.TLSSecretName))
|
||||
}
|
||||
keyBytes, found := s.Data[core.TLSPrivateKeyKey]
|
||||
if !found {
|
||||
return nil, errors.WithStack(errors.Errorf("No %s found in secret %s", core.TLSPrivateKeyKey, cfg.TLSSecretName))
|
||||
}
|
||||
cert = string(certBytes)
|
||||
key = string(keyBytes)
|
||||
fetcher = util.NewSecretTLSConfig(cli.Secrets(cfg.TLSSecretNamespace), cfg.TLSSecretName)
|
||||
} else {
|
||||
serverLogger.Str("addr", cfg.Address).Info("Using SelfSigned TLS Certificate")
|
||||
options := certificates.CreateCertificateOptions{
|
||||
CommonName: cfg.PodName,
|
||||
Hosts: []string{cfg.PodName, cfg.PodIP},
|
||||
ValidFrom: time.Now(),
|
||||
ValidFor: time.Hour * 24 * 365 * 10,
|
||||
IsCA: false,
|
||||
ECDSACurve: "P256",
|
||||
fetcher = util.NewSelfSignedTLSConfig(cfg.PodName, cfg.PodIP)
|
||||
}
|
||||
var err error
|
||||
cert, key, err = certificates.CreateCertificate(options, nil)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
tlsConfig, err := createTLSConfig(cert, key)
|
||||
|
||||
tlsConfig, err := fetcher.Eval(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
@ -256,19 +229,6 @@ func (s *Server) Run() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// createTLSConfig creates a TLS config based on given config
|
||||
func createTLSConfig(cert, key string) (*tls.Config, error) {
|
||||
var result *tls.Config
|
||||
c, err := tls.X509KeyPair([]byte(cert), []byte(key))
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
result = &tls.Config{
|
||||
Certificates: []tls.Certificate{c},
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ready(probes ...*probe.ReadyProbe) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
for _, probe := range probes {
|
||||
|
|
|
@ -26,6 +26,11 @@ const ProfileGroup = "profiles.arangodb.com"
|
|||
|
||||
const ProfilesDeployment = ProfileGroup + "/deployment"
|
||||
const ProfilesIntegrationPrefix = "integration." + ProfileGroup
|
||||
const ProfilesList = ProfileGroup + "/profiles"
|
||||
|
||||
const ProfilesAnnotationApplied = ProfileGroup + "/applied"
|
||||
const ProfilesAnnotationChecksum = ProfileGroup + "/checksum"
|
||||
const ProfilesAnnotationProfiles = ProfileGroup + "/profiles"
|
||||
|
||||
const (
|
||||
ProfilesIntegrationAuthn = "authn"
|
||||
|
|
76
pkg/util/http/error.go
Normal file
76
pkg/util/http/error.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// 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 http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func WrapError(code int, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewError(code, err.Error())
|
||||
}
|
||||
|
||||
func NewError(code int, format string, args ...any) error {
|
||||
return Error{
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func IsError(err error) (Error, bool) {
|
||||
if err == nil {
|
||||
return Error{}, false
|
||||
}
|
||||
|
||||
var v Error
|
||||
if errors.As(err, &v) {
|
||||
return v, true
|
||||
}
|
||||
|
||||
return Error{}, false
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("HTTP Error (%d): %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
func (e Error) JSON() []byte {
|
||||
data, err := json.Marshal(map[string]any{
|
||||
"Code": e.Code,
|
||||
"Message": e.Message,
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
|
@ -21,140 +21,147 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/arangodb-helper/go-certificates"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
func NewServer(server *http.Server) PlainServer {
|
||||
return &plainServer{server: server}
|
||||
func DefaultHTTPServerSettings(in *http.Server, _ context.Context) error {
|
||||
in.ReadTimeout = time.Second * 30
|
||||
in.ReadHeaderTimeout = time.Second * 15
|
||||
in.WriteTimeout = time.Second * 30
|
||||
in.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServerRunner interface {
|
||||
Stop()
|
||||
Wait() error
|
||||
func WithTLSConfigFetcherGen(gen func() util.TLSConfigFetcher) util.ModEP1[http.Server, context.Context] {
|
||||
return WithTLSConfigFetcher(gen())
|
||||
}
|
||||
|
||||
type PlainServer interface {
|
||||
Server
|
||||
WithSSL(key, cert string) (Server, error)
|
||||
WithKeyfile(keyfile string) (Server, error)
|
||||
func WithTLSConfigFetcher(fetcher util.TLSConfigFetcher) util.ModEP1[http.Server, context.Context] {
|
||||
return func(in *http.Server, p1 context.Context) error {
|
||||
v, err := fetcher.Eval(p1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
in.TLSConfig = v
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithServeMux(mods ...util.Mod[http.ServeMux]) util.ModEP1[http.Server, context.Context] {
|
||||
return func(in *http.Server, p1 context.Context) error {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
util.ApplyMods(mux, mods...)
|
||||
|
||||
in.Handler = mux
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, mods ...util.ModEP1[http.Server, context.Context]) (Server, error) {
|
||||
var sv http.Server
|
||||
|
||||
if err := util.ApplyModsEP1(&sv, ctx, mods...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &server{
|
||||
server: &sv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Server interface {
|
||||
Start() (ServerRunner, error)
|
||||
AsyncAddr(ctx context.Context, addr string) func() error
|
||||
Async(ctx context.Context, ln net.Listener) func() error
|
||||
|
||||
StartAddr(ctx context.Context, addr string) error
|
||||
Start(ctx context.Context, ln net.Listener) error
|
||||
}
|
||||
|
||||
var _ Server = &tlsServer{}
|
||||
|
||||
type tlsServer struct {
|
||||
type server struct {
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
func (t *tlsServer) Start() (ServerRunner, error) {
|
||||
i := serverRunner{
|
||||
stopCh: make(chan struct{}),
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
func (s *server) AsyncAddr(ctx context.Context, addr string) func() error {
|
||||
var err error
|
||||
|
||||
go i.run(t.server, func(s *http.Server) error {
|
||||
return s.ListenAndServeTLS("", "")
|
||||
})
|
||||
done := make(chan any)
|
||||
|
||||
return &i, nil
|
||||
}
|
||||
|
||||
var _ PlainServer = &plainServer{}
|
||||
|
||||
type plainServer struct {
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
func (p *plainServer) WithKeyfile(keyfile string) (Server, error) {
|
||||
certificate, err := certificates.LoadKeyFile(keyfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := p.server
|
||||
s.TLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
}
|
||||
|
||||
return &tlsServer{server: s}, nil
|
||||
}
|
||||
|
||||
func (p *plainServer) Start() (ServerRunner, error) {
|
||||
i := serverRunner{
|
||||
stopCh: make(chan struct{}),
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
go i.run(p.server, func(s *http.Server) error {
|
||||
return s.ListenAndServe()
|
||||
})
|
||||
|
||||
return &i, nil
|
||||
}
|
||||
|
||||
func (p *plainServer) WithSSL(key, cert string) (Server, error) {
|
||||
certificate, err := tls.LoadX509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := p.server
|
||||
s.TLSConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
}
|
||||
|
||||
return &tlsServer{server: s}, nil
|
||||
}
|
||||
|
||||
var _ ServerRunner = &serverRunner{}
|
||||
|
||||
type serverRunner struct {
|
||||
lock sync.Mutex
|
||||
|
||||
stopCh chan struct{}
|
||||
doneCh chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *serverRunner) run(server *http.Server, f func(s *http.Server) error) {
|
||||
go func() {
|
||||
defer close(s.doneCh)
|
||||
if err := f(server); err != nil {
|
||||
defer close(done)
|
||||
|
||||
err = s.StartAddr(ctx, addr)
|
||||
}()
|
||||
|
||||
return func() error {
|
||||
<-done
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) Async(ctx context.Context, ln net.Listener) func() error {
|
||||
var err error
|
||||
|
||||
done := make(chan any)
|
||||
|
||||
go func() {
|
||||
defer close(done)
|
||||
|
||||
err = s.Start(ctx, ln)
|
||||
}()
|
||||
|
||||
return func() error {
|
||||
<-done
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) StartAddr(ctx context.Context, addr string) error {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Start(ctx, ln)
|
||||
}
|
||||
|
||||
func (s *server) Start(ctx context.Context, ln net.Listener) error {
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
|
||||
if err := s.server.Close(); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
s.err = err
|
||||
logger.Err(err).Warn("Unable to close server")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
<-s.stopCh
|
||||
|
||||
server.Close()
|
||||
|
||||
<-s.doneCh
|
||||
}
|
||||
|
||||
func (s *serverRunner) Stop() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
select {
|
||||
case <-s.stopCh:
|
||||
return
|
||||
default:
|
||||
close(s.stopCh)
|
||||
if s.server.TLSConfig == nil {
|
||||
if err := s.server.Serve(ln); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := s.server.ServeTLS(ln, "", ""); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serverRunner) Wait() error {
|
||||
<-s.doneCh
|
||||
|
||||
return s.err
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -122,6 +122,18 @@ func FormatList[A, B any](in []A, format func(A) B) []B {
|
|||
return r
|
||||
}
|
||||
|
||||
func FilterList[A any](in []A, filter func(A) bool) []A {
|
||||
r := make([]A, 0, len(in))
|
||||
|
||||
for _, el := range in {
|
||||
if filter(el) {
|
||||
r = append(r, el)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func ContainsList[A comparable](in []A, item A) bool {
|
||||
for _, el := range in {
|
||||
if el == item {
|
||||
|
|
|
@ -22,16 +22,6 @@ package util
|
|||
|
||||
func emptyMod[T any](_ *T) {}
|
||||
|
||||
func WithMods[T any](mods ...Mod[T]) Mod[T] {
|
||||
return func(in *T) {
|
||||
for _, m := range mods {
|
||||
if m != nil {
|
||||
m(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Mod[T any] func(in *T)
|
||||
|
||||
func (m Mod[T]) Optional() Mod[T] {
|
||||
|
@ -47,3 +37,51 @@ func ApplyMods[T any](in *T, mods ...Mod[T]) {
|
|||
mod(in)
|
||||
}
|
||||
}
|
||||
|
||||
func emptyModE[T any](_ *T) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ModE[T any] func(in *T) error
|
||||
|
||||
func (m ModE[T]) Optional() ModE[T] {
|
||||
if m == nil {
|
||||
return emptyModE[T]
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func ApplyModsE[T any](in *T, mods ...ModE[T]) error {
|
||||
for _, mod := range mods {
|
||||
if err := mod(in); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func emptyModEP1[T, P1 any](_ *T, _ P1) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ModEP1[T, P1 any] func(in *T, p1 P1) error
|
||||
|
||||
func (m ModEP1[T, P1]) Optional() ModEP1[T, P1] {
|
||||
if m == nil {
|
||||
return emptyModEP1[T, P1]
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func ApplyModsEP1[T, P1 any](in *T, p1 P1, mods ...ModEP1[T, P1]) error {
|
||||
for _, mod := range mods {
|
||||
if err := mod(in, p1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ package util
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NewPointer returns a reference to a copy of the pointer value
|
||||
|
@ -135,6 +137,42 @@ func InitType[T interface{}](in *T) *T {
|
|||
return &q
|
||||
}
|
||||
|
||||
func DeepType[T any]() (T, error) {
|
||||
var z T
|
||||
|
||||
if err := InitDeepType(&z); err != nil {
|
||||
return Default[T](), err
|
||||
}
|
||||
|
||||
return z, nil
|
||||
}
|
||||
|
||||
func InitDeepType(in any) error {
|
||||
return initDeepType(reflect.ValueOf(in))
|
||||
}
|
||||
|
||||
func initDeepType(v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Pointer:
|
||||
if !v.Elem().CanSet() {
|
||||
return errors.Errorf("Unable to set interface")
|
||||
}
|
||||
|
||||
switch v.Elem().Kind() {
|
||||
case reflect.Pointer:
|
||||
nv := reflect.New(v.Type().Elem().Elem())
|
||||
if err := initDeepType(nv); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Elem().Set(nv)
|
||||
default:
|
||||
v.Elem().Set(reflect.New(v.Elem().Type()).Elem())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ConditionalFunction[T interface{}] func() (T, bool)
|
||||
type ConditionalP1Function[T, P1 interface{}] func(p1 P1) (T, bool)
|
||||
|
||||
|
|
58
pkg/util/tests/http.go
Normal file
58
pkg/util/tests/http.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
goHttp "net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||
)
|
||||
|
||||
func NewHTTPServer(ctx context.Context, t *testing.T, mods ...util.ModEP1[goHttp.Server, context.Context]) string {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
pr, ok := ln.Addr().(*net.TCPAddr)
|
||||
require.True(t, ok)
|
||||
|
||||
addr, port := pr.IP.String(), pr.Port
|
||||
|
||||
server, err := http.NewServer(ctx, mods...)
|
||||
require.NoError(t, err)
|
||||
|
||||
closer := server.Async(ctx, ln)
|
||||
|
||||
WaitForTCPPort(addr, port).WithContextTimeoutT(t, ctx, 10*time.Second, 125*time.Millisecond)
|
||||
|
||||
go func() {
|
||||
require.NoError(t, closer())
|
||||
}()
|
||||
|
||||
return fmt.Sprintf("%s:%d", addr, port)
|
||||
}
|
|
@ -55,6 +55,7 @@ import (
|
|||
arangoClientSet "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
|
||||
operator "github.com/arangodb/kube-arangodb/pkg/operatorV2"
|
||||
"github.com/arangodb/kube-arangodb/pkg/operatorV2/operation"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
|
@ -1506,13 +1507,8 @@ func NewMetaObjectInDefaultNamespace[T meta.Object](t *testing.T, name string, m
|
|||
}
|
||||
|
||||
func NewMetaObject[T meta.Object](t *testing.T, namespace, name string, mods ...MetaObjectMod[T]) T {
|
||||
var obj T
|
||||
|
||||
if objT := reflect.TypeOf(obj); objT.Kind() == reflect.Pointer {
|
||||
newObj := reflect.New(objT.Elem())
|
||||
|
||||
reflect.ValueOf(&obj).Elem().Set(newObj)
|
||||
}
|
||||
obj, err := util.DeepType[T]()
|
||||
require.NoError(t, err)
|
||||
|
||||
if IsNamespaced(obj) {
|
||||
obj.SetNamespace(namespace)
|
||||
|
|
42
pkg/util/tests/port.go
Normal file
42
pkg/util/tests/port.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// 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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func WaitForTCPPort(addr string, port int) Timeout {
|
||||
return func() error {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", addr, port), time.Second)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := conn.Close(); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Interrupt()
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -83,6 +84,36 @@ func (t Timeout) WithTimeout(timeout, interval time.Duration) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (t Timeout) WithContextTimeout(ctx context.Context, timeout, interval time.Duration) error {
|
||||
timeoutT := time.NewTimer(timeout)
|
||||
defer timeoutT.Stop()
|
||||
|
||||
intervalT := time.NewTicker(interval)
|
||||
defer intervalT.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Errorf("ContextCancelled!")
|
||||
case <-timeoutT.C:
|
||||
return errors.Errorf("Timeouted!")
|
||||
case <-intervalT.C:
|
||||
if err := t(); err != nil {
|
||||
var interrupt interrupt
|
||||
if errors.As(err, &interrupt) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t Timeout) WithTimeoutT(z *testing.T, timeout, interval time.Duration) {
|
||||
require.NoError(z, t.WithTimeout(timeout, interval))
|
||||
}
|
||||
|
||||
func (t Timeout) WithContextTimeoutT(z *testing.T, ctx context.Context, timeout, interval time.Duration) {
|
||||
require.NoError(z, t.WithContextTimeout(ctx, timeout, interval))
|
||||
}
|
||||
|
|
121
pkg/util/tls.go
Normal file
121
pkg/util/tls.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb-helper/go-certificates"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/globals"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector/generic"
|
||||
)
|
||||
|
||||
type TLSConfigFetcher func(ctx context.Context) (*tls.Config, error)
|
||||
|
||||
func (t TLSConfigFetcher) Eval(ctx context.Context) (*tls.Config, error) {
|
||||
if t == nil {
|
||||
return EmptyTLSConfig(ctx)
|
||||
}
|
||||
|
||||
return t(ctx)
|
||||
}
|
||||
|
||||
func EmptyTLSConfig(ctx context.Context) (*tls.Config, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewSelfSignedTLSConfig(cn string, names ...string) TLSConfigFetcher {
|
||||
return func(ctx context.Context) (*tls.Config, error) {
|
||||
options := certificates.CreateCertificateOptions{
|
||||
CommonName: cn,
|
||||
Hosts: append([]string{cn}, names...),
|
||||
ValidFrom: time.Now(),
|
||||
ValidFor: time.Hour * 24 * 365 * 10,
|
||||
IsCA: false,
|
||||
ECDSACurve: "P256",
|
||||
}
|
||||
|
||||
cert, priv, err := certificates.CreateCertificate(options, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result *tls.Config
|
||||
c, err := tls.X509KeyPair([]byte(cert), []byte(priv))
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
result = &tls.Config{
|
||||
Certificates: []tls.Certificate{c},
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewSecretTLSConfig(client generic.GetInterface[*core.Secret], name string) TLSConfigFetcher {
|
||||
return func(ctx context.Context) (*tls.Config, error) {
|
||||
nctx, cancel := globals.GetGlobals().Timeouts().Kubernetes().WithTimeout(ctx)
|
||||
defer cancel()
|
||||
|
||||
s, err := client.Get(nctx, name, meta.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certBytes, found := s.Data[core.TLSCertKey]
|
||||
if !found {
|
||||
return nil, errors.Errorf("No %s found in secret %s", core.TLSCertKey, name)
|
||||
}
|
||||
keyBytes, found := s.Data[core.TLSPrivateKeyKey]
|
||||
if !found {
|
||||
return nil, errors.Errorf("No %s found in secret %s", core.TLSPrivateKeyKey, name)
|
||||
}
|
||||
|
||||
var result *tls.Config
|
||||
c, err := tls.X509KeyPair(certBytes, keyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
result = &tls.Config{
|
||||
Certificates: []tls.Certificate{c},
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewKeyfileTLSConfig(keyfile string) TLSConfigFetcher {
|
||||
return func(ctx context.Context) (*tls.Config, error) {
|
||||
certificate, err := certificates.LoadKeyFile(keyfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
}, nil
|
||||
}
|
||||
}
|
329
pkg/webhook/admission.go
Normal file
329
pkg/webhook/admission.go
Normal file
|
@ -0,0 +1,329 @@
|
|||
//
|
||||
// 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 webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
goHttp "net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
admission "k8s.io/api/admission/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/shutdown"
|
||||
)
|
||||
|
||||
func NewAdmissionHandler[T meta.Object](name, group, version, kind, resource string, handlers ...Handler[T]) Admission {
|
||||
return admissionImpl[T]{
|
||||
name: name,
|
||||
group: group,
|
||||
version: version,
|
||||
kind: kind,
|
||||
resource: resource,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
type AdmissionRequestType int
|
||||
|
||||
const (
|
||||
AdmissionRequestValidate AdmissionRequestType = iota
|
||||
AdmissionRequestMutate
|
||||
)
|
||||
|
||||
type Admissions []Admission
|
||||
|
||||
func (a Admissions) Register() util.Mod[goHttp.ServeMux] {
|
||||
return func(in *goHttp.ServeMux) {
|
||||
for _, handler := range a {
|
||||
log := logger.Str("name", handler.Name())
|
||||
|
||||
log.Info("Registering handler")
|
||||
|
||||
if endpoint := fmt.Sprintf("/webhook/%s/%s/validate", gvsAsPath(handler.Resource()), handler.Name()); endpoint != "" {
|
||||
log.Str("endpoint", endpoint).Info("Registered Validate handler")
|
||||
in.HandleFunc(endpoint, handler.Validate)
|
||||
}
|
||||
|
||||
if endpoint := fmt.Sprintf("/webhook/%s/%s/mutate", gvsAsPath(handler.Resource()), handler.Name()); endpoint != "" {
|
||||
log.Str("endpoint", endpoint).Info("Registered Mutate handler")
|
||||
in.HandleFunc(endpoint, handler.Mutate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func gvsAsPath(in meta.GroupVersionResource) string {
|
||||
if in.Group == "" {
|
||||
return fmt.Sprintf("core/%s/%s", in.Version, in.Resource)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s/%s", in.Group, in.Version, in.Resource)
|
||||
}
|
||||
|
||||
type Admission interface {
|
||||
Name() string
|
||||
|
||||
Kind() meta.GroupVersionKind
|
||||
Resource() meta.GroupVersionResource
|
||||
|
||||
Validate(goHttp.ResponseWriter, *goHttp.Request)
|
||||
Mutate(goHttp.ResponseWriter, *goHttp.Request)
|
||||
}
|
||||
|
||||
type admissionImpl[T meta.Object] struct {
|
||||
name, group, version, kind, resource string
|
||||
|
||||
handlers []Handler[T]
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) Kind() meta.GroupVersionKind {
|
||||
return meta.GroupVersionKind{
|
||||
Group: a.group,
|
||||
Version: a.version,
|
||||
Kind: a.kind,
|
||||
}
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) Resource() meta.GroupVersionResource {
|
||||
return meta.GroupVersionResource{
|
||||
Group: a.group,
|
||||
Version: a.version,
|
||||
Resource: a.resource,
|
||||
}
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) Validate(writer goHttp.ResponseWriter, request *goHttp.Request) {
|
||||
a.request(AdmissionRequestValidate, writer, request)
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) Mutate(writer goHttp.ResponseWriter, request *goHttp.Request) {
|
||||
a.request(AdmissionRequestMutate, writer, request)
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) request(t AdmissionRequestType, writer goHttp.ResponseWriter, request *goHttp.Request) {
|
||||
log := logger.Wrap(logging.HTTPRequestWrap(request))
|
||||
|
||||
log.Info("Request Received")
|
||||
|
||||
timeout := time.Second
|
||||
|
||||
if request.URL.Query().Has("timeout") {
|
||||
if v, err := time.ParseDuration(request.URL.Query().Get("timeout")); err == nil {
|
||||
if v > 500*time.Millisecond {
|
||||
timeout = v - 200*time.Millisecond
|
||||
} else {
|
||||
timeout = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx, c := context.WithTimeout(shutdown.Context(), timeout)
|
||||
defer c()
|
||||
|
||||
code, data := a.requestWriterJSON(ctx, log, t, request)
|
||||
writer.WriteHeader(code)
|
||||
if len(data) > 0 {
|
||||
if _, err := util.WriteAll(writer, data); err != nil {
|
||||
log.Err(err).Warn("Unable to send response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) requestWriterJSON(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *goHttp.Request) (int, []byte) {
|
||||
code, obj, err := a.requestWriter(ctx, log, t, request)
|
||||
|
||||
if err != nil {
|
||||
if herr, ok := http.IsError(err); ok {
|
||||
return herr.Code, herr.JSON()
|
||||
}
|
||||
|
||||
log.Err(err).Warn("Unexpected Error")
|
||||
return goHttp.StatusInternalServerError, nil
|
||||
}
|
||||
|
||||
if reflect.ValueOf(obj).IsZero() {
|
||||
return code, nil
|
||||
}
|
||||
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
log.Err(err).Warn("Unable to marshal response")
|
||||
return goHttp.StatusInternalServerError, nil
|
||||
}
|
||||
|
||||
return code, data
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) requestWriter(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *goHttp.Request) (int, any, error) {
|
||||
switch t {
|
||||
case AdmissionRequestValidate, AdmissionRequestMutate:
|
||||
default:
|
||||
log.Warn("Invalid AdmissionRequestType")
|
||||
return 0, nil, http.NewError(goHttp.StatusBadRequest, "Invalid AdmissionRequestType")
|
||||
}
|
||||
|
||||
if request.Method != goHttp.MethodPost {
|
||||
return 0, nil, http.NewError(goHttp.StatusMethodNotAllowed, "Method '%s' not allowed, expected '%s'", request.Method, goHttp.MethodPost)
|
||||
}
|
||||
|
||||
var req admission.AdmissionReview
|
||||
|
||||
if err := json.NewDecoder(request.Body).Decode(&req); err != nil {
|
||||
return 0, nil, http.WrapError(goHttp.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
resp := a.admissionHandle(ctx, log, t, req.Request)
|
||||
|
||||
return 200, admission.AdmissionReview{
|
||||
TypeMeta: req.TypeMeta,
|
||||
Response: resp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) admissionHandle(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest) *admission.AdmissionResponse {
|
||||
if request == nil {
|
||||
return &admission.AdmissionResponse{
|
||||
Allowed: false,
|
||||
}
|
||||
}
|
||||
|
||||
if request.Kind != a.Kind() {
|
||||
return &admission.AdmissionResponse{
|
||||
UID: request.UID,
|
||||
Allowed: false,
|
||||
Result: &meta.Status{
|
||||
Message: fmt.Sprintf("Invalid Kind. Got '%s', expected '%s'", request.Kind.String(), a.Kind().String()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := a.admissionHandleE(ctx, log, t, request)
|
||||
if err != nil {
|
||||
return &admission.AdmissionResponse{
|
||||
UID: request.UID,
|
||||
Allowed: false,
|
||||
Result: &meta.Status{
|
||||
Message: fmt.Sprintf("Unexpected error: %s", err.Error()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return &admission.AdmissionResponse{
|
||||
UID: request.UID,
|
||||
Allowed: false,
|
||||
Result: &meta.Status{
|
||||
Message: "Missing Response element",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
resp.UID = request.UID
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) admissionHandleE(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest) (*admission.AdmissionResponse, error) {
|
||||
old, err := a.evaluateObject(request.OldObject.Raw)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to parse old object")
|
||||
}
|
||||
|
||||
new, err := a.evaluateObject(request.Object.Raw)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Unable to parse new object")
|
||||
}
|
||||
|
||||
switch t {
|
||||
case AdmissionRequestValidate:
|
||||
for _, handler := range a.handlers {
|
||||
if handler.CanHandle(ctx, log, t, request, old, new) {
|
||||
if v, ok := handler.(ValidationHandler[T]); ok {
|
||||
result, err := v.Validate(ctx, log, t, request, old, new)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.AsResponse()
|
||||
}
|
||||
|
||||
return ValidationResponse{
|
||||
Allowed: false,
|
||||
Message: "Request not handled",
|
||||
}.AsResponse()
|
||||
}
|
||||
}
|
||||
|
||||
return &admission.AdmissionResponse{Allowed: true}, nil
|
||||
case AdmissionRequestMutate:
|
||||
for _, handler := range a.handlers {
|
||||
if handler.CanHandle(ctx, log, t, request, old, new) {
|
||||
if v, ok := handler.(MutationHandler[T]); ok {
|
||||
result, err := v.Mutate(ctx, log, t, request, old, new)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.AsResponse()
|
||||
}
|
||||
|
||||
return ValidationResponse{
|
||||
Allowed: false,
|
||||
Message: "Request not handled",
|
||||
}.AsResponse()
|
||||
}
|
||||
}
|
||||
|
||||
return &admission.AdmissionResponse{Allowed: true}, nil
|
||||
default:
|
||||
return &admission.AdmissionResponse{
|
||||
Allowed: false,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a admissionImpl[T]) evaluateObject(data []byte) (T, error) {
|
||||
if len(data) == 0 {
|
||||
return util.Default[T](), nil
|
||||
}
|
||||
|
||||
obj, err := util.DeepType[T]()
|
||||
if err != nil {
|
||||
return util.Default[T](), err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &obj); err != nil {
|
||||
return util.Default[T](), err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
52
pkg/webhook/handler.go
Normal file
52
pkg/webhook/handler.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// 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 webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
admission "k8s.io/api/admission/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
)
|
||||
|
||||
type CanHandleFunc[T meta.Object] func(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new T) bool
|
||||
|
||||
type MutateFunc[T meta.Object] func(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new T) (MutationResponse, error)
|
||||
|
||||
type ValidateFunc[T meta.Object] func(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new T) (ValidationResponse, error)
|
||||
|
||||
type Handler[T meta.Object] interface {
|
||||
CanHandle(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new T) bool
|
||||
}
|
||||
|
||||
type MutationHandler[T meta.Object] interface {
|
||||
Handler[T]
|
||||
|
||||
Mutate(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new T) (MutationResponse, error)
|
||||
}
|
||||
|
||||
type ValidationHandler[T meta.Object] interface {
|
||||
Handler[T]
|
||||
|
||||
Validate(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new T) (ValidationResponse, error)
|
||||
}
|
25
pkg/webhook/logger.go
Normal file
25
pkg/webhook/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 webhook
|
||||
|
||||
import "github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
|
||||
var logger = logging.Global().RegisterAndGetLogger("webhook", logging.Info)
|
89
pkg/webhook/responses.go
Normal file
89
pkg/webhook/responses.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
admission "k8s.io/api/admission/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
)
|
||||
|
||||
func NewValidationResponse(allowed bool, msg string, args ...any) ValidationResponse {
|
||||
return ValidationResponse{
|
||||
Allowed: allowed,
|
||||
Message: fmt.Sprintf(msg, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type ValidationResponse struct {
|
||||
Allowed bool
|
||||
Message string
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
func (v ValidationResponse) AsResponse() (*admission.AdmissionResponse, error) {
|
||||
if v.Allowed {
|
||||
return &admission.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Warnings: v.Warnings,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &admission.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Warnings: v.Warnings,
|
||||
Result: &meta.Status{
|
||||
Message: v.Message,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type MutationResponse struct {
|
||||
ValidationResponse
|
||||
|
||||
Patch patch.Items
|
||||
}
|
||||
|
||||
func (v MutationResponse) AsResponse() (*admission.AdmissionResponse, error) {
|
||||
resp, err := v.ValidationResponse.AsResponse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(v.Patch) == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
q, err := json.Marshal(v.Patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Patch = q
|
||||
resp.PatchType = util.NewType(admission.PatchTypeJSONPatch)
|
||||
|
||||
return resp, nil
|
||||
}
|
111
pkg/webhook/suite_pod_test.go
Normal file
111
pkg/webhook/suite_pod_test.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
//
|
||||
// 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 webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
admission "k8s.io/api/admission/v1"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
)
|
||||
|
||||
func newPodAdmissionRequest(t *testing.T, name, namespace string, op admission.Operation, old, new *core.Pod) *admission.AdmissionRequest {
|
||||
req := &admission.AdmissionRequest{
|
||||
UID: "",
|
||||
Kind: meta.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
Resource: meta.GroupVersionResource{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Operation: op,
|
||||
}
|
||||
|
||||
if old != nil {
|
||||
data, err := json.Marshal(old)
|
||||
require.NoError(t, err)
|
||||
req.OldObject.Raw = data
|
||||
}
|
||||
|
||||
if new != nil {
|
||||
data, err := json.Marshal(new)
|
||||
require.NoError(t, err)
|
||||
req.Object.Raw = data
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func newPodAdmission(name string, handlers ...Handler[*core.Pod]) Admission {
|
||||
return NewAdmissionHandler[*core.Pod](name, "", "v1", "Pod", "pods", handlers...)
|
||||
}
|
||||
|
||||
func newPodHandler(can CanHandleFunc[*core.Pod],
|
||||
mutate MutateFunc[*core.Pod],
|
||||
validate ValidateFunc[*core.Pod]) Handler[*core.Pod] {
|
||||
return podHandler{
|
||||
can: can,
|
||||
mutate: mutate,
|
||||
validate: validate,
|
||||
}
|
||||
}
|
||||
|
||||
type podHandler struct {
|
||||
can CanHandleFunc[*core.Pod]
|
||||
mutate MutateFunc[*core.Pod]
|
||||
validate ValidateFunc[*core.Pod]
|
||||
}
|
||||
|
||||
func (p podHandler) Validate(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) (ValidationResponse, error) {
|
||||
if p.validate == nil {
|
||||
return ValidationResponse{}, nil
|
||||
}
|
||||
|
||||
return p.validate(ctx, log, t, request, old, new)
|
||||
}
|
||||
|
||||
func (p podHandler) Mutate(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) (MutationResponse, error) {
|
||||
if p.mutate == nil {
|
||||
return MutationResponse{}, nil
|
||||
}
|
||||
|
||||
return p.mutate(ctx, log, t, request, old, new)
|
||||
}
|
||||
|
||||
func (p podHandler) CanHandle(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) bool {
|
||||
if p.can == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.can(ctx, log, t, request, old, new)
|
||||
}
|
129
pkg/webhook/suite_test.go
Normal file
129
pkg/webhook/suite_test.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// 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 webhook
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
goHttp "net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
admission "k8s.io/api/admission/v1"
|
||||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/logging"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/tests"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
ctx, c := context.WithCancel(context.Background())
|
||||
defer c()
|
||||
|
||||
addr := startHTTPServer(t, ctx, newPodAdmission("test", newPodHandler(func(ctx context.Context, log logging.Logger, t AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) bool {
|
||||
return true
|
||||
}, nil, func(ctx context.Context, log logging.Logger, at AdmissionRequestType, request *admission.AdmissionRequest, old, new *core.Pod) (ValidationResponse, error) {
|
||||
require.Nil(t, old)
|
||||
require.NotNil(t, new)
|
||||
|
||||
require.EqualValues(t, AdmissionRequestValidate, at)
|
||||
|
||||
return ValidationResponse{
|
||||
Allowed: true,
|
||||
}, nil
|
||||
})))
|
||||
|
||||
resp := requestPod(t, addr, "test", AdmissionRequestValidate,
|
||||
newPodAdmissionRequest(t, "test", tests.FakeNamespace, admission.Create, nil, &core.Pod{
|
||||
ObjectMeta: meta.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: tests.FakeNamespace,
|
||||
},
|
||||
}),
|
||||
)
|
||||
require.NotNil(t, resp)
|
||||
require.True(t, resp.Allowed)
|
||||
}
|
||||
|
||||
func requestPod(t *testing.T, addr string, name string, mode AdmissionRequestType, req *admission.AdmissionRequest) *admission.AdmissionResponse {
|
||||
return request(t, addr, meta.GroupVersionResource{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
}, name, mode, req)
|
||||
}
|
||||
|
||||
func request(t *testing.T, addr string, gvs meta.GroupVersionResource, name string, mode AdmissionRequestType, request *admission.AdmissionRequest) *admission.AdmissionResponse {
|
||||
if request == nil {
|
||||
request = &admission.AdmissionRequest{}
|
||||
}
|
||||
|
||||
request.UID = uuid.NewUUID()
|
||||
|
||||
data, err := json.Marshal(admission.AdmissionReview{
|
||||
Request: request,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
var c = goHttp.Client{
|
||||
Transport: &goHttp.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("https://%s/webhook/%s/%s/%s", addr, gvsAsPath(gvs), name, util.BoolSwitch(mode == AdmissionRequestValidate, "validate", "mutate"))
|
||||
|
||||
t.Logf("Request send to: %s", url)
|
||||
|
||||
resp, err := c.Post(url, "application/json", bytes.NewReader(data))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualValues(t, 200, resp.StatusCode)
|
||||
|
||||
var rv admission.AdmissionReview
|
||||
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&rv))
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
require.NotNil(t, rv.Response)
|
||||
require.EqualValues(t, request.UID, rv.Response.UID)
|
||||
|
||||
return rv.Response
|
||||
}
|
||||
|
||||
func startHTTPServer(t *testing.T, ctx context.Context, admissions ...Admission) string {
|
||||
return tests.NewHTTPServer(ctx, t,
|
||||
http.DefaultHTTPServerSettings,
|
||||
http.WithTLSConfigFetcher(util.NewSelfSignedTLSConfig("localhost", "127.0.0.1")),
|
||||
http.WithServeMux(
|
||||
Admissions(admissions).Register(),
|
||||
),
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue