Skip to content

Commit f24e67d

Browse files
committed
kms: add helper for TechPreview V1
1 parent 28a799f commit f24e67d

2 files changed

Lines changed: 263 additions & 0 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package kms
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/openshift/api/features"
7+
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
8+
corev1 "k8s.io/api/core/v1"
9+
)
10+
11+
// AddKMSPluginVolumeAndMountToPodSpec conditionally adds the KMS plugin volume mount to the specified container.
12+
// It assumes the pod spec does not already contain the KMS volume or mount; no deduplication is performed.
13+
// Deprecated: this is a temporary solution to get KMS TP v1 out. We should come up with a different approach afterwards.
14+
func AddKMSPluginVolumeAndMountToPodSpec(podSpec *corev1.PodSpec, containerName string, featureGateAccessor featuregates.FeatureGateAccess) error {
15+
if podSpec == nil {
16+
return fmt.Errorf("pod spec cannot be nil")
17+
}
18+
19+
if !featureGateAccessor.AreInitialFeatureGatesObserved() {
20+
return nil
21+
}
22+
23+
featureGates, err := featureGateAccessor.CurrentFeatureGates()
24+
if err != nil {
25+
return fmt.Errorf("failed to get feature gates: %w", err)
26+
}
27+
28+
if !featureGates.Enabled(features.FeatureGateKMSEncryption) {
29+
return nil
30+
}
31+
32+
containerIndex := -1
33+
for i, container := range podSpec.Containers {
34+
if container.Name == containerName {
35+
containerIndex = i
36+
break
37+
}
38+
}
39+
40+
if containerIndex < 0 {
41+
return fmt.Errorf("container %s not found", containerName)
42+
}
43+
44+
container := &podSpec.Containers[containerIndex]
45+
container.VolumeMounts = append(container.VolumeMounts,
46+
corev1.VolumeMount{
47+
Name: "kms-plugin-socket",
48+
MountPath: "/var/run/kmsplugin",
49+
},
50+
)
51+
52+
directoryOrCreate := corev1.HostPathDirectoryOrCreate
53+
podSpec.Volumes = append(podSpec.Volumes,
54+
corev1.Volume{
55+
Name: "kms-plugin-socket",
56+
VolumeSource: corev1.VolumeSource{
57+
HostPath: &corev1.HostPathVolumeSource{
58+
Path: "/var/run/kmsplugin",
59+
Type: &directoryOrCreate,
60+
},
61+
},
62+
},
63+
)
64+
65+
return nil
66+
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package kms
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
configv1 "github.com/openshift/api/config/v1"
8+
"github.com/openshift/api/features"
9+
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
10+
corev1 "k8s.io/api/core/v1"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestAddKMSPluginVolume(t *testing.T) {
16+
directoryOrCreate := corev1.HostPathDirectoryOrCreate
17+
18+
tests := []struct {
19+
name string
20+
featureGateAccessor featuregates.FeatureGateAccess
21+
actualPodSpec *corev1.PodSpec
22+
expectedPodSpec *corev1.PodSpec
23+
containerName string
24+
expectError bool
25+
}{
26+
{
27+
name: "nil pod: returns error",
28+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccess(
29+
[]configv1.FeatureGateName{features.FeatureGateKMSEncryption},
30+
[]configv1.FeatureGateName{},
31+
),
32+
actualPodSpec: nil,
33+
expectedPodSpec: nil,
34+
containerName: "kube-apiserver",
35+
expectError: true,
36+
},
37+
{
38+
name: "container not found: returns error",
39+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccess(
40+
[]configv1.FeatureGateName{features.FeatureGateKMSEncryption},
41+
[]configv1.FeatureGateName{},
42+
),
43+
actualPodSpec: &corev1.PodSpec{
44+
Containers: []corev1.Container{
45+
{Name: "other-container"},
46+
},
47+
},
48+
expectedPodSpec: nil,
49+
containerName: "kube-apiserver",
50+
expectError: true,
51+
},
52+
{
53+
name: "feature gates not observed: no volume added",
54+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccessForTesting(
55+
nil,
56+
nil,
57+
make(chan struct{}),
58+
nil,
59+
),
60+
actualPodSpec: &corev1.PodSpec{
61+
Containers: []corev1.Container{
62+
{Name: "kube-apiserver"},
63+
},
64+
},
65+
expectedPodSpec: &corev1.PodSpec{
66+
Containers: []corev1.Container{
67+
{Name: "kube-apiserver"},
68+
},
69+
},
70+
containerName: "kube-apiserver",
71+
expectError: false,
72+
},
73+
{
74+
name: "feature gate accessor error: returns error",
75+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccessForTesting(
76+
nil,
77+
nil,
78+
func() chan struct{} { ch := make(chan struct{}); close(ch); return ch }(),
79+
errors.New("some error"),
80+
),
81+
actualPodSpec: &corev1.PodSpec{
82+
Containers: []corev1.Container{
83+
{Name: "kube-apiserver"},
84+
},
85+
},
86+
expectedPodSpec: nil,
87+
containerName: "kube-apiserver",
88+
expectError: true,
89+
},
90+
{
91+
name: "KMSEncryption feature gate disabled: no volume added",
92+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccess(
93+
[]configv1.FeatureGateName{},
94+
[]configv1.FeatureGateName{features.FeatureGateKMSEncryption},
95+
),
96+
actualPodSpec: &corev1.PodSpec{
97+
Containers: []corev1.Container{
98+
{Name: "kube-apiserver"},
99+
},
100+
},
101+
expectedPodSpec: &corev1.PodSpec{
102+
Containers: []corev1.Container{
103+
{Name: "kube-apiserver"},
104+
},
105+
},
106+
containerName: "kube-apiserver",
107+
expectError: false,
108+
},
109+
{
110+
name: "KMSEncryption feature gate enabled: volume and mount added",
111+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccess(
112+
[]configv1.FeatureGateName{features.FeatureGateKMSEncryption},
113+
[]configv1.FeatureGateName{},
114+
),
115+
actualPodSpec: &corev1.PodSpec{
116+
Containers: []corev1.Container{
117+
{Name: "kube-apiserver"},
118+
},
119+
},
120+
expectedPodSpec: &corev1.PodSpec{
121+
Containers: []corev1.Container{
122+
{
123+
Name: "kube-apiserver",
124+
VolumeMounts: []corev1.VolumeMount{
125+
{Name: "kms-plugin-socket", MountPath: "/var/run/kmsplugin"},
126+
},
127+
},
128+
},
129+
Volumes: []corev1.Volume{
130+
{
131+
Name: "kms-plugin-socket",
132+
VolumeSource: corev1.VolumeSource{
133+
HostPath: &corev1.HostPathVolumeSource{
134+
Path: "/var/run/kmsplugin",
135+
Type: &directoryOrCreate,
136+
},
137+
},
138+
},
139+
},
140+
},
141+
containerName: "kube-apiserver",
142+
expectError: false,
143+
},
144+
{
145+
name: "KMSEncryption feature gate enabled: only kube-apiserver container gets mount",
146+
featureGateAccessor: featuregates.NewHardcodedFeatureGateAccess(
147+
[]configv1.FeatureGateName{features.FeatureGateKMSEncryption},
148+
[]configv1.FeatureGateName{},
149+
),
150+
actualPodSpec: &corev1.PodSpec{
151+
Containers: []corev1.Container{
152+
{Name: "other-container"},
153+
{Name: "kube-apiserver"},
154+
{Name: "another-container"},
155+
},
156+
},
157+
expectedPodSpec: &corev1.PodSpec{
158+
Containers: []corev1.Container{
159+
{Name: "other-container"},
160+
{
161+
Name: "kube-apiserver",
162+
VolumeMounts: []corev1.VolumeMount{
163+
{Name: "kms-plugin-socket", MountPath: "/var/run/kmsplugin"},
164+
},
165+
},
166+
{Name: "another-container"},
167+
},
168+
Volumes: []corev1.Volume{
169+
{
170+
Name: "kms-plugin-socket",
171+
VolumeSource: corev1.VolumeSource{
172+
HostPath: &corev1.HostPathVolumeSource{
173+
Path: "/var/run/kmsplugin",
174+
Type: &directoryOrCreate,
175+
},
176+
},
177+
},
178+
},
179+
},
180+
containerName: "kube-apiserver",
181+
expectError: false,
182+
},
183+
}
184+
185+
for _, tt := range tests {
186+
t.Run(tt.name, func(t *testing.T) {
187+
err := AddKMSPluginVolumeAndMountToPodSpec(tt.actualPodSpec, tt.containerName, tt.featureGateAccessor)
188+
189+
if tt.expectError {
190+
require.Error(t, err)
191+
return
192+
}
193+
require.NoError(t, err)
194+
require.Equal(t, tt.expectedPodSpec, tt.actualPodSpec)
195+
})
196+
}
197+
}

0 commit comments

Comments
 (0)