Skip to content

Commit 290c27e

Browse files
author
Mattias Andersson
committed
Add possibility to add resource request/limit to initContainer
This commit enables user to configure resource request and limit for cpu and memory on the "setup-ca-certs" initContainer. By not being able to set resources it blocks creation of the Pod if a namespace has a ResourceQuota in place. Signed-off-by: Mattias Andersson <[email protected]>
1 parent 6cdee4a commit 290c27e

File tree

2 files changed

+160
-3
lines changed

2 files changed

+160
-3
lines changed

pkg/certinjectionwebhook/admission_controller.go

+57-1
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@ import (
88
"context"
99
"encoding/json"
1010
"fmt"
11-
1211
"github.com/pkg/errors"
1312
admissionv1 "k8s.io/api/admission/v1"
1413
corev1 "k8s.io/api/core/v1"
1514
apierrors "k8s.io/apimachinery/pkg/api/errors"
15+
"k8s.io/apimachinery/pkg/api/resource"
1616
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1717
"k8s.io/apimachinery/pkg/runtime"
1818
"k8s.io/apimachinery/pkg/runtime/serializer"
1919
"knative.dev/pkg/apis"
2020
"knative.dev/pkg/apis/duck"
2121
"knative.dev/pkg/logging"
2222
"knative.dev/pkg/webhook"
23+
"os"
2324

2425
"github.com/vmware-tanzu/cert-injection-webhook/pkg/certs"
2526
)
@@ -187,6 +188,18 @@ func (ac *admissionController) SetEnvVars(ctx context.Context, obj *corev1.Pod)
187188
}
188189
}
189190

191+
func ParseResource(envVar string) (resource.Quantity, error) {
192+
value, found := os.LookupEnv(envVar)
193+
if !found {
194+
return resource.Quantity{}, nil // Return an empty Quantity if env var is missing
195+
}
196+
qty, err := resource.ParseQuantity(value)
197+
if err != nil {
198+
return resource.Quantity{}, fmt.Errorf("failed to parse %s: %w", envVar, err)
199+
}
200+
return qty, nil
201+
}
202+
190203
func (ac *admissionController) SetCaCerts(ctx context.Context, obj *corev1.Pod) {
191204
if ac.caCertsData == "" {
192205
return
@@ -224,6 +237,44 @@ func (ac *admissionController) SetCaCerts(ctx context.Context, obj *corev1.Pod)
224237
})
225238
}
226239

240+
var resources corev1.ResourceRequirements
241+
242+
if cpuRequest, err := ParseResource("INIT_CONTAINER_CPU_REQUEST"); err == nil {
243+
if resources.Requests == nil {
244+
resources.Requests = corev1.ResourceList{}
245+
}
246+
resources.Requests[corev1.ResourceCPU] = cpuRequest
247+
} else {
248+
fmt.Printf("Warning: %v\n", err)
249+
}
250+
251+
if memoryRequest, err := ParseResource("INIT_CONTAINER_MEMORY_REQUEST"); err == nil {
252+
if resources.Requests == nil {
253+
resources.Requests = corev1.ResourceList{}
254+
}
255+
resources.Requests[corev1.ResourceMemory] = memoryRequest
256+
} else {
257+
fmt.Printf("Warning: %v\n", err)
258+
}
259+
260+
if cpuLimit, err := ParseResource("INIT_CONTAINER_CPU_LIMIT"); err == nil {
261+
if resources.Limits == nil {
262+
resources.Limits = corev1.ResourceList{}
263+
}
264+
resources.Limits[corev1.ResourceCPU] = cpuLimit
265+
} else {
266+
fmt.Printf("Warning: %v\n", err)
267+
}
268+
269+
if memoryLimit, err := ParseResource("INIT_CONTAINER_MEMORY_LIMIT"); err == nil {
270+
if resources.Limits == nil {
271+
resources.Limits = corev1.ResourceList{}
272+
}
273+
resources.Limits[corev1.ResourceMemory] = memoryLimit
274+
} else {
275+
fmt.Printf("Warning: %v\n", err)
276+
}
277+
227278
container := corev1.Container{
228279
Name: "setup-ca-certs",
229280
Image: ac.setupCACertsImage,
@@ -244,6 +295,11 @@ func (ac *admissionController) SetCaCerts(ctx context.Context, obj *corev1.Pod)
244295
Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}},
245296
},
246297
}
298+
299+
if len(resources.Requests) > 0 || len(resources.Limits) > 0 {
300+
container.Resources = resources
301+
}
302+
247303
obj.Spec.InitContainers = append([]corev1.Container{container}, obj.Spec.InitContainers...)
248304
}
249305

pkg/certinjectionwebhook/admission_controller_test.go

+103-2
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ package certinjectionwebhook_test
66
import (
77
"context"
88
"encoding/json"
9+
"os"
910
"testing"
1011

1112
jp "github.com/evanphx/json-patch/v5"
1213
"github.com/sclevine/spec"
1314
"github.com/stretchr/testify/assert"
1415
"github.com/stretchr/testify/require"
16+
"github.com/vmware-tanzu/cert-injection-webhook/pkg/certinjectionwebhook"
1517
"gomodules.xyz/jsonpatch/v3"
1618
admissionv1 "k8s.io/api/admission/v1"
1719
corev1 "k8s.io/api/core/v1"
1820
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1921
"k8s.io/apimachinery/pkg/runtime"
2022
wtesting "knative.dev/pkg/webhook/testing"
21-
22-
"github.com/vmware-tanzu/cert-injection-webhook/pkg/certinjectionwebhook"
2323
)
2424

2525
func TestPodAdmissionController(t *testing.T) {
@@ -1396,3 +1396,104 @@ func testPodAdmissionController(t *testing.T, when spec.G, it spec.S) {
13961396
})
13971397

13981398
}
1399+
1400+
func TestParseResource(t *testing.T) {
1401+
tests := []struct {
1402+
name string
1403+
envVar string
1404+
envValue string
1405+
expectErr bool
1406+
expectVal string
1407+
}{
1408+
{"Valid CPU Request", "TEST_CPU_REQUEST", "100m", false, "100m"},
1409+
{"Valid Memory Request", "TEST_MEMORY_REQUEST", "128Mi", false, "128Mi"},
1410+
{"Invalid Format", "TEST_INVALID", "invalid", true, ""},
1411+
{"Missing Env Var", "TEST_MISSING", "", false, ""},
1412+
}
1413+
1414+
for _, tt := range tests {
1415+
t.Run(tt.name, func(t *testing.T) {
1416+
// Set or unset the environment variable
1417+
if tt.envValue != "" {
1418+
os.Setenv(tt.envVar, tt.envValue)
1419+
defer os.Unsetenv(tt.envVar)
1420+
} else {
1421+
os.Unsetenv(tt.envVar)
1422+
}
1423+
1424+
qty, err := certinjectionwebhook.ParseResource(tt.envVar)
1425+
if (err != nil) != tt.expectErr {
1426+
t.Errorf("Expected error: %v, got: %v", tt.expectErr, err)
1427+
}
1428+
1429+
if err == nil && tt.expectVal != "" && qty.String() != tt.expectVal {
1430+
t.Errorf("Expected value: %s, got: %s", tt.expectVal, qty.String())
1431+
}
1432+
})
1433+
}
1434+
}
1435+
1436+
func TestResourceRequirementsParsing(t *testing.T) {
1437+
// Set environment variables
1438+
os.Setenv("INIT_CONTAINER_CPU_REQUEST", "200m")
1439+
os.Setenv("INIT_CONTAINER_MEMORY_REQUEST", "256Mi")
1440+
os.Setenv("INIT_CONTAINER_CPU_LIMIT", "1")
1441+
os.Setenv("INIT_CONTAINER_MEMORY_LIMIT", "512Mi")
1442+
defer func() {
1443+
os.Unsetenv("INIT_CONTAINER_CPU_REQUEST")
1444+
os.Unsetenv("INIT_CONTAINER_MEMORY_REQUEST")
1445+
os.Unsetenv("INIT_CONTAINER_CPU_LIMIT")
1446+
os.Unsetenv("INIT_CONTAINER_MEMORY_LIMIT")
1447+
}()
1448+
1449+
var resources corev1.ResourceRequirements
1450+
1451+
// Apply the logic under test
1452+
if cpuRequest, err := certinjectionwebhook.ParseResource("INIT_CONTAINER_CPU_REQUEST"); err == nil {
1453+
if resources.Requests == nil {
1454+
resources.Requests = corev1.ResourceList{}
1455+
}
1456+
resources.Requests[corev1.ResourceCPU] = cpuRequest
1457+
}
1458+
if memoryRequest, err := certinjectionwebhook.ParseResource("INIT_CONTAINER_MEMORY_REQUEST"); err == nil {
1459+
if resources.Requests == nil {
1460+
resources.Requests = corev1.ResourceList{}
1461+
}
1462+
resources.Requests[corev1.ResourceMemory] = memoryRequest
1463+
}
1464+
if cpuLimit, err := certinjectionwebhook.ParseResource("INIT_CONTAINER_CPU_LIMIT"); err == nil {
1465+
if resources.Limits == nil {
1466+
resources.Limits = corev1.ResourceList{}
1467+
}
1468+
resources.Limits[corev1.ResourceCPU] = cpuLimit
1469+
}
1470+
if memoryLimit, err := certinjectionwebhook.ParseResource("INIT_CONTAINER_MEMORY_LIMIT"); err == nil {
1471+
if resources.Limits == nil {
1472+
resources.Limits = corev1.ResourceList{}
1473+
}
1474+
resources.Limits[corev1.ResourceMemory] = memoryLimit
1475+
}
1476+
1477+
expectedRequests := map[corev1.ResourceName]string{
1478+
corev1.ResourceCPU: "200m",
1479+
corev1.ResourceMemory: "256Mi",
1480+
}
1481+
expectedLimits := map[corev1.ResourceName]string{
1482+
corev1.ResourceCPU: "1",
1483+
corev1.ResourceMemory: "512Mi",
1484+
}
1485+
1486+
for k, v := range expectedRequests {
1487+
qty := resources.Requests[k] // Copy value from map
1488+
if qty.String() != v {
1489+
t.Errorf("Expected request %s for %s, got %s", v, k, qty.String())
1490+
}
1491+
}
1492+
1493+
for k, v := range expectedLimits {
1494+
qty := resources.Limits[k] // Copy value from map
1495+
if qty.String() != v {
1496+
t.Errorf("Expected limit %s for %s, got %s", v, k, qty.String())
1497+
}
1498+
}
1499+
}

0 commit comments

Comments
 (0)