diff --git a/charts/vcluster/.helmignore b/charts/vcluster/.helmignore new file mode 100644 index 000000000..f0c13194 --- /dev/null +++ b/charts/vcluster/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/vcluster/Chart.yaml b/charts/vcluster/Chart.yaml new file mode 100644 index 000000000..90af72aa --- /dev/null +++ b/charts/vcluster/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +description: vcluster - Virtual Kubernetes Clusters +home: https://vcluster.com +icon: https://static.loft.sh/branding/logos/vcluster/vertical/vcluster_vertical.svg +keywords: + - developer + - development + - sharing + - share + - multi-tenancy + - tenancy + - cluster + - space + - namespace + - vcluster + - vclusters +maintainers: + - name: LoftLabs +name: vcluster +sources: + - https://github.com/loft-sh/vcluster +type: application +version: 0.15.2 +appVersion: 0.15.2 diff --git a/charts/vcluster/README.md b/charts/vcluster/README.md new file mode 100644 index 000000000..88c907fa --- /dev/null +++ b/charts/vcluster/README.md @@ -0,0 +1,60 @@ +## vcluster + +### **[GitHub](https://github.com/loft-sh/vcluster)** • **[Website](https://www.vcluster.com)** • **[Quickstart](https://www.vcluster.com/docs/getting-started/setup)** • **[Documentation](https://www.vcluster.com/docs/what-are-virtual-clusters)** • **[Blog](https://loft.sh/blog)** • **[Twitter](https://twitter.com/loft_sh)** • **[Slack](https://slack.loft.sh/)** + +Create fully functional virtual Kubernetes clusters - Each vcluster runs inside a namespace of the underlying k8s cluster. It's cheaper than creating separate full-blown clusters and it offers better multi-tenancy and isolation than regular namespaces. + +## Prerequisites +- Kubernetes 1.18+ +- Helm 3+ + +## Get Helm Repository Info + +``` +helm repo add loft-sh https://charts.loft.sh +helm repo update +``` +See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation. + +## Install Helm Chart + +``` +helm upgrade [RELEASE_NAME] loft-sh/vcluster -n [RELEASE_NAMESPACE] --create-namespace --install +``` + +See [vcluster docs](https://vcluster.com/docs) for configuration options. + +See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation. + +## Connect to the vcluster + +In order to connect to the installed vcluster, please install [vcluster cli](https://www.vcluster.com/docs/getting-started/setup) and run: +``` +vcluster connect [RELEASE_NAME] -n [RELEASE_NAMESPACE] +``` + +## Uninstall Helm Chart + +``` +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation. + +### Why Virtual Kubernetes Clusters? + +- **Cluster Scoped Resources**: much more powerful than simple namespaces (virtual clusters allow users to use CRDs, namespaces, cluster roles etc.) +- **Ease of Use**: usable in any Kubernetes cluster and created in seconds either via a single command or [cluster-api](https://github.com/loft-sh/cluster-api-provider-vcluster) +- **Cost Efficient**: much cheaper and efficient than "real" clusters (single pod and shared resources just like for namespaces) +- **Lightweight**: built upon the ultra-fast k3s distribution with minimal overhead per virtual cluster (other distributions work as well) +- **Strict isolation**: complete separate Kubernetes control plane and access point for each vcluster while still being able to share certain services of the underlying host cluster +- **Cluster Wide Permissions**: allow users to install apps which require cluster-wide permissions while being limited to actually just one namespace within the host cluster +- **Great for Testing**: allow you to test different Kubernetes versions inside a single host cluster which may have a different version than the virtual clusters + +Learn more on [www.vcluster.com](https://vcluster.com). + +![vcluster Intro](https://github.com/loft-sh/vcluster/raw/main/docs/static/media/vcluster-comparison.png) + +Learn more in the [documentation](https://vcluster.com/docs/what-are-virtual-clusters). diff --git a/charts/vcluster/templates/NOTES.txt b/charts/vcluster/templates/NOTES.txt new file mode 100644 index 000000000..6cf960d6 --- /dev/null +++ b/charts/vcluster/templates/NOTES.txt @@ -0,0 +1,10 @@ +Thank you for installing vcluster. + +Your vcluster is named {{ .Release.Name }} in namespace {{ .Release.Namespace }}. + +To connect to the vcluster, use vcluster CLI (https://www.vcluster.com/docs/getting-started/setup): + $ vcluster connect {{ .Release.Name }} -n {{ .Release.Namespace }} + $ vcluster connect {{ .Release.Name }} -n {{ .Release.Namespace }} -- kubectl get ns + + +For more information, please take a look at the vcluster docs at https://www.vcluster.com/docs diff --git a/charts/vcluster/templates/_helpers.tpl b/charts/vcluster/templates/_helpers.tpl new file mode 100644 index 000000000..2bda0207 --- /dev/null +++ b/charts/vcluster/templates/_helpers.tpl @@ -0,0 +1,168 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} + +{{/* +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 "vcluster.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Whether the ingressclasses syncer should be enabled +*/}} +{{- define "vcluster.syncIngressclassesEnabled" -}} +{{- if or (.Values.sync.ingressclasses).enabled (and .Values.sync.ingresses.enabled (not .Values.sync.ingressclasses)) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Whether to create a cluster role or not +*/}} +{{- define "vcluster.createClusterRole" -}} +{{- if or (not (empty (include "vcluster.serviceMapping.fromHost" . ))) (not (empty (include "vcluster.plugin.clusterRoleExtraRules" . ))) (not (empty (include "vcluster.generic.clusterRoleExtraRules" . ))) .Values.rbac.clusterRole.create .Values.sync.hoststorageclasses.enabled (index ((index .Values.sync "legacy-storageclasses") | default (dict "enabled" false)) "enabled") (include "vcluster.syncIngressclassesEnabled" . ) .Values.sync.nodes.enabled .Values.sync.persistentvolumes.enabled .Values.sync.storageclasses.enabled .Values.sync.priorityclasses.enabled .Values.sync.volumesnapshots.enabled .Values.proxy.metricsServer.nodes.enabled .Values.multiNamespaceMode.enabled -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{- define "vcluster.clusterRoleName" -}} +{{- printf "vc-%s-v-%s" .Release.Name .Release.Namespace | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "vcluster.clusterRoleNameMultinamespace" -}} +{{- printf "vc-mn-%s-v-%s" .Release.Name .Release.Namespace | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vcluster.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Get +*/}} +{{- $}} +{{- define "vcluster.admin.accessKey" -}} +{{- now | unixEpoch | toString | trunc 8 | sha256sum -}} +{{- end -}} + +{{/* +Syncer flags for enabling/disabling controllers +Prints only the flags that modify the defaults: +- when default controller has enabled: false => `- "--sync=-controller` +- when non-default controller has enabled: true => `- "--sync=controller` +*/}} +{{- define "vcluster.syncer.syncArgs" -}} +{{- $defaultEnabled := list "services" "configmaps" "secrets" "endpoints" "pods" "events" "persistentvolumeclaims" "fake-nodes" "fake-persistentvolumes" -}} +{{- if and (hasKey .Values.sync.nodes "enableScheduler") .Values.sync.nodes.enableScheduler -}} + {{- $defaultEnabled = concat $defaultEnabled (list "csinodes" "csidrivers" "csistoragecapacities" ) -}} +{{- end -}} +{{- range $key, $val := .Values.sync }} +{{- if and (has $key $defaultEnabled) (not $val.enabled) }} +- --sync=-{{ $key }} +{{- else if and (not (has $key $defaultEnabled)) ($val.enabled)}} +{{- if eq $key "legacy-storageclasses" }} +- --sync=hoststorageclasses +{{- else }} +- --sync={{ $key }} +{{- end -}} +{{- end -}} +{{- end -}} +{{- if not (include "vcluster.syncIngressclassesEnabled" . ) }} +- --sync=-ingressclasses +{{- end -}} +{{- end -}} + +{{/* +Cluster role rules defined by plugins +*/}} +{{- define "vcluster.plugin.clusterRoleExtraRules" -}} +{{- range $key, $container := .Values.plugin }} +{{- if $container.rbac }} +{{- if $container.rbac.clusterRole }} +{{- if $container.rbac.clusterRole.extraRules }} +{{- range $ruleIndex, $rule := $container.rbac.clusterRole.extraRules }} +- {{ toJson $rule }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Cluster role rules defined in generic syncer +*/}} +{{- define "vcluster.generic.clusterRoleExtraRules" -}} +{{- if .Values.sync.generic.clusterRole }} +{{- if .Values.sync.generic.clusterRole.extraRules}} +{{- range $ruleIndex, $rule := .Values.sync.generic.clusterRole.extraRules }} +- {{ toJson $rule }} +{{- end }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Role rules defined by plugins +*/}} +{{- define "vcluster.plugin.roleExtraRules" -}} +{{- range $key, $container := .Values.plugin }} +{{- if $container.rbac }} +{{- if $container.rbac.role }} +{{- if $container.rbac.role.extraRules }} +{{- range $ruleIndex, $rule := $container.rbac.role.extraRules }} +- {{ toJson $rule }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Role rules defined in generic syncer +*/}} +{{- define "vcluster.generic.roleExtraRules" -}} +{{- if .Values.sync.generic.role }} +{{- if .Values.sync.generic.role.extraRules}} +{{- range $ruleIndex, $rule := .Values.sync.generic.role.extraRules }} +- {{ toJson $rule }} +{{- end }} +{{- end }} +{{- end }} +{{- end -}} + +{{/* +Virtual cluster service mapping +*/}} +{{- define "vcluster.serviceMapping.fromVirtual" -}} +{{- range $key, $value := .Values.mapServices.fromVirtual }} +- '--map-virtual-service={{ $value.from }}={{ $value.to }}' +{{- end }} +{{- end -}} + +{{/* +Host cluster service mapping +*/}} +{{- define "vcluster.serviceMapping.fromHost" -}} +{{- range $key, $value := .Values.mapServices.fromHost }} +- '--map-host-service={{ $value.from }}={{ $value.to }}' +{{- end }} +{{- end -}} diff --git a/charts/vcluster/templates/_k3s_helpers.tpl b/charts/vcluster/templates/_k3s_helpers.tpl new file mode 100644 index 000000000..c557048f --- /dev/null +++ b/charts/vcluster/templates/_k3s_helpers.tpl @@ -0,0 +1,29 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Returns the desired workload kind (StatefulSet / Deployment) for k3s +*/}} +{{- define "vcluster.k3s.workloadKind" -}} +{{- ternary "Deployment" "StatefulSet" (.Values.enableHA) -}} +{{- end -}} + +{{/* +Returns the name of the secret containing the k3s tokens. +*/}} +{{- define "vcluster.k3s.tokenSecretName" -}} +{{- with .Values.serverToken.secretKeyRef.name -}} +{{- . -}} +{{- else -}} +{{- printf "%s-tokens" .Release.Name -}} +{{- end -}} +{{- end -}} + +{{/* +Returns the secret key name containing the k3s server token. +*/}} +{{- define "vcluster.k3s.serverTokenKey" -}} +{{- with .Values.serverToken.secretKeyRef.key -}} +{{- . -}} +{{- else -}} +{{- "server-token" -}} +{{- end -}} +{{- end -}} diff --git a/charts/vcluster/templates/coredns.yaml b/charts/vcluster/templates/coredns.yaml new file mode 100644 index 000000000..1408b870 --- /dev/null +++ b/charts/vcluster/templates/coredns.yaml @@ -0,0 +1,276 @@ +{{- if not .Values.headless }} +{{- if .Values.coredns.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-coredns + namespace: {{ .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +data: +{{- if .Values.coredns.manifests }} + coredns.yaml: |- +{{ .Values.coredns.manifests | indent 4 }} +{{- else }} + coredns.yaml: |- + apiVersion: v1 + kind: ServiceAccount + metadata: + name: coredns + namespace: kube-system + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + labels: + kubernetes.io/bootstrapping: rbac-defaults + name: system:coredns + rules: + - apiGroups: + - "" + resources: + - endpoints + - services + - pods + - namespaces + verbs: + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + --- + apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + annotations: + rbac.authorization.kubernetes.io/autoupdate: "true" + labels: + kubernetes.io/bootstrapping: rbac-defaults + name: system:coredns + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:coredns + subjects: + - kind: ServiceAccount + name: coredns + namespace: kube-system + --- + apiVersion: v1 + kind: ConfigMap + metadata: + name: coredns + namespace: kube-system + data: + Corefile: | + {{- if .Values.coredns.config }} +{{ .Values.coredns.config | indent 8 }} + {{- else }} + .:1053 { + {{`{{.LOG_IN_DEBUG}}`}} + errors + health + ready + rewrite name regex .*\.nodes\.vcluster\.com kubernetes.default.svc.cluster.local + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + {{- if .Values.fallbackHostDns }} + fallthrough cluster.local in-addr.arpa ip6.arpa + {{- else }} + fallthrough in-addr.arpa ip6.arpa + {{- end }} + } + hosts /etc/coredns/NodeHosts { + ttl 60 + reload 15s + fallthrough + } + prometheus :9153 + {{- if .Values.fallbackHostDns }} + forward . {{`{{.HOST_CLUSTER_DNS}}`}} + {{- else if and .Values.isolation.enabled .Values.isolation.networkPolicy.enabled }} + forward . /etc/resolv.conf 8.8.8.8 { + policy sequential + } + {{- else }} + forward . /etc/resolv.conf + {{- end }} + cache 30 + loop + reload + loadbalance + } + + import /etc/coredns/custom/*.server + {{- end }} + NodeHosts: "" + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: coredns + namespace: kube-system + labels: + k8s-app: kube-dns + kubernetes.io/name: "CoreDNS" + spec: + replicas: {{ .Values.coredns.replicas }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + selector: + matchLabels: + k8s-app: kube-dns + template: + metadata: + {{- if .Values.coredns.podAnnotations }} + annotations: +{{ toYaml .Values.coredns.podAnnotations | indent 12 }} + {{- end }} + labels: + k8s-app: kube-dns + {{- range $k, $v := .Values.coredns.podLabels }} + {{ $k }}: {{ $v | quote }} + {{- end }} + spec: + priorityClassName: "system-cluster-critical" + serviceAccountName: coredns + nodeSelector: + kubernetes.io/os: linux + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + k8s-app: kube-dns + {{- if .Values.isolation.enabled }} + securityContext: + seccompProfile: + type: RuntimeDefault + {{- end }} + containers: + - name: coredns + {{- if .Values.coredns.image }} + image: {{ .Values.coredns.image }} + {{- else }} + image: {{`{{.IMAGE}}`}} + {{- end }} + imagePullPolicy: IfNotPresent + resources: +{{ toYaml .Values.coredns.resources | indent 16}} + args: [ "-conf", "/etc/coredns/Corefile" ] + volumeMounts: + - name: config-volume + mountPath: /etc/coredns + readOnly: true + - name: custom-config-volume + mountPath: /etc/coredns/custom + readOnly: true + ports: + - containerPort: 1053 + name: dns + protocol: UDP + - containerPort: 1053 + name: dns-tcp + protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP + securityContext: + runAsNonRoot: true + runAsUser: {{`{{.RUN_AS_USER}}`}} + runAsGroup: {{`{{.RUN_AS_GROUP}}`}} + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + livenessProbe: + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /ready + port: 8181 + scheme: HTTP + initialDelaySeconds: 0 + periodSeconds: 2 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + dnsPolicy: Default + volumes: + - name: config-volume + configMap: + name: coredns + items: + - key: Corefile + path: Corefile + - key: NodeHosts + path: NodeHosts + - name: custom-config-volume + configMap: + name: coredns-custom + optional: true + --- + apiVersion: v1 + kind: Service + metadata: + name: kube-dns + namespace: kube-system + annotations: + prometheus.io/port: "9153" + prometheus.io/scrape: "true" + {{- if .Values.coredns.service.annotations }} +{{ toYaml .Values.coredns.service.annotations | indent 8 }} + {{- end }} + labels: + k8s-app: kube-dns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: "CoreDNS" + spec: + selector: + k8s-app: kube-dns + type: {{ .Values.coredns.service.type }} + {{- if (eq (.Values.coredns.service.type) "LoadBalancer") }} + {{- if .Values.coredns.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.coredns.service.externalTrafficPolicy }} + {{- end }} + {{- if .Values.coredns.service.externalIPs }} + externalIPs: + {{- range $f := .Values.coredns.service.externalIPs }} + - {{ $f }} + {{- end }} + {{- end }} + {{- end }} + ports: + - name: dns + port: 53 + targetPort: 1053 + protocol: UDP + - name: dns-tcp + port: 53 + targetPort: 1053 + protocol: TCP + - name: metrics + port: 9153 + protocol: TCP +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/vcluster/templates/daemonset-hostpath-mapper.yaml b/charts/vcluster/templates/daemonset-hostpath-mapper.yaml new file mode 100644 index 000000000..78b7a89d --- /dev/null +++ b/charts/vcluster/templates/daemonset-hostpath-mapper.yaml @@ -0,0 +1,106 @@ +{{- if .Values.hostpathMapper.enabled }} +apiVersion: apps/v1 +{{- if not .Values.hostpathMapper.dev }} +kind: DaemonSet +{{- else }} +kind: Deployment +{{- end }} +metadata: + name: {{ .Release.Name }}-hostpath-mapper + namespace: {{ .Release.Namespace }} + labels: + app: vcluster-hostpath-mapper + component: hostpath-mapper + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{- if .Values.labels }} +{{ toYaml .Values.labels | indent 4 }} +{{- end }} + {{- if .Values.annotations }} + annotations: +{{ toYaml .Values.annotations | indent 4 }} + {{- end }} +spec: + {{- if .Values.hostpathMapper.dev }} + replicas: 1 + {{- end }} + selector: + matchLabels: + app: vcluster-hostpath-mapper + release: {{ .Release.Name }} + component: hostpath-mapper + template: + metadata: + labels: + app: vcluster-hostpath-mapper + release: {{ .Release.Name }} + component: hostpath-mapper + spec: + {{- if .Values.serviceAccount.name }} + serviceAccountName: {{ .Values.serviceAccount.name }} + {{- else }} + serviceAccountName: vc-{{ .Release.Name }} + {{- end }} + containers: + - name: hostpath-mapper + {{- if .Values.hostpathMapper.image }} + image: "{{ .Values.defaultImageRegistry }}{{ .Values.hostpathMapper.image }}" + {{- else }} + image: "{{ .Values.defaultImageRegistry }}ghcr.io/loft-sh/vcluster:{{ .Chart.Version }}" + {{- end }} + {{- if .Values.syncer.workingDir }} + workingDir: {{ .Values.syncer.workingDir }} + {{- end }} + command: + - /vcluster + env: + - name: VCLUSTER_HOSTPATH_MAPPER_CURRENT_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + args: + - maphostpaths + - --name={{ .Release.Name }} + - --target-namespace={{ .Release.Namespace }} + volumeMounts: + - name: logs + mountPath: /var/log + - name: virtual-logs + mountPath: /tmp/vcluster/{{ .Release.Namespace }}/{{ .Release.Name }}/log + - name: pod-logs + mountPath: /var/log/pods + - name: virtual-pod-logs + mountPath: /tmp/vcluster/{{ .Release.Namespace }}/{{ .Release.Name }}/log/pods + - name: kubelet-pods + mountPath: /var/vcluster/physical/kubelet/pods + - name: virtual-kubelet-pods + mountPath: /tmp/vcluster/{{ .Release.Namespace }}/{{ .Release.Name }}/kubelet/pods + - name: kubeconfig + mountPath: /data/server/tls + resources: +{{ toYaml .Values.hostpathMapper.resources | indent 10 }} + volumes: + - name: logs + hostPath: + path: /var/log + - name: virtual-logs + hostPath: + path: /tmp/vcluster/{{ .Release.Namespace }}/{{ .Release.Name }}/log + - name: pod-logs + hostPath: + path: /var/log/pods + - name: kubelet-pods + hostPath: + path: /var/lib/kubelet/pods + - name: virtual-pod-logs + hostPath: + path: /tmp/vcluster/{{ .Release.Namespace }}/{{ .Release.Name }}/log/pods + - name: virtual-kubelet-pods + hostPath: + path: /tmp/vcluster/{{ .Release.Namespace }}/{{ .Release.Name }}/kubelet/pods + - name: kubeconfig + secret: + secretName: vc-{{ .Release.Name }} +{{ end }} + diff --git a/charts/vcluster/templates/ingress.yaml b/charts/vcluster/templates/ingress.yaml new file mode 100644 index 000000000..0587ecda --- /dev/null +++ b/charts/vcluster/templates/ingress.yaml @@ -0,0 +1,27 @@ +{{- if .Values.ingress.enabled }} +apiVersion: {{ .Values.ingress.apiVersion }} +kind: Ingress +metadata: +{{- $annotations := merge .Values.ingress.annotations .Values.globalAnnotations }} + {{- if $annotations }} + annotations: + {{- toYaml $annotations | nindent 4 }} + {{- end }} + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} +spec: + {{- if .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- end }} + rules: + - host: {{ .Values.ingress.host | quote }} + http: + paths: + - backend: + service: + name: {{ .Release.Name }} + port: + name: https + path: / + pathType: {{ .Values.ingress.pathType }} +{{- end }} diff --git a/charts/vcluster/templates/init-configmap.yaml b/charts/vcluster/templates/init-configmap.yaml new file mode 100644 index 000000000..bf0f2ed2 --- /dev/null +++ b/charts/vcluster/templates/init-configmap.yaml @@ -0,0 +1,52 @@ +{{- if .Values.init.manifests }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-init-manifests + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +data: + manifests: |- + {{ .Values.init.manifests | nindent 4 | trim }} + {{ tpl .Values.init.manifestsTemplate $ | nindent 4 | trim }} + {{- if .Values.init.helm }} + charts: |- + {{- range .Values.init.helm }} + {{- /* only render this chart entry if either of chart or bundle is defined */}} + {{- if or .chart .bundle }} + - name: {{ .chart.name }} + repo: {{ .chart.repo }} + version: {{ .chart.version }} + {{- if .chart.username }} + username: {{ .chart.username }} + {{- end }} + {{- if .chart.password }} + password: {{ .chart.password }} + {{- end }} + {{- else if .bundle }} + bundle: {{ .bundle }} + {{- end }} + {{- if .insecure }} + insecure: true + {{- end}} + {{- if or .values .valuesTemplate }} + values: |- + {{ (.values | default "") | nindent 8 | trim }} + {{ tpl (.valuesTemplate | default "") $ | nindent 8 | trim }} + {{- end}} + {{- if .release }} + timeout: {{ .timeout | default "120s" | quote }} + releaseName: {{ .release.name }} + releaseNamespace: {{ .release.namespace }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/vcluster/templates/limitrange.yaml b/charts/vcluster/templates/limitrange.yaml new file mode 100644 index 000000000..ed219d06 --- /dev/null +++ b/charts/vcluster/templates/limitrange.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.isolation.enabled .Values.isolation.limitRange.enabled }} +apiVersion: v1 +kind: LimitRange +metadata: + name: {{ .Release.Name }}-limit-range + namespace: {{ .Values.isolation.namespace | default .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +spec: + limits: + - default: + {{- range $key, $val := .Values.isolation.limitRange.default }} + {{ $key }}: {{ $val | quote }} + {{- end }} + defaultRequest: + {{- range $key, $val := .Values.isolation.limitRange.defaultRequest }} + {{ $key }}: {{ $val | quote }} + {{- end }} + type: Container +{{- end }} diff --git a/charts/vcluster/templates/networkpolicy.yaml b/charts/vcluster/templates/networkpolicy.yaml new file mode 100644 index 000000000..e40d6335 --- /dev/null +++ b/charts/vcluster/templates/networkpolicy.yaml @@ -0,0 +1,74 @@ +{{- if and .Values.isolation.enabled .Values.isolation.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-workloads + namespace: {{ .Values.isolation.namespace | default .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + vcluster.loft.sh/managed-by: {{ .Release.Name }} + egress: + # Allows outgoing connections to the vcluster control plane + - ports: + - port: 443 + - port: 8443 + to: + - podSelector: + matchLabels: + release: {{ .Release.Name }} + # Allows outgoing connections to DNS server + - ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + # Allows outgoing connections to the internet or + # other vcluster workloads + - to: + - podSelector: + matchLabels: + vcluster.loft.sh/managed-by: {{ .Release.Name }} + - ipBlock: + cidr: {{ .Values.isolation.networkPolicy.outgoingConnections.ipBlock.cidr }} + except: + {{- range .Values.isolation.networkPolicy.outgoingConnections.ipBlock.except }} + - {{ . }} + {{- end }} + policyTypes: + - Egress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-control-plane + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + release: {{ .Release.Name }} + egress: + # Allows outgoing connections to all pods with + # port 443, 8443 or 6443. This is needed for host Kubernetes + # access + - ports: + - port: 443 + - port: 8443 + - port: 6443 + # Allows outgoing connections to all vcluster workloads + # or kube system dns server + - to: + - podSelector: {} + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: 'kube-system' + podSelector: + matchLabels: + k8s-app: kube-dns + policyTypes: + - Egress +{{- end }} diff --git a/charts/vcluster/templates/pdb.yaml b/charts/vcluster/templates/pdb.yaml new file mode 100644 index 000000000..ad7da6a2 --- /dev/null +++ b/charts/vcluster/templates/pdb.yaml @@ -0,0 +1,27 @@ +{{- if and (.Values.enableHA) (.Values.podDisruptionBudget.enabled) (gt (int .Values.replicas) 1) -}} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +spec: + {{- with .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ . }} + {{- end }} + {{- with .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ . }} + {{- end }} + selector: + matchLabels: + app: vcluster + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/vcluster/templates/pre-install-hook-secret.yaml b/charts/vcluster/templates/pre-install-hook-secret.yaml new file mode 100644 index 000000000..af9d309f --- /dev/null +++ b/charts/vcluster/templates/pre-install-hook-secret.yaml @@ -0,0 +1,19 @@ +{{- if (and (.Values.enableHA) (not .Values.serverToken.secretKeyRef)) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "vcluster.k3s.tokenSecretName" . | quote }} + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: pre-install + helm.sh/hook-weight: "3" + # helm.sh/hook-delete-policy: before-hook-creation # Default value + helm.sh/resource-policy: keep +type: Opaque +data: +{{- if .Values.serverToken.value }} + server-token: {{ .Values.serverToken.value | b64enc | quote }} +{{- else }} + server-token: {{ (randAlphaNum 32) | b64enc | quote }} +{{- end }} +{{- end }} diff --git a/charts/vcluster/templates/rbac/clusterrole.yaml b/charts/vcluster/templates/rbac/clusterrole.yaml new file mode 100644 index 000000000..d4212fa6 --- /dev/null +++ b/charts/vcluster/templates/rbac/clusterrole.yaml @@ -0,0 +1,92 @@ +{{- if (include "vcluster.createClusterRole" . ) -}} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "vcluster.clusterRoleName" . }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +rules: + {{- if or .Values.sync.nodes.enabled .Values.rbac.clusterRole.create }} + - apiGroups: [""] + resources: ["nodes", "nodes/status"] + verbs: ["get", "watch", "list"] + - apiGroups: [""] + resources: [ "pods", "nodes/metrics", "nodes/stats"] + verbs: ["get", "watch", "list"] + {{- end }} + {{- if and (or .Values.sync.nodes.enabled .Values.rbac.clusterRole.create) (or (not .Values.isolation.enabled) (and .Values.isolation.nodeProxyPermission.enabled .Values.isolation.enabled)) }} + - apiGroups: [""] + resources: ["nodes/proxy"] + verbs: ["get", "watch", "list"] + {{- end }} + {{- if or (and .Values.sync.nodes.enabled .Values.sync.nodes.syncNodeChanges) .Values.rbac.clusterRole.create }} + - apiGroups: [""] + resources: ["nodes", "nodes/status"] + verbs: ["update", "patch"] + {{- end }} + {{- if or .Values.sync.persistentvolumes.enabled .Values.rbac.clusterRole.create }} + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["create", "delete", "patch", "update", "get", "watch", "list"] + {{- end }} + {{- if .Values.sync.nodes.enableScheduler }} + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses","csinodes","csidrivers","csistoragecapacities"] + verbs: ["get", "watch", "list"] + {{- end }} + {{- if (include "vcluster.syncIngressclassesEnabled" . ) }} + - apiGroups: ["networking.k8s.io"] + resources: ["ingressclasses"] + verbs: ["get", "watch", "list"] + {{- end }} + {{- if or .Values.sync.storageclasses.enabled .Values.rbac.clusterRole.create }} + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["create", "delete", "patch", "update", "get", "watch", "list"] + {{- end }} + {{- if or .Values.sync.hoststorageclasses.enabled (index ((index .Values.sync "legacy-storageclasses") | default (dict "enabled" false)) "enabled") .Values.rbac.clusterRole.create }} + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses"] + verbs: ["get", "watch", "list"] + {{- end }} + {{- if or .Values.sync.priorityclasses.enabled .Values.rbac.clusterRole.create }} + - apiGroups: ["scheduling.k8s.io"] + resources: ["priorityclasses"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if or .Values.sync.volumesnapshots.enabled .Values.rbac.clusterRole.create }} + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotclasses"] + verbs: ["get", "list", "watch"] + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshotcontents"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- include "vcluster.plugin.clusterRoleExtraRules" . | indent 2 }} + {{- include "vcluster.generic.clusterRoleExtraRules" . | indent 2 }} + {{- if (not (empty (include "vcluster.serviceMapping.fromHost" . ))) }} + - apiGroups: [""] + resources: ["services"] + verbs: ["get", "watch", "list"] + {{- end }} + {{- if .Values.multiNamespaceMode.enabled }} + - apiGroups: [""] + resources: ["namespaces"] + verbs: ["create", "delete", "patch", "update", "get", "watch", "list"] + - apiGroups: [""] + resources: ["serviceaccounts"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if .Values.proxy.metricsServer.nodes.enabled }} + - apiGroups: ["metrics.k8s.io"] + resources: ["nodes"] + verbs: ["get", "list"] + {{- end }} +{{- end }} diff --git a/charts/vcluster/templates/rbac/clusterrolebinding.yaml b/charts/vcluster/templates/rbac/clusterrolebinding.yaml new file mode 100644 index 000000000..0a5645d2 --- /dev/null +++ b/charts/vcluster/templates/rbac/clusterrolebinding.yaml @@ -0,0 +1,27 @@ +{{- if (include "vcluster.createClusterRole" . ) -}} +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ template "vcluster.clusterRoleName" . }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +subjects: + - kind: ServiceAccount + {{- if .Values.serviceAccount.name }} + name: {{ .Values.serviceAccount.name }} + {{- else }} + name: vc-{{ .Release.Name }} + {{- end }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ template "vcluster.clusterRoleName" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/vcluster/templates/rbac/role.yaml b/charts/vcluster/templates/rbac/role.yaml new file mode 100644 index 000000000..5e1decf0 --- /dev/null +++ b/charts/vcluster/templates/rbac/role.yaml @@ -0,0 +1,95 @@ +{{- if .Values.rbac.role.create }} +{{- if .Values.multiNamespaceMode.enabled }} +kind: ClusterRole +{{- else -}} +kind: Role +{{- end }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: +{{- if .Values.multiNamespaceMode.enabled }} + name: {{ template "vcluster.clusterRoleNameMultinamespace" . }} +{{- else }} + name: {{ .Release.Name }} +{{- end }} + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +rules: + - apiGroups: [""] + resources: ["configmaps", "secrets", "services", "pods", "pods/attach", "pods/portforward", "pods/exec", "persistentvolumeclaims"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- if or .Values.sync.pods.status .Values.rbac.role.extended }} + - apiGroups: [""] + resources: ["pods/status"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if or .Values.sync.pods.ephemeralContainers .Values.rbac.role.extended }} + {{- if ge (.Capabilities.KubeVersion.Minor | int) 23 }} + - apiGroups: [""] + resources: ["pods/ephemeralcontainers"] + verbs: ["patch", "update"] + {{- end }} + {{- end }} + {{- if or .Values.sync.endpoints.enabled .Values.rbac.role.extended .Values.headless }} + - apiGroups: [""] + resources: ["endpoints"] + verbs: ["create", "delete", "patch", "update"] + {{- end }} + {{- if or .Values.enableHA .Values.rbac.role.extended }} + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + - apiGroups: [""] + resources: ["endpoints", "events", "pods/log"] + verbs: ["get", "list", "watch"] + {{- if or .Values.sync.ingresses.enabled}} + - apiGroups: ["networking.k8s.io"] + resources: ["ingresses"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + - apiGroups: ["apps"] + resources: ["statefulsets", "replicasets", "deployments"] + verbs: ["get", "list", "watch"] + {{- if or .Values.sync.networkpolicies.enabled .Values.rbac.role.extended }} + - apiGroups: ["networking.k8s.io"] + resources: ["networkpolicies"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if or .Values.sync.volumesnapshots.enabled .Values.rbac.role.extended }} + - apiGroups: ["snapshot.storage.k8s.io"] + resources: ["volumesnapshots"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if or .Values.sync.serviceaccounts.enabled .Values.rbac.role.extended }} + - apiGroups: [""] + resources: ["serviceaccounts"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if or .Values.sync.poddisruptionbudgets.enabled .Values.rbac.role.extended }} + - apiGroups: ["policy"] + resources: ["poddisruptionbudgets"] + verbs: ["create", "delete", "patch", "update", "get", "list", "watch"] + {{- end }} + {{- if .Values.openshift.enable }} + {{- if .Values.sync.endpoints.enabled }} + - apiGroups: [""] + resources: ["endpoints/restricted"] + verbs: ["create"] + {{- end }} + {{- end }} + {{- if .Values.proxy.metricsServer.pods.enabled }} + - apiGroups: ["metrics.k8s.io"] + resources: ["pods"] + verbs: ["get", "list"] + {{- end }} + {{- include "vcluster.plugin.roleExtraRules" . | indent 2 }} + {{- include "vcluster.generic.roleExtraRules" . | indent 2 }} +{{- end }} diff --git a/charts/vcluster/templates/rbac/rolebinding.yaml b/charts/vcluster/templates/rbac/rolebinding.yaml new file mode 100644 index 000000000..676e5002 --- /dev/null +++ b/charts/vcluster/templates/rbac/rolebinding.yaml @@ -0,0 +1,41 @@ +{{- if .Values.rbac.role.create }} +{{- if .Values.multiNamespaceMode.enabled }} +kind: ClusterRoleBinding +{{- else -}} +kind: RoleBinding +{{- end }} +apiVersion: rbac.authorization.k8s.io/v1 +metadata: +{{- if .Values.multiNamespaceMode.enabled }} + name: {{ template "vcluster.clusterRoleNameMultinamespace" . }} +{{- else }} + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} +{{- end }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +subjects: + - kind: ServiceAccount + {{- if .Values.serviceAccount.name }} + name: {{ .Values.serviceAccount.name }} + {{- else }} + name: vc-{{ .Release.Name }} + {{- end }} + namespace: {{ .Release.Namespace }} +roleRef: +{{- if .Values.multiNamespaceMode.enabled }} + kind: ClusterRole + name: {{ template "vcluster.clusterRoleNameMultinamespace" . }} +{{- else }} + kind: Role + name: {{ .Release.Name }} +{{- end }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/vcluster/templates/resourcequota.yaml b/charts/vcluster/templates/resourcequota.yaml new file mode 100644 index 000000000..b19d89ff --- /dev/null +++ b/charts/vcluster/templates/resourcequota.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.isolation.enabled .Values.isolation.resourceQuota.enabled }} +apiVersion: v1 +kind: ResourceQuota +metadata: + name: {{ .Release.Name }}-quota + namespace: {{ .Values.isolation.namespace | default .Release.Namespace }} + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +spec: + hard: + {{- range $key, $val := .Values.isolation.resourceQuota.quota }} + {{ $key }}: {{ $val | quote }} + {{- end }} + + {{- if .Values.isolation.resourceQuota.scopeSelector.matchExpressions }} + scopeSelector: + matchExpressions: + {{- toYaml .Values.isolation.resourceQuota.scopeSelector.matchExpressions | nindent 4 }} + {{- end}} + + {{- if .Values.isolation.resourceQuota.scopes }} + scopes: + {{- toYaml .Values.isolation.resourceQuota.scopes | nindent 4 }} + {{- end}} + +{{- end }} diff --git a/charts/vcluster/templates/service.yaml b/charts/vcluster/templates/service.yaml new file mode 100644 index 000000000..78e3ef46 --- /dev/null +++ b/charts/vcluster/templates/service.yaml @@ -0,0 +1,91 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- $annotations := merge .Values.globalAnnotations .Values.syncer.serviceAnnotations }} + {{- if $annotations }} + annotations: +{{ toYaml $annotations | indent 4 }} + {{- end }} +spec: + type: {{ if eq .Values.service.type "LoadBalancer" -}}ClusterIP{{- else }} {{- .Values.service.type }} {{- end }} + ports: + - name: https + port: 443 + {{- if not .Values.headless }} + targetPort: 8443 + {{- end }} + protocol: TCP + - name: kubelet + port: 10250 + {{- if not .Values.headless }} + targetPort: 8443 + {{- end }} + protocol: TCP + {{- if .Values.service.externalIPs }} + externalIPs: + {{- range $f := .Values.service.externalIPs }} + - {{ $f }} + {{- end }} + {{- end }} + {{- if eq .Values.service.type "NodePort" }} + {{- if .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} + {{- end }} + {{- end }} + {{- if not .Values.headless }} + selector: + app: vcluster + release: {{ .Release.Name }} + {{- end }} +--- +{{ if eq .Values.service.type "LoadBalancer" }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-lb + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- $annotations := merge .Values.globalAnnotations .Values.syncer.serviceAnnotations }} + {{- if $annotations }} + annotations: +{{ toYaml $annotations | indent 4 }} + {{- end }} +spec: + type: LoadBalancer + ports: + - name: https + port: 443 + targetPort: 8443 + protocol: TCP + {{- if .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} + {{- end }} + {{- if not .Values.headless }} + selector: + app: vcluster + release: {{ .Release.Name }} + {{- end }} + {{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.service.loadBalancerClass }} + {{- end }} + {{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- range $f := .Values.service.loadBalancerSourceRanges }} + - "{{ $f }}" + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/vcluster/templates/serviceaccount.yaml b/charts/vcluster/templates/serviceaccount.yaml new file mode 100644 index 000000000..50499803 --- /dev/null +++ b/charts/vcluster/templates/serviceaccount.yaml @@ -0,0 +1,20 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vc-{{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- if .Values.globalAnnotations }} + annotations: +{{ toYaml .Values.globalAnnotations | indent 4 }} + {{- end }} +{{- if .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: +{{ toYaml .Values.serviceAccount.imagePullSecrets | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/vcluster/templates/statefulset-service.yaml b/charts/vcluster/templates/statefulset-service.yaml new file mode 100644 index 000000000..a2f481ac --- /dev/null +++ b/charts/vcluster/templates/statefulset-service.yaml @@ -0,0 +1,29 @@ +{{- if not .Values.headless }} +{{- if (eq (include "vcluster.k3s.workloadKind" .) "StatefulSet") }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-headless + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "vcluster.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- $annotations := merge .Values.globalAnnotations .Values.syncer.serviceAnnotations }} + {{- if $annotations }} + annotations: +{{ toYaml $annotations | indent 4 }} + {{- end }} +spec: + ports: + - name: https + port: 443 + targetPort: 8443 + protocol: TCP + clusterIP: None + selector: + app: vcluster + release: "{{ .Release.Name }}" +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/vcluster/templates/statefulset.yaml b/charts/vcluster/templates/statefulset.yaml new file mode 100644 index 000000000..890da611 --- /dev/null +++ b/charts/vcluster/templates/statefulset.yaml @@ -0,0 +1,415 @@ +{{- if not .Values.headless }} +{{- $kind := include "vcluster.k3s.workloadKind" . -}} +apiVersion: apps/v1 +kind: {{ $kind }} +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{- if .Values.labels }} +{{ toYaml .Values.labels | indent 4 }} +{{- end }} +{{- $annotations := merge .Values.annotations .Values.globalAnnotations }} + {{- if $annotations }} + annotations: +{{ toYaml $annotations | indent 4 }} + {{- end }} +spec: +{{- if (eq $kind "StatefulSet") }} + serviceName: {{ .Release.Name }}-headless +{{- end }} + replicas: {{ .Values.replicas }} +{{- if (and (eq $kind "Deployment") (.Values.enableHA)) }} + strategy: + rollingUpdate: + maxSurge: 1 + {{- if (eq (int .Values.replicas) 1) }} + maxUnavailable: 0 + {{- else }} + maxUnavailable: 1 + {{- end }} + type: RollingUpdate +{{- end }} + selector: + matchLabels: + app: vcluster + release: {{ .Release.Name }} + {{- if (eq $kind "StatefulSet") }} + {{- if (hasKey .Values "volumeClaimTemplates") }} + volumeClaimTemplates: +{{ toYaml .Values.volumeClaimTemplates | indent 4 }} + {{- else if .Values.storage.persistence }} + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: [ "ReadWriteOnce" ] + {{- if .Values.storage.className }} + storageClassName: {{ .Values.storage.className }} + {{- end }} + resources: + requests: + storage: {{ .Values.storage.size }} + {{- end }} + {{- end }} + template: + metadata: + {{- if .Values.podAnnotations }} + annotations: +{{ toYaml .Values.podAnnotations | indent 8 }} + {{- end }} + labels: + app: vcluster + release: {{ .Release.Name }} + {{- range $k, $v := .Values.podLabels }} + {{ $k }}: {{ $v | quote }} + {{- end }} + spec: + terminationGracePeriodSeconds: 10 + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + {{- else if .Values.enableHA }} + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + # if possible avoid scheduling more than one pod on one node + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - vcluster + - key: release + operator: In + values: + - {{ .Release.Name }} + topologyKey: "kubernetes.io/hostname" + # if possible avoid scheduling pod onto node that is in the same zone as one or more vcluster pods are running + - weight: 50 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - vcluster + - key: release + operator: In + values: + - {{ .Release.Name }} + topologyKey: topology.kubernetes.io/zone + {{- end }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} + {{- if .Values.serviceAccount.name }} + serviceAccountName: {{ .Values.serviceAccount.name }} + {{- else }} + serviceAccountName: vc-{{ .Release.Name }} + {{- end }} + volumes: + {{- if or .Values.securityContext.runAsUser .Values.securityContext.runAsNonRoot }} + - name: helm-cache + emptyDir: {} + - name: tmp + emptyDir: {} + {{- end }} + - name: config + emptyDir: {} + {{- if .Values.volumes }} +{{ toYaml .Values.volumes | indent 8 }} + {{- end }} + {{- if .Values.coredns.enabled }} + - name: coredns + configMap: + name: {{ .Release.Name }}-coredns + {{- end }} + {{- if not .Values.storage.persistence }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + {{- if .Values.fsGroup }} + securityContext: + fsGroup: {{ .Values.fsGroup }} + {{- end }} + containers: + {{- if not .Values.vcluster.disabled }} + - image: {{ .Values.defaultImageRegistry }}{{ .Values.vcluster.image }} + name: vcluster + # k3s has a problem running as pid 1 and disabled agents on cgroupv2 + # nodes as it will try to evacuate the cgroups there. Starting k3s + # through a shell makes it non pid 1 and prevents this from happening + command: + - /bin/sh + args: + - -c + - {{ range $f := .Values.vcluster.command -}} + {{ $f }} + {{- end }} + {{- range $f := .Values.vcluster.baseArgs }} + {{ $f }} + {{- end }} + {{- if not .Values.sync.nodes.enableScheduler }} + --disable-scheduler + --kube-controller-manager-arg=controllers=*,-nodeipam,-nodelifecycle,-persistentvolume-binder,-attachdetach,-persistentvolume-expander,-cloud-node-lifecycle,-ttl + --kube-apiserver-arg=endpoint-reconciler-type=none + {{- else }} + --kube-controller-manager-arg=controllers=*,-nodeipam,-persistentvolume-binder,-attachdetach,-persistentvolume-expander,-cloud-node-lifecycle,-ttl + --kube-apiserver-arg=endpoint-reconciler-type=none + --kube-controller-manager-arg=node-monitor-grace-period=1h + --kube-controller-manager-arg=node-monitor-period=1h + {{- end }} + {{- if .Values.serviceCIDR }} + --service-cidr={{ .Values.serviceCIDR }} + {{- else }} + --service-cidr=$(SERVICE_CIDR) + {{- end }} + {{- range $f := .Values.vcluster.extraArgs }} + {{ $f }} + {{- end }} + && true + env: + {{- if .Values.vcluster.env }} +{{ toYaml .Values.vcluster.env | indent 10 }} + {{- end }} + {{- if .Values.enableHA }} + - name: K3S_TOKEN + valueFrom: + secretKeyRef: + name: {{ include "vcluster.k3s.tokenSecretName" . | quote }} + key: {{ include "vcluster.k3s.serverTokenKey" . | quote }} + {{- end }} + {{- if not .Values.serviceCIDR }} + - name: SERVICE_CIDR + valueFrom: + configMapKeyRef: + name: "vc-cidr-{{ .Release.Name }}" + key: cidr + {{- end }} + securityContext: +{{ toYaml .Values.securityContext | indent 10 }} + volumeMounts: + - name: config + mountPath: /etc/rancher +{{ toYaml .Values.vcluster.volumeMounts | indent 10 }} + resources: +{{ toYaml .Values.vcluster.resources | indent 10 }} + {{- end }} + {{- if not .Values.syncer.disabled }} + - name: syncer + {{- if .Values.syncer.image }} + image: "{{ .Values.defaultImageRegistry }}{{ .Values.syncer.image }}" + {{- else }} + image: "{{ .Values.defaultImageRegistry }}ghcr.io/loft-sh/vcluster:{{ .Chart.Version }}" + {{- end }} + {{- if .Values.syncer.workingDir }} + workingDir: {{ .Values.syncer.workingDir }} + {{- end }} + {{- if .Values.syncer.command }} + command: + {{- range $f := .Values.syncer.command }} + - {{ $f | quote }} + {{- end }} + {{- end }} + {{- if not .Values.syncer.noArgs }} + args: + - --name={{ .Release.Name }} + - --service-account=vc-workload-{{ .Release.Name }} + {{- range $key, $container := .Values.plugin }} + {{- if not $container.optional }} + - --plugins={{ $key }} + {{- end }} + {{- end }} + {{- if .Values.sync.nodes.enableScheduler }} + - --enable-scheduler + {{- end }} + {{- if .Values.defaultImageRegistry }} + - --default-image-registry={{ .Values.defaultImageRegistry }} + {{- end }} + {{- if .Values.syncer.kubeConfigContextName }} + - --kube-config-context-name={{ .Values.syncer.kubeConfigContextName }} + {{- end }} + {{- if .Values.enableHA }} + - --leader-elect=true + {{- else }} + - --leader-elect=false + {{- end }} + {{- if .Values.ingress.enabled }} + - --tls-san={{ .Values.ingress.host }} + {{- end }} + {{- if .Values.isolation.enabled }} + - --enforce-pod-security-standard={{ .Values.isolation.podSecurityStandard }} + {{- end}} + {{- include "vcluster.syncer.syncArgs" . | indent 10 }} + {{- if .Values.sync.nodes.syncAllNodes }} + - --sync-all-nodes + {{- end }} + {{- if .Values.sync.nodes.nodeSelector }} + - --node-selector={{ .Values.sync.nodes.nodeSelector }} + {{- end }} + {{- if .Values.hostpathMapper.enabled }} + - --rewrite-host-paths=true + {{- end }} + {{- if .Values.multiNamespaceMode.enabled }} + - --multi-namespace-mode=true + {{- end }} + {{- if .Values.sync.configmaps.all }} + - --sync-all-configmaps=true + {{- end }} + {{- if .Values.sync.secrets.all }} + - --sync-all-secrets=true + {{- end }} + {{- if not .Values.sync.nodes.fakeKubeletIPs }} + - --fake-kubelet-ips=false + {{- end }} + {{- if or .Values.proxy.metricsServer.nodes.enabled .Values.proxy.metricsServer.pods.enabled }} + - --proxy-metrics-server=true + {{- end }} + {{- range $f := .Values.syncer.extraArgs }} + - {{ $f | quote }} + {{- end }} + {{- include "vcluster.serviceMapping.fromHost" . | indent 10 }} + {{- include "vcluster.serviceMapping.fromVirtual" . | indent 10 }} + {{- else }} + args: +{{ toYaml .Values.syncer.extraArgs | indent 10 }} + {{- end }} + {{- if .Values.syncer.livenessProbe }} + {{- if .Values.syncer.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /healthz + port: 8443 + scheme: HTTPS + failureThreshold: 60 + initialDelaySeconds: 60 + periodSeconds: 2 + {{- end }} + {{- end }} + {{- if .Values.syncer.readinessProbe }} + {{- if .Values.syncer.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /readyz + port: 8443 + scheme: HTTPS + failureThreshold: 60 + periodSeconds: 2 + {{- end }} + {{- end }} + securityContext: +{{ toYaml .Values.securityContext | indent 10 }} + env: + {{- if .Values.syncer.env }} +{{ toYaml .Values.syncer.env | indent 10 }} + {{- end }} + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + {{- if eq (.Values.replicas | toString | atoi) 1 }} + - name: VCLUSTER_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- end }} + {{- if .Values.sync.generic.config }} + - name: CONFIG + value: |- + {{- .Values.sync.generic.config | nindent 14 }} + {{- end }} + - name: VCLUSTER_TELEMETRY_CONFIG + value: {{ .Values.telemetry | toJson | quote }} + volumeMounts: + {{- if or .Values.securityContext.runAsUser .Values.securityContext.runAsNonRoot }} + - name: helm-cache + mountPath: /.cache/helm + - name: tmp + mountPath: /tmp + {{- end }} + {{- if .Values.coredns.enabled }} + - name: coredns + mountPath: /manifests/coredns + readOnly: true + {{- end }} +{{ toYaml .Values.syncer.volumeMounts | indent 10 }} + {{- if .Values.syncer.extraVolumeMounts }} +{{ toYaml .Values.syncer.extraVolumeMounts | indent 10 }} + {{- end }} + resources: +{{ toYaml .Values.syncer.resources | indent 10 }} + {{- end }} + {{- $counter := -1 -}} + {{- range $key, $container := .Values.plugin }} + {{- $counter = add1 $counter }} + - image: {{ $.Values.defaultImageRegistry }}{{ $container.image }} + {{- if $container.name }} + name: {{ $container.name | quote }} + {{- else }} + name: {{ $key | quote }} + {{- end }} + {{- if $container.imagePullPolicy }} + imagePullPolicy: {{ $container.imagePullPolicy }} + {{- end }} + {{- if $container.workingDir }} + workingDir: {{ $container.workingDir }} + {{- end }} + {{- if $container.command }} + command: + {{- range $commandIndex, $command := $container.command }} + - {{ $command | quote }} + {{- end }} + {{- end }} + {{- if $container.args }} + args: + {{- range $argIndex, $arg := $container.args }} + - {{ $arg | quote }} + {{- end }} + {{- end }} + {{- if $container.terminationMessagePath }} + terminationMessagePath: {{ $container.terminationMessagePath }} + {{- end }} + {{- if $container.terminationMessagePolicy }} + terminationMessagePolicy: {{ $container.terminationMessagePolicy }} + {{- end }} + env: + - name: VCLUSTER_PLUGIN_ADDRESS + value: "localhost:{{ add 14000 $counter }}" + - name: VCLUSTER_PLUGIN_NAME + value: "{{ $key }}" + {{- if $container.env }} +{{ toYaml $container.env | indent 10 }} + {{- end }} + envFrom: +{{ toYaml $container.envFrom | indent 10 }} + securityContext: +{{ toYaml $container.securityContext | indent 10 }} + lifecycle: +{{ toYaml $container.lifecycle | indent 10 }} + livenessProbe: +{{ toYaml $container.livenessProbe | indent 10 }} + readinessProbe: +{{ toYaml $container.readinessProbe | indent 10 }} + startupProbe: +{{ toYaml $container.startupProbe | indent 10 }} + volumeDevices: +{{ toYaml $container.volumeDevices | indent 10 }} + volumeMounts: +{{ toYaml $container.volumeMounts | indent 10 }} + {{- if $container.resources }} + resources: +{{ toYaml $container.resources | indent 10 }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/vcluster/templates/workloadserviceaccount.yaml b/charts/vcluster/templates/workloadserviceaccount.yaml new file mode 100644 index 000000000..8d05fc1e --- /dev/null +++ b/charts/vcluster/templates/workloadserviceaccount.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: vc-workload-{{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app: vcluster + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + {{- $annotations := merge .Values.globalAnnotations .Values.workloadServiceAccount.annotations }} + {{- if $annotations }} + annotations: +{{ toYaml $annotations | indent 4 }} + {{- end }} +{{- if .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: +{{ toYaml .Values.serviceAccount.imagePullSecrets | indent 2 }} +{{- end }} diff --git a/charts/vcluster/values.yaml b/charts/vcluster/values.yaml new file mode 100644 index 000000000..1a0c091a --- /dev/null +++ b/charts/vcluster/values.yaml @@ -0,0 +1,446 @@ +# These annotations will be applied to all objects created in this chart +globalAnnotations: {} + +# If the control plane is deployed in high availability mode +# Make sure to scale up the replicas and use an external datastore +enableHA: false + +# If true, will deploy vcluster in headless mode, which means no deployment +# or statefulset is created. +headless: false + +# DefaultImageRegistry will be prepended to all deployed vcluster images, such as the vcluster pod, coredns etc.. Deployed +# images within the vcluster will not be rewritten. +defaultImageRegistry: "" + +# Plugins that should get loaded. Usually you want to apply those via 'vcluster create ... -f https://.../plugin.yaml' +plugin: {} +# Manually configure a plugin called test +# test: +# image: ... +# env: ... +# rbac: +# clusterRole: +# extraRules: ... +# role: +# extraRules: ... + +# Resource syncers that should be enabled/disabled. +# Enabling syncers will impact RBAC Role and ClusterRole permissions. +# To disable a syncer set "enabled: false". +# See docs for details - https://www.vcluster.com/docs/architecture/synced-resources +sync: + services: + enabled: true + configmaps: + enabled: true + all: false + secrets: + all: false + enabled: true + endpoints: + enabled: true + pods: + enabled: true + ephemeralContainers: false + status: false + events: + enabled: true + persistentvolumeclaims: + enabled: true + ingresses: + enabled: false + ingressclasses: + {} + # By default IngressClasses sync is enabled when the Ingress sync is enabled + # but it can be explicitly disabled by setting: + # enabled: false + fake-nodes: + enabled: true + fake-persistentvolumes: + enabled: true + nodes: + fakeKubeletIPs: true + enabled: false + # If nodes sync is enabled, and syncAllNodes = true, the virtual cluster + # will sync all nodes instead of only the ones where some pods are running. + syncAllNodes: false + # nodeSelector is used to limit which nodes get synced to the vcluster, + # and which nodes are used to run vcluster pods. + # A valid string representation of a label selector must be used. + nodeSelector: "" + # if true, vcluster will run with a scheduler and node changes are possible + # from within the virtual cluster. This is useful if you would like to + # taint, drain and label nodes from within the virtual cluster + enableScheduler: false + # DEPRECATED: use enable scheduler instead + # syncNodeChanges allows vcluster user edits of the nodes to be synced down to the host nodes. + # Write permissions on node resource will be given to the vcluster. + syncNodeChanges: false + persistentvolumes: + enabled: false + storageclasses: + enabled: false + # formerly named - "legacy-storageclasses" + hoststorageclasses: + enabled: false + priorityclasses: + enabled: false + networkpolicies: + enabled: false + volumesnapshots: + enabled: false + poddisruptionbudgets: + enabled: false + serviceaccounts: + enabled: false + # generic CRD configuration + generic: + config: |- + --- + +# If enabled, will fallback to host dns for resolving domains. This +# is useful if using istio or dapr in the host cluster and sidecar +# containers cannot connect to the central instance. Its also useful +# if you want to access host cluster services from within the vcluster. +fallbackHostDns: false + +# Map Services between host and virtual cluster +mapServices: + # Services that should get mapped from the + # virtual cluster to the host cluster. + # vcluster will make sure to sync the service + # ip to the host cluster automatically as soon + # as the service exists. + # For example: + # fromVirtual: + # from: my-namespace/name + # to: host-service + fromVirtual: [] + # Same as from virtual, but instead sync services + # from the host cluster into the virtual cluster. + # If the namespace does not exist, vcluster will + # also create the namespace for the service. + fromHost: [] + +proxy: + metricsServer: + nodes: + enabled: false + pods: + enabled: false + +hostpathMapper: + enabled: false + resources: + {} + # limits: + # cpu: 40m + # memory: 100Mi + # requests: + # cpu: 20m + # memory: 50Mi + +# Syncer configuration +syncer: + # Image to use for the syncer + # image: ghcr.io/loft-sh/vcluster:0.15.2 + extraArgs: [] + env: [] + livenessProbe: + enabled: true + readinessProbe: + enabled: true + volumeMounts: + - mountPath: /data + name: data + readOnly: true + extraVolumeMounts: [] + resources: + limits: + cpu: 1000m + memory: 512Mi + requests: + # ensure that cpu/memory requests are high enough. + # for example gke wants minimum 10m/32Mi here! + cpu: 20m + memory: 64Mi + kubeConfigContextName: "my-vcluster" + serviceAnnotations: {} + +# Virtual Cluster (k3s) configuration +vcluster: + # Image to use for the virtual cluster + image: rancher/k3s:v1.26.0-k3s1 + command: + - /bin/k3s + baseArgs: + - server + - --write-kubeconfig=/data/k3s-config/kube-config.yaml + - --data-dir=/data + - --disable=traefik,servicelb,metrics-server,local-storage,coredns + - --disable-network-policy + - --disable-agent + - --disable-cloud-controller + - --flannel-backend=none + extraArgs: [] + volumeMounts: + - mountPath: /data + name: data + env: [] + resources: + limits: + memory: 2Gi + requests: + cpu: 200m + memory: 256Mi + +# Storage settings for the vcluster +storage: + # If this is disabled, vcluster will use an emptyDir instead + # of a PersistentVolumeClaim + persistence: true + # Size of the persistent volume claim + size: 5Gi + # Optional StorageClass used for the pvc + # if empty default StorageClass defined in your host cluster will be used + # className: + +# Extra volumes that should be created for the StatefulSet +volumes: [] + +# Service account that should be used by the vcluster +serviceAccount: + create: true + # Optional name of the service account to use + # name: default + # Optional pull secrets + # imagePullSecrets: + # - name: my-pull-secret + +# Service account that should be used by the pods synced by vcluster +workloadServiceAccount: + # This is not supported in multi-namespace mode + annotations: {} + +# Roles & ClusterRoles for the vcluster +rbac: + clusterRole: + # Deprecated ! + # Necessary cluster roles are created based on the enabled syncers (.sync.*.enabled) + # Support for this value will be removed in a future version of the vcluster + create: false + role: + # Deprecated ! + # Support for this value will be removed in a future version of the vcluster + # and basic role will always be created + create: true + # Deprecated ! + # Necessary extended roles are created based on the enabled syncers (.sync.*.enabled) + # Support for this value will be removed in a future version of the vcluster + extended: false + +# The amount of replicas to run the statefulset with +replicas: 1 + +# NodeSelector used to schedule the vcluster +nodeSelector: {} + +# Affinity to apply to the vcluster statefulset +affinity: {} + +# PriorityClassName to apply to the vcluster statefulset +priorityClassName: "" + +# Tolerations to apply to the vcluster statefulset +tolerations: [] + +# Extra Labels for the stateful set +labels: {} +podLabels: {} + +# Extra Annotations for the stateful set +annotations: {} +podAnnotations: {} + +# PodDisruptionBudget settings for the vcluster +# (takes effect only if high availability mode is enabled and more than one replica is created) +podDisruptionBudget: + # Controls wether a PodDisruptionBudget will be created + enabled: false + minAvailable: 1 + # maxUnavailable: 1 + +# k3s token settings for the vcluster +# (take effect only if high availability mode is enabled) +serverToken: + # Shared secret used to join a k3s server to the cluster. If empty, a random token value will be generated. + # (Note that this token is also used to generate the encryption key for important content in the database e.g., bootstrap data) + value: "" + # Reference to an existing secret key used as value for the k3s server token + secretKeyRef: + {} + # name: "" + # key: "" + +# Service configurations +service: + type: ClusterIP + + # Optional configuration + # A list of IP addresses for which nodes in the cluster will also accept traffic for this service. + # These IPs are not managed by Kubernetes; e.g., an external load balancer. + externalIPs: [] + + # Optional configuration for LoadBalancer & NodePort service types + # Route external traffic to node-local or cluster-wide endpoints [ Local | Cluster ] + externalTrafficPolicy: "" + + # Optional configuration for LoadBalancer service type + # Specify IP of load balancer to be created + loadBalancerIP: "" + # CIDR block(s) for the service allowlist + loadBalancerSourceRanges: [] + # Set the loadBalancerClass if using an external load balancer controller + loadBalancerClass: "" + +# Configure the ingress resource that allows you to access the vcluster +ingress: + # Enable ingress record generation + enabled: false + # Ingress path type + pathType: ImplementationSpecific + apiVersion: networking.k8s.io/v1 + ingressClassName: "" + host: vcluster.local + annotations: + nginx.ingress.kubernetes.io/backend-protocol: HTTPS + nginx.ingress.kubernetes.io/ssl-passthrough: "true" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + +# Configure SecurityContext of the containers in the VCluster pod +securityContext: + allowPrivilegeEscalation: false + # capabilities: + # drop: + # - all + # readOnlyRootFilesystem will be set to true by default at a later release + # currently leaving it undefined for backwards compatibility with older vcluster cli versions + # readOnlyRootFilesystem: true + + # To run vcluster pod as non-root uncomment runAsUser and runAsNonRoot values. + # Update the runAsUser value if your cluster has limitations on user UIDs. + # For installation on OpenShift leave the runAsUser undefined (commented out). + # runAsUser: 12345 + # runAsNonRoot: true + +# Set "enable" to true when running vcluster in an OpenShift host +# This will add an extra rule to the deployed role binding in order +# to manage service endpoints +openshift: + enable: false + +# If enabled will deploy the coredns configmap +coredns: + enabled: true + replicas: 1 + # image: my-core-dns-image:latest + # config: |- + # .:1053 { + # ... + # CoreDNS service configurations + service: + type: ClusterIP + # Configuration for LoadBalancer service type + externalIPs: [] + externalTrafficPolicy: "" + # Extra Annotations + annotations: {} + resources: + limits: + cpu: 1000m + memory: 170Mi + requests: + cpu: 3m + memory: 16Mi + # if below option is configured, it will override the coredns manifests with the following string + # manifests: |- + # apiVersion: ... + # ... + podAnnotations: {} + podLabels: {} + +# If enabled will deploy vcluster in an isolated mode with pod security +# standards, limit ranges and resource quotas +isolation: + enabled: false + namespace: null + + podSecurityStandard: baseline + + # If enabled will add node/proxy permission to the cluster role + # in isolation mode + nodeProxyPermission: + enabled: false + + resourceQuota: + enabled: true + quota: + requests.cpu: 10 + requests.memory: 20Gi + requests.storage: "100Gi" + requests.ephemeral-storage: 60Gi + limits.cpu: 20 + limits.memory: 40Gi + limits.ephemeral-storage: 160Gi + services.nodeports: 0 + services.loadbalancers: 1 + count/endpoints: 40 + count/pods: 20 + count/services: 20 + count/secrets: 100 + count/configmaps: 100 + count/persistentvolumeclaims: 20 + scopeSelector: + matchExpressions: + scopes: + + limitRange: + enabled: true + default: + ephemeral-storage: 8Gi + memory: 512Mi + cpu: "1" + defaultRequest: + ephemeral-storage: 3Gi + memory: 128Mi + cpu: 100m + + networkPolicy: + enabled: true + outgoingConnections: + ipBlock: + cidr: 0.0.0.0/0 + except: + - 100.64.0.0/10 + - 127.0.0.0/8 + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + +# manifests to setup when initializing a vcluster +init: + manifests: |- + --- + # The contents of manifests-template will be templated using helm + # this allows you to use helm values inside, e.g.: {{ .Release.Name }} + manifestsTemplate: "" + helm: [] + +multiNamespaceMode: + enabled: false + +telemetry: + disabled: "false" + instanceCreator: "helm" + instanceCreatorUID: ""