diff --git a/dpf-utils/cmd/dpucniprovisioner/main.go b/dpf-utils/cmd/dpucniprovisioner/main.go index bc82337..d2d3a36 100644 --- a/dpf-utils/cmd/dpucniprovisioner/main.go +++ b/dpf-utils/cmd/dpucniprovisioner/main.go @@ -128,6 +128,7 @@ func main() { } provisioner := dpucniprovisioner.New(ctx, mode, c, ovsClient, networkhelper.New(), exec, clientset, vtepIPNet, gateway, vtepCIDR, hostCIDR, pfIPNet, node, gatewayDiscoveryNetwork, ovnMTU) + provisioner.K8sAPIServer = os.Getenv("K8S_APISERVER") err = provisioner.RunOnce() if err != nil { diff --git a/dpf-utils/internal/cniprovisioner/dpu/provisioner.go b/dpf-utils/internal/cniprovisioner/dpu/provisioner.go index 265194b..d03841c 100644 --- a/dpf-utils/internal/cniprovisioner/dpu/provisioner.go +++ b/dpf-utils/internal/cniprovisioner/dpu/provisioner.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "time" "github.com/nvidia/doca-platform/pkg/utils/networkhelper" @@ -84,6 +85,10 @@ const ( // netplanApplyCooldownDuration determines the cooldown period of a successful netplan apply command before a // subsequent netplan apply command is executed. netplanApplyCooldownDuration = time.Minute * 2 + // hostBootstrapKubeconfigPath is the path where bootstrap kubeconfig is written for ovnkube identity. + hostBootstrapKubeconfigPath = "/host-kubernetes/kubelet.conf" + // hostNodeNameFilePath is the path used to publish mapped host node name for other containers in the pod. + hostNodeNameFilePath = "/var/run/ovn-kubernetes/host-node-name" ) type DPUCNIProvisioner struct { @@ -98,6 +103,13 @@ type DPUCNIProvisioner struct { // FileSystemRoot controls the file system root. It's used for enabling easier testing of the package. Defaults to // empty. FileSystemRoot string + // K8sAPIServer is the host cluster API server endpoint used in the generated bootstrap kubeconfig. + // Leave empty to skip bootstrap kubeconfig generation. + K8sAPIServer string + // BootstrapKubeconfigPath is where the bootstrap kubeconfig is written. Defaults to hostBootstrapKubeconfigPath. + BootstrapKubeconfigPath string + // HostNodeNameFilePath is where the mapped host node name is written. Defaults to hostNodeNameFilePath. + HostNodeNameFilePath string // vtepIPNet is the IP that should be added to the VTEP interface. vtepIPNet *net.IPNet @@ -154,6 +166,9 @@ func New(ctx context.Context, exec: exec, kubernetesClient: kubernetesClient, FileSystemRoot: "", + K8sAPIServer: "", + BootstrapKubeconfigPath: hostBootstrapKubeconfigPath, + HostNodeNameFilePath: hostNodeNameFilePath, vtepIPNet: vtepIPNet, gateway: gateway, vtepCIDR: vtepCIDR, @@ -208,9 +223,13 @@ func (p *DPUCNIProvisioner) EnsureConfiguration() { // configure runs the provisioning flow once func (p *DPUCNIProvisioner) configure() error { klog.Info("Configuring Kubernetes host name in OVS") - if err := p.findAndSetKubernetesHostNameInOVS(); err != nil { + hostName, err := p.findAndSetKubernetesHostNameInOVS() + if err != nil { return fmt.Errorf("error while setting the Kubernetes Host Name in OVS: %w", err) } + if err := p.writeHostIdentityBootstrapArtifacts(hostName); err != nil { + return fmt.Errorf("error while writing host identity bootstrap artifacts: %w", err) + } if p.mode == ExternalIPAM { klog.Info("Configuring br-ovn") @@ -238,22 +257,83 @@ func (p *DPUCNIProvisioner) configure() error { } // findAndSetKubernetesHostNameInOVS discovers and sets the Kubernetes Host Name in OVS -func (p *DPUCNIProvisioner) findAndSetKubernetesHostNameInOVS() error { +func (p *DPUCNIProvisioner) findAndSetKubernetesHostNameInOVS() (string, error) { nodeClient := p.kubernetesClient.CoreV1().Nodes() n, err := nodeClient.Get(p.ctx, p.dpuHostName, metav1.GetOptions{}) if err != nil { - return fmt.Errorf("error while getting Kubernetes Node: %w", err) + return "", fmt.Errorf("error while getting Kubernetes Node: %w", err) } hostName, ok := n.Labels[constants.HostNameDPULabelKey] if !ok { - return fmt.Errorf("required label %s is not set on node %s in the DPU cluster", constants.HostNameDPULabelKey, p.dpuHostName) + return "", fmt.Errorf("required label %s is not set on node %s in the DPU cluster", constants.HostNameDPULabelKey, p.dpuHostName) + } + hostName = strings.TrimSpace(hostName) + if hostName == "" { + return "", fmt.Errorf("label %s on node %s cannot be empty", constants.HostNameDPULabelKey, p.dpuHostName) } if err := p.ovsClient.SetKubernetesHostNodeName(hostName); err != nil { - return fmt.Errorf("error while setting the Kubernetes Host Name in OVS: %w", err) + return "", fmt.Errorf("error while setting the Kubernetes Host Name in OVS: %w", err) } if err := p.ovsClient.SetHostName(hostName); err != nil { - return fmt.Errorf("error while setting the hostname external ID in OVS: %w", err) + return "", fmt.Errorf("error while setting the hostname external ID in OVS: %w", err) + } + return hostName, nil +} + +// writeHostIdentityBootstrapArtifacts writes host identity data for other pod containers. +// It writes artifacts only when K8sAPIServer is set, which enables the bootstrap flow. +func (p *DPUCNIProvisioner) writeHostIdentityBootstrapArtifacts(hostName string) error { + if strings.TrimSpace(p.K8sAPIServer) == "" { + return nil + } + + hostNodeNamePath := p.HostNodeNameFilePath + if hostNodeNamePath == "" { + hostNodeNamePath = hostNodeNameFilePath + } + hostNodeNamePath = filepath.Join(p.FileSystemRoot, hostNodeNamePath) + if err := os.MkdirAll(filepath.Dir(hostNodeNamePath), 0755); err != nil { + return fmt.Errorf("error while creating directory for host node name file %s: %w", hostNodeNamePath, err) + } + if err := os.WriteFile(hostNodeNamePath, []byte(hostName+"\n"), 0644); err != nil { + return fmt.Errorf("error while writing host node name file %s: %w", hostNodeNamePath, err) + } + + bootstrapPath := p.BootstrapKubeconfigPath + if bootstrapPath == "" { + bootstrapPath = hostBootstrapKubeconfigPath + } + bootstrapPath = filepath.Join(p.FileSystemRoot, bootstrapPath) + if err := os.MkdirAll(filepath.Dir(bootstrapPath), 0700); err != nil { + return fmt.Errorf("error while creating directory for bootstrap kubeconfig %s: %w", bootstrapPath, err) + } + + bootstrapKubeconfig := fmt.Sprintf(`apiVersion: v1 +kind: Config +clusters: +- cluster: + certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + server: %s + name: host-cluster +contexts: +- context: + cluster: host-cluster + user: ovn-dpu-bootstrap + name: ovn-dpu-bootstrap +current-context: ovn-dpu-bootstrap +users: +- name: ovn-dpu-bootstrap + user: + tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + as: system:node:%s + as-groups: + - system:nodes + - system:authenticated +`, strings.TrimSpace(p.K8sAPIServer), hostName) + + if err := os.WriteFile(bootstrapPath, []byte(bootstrapKubeconfig), 0600); err != nil { + return fmt.Errorf("error while writing bootstrap kubeconfig %s: %w", bootstrapPath, err) } return nil } diff --git a/helm/ovn-kubernetes-dpf/templates/common.yaml b/helm/ovn-kubernetes-dpf/templates/common.yaml index 002d688..63f8ac3 100644 --- a/helm/ovn-kubernetes-dpf/templates/common.yaml +++ b/helm/ovn-kubernetes-dpf/templates/common.yaml @@ -87,6 +87,16 @@ rules: - egressservices - adminpolicybasedexternalroutes verbs: [ "get", "list", "watch" ] +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- apiGroups: ["certificates.k8s.io"] + resources: + - certificatesigningrequests + verbs: + - create + - get + - list + - watch +{{- end }} - apiGroups: [""] resources: - events @@ -129,4 +139,4 @@ rules: verbs: - get - create -{{- end }} \ No newline at end of file +{{- end }} diff --git a/helm/ovn-kubernetes-dpf/templates/dpu-manifests.yaml b/helm/ovn-kubernetes-dpf/templates/dpu-manifests.yaml index 017c16a..1e29b95 100644 --- a/helm/ovn-kubernetes-dpf/templates/dpu-manifests.yaml +++ b/helm/ovn-kubernetes-dpf/templates/dpu-manifests.yaml @@ -1,3 +1,5 @@ +{{- $identityEnabled := hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false -}} +{{- $useSecretBootstrap := and $identityEnabled (ne (default "" .Values.dpuManifests.kubernetesSecretName) "") -}} {{- if .Values.dpuManifests.enabled }} --- apiVersion: v1 @@ -53,9 +55,15 @@ roleRef: kind: ClusterRole apiGroup: rbac.authorization.k8s.io subjects: +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- kind: Group + name: system:ovn-nodes + apiGroup: rbac.authorization.k8s.io +{{- else }} - kind: ServiceAccount name: {{ include "ovn-kubernetes.fullname" . }}-node namespace: {{ .Release.Namespace }} +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -80,9 +88,15 @@ roleRef: kind: Role apiGroup: rbac.authorization.k8s.io subjects: +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- kind: Group + name: system:ovn-nodes + apiGroup: rbac.authorization.k8s.io +{{- else }} - kind: ServiceAccount name: {{ include "ovn-kubernetes.fullname" . }}-node namespace: {{ .Release.Namespace }} +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -274,6 +288,13 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + {{- if $useSecretBootstrap }} + - name: K8S_APISERVER + valueFrom: + configMapKeyRef: + name: {{ include "ovn-kubernetes.fullname" . }}-config + key: k8s_apiserver + {{- end }} {{- if .Values.dpuManifests.externalDHCP }} # Needed so that systemctl can run inside the container - name: SYSTEMCTL_FORCE_BUS @@ -327,6 +348,12 @@ spec: readOnly: true - mountPath: /var/run/openvswitch/ name: host-var-run-ovs + - mountPath: /var/run/ovn-kubernetes + name: host-var-run-ovn-kubernetes + {{- if $useSecretBootstrap }} + - mountPath: /host-kubernetes + name: host-kubeconfig + {{- end }} containers: - name: nb-ovsdb image: {{ .Values.dpuManifests.image.repository }}:{{ .Values.dpuManifests.image.tag }} @@ -501,7 +528,15 @@ spec: - name: doca-ovnkube-controller image: {{ .Values.dpuManifests.image.repository }}:{{ .Values.dpuManifests.image.tag }} imagePullPolicy: {{ .Values.dpuManifests.image.pullPolicy }} - command: ["/root/ovnkube.sh", "ovnkube-controller-with-node"] + command: + - /bin/sh + - -ec + - | + host_node_name_file=/var/run/ovn-kubernetes/host-node-name + if [ -s "${host_node_name_file}" ]; then + export K8S_NODE_DPU="$(tr -d '[:space:]' < "${host_node_name_file}")" + fi + exec /root/ovnkube.sh ovnkube-controller-with-node securityContext: runAsUser: 0 privileged: true @@ -604,6 +639,10 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + - name: K8S_NODE_DPU + valueFrom: + fieldRef: + fieldPath: spec.nodeName - name: K8S_NODE_IP valueFrom: fieldRef: @@ -702,7 +741,7 @@ spec: - name: OVN_ENABLE_MULTI_EXTERNAL_GATEWAY value: "false" - name: OVN_ENABLE_OVNKUBE_IDENTITY - value: "false" + value: {{ hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false | quote }} - name: OVN_DISABLE_REQUESTEDCHASSIS value: {{ default "false" .Values.dpuManifests.ovnDisableRequestedchassis | quote }} - name: OVN_ENABLE_SVC_TEMPLATE_SUPPORT @@ -726,7 +765,15 @@ spec: - name: ovn-controller image: {{ .Values.dpuManifests.image.repository }}:{{ .Values.dpuManifests.image.tag }} imagePullPolicy: {{ .Values.dpuManifests.image.pullPolicy }} - command: ["/root/ovnkube.sh", "ovn-controller"] + command: + - /bin/sh + - -ec + - | + host_node_name_file=/var/run/ovn-kubernetes/host-node-name + if [ -s "${host_node_name_file}" ]; then + export K8S_NODE_DPU="$(tr -d '[:space:]' < "${host_node_name_file}")" + fi + exec /root/ovnkube.sh ovn-controller securityContext: runAsUser: 0 capabilities: @@ -744,6 +791,8 @@ spec: name: host-var-run-ovs - mountPath: /var/run/ovn/ name: host-var-run-ovs + - mountPath: /var/run/ovn-kubernetes + name: host-var-run-ovn-kubernetes - mountPath: /ovn-cert name: host-ovn-cert readOnly: true @@ -761,6 +810,10 @@ spec: configMapKeyRef: name: {{ include "ovn-kubernetes.fullname" . }}-config key: k8s_apiserver + - name: K8S_NODE_DPU + valueFrom: + fieldRef: + fieldPath: spec.nodeName - name: OVN_KUBERNETES_NAMESPACE valueFrom: fieldRef: @@ -845,8 +898,12 @@ spec: hostPath: path: /var/run/dbus - name: host-kubeconfig + {{- if $useSecretBootstrap }} + emptyDir: {} + {{- else }} hostPath: path: /etc/kubernetes/ + {{- end }} - name: host-kubelet hostPath: path: /var/lib/kubelet diff --git a/helm/ovn-kubernetes-dpf/templates/host-with-dpu-manifests.yaml b/helm/ovn-kubernetes-dpf/templates/host-with-dpu-manifests.yaml index 17cd6cb..96610a4 100644 --- a/helm/ovn-kubernetes-dpf/templates/host-with-dpu-manifests.yaml +++ b/helm/ovn-kubernetes-dpf/templates/host-with-dpu-manifests.yaml @@ -15,9 +15,15 @@ roleRef: kind: ClusterRole apiGroup: rbac.authorization.k8s.io subjects: +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- kind: Group + name: system:ovn-nodes + apiGroup: rbac.authorization.k8s.io +{{- else }} - kind: ServiceAccount name: {{ include "ovn-kubernetes.fullname" . }}-node-dpu-host namespace: {{ .Release.Namespace }} +{{- end }} - kind: ServiceAccount name: {{ .Values.nodeWithDPUManifests.dpuServiceAccountName }} namespace: {{ .Values.nodeWithDPUManifests.dpuServiceAccountNamespace }} @@ -37,6 +43,45 @@ subjects: - kind: ServiceAccount name: {{ .Values.nodeWithDPUManifests.dpuServiceAccountName }} namespace: {{ .Values.nodeWithDPUManifests.dpuServiceAccountNamespace }} +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "ovn-kubernetes.fullname" . }}-node-dpu-host-impersonator +rules: +- apiGroups: [""] + resources: + - users + verbs: + - impersonate +- apiGroups: [""] + resources: + - groups + resourceNames: + - system:nodes + - system:authenticated + verbs: + - impersonate +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "ovn-kubernetes.fullname" . }}-node-dpu-host-impersonator +roleRef: + name: {{ include "ovn-kubernetes.fullname" . }}-node-dpu-host-impersonator + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: {{ .Values.nodeWithDPUManifests.dpuServiceAccountName }} + namespace: {{ .Values.nodeWithDPUManifests.dpuServiceAccountNamespace }} +{{- if ne (default "" .Values.dpuManifests.kubernetesSecretName) "" }} +- kind: ServiceAccount + name: {{ .Values.dpuManifests.kubernetesSecretName }} + namespace: {{ .Release.Namespace }} +{{- end }} +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -48,9 +93,15 @@ roleRef: kind: Role apiGroup: rbac.authorization.k8s.io subjects: +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- kind: Group + name: system:ovn-nodes + apiGroup: rbac.authorization.k8s.io +{{- else }} - kind: ServiceAccount name: {{ include "ovn-kubernetes.fullname" . }}-node-dpu-host namespace: {{ .Release.Namespace }} +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -255,7 +306,7 @@ spec: - name: OVN_EX_GW_NETWORK_INTERFACE value: "" - name: OVN_ENABLE_OVNKUBE_IDENTITY - value: "false" + value: {{ hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false | quote }} - name: OVN_ENABLE_INTERCONNECT value: "true" - name: OVNKUBE_NODE_MODE diff --git a/helm/ovn-kubernetes-dpf/templates/host-without-dpu-manifests.yaml b/helm/ovn-kubernetes-dpf/templates/host-without-dpu-manifests.yaml index 98a3d84..b829f03 100644 --- a/helm/ovn-kubernetes-dpf/templates/host-without-dpu-manifests.yaml +++ b/helm/ovn-kubernetes-dpf/templates/host-without-dpu-manifests.yaml @@ -15,9 +15,15 @@ roleRef: kind: ClusterRole apiGroup: rbac.authorization.k8s.io subjects: +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- kind: Group + name: system:ovn-nodes + apiGroup: rbac.authorization.k8s.io +{{- else }} - kind: ServiceAccount name: {{ include "ovn-kubernetes.fullname" . }}-node namespace: {{ .Release.Namespace }} +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -42,9 +48,15 @@ roleRef: kind: Role apiGroup: rbac.authorization.k8s.io subjects: +{{- if eq (hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false) true }} +- kind: Group + name: system:ovn-nodes + apiGroup: rbac.authorization.k8s.io +{{- else }} - kind: ServiceAccount name: {{ include "ovn-kubernetes.fullname" . }}-node namespace: {{ .Release.Namespace }} +{{- end }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -491,7 +503,7 @@ spec: - name: OVN_ENABLE_MULTI_EXTERNAL_GATEWAY value: "false" - name: OVN_ENABLE_OVNKUBE_IDENTITY - value: "false" + value: {{ hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false | quote }} - name: OVN_ENABLE_SVC_TEMPLATE_SUPPORT value: "false" - name: OVN_ENABLE_DNSNAMERESOLVER diff --git a/helm/ovn-kubernetes-dpf/templates/ovnkube-identity-manifests.yaml b/helm/ovn-kubernetes-dpf/templates/ovnkube-identity-manifests.yaml new file mode 100644 index 0000000..84064a5 --- /dev/null +++ b/helm/ovn-kubernetes-dpf/templates/ovnkube-identity-manifests.yaml @@ -0,0 +1,190 @@ +{{- $identityEnabled := hasKey .Values.global "enableOvnKubeIdentity" | ternary .Values.global.enableOvnKubeIdentity false -}} +{{- if and (eq $identityEnabled true) .Values.commonManifests.enabled .Values.controlPlaneManifests.enabled }} +{{- $fullname := include "ovn-kubernetes.fullname" . -}} +{{- $interconnectEnabled := hasKey .Values.global "enableInterconnect" | ternary .Values.global.enableInterconnect true -}} +{{- $ca := genCA (printf "%s-self-signed-ca" $fullname) 400 }} +{{- $cert := genSignedCert "localhost" nil (list "localhost") 365 $ca }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $fullname }}-identity + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ $fullname }}-identity +roleRef: + name: {{ $fullname }}-identity + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: {{ $fullname }}-identity + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $fullname }}-identity-configmaps + namespace: {{ .Release.Namespace }} +roleRef: + name: {{ $fullname }}-configmaps + kind: Role + apiGroup: rbac.authorization.k8s.io +subjects: +- kind: ServiceAccount + name: {{ $fullname }}-identity + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ $fullname }}-identity +rules: +- apiGroups: [""] + resources: + - nodes + verbs: ["get", "list", "watch"] +- apiGroups: ["certificates.k8s.io"] + resources: + - certificatesigningrequests + verbs: ["get", "list", "watch"] +- apiGroups: ["certificates.k8s.io"] + resources: + - certificatesigningrequests/approval + verbs: ["update"] +- apiGroups: [""] + resources: + - events + verbs: ["create", "patch", "update"] +- apiGroups: ["certificates.k8s.io"] + resources: + - signers + resourceNames: + - kubernetes.io/kube-apiserver-client + verbs: ["approve"] +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $fullname }}-webhook-cert + namespace: {{ .Release.Namespace }} +data: + tls.crt: {{ $cert.Cert | b64enc | quote }} + tls.key: {{ $cert.Key | b64enc | quote }} +type: kubernetes.io/tls +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ $fullname }}-admission-webhook-node +webhooks: +- name: {{ printf "%s-admission-webhook-node.k8s.io" $fullname }} + clientConfig: + url: https://localhost:9443/node + caBundle: {{ $ca.Cert | b64enc | quote }} + admissionReviewVersions: ["v1"] + sideEffects: None + rules: + - operations: ["UPDATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["nodes/status"] + scope: "*" +{{- if eq $interconnectEnabled true }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ $fullname }}-admission-webhook-pod +webhooks: +- name: {{ printf "%s-admission-webhook-pod.k8s.io" $fullname }} + clientConfig: + url: https://localhost:9443/pod + caBundle: {{ $ca.Cert | b64enc | quote }} + admissionReviewVersions: ["v1"] + sideEffects: None + rules: + - operations: ["UPDATE"] + apiGroups: ["*"] + apiVersions: ["*"] + resources: ["pods/status"] + scope: "*" +{{- end }} +--- +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: {{ $fullname }}-identity + namespace: {{ .Release.Namespace }} + annotations: + kubernetes.io/description: | + This DaemonSet launches the ovnkube-identity networking component on control-plane nodes. +spec: + revisionHistoryLimit: 10 + selector: + matchLabels: + app.kubernetes.io/component: ovnkube-identity + {{- include "ovn-kubernetes.selectorLabels" . | nindent 6 }} + updateStrategy: + rollingUpdate: + maxSurge: 100% + maxUnavailable: 0 + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/component: ovnkube-identity + kubernetes.io/os: "linux" + ovn.dpu.nvidia.com/skip-injection: "" + {{- include "ovn-kubernetes.selectorLabels" . | nindent 8 }} + spec: + {{- if .Values.global.imagePullSecretName }} + imagePullSecrets: + - name: {{ .Values.global.imagePullSecretName }} + {{- end }} + priorityClassName: "system-cluster-critical" + serviceAccountName: {{ $fullname }}-identity + hostNetwork: true + dnsPolicy: Default + nodeSelector: + node-role.kubernetes.io/control-plane: "" + kubernetes.io/os: "linux" + containers: + - name: ovnkube-identity + image: {{ .Values.controlPlaneManifests.image.repository }}:{{ .Values.controlPlaneManifests.image.tag }} + imagePullPolicy: {{ .Values.controlPlaneManifests.image.pullPolicy }} + command: ["/root/ovnkube.sh", "ovnkube-identity"] + securityContext: + runAsUser: 0 + terminationMessagePolicy: FallbackToLogsOnError + resources: + requests: + cpu: 100m + memory: 300Mi + volumeMounts: + - mountPath: /etc/webhook-cert/ + name: webhook-cert + env: + - name: OVN_DAEMONSET_VERSION + value: "1.2.0" + - name: K8S_APISERVER + valueFrom: + configMapKeyRef: + name: {{ $fullname }}-config + key: k8s_apiserver + - name: OVNKUBE_LOGLEVEL + value: "4" + - name: OVN_ENABLE_INTERCONNECT + value: {{ $interconnectEnabled | quote }} + - name: OVN_HYBRID_OVERLAY_ENABLE + value: "" + volumes: + - name: webhook-cert + secret: + secretName: {{ $fullname }}-webhook-cert + tolerations: + - operator: "Exists" +{{- end }} diff --git a/helm/ovn-kubernetes-dpf/values.yaml b/helm/ovn-kubernetes-dpf/values.yaml index 3245146..f3740bc 100644 --- a/helm/ovn-kubernetes-dpf/values.yaml +++ b/helm/ovn-kubernetes-dpf/values.yaml @@ -80,6 +80,8 @@ mtu: 1400 global: # -- The name of the secret used to pull images. Applies to all relevant manifests. imagePullSecretName: "" + # -- Enable OVN kube node identity feature (per-node cert + admission webhook). + enableOvnKubeIdentity: true # -- Variables related to common manifests used by components in both DPU and Host cluster commonManifests: diff --git a/helm/ovn-kubernetes-dpf/values.yaml.tmpl b/helm/ovn-kubernetes-dpf/values.yaml.tmpl index eb60ece..91c6d29 100644 --- a/helm/ovn-kubernetes-dpf/values.yaml.tmpl +++ b/helm/ovn-kubernetes-dpf/values.yaml.tmpl @@ -79,6 +79,8 @@ mtu: 1400 global: # -- The name of the secret used to pull images. Applies to all relevant manifests. imagePullSecretName: "" + # -- Enable OVN kube node identity feature (per-node cert + admission webhook). + enableOvnKubeIdentity: true # -- Variables related to common manifests used by components in both DPU and Host cluster commonManifests: