From 1e65a2905295eb0dae62b5970b083f74264ccee9 Mon Sep 17 00:00:00 2001 From: Fabian Kramm Date: Thu, 18 Apr 2024 15:17:02 +0200 Subject: [PATCH] refactor: allow specifying resources for init container --- chart/values.schema.json | 18 ++++++- chart/values.yaml | 16 +++++- config/config.go | 12 ++++- config/values.yaml | 10 +++- pkg/config/config.go | 2 +- pkg/config/legacyconfig/migrate.go | 2 +- .../resources/pods/translate/hosts.go | 16 ++---- .../resources/pods/translate/translator.go | 52 ++++++++++++++++--- 8 files changed, 99 insertions(+), 29 deletions(-) diff --git a/chart/values.schema.json b/chart/values.schema.json index a94f1952f..7cec40bdf 100755 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -2711,9 +2711,23 @@ "type": "boolean", "description": "Enabled specifies if rewriting stateful set pods should be enabled." }, - "initContainerImage": { + "initContainer": { + "$ref": "#/$defs/SyncRewriteHostsInitContainer", + "description": "InitContainer holds extra options for the init container used by vCluster to rewrite the FQDN for stateful set pods." + } + }, + "additionalProperties": false, + "type": "object" + }, + "SyncRewriteHostsInitContainer": { + "properties": { + "image": { "type": "string", - "description": "InitContainerImage is the image virtual cluster should use to rewrite this FQDN." + "description": "Image is the image virtual cluster should use to rewrite this FQDN." + }, + "resources": { + "$ref": "#/$defs/Resources", + "description": "Resources are the resources that should be assigned to the init container for each stateful set init container." } }, "additionalProperties": false, diff --git a/chart/values.yaml b/chart/values.yaml index c30334b9f..69af82630 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -39,8 +39,20 @@ sync: rewriteHosts: # Enabled specifies if rewriting stateful set pods should be enabled. enabled: true - # InitContainerImage is the image virtual cluster should use to rewrite this FQDN. - initContainerImage: "library/alpine:3.13.1" + # InitContainer holds extra options for the init container used by vCluster to rewrite the FQDN for stateful set pods. + initContainer: + # Image is the image virtual cluster should use to rewrite this FQDN. + image: "library/alpine:3.13.1" + # Resources are the resources that should be assigned to the init container for each stateful set init container. + resources: + # Limits are resource limits for the container + limits: + cpu: 30m + memory: 64Mi + # Requests are minimal resources that will be consumed by the container + requests: + cpu: 30m + memory: 64Mi # Ingresses defines if ingresses created within the virtual cluster should get synced to the host cluster. ingresses: enabled: false diff --git a/config/config.go b/config/config.go index e14a677fd..a3a17fe0b 100644 --- a/config/config.go +++ b/config/config.go @@ -331,8 +331,16 @@ type SyncRewriteHosts struct { // Enabled specifies if rewriting stateful set pods should be enabled. Enabled bool `json:"enabled,omitempty"` - // InitContainerImage is the image virtual cluster should use to rewrite this FQDN. - InitContainerImage string `json:"initContainerImage,omitempty"` + // InitContainer holds extra options for the init container used by vCluster to rewrite the FQDN for stateful set pods. + InitContainer SyncRewriteHostsInitContainer `json:"initContainer,omitempty"` +} + +type SyncRewriteHostsInitContainer struct { + // Image is the image virtual cluster should use to rewrite this FQDN. + Image string `json:"image,omitempty"` + + // Resources are the resources that should be assigned to the init container for each stateful set init container. + Resources Resources `json:"resources,omitempty"` } type SyncNodes struct { diff --git a/config/values.yaml b/config/values.yaml index 8754b9dd9..e0bd4b113 100644 --- a/config/values.yaml +++ b/config/values.yaml @@ -22,7 +22,15 @@ sync: useSecretsForSATokens: false rewriteHosts: enabled: true - initContainerImage: "library/alpine:3.13.1" + initContainer: + image: "library/alpine:3.13.1" + resources: + limits: + cpu: 30m + memory: 64Mi + requests: + cpu: 30m + memory: 64Mi ingresses: enabled: false priorityClasses: diff --git a/pkg/config/config.go b/pkg/config/config.go index f6ed9c7c3..a253ae400 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -165,7 +165,7 @@ func (v VirtualClusterConfig) LegacyOptions() (*legacyconfig.LegacyVirtualCluste EnforceNodeSelector: true, PluginListenAddress: "localhost:10099", OverrideHosts: v.Sync.ToHost.Pods.RewriteHosts.Enabled, - OverrideHostsContainerImage: v.Sync.ToHost.Pods.RewriteHosts.InitContainerImage, + OverrideHostsContainerImage: v.Sync.ToHost.Pods.RewriteHosts.InitContainer.Image, ServiceAccountTokenSecrets: v.Sync.ToHost.Pods.UseSecretsForSATokens, ClusterDomain: v.Networking.Advanced.ClusterDomain, LeaderElect: v.ControlPlane.StatefulSet.HighAvailability.Replicas > 1, diff --git a/pkg/config/legacyconfig/migrate.go b/pkg/config/legacyconfig/migrate.go index 8bf69c971..d94df666c 100644 --- a/pkg/config/legacyconfig/migrate.go +++ b/pkg/config/legacyconfig/migrate.go @@ -924,7 +924,7 @@ func migrateFlag(key, value string, newConfig *config.Config) error { return fmt.Errorf("value is missing") } - newConfig.Sync.ToHost.Pods.RewriteHosts.InitContainerImage = value + newConfig.Sync.ToHost.Pods.RewriteHosts.InitContainer.Image = value case "cluster-domain": if value == "" { return fmt.Errorf("value is missing") diff --git a/pkg/controllers/resources/pods/translate/hosts.go b/pkg/controllers/resources/pods/translate/hosts.go index 9b6e175ac..fa0c6ebe5 100644 --- a/pkg/controllers/resources/pods/translate/hosts.go +++ b/pkg/controllers/resources/pods/translate/hosts.go @@ -3,7 +3,6 @@ package translate import ( "github.com/loft-sh/vcluster/pkg/coredns" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" ) const ( @@ -24,13 +23,13 @@ var ( } ) -func rewritePodHostnameFQDN(pPod *corev1.Pod, defaultImageRegistry, hostsRewriteImage, fromHost, toHostname, toHostnameFQDN string) { +func (t *translator) rewritePodHostnameFQDN(pPod *corev1.Pod, fromHost, toHostname, toHostnameFQDN string) { if pPod.Annotations == nil || pPod.Annotations[DisableSubdomainRewriteAnnotation] != "true" || pPod.Annotations[HostsRewrittenAnnotation] != "true" { userID := coredns.GetUserID() groupID := coredns.GetGroupID() initContainer := corev1.Container{ Name: HostsRewriteContainerName, - Image: defaultImageRegistry + hostsRewriteImage, + Image: t.defaultImageRegistry + t.overrideHostsImage, Command: []string{"sh"}, Args: []string{"-c", "sed -E -e 's/^(\\d+.\\d+.\\d+.\\d+\\s+)" + fromHost + "$/\\1 " + toHostnameFQDN + " " + toHostname + "/' /etc/hosts > /hosts/hosts"}, SecurityContext: &corev1.SecurityContext{ @@ -41,16 +40,7 @@ func rewritePodHostnameFQDN(pPod *corev1.Pod, defaultImageRegistry, hostsRewrite AllowPrivilegeEscalation: &privilegeEscalation, SeccompProfile: &seccompProfile, }, - Resources: corev1.ResourceRequirements{ - Limits: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceCPU: resource.MustParse("30m"), - corev1.ResourceMemory: resource.MustParse("64Mi"), - }, - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceCPU: resource.MustParse("30m"), - corev1.ResourceMemory: resource.MustParse("64Mi"), - }, - }, + Resources: t.overrideHostsResources, VolumeMounts: []corev1.VolumeMount{ { MountPath: "/hosts", diff --git a/pkg/controllers/resources/pods/translate/translator.go b/pkg/controllers/resources/pods/translate/translator.go index b9a424af5..4ef80f181 100644 --- a/pkg/controllers/resources/pods/translate/translator.go +++ b/pkg/controllers/resources/pods/translate/translator.go @@ -20,6 +20,7 @@ import ( appsv1 "k8s.io/api/apps/v1" authenticationv1 "k8s.io/api/authentication/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -68,6 +69,20 @@ func NewTranslator(ctx *synccontext.RegisterContext, eventRecorder record.EventR virtualLogsPath := path.Join(virtualPath, "log") virtualKubeletPath := path.Join(virtualPath, "kubelet") + // parse resource requirements + resourceRequirements := corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{}, + Requests: map[corev1.ResourceName]resource.Quantity{}, + } + resourceRequirements.Limits, err = parseResources(ctx.Config.Sync.ToHost.Pods.RewriteHosts.InitContainer.Resources.Limits) + if err != nil { + return nil, fmt.Errorf("parse init container resource limits: %w", err) + } + resourceRequirements.Requests, err = parseResources(ctx.Config.Sync.ToHost.Pods.RewriteHosts.InitContainer.Resources.Requests) + if err != nil { + return nil, fmt.Errorf("parse init container resource requests: %w", err) + } + return &translator{ vClientConfig: ctx.VirtualManager.GetConfig(), vClient: ctx.VirtualManager.GetClient(), @@ -84,12 +99,15 @@ func NewTranslator(ctx *synccontext.RegisterContext, eventRecorder record.EventR serviceAccountSecretsEnabled: ctx.Config.Sync.ToHost.Pods.UseSecretsForSATokens, clusterDomain: ctx.Config.Networking.Advanced.ClusterDomain, serviceAccount: ctx.Config.ControlPlane.Advanced.WorkloadServiceAccount.Name, - overrideHosts: ctx.Config.Sync.ToHost.Pods.RewriteHosts.Enabled, - overrideHostsImage: ctx.Config.Sync.ToHost.Pods.RewriteHosts.InitContainerImage, - serviceAccountsEnabled: ctx.Config.Sync.ToHost.ServiceAccounts.Enabled, - priorityClassesEnabled: ctx.Config.Sync.ToHost.PriorityClasses.Enabled, - enableScheduler: ctx.Config.ControlPlane.Advanced.VirtualScheduler.Enabled, - syncedLabels: ctx.Config.Experimental.SyncSettings.SyncLabels, + + overrideHosts: ctx.Config.Sync.ToHost.Pods.RewriteHosts.Enabled, + overrideHostsImage: ctx.Config.Sync.ToHost.Pods.RewriteHosts.InitContainer.Image, + overrideHostsResources: resourceRequirements, + + serviceAccountsEnabled: ctx.Config.Sync.ToHost.ServiceAccounts.Enabled, + priorityClassesEnabled: ctx.Config.Sync.ToHost.PriorityClasses.Enabled, + enableScheduler: ctx.Config.ControlPlane.Advanced.VirtualScheduler.Enabled, + syncedLabels: ctx.Config.Experimental.SyncSettings.SyncLabels, mountPhysicalHostPaths: ctx.Config.ControlPlane.HostPathMapper.Enabled && !ctx.Config.ControlPlane.HostPathMapper.Central, @@ -120,6 +138,7 @@ type translator struct { serviceAccount string overrideHosts bool overrideHostsImage string + overrideHostsResources corev1.ResourceRequirements priorityClassesEnabled bool enableScheduler bool syncedLabels []string @@ -264,7 +283,7 @@ func (t *translator) Translate(ctx context.Context, vPod *corev1.Pod, services [ // would be deployed in a non virtual kubernetes cluster if pPod.Spec.Subdomain != "" { if t.overrideHosts { - rewritePodHostnameFQDN(pPod, t.defaultImageRegistry, t.overrideHostsImage, pPod.Spec.Hostname, pPod.Spec.Hostname, pPod.Spec.Hostname+"."+pPod.Spec.Subdomain+"."+vPod.Namespace+".svc."+t.clusterDomain) + t.rewritePodHostnameFQDN(pPod, pPod.Spec.Hostname, pPod.Spec.Hostname, pPod.Spec.Hostname+"."+pPod.Spec.Subdomain+"."+vPod.Namespace+".svc."+t.clusterDomain) } pPod.Spec.Subdomain = "" @@ -855,3 +874,22 @@ func ServicesToEnvironmentVariables(enableServiceLinks *bool, services []*corev1 } return retMap } + +func parseResources(resources map[string]interface{}) (corev1.ResourceList, error) { + resourceList := corev1.ResourceList{} + for key, value := range resources { + strValue, ok := value.(string) + if !ok { + return nil, fmt.Errorf("resource value of %s is not a string", key) + } + + parsedQuantity, err := resource.ParseQuantity(strValue) + if err != nil { + return nil, fmt.Errorf("error parsing resource value %s (%s): %w", key, strValue, err) + } + + resourceList[corev1.ResourceName(key)] = parsedQuantity + } + + return resourceList, nil +}