Skip to content

Commit 524bec1

Browse files
committed
Remove deployment/daemonset when necessary
1 parent 2316903 commit 524bec1

File tree

5 files changed

+149
-56
lines changed

5 files changed

+149
-56
lines changed

internal/framework/controller/resource.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,10 @@ package controller
22

33
import (
44
"fmt"
5-
6-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7-
"k8s.io/apimachinery/pkg/types"
85
)
96

107
// CreateNginxResourceName creates the base resource name for all nginx resources
118
// created by the control plane.
129
func CreateNginxResourceName(prefix, suffix string) string {
1310
return fmt.Sprintf("%s-%s", prefix, suffix)
1411
}
15-
16-
// ObjectMetaToNamespacedName converts ObjectMeta to NamespacedName.
17-
func ObjectMetaToNamespacedName(meta metav1.ObjectMeta) types.NamespacedName {
18-
return types.NamespacedName{
19-
Namespace: meta.Namespace,
20-
Name: meta.Name,
21-
}
22-
}

internal/mode/static/provisioner/handler.go

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -268,40 +268,25 @@ func (h *eventHandler) deprovisionSecretsForAllGateways(ctx context.Context, sec
268268

269269
switch {
270270
case strings.HasSuffix(resources.AgentTLSSecret.Name, secret):
271-
if err := h.provisioner.deleteSecret(
272-
ctx,
273-
controller.ObjectMetaToNamespacedName(resources.AgentTLSSecret),
274-
); err != nil {
271+
if err := h.provisioner.deleteObject(ctx, &corev1.Secret{ObjectMeta: resources.AgentTLSSecret}); err != nil {
275272
allErrs = append(allErrs, err)
276273
}
277274
case strings.HasSuffix(resources.PlusJWTSecret.Name, secret):
278-
if err := h.provisioner.deleteSecret(
279-
ctx,
280-
controller.ObjectMetaToNamespacedName(resources.PlusJWTSecret),
281-
); err != nil {
275+
if err := h.provisioner.deleteObject(ctx, &corev1.Secret{ObjectMeta: resources.PlusJWTSecret}); err != nil {
282276
allErrs = append(allErrs, err)
283277
}
284278
case strings.HasSuffix(resources.PlusCASecret.Name, secret):
285-
if err := h.provisioner.deleteSecret(
286-
ctx,
287-
controller.ObjectMetaToNamespacedName(resources.PlusCASecret),
288-
); err != nil {
279+
if err := h.provisioner.deleteObject(ctx, &corev1.Secret{ObjectMeta: resources.PlusCASecret}); err != nil {
289280
allErrs = append(allErrs, err)
290281
}
291282
case strings.HasSuffix(resources.PlusClientSSLSecret.Name, secret):
292-
if err := h.provisioner.deleteSecret(
293-
ctx,
294-
controller.ObjectMetaToNamespacedName(resources.PlusClientSSLSecret),
295-
); err != nil {
283+
if err := h.provisioner.deleteObject(ctx, &corev1.Secret{ObjectMeta: resources.PlusClientSSLSecret}); err != nil {
296284
allErrs = append(allErrs, err)
297285
}
298286
default:
299287
for _, dockerSecret := range resources.DockerSecrets {
300288
if strings.HasSuffix(dockerSecret.Name, secret) {
301-
if err := h.provisioner.deleteSecret(
302-
ctx,
303-
controller.ObjectMetaToNamespacedName(dockerSecret),
304-
); err != nil {
289+
if err := h.provisioner.deleteObject(ctx, &corev1.Secret{ObjectMeta: dockerSecret}); err != nil {
305290
allErrs = append(allErrs, err)
306291
}
307292
}

internal/mode/static/provisioner/provisioner.go

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ func (p *NginxProvisioner) provisionNginx(
265265
p.store.registerResourceInGatewayConfig(client.ObjectKeyFromObject(gateway), obj)
266266
}
267267

268-
// if agent configmap was updated, then we'll need to restart the deployment
268+
// if agent configmap was updated, then we'll need to restart the deployment/daemonset
269269
if agentConfigMapUpdated && !deploymentCreated {
270270
updateCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
271271
defer cancel()
@@ -286,7 +286,7 @@ func (p *NginxProvisioner) provisionNginx(
286286
}
287287

288288
p.cfg.Logger.V(1).Info(
289-
"Restarting nginx deployment after agent configmap update",
289+
"Restarting nginx after agent configmap update",
290290
"name", object.GetName(),
291291
"namespace", object.GetNamespace(),
292292
)
@@ -296,7 +296,7 @@ func (p *NginxProvisioner) provisionNginx(
296296
object,
297297
corev1.EventTypeWarning,
298298
"RestartFailed",
299-
"Failed to restart nginx deployment after agent config update: %s",
299+
"Failed to restart nginx after agent config update: %s",
300300
err.Error(),
301301
)
302302
return err
@@ -361,11 +361,11 @@ func (p *NginxProvisioner) deprovisionNginx(ctx context.Context, gatewayNSName t
361361

362362
objects := p.buildNginxResourceObjectsForDeletion(deploymentNSName)
363363

364-
createCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
364+
deleteCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
365365
defer cancel()
366366

367367
for _, obj := range objects {
368-
if err := p.k8sClient.Delete(createCtx, obj); err != nil && !apierrors.IsNotFound(err) {
368+
if err := p.k8sClient.Delete(deleteCtx, obj); err != nil && !apierrors.IsNotFound(err) {
369369
p.cfg.EventRecorder.Eventf(
370370
obj,
371371
corev1.EventTypeWarning,
@@ -384,6 +384,28 @@ func (p *NginxProvisioner) deprovisionNginx(ctx context.Context, gatewayNSName t
384384
return nil
385385
}
386386

387+
func (p *NginxProvisioner) deleteObject(ctx context.Context, obj client.Object) error {
388+
if !p.isLeader() {
389+
return nil
390+
}
391+
392+
deleteCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
393+
defer cancel()
394+
395+
if err := p.k8sClient.Delete(deleteCtx, obj); err != nil && !apierrors.IsNotFound(err) {
396+
p.cfg.EventRecorder.Eventf(
397+
obj,
398+
corev1.EventTypeWarning,
399+
"DeleteFailed",
400+
"Failed to delete nginx resource: %s",
401+
err.Error(),
402+
)
403+
return err
404+
}
405+
406+
return nil
407+
}
408+
387409
// isUserSecret determines if the provided secret name is a special user secret,
388410
// for example an NGINX docker registry secret or NGINX Plus secret.
389411
func (p *NginxProvisioner) isUserSecret(name string) bool {
@@ -404,25 +426,6 @@ func (p *NginxProvisioner) isUserSecret(name string) bool {
404426
return false
405427
}
406428

407-
func (p *NginxProvisioner) deleteSecret(ctx context.Context, secretNSName types.NamespacedName) error {
408-
if !p.isLeader() {
409-
return nil
410-
}
411-
412-
secret := &corev1.Secret{
413-
ObjectMeta: metav1.ObjectMeta{
414-
Name: secretNSName.Name,
415-
Namespace: secretNSName.Namespace,
416-
},
417-
}
418-
419-
if err := p.k8sClient.Delete(ctx, secret); err != nil && !apierrors.IsNotFound(err) {
420-
return err
421-
}
422-
423-
return nil
424-
}
425-
426429
// RegisterGateway is called by the main event handler when a Gateway API resource event occurs
427430
// and the graph is built. The provisioner updates the Gateway config in the store and then:
428431
// - If it's a valid Gateway, create or update nginx resources associated with the Gateway, if necessary.
@@ -447,6 +450,20 @@ func (p *NginxProvisioner) RegisterGateway(
447450
p.cfg.Logger.Error(err, "error building some nginx resources")
448451
}
449452

453+
// If NGINX deployment type switched between Deployment and DaemonSet, clean up the old one.
454+
nginxResources := p.store.getNginxResourcesForGateway(gatewayNSName)
455+
if nginxResources != nil {
456+
if needToDeleteDaemonSet(nginxResources) {
457+
if err := p.deleteObject(ctx, &appsv1.DaemonSet{ObjectMeta: nginxResources.DaemonSet}); err != nil {
458+
p.cfg.Logger.Error(err, "error deleting nginx resource")
459+
}
460+
} else if needToDeleteDeployment(nginxResources) {
461+
if err := p.deleteObject(ctx, &appsv1.Deployment{ObjectMeta: nginxResources.Deployment}); err != nil {
462+
p.cfg.Logger.Error(err, "error deleting nginx resource")
463+
}
464+
}
465+
}
466+
450467
if err := p.provisionNginx(ctx, resourceName, gateway.Source, objects); err != nil {
451468
return fmt.Errorf("error provisioning nginx resources: %w", err)
452469
}
@@ -458,3 +475,31 @@ func (p *NginxProvisioner) RegisterGateway(
458475

459476
return nil
460477
}
478+
479+
func needToDeleteDeployment(cfg *NginxResources) bool {
480+
if cfg.Deployment.Name != "" {
481+
if cfg.Gateway != nil && cfg.Gateway.EffectiveNginxProxy != nil &&
482+
cfg.Gateway.EffectiveNginxProxy.Kubernetes != nil &&
483+
cfg.Gateway.EffectiveNginxProxy.Kubernetes.DaemonSet != nil {
484+
return true
485+
}
486+
}
487+
488+
return false
489+
}
490+
491+
func needToDeleteDaemonSet(cfg *NginxResources) bool {
492+
if cfg.DaemonSet.Name != "" && cfg.Gateway != nil {
493+
if cfg.Gateway.EffectiveNginxProxy != nil &&
494+
cfg.Gateway.EffectiveNginxProxy.Kubernetes != nil &&
495+
cfg.Gateway.EffectiveNginxProxy.Kubernetes.Deployment != nil {
496+
return true
497+
} else if cfg.Gateway.EffectiveNginxProxy == nil ||
498+
cfg.Gateway.EffectiveNginxProxy.Kubernetes == nil ||
499+
cfg.Gateway.EffectiveNginxProxy.Kubernetes.DaemonSet == nil {
500+
return true
501+
}
502+
}
503+
504+
return false
505+
}

internal/mode/static/provisioner/provisioner_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,80 @@ func TestRegisterGateway(t *testing.T) {
314314
g.Expect(deploymentStore.RemoveCallCount()).To(Equal(1))
315315
}
316316

317+
func TestRegisterGateway_CleansUpOldDeploymentOrDaemonSet(t *testing.T) {
318+
t.Parallel()
319+
g := NewWithT(t)
320+
321+
// Setup: Gateway switches from Deployment to DaemonSet
322+
gateway := &graph.Gateway{
323+
Source: &gatewayv1.Gateway{
324+
ObjectMeta: metav1.ObjectMeta{
325+
Name: "gw",
326+
Namespace: "default",
327+
},
328+
},
329+
Valid: true,
330+
EffectiveNginxProxy: &graph.EffectiveNginxProxy{
331+
Kubernetes: &ngfAPIv1alpha2.KubernetesSpec{
332+
DaemonSet: &ngfAPIv1alpha2.DaemonSetSpec{},
333+
},
334+
},
335+
}
336+
337+
// Create a fake deployment that should be cleaned up
338+
oldDeployment := &appsv1.Deployment{
339+
ObjectMeta: metav1.ObjectMeta{
340+
Name: "gw-nginx",
341+
Namespace: "default",
342+
},
343+
}
344+
provisioner, fakeClient, _ := defaultNginxProvisioner(gateway.Source, oldDeployment)
345+
// Simulate store tracking an old Deployment
346+
provisioner.store.nginxResources[types.NamespacedName{Name: "gw", Namespace: "default"}] = &NginxResources{
347+
Deployment: oldDeployment.ObjectMeta,
348+
}
349+
350+
// RegisterGateway should clean up the Deployment and create a DaemonSet
351+
g.Expect(provisioner.RegisterGateway(t.Context(), gateway, "gw-nginx")).To(Succeed())
352+
353+
// Deployment should be deleted
354+
err := fakeClient.Get(t.Context(), types.NamespacedName{Name: "gw-nginx", Namespace: "default"}, &appsv1.Deployment{})
355+
g.Expect(err).To(HaveOccurred())
356+
357+
// DaemonSet should exist
358+
err = fakeClient.Get(t.Context(), types.NamespacedName{Name: "gw-nginx", Namespace: "default"}, &appsv1.DaemonSet{})
359+
g.Expect(err).ToNot(HaveOccurred())
360+
361+
// Now test the opposite: switch from DaemonSet to Deployment
362+
gateway.EffectiveNginxProxy = &graph.EffectiveNginxProxy{
363+
Kubernetes: &ngfAPIv1alpha2.KubernetesSpec{
364+
Deployment: &ngfAPIv1alpha2.DeploymentSpec{},
365+
},
366+
}
367+
368+
oldDaemonSet := &appsv1.DaemonSet{
369+
ObjectMeta: metav1.ObjectMeta{
370+
Name: "gw-nginx",
371+
Namespace: "default",
372+
},
373+
}
374+
375+
provisioner, fakeClient, _ = defaultNginxProvisioner(gateway.Source, oldDaemonSet)
376+
provisioner.store.nginxResources[types.NamespacedName{Name: "gw", Namespace: "default"}] = &NginxResources{
377+
DaemonSet: oldDaemonSet.ObjectMeta,
378+
}
379+
380+
g.Expect(provisioner.RegisterGateway(t.Context(), gateway, "gw-nginx")).To(Succeed())
381+
382+
// DaemonSet should be deleted
383+
err = fakeClient.Get(t.Context(), types.NamespacedName{Name: "gw-nginx", Namespace: "default"}, &appsv1.DaemonSet{})
384+
g.Expect(err).To(HaveOccurred())
385+
386+
// Deployment should exist
387+
err = fakeClient.Get(t.Context(), types.NamespacedName{Name: "gw-nginx", Namespace: "default"}, &appsv1.Deployment{})
388+
g.Expect(err).ToNot(HaveOccurred())
389+
}
390+
317391
func TestNonLeaderProvisioner(t *testing.T) {
318392
t.Parallel()
319393
g := NewWithT(t)

internal/mode/static/provisioner/store_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func TestRegisterResourceInGatewayConfig(t *testing.T) {
104104
store := newStore([]string{"docker-secret"}, "agent-tls-secret", "jwt-secret", "ca-secret", "client-ssl-secret")
105105
nsName := types.NamespacedName{Name: "test-gateway", Namespace: "default"}
106106

107-
registerAndGetResources := func(obj interface{}) *NginxResources {
107+
registerAndGetResources := func(obj any) *NginxResources {
108108
changed := store.registerResourceInGatewayConfig(nsName, obj)
109109
g.Expect(changed).To(BeTrue(), fmt.Sprintf("failed: %T", obj))
110110
g.Expect(store.nginxResources).To(HaveKey(nsName), fmt.Sprintf("failed: %T", obj))

0 commit comments

Comments
 (0)