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
|
pkg: k8s.io/api/batch/v1
|
||||||
- alias: core
|
- alias: core
|
||||||
pkg: k8s.io/api/core/v1
|
pkg: k8s.io/api/core/v1
|
||||||
|
- alias: admission
|
||||||
|
pkg: k8s.io/api/admission/v1
|
||||||
- alias: policy
|
- alias: policy
|
||||||
pkg: k8s.io/api/policy/v1
|
pkg: k8s.io/api/policy/v1
|
||||||
- alias: storage
|
- alias: storage
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
- (Feature) (Scheduler) Shutdown Integration
|
- (Feature) (Scheduler) Shutdown Integration
|
||||||
- (Feature) CertManager Integration
|
- (Feature) CertManager Integration
|
||||||
- (Feature) (Networking) Gateway Options sync
|
- (Feature) (Networking) Gateway Options sync
|
||||||
|
- (Feature) Webhooks
|
||||||
|
|
||||||
## [1.2.43](https://github.com/arangodb/kube-arangodb/tree/1.2.43) (2024-10-14)
|
## [1.2.43](https://github.com/arangodb/kube-arangodb/tree/1.2.43) (2024-10-14)
|
||||||
- (Feature) ArangoRoute CRD
|
- (Feature) ArangoRoute CRD
|
||||||
|
|
|
@ -195,7 +195,7 @@ Flags:
|
||||||
--kubernetes.max-batch-size int Size of batch during objects read (default 256)
|
--kubernetes.max-batch-size int Size of batch during objects read (default 256)
|
||||||
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
||||||
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
||||||
--log.level stringArray Set log levels in format <level> or <logger>=<level>. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, 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)
|
--log.sampling If true, operator will try to minimize duplication of logging events (default true)
|
||||||
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
||||||
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
||||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
||||||
scheme: HTTPS
|
scheme: HTTPS
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
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 }}
|
{{- end }}
|
||||||
tolerations:
|
tolerations:
|
||||||
- key: "node.kubernetes.io/unreachable"
|
- 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
|
acs: true
|
||||||
at: true
|
at: true
|
||||||
debug: false
|
debug: false
|
||||||
|
webhooks:
|
||||||
|
enabled: false
|
||||||
|
args: []
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 1
|
||||||
|
memory: 128Mi
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 128Mi
|
||||||
certificate:
|
certificate:
|
||||||
enabled: false
|
enabled: false
|
||||||
ca:
|
ca:
|
||||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
||||||
scheme: HTTPS
|
scheme: HTTPS
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
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 }}
|
{{- end }}
|
||||||
tolerations:
|
tolerations:
|
||||||
- key: "node.kubernetes.io/unreachable"
|
- 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
|
acs: true
|
||||||
at: true
|
at: true
|
||||||
debug: false
|
debug: false
|
||||||
|
webhooks:
|
||||||
|
enabled: false
|
||||||
|
args: []
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 1
|
||||||
|
memory: 128Mi
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 128Mi
|
||||||
certificate:
|
certificate:
|
||||||
enabled: false
|
enabled: false
|
||||||
ca:
|
ca:
|
||||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
||||||
scheme: HTTPS
|
scheme: HTTPS
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
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 }}
|
{{- end }}
|
||||||
tolerations:
|
tolerations:
|
||||||
- key: "node.kubernetes.io/unreachable"
|
- 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
|
acs: true
|
||||||
at: true
|
at: true
|
||||||
debug: false
|
debug: false
|
||||||
|
webhooks:
|
||||||
|
enabled: false
|
||||||
|
args: []
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 1
|
||||||
|
memory: 128Mi
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 128Mi
|
||||||
certificate:
|
certificate:
|
||||||
enabled: false
|
enabled: false
|
||||||
ca:
|
ca:
|
||||||
|
|
|
@ -191,6 +191,67 @@ spec:
|
||||||
scheme: HTTPS
|
scheme: HTTPS
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
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 }}
|
{{- end }}
|
||||||
tolerations:
|
tolerations:
|
||||||
- key: "node.kubernetes.io/unreachable"
|
- 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
|
acs: true
|
||||||
at: true
|
at: true
|
||||||
debug: false
|
debug: false
|
||||||
|
webhooks:
|
||||||
|
enabled: false
|
||||||
|
args: []
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 1
|
||||||
|
memory: 128Mi
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 128Mi
|
||||||
certificate:
|
certificate:
|
||||||
enabled: false
|
enabled: false
|
||||||
ca:
|
ca:
|
||||||
|
|
|
@ -84,9 +84,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logger = logging.Global().RegisterAndGetLogger("root", logging.Info)
|
|
||||||
eventRecorder = logging.Global().RegisterAndGetLogger("root-event-recorder", logging.Info)
|
|
||||||
|
|
||||||
cmdMain = cobra.Command{
|
cmdMain = cobra.Command{
|
||||||
Use: "arangodb_operator",
|
Use: "arangodb_operator",
|
||||||
Run: executeMain,
|
Run: executeMain,
|
||||||
|
|
|
@ -22,17 +22,16 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/arangodb/kube-arangodb/pkg/exporter"
|
"github.com/arangodb/kube-arangodb/pkg/exporter"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util"
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||||
|
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -67,23 +66,14 @@ func init() {
|
||||||
|
|
||||||
func cmdExporterCheck(cmd *cobra.Command, args []string) {
|
func cmdExporterCheck(cmd *cobra.Command, args []string) {
|
||||||
if err := cmdExporterCheckE(); err != nil {
|
if err := cmdExporterCheckE(); err != nil {
|
||||||
log.Error().Err(err).Msgf("Fatal")
|
logger.Err(err).Error("Fatal")
|
||||||
os.Exit(1)
|
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 {
|
func cmdExporterCheckE() error {
|
||||||
|
ctx := util.CreateSignalContext(context.Background())
|
||||||
|
|
||||||
if len(exporterInput.endpoints) < 1 {
|
if len(exporterInput.endpoints) < 1 {
|
||||||
return errors.Errorf("Requires at least one ArangoDB Endpoint to be present")
|
return errors.Errorf("Requires at least one ArangoDB Endpoint to be present")
|
||||||
}
|
}
|
||||||
|
@ -117,26 +107,34 @@ func cmdExporterCheckE() error {
|
||||||
return string(data), nil
|
return string(data), nil
|
||||||
}, false, 15*time.Second)
|
}, 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 exporterInput.keyfile != "" {
|
||||||
if e, err := exporter.WithKeyfile(exporterInput.keyfile); err != nil {
|
return util.NewKeyfileTLSConfig(exporterInput.keyfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
return err
|
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
|
### .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
|
storage
|
||||||
task
|
task
|
||||||
version
|
version
|
||||||
|
webhook
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
--action.PVCResize.concurrency int Define limit of concurrent PVC Resizes on the cluster (default 32)
|
--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.max-batch-size int Size of batch during objects read (default 256)
|
||||||
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
--kubernetes.qps float32 Number of queries per second for k8s API (default 15)
|
||||||
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
--log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty")
|
||||||
--log.level stringArray Set log levels in format <level> or <logger>=<level>. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, 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)
|
--log.sampling If true, operator will try to minimize duplication of logging events (default true)
|
||||||
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
--memory-limit uint Define memory limit for hard shutdown and the dump of goroutines. Used for testing
|
||||||
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
--metrics.excluded-prefixes stringArray List of the excluded metrics prefixes
|
||||||
|
|
|
@ -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:
|
1) Create a kubernetes [Secret](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/) with root password:
|
||||||
```bash
|
```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:
|
1) Then specify the newly created secret in the ArangoDeploymentSpec:
|
||||||
|
|
|
@ -29,6 +29,20 @@ metadata:
|
||||||
profiles.arangodb.com/deployment: << deployment name >>
|
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
|
### Integrations
|
||||||
|
|
||||||
To enable integration in specific version, labels needs to be added:
|
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,
|
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, 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, 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,
|
0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x1a, 0x1f, 0x67, 0x6f, 0x6f,
|
||||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69,
|
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d,
|
||||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x23, 0x0a,
|
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d,
|
||||||
0x0d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12,
|
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a,
|
||||||
0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61,
|
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||||
0x74, 0x68, 0x22, 0x71, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f,
|
0x68, 0x22, 0x6f, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62,
|
||||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
|
0x6a, 0x65, 0x63, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53,
|
0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f,
|
||||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61,
|
0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||||
0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
0x12, 0x30, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||||
0x32, 0x1d, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e,
|
||||||
0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x68, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
0x66, 0x6f, 0x22, 0x68, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f,
|
||||||
0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04,
|
0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a,
|
||||||
0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
|
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x3d, 0x0a,
|
||||||
0x12, 0x3d, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
|
0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
||||||
0x6d, 0x70, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x22,
|
0x0b, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x22, 0x3e, 0x0a, 0x14,
|
||||||
0x3e, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74,
|
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74,
|
0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x01,
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74,
|
0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x88, 0x01,
|
||||||
0x65, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x22,
|
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x22, 0x17, 0x0a, 0x15,
|
||||||
0x17, 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74,
|
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73,
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x49, 0x0a, 0x1a, 0x53, 0x74, 0x6f, 0x72,
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x1a, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52,
|
0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
|
0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e,
|
0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70,
|
0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22,
|
||||||
0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
0x33, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64,
|
||||||
0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14,
|
||||||
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63,
|
||||||
0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x60, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72,
|
0x68, 0x75, 0x6e, 0x6b, 0x22, 0x5f, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56,
|
||||||
0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18,
|
0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e,
|
0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||||
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04,
|
0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12,
|
||||||
0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02, 0x20,
|
0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
|
||||||
0x01, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x50, 0x0a, 0x1c, 0x53, 0x74,
|
0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x50, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65,
|
0x56, 0x32, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73,
|
||||||
0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79,
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01,
|
||||||
0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73,
|
0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63,
|
||||||
0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01,
|
0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
|
||||||
0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x22, 0x49, 0x0a, 0x1a,
|
0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x22, 0x48, 0x0a, 0x1a, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||||
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,
|
|
||||||
0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
|
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,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e,
|
0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74,
|
||||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49,
|
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||||
0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x4b, 0x0a, 0x1c, 0x53, 0x74, 0x6f,
|
0x68, 0x22, 0x4f, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65,
|
||||||
0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65,
|
0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74,
|
0x12, 0x30, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
|
||||||
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||||
0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68,
|
0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e,
|
||||||
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
0x66, 0x6f, 0x22, 0x4a, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44,
|
||||||
0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52,
|
0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
0x73, 0x74, 0x12, 0x2a, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52,
|
0x32, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
|
0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e,
|
0x0a, 0x1d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||||
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70,
|
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||||
0x61, 0x74, 0x68, 0x22, 0x4f, 0x0a, 0x1c, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
0x49, 0x0a, 0x1b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74,
|
||||||
0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a,
|
||||||
0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73,
|
||||||
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74,
|
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
||||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x05, 0x66,
|
0x50, 0x61, 0x74, 0x68, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x4e, 0x0a, 0x1c, 0x53, 0x74,
|
||||||
0x69, 0x6c, 0x65, 0x73, 0x32, 0xad, 0x04, 0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||||
0x56, 0x32, 0x12, 0x47, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x1e, 0x2e, 0x73, 0x68, 0x75,
|
0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69,
|
||||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49,
|
0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x74, 0x6f, 0x72,
|
||||||
0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x68, 0x75,
|
0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4f, 0x62, 0x6a,
|
||||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x49,
|
0x65, 0x63, 0x74, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x32, 0xa1, 0x04, 0x0a, 0x09, 0x53,
|
||||||
0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x0a, 0x52,
|
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x12, 0x45, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74,
|
||||||
0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x68, 0x75, 0x74,
|
0x12, 0x1d, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
||||||
0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65,
|
0x67, 0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 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,
|
||||||
0x25, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61,
|
0x65, 0x56, 0x32, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||||
0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
|
0x59, 0x0a, 0x0a, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x23, 0x2e,
|
||||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5e, 0x0a, 0x0b, 0x57, 0x72, 0x69, 0x74,
|
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56,
|
||||||
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x57, 0x72, 0x69, 0x74,
|
0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f,
|
||||||
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26,
|
0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
|
||||||
0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
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,
|
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,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64,
|
||||||
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x23, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||||
0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f,
|
0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62,
|
||||||
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73,
|
0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74,
|
||||||
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56,
|
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48,
|
||||||
0x32, 0x48, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a,
|
0x65, 0x12, 0x5d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||||
0x65, 0x63, 0x74, 0x12, 0x26, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53,
|
0x74, 0x12, 0x25, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72,
|
||||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62,
|
0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63,
|
||||||
0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x73, 0x68,
|
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
||||||
0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32,
|
0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x44, 0x65, 0x6c, 0x65,
|
||||||
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70,
|
0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65,
|
0x12, 0x5c, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12,
|
||||||
0x63, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53,
|
0x24, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65,
|
0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65,
|
||||||
0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x68, 0x75,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e,
|
||||||
0x74, 0x64, 0x6f, 0x77, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c,
|
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x56, 0x32, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a,
|
||||||
0x69, 0x73, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x46,
|
||||||
0x73, 0x65, 0x30, 0x01, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x61,
|
||||||
0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x6f, 0x64, 0x62, 0x2f, 0x6b, 0x75, 0x62, 0x65,
|
0x6e, 0x67, 0x6f, 0x64, 0x62, 0x2f, 0x6b, 0x75, 0x62, 0x65, 0x2d, 0x61, 0x72, 0x61, 0x6e, 0x67,
|
||||||
0x2d, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x6f, 0x64, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72,
|
0x6f, 0x64, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x76,
|
0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x66, 0x69,
|
||||||
0x32, 0x2f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72,
|
0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
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_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||||
var file_integrations_storage_v2_definition_storage_proto_goTypes = []interface{}{
|
var file_integrations_storage_v2_definition_storage_proto_goTypes = []interface{}{
|
||||||
(*StorageV2Path)(nil), // 0: shutdown.StorageV2Path
|
(*StorageV2Path)(nil), // 0: storage.StorageV2Path
|
||||||
(*StorageV2Object)(nil), // 1: shutdown.StorageV2Object
|
(*StorageV2Object)(nil), // 1: storage.StorageV2Object
|
||||||
(*StorageV2ObjectInfo)(nil), // 2: shutdown.StorageV2ObjectInfo
|
(*StorageV2ObjectInfo)(nil), // 2: storage.StorageV2ObjectInfo
|
||||||
(*StorageV2InitRequest)(nil), // 3: shutdown.StorageV2InitRequest
|
(*StorageV2InitRequest)(nil), // 3: storage.StorageV2InitRequest
|
||||||
(*StorageV2InitResponse)(nil), // 4: shutdown.StorageV2InitResponse
|
(*StorageV2InitResponse)(nil), // 4: storage.StorageV2InitResponse
|
||||||
(*StorageV2ReadObjectRequest)(nil), // 5: shutdown.StorageV2ReadObjectRequest
|
(*StorageV2ReadObjectRequest)(nil), // 5: storage.StorageV2ReadObjectRequest
|
||||||
(*StorageV2ReadObjectResponse)(nil), // 6: shutdown.StorageV2ReadObjectResponse
|
(*StorageV2ReadObjectResponse)(nil), // 6: storage.StorageV2ReadObjectResponse
|
||||||
(*StorageV2WriteObjectRequest)(nil), // 7: shutdown.StorageV2WriteObjectRequest
|
(*StorageV2WriteObjectRequest)(nil), // 7: storage.StorageV2WriteObjectRequest
|
||||||
(*StorageV2WriteObjectResponse)(nil), // 8: shutdown.StorageV2WriteObjectResponse
|
(*StorageV2WriteObjectResponse)(nil), // 8: storage.StorageV2WriteObjectResponse
|
||||||
(*StorageV2HeadObjectRequest)(nil), // 9: shutdown.StorageV2HeadObjectRequest
|
(*StorageV2HeadObjectRequest)(nil), // 9: storage.StorageV2HeadObjectRequest
|
||||||
(*StorageV2HeadObjectResponse)(nil), // 10: shutdown.StorageV2HeadObjectResponse
|
(*StorageV2HeadObjectResponse)(nil), // 10: storage.StorageV2HeadObjectResponse
|
||||||
(*StorageV2DeleteObjectRequest)(nil), // 11: shutdown.StorageV2DeleteObjectRequest
|
(*StorageV2DeleteObjectRequest)(nil), // 11: storage.StorageV2DeleteObjectRequest
|
||||||
(*StorageV2DeleteObjectResponse)(nil), // 12: shutdown.StorageV2DeleteObjectResponse
|
(*StorageV2DeleteObjectResponse)(nil), // 12: storage.StorageV2DeleteObjectResponse
|
||||||
(*StorageV2ListObjectsRequest)(nil), // 13: shutdown.StorageV2ListObjectsRequest
|
(*StorageV2ListObjectsRequest)(nil), // 13: storage.StorageV2ListObjectsRequest
|
||||||
(*StorageV2ListObjectsResponse)(nil), // 14: shutdown.StorageV2ListObjectsResponse
|
(*StorageV2ListObjectsResponse)(nil), // 14: storage.StorageV2ListObjectsResponse
|
||||||
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
|
(*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
|
||||||
}
|
}
|
||||||
var file_integrations_storage_v2_definition_storage_proto_depIdxs = []int32{
|
var file_integrations_storage_v2_definition_storage_proto_depIdxs = []int32{
|
||||||
0, // 0: shutdown.StorageV2Object.path:type_name -> shutdown.StorageV2Path
|
0, // 0: storage.StorageV2Object.path:type_name -> storage.StorageV2Path
|
||||||
2, // 1: shutdown.StorageV2Object.info:type_name -> shutdown.StorageV2ObjectInfo
|
2, // 1: storage.StorageV2Object.info:type_name -> storage.StorageV2ObjectInfo
|
||||||
15, // 2: shutdown.StorageV2ObjectInfo.last_updated:type_name -> google.protobuf.Timestamp
|
15, // 2: storage.StorageV2ObjectInfo.last_updated:type_name -> google.protobuf.Timestamp
|
||||||
0, // 3: shutdown.StorageV2ReadObjectRequest.path:type_name -> shutdown.StorageV2Path
|
0, // 3: storage.StorageV2ReadObjectRequest.path:type_name -> storage.StorageV2Path
|
||||||
0, // 4: shutdown.StorageV2WriteObjectRequest.path:type_name -> shutdown.StorageV2Path
|
0, // 4: storage.StorageV2WriteObjectRequest.path:type_name -> storage.StorageV2Path
|
||||||
0, // 5: shutdown.StorageV2HeadObjectRequest.path:type_name -> shutdown.StorageV2Path
|
0, // 5: storage.StorageV2HeadObjectRequest.path:type_name -> storage.StorageV2Path
|
||||||
2, // 6: shutdown.StorageV2HeadObjectResponse.info:type_name -> shutdown.StorageV2ObjectInfo
|
2, // 6: storage.StorageV2HeadObjectResponse.info:type_name -> storage.StorageV2ObjectInfo
|
||||||
0, // 7: shutdown.StorageV2DeleteObjectRequest.path:type_name -> shutdown.StorageV2Path
|
0, // 7: storage.StorageV2DeleteObjectRequest.path:type_name -> storage.StorageV2Path
|
||||||
0, // 8: shutdown.StorageV2ListObjectsRequest.path:type_name -> shutdown.StorageV2Path
|
0, // 8: storage.StorageV2ListObjectsRequest.path:type_name -> storage.StorageV2Path
|
||||||
1, // 9: shutdown.StorageV2ListObjectsResponse.files:type_name -> shutdown.StorageV2Object
|
1, // 9: storage.StorageV2ListObjectsResponse.files:type_name -> storage.StorageV2Object
|
||||||
3, // 10: shutdown.StorageV2.Init:input_type -> shutdown.StorageV2InitRequest
|
3, // 10: storage.StorageV2.Init:input_type -> storage.StorageV2InitRequest
|
||||||
5, // 11: shutdown.StorageV2.ReadObject:input_type -> shutdown.StorageV2ReadObjectRequest
|
5, // 11: storage.StorageV2.ReadObject:input_type -> storage.StorageV2ReadObjectRequest
|
||||||
7, // 12: shutdown.StorageV2.WriteObject:input_type -> shutdown.StorageV2WriteObjectRequest
|
7, // 12: storage.StorageV2.WriteObject:input_type -> storage.StorageV2WriteObjectRequest
|
||||||
9, // 13: shutdown.StorageV2.HeadObject:input_type -> shutdown.StorageV2HeadObjectRequest
|
9, // 13: storage.StorageV2.HeadObject:input_type -> storage.StorageV2HeadObjectRequest
|
||||||
11, // 14: shutdown.StorageV2.DeleteObject:input_type -> shutdown.StorageV2DeleteObjectRequest
|
11, // 14: storage.StorageV2.DeleteObject:input_type -> storage.StorageV2DeleteObjectRequest
|
||||||
13, // 15: shutdown.StorageV2.ListObjects:input_type -> shutdown.StorageV2ListObjectsRequest
|
13, // 15: storage.StorageV2.ListObjects:input_type -> storage.StorageV2ListObjectsRequest
|
||||||
4, // 16: shutdown.StorageV2.Init:output_type -> shutdown.StorageV2InitResponse
|
4, // 16: storage.StorageV2.Init:output_type -> storage.StorageV2InitResponse
|
||||||
6, // 17: shutdown.StorageV2.ReadObject:output_type -> shutdown.StorageV2ReadObjectResponse
|
6, // 17: storage.StorageV2.ReadObject:output_type -> storage.StorageV2ReadObjectResponse
|
||||||
8, // 18: shutdown.StorageV2.WriteObject:output_type -> shutdown.StorageV2WriteObjectResponse
|
8, // 18: storage.StorageV2.WriteObject:output_type -> storage.StorageV2WriteObjectResponse
|
||||||
10, // 19: shutdown.StorageV2.HeadObject:output_type -> shutdown.StorageV2HeadObjectResponse
|
10, // 19: storage.StorageV2.HeadObject:output_type -> storage.StorageV2HeadObjectResponse
|
||||||
12, // 20: shutdown.StorageV2.DeleteObject:output_type -> shutdown.StorageV2DeleteObjectResponse
|
12, // 20: storage.StorageV2.DeleteObject:output_type -> storage.StorageV2DeleteObjectResponse
|
||||||
14, // 21: shutdown.StorageV2.ListObjects:output_type -> shutdown.StorageV2ListObjectsResponse
|
14, // 21: storage.StorageV2.ListObjects:output_type -> storage.StorageV2ListObjectsResponse
|
||||||
16, // [16:22] is the sub-list for method output_type
|
16, // [16:22] is the sub-list for method output_type
|
||||||
10, // [10:16] is the sub-list for method input_type
|
10, // [10:16] is the sub-list for method input_type
|
||||||
10, // [10:10] is the sub-list for extension type_name
|
10, // [10:10] is the sub-list for extension type_name
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
package shutdown;
|
package storage;
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto";
|
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) {
|
func (c *storageV2Client) Init(ctx context.Context, in *StorageV2InitRequest, opts ...grpc.CallOption) (*StorageV2InitResponse, error) {
|
||||||
out := new(StorageV2InitResponse)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (c *storageV2Client) HeadObject(ctx context.Context, in *StorageV2HeadObjectRequest, opts ...grpc.CallOption) (*StorageV2HeadObjectResponse, error) {
|
||||||
out := new(StorageV2HeadObjectResponse)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (c *storageV2Client) DeleteObject(ctx context.Context, in *StorageV2DeleteObjectRequest, opts ...grpc.CallOption) (*StorageV2DeleteObjectResponse, error) {
|
||||||
out := new(StorageV2DeleteObjectResponse)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ func _StorageV2_Init_Handler(srv interface{}, ctx context.Context, dec func(inte
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/shutdown.StorageV2/Init",
|
FullMethod: "/storage.StorageV2/Init",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(StorageV2Server).Init(ctx, req.(*StorageV2InitRequest))
|
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{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/shutdown.StorageV2/HeadObject",
|
FullMethod: "/storage.StorageV2/HeadObject",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(StorageV2Server).HeadObject(ctx, req.(*StorageV2HeadObjectRequest))
|
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{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/shutdown.StorageV2/DeleteObject",
|
FullMethod: "/storage.StorageV2/DeleteObject",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(StorageV2Server).DeleteObject(ctx, req.(*StorageV2DeleteObjectRequest))
|
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,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
var StorageV2_ServiceDesc = grpc.ServiceDesc{
|
var StorageV2_ServiceDesc = grpc.ServiceDesc{
|
||||||
ServiceName: "shutdown.StorageV2",
|
ServiceName: "storage.StorageV2",
|
||||||
HandlerType: (*StorageV2Server)(nil),
|
HandlerType: (*StorageV2Server)(nil),
|
||||||
Methods: []grpc.MethodDesc{
|
Methods: []grpc.MethodDesc{
|
||||||
{
|
{
|
||||||
|
|
5
pkg/api/api.go
generated
5
pkg/api/api.go
generated
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// DISCLAIMER
|
// DISCLAIMER
|
||||||
//
|
//
|
||||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -21,6 +21,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -73,7 +74,7 @@ func NewServer(cli typedCore.CoreV1Interface, cfg ServerConfig) (*Server, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig, err := prepareTLSConfig(cli, cfg)
|
tlsConfig, err := prepareTLSConfig(cli, cfg).Eval(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
63
pkg/api/tls.go
generated
63
pkg/api/tls.go
generated
|
@ -21,70 +21,15 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
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"
|
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
|
|
||||||
"github.com/arangodb-helper/go-certificates"
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
|
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func prepareTLSConfig(cli typedCore.CoreV1Interface, cfg ServerConfig) (*tls.Config, error) {
|
func prepareTLSConfig(cli typedCore.CoreV1Interface, cfg ServerConfig) util.TLSConfigFetcher {
|
||||||
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) {
|
|
||||||
if cfg.TLSSecretName != "" {
|
if cfg.TLSSecretName != "" {
|
||||||
// Load TLS certificate from secret
|
return util.NewSecretTLSConfig(cli.Secrets(cfg.Namespace), cfg.TLSSecretName)
|
||||||
s, err := cli.Secrets(cfg.Namespace).Get(context.Background(), cfg.TLSSecretName, meta.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
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
|
return util.NewSelfSignedTLSConfig(cfg.ServerName, cfg.ServerAltNames...)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProfileContainerTemplate struct {
|
type ProfileContainerTemplate struct {
|
||||||
|
// Containers applies values per container
|
||||||
Containers schedulerContainerApi.Containers `json:"containers,omitempty"`
|
Containers schedulerContainerApi.Containers `json:"containers,omitempty"`
|
||||||
|
|
||||||
|
// All applies generic values to all Containers
|
||||||
All *schedulerContainerApi.Generic `json:"all,omitempty"`
|
All *schedulerContainerApi.Generic `json:"all,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProfileTemplate struct {
|
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"`
|
Priority *int `json:"priority,omitempty"`
|
||||||
|
|
||||||
|
// Pod Template
|
||||||
Pod *schedulerPodApi.Pod `json:"pod,omitempty"`
|
Pod *schedulerPodApi.Pod `json:"pod,omitempty"`
|
||||||
|
|
||||||
|
// Container Template
|
||||||
Container *ProfileContainerTemplate `json:"container,omitempty"`
|
Container *ProfileContainerTemplate `json:"container,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// DISCLAIMER
|
// DISCLAIMER
|
||||||
//
|
//
|
||||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -60,6 +60,8 @@ func (p Path) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(v)
|
return json.Marshal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Items []Item
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Op Operation `json:"op"`
|
Op Operation `json:"op"`
|
||||||
Path Path `json:"path"`
|
Path Path `json:"path"`
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// DISCLAIMER
|
// DISCLAIMER
|
||||||
//
|
//
|
||||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -20,13 +20,6 @@
|
||||||
|
|
||||||
package exporter
|
package exporter
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Authentication func() (string, error)
|
type Authentication func() (string, error)
|
||||||
|
|
||||||
// CreateArangodJwtAuthorizationHeader calculates a JWT authorization header, for authorization
|
// CreateArangodJwtAuthorizationHeader calculates a JWT authorization header, for authorization
|
||||||
|
@ -35,27 +28,3 @@ type Authentication func() (string, error)
|
||||||
func CreateArangodJwtAuthorizationHeader(jwt string) (string, error) {
|
func CreateArangodJwtAuthorizationHeader(jwt string) (string, error) {
|
||||||
return "bearer " + jwt, nil
|
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/gin-gonic/gin"
|
||||||
"github.com/jessevdk/go-assets"
|
"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"
|
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/dashboard"
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||||
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/metrics"
|
"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)),
|
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
||||||
}
|
}
|
||||||
|
|
||||||
var cert, key string
|
var fetcher util.TLSConfigFetcher
|
||||||
if cfg.TLSSecretName != "" && cfg.TLSSecretNamespace != "" {
|
if cfg.TLSSecretName != "" && cfg.TLSSecretNamespace != "" {
|
||||||
serverLogger.Str("addr", cfg.Address).Str("secret", cfg.TLSSecretName).Str("secret-namespace", cfg.TLSSecretNamespace).Info("Using existing TLS Certificate")
|
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{})
|
fetcher = util.NewSecretTLSConfig(cli.Secrets(cfg.TLSSecretNamespace), cfg.TLSSecretName)
|
||||||
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)
|
|
||||||
} else {
|
} else {
|
||||||
serverLogger.Str("addr", cfg.Address).Info("Using SelfSigned TLS Certificate")
|
serverLogger.Str("addr", cfg.Address).Info("Using SelfSigned TLS Certificate")
|
||||||
options := certificates.CreateCertificateOptions{
|
fetcher = util.NewSelfSignedTLSConfig(cfg.PodName, cfg.PodIP)
|
||||||
CommonName: cfg.PodName,
|
|
||||||
Hosts: []string{cfg.PodName, cfg.PodIP},
|
|
||||||
ValidFrom: time.Now(),
|
|
||||||
ValidFor: time.Hour * 24 * 365 * 10,
|
|
||||||
IsCA: false,
|
|
||||||
ECDSACurve: "P256",
|
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
cert, key, err = certificates.CreateCertificate(options, nil)
|
tlsConfig, err := fetcher.Eval(context.Background())
|
||||||
if err != nil {
|
|
||||||
return nil, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tlsConfig, err := createTLSConfig(cert, key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -256,19 +229,6 @@ func (s *Server) Run() error {
|
||||||
return nil
|
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) {
|
func ready(probes ...*probe.ReadyProbe) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, probe := range probes {
|
for _, probe := range probes {
|
||||||
|
|
|
@ -26,6 +26,11 @@ const ProfileGroup = "profiles.arangodb.com"
|
||||||
|
|
||||||
const ProfilesDeployment = ProfileGroup + "/deployment"
|
const ProfilesDeployment = ProfileGroup + "/deployment"
|
||||||
const ProfilesIntegrationPrefix = "integration." + ProfileGroup
|
const ProfilesIntegrationPrefix = "integration." + ProfileGroup
|
||||||
|
const ProfilesList = ProfileGroup + "/profiles"
|
||||||
|
|
||||||
|
const ProfilesAnnotationApplied = ProfileGroup + "/applied"
|
||||||
|
const ProfilesAnnotationChecksum = ProfileGroup + "/checksum"
|
||||||
|
const ProfilesAnnotationProfiles = ProfileGroup + "/profiles"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProfilesIntegrationAuthn = "authn"
|
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
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"time"
|
||||||
|
|
||||||
"github.com/arangodb-helper/go-certificates"
|
|
||||||
|
|
||||||
|
"github.com/arangodb/kube-arangodb/pkg/util"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(server *http.Server) PlainServer {
|
func DefaultHTTPServerSettings(in *http.Server, _ context.Context) error {
|
||||||
return &plainServer{server: server}
|
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 {
|
func WithTLSConfigFetcherGen(gen func() util.TLSConfigFetcher) util.ModEP1[http.Server, context.Context] {
|
||||||
Stop()
|
return WithTLSConfigFetcher(gen())
|
||||||
Wait() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlainServer interface {
|
func WithTLSConfigFetcher(fetcher util.TLSConfigFetcher) util.ModEP1[http.Server, context.Context] {
|
||||||
Server
|
return func(in *http.Server, p1 context.Context) error {
|
||||||
WithSSL(key, cert string) (Server, error)
|
v, err := fetcher.Eval(p1)
|
||||||
WithKeyfile(keyfile string) (Server, error)
|
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 {
|
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 server struct {
|
||||||
|
|
||||||
type tlsServer struct {
|
|
||||||
server *http.Server
|
server *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tlsServer) Start() (ServerRunner, error) {
|
func (s *server) AsyncAddr(ctx context.Context, addr string) func() error {
|
||||||
i := serverRunner{
|
var err error
|
||||||
stopCh: make(chan struct{}),
|
|
||||||
doneCh: make(chan struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
go i.run(t.server, func(s *http.Server) error {
|
done := make(chan any)
|
||||||
return s.ListenAndServeTLS("", "")
|
|
||||||
})
|
|
||||||
|
|
||||||
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() {
|
go func() {
|
||||||
defer close(s.doneCh)
|
defer close(done)
|
||||||
if err := f(server); err != nil {
|
|
||||||
|
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) {
|
if !errors.Is(err, http.ErrServerClosed) {
|
||||||
s.err = err
|
logger.Err(err).Warn("Unable to close server")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
<-s.stopCh
|
if s.server.TLSConfig == nil {
|
||||||
|
if err := s.server.Serve(ln); err != nil {
|
||||||
server.Close()
|
if !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
return err
|
||||||
<-s.doneCh
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := s.server.ServeTLS(ln, "", ""); err != nil {
|
||||||
|
if !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serverRunner) Stop() {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
select {
|
|
||||||
case <-s.stopCh:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
close(s.stopCh)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serverRunner) Wait() error {
|
return nil
|
||||||
<-s.doneCh
|
|
||||||
|
|
||||||
return s.err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,18 @@ func FormatList[A, B any](in []A, format func(A) B) []B {
|
||||||
return r
|
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 {
|
func ContainsList[A comparable](in []A, item A) bool {
|
||||||
for _, el := range in {
|
for _, el := range in {
|
||||||
if el == item {
|
if el == item {
|
||||||
|
|
|
@ -22,16 +22,6 @@ package util
|
||||||
|
|
||||||
func emptyMod[T any](_ *T) {}
|
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)
|
type Mod[T any] func(in *T)
|
||||||
|
|
||||||
func (m Mod[T]) Optional() Mod[T] {
|
func (m Mod[T]) Optional() Mod[T] {
|
||||||
|
@ -47,3 +37,51 @@ func ApplyMods[T any](in *T, mods ...Mod[T]) {
|
||||||
mod(in)
|
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 (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPointer returns a reference to a copy of the pointer value
|
// NewPointer returns a reference to a copy of the pointer value
|
||||||
|
@ -135,6 +137,42 @@ func InitType[T interface{}](in *T) *T {
|
||||||
return &q
|
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 ConditionalFunction[T interface{}] func() (T, bool)
|
||||||
type ConditionalP1Function[T, P1 interface{}] func(p1 P1) (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"
|
arangoClientSet "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
|
||||||
operator "github.com/arangodb/kube-arangodb/pkg/operatorV2"
|
operator "github.com/arangodb/kube-arangodb/pkg/operatorV2"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/operatorV2/operation"
|
"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/errors"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
|
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors"
|
||||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
"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 {
|
func NewMetaObject[T meta.Object](t *testing.T, namespace, name string, mods ...MetaObjectMod[T]) T {
|
||||||
var obj T
|
obj, err := util.DeepType[T]()
|
||||||
|
require.NoError(t, err)
|
||||||
if objT := reflect.TypeOf(obj); objT.Kind() == reflect.Pointer {
|
|
||||||
newObj := reflect.New(objT.Elem())
|
|
||||||
|
|
||||||
reflect.ValueOf(&obj).Elem().Set(newObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsNamespaced(obj) {
|
if IsNamespaced(obj) {
|
||||||
obj.SetNamespace(namespace)
|
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
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
func (t Timeout) WithTimeoutT(z *testing.T, timeout, interval time.Duration) {
|
||||||
require.NoError(z, t.WithTimeout(timeout, interval))
|
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