mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
Some checks failed
Webhook Upgrade Validation / webhook-upgrade-check (push) Failing after 49s
* Feat(logging): Add colorized logging support with DevLogs option Signed-off-by: vishal210893 <vishal210893@gmail.com> * Fix: return error on directory walk failure in lookupFilesInRoot Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Add logger setup for local development with DevLogs option Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Enable/Disable DevLogs option in kubevela-controller configuration Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Make DevLogs configurable in kubevela-controller and values.yaml Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Add devLogs option for formatted logging support in README and values.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Update logger setup for DevLogs and handle errors in file lookup Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Enhance caching by capturing Go cache directories for improved build performance Signed-off-by: vishal210893 <vishal210893@gmail.com> * Feat(logging): Enhance caching by capturing Go cache directories for improved build performance Signed-off-by: vishal210893 <vishal210893@gmail.com> --------- Signed-off-by: vishal210893 <vishal210893@gmail.com> Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com>
485 lines
18 KiB
YAML
485 lines
18 KiB
YAML
{{- if .Values.serviceAccount.create -}}
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: {{ include "kubevela.serviceAccountName" . }}
|
|
namespace: {{ .Release.Namespace }}
|
|
labels:
|
|
{{- include "kubevela.labels" . | nindent 4 }}
|
|
{{- with .Values.serviceAccount.annotations }}
|
|
annotations:
|
|
{{- toYaml . | nindent 4 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{ if .Values.authentication.enabled }}
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRole
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:manager
|
|
rules:
|
|
- apiGroups: ["core.oam.dev", "terraform.core.oam.dev", "prism.oam.dev", "standard.oam.dev"]
|
|
resources: ["*"]
|
|
verbs: ["*"]
|
|
- apiGroups: ["cluster.open-cluster-management.io"]
|
|
resources: ["managedclusters"]
|
|
verbs: ["get", "list", "watch"]
|
|
- apiGroups: [""]
|
|
resources: ["users", "groups", "serviceaccounts"]
|
|
verbs: ["impersonate"]
|
|
- apiGroups: [""]
|
|
resources: ["namespaces", "secrets", "services"]
|
|
verbs: ["get", "watch", "list"]
|
|
- apiGroups: [""]
|
|
resources: ["configmaps", "events"]
|
|
verbs: ["*"]
|
|
- apiGroups: ["apps"]
|
|
resources: ["controllerrevisions"]
|
|
verbs: ["*"]
|
|
- apiGroups: ["apiregistration.k8s.io"]
|
|
resources: ["apiservices"]
|
|
verbs: ["get", "list", "watch", "update"]
|
|
- apiGroups: ["coordination.k8s.io"]
|
|
resources: ["leases"]
|
|
verbs: ["*"]
|
|
- apiGroups: ["admissionregistration.k8s.io"]
|
|
resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"]
|
|
verbs: ["get", "list", "watch"]
|
|
- apiGroups: ["flowcontrol.apiserver.k8s.io"]
|
|
resources: ["prioritylevelconfigurations", "flowschemas"]
|
|
verbs: ["get", "list", "watch"]
|
|
- apiGroups: ["authentication.k8s.io"]
|
|
resources: ["tokenreviews"]
|
|
verbs: ["*"]
|
|
- apiGroups: ["authorization.k8s.io"]
|
|
resources: ["subjectaccessreviews"]
|
|
verbs: ["*"]
|
|
---
|
|
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRoleBinding
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:manager-authentication-rolebinding
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: ClusterRole
|
|
name: {{ include "kubevela.fullname" . }}:manager
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: {{ include "kubevela.serviceAccountName" . }}
|
|
namespace: {{ .Release.Namespace }}
|
|
|
|
{{ else }}
|
|
|
|
---
|
|
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRoleBinding
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:manager-rolebinding
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: ClusterRole
|
|
name: "cluster-admin"
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: {{ include "kubevela.serviceAccountName" . }}
|
|
namespace: {{ .Release.Namespace }}
|
|
|
|
{{ end }}
|
|
|
|
|
|
{{ if and .Values.sharding.enabled .Values.authentication.enabled }}
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: Role
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:shard-scheduler
|
|
namespace: {{ .Release.Namespace }}
|
|
rules:
|
|
- apiGroups: [""]
|
|
resources: ["pods"]
|
|
verbs: ["get", "list", "watch"]
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: RoleBinding
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:shard-scheduler
|
|
namespace: {{ .Release.Namespace }}
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: Role
|
|
name: {{ include "kubevela.fullname" . }}:shard-scheduler
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: {{ include "kubevela.serviceAccountName" . }}
|
|
{{ end }}
|
|
|
|
---
|
|
# permissions to do leader election.
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: Role
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:leader-election-role
|
|
namespace: {{ .Release.Namespace }}
|
|
rules:
|
|
- apiGroups:
|
|
- ""
|
|
resources:
|
|
- configmaps
|
|
verbs:
|
|
- get
|
|
- list
|
|
- watch
|
|
- create
|
|
- update
|
|
- patch
|
|
- delete
|
|
- apiGroups:
|
|
- ""
|
|
resources:
|
|
- configmaps/status
|
|
verbs:
|
|
- get
|
|
- update
|
|
- patch
|
|
- apiGroups:
|
|
- ""
|
|
resources:
|
|
- events
|
|
verbs:
|
|
- create
|
|
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: RoleBinding
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:leader-election-rolebinding
|
|
namespace: {{ .Release.Namespace }}
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: Role
|
|
name: {{ include "kubevela.fullname" . }}:leader-election-role
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: {{ include "kubevela.serviceAccountName" . }}
|
|
|
|
---
|
|
# permissions to read the view of VelaQL, schemas, and templates.
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: Role
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:template-reader-role
|
|
namespace: {{ .Release.Namespace }}
|
|
rules:
|
|
- apiGroups:
|
|
- ""
|
|
resources:
|
|
- configmaps
|
|
verbs:
|
|
- get
|
|
- list
|
|
- watch
|
|
- apiGroups:
|
|
- ""
|
|
resources:
|
|
- configmaps/status
|
|
verbs:
|
|
- get
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: RoleBinding
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}:template-reader-binding
|
|
namespace: {{ .Release.Namespace }}
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: Role
|
|
name: {{ include "kubevela.fullname" . }}:template-reader-role
|
|
subjects:
|
|
- kind: Group
|
|
name: template-reader
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}
|
|
namespace: {{ .Release.Namespace }}
|
|
labels:
|
|
controller.oam.dev/name: vela-core
|
|
{{- include "kubevela.labels" . | nindent 4 }}
|
|
spec:
|
|
replicas: {{ .Values.replicaCount }}
|
|
selector:
|
|
matchLabels:
|
|
{{- include "kubevela.selectorLabels" . | nindent 6 }}
|
|
template:
|
|
metadata:
|
|
labels:
|
|
{{- include "kubevela.selectorLabels" . | nindent 8 }}
|
|
{{ if .Values.sharding.enabled }}
|
|
controller.core.oam.dev/shard-id: master
|
|
{{ end }}
|
|
annotations:
|
|
prometheus.io/path: /metrics
|
|
prometheus.io/port: "8080"
|
|
prometheus.io/scrape: "true"
|
|
spec:
|
|
{{- with .Values.imagePullSecrets }}
|
|
imagePullSecrets:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
serviceAccountName: {{ include "kubevela.serviceAccountName" . }}
|
|
securityContext:
|
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
|
containers:
|
|
- name: {{ .Release.Name }}
|
|
securityContext:
|
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
|
args:
|
|
- "--metrics-addr=:8080"
|
|
- "--enable-leader-election"
|
|
{{ if ne .Values.logFilePath "" }}
|
|
- "--log-file-path={{ .Values.logFilePath }}"
|
|
- "--log-file-max-size={{ .Values.logFileMaxSize }}"
|
|
{{ end }}
|
|
{{ if .Values.logDebug }}
|
|
- "--log-debug=true"
|
|
{{ end }}
|
|
{{ if .Values.admissionWebhooks.enabled }}
|
|
- "--use-webhook=true"
|
|
- "--webhook-port={{ .Values.webhookService.port }}"
|
|
- "--webhook-cert-dir={{ .Values.admissionWebhooks.certificate.mountPath }}"
|
|
{{ end }}
|
|
{{ if ne .Values.optimize.cachedGvks "" }}
|
|
- "--optimize-cached-gvks={{ .Values.optimize.cachedGvks }}"
|
|
{{ end }}
|
|
{{ if .Values.optimize.markWithProb }}
|
|
- "--optimize-mark-with-prob={{ .Values.optimize.markWithProb }}"
|
|
{{ end }}
|
|
{{ if .Values.optimize.disableComponentRevision }}
|
|
- "--optimize-disable-component-revision"
|
|
{{ end }}
|
|
{{ if .Values.optimize.disableApplicationRevision }}
|
|
- "--optimize-disable-application-revision"
|
|
{{ end }}
|
|
{{ if .Values.optimize.enableInMemoryWorkflowContext }}
|
|
- "--optimize-enable-in-memory-workflow-context"
|
|
{{ end }}
|
|
{{ if .Values.optimize.disableResourceApplyDoubleCheck }}
|
|
- "--optimize-disable-resource-apply-double-check"
|
|
{{ end }}
|
|
{{ if not .Values.optimize.enableResourceTrackerDeleteOnlyTrigger }}
|
|
- "--optimize-enable-resource-tracker-delete-only-trigger=false"
|
|
{{ end }}
|
|
- "--health-addr=:{{ .Values.healthCheck.port }}"
|
|
- "--system-definition-namespace={{ include "systemDefinitionNamespace" . }}"
|
|
- "--application-revision-limit={{ .Values.applicationRevisionLimit }}"
|
|
- "--definition-revision-limit={{ .Values.definitionRevisionLimit }}"
|
|
{{ if .Values.multicluster.enabled }}
|
|
- "--enable-cluster-gateway"
|
|
{{ if .Values.multicluster.clusterGateway.direct }}
|
|
- "--cluster-gateway-url={{ .Release.Name }}-cluster-gateway-service:9443"
|
|
{{ if .Values.multicluster.clusterGateway.secureTLS.enabled }}
|
|
- "--cluster-gateway-ca-file=/cluster-gateway-tls-cert/ca"
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ if .Values.multicluster.metrics.enabled }}
|
|
- "--enable-cluster-metrics"
|
|
{{ end }}
|
|
- "--application-re-sync-period={{ .Values.controllerArgs.reSyncPeriod }}"
|
|
- "--concurrent-reconciles={{ .Values.concurrentReconciles }}"
|
|
- "--kube-api-qps={{ .Values.kubeClient.qps }}"
|
|
- "--kube-api-burst={{ .Values.kubeClient.burst }}"
|
|
- "--max-workflow-wait-backoff-time={{ .Values.workflow.backoff.maxTime.waitState }}"
|
|
- "--max-workflow-failed-backoff-time={{ .Values.workflow.backoff.maxTime.failedState }}"
|
|
- "--max-workflow-step-error-retry-times={{ .Values.workflow.step.errorRetryTimes }}"
|
|
- "--enable-external-package-for-default-compiler={{ .Values.workflow.enableExternalPackageForDefaultCompiler }}"
|
|
- "--enable-external-package-watch-for-default-compiler={{ .Values.workflow.enableExternalPackageWatchForDefaultCompiler }}"
|
|
- "--feature-gates=EnableSuspendOnFailure={{- .Values.workflow.enableSuspendOnFailure | toString -}}"
|
|
- "--feature-gates=GzipResourceTracker={{- .Values.featureGates.gzipResourceTracker | toString -}}"
|
|
- "--feature-gates=AuthenticateApplication={{- .Values.authentication.enabled | toString -}}"
|
|
- "--feature-gates=ZstdResourceTracker={{- .Values.featureGates.zstdResourceTracker | toString -}}"
|
|
- "--feature-gates=ApplyOnce={{- .Values.featureGates.applyOnce | toString -}}"
|
|
- "--feature-gates=MultiStageComponentApply= {{- .Values.featureGates.multiStageComponentApply | toString -}}"
|
|
- "--feature-gates=GzipApplicationRevision={{- .Values.featureGates.gzipApplicationRevision | toString -}}"
|
|
- "--feature-gates=ZstdApplicationRevision={{- .Values.featureGates.zstdApplicationRevision | toString -}}"
|
|
- "--feature-gates=PreDispatchDryRun={{- .Values.featureGates.preDispatchDryRun | toString -}}"
|
|
- "--feature-gates=DisableBootstrapClusterInfo={{- .Values.featureGates.disableBootstrapClusterInfo | toString -}}"
|
|
- "--feature-gates=InformerCacheFilterUnnecessaryFields={{- .Values.featureGates.informerCacheFilterUnnecessaryFields | toString -}}"
|
|
- "--feature-gates=SharedDefinitionStorageForApplicationRevision={{- .Values.featureGates.sharedDefinitionStorageForApplicationRevision | toString -}}"
|
|
- "--feature-gates=DisableWorkflowContextConfigMapCache={{- .Values.featureGates.disableWorkflowContextConfigMapCache | toString -}}"
|
|
- "--feature-gates=EnableCueValidation={{- .Values.featureGates.enableCueValidation | toString -}}"
|
|
- "--feature-gates=EnableApplicationStatusMetrics={{- .Values.featureGates.enableApplicationStatusMetrics | toString -}}"
|
|
- "--feature-gates=ValidateResourcesExist={{- .Values.featureGates.validateResourcesExist | toString -}}"
|
|
- "--feature-gates=ValidateDefinitionPermissions={{ .Values.authorization.definitionValidationEnabled | toString -}}"
|
|
{{ if .Values.authentication.enabled }}
|
|
{{ if .Values.authentication.withUser }}
|
|
- "--authentication-with-user"
|
|
{{ end }}
|
|
- "--authentication-default-user={{ .Values.authentication.defaultUser }}"
|
|
- "--authentication-group-pattern={{ .Values.authentication.groupPattern }}"
|
|
{{ end }}
|
|
{{ if .Values.sharding.enabled }}
|
|
- "--enable-sharding"
|
|
- "--schedulable-shards={{ .Values.sharding.schedulableShards }}"
|
|
- "--feature-gates=ValidateComponentWhenSharding={{- .Values.featureGates.validateComponentWhenSharding | toString -}}"
|
|
- "--feature-gates=DisableWebhookAutoSchedule={{- .Values.featureGates.disableWebhookAutoSchedule | toString -}}"
|
|
{{ end }}
|
|
- "--dev-logs={{ .Values.devLogs }}"
|
|
image: {{ .Values.imageRegistry }}{{ .Values.image.repository }}:{{ .Values.image.tag }}
|
|
imagePullPolicy: {{ quote .Values.image.pullPolicy }}
|
|
resources:
|
|
{{- toYaml .Values.resources | nindent 12 }}
|
|
{{ if .Values.admissionWebhooks.enabled }}
|
|
ports:
|
|
- containerPort: {{ .Values.webhookService.port }}
|
|
name: webhook-server
|
|
protocol: TCP
|
|
- containerPort: {{ .Values.healthCheck.port }}
|
|
name: healthz
|
|
protocol: TCP
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /readyz
|
|
port: healthz
|
|
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
|
|
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
|
|
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
|
|
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
|
|
successThreshold: {{ .Values.readinessProbe.successThreshold }}
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /healthz
|
|
port: healthz
|
|
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
|
|
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
|
|
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
|
|
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
|
|
successThreshold: {{ .Values.livenessProbe.successThreshold }}
|
|
volumeMounts:
|
|
- mountPath: {{ .Values.admissionWebhooks.certificate.mountPath }}
|
|
name: tls-cert-vol
|
|
readOnly: true
|
|
{{ if and .Values.multicluster.enabled .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.direct }}
|
|
- mountPath: /cluster-gateway-tls-cert
|
|
name: tls-cert-vol-cg
|
|
readOnly: true
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ if .Values.admissionWebhooks.enabled }}
|
|
volumes:
|
|
- name: tls-cert-vol
|
|
secret:
|
|
defaultMode: 420
|
|
secretName: {{ template "kubevela.fullname" . }}-admission
|
|
{{ if and .Values.multicluster.enabled .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.direct }}
|
|
- name: tls-cert-vol-cg
|
|
secret:
|
|
defaultMode: 420
|
|
secretName: {{ template "kubevela.fullname" . }}-cluster-gateway-tls-v2
|
|
{{ end }}
|
|
{{ end }}
|
|
{{- with .Values.nodeSelector }}
|
|
nodeSelector:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
affinity:
|
|
{{ if .Values.affinity }}
|
|
{{- toYaml .Values.affinity | nindent 8 }}
|
|
{{ else }}
|
|
podAntiAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- podAffinityTerm:
|
|
labelSelector:
|
|
matchLabels:
|
|
{{- include "kubevela.selectorLabels" . | nindent 20 }}
|
|
topologyKey: kubernetes.io/hostname
|
|
weight: 100
|
|
{{ end }}
|
|
{{- with .Values.tolerations }}
|
|
tolerations:
|
|
{{- toYaml . | nindent 8 }}
|
|
{{- end }}
|
|
|
|
|
|
{{- if and (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") .Values.core.metrics.enabled }}
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}-metrics
|
|
namespace: {{ .Release.Namespace }}
|
|
labels:
|
|
component: vela-core-controller
|
|
spec:
|
|
ports:
|
|
- name: http
|
|
protocol: TCP
|
|
port: 9443
|
|
targetPort: 9443
|
|
- name: metrics
|
|
protocol: TCP
|
|
port: 8080
|
|
targetPort: 8080
|
|
selector:
|
|
{{- include "kubevela.selectorLabels" . | nindent 4 }}
|
|
{{ if .Values.sharding.enabled }}
|
|
controller.core.oam.dev/shard-id: master
|
|
{{ end }}
|
|
{{- end -}}
|
|
|
|
{{- if and (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") .Values.core.metrics.enabled .Values.core.metrics.serviceMonitor.enabled }}
|
|
---
|
|
apiVersion: monitoring.coreos.com/v1
|
|
kind: ServiceMonitor
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}-metrics
|
|
namespace: {{ .Release.Namespace }}
|
|
labels:
|
|
{{- with .Values.core.metrics.serviceMonitor.additionalLabels }}
|
|
{{- toYaml . | nindent 4 }}
|
|
{{- end }}
|
|
spec:
|
|
endpoints:
|
|
- honorLabels: true
|
|
interval: 10s
|
|
path: /metrics
|
|
port: metrics
|
|
scheme: http
|
|
namespaceSelector:
|
|
matchNames:
|
|
- {{ .Release.Namespace }}
|
|
selector:
|
|
matchLabels:
|
|
component: vela-core-controller
|
|
{{- end -}}
|
|
|
|
{{- if and (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") .Values.multicluster.metrics.enabled .Values.multicluster.clusterGateway.serviceMonitor.enabled }}
|
|
---
|
|
apiVersion: monitoring.coreos.com/v1
|
|
kind: ServiceMonitor
|
|
metadata:
|
|
name: {{ include "kubevela.fullname" . }}-cluster-gateway-metrics
|
|
namespace: {{ .Release.Namespace }}
|
|
labels:
|
|
{{- with .Values.multicluster.clusterGateway.serviceMonitor.additionalLabels }}
|
|
{{- toYaml . | nindent 4 }}
|
|
{{- end }}
|
|
spec:
|
|
endpoints:
|
|
- honorLabels: true
|
|
interval: 10s
|
|
path: /metrics
|
|
port: default
|
|
scheme: http
|
|
namespaceSelector:
|
|
matchNames:
|
|
- {{ .Release.Namespace }}
|
|
selector:
|
|
matchLabels:
|
|
{{- include "kubevela-cluster-gateway.selectorLabels" . | nindent 4 }}
|
|
{{- end -}}
|