commit bd1ed71ac471f119b61d9149c6cc4cbced302751 Author: Conan Scott Date: Wed Jan 21 17:37:42 2026 +1100 first commit diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..97b0061 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +description: Amplify Fusion chart for Kubernetes +name: amplify-fusion +type: application +version: 1.13.1 diff --git a/crd/orchestrator.yaml b/crd/orchestrator.yaml new file mode 100644 index 0000000..fde97e8 --- /dev/null +++ b/crd/orchestrator.yaml @@ -0,0 +1,187 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: orchestrators.operator.fusion.axway.com +spec: + group: operator.fusion.axway.com + names: + kind: Orchestrator + listKind: OrchestratorList + plural: orchestrators + shortNames: + - orch + singular: orchestrator + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.phase + name: Phase + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: Orchestrator is the Schema for the orchestrators API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: OrchestratorSpec defines the desired state of Orchestrator + properties: + annotations: + additionalProperties: + type: string + description: Annotations to be added to the orchestrator + type: object + configMapData: + additionalProperties: + type: string + description: Optional map to provide support for overriding/adding + new properties in configMap + type: object + labels: + additionalProperties: + type: string + description: Labels to be added to the orchestrator + type: object + orgSchema: + description: OrgSchema is the name of the tenant + minLength: 1 + type: string + resources: + description: Resources defines the resource requirements for the container + properties: + cpuLimit: + anyOf: + - type: integer + - type: string + description: CPU limit + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + cpuRequest: + anyOf: + - type: integer + - type: string + description: CPU request + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memoryLimit: + anyOf: + - type: integer + - type: string + description: Memory limit + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memoryRequest: + anyOf: + - type: integer + - type: string + description: Memory request + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + required: + - orgSchema + type: object + status: + description: OrchestratorStatus defines the observed state of Orchestrator + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + phase: + description: Phase represents the current phase of the Orchestrator + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} \ No newline at end of file diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 0000000..70a0d51 --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,207 @@ +{{- define "parent.dataplaneMode" -}} +{{- (eq .Values.global.clusterKey "") | ternary "shared" (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).mode) }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "dataplane.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "dataplane.labels" -}} +helm.sh/chart: {{ include "dataplane.chart" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +{{- end }} + +{{/* +Defines a reusable initContainer for the dataplane, responsible for creating a log directory for the included service in the EFS volume before the main container starts. +Usage: +{{ include "dataplane.createLogDirectoryInitContainer" (dict "root" . "serviceName" "my-service" "securityContext" ".Values..securityContext" "additionalPathParam" "additional-path-param" ) }} +*/}} + +{{- define "dataplane.createLogDirectoryInitContainer" -}} +initContainers: +- name: {{ .root.Chart.Name }}-init + securityContext: + {{- toYaml .securityContext | nindent 4 }} + image: "{{ default .root.Values.global.image.repository .root.Values.global.alpinetools.image.repository }}/{{ .root.Values.global.alpinetools.image.name }}:{{ .root.Values.global.alpinetools.image.tag }}" + imagePullPolicy: {{ .root.Values.global.image.pullPolicy }} + volumeMounts: + - name: {{ .root.Values.global.volumeStorageName }} + mountPath: /efs + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + envFrom: + - configMapRef: + name: configmap-common + command: + - sh + - -c + - > + {{ include "dataplane.createLogDirectory" (dict "serviceName" .serviceName "efsRoot" "${efs_root}" "podName" "${POD_NAME}" "additionalPathParam" .additionalPathParam) }} +{{- end }} + +{{/* +Creates a static log directory for the service pod in the EFS volume. +*/}} + +{{- define "dataplane.createLogDirectory" -}} + mkdir -p "{{ .efsRoot}}/logs/{{ .serviceName}}/{{ .podName}}/{{ .additionalPathParam}}" || { + echo "Failed to create log directory: {{ .efsRoot}}/logs/{{ .serviceName}}/{{ .podName}}/{{ .additionalPathParam}}" >&2; + exit 1; + } +{{- end}} + + +{{/* +Common init script waits for Valkey and pre-deploy. +Usage: +- Full script: {{ include "dataplane.commonInit" (dict "context" . "buildTag" .Values.someService.image.buildTag) }} +- External Valkey only: {{ include "dataplane.commonInit" (dict "context" . "valkeyOnly" true) }} +- Internal Valkey: {{ include "dataplane.commonInit" (dict "context" . "valkeyOnly" true "internal" true "replicas" 3) }} +*/}} +{{- define "dataplane.commonInit" -}} +{{- if not .context -}} +{{- fail "dataplane.commonInit: context is required" -}} +{{- end -}} +{{- if and (not .buildTag) (not .valkeyOnly) -}} +{{- fail "dataplane.commonInit: either buildTag or valkeyOnly=true is required" -}} +{{- end -}} +{{- if and .internal (not .replicas) -}} +{{- fail "dataplane.commonInit: replicas is required when internal=true" -}} +{{- end -}} +{{- if .internal -}} +{{- include "dataplane.internalValkeyInit" . -}} +{{- else -}} +{{- include "dataplane.externalValkeyInit" . -}} +{{- end -}} +{{- if .buildTag }} +version="{{ .buildTag }}" ; +predeploy_version="{{ .buildTag }}" ; +mkdir -p "$(dirname ${server_truststore_path})" ; +{{- end }} +{{- end -}} + +{{/* +Internal Valkey initialization - expects a headless service and will resolve each address within. +Usage: Called internally by dataplane.commonInit with full context dict +*/}} +{{- define "dataplane.internalValkeyInit" -}} +expected_replicas={{ .replicas }}; +start_time=$(date +%s); +max_duration=300; +valkey_ready=false; +echo "Starting Valkey readiness check. Waiting for ${expected_replicas} replicas to be resolved and reachable within ${max_duration} minutes..." + +until [ $(($(date +%s) - start_time)) -ge ${max_duration} ]; do + # Resolve the hostnames + all_resolved=""; + {{- range .context.Values.valkey.hosts }} + host={{ .hostname }}; + host_port={{ .port | default 6379 }}; + # Use a timeout for nslookup itself to prevent it from hanging + host_ips=$(nslookup -timeout=2 ${host} 2>/dev/null | awk '/^Address: / {print $2}'); + echo " Host '${host}' resolved to IPs: ${host_ips:-'none'}"; + for ip in $host_ips; do + all_resolved="${all_resolved} ${ip}:${host_port}"; + done + {{- end }} + + # Check valid number of hosts + unique_endpoints=$(echo "$all_resolved" | tr ' ' '\n' | grep . | sort -u); + unique_count=$(echo "${unique_endpoints}" | wc -l); + echo "Found ${unique_count} unique endpoints. Expecting ${expected_replicas}." + + if [ "${unique_count}" -ne "${expected_replicas}" ]; then + echo "Endpoint count does not match expected count. Retrying in 5 seconds..." + sleep 5; + continue; + fi + + # Check port connectivity + echo "Endpoint count is correct. Checking connectivity..." + reachable_count=0; + for entry in $unique_endpoints; do + ip=$(echo $entry | cut -d: -f1); + port=$(echo $entry | cut -d: -f2); + if nc -w 2 -z ${ip} ${port} >/dev/null 2>&1; then + echo "[SUCCESS] Connectivity to ${ip}:${port} is OK." + reachable_count=$((reachable_count + 1)); + else + echo "[FAILED] Could not connect to ${ip}:${port}." + fi; + done; + + # Check all endpoints were reachable + + if [ $reachable_count -eq $expected_replicas ]; then + echo "[SUCCESS] All ${expected_replicas} Valkey replicas are resolved and reachable." + valkey_ready=true; + break; + fi; + echo "Only ${reachable_count} of ${expected_replicas} endpoints were reachable. Retrying in 5 seconds..." + sleep 5; +done; +if [ "${valkey_ready}" != "true" ]; then + echo "[ERROR] Timed out after ${max_duration} seconds. Could not confirm readiness of all Valkey replicas." + exit 1; +fi; +{{- end -}} + + +{{/* +External Valkey initialization - Takes the hosts at face value, no DNS checks. +Usage: Called internally by dataplane.commonInit with full context dict +*/}} +{{- define "dataplane.externalValkeyInit" -}} +# External Valkey mode - direct host connectivity check +c=0; +x={{ len .context.Values.valkey.hosts }}; + +until [ $c -eq $x ]; do + c=0; + {{- range .context.Values.valkey.hosts }} + host={{ .hostname }}; + port={{ .port | default 6379 }}; + echo "Checking connectivity to ${host}:${port}"; + if nc -w 3 -v ${host} ${port}; then + c=$((c+1)); + fi; + {{- end }} + if [ $c -ne $x ]; then + echo "Only $c out of $x hosts are reachable, retrying..."; + sleep 2; + fi; +done; +echo "All ${x} Valkey hosts are reachable"; +{{- end -}} + + +{{/* +Get the Unique ports in a list of hosts with an optional port field. +Usage: {{ include "dataplane.collectPorts" (dict "hosts" .Values.valkey.hosts "defaultPort" 6379) }} +*/}} +{{- define "dataplane.collectPorts" -}} +{{- $hosts := .hosts -}} +{{- $defaultPort := .defaultPort -}} +{{- $ports := dict -}} +{{- range $hosts -}} +{{- $port := .port | default $defaultPort -}} +{{- $_ := set $ports ($port | toString) true -}} +{{- end -}} +{{- range $portValue, $_ := $ports }} +- {{ $portValue }} +{{- end -}} +{{- end -}} diff --git a/templates/common/NOTES.txt b/templates/common/NOTES.txt new file mode 100644 index 0000000..2031204 --- /dev/null +++ b/templates/common/NOTES.txt @@ -0,0 +1,8 @@ +Thank you for installing the {{ .Chart.Name }} chart for the Amplify Integration Platform. + +Your release is named {{ .Release.Name }}. + +To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get all {{ .Release.Name }} diff --git a/templates/common/certificate.yaml b/templates/common/certificate.yaml new file mode 100644 index 0000000..a880148 --- /dev/null +++ b/templates/common/certificate.yaml @@ -0,0 +1,41 @@ +{{- if .Values.common.certificate.enabled -}} +{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace "domain-certificate" ).metadata | required "Secret 'domain-certificate' containing p12 certificate is required. Create it external to helm chart or set common.certificate.enabled to true, to autogenerate cert using cert-manager" }} +{{- else }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: domain-certificate +spec: + # Secret names are always required. + secretName: domain-certificate + duration: {{ .Values.common.certificate.generate.duration }} + renewBefore: {{ .Values.common.certificate.generate.renewBefore }} + subject: {{- toYaml .Values.common.certificate.generate.subject | nindent 4 }} + {{- if eq (include "parent.dataplaneMode" . ) "shared" }} + commonName: "*.{{ .Values.global.external_domain }}" + {{- else if .Values.global.multihost.enabled }} + commonName: "*.{{ .Values.global.external_domain }}" + {{- else }} + commonName: "{{ .Values.global.external_domain }}" + {{- end }} + isCA: false + privateKey: + algorithm: RSA + encoding: PKCS8 + size: 2048 + dnsNames: + {{- if eq (include "parent.dataplaneMode" . ) "shared" }} + - "*.{{ .Values.global.external_domain }}" + {{- else if .Values.global.multihost.enabled }} + - "*.{{ .Values.global.external_domain }}" + {{- end }} + - {{ .Values.global.external_domain }} + issuerRef: {{- toYaml .Values.common.certificate.generate.issuerRef | nindent 4 }} + keystores: + pkcs12: + create: true + profile: LegacyDES + passwordSecretRef: + name: certificate-password + key: password +{{- end -}} \ No newline at end of file diff --git a/templates/common/configmap.yaml b/templates/common/configmap.yaml new file mode 100644 index 0000000..bcdf875 --- /dev/null +++ b/templates/common/configmap.yaml @@ -0,0 +1,78 @@ +{{- if eq .Values.common.externalConfigMaps false }} +{{- $domain := ((eq .Values.global.clusterKey "") | ternary .Values.global.ctlplane_domain (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).domain )) | required "If deploying a shared dataplane, set your control plane domain using .Values.global.ctlplane_domain. If not set a valid clusterKey" }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: configmap-common + namespace: {{ .Release.Namespace }} +data: + s3_payload: "{{ tpl .Values.common.s3_payload . }}" + app_env: {{ .Values.global.appEnv }} + tenant_namespace: "{{ .Release.Namespace }}" + k8_namespace: "{{ .Release.Namespace }}" + cluster_name: {{ (eq .Values.global.clusterKey "") | ternary "Shared Data Plane" (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).name) }} + cluster_type: {{ (eq .Values.global.clusterKey "") | ternary "MANAGED_SHARED" (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).runtimeType) }} + domain: {{ $domain }} + tenant: {{ (eq .Values.global.clusterKey "") | ternary "shared" (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).tenant) }} + im_url: {{ (eq .Values.global.clusterKey "") | ternary (printf "wss://services.%s/monitor/ws" .Values.global.ctlplane_domain) (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).im_url) }} + sink_ws_url: {{ (eq .Values.global.clusterKey "") | ternary (printf "wss://services.%s/auditservice/ws" .Values.global.ctlplane_domain) (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).audit_service_url) }} + mode: {{ include "parent.dataplaneMode" . }} + server_cert_path: /certificate/{{ .Values.common.certificate.name }} + sftp_server_cert_path: /certificate/{{ .Values.common.certificate.name }} + support_email: "{{ tpl .Values.common.support_email . }}" + {{- if eq (include "parent.dataplaneMode" . ) "shared" }} + server_truststore_path: /efs/certificate/{{ include "parent.dataplaneMode" . }}/{{ tpl .Values.common.truststore . }} + efs_root: /efs + {{- else }} + clusterRefId: "{{ tpl .Values.common.clusterRefId . }}" + server_truststore_path: /efs/certificates/{{ tpl .Values.common.clusterRefId . }}/{{ tpl .Values.common.truststore . }} + efs_root: /efs/clusters/{{ tpl .Values.common.clusterRefId . }} + {{- end }} + sftp_server_port: "{{ tpl .Values.common.sftp_server_port . }}" + admin_email: "{{ tpl .Values.common.admin_email . }}" + dxchange_email_host: "{{ tpl .Values.common.email_host . }}" + dxchange_email_port: "{{ tpl .Values.common.email_port . }}" + dxchange_email_usetls: "{{ .Values.common.email_usetls }}" + dxchange_email_username: "{{ tpl .Values.common.email_username . }}" + dxchange_email_authentication: "{{ .Values.common.email_authentication }}" + dxchange_email_frommailid: "{{ tpl .Values.common.email_frommailid . }}" + log_file_enabled: "{{ .Values.common.logFileEnabled }}" + {{- if .Values.common.openTelemetry.enabled }} + otel_enabled: "{{ .Values.common.openTelemetry.enabled }}" + {{- if .Values.common.openTelemetry.exporter.http.enabled }} + otel_exporter_http_enabled: "{{ .Values.common.openTelemetry.exporter.http.enabled }}" + otel_exporter_http_endpoint: "{{ .Values.common.openTelemetry.exporter.http.endpoint }}" + {{- end }} + {{- if .Values.common.openTelemetry.exporter.grpc.enabled }} + otel_exporter_grpc_enabled: "{{ .Values.common.openTelemetry.exporter.grpc.enabled }}" + otel_exporter_grpc_endpoint: "{{ .Values.common.openTelemetry.exporter.grpc.endpoint }}" + {{- end }} + {{- end }} + {{- if .Values.valkey.enabled }} + datagrid_deployment_model: "{{ .Values.valkey.client.deployment_model }}" + datagrid_implementation: VALKEY + datagrid_read_mode: "{{ .Values.valkey.client.read_mode }}" + datagrid_username: "{{ .Values.valkey.client.datagrid_username }}" + datagrid_use_insecure_connection: "{{ not .Values.valkey.client.use_secure_connection }}" + datagrid_secure_connection_verification_mode: "{{ .Values.valkey.client.security_mode }}" + datagrid_addresses: "{{- range $i, $v := .Values.valkey.hosts }}{{ if $i }},{{ end }}{{ $v.hostname }}{{ if $v.port }}:{{ $v.port }}{{ end }}{{ end -}}" + datagrid_primary_connection_pool_min: "{{ .Values.valkey.client.datagrid_primary_connection_pool_min }}" + datagrid_primary_connection_pool_max: "{{ .Values.valkey.client.datagrid_primary_connection_pool_max }}" + datagrid_replica_connection_pool_min: "{{ .Values.valkey.client.datagrid_replica_connection_pool_min }}" + datagrid_replica_connection_pool_max: "{{ .Values.valkey.client.datagrid_replica_connection_pool_max }}" + datagrid_worker_threads: "{{ .Values.valkey.client.datagrid_worker_threads }}" + datagrid_idle_connection_timeout_millis: "{{ .Values.valkey.client.datagrid_idle_connection_timeout_millis }}" + datagrid_cluster_connect_timeout_millis: "{{ .Values.valkey.client.datagrid_cluster_connect_timeout_millis }}" + datagrid_response_timeout_millis: "{{ .Values.valkey.client.datagrid_response_timeout_millis }}" + datagrid_number_of_retries: "{{ .Values.valkey.client.datagrid_number_of_retries }}" + datagrid_retry_interval_millis: "{{ .Values.valkey.client.datagrid_retry_interval_millis }}" + datagrid_subscription_mode: "{{ .Values.valkey.client.subscription_mode }}" + datagrid_subscriptions_per_connection: "{{ .Values.valkey.client.datagrid_subscriptions_per_connection }}" + datagrid_subscription_connection_pool_size: "{{ .Values.valkey.client.datagrid_subscription_connection_pool_size }}" + datagrid_subscription_connection_min_idle_size: "{{ .Values.valkey.client.datagrid_subscription_connection_min_idle_size }}" + datagrid_subscription_connection_timeout_millis: "{{ .Values.valkey.client.datagrid_subscription_connection_timeout_millis }}" + {{- end }} + {{- with .Values.common.hsm }} + hsmCacheExpiration: "{{ .hsmCacheExpiration }}" + {{- end }} +{{- end }} diff --git a/templates/common/general_conditions.yaml b/templates/common/general_conditions.yaml new file mode 100644 index 0000000..6726b3e --- /dev/null +++ b/templates/common/general_conditions.yaml @@ -0,0 +1,5 @@ +{{- if not ( kindIs "string" .Values.common.acceptGeneralConditions ) }} +{{- fail "The acceptGeneralConditions value must be a string. Check that you are using double quotes in your values file, or --set-string if using the command line. See the information in the values.yaml file, or on the online documentation." }} +{{- else if ne .Values.common.acceptGeneralConditions "yes" }} +{{- fail "You need to accept the General Conditions in order to install the application. See the information in the values.yaml file, or on the online documentation." }} +{{- end }} \ No newline at end of file diff --git a/templates/common/jobs/domain-cert-watch/_helpers.tpl b/templates/common/jobs/domain-cert-watch/_helpers.tpl new file mode 100644 index 0000000..2e11f9f --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/_helpers.tpl @@ -0,0 +1,30 @@ +{{- define "domainCertWatch.appName" -}} +{{- $name := default "domain-cert-watch" .Values.common.domainCertWatch.nameOverride -}} +{{- $env := default "env" .Values.global.appEnv -}} +{{- printf "%s-%s" $name $env | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "domainCertWatch.serviceAccountName" -}} +{{- if .Values.common.domainCertWatch.serviceAccount.enabled -}} + {{ default "domain-cert-watch" .Values.common.domainCertWatch.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.common.domainCertWatch.serviceAccount.name }} +{{- end -}} +{{- end -}} + + +{{- define "domainCertWatch.domainCertHash" -}} +{{- $secret := (lookup "v1" "Secret" .Release.Namespace "domain-certificate") -}} +{{- if $secret -}} +{{- $secretData := $secret.data -}} +{{- $json := toJson $secretData -}} +{{- printf "%s" $json | sha1sum -}} +{{- else -}} +{{- "UNINITIALIZED" -}} +{{- end -}} +{{- end -}} + + diff --git a/templates/common/jobs/domain-cert-watch/calico.netpol.yaml b/templates/common/jobs/domain-cert-watch/calico.netpol.yaml new file mode 100644 index 0000000..605b4e5 --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/calico.netpol.yaml @@ -0,0 +1,33 @@ +{{- if .Values.common.domainCertWatch.calicoNetpol.enabled }} +apiVersion: projectcalico.org/v3 +kind: NetworkPolicy +metadata: + name: domain-cert-watch-network-policy + namespace: {{ .Release.Namespace }} +spec: + order: 10 + selector: dplane == 'domain-cert-watch-job' + types: + - Egress + egress: + # allow to communicate to DNS pods + - action: Allow + protocol: UDP + destination: + namespaceSelector: projectcalico.org/name == 'kube-system' + ports: + - 53 + - action: Allow + protocol: TCP + destination: + namespaceSelector: projectcalico.org/name == 'kube-system' + ports: + - 53 + # allow to communicate with k8s api server + - action: Allow + destination: + services: + name: kubernetes + namespace: default + protocol: TCP +{{- end}} \ No newline at end of file diff --git a/templates/common/jobs/domain-cert-watch/domain-cert-watch-configmap.yaml b/templates/common/jobs/domain-cert-watch/domain-cert-watch-configmap.yaml new file mode 100644 index 0000000..e6cbe7d --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/domain-cert-watch-configmap.yaml @@ -0,0 +1,11 @@ +{{- if .Values.common.domainCertWatch.enabled -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "domainCertWatch.appName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "dataplane.labels" . | nindent 4 }} +data: + sha: {{ template "domainCertWatch.domainCertHash" . }} +{{- end }} \ No newline at end of file diff --git a/templates/common/jobs/domain-cert-watch/domain-cert-watch-cronjob.yaml b/templates/common/jobs/domain-cert-watch/domain-cert-watch-cronjob.yaml new file mode 100644 index 0000000..b9b1ff8 --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/domain-cert-watch-cronjob.yaml @@ -0,0 +1,79 @@ +{{- if .Values.common.domainCertWatch.enabled -}} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ template "domainCertWatch.appName" . }} + namespace: {{ .Release.Namespace }} + labels: + dplane: "domain-cert-watch-job" +spec: + concurrencyPolicy: Forbid + failedJobsHistoryLimit: 1 + jobTemplate: + spec: + ttlSecondsAfterFinished: {{ .Values.common.domainCertWatch.job_ttl }} + template: + metadata: + labels: + dplane: "domain-cert-watch-job" + spec: + serviceAccountName: {{ include "domainCertWatch.serviceAccountName" . }} + containers: + - image: "{{ default .Values.global.image.repository .Values.global.alpinetools.image.repository }}/{{ .Values.global.alpinetools.image.name }}:{{ .Values.global.alpinetools.image.tag }}" + imagePullPolicy: {{ .Values.global.image.pullPolicy }} + command: [ "/bin/sh", "-c" ] + args: + - | + cm_name={{ template "domainCertWatch.appName" . }} + if dcert=$(kubectl get secrets domain-certificate -o jsonpath='{.data}'); then + dc_sha=$(echo -n $dcert | sha1sum | awk '{print $1}'); + echo "Generated domain-certificate secret sha - $dc_sha"; + if dcert_cm=$(kubectl get configmap $cm_name -o json); then + stored_sha=$(echo -n $dcert_cm | jq -r .data.sha); + echo "Retrieved domain-certificate stored sha - $stored_sha"; + if [[ "$stored_sha" == "UNINITIALIZED" || "$stored_sha" != "$dc_sha" ]]; then + echo "Stored sha found in configmap $cm_name does not match, updating entry"; + if kubectl create configmap $cm_name --from-literal=sha="$dc_sha" -o yaml --dry-run=client | kubectl apply -f -; then + echo "Updated configmap $cm_name with new sha - $dc_sha"; + if [[ "$stored_sha" != "UNINITIALIZED" ]]; then + echo "The domain-certificate secret has changed, rolling envoy and inbound-worker deployments"; + kubectl rollout restart deployment -l dplane=envoy; + kubectl rollout restart deployment -l dplane=inbound-worker; + fi + exit 0; + else + echo "Failed to update configmap $cm_name"; + exit 1; + fi + else + echo "The secret domain-certificate has not changed, no action needed"; + exit 0; + fi + else + echo "Failed to retrieve stored domain-certificate sha"; + exit 1; + fi + else + echo "Could not get the secret domain-certificate"; + exit 1; + fi + name: domain-cert-watch + {{- with .Values.common.domainCertWatch.securityContext }} + securityContext: + {{- toYaml . | nindent 16 }} + {{- end }} + restartPolicy: Never + {{- with .Values.global.image.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if .Values.common.domainCertWatch.podSecurityContextEnabled -}} + {{- with .Values.common.domainCertWatch.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + schedule: {{ .Values.common.domainCertWatch.schedule | squote }} + successfulJobsHistoryLimit: 1 + suspend: false +{{- end }} diff --git a/templates/common/jobs/domain-cert-watch/role.yaml b/templates/common/jobs/domain-cert-watch/role.yaml new file mode 100644 index 0000000..be9aac9 --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/role.yaml @@ -0,0 +1,33 @@ +{{- if ( and .Values.common.domainCertWatch.serviceAccount.enabled ( not .Values.common.domainCertWatch.serviceAccount.preexisting ) ) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "domainCertWatch.appName" . }}-role + labels: + {{- include "dataplane.labels" . | nindent 4 }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - patch + - update + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - patch +{{- end }} \ No newline at end of file diff --git a/templates/common/jobs/domain-cert-watch/roleBinding.yaml b/templates/common/jobs/domain-cert-watch/roleBinding.yaml new file mode 100644 index 0000000..0b2b5bf --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/roleBinding.yaml @@ -0,0 +1,16 @@ +{{- if ( and .Values.common.domainCertWatch.serviceAccount.enabled ( not .Values.common.domainCertWatch.serviceAccount.preexisting ) ) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "domainCertWatch.appName" . }}-role-binding + labels: + {{- include "dataplane.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "domainCertWatch.appName" . }}-role +subjects: + - kind: ServiceAccount + name: {{ include "domainCertWatch.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/templates/common/jobs/domain-cert-watch/serviceaccount.yaml b/templates/common/jobs/domain-cert-watch/serviceaccount.yaml new file mode 100644 index 0000000..280c1a5 --- /dev/null +++ b/templates/common/jobs/domain-cert-watch/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.common.domainCertWatch.serviceAccount.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "domainCertWatch.serviceAccountName" . }} + labels: + {{- include "dataplane.labels" . | nindent 4 }} + {{- with .Values.common.domainCertWatch.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/templates/common/persistentvolume-az.yaml b/templates/common/persistentvolume-az.yaml new file mode 100644 index 0000000..e4c635c --- /dev/null +++ b/templates/common/persistentvolume-az.yaml @@ -0,0 +1,28 @@ +{{- if .Values.common.azfiles.enabled -}} +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ .Release.Namespace }}-{{ .Values.common.persistence.volumeName }} +spec: + accessModes: + - {{ .Values.common.persistence.volumeAccessMode }} + capacity: + storage: {{ .Values.common.persistence.volumeCapacity }} + persistentVolumeReclaimPolicy: {{ .Values.common.persistence.volumeReclaimPolicy }} + claimRef: + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + namespace: {{ .Release.Namespace }} + csi: + driver: file.csi.azure.com + readOnly: false + volumeHandle: {{.Values.common.azfiles.resourceGroup}}#{{.Values.common.azfiles.storageAccountName}}#{{.Values.common.azfiles.fileshareName}}###{{.Release.Namespace}} + volumeAttributes: + resourceGroup: {{ .Values.common.azfiles.resourceGroup}} + shareName: {{.Values.common.azfiles.fileshareName}} + nodeStageSecretRef: + name: {{ .Values.common.azfiles.secretName }} + namespace: {{ .Release.Namespace }} + mountOptions: + - uid=10010 + - gid=10020 +{{- end -}} diff --git a/templates/common/persistentvolume-efs.yaml b/templates/common/persistentvolume-efs.yaml new file mode 100644 index 0000000..c3a9ea2 --- /dev/null +++ b/templates/common/persistentvolume-efs.yaml @@ -0,0 +1,20 @@ +{{- if .Values.common.efs.enabled -}} +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ .Release.Namespace }}-{{ .Values.common.persistence.volumeName }} +spec: + capacity: + # This doesn't matter for efs, but k8s requires this field to exist + storage: {{ .Values.common.persistence.volumeCapacity }} + volumeMode: Filesystem + accessModes: + - {{ .Values.common.persistence.volumeAccessMode }} + persistentVolumeReclaimPolicy: {{ .Values.common.persistence.volumeReclaimPolicy }} + claimRef: + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + namespace: {{ .Release.Namespace }} + csi: + driver: efs.csi.aws.com + volumeHandle: {{ .Values.common.efs.volumeHandle }} +{{- end -}} diff --git a/templates/common/persistentvolume-nfs.yaml b/templates/common/persistentvolume-nfs.yaml new file mode 100644 index 0000000..e6fbbbf --- /dev/null +++ b/templates/common/persistentvolume-nfs.yaml @@ -0,0 +1,32 @@ +{{- if and .Values.common.nfs.staticPvc .Values.common.nfs.enabled -}} +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ .Release.Namespace }}-{{ .Values.common.persistence.volumeName }} +spec: + capacity: + # This doesn't matter for efs, but k8s requires this field to exist + storage: {{ .Values.common.persistence.volumeCapacity }} + volumeMode: Filesystem + accessModes: + - {{ .Values.common.persistence.volumeAccessMode }} + persistentVolumeReclaimPolicy: {{ .Values.common.persistence.volumeReclaimPolicy }} + storageClassName: "{{ .Values.common.nfs.storageClassName }}" + claimRef: + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + namespace: {{ .Release.Namespace }} + mountOptions: {{ toYaml .Values.common.nfs.mountOptions | nindent 2 }} + {{- if eq .Values.common.nfs.mode "csi" }} + csi: + driver: nfs.csi.k8s.io + readOnly: false + volumeHandle: {{ .Values.common.nfs.server }}{{ .Values.common.nfs.path }} + volumeAttributes: + server: {{ .Values.common.nfs.server }} + share: {{ .Values.common.nfs.path }} + {{- else -}} + nfs: + path: {{ .Values.nfs.path }} + server: {{ .Values.nfs.server }} + {{- end -}} +{{- end -}} diff --git a/templates/common/persistentvolumeclaim-az.yaml b/templates/common/persistentvolumeclaim-az.yaml new file mode 100644 index 0000000..f38be57 --- /dev/null +++ b/templates/common/persistentvolumeclaim-az.yaml @@ -0,0 +1,15 @@ +{{- if .Values.common.azfiles.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} +spec: + accessModes: + - {{ .Values.common.persistence.claimAccessMode }} + storageClassName: "manage-csi" + resources: + requests: + storage: {{ .Values.common.persistence.claimRequestCapacity }} + volumeMode: Filesystem + volumeName: {{ .Release.Namespace }}-{{ .Values.common.persistence.volumeName }} +{{- end -}} \ No newline at end of file diff --git a/templates/common/persistentvolumeclaim-efs.yaml b/templates/common/persistentvolumeclaim-efs.yaml new file mode 100644 index 0000000..8321836 --- /dev/null +++ b/templates/common/persistentvolumeclaim-efs.yaml @@ -0,0 +1,15 @@ +{{- if .Values.common.efs.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + namespace: {{ .Release.Namespace }} +spec: + accessModes: + - {{ .Values.common.persistence.claimAccessMode }} + resources: + requests: + storage: {{ .Values.common.persistence.claimRequestCapacity }} + volumeMode: Filesystem + volumeName: {{ .Release.Namespace }}-{{ .Values.common.persistence.volumeName }} +{{- end -}} diff --git a/templates/common/persistentvolumeclaim-nfs.yaml b/templates/common/persistentvolumeclaim-nfs.yaml new file mode 100644 index 0000000..fa52b13 --- /dev/null +++ b/templates/common/persistentvolumeclaim-nfs.yaml @@ -0,0 +1,15 @@ +{{- if .Values.common.nfs.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + # need to use efs here, though it's nfs, for compatibility with legacy setup + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + namespace: {{ .Release.Namespace }} +spec: + accessModes: + - {{ .Values.common.persistence.claimAccessMode }} + storageClassName: "{{ .Values.common.nfs.storageClassName }}" + resources: + requests: + storage: {{ .Values.common.persistence.claimRequestCapacity }} +{{- end -}} diff --git a/templates/common/persistentvolumeclaim.yaml b/templates/common/persistentvolumeclaim.yaml new file mode 100644 index 0000000..8c7f212 --- /dev/null +++ b/templates/common/persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +{{- if .Values.common.pvc.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + namespace: {{ .Release.Namespace }} +spec: + accessModes: + - {{ .Values.common.pvc.claimAccessMode }} + storageClassName: {{ .Values.common.pvc.storageClass }} + resources: + requests: + storage: {{ .Values.common.pvc.claimRequestCapacity }} +{{- end -}} \ No newline at end of file diff --git a/templates/common/secret-ampint-docker-artifactory.yaml b/templates/common/secret-ampint-docker-artifactory.yaml new file mode 100644 index 0000000..d15980c --- /dev/null +++ b/templates/common/secret-ampint-docker-artifactory.yaml @@ -0,0 +1,10 @@ +{{- if not (eq .Values.global.image.createPullSecret.dockerconfigjson "") }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: ampint-docker-artifactory +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ required "a valid dockerconfigjson is required for the secret ampint-docker-artifactory" .Values.global.image.createPullSecret.dockerconfigjson }} +{{- end }} \ No newline at end of file diff --git a/templates/common/secret-cert-p12.yaml b/templates/common/secret-cert-p12.yaml new file mode 100644 index 0000000..e69de29 diff --git a/templates/common/secret-certificate-password.yaml b/templates/common/secret-certificate-password.yaml new file mode 100644 index 0000000..f5ca512 --- /dev/null +++ b/templates/common/secret-certificate-password.yaml @@ -0,0 +1,12 @@ +{{- if eq .Values.common.certificate.password "" -}} +{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace "certificate-password" ).metadata | required "Secret 'certificate-password' is required. Create it external to helm chart or set common.certificate.password" }} +{{- else }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: certificate-password +type: "Opaque" +data: + password: {{ required "common.certificate.password is required for the secret certificate-password" .Values.common.certificate.password }} +{{- end }} \ No newline at end of file diff --git a/templates/common/secret-cluster-details.yaml b/templates/common/secret-cluster-details.yaml new file mode 100644 index 0000000..3460b77 --- /dev/null +++ b/templates/common/secret-cluster-details.yaml @@ -0,0 +1,13 @@ +{{- if .Values.common.clusterDetails.existingSecret -}} +{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace .Values.common.clusterDetails.name ).metadata | required "Secret .Values.common.clusterDetails.name is required. Create it external to helm chart or set existingSecret to false" }} +{{- else }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.common.clusterDetails.name }} +type: "Opaque" +data: + dxchange_jwt_secret: {{ ((eq .Values.global.clusterKey "") | ternary .Values.common.clusterDetails.jwtSecret (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).secretKey | b64enc)) | required "set required common.clusterDetails values if using shared dataplane. Otherwise set global.clusterKey" }} + cluster_id: {{ ((eq .Values.global.clusterKey "") | ternary .Values.common.clusterDetails.clusterId (printf "%s" (.Values.global.clusterKey | b64dec | fromJson).id | b64enc)) | required "set required common.clusterDetails values if using shared dataplane. Otherwise set global.clusterKey" }} +{{- end }} \ No newline at end of file diff --git a/templates/common/secret-frommail.yaml b/templates/common/secret-frommail.yaml new file mode 100644 index 0000000..baf096a --- /dev/null +++ b/templates/common/secret-frommail.yaml @@ -0,0 +1,15 @@ +{{- if .Values.common.email_authentication -}} +{{- if .Values.common.fromMail.existingSecret -}} +{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace .Values.common.fromMail.name ).metadata | required "Secret .Values.common.fromMail.name is required. Create it external to helm chart or set existingSecret to false" }} +{{- else }} + +{{ $fromMailPass := .Values.common.fromMail.password | required "Value password is required for the secret fromMail" -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.common.fromMail.name }} +type: "Opaque" +data: + dxchange_email_frompassword: {{ $fromMailPass | quote }} +{{- end -}} +{{- end -}} diff --git a/templates/common/secret-storage-az.yaml b/templates/common/secret-storage-az.yaml new file mode 100644 index 0000000..7667b57 --- /dev/null +++ b/templates/common/secret-storage-az.yaml @@ -0,0 +1,19 @@ +{{- if .Values.common.azfiles.enabled -}} +{{- if .Values.common.azfiles.existingSecret -}} +{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace .Values.common.azfiles.secretName ).metadata | required "Secret .Values.common.azfiles.secretName is required when azfiles enabled. Create it external to helm chart or set existingSecret to false" }} +{{- else }} + +{{ $storageAccName := ((.Values.common.azfiles.storageAccountName | b64enc )) | trim | required "Value storageAccountName is required for the secret azurefs-secret" -}} +{{ $storageAccKey := .Values.common.azfiles.azureStorageAccountKey | required "Value azureStorageAccountKey is required for the secret azurefs-secret" -}} + +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.common.azfiles.secretName }} +type: "Opaque" +data: + azurestorageaccountname: {{ $storageAccName | quote }} + azurestorageaccountkey: {{ $storageAccKey | quote }} + +{{- end -}} +{{- end -}} diff --git a/templates/envoy/NOTES.txt b/templates/envoy/NOTES.txt new file mode 100644 index 0000000..2031204 --- /dev/null +++ b/templates/envoy/NOTES.txt @@ -0,0 +1,8 @@ +Thank you for installing the {{ .Chart.Name }} chart for the Amplify Integration Platform. + +Your release is named {{ .Release.Name }}. + +To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get all {{ .Release.Name }} diff --git a/templates/envoy/_helpers.tpl b/templates/envoy/_helpers.tpl new file mode 100644 index 0000000..677ba7b --- /dev/null +++ b/templates/envoy/_helpers.tpl @@ -0,0 +1,59 @@ +{{/* +Envoy. +*/}} +{{- define "envoy.name" -}} +{{- default "envoy" .Values.envoy.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "envoy.fullname" -}} +{{- if .Values.envoy.fullnameOverride }} +{{- .Values.envoy.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default "envoy" .Values.envoy.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + + +{{/* +Common labels +*/}} +{{- define "envoy.labels" -}} +{{ include "dataplane.labels" . }} +{{ include "envoy.selectorLabels" . }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "envoy.selectorLabels" -}} +app.kubernetes.io/name: {{ include "envoy.name" . }} +app: {{ include "envoy.appName" . }} +dplane: "envoy" +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "envoy.serviceAccountName" -}} +{{- if .Values.envoy.serviceAccount.create }} +{{- default (include "envoy.fullname" .) .Values.envoy.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.envoy.serviceAccount.name }} +{{- end }} +{{- end }} + +{{- define "envoy.appName" -}} +{{- $name := default "envoy" .Values.envoy.nameOverride -}} +{{- $env := default "envoy" .Values.global.appEnv -}} +{{- printf "%s-%s" $name $env | trunc 63 | trimSuffix "-" -}} +{{- end }} \ No newline at end of file diff --git a/templates/envoy/calico.netpol.yaml b/templates/envoy/calico.netpol.yaml new file mode 100644 index 0000000..4b62123 --- /dev/null +++ b/templates/envoy/calico.netpol.yaml @@ -0,0 +1,47 @@ +{{- if .Values.envoy.calicoNetpol.enabled }} +apiVersion: projectcalico.org/v3 +kind: NetworkPolicy +metadata: + name: {{ template "envoy.appName" . }} + namespace: {{ .Release.Namespace }} +spec: + order: 10 + selector: dplane == 'envoy' + types: + - Ingress + - Egress + ingress: + ### traffic from the load balancer ### + - action: Allow + protocol: TCP + source: {{ toYaml .Values.envoy.calicoNetpol.subnetEntityRule | nindent 8 }} + destination: + ports: + {{- range .Values.envoy.service.ports }} + - {{ .targetPort }} + {{end }} + egress: + # allow to communicate to DNS pods + - action: Allow + protocol: UDP + destination: + namespaceSelector: projectcalico.org/name == 'kube-system' + ports: + - 53 + - action: Allow + protocol: TCP + destination: + namespaceSelector: projectcalico.org/name == 'kube-system' + ports: + - 53 + ### all egress traffic to the inbound-worker,orchestrator ### + - action: Allow + protocol: TCP + destination: + selector: dplane == 'inbound-worker' || dplane == 'orchestrator' || dplane == 'pep-server' + namespaceSelector: projectcalico.org/name == '{{ .Release.Namespace }}' + ### Traffic to the internet ### + - action: Allow + protocol: TCP + destination: {{ toYaml .Values.envoy.calicoNetpol.outboundRule | nindent 8 }} +{{- end}} diff --git a/templates/envoy/configmap.yaml b/templates/envoy/configmap.yaml new file mode 100644 index 0000000..5b736c2 --- /dev/null +++ b/templates/envoy/configmap.yaml @@ -0,0 +1,602 @@ +{{- if eq .Values.common.externalConfigMaps false }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "envoy.appName" . }} + labels: +{{ include "envoy.labels" . | indent 4 }} +data: + {{- range $key, $value := .Values.envoy.files }} + {{ $key }}: |- + {{ $value | default "" | indent 4 }} + {{- end -}} + {{- if (index .Values.envoy.templates "envoy.yaml") }} + envoy.yaml: |- + {{ $valueWithDefault := default "" (index .Values.envoy.templates "envoy.yaml") -}} + {{ tpl $valueWithDefault $ | indent 4 }} + {{- else }} + envoy.yaml: |- + node: + cluster: ampint-gw + id: ampint-gw + + admin: + address: + socket_address: + address: 0.0.0.0 + port_value: {{ .Values.envoy.adminPorts.admin.containerPort }} + + dynamic_resources: + cds_config: + resource_api_version: V3 + api_config_source: + api_type: GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + lds_config: + resource_api_version: V3 + api_config_source: + api_type: GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + + static_resources: + listeners: + - name: internal + address: + socket_address: + address: 0.0.0.0 + port_value: 9902 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: internal + codec_type: AUTO + stream_idle_timeout: 15s + use_remote_address: true + xff_num_trusted_hops: 0 + common_http_protocol_options: + idle_timeout: 15s + route_config: + name: internal + validate_clusters: false + virtual_hosts: + - name: internal + domains: + - "*" + routes: + - match: + path: "/started" + route: + cluster: blackhole + http_filters: + - name: envoy.filters.http.lua + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inline_code: | + function envoy_on_request(request_handle) + local headers, body = request_handle:httpCall( + "service_admin", + { + [":method"] = "GET", + [":path"] = "/stats?filter=http.apim.rds.ampint-api.update_success&format=text", + [":authority"] = "internal" + }, + "", 2000, false) + + local headersMtls, bodyMtls = request_handle:httpCall( + "service_admin", + { + [":method"] = "GET", + [":path"] = "/stats?filter=http.apim.rds.ampint-api-mtls.update_success&format=text", + [":authority"] = "internal" + }, + "", 2000, false) + + local count = string.gsub(body, "^%s*.+:%s*(.-)%s*$", "%1") + local countMtls = string.match(bodyMtls, ":[%s]*(%d+)") + + if count == "0" or countMtls == "0" then + request_handle:respond( + {[":status"] = "503"}, + "Not Ready") + else + request_handle:respond( + {[":status"] = "200"}, + "Ready") + end + end + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + {{ if .Values.global.multihost.enabled }} + {{ if .Values.global.multihost.listeners.tcp.http.enabled }} + - name: multiHostHttp + address: + socket_address: + address: 0.0.0.0 + port_value: 9080 + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + "allow_requests_without_proxy_protocol": true + filter_chains: + - filter_chain_match: + server_names: + - "{{ .Values.global.multihost.listeners.tcp.http.hostPrefix }}.{{ .Values.global.external_domain }}" + - filters: + - name: envoy.filters.network.connection_limit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit + stat_prefix: traffic_max_connections + max_connections: 30000 + delay: 10s + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: http + cluster: inbound-worker-http + max_connect_attempts: 3 + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + {{- end }} + + {{ if .Values.global.multihost.listeners.ssh.sftp.enabled }} + - name: multiHostSftp + address: + socket_address: + address: 0.0.0.0 + port_value: 2222 + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + "allow_requests_without_proxy_protocol": true + - name: "envoy.filters.listener.tls_inspector" + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: + - "{{ .Values.global.multihost.listeners.ssh.sftp.hostPrefix }}.{{ .Values.global.external_domain }}" + - filters: + - name: envoy.filters.network.connection_limit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit + stat_prefix: ssh_max_connections + max_connections: 30000 + delay: 10s + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: ssh + cluster: inbound-worker-ssh + max_connect_attempts: 3 + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + {{- end }} + + {{ else }} + - name: https-1 + address: + socket_address: + address: 0.0.0.0 + port_value: 9443 + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + "allow_requests_without_proxy_protocol": true + - name: "envoy.filters.listener.tls_inspector" + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: + {{- if eq (include "parent.dataplaneMode" . ) "shared" }} + - "*.{{ .Values.global.external_domain }}" + {{- else }} + - "{{ .Values.global.external_domain }}" + {{- end }} + filters: + - name: envoy.filters.network.connection_limit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit + stat_prefix: traffic_max_connections + max_connections: 30000 + delay: 10s + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: https + cluster: inbound-worker-https + max_connect_attempts: 3 + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + + - name: https-2 + address: + socket_address: + address: 0.0.0.0 + port_value: 8443 + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + "allow_requests_without_proxy_protocol": true + - name: "envoy.filters.listener.tls_inspector" + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: + {{- if eq (include "parent.dataplaneMode" . ) "shared" }} + - "*.{{ .Values.global.external_domain }}" + {{- else }} + - "{{ .Values.global.external_domain }}" + {{- end }} + filters: + - name: envoy.filters.network.connection_limit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit + stat_prefix: traffic_max_connections + max_connections: 30000 + delay: 10s + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: https + cluster: inbound-worker-https + max_connect_attempts: 3 + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + + - name: http + address: + socket_address: + address: 0.0.0.0 + port_value: 9080 + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + "allow_requests_without_proxy_protocol": true + filter_chains: + - filters: + - name: envoy.filters.network.connection_limit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit + stat_prefix: traffic_max_connections + max_connections: 30000 + delay: 10s + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: http + cluster: inbound-worker-http + max_connect_attempts: 3 + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + + - name: ssh + address: + socket_address: + address: 0.0.0.0 + port_value: 2222 + listener_filters: + - name: envoy.filters.listener.proxy_protocol + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol + "allow_requests_without_proxy_protocol": true + - name: "envoy.filters.listener.tls_inspector" + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + filters: + - name: envoy.filters.network.connection_limit + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.connection_limit.v3.ConnectionLimit + stat_prefix: ssh_max_connections + max_connections: 30000 + delay: 10s + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: ssh + cluster: inbound-worker-ssh + max_connect_attempts: 3 + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + {{- end }} + + clusters: + - name: service_admin + connect_timeout: 0.25s + type: STATIC + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + # Configure an HTTP/2 keep-alive to detect connection issues and reconnect + # to the admin server if the connection is no longer responsive. + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: service_admin + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 9901 + - name: xds_cluster + connect_timeout: 0.25s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: xds_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: pep-server-{{ .Values.global.appEnv }} + port_value: 9090 + #Note the health checks needs only to be applied to one of the pep_clusters + health_checks: + - timeout: 2s + interval: 3s + unhealthy_threshold: 2 + healthy_threshold: 2 + grpc_health_check: + service_name: "" + no_traffic_healthy_interval: 5s + + {{- if .Values.common.openTelemetry.enabled }} + - name: otel_cluster + connect_timeout: 0.25s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: otel_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: pep-server-{{ .Values.global.appEnv }} + port_value: 9090 + {{- end }} + - name: als_cluster + connect_timeout: 0.25s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: als_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: pep-server-{{ .Values.global.appEnv }} + port_value: 9090 + - name: ext_authz_cluster + connect_timeout: 0.25s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: ext_authz_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: pep-server-{{ .Values.global.appEnv }} + port_value: 9090 + - name: ext_proc_start_audit_cluster + connect_timeout: 0.25s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: ext_proc_start_audit_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: pep-server-{{ .Values.global.appEnv }} + port_value: 9090 + {{- if ne (include "parent.dataplaneMode" . ) "shared" }} + - name: orchestrator_cluster + connect_timeout: {{ .Values.envoy.timeouts.connectTimeout }} + type: STRICT_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + load_assignment: + cluster_name: orchestrator_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: orchestrator-{{ .Values.global.appEnv }} + port_value: 50051 + + health_checks: + - timeout: 3s + interval: 3s + unhealthy_threshold: 1 + healthy_threshold: 2 + grpc_health_check: + service_name: "" + no_traffic_healthy_interval: 30s + + - name: orchestrator_cluster-sse + connect_timeout: {{ .Values.envoy.timeouts.connectTimeout }} + type: STRICT_DNS + lb_policy: ROUND_ROBIN + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http_protocol_options: {} + load_assignment: + cluster_name: orchestrator_cluster-sse + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: orchestrator-{{ .Values.global.appEnv }} + port_value: 50052 + {{- end }} + - name: inbound-worker-sse + connect_timeout: {{ .Values.envoy.timeouts.connectTimeout }} + http_protocol_options: + allow_chunked_length: true + typed_extension_protocol_options: + envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + type: LOGICAL_DNS + load_assignment: + cluster_name: inbound-worker-sse + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: inbound-worker-{{ .Values.global.appEnv }} + port_value: 7080 + health_checks: + - timeout: 1s + interval: 20s + unhealthy_threshold: 3 + healthy_threshold: 3 + tcp_health_check: {} + + - name: inbound-worker-http + connect_timeout: {{ .Values.envoy.timeouts.connectTimeout }} + http_protocol_options: + allow_chunked_length: true + typed_extension_protocol_options: + envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + type: LOGICAL_DNS + load_assignment: + cluster_name: inbound-worker-http + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: inbound-worker-{{ .Values.global.appEnv }} + port_value: 9080 + health_checks: + - timeout: 1s + interval: 20s + unhealthy_threshold: 3 + healthy_threshold: 3 + tcp_health_check: {} + - name: inbound-worker-https + connect_timeout: {{ .Values.envoy.timeouts.connectTimeout }} + http_protocol_options: + allow_chunked_length: true + typed_extension_protocol_options: + envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + type: LOGICAL_DNS + load_assignment: + cluster_name: inbound-worker-https + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: inbound-worker-{{ .Values.global.appEnv }} + port_value: 9443 + health_checks: + - timeout: 1s + interval: 20s + unhealthy_threshold: 3 + healthy_threshold: 3 + tcp_health_check: {} + - name: inbound-worker-ssh + connect_timeout: {{ .Values.envoy.timeouts.connectTimeout }} + typed_extension_protocol_options: + envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.tcp.v3.TcpProtocolOptions + idle_timeout: {{ .Values.envoy.timeouts.idleTimeout }} + type: LOGICAL_DNS + load_assignment: + cluster_name: inbound-worker-ssh + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: inbound-worker-{{ .Values.global.appEnv }} + port_value: 2222 + health_checks: + - timeout: 1s + interval: 20s + unhealthy_threshold: 3 + healthy_threshold: 3 + tcp_health_check: {} + {{- end }} +{{- end }} diff --git a/templates/envoy/deployment.yaml b/templates/envoy/deployment.yaml new file mode 100644 index 0000000..96db0f7 --- /dev/null +++ b/templates/envoy/deployment.yaml @@ -0,0 +1,180 @@ +{{- if .Values.envoy.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "envoy.appName" . }} + labels: +{{ include "envoy.labels" . | indent 4 }} +spec: + {{- if not .Values.envoy.autoscaling.enabled }} + replicas: {{ .Values.envoy.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "envoy.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.envoy.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.envoy.strategy.rollingUpdate.maxSurge }} + maxUnavailable: {{ .Values.envoy.strategy.rollingUpdate.maxUnavailable }} + template: + metadata: + labels: + {{- include "envoy.selectorLabels" . | nindent 8 }} + {{- if .Values.envoy.podLabels }} + ## Custom pod labels + {{- range $key, $value := .Values.envoy.podLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + annotations: + checksum/config: {{ include (print .Template.BasePath "/envoy/configmap.yaml") . | sha256sum }} + {{- if .Values.envoy.podAnnotations }} + ## Custom pod annotations + {{- range $key, $value := .Values.envoy.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + spec: + {{- with .Values.global.image.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.envoy.podSecurityContextEnabled }} + securityContext: + {{ toYaml .Values.envoy.podSecurityContext | nindent 8 }} + {{- end }} + {{- if .Values.envoy.serviceAccount.enabled }} + serviceAccountName: {{ include "envoy.serviceAccountName" . }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.envoy.terminationGracePeriodSeconds }} + #forces the use of tcp for dns resolutions + dnsConfig: + options: + - name: use-vc + {{- if .Values.fluentBit.enabled }} + {{- include "dataplane.createLogDirectoryInitContainer" (dict "serviceName" "envoy" "securityContext" .Values.envoy.securityContext "additionalPathParam" "" "root" .) | nindent 6 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.envoy.securityContext | nindent 12 }} + image: "{{ default .Values.global.image.repository .Values.envoy.image.repository }}/{{ .Values.envoy.image.name }}:{{ .Values.envoy.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.global.image.pullPolicy }} + envFrom: + - secretRef: + name: {{ .Values.common.clusterDetails.name }} + - configMapRef: + name: configmap-common + command: + {{ toYaml .Values.envoy.command | nindent 12 }} + args: + {{- if $.Values.envoy.argsTemplate }} + {{ tpl $.Values.envoy.argsTemplate $ | nindent 12}} + {{- else }} + {{ toYaml .Values.envoy.args | nindent 12 }} + {{- end }} + ports: + {{- with .Values.envoy.adminPorts }} + {{- range $key, $port := . }} + - name: {{ $key }} + {{ toYaml $port | nindent 14 }} + {{- end }} + {{- end }} + {{- if .Values.global.multihost.enabled }} + - name: ssh + protocol: TCP + containerPort: 2222 + - name: tls + protocol: TCP + containerPort: 8443 + - name: http + protocol: TCP + containerPort: 9080 + {{- else }} + {{- with .Values.envoy.ports }} + {{- range $key, $port := . }} + - name: {{ $key }} + {{ toYaml $port | nindent 14 }} + {{- end }} + {{- end }} + {{- end }} + livenessProbe: {{- toYaml .Values.envoy.livenessProbe | nindent 12 }} + startupProbe: {{- toYaml .Values.envoy.startupProbe | nindent 12 }} + readinessProbe: {{- toYaml .Values.envoy.readinessProbe | nindent 12 }} + env: + - name: server_truststore_password + valueFrom: + secretKeyRef: + key: password + name: certificate-password + - name: server_cert_password + valueFrom: + secretKeyRef: + key: password + name: certificate-password + - name: sftp_server_cert_password + valueFrom: + secretKeyRef: + key: password + name: certificate-password + - name: LOGLEVEL + value: "{{ .Values.envoy.logLevel }}" + {{- with .Values.envoy.env }} + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{ toYaml .Values.envoy.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /config + - name: {{ .Values.global.volumeStorageName }} + mountPath: /efs + {{- range $key, $value := .Values.envoy.secretMounts }} + - name: {{ $key }} + mountPath: {{ $value.mountPath }} + {{- end }} + - mountPath: "/certificate/{{ .Values.common.certificate.name }}" + subPath: {{ .Values.common.certificate.name }} + name: domain-certificate + lifecycle: + {{ toYaml .Values.envoy.lifecycle | nindent 12 }} + {{- with .Values.envoy.nodeSelector }} + nodeSelector: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.envoy.affinity }} + affinity: + {{ toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.envoy.tolerations }} + tolerations: + {{ toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: config + projected: + defaultMode: 420 + sources: + - configMap: + name: {{ template "envoy.appName" . }} + - name: workdir + emptyDir: {} + - name: {{ .Values.global.volumeStorageName }} + persistentVolumeClaim: + claimName: {{ .Release.Namespace }}-{{ .Values.global.claimName }} + {{- range $key, $value := .Values.envoy.secretMounts }} + - name: {{ $key }} + secret: + secretName: {{ $value.secretName }} + defaultMode: {{ $value.defaultMode }} + {{- end }} + - name: cert + emptyDir: {} + - name: domain-certificate + secret: + secretName: domain-certificate + items: + - key: {{ .Values.common.certificate.name }} + path: {{ .Values.common.certificate.name }} +{{- end }} diff --git a/templates/envoy/hpa.yaml b/templates/envoy/hpa.yaml new file mode 100644 index 0000000..3e5db45 --- /dev/null +++ b/templates/envoy/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.envoy.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ template "envoy.appName" . }} + labels: + {{- include "envoy.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "envoy.appName" . }} + minReplicas: {{ .Values.envoy.autoscaling.minReplicas }} + maxReplicas: {{ .Values.envoy.autoscaling.maxReplicas }} + metrics: + {{- if .Values.envoy.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.envoy.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.envoy.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.envoy.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/templates/envoy/poddisruptionbudget.yaml b/templates/envoy/poddisruptionbudget.yaml new file mode 100644 index 0000000..a6db828 --- /dev/null +++ b/templates/envoy/poddisruptionbudget.yaml @@ -0,0 +1,12 @@ +{{- if .Values.envoy.podDisruptionBudget.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ template "envoy.appName" . }} + namespace: {{ .Release.Namespace }} +spec: + minAvailable: {{ .Values.envoy.podDisruptionBudget.minPods }} + selector: + matchLabels: + dplane: {{ .Chart.Name }} +{{- end}} diff --git a/templates/envoy/routes.yaml b/templates/envoy/routes.yaml new file mode 100644 index 0000000..fa948f2 --- /dev/null +++ b/templates/envoy/routes.yaml @@ -0,0 +1,90 @@ +{{- if and .Values.global.multihost.enabled .Values.envoy.route.https.enabled -}} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "envoy.appName" . }}-https + namespace: {{ .Release.Namespace }} + labels: +{{- include "envoy.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ template "envoy.appName" . }} +spec: + host: {{ .Values.global.multihost.listeners.tls.https.hostPrefix }}.{{ .Values.global.external_domain }} + to: + kind: Service + name: {{ template "envoy.appName" . }} + port: + targetPort: 8443 + tls: + termination: passthrough + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +{{- end }} + +--- + +{{- if and .Values.global.multihost.enabled .Values.envoy.route.api.enabled -}} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "envoy.appName" . }}-apim + namespace: {{ .Release.Namespace }} + labels: +{{- include "envoy.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ template "envoy.appName" . }} +spec: + host: {{ .Values.global.multihost.listeners.tls.api.hostPrefix }}.{{ .Values.global.external_domain }} + to: + kind: Service + name: {{ template "envoy.appName" . }} + port: + targetPort: 8443 + tls: + termination: passthrough + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +{{- end }} + +--- + +{{- if and .Values.global.multihost.enabled .Values.envoy.route.webhook.enabled -}} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "envoy.appName" . }}-webhook + namespace: {{ .Release.Namespace }} + labels: +{{- include "envoy.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ template "envoy.appName" . }} +spec: + host: {{ .Values.global.multihost.listeners.tls.webhook.hostPrefix }}.{{ .Values.global.external_domain }} + to: + kind: Service + name: {{ template "envoy.appName" . }} + port: + targetPort: 8443 + tls: + termination: passthrough + insecureEdgeTerminationPolicy: Redirect + wildcardPolicy: None +{{- end }} + +--- + +{{- if and .Values.global.multihost.enabled .Values.envoy.route.http.enabled -}} +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: {{ template "envoy.appName" . }}-http + namespace: {{ .Release.Namespace }} + labels: + {{- include "envoy.labels" . | nindent 4 }} + app.kubernetes.io/component: {{ template "envoy.appName" . }} +spec: + host: {{ .Values.global.multihost.listeners.tcp.http.hostPrefix }}.{{ .Values.global.external_domain }} + to: + kind: Service + name: {{ template "envoy.appName" . }} + port: + targetPort: 9080 + wildcardPolicy: None +{{- end }} diff --git a/templates/envoy/service.yaml b/templates/envoy/service.yaml new file mode 100644 index 0000000..6412df9 --- /dev/null +++ b/templates/envoy/service.yaml @@ -0,0 +1,55 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "envoy.appName" . }} + namespace: {{ .Release.Namespace }} + {{- if .Values.envoy.service.annotations }} + {{- with .Values.envoy.service.annotations }} + annotations: + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- else }} + annotations: + {{- end }} + {{- if eq (include "parent.dataplaneMode" . ) "shared" }} + external-dns.alpha.kubernetes.io/hostname: {{ .Values.global.shared_lb_prefix }}.{{ .Values.global.external_domain }} + {{- else if .Values.global.multihost.enabled}} + external-dns.alpha.kubernetes.io/hostname: "*.{{ .Values.global.external_domain }}" + {{- else }} + external-dns.alpha.kubernetes.io/hostname: "{{ .Values.global.external_domain }}" + {{- end }} + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "{{ .Values.envoy.timeouts.loadbalancer }}" + labels: + {{- include "envoy.labels" . | nindent 4 }} +spec: + type: {{ .Values.envoy.service.type }} + selector: + {{- include "envoy.selectorLabels" . | nindent 4 }} + ports: + {{- if .Values.envoy.exposeProxyAdminPort }} + - name: admin + port: {{ .Values.envoy.proxyAdminPort }} + targetPort: admin + protocol: TCP + {{- end }} + {{- if .Values.global.multihost.enabled }} + - name: ssh + protocol: TCP + port: {{ .Values.global.multihost.listeners.ssh.port }} + targetPort: 2222 + - name: tls + protocol: TCP + port: {{ .Values.global.multihost.listeners.tls.port }} + targetPort: 8443 + - name: http + protocol: TCP + port: {{ .Values.global.multihost.listeners.tcp.port }} + targetPort: 9080 + {{- else }} + {{- range $port := .Values.envoy.service.ports }} + - name: {{ $port.name }} + protocol: {{ $port.protocol }} + port: {{ $port.port }} + targetPort: {{ $port.targetPort }} + {{- end }} + {{- end }} diff --git a/templates/envoy/serviceaccount.yaml b/templates/envoy/serviceaccount.yaml new file mode 100644 index 0000000..ed740de --- /dev/null +++ b/templates/envoy/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if ( and .Values.envoy.serviceAccount.enabled ( not .Values.envoy.serviceAccount.preexisting ) ) -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "envoy.serviceAccountName" . }} + labels: + {{- include "envoy.labels" . | nindent 4 }} + {{- with .Values.envoy.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.envoy.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/templates/fluent-bit/NOTES.txt b/templates/fluent-bit/NOTES.txt new file mode 100644 index 0000000..2031204 --- /dev/null +++ b/templates/fluent-bit/NOTES.txt @@ -0,0 +1,8 @@ +Thank you for installing the {{ .Chart.Name }} chart for the Amplify Integration Platform. + +Your release is named {{ .Release.Name }}. + +To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get all {{ .Release.Name }} diff --git a/templates/fluent-bit/_helpers.tpl b/templates/fluent-bit/_helpers.tpl new file mode 100644 index 0000000..5f4238f --- /dev/null +++ b/templates/fluent-bit/_helpers.tpl @@ -0,0 +1,64 @@ +{{- define "fluent-bit.name" -}} +{{- default "fluent-bit" .Values.fluentBit.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "fluent-bit.appName" -}} +{{- $name := default "fluent-bit" .Values.fluentBit.nameOverride -}} +{{- $env := default "fluent-bit" .Values.global.appEnv -}} +{{- printf "%s-%s" $name $env | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "fluent-bit.labels" -}} +{{ include "dataplane.labels" . }} +{{ include "fluent-bit.selectorLabels" . }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "fluent-bit.selectorLabels" -}} +app.kubernetes.io/name: {{ include "fluent-bit.name" . }} +app: {{ include "fluent-bit.appName" . }} +dplane: "fluent-bit" +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "fluent-bit.serviceAccountName" -}} +{{- if .Values.fluentBit.serviceAccount.enabled -}} + {{ default (include "fluent-bit.name" .) .Values.fluentBit.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.fluentBit.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Fluent-bit image with tag/digest +*/}} +{{- define "fluent-bit.image" -}} +{{ default .Values.global.image.repository .Values.fluentBit.image.repository }}/{{ .Values.fluentBit.image.name }}:{{ .Values.fluentBit.image.buildTag | default .Chart.AppVersion }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "logrotate.serviceAccountName" -}} +{{- if .Values.fluentBit.logrotate.serviceAccount.enabled -}} + {{ default "logrotate" .Values.fluentBit.logrotate.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.fluentBit.logrotate.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Value of efs_root based on the dataplane type +*/}} +{{- define "efs_root.value" -}} +{{- $dataplaneMode := include "parent.dataplaneMode" . -}} +{{- $clusterRefId := tpl .Values.common.clusterRefId . -}} +{{- if eq $dataplaneMode "shared" -}}/efs{{- else -}}/efs/clusters/{{ $clusterRefId }}{{- end -}} +{{- end -}} diff --git a/templates/fluent-bit/calico.netpol.yaml b/templates/fluent-bit/calico.netpol.yaml new file mode 100644 index 0000000..ae0904c --- /dev/null +++ b/templates/fluent-bit/calico.netpol.yaml @@ -0,0 +1,39 @@ +{{- if .Values.fluentBit.calicoNetpol.enabled }} +apiVersion: projectcalico.org/v3 +kind: NetworkPolicy +metadata: + name: {{ template "fluent-bit.appName" . }} + namespace: {{ .Release.Namespace }} +spec: + order: 10 + selector: dplane == 'fluent-bit' + types: + - Egress + egress: + # allow to communicate to DNS pods + - action: Allow + protocol: UDP + destination: + namespaceSelector: projectcalico.org/name == 'kube-system' + ports: + - 53 + - action: Allow + protocol: TCP + destination: + namespaceSelector: projectcalico.org/name == 'kube-system' + ports: + - 53 + # allow to communicate with itself for clustering + - action: Allow + destination: + selector: dplane == 'fluent-bit' + namespaceSelector: projectcalico.org/name == '{{ .Release.Namespace }}' + protocol: TCP + # allow to communicate with k8s api server + - action: Allow + destination: + services: + name: kubernetes + namespace: default + protocol: TCP +{{- end}} diff --git a/templates/fluent-bit/configmap-luascript.yaml b/templates/fluent-bit/configmap-luascript.yaml new file mode 100644 index 0000000..54f48f6 --- /dev/null +++ b/templates/fluent-bit/configmap-luascript.yaml @@ -0,0 +1,104 @@ +{{- if and .Values.fluentBit.enabled (eq .Values.fluentBit.kind "DaemonSet") -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "fluent-bit.appName" . }}-luascript + namespace: {{ .Release.Namespace }} + labels: + {{- include "fluent-bit.labels" . | nindent 4 }} +data: + create_runtime_Directories.lua: | + local function resolve_folder_path(folder_path, file_name) + if file_name == "inbound" then + folder_path = folder_path .. "/" .. file_name + end + return folder_path + end + + local function write_log(folder_path, file_name, log_content) + local file_path = folder_path .. "/" .. file_name .. ".log" + local file, err = io.open(file_path, "a") + if file then + -- Write log content to the file + file:write(log_content .. "\n") + file:close() + print(string.format("Successfully wrote log to file: %s", file_path)) + else + print(string.format("Failed to open file: %s, error: %s", file_path, err)) + end + end + + local function handle_txn_logs(tag, timestamp, record, file_name) + local orgId = record["orgSchema"] + local modeId = record["modeId"] + local transactionId = record["transactionId"] + local log_content = record["log"] + + if not orgId or not modeId then + print("Skipping directory creation: orgId or modeId is nil") + return 1, timestamp, record + end + + local efs_root = os.getenv("efs_root") + print(string.format("efs root value...: %s", efs_root)) + local folder_path = string.format("%s/logs/%s/%s/transaction/%s", efs_root, orgId, modeId, transactionId) + folder_path = resolve_folder_path(folder_path, file_name) + + local success, exit_type, exit_code = os.execute("mkdir -p " .. folder_path) + + if success then + write_log(folder_path, file_name, log_content) + else + print(string.format("Failed to create folder: %s", folder_path)) + end + return 1, timestamp, record + end + + local function handle_app_logs(tag, timestamp, record, dir_name, file_name) + local log_content = record["log"] + local pod_name = record["kubernetes"]["pod_name"] + print(string.format("pod name...: %s", pod_name)) + + local efs_root = os.getenv("efs_root") + print(string.format("efs root value...: %s", efs_root)) + local folder_path = string.format("%s/logs/%s/%s", efs_root, dir_name, pod_name) + folder_path = resolve_folder_path(folder_path, file_name) + + write_log(folder_path, file_name, log_content) + + return 1, timestamp, record + end + + function handle_orchestrator_txn_logs(tag, timestamp, record) + return handle_txn_logs(tag, timestamp, record, "ir") + end + + function handle_inbound_txn_logs(tag, timestamp, record) + return handle_txn_logs(tag, timestamp, record, "inbound") + end + + function handle_fusion_operator_logs(tag, timestamp, record) + return handle_app_logs(tag, timestamp, record, "fusion-operator", "fusion-operator") + end + + function handle_envoy_logs(tag, timestamp, record) + return handle_app_logs(tag, timestamp, record, "envoy", "envoy") + end + + function handle_orchestrator_logs(tag, timestamp, record) + return handle_app_logs(tag, timestamp, record, "ir", "orchestrator") + end + + function handle_inbound_logs(tag, timestamp, record) + return handle_app_logs(tag, timestamp, record, "inbound", "inbound") + end + + function handle_pep_server_logs(tag, timestamp, record) + return handle_app_logs(tag, timestamp, record, "pep-server", "pep-server") + end + + function handle_sink_agent_logs(tag, timestamp, record) + return handle_app_logs(tag, timestamp, record, "sinkagent", "sinkagent") + end + +{{- end }} \ No newline at end of file diff --git a/templates/fluent-bit/configmap_daemonset.yaml b/templates/fluent-bit/configmap_daemonset.yaml new file mode 100644 index 0000000..7681fe7 --- /dev/null +++ b/templates/fluent-bit/configmap_daemonset.yaml @@ -0,0 +1,242 @@ +{{- if and .Values.fluentBit.enabled (eq .Values.fluentBit.kind "DaemonSet") -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "fluent-bit.appName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "fluent-bit.labels" . | nindent 4 }} +data: + custom_parsers.conf: | + [PARSER] + Name docker_no_time + Format json + Time_Keep Off + Time_Key time + Time_Format %Y-%m-%dT%H:%M:%S.%L + [PARSER] + Name parser + Format regex + Regex ^(?[^\s]+) (?[0-9a-fA-F-]{36}) (?[0-9a-fA-F-]{36}) (?