From 9e493bc6424701e55e7533e8e75f4efdae63d522 Mon Sep 17 00:00:00 2001 From: Yolean k8s-qa Date: Tue, 12 May 2026 07:56:00 +0000 Subject: [PATCH] feat(provision/envoygateway): configurable controller + proxy resource requests Upstream Envoy Gateway ships with 100m/256Mi controller and 100m/512Mi data-plane proxy requests, which oversubscribe a 2GB-RAM appliance node and hurt single-user dev clusters that never push the proxy past idle. Two new config knobs under gateway.resources.{controller,proxy}: each takes a Kubernetes-style CPU + Memory request string. applyGatewayDefaults fills 10m/64Mi controller and 10m/128Mi proxy when EG is enabled, so a fresh provision shrinks the bundle without any operator action. Skip:true leaves Resources zero so a rendered config still distinguishes "operator opted out" from "defaults applied". Mechanism per pod: - Controller: a partial Deployment manifest (apps/v1, name=envoy-gateway in envoy-gateway-system, container name=envoy-gateway with resources.requests) applied via server-side apply with field-manager=y-cluster. SSA owns only requests; upstream limits (currently 1Gi memory, no CPU cap), image, env, replicas stay with their original owners. - Proxy: an EnvoyProxy CR (gateway.envoyproxy.io/v1alpha1, name=y-cluster in envoy-gateway-system) carrying spec.provider.kubernetes.envoyDeployment.container. resources.requests. The default GatewayClass gains a parametersRef pointing at it so every Gateway under that class inherits the shape with no per-Gateway boilerplate. Limits are not modelled in the config: y-cluster's stance is that bursty idle workloads stay healthier under upstream's existing limits than under tighter ones we'd have to guess. Operators who need limits can apply their own EnvoyProxy CR on top -- EG honours the latest reconciled spec. Wire-through: docker, qemu, multipass providers all pass cfg.Gateway.Resources.{Controller,Proxy} to envoygateway.Install. Schema files regenerated. --- pkg/provision/config/common.go | 47 +++++++++++- pkg/provision/config/gateway_test.go | 53 ++++++++++++++ pkg/provision/docker/docker.go | 12 ++-- pkg/provision/envoygateway/embed.go | 84 +++++++++++++++++++++- pkg/provision/envoygateway/embed_test.go | 75 ++++++++++++++++++- pkg/provision/envoygateway/install.go | 52 +++++++++++++- pkg/provision/multipass/multipass.go | 10 ++- pkg/provision/qemu/qemu.go | 12 ++-- pkg/provision/schema/common.schema.json | 32 +++++++++ pkg/provision/schema/docker.schema.json | 32 +++++++++ pkg/provision/schema/multipass.schema.json | 32 +++++++++ pkg/provision/schema/qemu.schema.json | 32 +++++++++ 12 files changed, 452 insertions(+), 21 deletions(-) diff --git a/pkg/provision/config/common.go b/pkg/provision/config/common.go index 4c1d8b6..c6df467 100644 --- a/pkg/provision/config/common.go +++ b/pkg/provision/config/common.go @@ -146,11 +146,40 @@ type GatewayConfig struct { // // Ignored when Skip is true. ClassName string `yaml:"className,omitempty" json:"className,omitempty" jsonschema:"default=y-cluster,description=GatewayClass name. Consumer Gateway resources reference this via gatewayClassName. Ignored when skip is true."` + + // Resources tunes resource requests on the EG controller pod + // and the per-Gateway envoy proxy pod. Defaults target a + // single-user/dev cluster; bump for production-shaped load. + // Upstream defaults are 100m/256Mi (controller) and + // 100m/512Mi (proxy), which oversubscribe a 2GB-RAM + // appliance node. + Resources GatewayResources `yaml:"resources,omitempty" json:"resources,omitempty" jsonschema:"description=Resource requests for the bundled EG install. Defaults: controller 10m/64Mi, proxy 10m/128Mi. Limits are left as upstream sets them."` +} + +// GatewayResources groups the two pods whose resource requests +// y-cluster manages: the EG controller (Deployment in +// envoy-gateway-system) and the per-Gateway envoy proxy +// (spawned by EG via the EnvoyProxy CR our default GatewayClass +// references). +type GatewayResources struct { + Controller ResourceRequests `yaml:"controller,omitempty" json:"controller,omitempty" jsonschema:"description=EG controller container requests. Default cpu 10m, memory 64Mi."` + Proxy ResourceRequests `yaml:"proxy,omitempty" json:"proxy,omitempty" jsonschema:"description=Per-Gateway envoy proxy container requests. Default cpu 10m, memory 128Mi."` +} + +// ResourceRequests is a minimal Kubernetes-resource-style +// shape covering CPU + memory requests only. Limits are not +// modelled here on purpose: y-cluster's stance is that bursty +// idle workloads are healthier under upstream's existing +// limits than under tighter ones we'd have to guess at. +type ResourceRequests struct { + CPU string `yaml:"cpu,omitempty" json:"cpu,omitempty" jsonschema:"description=CPU request in Kubernetes notation (e.g. 10m, 0.5, 1)."` + Memory string `yaml:"memory,omitempty" json:"memory,omitempty" jsonschema:"description=Memory request in Kubernetes notation (e.g. 64Mi, 256Mi, 1Gi)."` } -// applyGatewayDefaults fills ClassName when the install is -// enabled. When Skip is set, ClassName is left as the user -// supplied it so debug logs make the operator's intent obvious. +// applyGatewayDefaults fills ClassName + Resources when the +// install is enabled. When Skip is set everything is left as +// the user supplied it so debug logs make the operator's +// intent obvious. func (c *CommonConfig) applyGatewayDefaults() { if c.Gateway.Skip { return @@ -158,6 +187,18 @@ func (c *CommonConfig) applyGatewayDefaults() { if c.Gateway.ClassName == "" { c.Gateway.ClassName = "y-cluster" } + if c.Gateway.Resources.Controller.CPU == "" { + c.Gateway.Resources.Controller.CPU = "10m" + } + if c.Gateway.Resources.Controller.Memory == "" { + c.Gateway.Resources.Controller.Memory = "64Mi" + } + if c.Gateway.Resources.Proxy.CPU == "" { + c.Gateway.Resources.Proxy.CPU = "10m" + } + if c.Gateway.Resources.Proxy.Memory == "" { + c.Gateway.Resources.Proxy.Memory = "128Mi" + } } // EffectiveGatewayClassName returns the GatewayClass name the diff --git a/pkg/provision/config/gateway_test.go b/pkg/provision/config/gateway_test.go index 4269113..213ed23 100644 --- a/pkg/provision/config/gateway_test.go +++ b/pkg/provision/config/gateway_test.go @@ -36,6 +36,59 @@ func TestGateway_SkipLeavesClassNameAlone(t *testing.T) { if c.Gateway.ClassName != "" { t.Fatalf("Skip:true should leave ClassName empty, got %q", c.Gateway.ClassName) } + // Skip also keeps Resources empty so a downstream consumer + // reading the rendered config can tell "operator didn't ask + // for an install" from "operator asked, defaults applied". + if c.Gateway.Resources != (GatewayResources{}) { + t.Fatalf("Skip:true should leave Resources zero, got %+v", c.Gateway.Resources) + } +} + +// TestGateway_DefaultResources pins the lower-than-upstream +// defaults that single-user dev clusters benefit from. Upstream +// EG ships 100m/256Mi controller and 100m/512Mi proxy, which +// oversubscribe a 2GB appliance node. Changing these values is +// a contract change for anyone running on those budgets. +func TestGateway_DefaultResources(t *testing.T) { + c := &CommonConfig{} + c.applyCommonDefaults() + if c.Gateway.Resources.Controller.CPU != "10m" { + t.Errorf("Controller.CPU: got %q, want 10m", c.Gateway.Resources.Controller.CPU) + } + if c.Gateway.Resources.Controller.Memory != "64Mi" { + t.Errorf("Controller.Memory: got %q, want 64Mi", c.Gateway.Resources.Controller.Memory) + } + if c.Gateway.Resources.Proxy.CPU != "10m" { + t.Errorf("Proxy.CPU: got %q, want 10m", c.Gateway.Resources.Proxy.CPU) + } + if c.Gateway.Resources.Proxy.Memory != "128Mi" { + t.Errorf("Proxy.Memory: got %q, want 128Mi", c.Gateway.Resources.Proxy.Memory) + } +} + +// TestGateway_PreservesExplicitResources: an operator setting +// any subset of fields keeps their explicit values; only +// unset fields default. +func TestGateway_PreservesExplicitResources(t *testing.T) { + c := &CommonConfig{Gateway: GatewayConfig{ + Resources: GatewayResources{ + Controller: ResourceRequests{CPU: "200m"}, + Proxy: ResourceRequests{Memory: "1Gi"}, + }, + }} + c.applyCommonDefaults() + if c.Gateway.Resources.Controller.CPU != "200m" { + t.Errorf("explicit Controller.CPU lost: %q", c.Gateway.Resources.Controller.CPU) + } + if c.Gateway.Resources.Controller.Memory != "64Mi" { + t.Errorf("unset Controller.Memory should default, got %q", c.Gateway.Resources.Controller.Memory) + } + if c.Gateway.Resources.Proxy.CPU != "10m" { + t.Errorf("unset Proxy.CPU should default, got %q", c.Gateway.Resources.Proxy.CPU) + } + if c.Gateway.Resources.Proxy.Memory != "1Gi" { + t.Errorf("explicit Proxy.Memory lost: %q", c.Gateway.Resources.Proxy.Memory) + } } // TestEffectiveGatewayClassName covers the helper Provision uses diff --git a/pkg/provision/docker/docker.go b/pkg/provision/docker/docker.go index 07a5860..b25269d 100644 --- a/pkg/provision/docker/docker.go +++ b/pkg/provision/docker/docker.go @@ -280,10 +280,14 @@ func Provision(ctx context.Context, cfg config.DockerConfig, logger *zap.Logger) logger.Info("envoy gateway install skipped (gateway.skip)") } else { if err := envoygateway.Install(ctx, envoygateway.Options{ - ContextName: cfg.Context, - GatewayClassName: cfg.Gateway.ClassName, - DNSHintIP: cfg.HostRoutableIP(), - Logger: logger, + ContextName: cfg.Context, + GatewayClassName: cfg.Gateway.ClassName, + DNSHintIP: cfg.HostRoutableIP(), + Logger: logger, + ControllerCPURequest: cfg.Gateway.Resources.Controller.CPU, + ControllerMemRequest: cfg.Gateway.Resources.Controller.Memory, + ProxyCPURequest: cfg.Gateway.Resources.Proxy.CPU, + ProxyMemRequest: cfg.Gateway.Resources.Proxy.Memory, }); err != nil { return nil, fmt.Errorf("install envoy gateway: %w", err) } diff --git a/pkg/provision/envoygateway/embed.go b/pkg/provision/envoygateway/embed.go index 6f129a0..c5d136e 100644 --- a/pkg/provision/envoygateway/embed.go +++ b/pkg/provision/envoygateway/embed.go @@ -25,15 +25,27 @@ const DNSHintIPAnnotation = "yolean.se/dns-hint-ip" // the configured class name. dnsHintIP is the value the provisioner // stamps under the DNSHintIPAnnotation; empty string omits the // annotations block entirely so an absent hint is distinguishable -// from a present-but-empty one. +// from a present-but-empty one. envoyProxyName, when non-empty, +// adds a parametersRef pointing at the EnvoyProxy CR of that name +// in the envoy-gateway-system namespace -- the upstream-blessed +// extension point for tuning the data-plane proxy's resources. // // Pure function so unit tests can pin the rendered shape against // a known-good baseline. -func GatewayClassYAML(name, dnsHintIP string) []byte { +func GatewayClassYAML(name, dnsHintIP, envoyProxyName string) []byte { var annotations string if dnsHintIP != "" { annotations = fmt.Sprintf(" annotations:\n %s: %s\n", DNSHintIPAnnotation, dnsHintIP) } + var parametersRef string + if envoyProxyName != "" { + parametersRef = fmt.Sprintf(` parametersRef: + group: gateway.envoyproxy.io + kind: EnvoyProxy + name: %s + namespace: %s +`, envoyProxyName, Namespace) + } return []byte(fmt.Sprintf(`--- # y-cluster default GatewayClass for the bundled Envoy Gateway # install. Consumer Gateway resources reference this name via @@ -47,6 +59,72 @@ metadata: name: %s %sspec: controllerName: %s -`, name, name, annotations, EGControllerName)) +%s`, name, name, annotations, EGControllerName, parametersRef)) +} + +// EnvoyProxyName is the metadata.name of the EnvoyProxy CR +// y-cluster applies in the envoy-gateway-system namespace. The +// default GatewayClass references it via parametersRef so +// Gateways under that class inherit the tuned resources without +// any per-Gateway boilerplate. +const EnvoyProxyName = "y-cluster" + +// EnvoyProxyYAML renders the EnvoyProxy CR that tunes the +// data-plane envoy proxy pod's resource requests. cpuRequest / +// memRequest land under spec.provider.kubernetes.envoyDeployment +// .container.resources.requests. Limits are left for EG's +// defaults (and the cluster's LimitRange, if any). +// +// The CR lives in envoy-gateway-system because that's the only +// namespace EG looks at for parametersRef of GatewayClass. +// +// Pure function for unit-test pinning. +func EnvoyProxyYAML(cpuRequest, memRequest string) []byte { + return []byte(fmt.Sprintf(`--- +# y-cluster's tuning for the per-Gateway envoy proxy pod. +# Referenced by the GatewayClass via parametersRef. +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: %s + namespace: %s +spec: + provider: + type: Kubernetes + kubernetes: + envoyDeployment: + container: + resources: + requests: + cpu: %s + memory: %s +`, EnvoyProxyName, Namespace, cpuRequest, memRequest)) +} + +// ControllerResourcesYAML is a partial Deployment manifest +// declaring ownership over the envoy-gateway controller +// container's resources.requests under server-side apply. The +// apply uses field-manager y-cluster; existing fields (image, +// env, replicas, container args) stay with their original +// owners. Limits are not declared -- intentional, so the +// upstream limit (currently 1Gi memory, no CPU cap) stays in +// effect. +func ControllerResourcesYAML(cpuRequest, memRequest string) []byte { + return []byte(fmt.Sprintf(`--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %s + namespace: %s +spec: + template: + spec: + containers: + - name: envoy-gateway + resources: + requests: + cpu: %s + memory: %s +`, DeploymentName, Namespace, cpuRequest, memRequest)) } diff --git a/pkg/provision/envoygateway/embed_test.go b/pkg/provision/envoygateway/embed_test.go index 81cd918..ed1c41f 100644 --- a/pkg/provision/envoygateway/embed_test.go +++ b/pkg/provision/envoygateway/embed_test.go @@ -10,7 +10,7 @@ import ( // entirely, so an absent hint is distinguishable from // "annotation present with empty value". func TestGatewayClassYAML_NoHintIP(t *testing.T) { - got := string(GatewayClassYAML("y-cluster", "")) + got := string(GatewayClassYAML("y-cluster", "", "")) if strings.Contains(got, "annotations") { t.Fatalf("expected no annotations block:\n%s", got) } @@ -23,13 +23,16 @@ func TestGatewayClassYAML_NoHintIP(t *testing.T) { if !strings.Contains(got, "controllerName: "+EGControllerName) { t.Fatalf("missing controller name:\n%s", got) } + if strings.Contains(got, "parametersRef") { + t.Fatalf("empty envoyProxyName should omit parametersRef:\n%s", got) + } } // TestGatewayClassYAML_WithHintIP guards the qemu/docker // host-loopback shape: the dnsHintIP value lands as a single // annotation under the GatewayClass metadata. func TestGatewayClassYAML_WithHintIP(t *testing.T) { - got := string(GatewayClassYAML("y-cluster", "127.0.0.1")) + got := string(GatewayClassYAML("y-cluster", "127.0.0.1", "")) if !strings.Contains(got, "annotations:") { t.Fatalf("missing annotations block:\n%s", got) } @@ -51,7 +54,7 @@ func TestGatewayClassYAML_WithHintIP(t *testing.T) { // both metadata.name and the doc comment. The comment header line // is part of the contract -- consumers grep for it during debug. func TestGatewayClassYAML_RespectsCustomName(t *testing.T) { - got := string(GatewayClassYAML("eg", "")) + got := string(GatewayClassYAML("eg", "", "")) if !strings.Contains(got, "name: eg") { t.Fatalf("missing custom name:\n%s", got) } @@ -59,3 +62,69 @@ func TestGatewayClassYAML_RespectsCustomName(t *testing.T) { t.Fatalf("comment should reference the configured name:\n%s", got) } } + +// TestGatewayClassYAML_WithEnvoyProxyRef pins the parametersRef +// shape EG expects: group / kind / name / namespace under +// spec.parametersRef, namespace fixed to the EG namespace. +func TestGatewayClassYAML_WithEnvoyProxyRef(t *testing.T) { + got := string(GatewayClassYAML("y-cluster", "", EnvoyProxyName)) + for _, want := range []string{ + "parametersRef:", + "group: gateway.envoyproxy.io", + "kind: EnvoyProxy", + "name: " + EnvoyProxyName, + "namespace: " + Namespace, + } { + if !strings.Contains(got, want) { + t.Errorf("missing %q:\n%s", want, got) + } + } +} + +// TestEnvoyProxyYAML_ShapesResources pins the EnvoyProxy CR +// fields y-cluster actually owns: requests under provider. +// kubernetes.envoyDeployment.container.resources. +func TestEnvoyProxyYAML_ShapesResources(t *testing.T) { + got := string(EnvoyProxyYAML("10m", "128Mi")) + for _, want := range []string{ + "apiVersion: gateway.envoyproxy.io/v1alpha1", + "kind: EnvoyProxy", + "name: " + EnvoyProxyName, + "namespace: " + Namespace, + "cpu: 10m", + "memory: 128Mi", + } { + if !strings.Contains(got, want) { + t.Errorf("missing %q:\n%s", want, got) + } + } + if strings.Contains(got, "limits:") { + t.Errorf("CR should declare requests only, not limits:\n%s", got) + } +} + +// TestControllerResourcesYAML_RequestsOnly pins the partial +// Deployment shape: requests-only, container matched by name so +// SSA targets the right container, no limits/image/env claimed +// (so upstream owners keep them). +func TestControllerResourcesYAML_RequestsOnly(t *testing.T) { + got := string(ControllerResourcesYAML("10m", "64Mi")) + for _, want := range []string{ + "kind: Deployment", + "name: " + DeploymentName, + "namespace: " + Namespace, + "- name: envoy-gateway", + "cpu: 10m", + "memory: 64Mi", + } { + if !strings.Contains(got, want) { + t.Errorf("missing %q:\n%s", want, got) + } + } + if strings.Contains(got, "limits:") { + t.Errorf("patch should declare requests only:\n%s", got) + } + if strings.Contains(got, "image:") { + t.Errorf("patch must not claim image (would fight upstream owner):\n%s", got) + } +} diff --git a/pkg/provision/envoygateway/install.go b/pkg/provision/envoygateway/install.go index 23519dd..21a4a6f 100644 --- a/pkg/provision/envoygateway/install.go +++ b/pkg/provision/envoygateway/install.go @@ -61,6 +61,20 @@ type Options struct { // // Ignored when GatewayClassName is empty (no GatewayClass apply). DNSHintIP string + + // ControllerCPURequest / ControllerMemRequest set the + // controller container's resources.requests via SSA. Empty + // strings mean "leave upstream's defaults"; the provisioner + // fills these from CommonConfig.Gateway.Resources.Controller. + ControllerCPURequest string + ControllerMemRequest string + + // ProxyCPURequest / ProxyMemRequest land on the EnvoyProxy + // CR the default GatewayClass references via parametersRef. + // When both are empty, no EnvoyProxy CR is applied and the + // GatewayClass has no parametersRef -- EG uses its defaults. + ProxyCPURequest string + ProxyMemRequest string } // Install resolves the per-version install.yaml from cache @@ -124,6 +138,22 @@ func Install(ctx context.Context, opts Options) error { return fmt.Errorf("apply install.yaml: %w", err) } + // Patch the controller container's resource requests before + // the rollout wait so the wait sees the ReplicaSet we shaped. + // Empty strings here mean "leave upstream alone" -- a test + // path or a config that explicitly wants stock requests. + if opts.ControllerCPURequest != "" || opts.ControllerMemRequest != "" { + logger.Info("patching envoy-gateway controller resources", + zap.String("cpu", opts.ControllerCPURequest), + zap.String("memory", opts.ControllerMemRequest), + ) + if err := kubectlApplyStdin(ctx, opts.ContextName, + ControllerResourcesYAML(opts.ControllerCPURequest, opts.ControllerMemRequest), + ); err != nil { + return fmt.Errorf("apply controller resources: %w", err) + } + } + if opts.ReadyTimeout >= 0 { timeout := opts.ReadyTimeout if timeout == 0 { @@ -139,12 +169,32 @@ func Install(ctx context.Context, opts Options) error { } } + // Apply EnvoyProxy first so the GatewayClass.parametersRef + // resolves on first reconcile. When proxy resources aren't + // configured we skip the CR and leave the GatewayClass + // parametersRef-less -- EG uses its built-in defaults. + envoyProxyName := "" + if opts.ProxyCPURequest != "" || opts.ProxyMemRequest != "" { + envoyProxyName = EnvoyProxyName + logger.Info("applying EnvoyProxy CR", + zap.String("name", envoyProxyName), + zap.String("cpu", opts.ProxyCPURequest), + zap.String("memory", opts.ProxyMemRequest), + ) + if err := kubectlApplyStdin(ctx, opts.ContextName, + EnvoyProxyYAML(opts.ProxyCPURequest, opts.ProxyMemRequest), + ); err != nil { + return fmt.Errorf("apply EnvoyProxy: %w", err) + } + } + if opts.GatewayClassName != "" { logger.Info("applying default GatewayClass", zap.String("name", opts.GatewayClassName), zap.String("dnsHintIP", opts.DNSHintIP), + zap.String("envoyProxyRef", envoyProxyName), ) - if err := kubectlApplyStdin(ctx, opts.ContextName, GatewayClassYAML(opts.GatewayClassName, opts.DNSHintIP)); err != nil { + if err := kubectlApplyStdin(ctx, opts.ContextName, GatewayClassYAML(opts.GatewayClassName, opts.DNSHintIP, envoyProxyName)); err != nil { return fmt.Errorf("apply GatewayClass: %w", err) } } diff --git a/pkg/provision/multipass/multipass.go b/pkg/provision/multipass/multipass.go index c247cd5..4dd6154 100644 --- a/pkg/provision/multipass/multipass.go +++ b/pkg/provision/multipass/multipass.go @@ -206,9 +206,13 @@ func Provision(ctx context.Context, cfg Config, logger *zap.Logger) (*Cluster, e // non-tunnel-NAT path the CHANGE_REQUEST_HINT_IP migration // names). if err := envoygateway.Install(ctx, envoygateway.Options{ - ContextName: cfg.Context, - GatewayClassName: cfg.Gateway.ClassName, - Logger: logger, + ContextName: cfg.Context, + GatewayClassName: cfg.Gateway.ClassName, + Logger: logger, + ControllerCPURequest: cfg.Gateway.Resources.Controller.CPU, + ControllerMemRequest: cfg.Gateway.Resources.Controller.Memory, + ProxyCPURequest: cfg.Gateway.Resources.Proxy.CPU, + ProxyMemRequest: cfg.Gateway.Resources.Proxy.Memory, }); err != nil { return nil, fmt.Errorf("install envoy gateway: %w", err) } diff --git a/pkg/provision/qemu/qemu.go b/pkg/provision/qemu/qemu.go index 4f33b2f..aeabdfa 100644 --- a/pkg/provision/qemu/qemu.go +++ b/pkg/provision/qemu/qemu.go @@ -345,10 +345,14 @@ func Provision(ctx context.Context, cfg Config, logger *zap.Logger) (*Cluster, e logger.Info("envoy gateway install skipped (gateway.skip)") } else { if err := envoygateway.Install(ctx, envoygateway.Options{ - ContextName: cfg.Context, - GatewayClassName: cfg.Gateway.ClassName, - DNSHintIP: cfg.hostRoutableIP(), - Logger: logger, + ContextName: cfg.Context, + GatewayClassName: cfg.Gateway.ClassName, + DNSHintIP: cfg.hostRoutableIP(), + Logger: logger, + ControllerCPURequest: cfg.Gateway.Resources.Controller.CPU, + ControllerMemRequest: cfg.Gateway.Resources.Controller.Memory, + ProxyCPURequest: cfg.Gateway.Resources.Proxy.CPU, + ProxyMemRequest: cfg.Gateway.Resources.Proxy.Memory, }); err != nil { return nil, fmt.Errorf("install envoy gateway: %w", err) } diff --git a/pkg/provision/schema/common.schema.json b/pkg/provision/schema/common.schema.json index f6c4511..8b8d0f2 100644 --- a/pkg/provision/schema/common.schema.json +++ b/pkg/provision/schema/common.schema.json @@ -66,6 +66,10 @@ "description": "GatewayClass name. Consumer Gateway resources reference this via gatewayClassName. Ignored when skip is true.", "type": "string" }, + "resources": { + "$ref": "#/$defs/GatewayResources", + "description": "Resource requests for the bundled EG install. Defaults: controller 10m/64Mi" + }, "skip": { "description": "If true", "type": "boolean" @@ -73,6 +77,20 @@ }, "type": "object" }, + "GatewayResources": { + "additionalProperties": false, + "properties": { + "controller": { + "$ref": "#/$defs/ResourceRequests", + "description": "EG controller container requests. Default cpu 10m" + }, + "proxy": { + "$ref": "#/$defs/ResourceRequests", + "description": "Per-Gateway envoy proxy container requests. Default cpu 10m" + } + }, + "type": "object" + }, "K3sConfig": { "additionalProperties": false, "properties": { @@ -209,6 +227,20 @@ }, "type": "object" }, + "ResourceRequests": { + "additionalProperties": false, + "properties": { + "cpu": { + "description": "CPU request in Kubernetes notation (e.g. 10m", + "type": "string" + }, + "memory": { + "description": "Memory request in Kubernetes notation (e.g. 64Mi", + "type": "string" + } + }, + "type": "object" + }, "StorageConfig": { "additionalProperties": false, "properties": { diff --git a/pkg/provision/schema/docker.schema.json b/pkg/provision/schema/docker.schema.json index 700d8c8..796b644 100644 --- a/pkg/provision/schema/docker.schema.json +++ b/pkg/provision/schema/docker.schema.json @@ -65,6 +65,10 @@ "description": "GatewayClass name. Consumer Gateway resources reference this via gatewayClassName. Ignored when skip is true.", "type": "string" }, + "resources": { + "$ref": "#/$defs/GatewayResources", + "description": "Resource requests for the bundled EG install. Defaults: controller 10m/64Mi" + }, "skip": { "description": "If true", "type": "boolean" @@ -72,6 +76,20 @@ }, "type": "object" }, + "GatewayResources": { + "additionalProperties": false, + "properties": { + "controller": { + "$ref": "#/$defs/ResourceRequests", + "description": "EG controller container requests. Default cpu 10m" + }, + "proxy": { + "$ref": "#/$defs/ResourceRequests", + "description": "Per-Gateway envoy proxy container requests. Default cpu 10m" + } + }, + "type": "object" + }, "K3sConfig": { "additionalProperties": false, "properties": { @@ -208,6 +226,20 @@ }, "type": "object" }, + "ResourceRequests": { + "additionalProperties": false, + "properties": { + "cpu": { + "description": "CPU request in Kubernetes notation (e.g. 10m", + "type": "string" + }, + "memory": { + "description": "Memory request in Kubernetes notation (e.g. 64Mi", + "type": "string" + } + }, + "type": "object" + }, "StorageConfig": { "additionalProperties": false, "properties": { diff --git a/pkg/provision/schema/multipass.schema.json b/pkg/provision/schema/multipass.schema.json index 61c3a72..df86eba 100644 --- a/pkg/provision/schema/multipass.schema.json +++ b/pkg/provision/schema/multipass.schema.json @@ -8,6 +8,10 @@ "description": "GatewayClass name. Consumer Gateway resources reference this via gatewayClassName. Ignored when skip is true.", "type": "string" }, + "resources": { + "$ref": "#/$defs/GatewayResources", + "description": "Resource requests for the bundled EG install. Defaults: controller 10m/64Mi" + }, "skip": { "description": "If true", "type": "boolean" @@ -15,6 +19,20 @@ }, "type": "object" }, + "GatewayResources": { + "additionalProperties": false, + "properties": { + "controller": { + "$ref": "#/$defs/ResourceRequests", + "description": "EG controller container requests. Default cpu 10m" + }, + "proxy": { + "$ref": "#/$defs/ResourceRequests", + "description": "Per-Gateway envoy proxy container requests. Default cpu 10m" + } + }, + "type": "object" + }, "K3sConfig": { "additionalProperties": false, "properties": { @@ -213,6 +231,20 @@ }, "type": "object" }, + "ResourceRequests": { + "additionalProperties": false, + "properties": { + "cpu": { + "description": "CPU request in Kubernetes notation (e.g. 10m", + "type": "string" + }, + "memory": { + "description": "Memory request in Kubernetes notation (e.g. 64Mi", + "type": "string" + } + }, + "type": "object" + }, "StorageConfig": { "additionalProperties": false, "properties": { diff --git a/pkg/provision/schema/qemu.schema.json b/pkg/provision/schema/qemu.schema.json index 909db33..cd99139 100644 --- a/pkg/provision/schema/qemu.schema.json +++ b/pkg/provision/schema/qemu.schema.json @@ -8,6 +8,10 @@ "description": "GatewayClass name. Consumer Gateway resources reference this via gatewayClassName. Ignored when skip is true.", "type": "string" }, + "resources": { + "$ref": "#/$defs/GatewayResources", + "description": "Resource requests for the bundled EG install. Defaults: controller 10m/64Mi" + }, "skip": { "description": "If true", "type": "boolean" @@ -15,6 +19,20 @@ }, "type": "object" }, + "GatewayResources": { + "additionalProperties": false, + "properties": { + "controller": { + "$ref": "#/$defs/ResourceRequests", + "description": "EG controller container requests. Default cpu 10m" + }, + "proxy": { + "$ref": "#/$defs/ResourceRequests", + "description": "Per-Gateway envoy proxy container requests. Default cpu 10m" + } + }, + "type": "object" + }, "K3sConfig": { "additionalProperties": false, "properties": { @@ -222,6 +240,20 @@ }, "type": "object" }, + "ResourceRequests": { + "additionalProperties": false, + "properties": { + "cpu": { + "description": "CPU request in Kubernetes notation (e.g. 10m", + "type": "string" + }, + "memory": { + "description": "Memory request in Kubernetes notation (e.g. 64Mi", + "type": "string" + } + }, + "type": "object" + }, "StorageConfig": { "additionalProperties": false, "properties": {