Skip to content

Commit 752f566

Browse files
authored
Merge pull request kubernetes#74737 from wk8/wk8/gmsa_bug_fix
Fixing a small bug with GMSA support
2 parents c360bac + 5e3f3b3 commit 752f566

File tree

6 files changed

+95
-39
lines changed

6 files changed

+95
-39
lines changed

pkg/features/kube_features.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
484484
ProcMountType: {Default: false, PreRelease: utilfeature.Alpha},
485485
TTLAfterFinished: {Default: false, PreRelease: utilfeature.Alpha},
486486
KubeletPodResources: {Default: false, PreRelease: utilfeature.Alpha},
487+
WindowsGMSA: {Default: false, PreRelease: utilfeature.Alpha},
487488

488489
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
489490
// unintentionally on either side:

pkg/kubelet/dockershim/docker_container.go

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,20 +167,28 @@ func (ds *dockerService) CreateContainer(_ context.Context, r *runtimeapi.Create
167167
if err != nil {
168168
return nil, err
169169
}
170-
defer func() {
171-
for _, err := range ds.performPlatformSpecificContainerCreationCleanup(cleanupInfo) {
172-
klog.Warningf("error when cleaning up after container %v's creation: %v", containerName, err)
173-
}
174-
}()
175170

176171
createResp, createErr := ds.client.CreateContainer(createConfig)
177172
if createErr != nil {
178173
createResp, createErr = recoverFromCreationConflictIfNeeded(ds.client, createConfig, createErr)
179174
}
180175

181176
if createResp != nil {
182-
return &runtimeapi.CreateContainerResponse{ContainerId: createResp.ID}, nil
177+
containerID := createResp.ID
178+
179+
if cleanupInfo != nil {
180+
// we don't perform the clean up just yet at that could destroy information
181+
// needed for the container to start (e.g. Windows credentials stored in
182+
// registry keys); instead, we'll clean up after the container successfully
183+
// starts or gets removed
184+
ds.containerCleanupInfos[containerID] = cleanupInfo
185+
}
186+
return &runtimeapi.CreateContainerResponse{ContainerId: containerID}, nil
183187
}
188+
189+
// the creation failed, let's clean up right away
190+
ds.performPlatformSpecificContainerCleanupAndLogErrors(containerName, cleanupInfo)
191+
184192
return nil, createErr
185193
}
186194

@@ -267,6 +275,8 @@ func (ds *dockerService) StartContainer(_ context.Context, r *runtimeapi.StartCo
267275
return nil, fmt.Errorf("failed to start container %q: %v", r.ContainerId, err)
268276
}
269277

278+
ds.performPlatformSpecificContainerForContainer(r.ContainerId)
279+
270280
return &runtimeapi.StartContainerResponse{}, nil
271281
}
272282

@@ -281,6 +291,8 @@ func (ds *dockerService) StopContainer(_ context.Context, r *runtimeapi.StopCont
281291

282292
// RemoveContainer removes the container.
283293
func (ds *dockerService) RemoveContainer(_ context.Context, r *runtimeapi.RemoveContainerRequest) (*runtimeapi.RemoveContainerResponse, error) {
294+
ds.performPlatformSpecificContainerForContainer(r.ContainerId)
295+
284296
// Ideally, log lifecycle should be independent of container lifecycle.
285297
// However, docker will remove container log after container is removed,
286298
// we can't prevent that now, so we also clean up the symlink here.
@@ -438,3 +450,20 @@ func (ds *dockerService) UpdateContainerResources(_ context.Context, r *runtimea
438450
}
439451
return &runtimeapi.UpdateContainerResourcesResponse{}, nil
440452
}
453+
454+
func (ds *dockerService) performPlatformSpecificContainerForContainer(containerID string) {
455+
if cleanupInfo, present := ds.containerCleanupInfos[containerID]; present {
456+
ds.performPlatformSpecificContainerCleanupAndLogErrors(containerID, cleanupInfo)
457+
delete(ds.containerCleanupInfos, containerID)
458+
}
459+
}
460+
461+
func (ds *dockerService) performPlatformSpecificContainerCleanupAndLogErrors(containerNameOrID string, cleanupInfo *containerCleanupInfo) {
462+
if cleanupInfo == nil {
463+
return
464+
}
465+
466+
for _, err := range ds.performPlatformSpecificContainerCleanup(cleanupInfo) {
467+
klog.Warningf("error when cleaning up after container %q: %v", containerNameOrID, err)
468+
}
469+
}

pkg/kubelet/dockershim/docker_container_unsupported.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,35 @@ import (
2323
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
2424
)
2525

26-
type containerCreationCleanupInfo struct{}
26+
type containerCleanupInfo struct{}
2727

2828
// applyPlatformSpecificDockerConfig applies platform-specific configurations to a dockertypes.ContainerCreateConfig struct.
29-
// The containerCreationCleanupInfo struct it returns will be passed as is to performPlatformSpecificContainerCreationCleanup
30-
// after the container has been created.
31-
func (ds *dockerService) applyPlatformSpecificDockerConfig(*runtimeapi.CreateContainerRequest, *dockertypes.ContainerCreateConfig) (*containerCreationCleanupInfo, error) {
29+
// The containerCleanupInfo struct it returns will be passed as is to performPlatformSpecificContainerCleanup
30+
// after either:
31+
// * the container creation has failed
32+
// * the container has been successfully started
33+
// * the container has been removed
34+
// whichever happens first.
35+
func (ds *dockerService) applyPlatformSpecificDockerConfig(*runtimeapi.CreateContainerRequest, *dockertypes.ContainerCreateConfig) (*containerCleanupInfo, error) {
3236
return nil, nil
3337
}
3438

35-
// performPlatformSpecificContainerCreationCleanup is responsible for doing any platform-specific cleanup
36-
// after a container creation. Any errors it returns are simply logged, but do not fail the container
37-
// creation.
38-
func (ds *dockerService) performPlatformSpecificContainerCreationCleanup(cleanupInfo *containerCreationCleanupInfo) (errors []error) {
39+
// performPlatformSpecificContainerCleanup is responsible for doing any platform-specific cleanup
40+
// after either:
41+
// * the container creation has failed
42+
// * the container has been successfully started
43+
// * the container has been removed
44+
// whichever happens first.
45+
// Any errors it returns are simply logged, but do not prevent the container from being started or
46+
// removed.
47+
func (ds *dockerService) performPlatformSpecificContainerCleanup(cleanupInfo *containerCleanupInfo) (errors []error) {
3948
return
4049
}
4150

42-
// platformSpecificContainerCreationInitCleanup is called when dockershim
51+
// platformSpecificContainerInitCleanup is called when dockershim
4352
// is starting, and is meant to clean up any cruft left by previous runs
4453
// creating containers.
4554
// Errors are simply logged, but don't prevent dockershim from starting.
46-
func (ds *dockerService) platformSpecificContainerCreationInitCleanup() (errors []error) {
55+
func (ds *dockerService) platformSpecificContainerInitCleanup() (errors []error) {
4756
return
4857
}

pkg/kubelet/dockershim/docker_container_windows.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,19 @@ import (
3333
"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
3434
)
3535

36-
type containerCreationCleanupInfo struct {
36+
type containerCleanupInfo struct {
3737
gMSARegistryValueName string
3838
}
3939

4040
// applyPlatformSpecificDockerConfig applies platform-specific configurations to a dockertypes.ContainerCreateConfig struct.
41-
// The containerCreationCleanupInfo struct it returns will be passed as is to performPlatformSpecificContainerCreationCleanup
42-
// after the container has been created.
43-
func (ds *dockerService) applyPlatformSpecificDockerConfig(request *runtimeapi.CreateContainerRequest, createConfig *dockertypes.ContainerCreateConfig) (*containerCreationCleanupInfo, error) {
44-
cleanupInfo := &containerCreationCleanupInfo{}
41+
// The containerCleanupInfo struct it returns will be passed as is to performPlatformSpecificContainerCleanup
42+
// after either:
43+
// * the container creation has failed
44+
// * the container has been successfully started
45+
// * the container has been removed
46+
// whichever happens first.
47+
func (ds *dockerService) applyPlatformSpecificDockerConfig(request *runtimeapi.CreateContainerRequest, createConfig *dockertypes.ContainerCreateConfig) (*containerCleanupInfo, error) {
48+
cleanupInfo := &containerCleanupInfo{}
4549

4650
if err := applyGMSAConfig(request.GetConfig(), createConfig, cleanupInfo); err != nil {
4751
return nil, err
@@ -58,7 +62,7 @@ func (ds *dockerService) applyPlatformSpecificDockerConfig(request *runtimeapi.C
5862
// When docker supports passing a credential spec's contents directly, we should switch to using that
5963
// as it will avoid cluttering the registry - there is a moby PR out for this:
6064
// https://github.com/moby/moby/pull/38777
61-
func applyGMSAConfig(config *runtimeapi.ContainerConfig, createConfig *dockertypes.ContainerCreateConfig, cleanupInfo *containerCreationCleanupInfo) error {
65+
func applyGMSAConfig(config *runtimeapi.ContainerConfig, createConfig *dockertypes.ContainerCreateConfig, cleanupInfo *containerCleanupInfo) error {
6266
credSpec := config.Annotations[kuberuntime.GMSASpecContainerAnnotationKey]
6367
if credSpec == "" {
6468
return nil
@@ -156,18 +160,23 @@ func randomString(length int) (string, error) {
156160
return hex.EncodeToString(randBytes), nil
157161
}
158162

159-
// performPlatformSpecificContainerCreationCleanup is responsible for doing any platform-specific cleanup
160-
// after a container creation. Any errors it returns are simply logged, but do not fail the container
161-
// creation.
162-
func (ds *dockerService) performPlatformSpecificContainerCreationCleanup(cleanupInfo *containerCreationCleanupInfo) (errors []error) {
163+
// performPlatformSpecificContainerCleanup is responsible for doing any platform-specific cleanup
164+
// after either:
165+
// * the container creation has failed
166+
// * the container has been successfully started
167+
// * the container has been removed
168+
// whichever happens first.
169+
// Any errors it returns are simply logged, but do not prevent the container from being started or
170+
// removed.
171+
func (ds *dockerService) performPlatformSpecificContainerCleanup(cleanupInfo *containerCleanupInfo) (errors []error) {
163172
if err := removeGMSARegistryValue(cleanupInfo); err != nil {
164173
errors = append(errors, err)
165174
}
166175

167176
return
168177
}
169178

170-
func removeGMSARegistryValue(cleanupInfo *containerCreationCleanupInfo) error {
179+
func removeGMSARegistryValue(cleanupInfo *containerCleanupInfo) error {
171180
if cleanupInfo == nil || cleanupInfo.gMSARegistryValueName == "" {
172181
return nil
173182
}
@@ -184,11 +193,11 @@ func removeGMSARegistryValue(cleanupInfo *containerCreationCleanupInfo) error {
184193
return nil
185194
}
186195

187-
// platformSpecificContainerCreationInitCleanup is called when dockershim
196+
// platformSpecificContainerInitCleanup is called when dockershim
188197
// is starting, and is meant to clean up any cruft left by previous runs
189198
// creating containers.
190199
// Errors are simply logged, but don't prevent dockershim from starting.
191-
func (ds *dockerService) platformSpecificContainerCreationInitCleanup() (errors []error) {
200+
func (ds *dockerService) platformSpecificContainerInitCleanup() (errors []error) {
192201
return removeAllGMSARegistryValues()
193202
}
194203

pkg/kubelet/dockershim/docker_container_windows_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func TestApplyGMSAConfig(t *testing.T) {
8282
defer setRandomReader(randomBytes)()
8383

8484
createConfig := &dockertypes.ContainerCreateConfig{}
85-
cleanupInfo := &containerCreationCleanupInfo{}
85+
cleanupInfo := &containerCleanupInfo{}
8686
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, createConfig, cleanupInfo)
8787

8888
assert.Nil(t, err)
@@ -105,7 +105,7 @@ func TestApplyGMSAConfig(t *testing.T) {
105105
defer setRegistryCreateKeyFunc(t, &dummyRegistryKey{})()
106106

107107
createConfig := &dockertypes.ContainerCreateConfig{}
108-
cleanupInfo := &containerCreationCleanupInfo{}
108+
cleanupInfo := &containerCleanupInfo{}
109109
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, createConfig, cleanupInfo)
110110

111111
assert.Nil(t, err)
@@ -127,15 +127,15 @@ func TestApplyGMSAConfig(t *testing.T) {
127127
t.Run("when there's an error generating the random value name", func(t *testing.T) {
128128
defer setRandomReader([]byte{})()
129129

130-
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, &dockertypes.ContainerCreateConfig{}, &containerCreationCleanupInfo{})
130+
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, &dockertypes.ContainerCreateConfig{}, &containerCleanupInfo{})
131131

132132
require.NotNil(t, err)
133133
assert.Contains(t, err.Error(), "error when generating gMSA registry value name: unable to generate random string")
134134
})
135135
t.Run("if there's an error opening the registry key", func(t *testing.T) {
136136
defer setRegistryCreateKeyFunc(t, &dummyRegistryKey{}, fmt.Errorf("dummy error"))()
137137

138-
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, &dockertypes.ContainerCreateConfig{}, &containerCreationCleanupInfo{})
138+
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, &dockertypes.ContainerCreateConfig{}, &containerCleanupInfo{})
139139

140140
require.NotNil(t, err)
141141
assert.Contains(t, err.Error(), "unable to open registry key")
@@ -145,7 +145,7 @@ func TestApplyGMSAConfig(t *testing.T) {
145145
key.setStringValueError = fmt.Errorf("dummy error")
146146
defer setRegistryCreateKeyFunc(t, key)()
147147

148-
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, &dockertypes.ContainerCreateConfig{}, &containerCreationCleanupInfo{})
148+
err := applyGMSAConfig(containerConfigWithGMSAAnnotation, &dockertypes.ContainerCreateConfig{}, &containerCleanupInfo{})
149149

150150
if assert.NotNil(t, err) {
151151
assert.Contains(t, err.Error(), "unable to write into registry value")
@@ -155,7 +155,7 @@ func TestApplyGMSAConfig(t *testing.T) {
155155
t.Run("if there is no GMSA annotation", func(t *testing.T) {
156156
createConfig := &dockertypes.ContainerCreateConfig{}
157157

158-
err := applyGMSAConfig(&runtimeapi.ContainerConfig{}, createConfig, &containerCreationCleanupInfo{})
158+
err := applyGMSAConfig(&runtimeapi.ContainerConfig{}, createConfig, &containerCleanupInfo{})
159159

160160
assert.Nil(t, err)
161161
assert.Nil(t, createConfig.HostConfig)
@@ -164,7 +164,7 @@ func TestApplyGMSAConfig(t *testing.T) {
164164

165165
func TestRemoveGMSARegistryValue(t *testing.T) {
166166
valueName := "k8s-cred-spec-1900254518529e2a3dedb85cdec03ce270559647459ab531f07af5eb1c5495fda709435ce82ab89c"
167-
cleanupInfoWithValue := &containerCreationCleanupInfo{gMSARegistryValueName: valueName}
167+
cleanupInfoWithValue := &containerCleanupInfo{gMSARegistryValueName: valueName}
168168

169169
t.Run("it does remove the registry value", func(t *testing.T) {
170170
key := &dummyRegistryKey{}
@@ -204,7 +204,7 @@ func TestRemoveGMSARegistryValue(t *testing.T) {
204204
key := &dummyRegistryKey{}
205205
defer setRegistryCreateKeyFunc(t, key)()
206206

207-
err := removeGMSARegistryValue(&containerCreationCleanupInfo{})
207+
err := removeGMSARegistryValue(&containerCleanupInfo{})
208208

209209
assert.Nil(t, err)
210210
assert.Equal(t, 0, len(key.deleteValueArgs))

pkg/kubelet/dockershim/docker_service.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ type dockerNetworkHost struct {
155155
*portMappingGetter
156156
}
157157

158-
var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPathLabelKey, sandboxIDLabelKey}
158+
var internalLabelKeys = []string{containerTypeLabelKey, containerLogPathLabelKey, sandboxIDLabelKey}
159159

160160
// ClientConfig is parameters used to initialize docker client
161161
type ClientConfig struct {
@@ -186,6 +186,7 @@ func NewDockerClientFromConfig(config *ClientConfig) libdocker.Interface {
186186
return nil
187187
}
188188

189+
// NewDockerService creates a new `DockerService` struct.
189190
// NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.
190191
func NewDockerService(config *ClientConfig, podSandboxImage string, streamingConfig *streaming.Config, pluginSettings *NetworkPluginSettings,
191192
cgroupsName string, kubeCgroupDriver string, dockershimRootDir string, startLocalStreamingServer bool) (DockerService, error) {
@@ -211,6 +212,7 @@ func NewDockerService(config *ClientConfig, podSandboxImage string, streamingCon
211212
checkpointManager: checkpointManager,
212213
startLocalStreamingServer: startLocalStreamingServer,
213214
networkReady: make(map[string]bool),
215+
containerCleanupInfos: make(map[string]*containerCleanupInfo),
214216
}
215217

216218
// check docker version compatibility.
@@ -305,6 +307,12 @@ type dockerService struct {
305307
// startLocalStreamingServer indicates whether dockershim should start a
306308
// streaming server on localhost.
307309
startLocalStreamingServer bool
310+
311+
// containerCleanupInfos maps container IDs to the `containerCleanupInfo` structs
312+
// needed to clean up after containers have been started or removed.
313+
// (see `applyPlatformSpecificDockerConfig` and `performPlatformSpecificContainerCleanup`
314+
// methods for more info).
315+
containerCleanupInfos map[string]*containerCleanupInfo
308316
}
309317

310318
// TODO: handle context.
@@ -411,7 +419,7 @@ func (ds *dockerService) Start() error {
411419
// initCleanup is responsible for cleaning up any crufts left by previous
412420
// runs. If there are any errros, it simply logs them.
413421
func (ds *dockerService) initCleanup() {
414-
errors := ds.platformSpecificContainerCreationInitCleanup()
422+
errors := ds.platformSpecificContainerInitCleanup()
415423

416424
for _, err := range errors {
417425
klog.Warningf("initialization error: %v", err)

0 commit comments

Comments
 (0)