Skip to content

Commit 24d1f46

Browse files
authored
Merge branch 'master' into master
2 parents b9e6052 + 42bbead commit 24d1f46

File tree

3 files changed

+213
-9
lines changed

3 files changed

+213
-9
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as
1717
* Live volume resize without pod restarts (AWS EBS, PVC)
1818
* Database connection pooling with PGBouncer
1919
* Support fast in place major version upgrade. Supports global upgrade of all clusters.
20-
* Pod protection during boostrap phase and configurable maintenance windows
20+
* Pod protection during bootstrap phase and configurable maintenance windows
2121
* Restore and cloning Postgres clusters on AWS, GCS and Azure
2222
* Additionally logical backups to S3 or GCS bucket can be configured
2323
* Standby cluster from S3 or GCS WAL archive

pkg/cluster/k8sres.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,9 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
13031303
c.logger.Warningf("initContainers specified but disabled in configuration - next statefulset creation would fail")
13041304
}
13051305
initContainers = spec.InitContainers
1306+
if err := c.validateContainers(initContainers); err != nil {
1307+
return nil, fmt.Errorf("invalid init containers: %v", err)
1308+
}
13061309
}
13071310

13081311
// backward compatible check for InitContainers
@@ -1455,6 +1458,10 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
14551458

14561459
sidecarContainers = patchSidecarContainers(sidecarContainers, volumeMounts, c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername))
14571460

1461+
if err := c.validateContainers(sidecarContainers); err != nil {
1462+
return nil, fmt.Errorf("invalid sidecar containers: %v", err)
1463+
}
1464+
14581465
tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
14591466
effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName)
14601467

@@ -2592,3 +2599,15 @@ func ensurePath(file string, defaultDir string, defaultFile string) string {
25922599
}
25932600
return file
25942601
}
2602+
2603+
func (c *Cluster) validateContainers(containers []v1.Container) error {
2604+
for i, container := range containers {
2605+
if container.Name == "" {
2606+
return fmt.Errorf("container[%d]: name is required", i)
2607+
}
2608+
if container.Image == "" {
2609+
return fmt.Errorf("container '%v': image is required", container.Name)
2610+
}
2611+
}
2612+
return nil
2613+
}

pkg/cluster/k8sres_test.go

Lines changed: 193 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,7 +1935,8 @@ func TestAdditionalVolume(t *testing.T) {
19351935
AdditionalVolumes: additionalVolumes,
19361936
Sidecars: []acidv1.Sidecar{
19371937
{
1938-
Name: sidecarName,
1938+
Name: sidecarName,
1939+
DockerImage: "test-image",
19391940
},
19401941
},
19411942
},
@@ -2163,10 +2164,12 @@ func TestSidecars(t *testing.T) {
21632164
},
21642165
Sidecars: []acidv1.Sidecar{
21652166
{
2166-
Name: "cluster-specific-sidecar",
2167+
Name: "cluster-specific-sidecar",
2168+
DockerImage: "test-image",
21672169
},
21682170
{
2169-
Name: "cluster-specific-sidecar-with-resources",
2171+
Name: "cluster-specific-sidecar-with-resources",
2172+
DockerImage: "test-image",
21702173
Resources: &acidv1.Resources{
21712174
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")},
21722175
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")},
@@ -2201,7 +2204,8 @@ func TestSidecars(t *testing.T) {
22012204
},
22022205
SidecarContainers: []v1.Container{
22032206
{
2204-
Name: "global-sidecar",
2207+
Name: "global-sidecar",
2208+
Image: "test-image",
22052209
},
22062210
// will be replaced by a cluster specific sidecar with the same name
22072211
{
@@ -2271,6 +2275,7 @@ func TestSidecars(t *testing.T) {
22712275
// cluster specific sidecar
22722276
assert.Contains(t, s.Spec.Template.Spec.Containers, v1.Container{
22732277
Name: "cluster-specific-sidecar",
2278+
Image: "test-image",
22742279
Env: env,
22752280
Resources: generateKubernetesResources("200m", "500m", "0.7Gi", "1.3Gi"),
22762281
ImagePullPolicy: v1.PullIfNotPresent,
@@ -2297,6 +2302,7 @@ func TestSidecars(t *testing.T) {
22972302
// global sidecar
22982303
assert.Contains(t, s.Spec.Template.Spec.Containers, v1.Container{
22992304
Name: "global-sidecar",
2305+
Image: "test-image",
23002306
Env: env,
23012307
VolumeMounts: mounts,
23022308
})
@@ -2325,6 +2331,180 @@ func TestSidecars(t *testing.T) {
23252331

23262332
}
23272333

2334+
func TestContainerValidation(t *testing.T) {
2335+
testCases := []struct {
2336+
name string
2337+
spec acidv1.PostgresSpec
2338+
clusterConfig Config
2339+
expectedError string
2340+
}{
2341+
{
2342+
name: "init container without image",
2343+
spec: acidv1.PostgresSpec{
2344+
PostgresqlParam: acidv1.PostgresqlParam{
2345+
PgVersion: "17",
2346+
},
2347+
TeamID: "myapp",
2348+
NumberOfInstances: 1,
2349+
Volume: acidv1.Volume{
2350+
Size: "1G",
2351+
},
2352+
InitContainers: []v1.Container{
2353+
{
2354+
Name: "invalid-initcontainer",
2355+
},
2356+
},
2357+
},
2358+
clusterConfig: Config{
2359+
OpConfig: config.Config{
2360+
PodManagementPolicy: "ordered_ready",
2361+
ProtectedRoles: []string{"admin"},
2362+
Auth: config.Auth{
2363+
SuperUsername: superUserName,
2364+
ReplicationUsername: replicationUserName,
2365+
},
2366+
},
2367+
},
2368+
expectedError: "image is required",
2369+
},
2370+
{
2371+
name: "sidecar without name",
2372+
spec: acidv1.PostgresSpec{
2373+
PostgresqlParam: acidv1.PostgresqlParam{
2374+
PgVersion: "17",
2375+
},
2376+
TeamID: "myapp",
2377+
NumberOfInstances: 1,
2378+
Volume: acidv1.Volume{
2379+
Size: "1G",
2380+
},
2381+
},
2382+
clusterConfig: Config{
2383+
OpConfig: config.Config{
2384+
PodManagementPolicy: "ordered_ready",
2385+
ProtectedRoles: []string{"admin"},
2386+
Auth: config.Auth{
2387+
SuperUsername: superUserName,
2388+
ReplicationUsername: replicationUserName,
2389+
},
2390+
SidecarContainers: []v1.Container{
2391+
{
2392+
Image: "test-image",
2393+
},
2394+
},
2395+
},
2396+
},
2397+
expectedError: "name is required",
2398+
},
2399+
{
2400+
name: "sidecar without image",
2401+
spec: acidv1.PostgresSpec{
2402+
PostgresqlParam: acidv1.PostgresqlParam{
2403+
PgVersion: "17",
2404+
},
2405+
TeamID: "myapp",
2406+
NumberOfInstances: 1,
2407+
Volume: acidv1.Volume{
2408+
Size: "1G",
2409+
},
2410+
Sidecars: []acidv1.Sidecar{
2411+
{
2412+
Name: "invalid-sidecar",
2413+
},
2414+
},
2415+
},
2416+
clusterConfig: Config{
2417+
OpConfig: config.Config{
2418+
PodManagementPolicy: "ordered_ready",
2419+
ProtectedRoles: []string{"admin"},
2420+
Auth: config.Auth{
2421+
SuperUsername: superUserName,
2422+
ReplicationUsername: replicationUserName,
2423+
},
2424+
},
2425+
},
2426+
expectedError: "image is required",
2427+
},
2428+
{
2429+
name: "valid containers pass validation",
2430+
spec: acidv1.PostgresSpec{
2431+
PostgresqlParam: acidv1.PostgresqlParam{
2432+
PgVersion: "17",
2433+
},
2434+
TeamID: "myapp",
2435+
NumberOfInstances: 1,
2436+
Volume: acidv1.Volume{
2437+
Size: "1G",
2438+
},
2439+
Sidecars: []acidv1.Sidecar{
2440+
{
2441+
Name: "valid-sidecar",
2442+
DockerImage: "busybox:latest",
2443+
},
2444+
},
2445+
InitContainers: []v1.Container{
2446+
{
2447+
Name: "valid-initcontainer",
2448+
Image: "alpine:latest",
2449+
},
2450+
},
2451+
},
2452+
clusterConfig: Config{
2453+
OpConfig: config.Config{
2454+
PodManagementPolicy: "ordered_ready",
2455+
ProtectedRoles: []string{"admin"},
2456+
Auth: config.Auth{
2457+
SuperUsername: superUserName,
2458+
ReplicationUsername: replicationUserName,
2459+
},
2460+
},
2461+
},
2462+
expectedError: "",
2463+
},
2464+
{
2465+
name: "multiple invalid sidecars",
2466+
spec: acidv1.PostgresSpec{
2467+
Sidecars: []acidv1.Sidecar{
2468+
{
2469+
Name: "sidecar1",
2470+
},
2471+
{
2472+
Name: "sidecar2",
2473+
},
2474+
},
2475+
},
2476+
expectedError: "image is required",
2477+
},
2478+
{
2479+
name: "empty container name and image",
2480+
spec: acidv1.PostgresSpec{
2481+
InitContainers: []v1.Container{
2482+
{
2483+
Name: "",
2484+
Image: "",
2485+
},
2486+
},
2487+
},
2488+
expectedError: "name is required",
2489+
},
2490+
}
2491+
2492+
for _, tc := range testCases {
2493+
t.Run(tc.name, func(t *testing.T) {
2494+
cluster := New(tc.clusterConfig, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder)
2495+
2496+
_, err := cluster.generateStatefulSet(&tc.spec)
2497+
2498+
if tc.expectedError != "" {
2499+
assert.Error(t, err)
2500+
assert.Contains(t, err.Error(), tc.expectedError)
2501+
} else {
2502+
assert.NoError(t, err)
2503+
}
2504+
})
2505+
}
2506+
}
2507+
23282508
func TestGeneratePodDisruptionBudget(t *testing.T) {
23292509
testName := "Test PodDisruptionBudget spec generation"
23302510

@@ -2618,7 +2798,8 @@ func TestGenerateService(t *testing.T) {
26182798
Name: "cluster-specific-sidecar",
26192799
},
26202800
{
2621-
Name: "cluster-specific-sidecar-with-resources",
2801+
Name: "cluster-specific-sidecar-with-resources",
2802+
DockerImage: "test-image",
26222803
Resources: &acidv1.Resources{
26232804
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")},
26242805
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")},
@@ -2928,6 +3109,7 @@ func TestGenerateResourceRequirements(t *testing.T) {
29283109
namespace := "default"
29293110
clusterNameLabel := "cluster-name"
29303111
sidecarName := "postgres-exporter"
3112+
dockerImage := "test-image"
29313113

29323114
// enforceMinResourceLimits will be called 2 times emitting 4 events (2x cpu, 2x memory raise)
29333115
// enforceMaxResourceRequests will be called 4 times emitting 6 events (2x cpu, 4x memory cap)
@@ -2993,7 +3175,8 @@ func TestGenerateResourceRequirements(t *testing.T) {
29933175
Spec: acidv1.PostgresSpec{
29943176
Sidecars: []acidv1.Sidecar{
29953177
{
2996-
Name: sidecarName,
3178+
Name: sidecarName,
3179+
DockerImage: dockerImage,
29973180
},
29983181
},
29993182
TeamID: "acid",
@@ -3232,7 +3415,8 @@ func TestGenerateResourceRequirements(t *testing.T) {
32323415
Spec: acidv1.PostgresSpec{
32333416
Sidecars: []acidv1.Sidecar{
32343417
{
3235-
Name: sidecarName,
3418+
Name: sidecarName,
3419+
DockerImage: dockerImage,
32363420
Resources: &acidv1.Resources{
32373421
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")},
32383422
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")},
@@ -3321,7 +3505,8 @@ func TestGenerateResourceRequirements(t *testing.T) {
33213505
Spec: acidv1.PostgresSpec{
33223506
Sidecars: []acidv1.Sidecar{
33233507
{
3324-
Name: sidecarName,
3508+
Name: sidecarName,
3509+
DockerImage: dockerImage,
33253510
Resources: &acidv1.Resources{
33263511
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")},
33273512
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")},

0 commit comments

Comments
 (0)