Skip to content

Commit 2fe41e1

Browse files
authored
add meshconfig reconciliation to gateway controllers (#57893) (#57915)
* add meshconfig reconciliation to gateway controllers * refactor test helper for lint --------- Signed-off-by: Lucas Copi <[email protected]>
1 parent b98aaa5 commit 2fe41e1

File tree

3 files changed

+112
-8
lines changed

3 files changed

+112
-8
lines changed

pilot/pkg/config/kube/gateway/deploymentcontroller.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ func NewDeploymentController(
331331
}
332332
}))
333333

334+
dc.env.Watcher.AddMeshHandler(func() {
335+
// if there is a change to the meshconfig we need to reprocess all gateways
336+
// to reconcile things like the deployment image
337+
for _, gw := range dc.gateways.List(corev1.NamespaceAll, klabels.Everything()) {
338+
dc.queue.AddObject(gw)
339+
}
340+
})
341+
334342
// we check if the generation has changed on a gateway, or if the annotations or labels have been updated
335343
// reconciliation is expensive, so status updates for attachedRoutes can be skipped.
336344

@@ -403,6 +411,7 @@ func (d *DeploymentController) Run(stop <-chan struct{}) {
403411
d.gateways.HasSynced,
404412
d.gatewayClasses.HasSynced,
405413
d.tagWatcher.HasSynced,
414+
d.env.Watcher.AsCollection().HasSynced,
406415
)
407416
d.queue.Run(stop)
408417
controllers.ShutdownAll(

pilot/pkg/config/kube/gateway/deploymentcontroller_test.go

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"istio.io/istio/pkg/config"
4747
"istio.io/istio/pkg/config/constants"
4848
"istio.io/istio/pkg/config/mesh"
49+
"istio.io/istio/pkg/config/mesh/meshwatcher"
4950
"istio.io/istio/pkg/config/schema/gvk"
5051
"istio.io/istio/pkg/config/schema/gvr"
5152
"istio.io/istio/pkg/kube"
@@ -574,7 +575,7 @@ metadata:
574575
kclient.NewWriteClient[*k8sbeta.Gateway](client).Create(tt.gw.DeepCopy())
575576
kclient.NewWriteClient[*appsv1.Deployment](client).Create(upgradeDeployment)
576577
stop := test.NewStop(t)
577-
env := model.NewEnvironment()
578+
env := newTestEnv()
578579
env.PushContext().ProxyConfigs = tt.pcs
579580
tw := revisions.NewTagWatcher(client, "")
580581
go tw.Run(stop)
@@ -612,6 +613,79 @@ metadata:
612613
}
613614
}
614615

616+
func TestMeshGatewayReconciliation(t *testing.T) {
617+
c := kube.NewFakeClient(&corev1.Namespace{
618+
ObjectMeta: metav1.ObjectMeta{
619+
Name: "default",
620+
},
621+
})
622+
tw := revisions.NewTagWatcher(c, "default")
623+
624+
// not using the new helper becase
625+
// we need to explicitly modify this
626+
// later in the thread
627+
env := model.NewEnvironment()
628+
m := mesh.DefaultMeshConfig()
629+
watch := meshwatcher.NewTestWatcher(m)
630+
env.Watcher = watch
631+
d := NewDeploymentController(c, "", env, testInjectionConfig(t, ""), func(fn func()) {}, tw, "", "")
632+
633+
reconciles := atomic.NewInt32(0)
634+
wantReconcile := int32(0)
635+
expectReconciled := func() {
636+
t.Helper()
637+
wantReconcile++
638+
assert.EventuallyEqual(t, reconciles.Load, wantReconcile, retry.Timeout(time.Second*5), retry.Message("no reconciliation"))
639+
}
640+
641+
writes := make(chan string, 2)
642+
d.patcher = func(g schema.GroupVersionResource, name string, namespace string, data []byte, subresources ...string) error {
643+
if g == gvr.Service {
644+
reconciles.Inc()
645+
}
646+
if g == gvr.KubernetesGateway {
647+
b, err := yaml.JSONToYAML(data)
648+
if err != nil {
649+
return err
650+
}
651+
writes <- string(b)
652+
}
653+
return nil
654+
}
655+
656+
stop := test.NewStop(t)
657+
gws := clienttest.Wrap(t, d.gateways)
658+
go tw.Run(stop)
659+
go d.Run(stop)
660+
c.RunAndWait(stop)
661+
kube.WaitForCacheSync("test", stop, d.queue.HasSynced)
662+
663+
// create the initial gateway for the deployment controller
664+
// to reconcile when the meshconfig changes
665+
defaultGateway := &k8sbeta.Gateway{
666+
ObjectMeta: metav1.ObjectMeta{
667+
Name: "gw",
668+
Namespace: "default",
669+
},
670+
Spec: k8s.GatewaySpec{
671+
GatewayClassName: k8s.ObjectName(features.GatewayAPIDefaultGatewayClass),
672+
},
673+
}
674+
gws.Create(defaultGateway)
675+
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch())
676+
expectReconciled()
677+
assert.ChannelIsEmpty(t, writes)
678+
679+
// modify the meshconfig to trigger a reprocess of the gateways
680+
m.DefaultConfig.Image = &istioio_networking_v1beta1.ProxyImage{ImageType: "distroless"}
681+
watch.Set(m)
682+
683+
// confirm the gateway is reconciled
684+
assert.ChannelHasItem(t, writes)
685+
expectReconciled()
686+
assert.ChannelIsEmpty(t, writes)
687+
}
688+
615689
func buildFilter(allowedNamespace string) kubetypes.DynamicObjectFilter {
616690
return kubetypes.NewStaticObjectFilter(func(obj any) bool {
617691
if ns, ok := obj.(string); ok {
@@ -638,7 +712,7 @@ func TestVersionManagement(t *testing.T) {
638712
},
639713
})
640714
tw := revisions.NewTagWatcher(c, "default")
641-
env := &model.Environment{}
715+
env := newTestEnv()
642716
d := NewDeploymentController(c, "", env, testInjectionConfig(t, ""), func(fn func()) {}, tw, "", "")
643717
reconciles := atomic.NewInt32(0)
644718
wantReconcile := int32(0)
@@ -678,7 +752,7 @@ func TestVersionManagement(t *testing.T) {
678752
},
679753
}
680754
gws.Create(defaultGateway)
681-
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch(ControllerVersion))
755+
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch())
682756
expectReconciled()
683757
assert.ChannelIsEmpty(t, writes)
684758
// Test fake doesn't actual do Apply, so manually do this
@@ -699,7 +773,7 @@ func TestVersionManagement(t *testing.T) {
699773
defaultGateway.Annotations = map[string]string{}
700774
gws.Update(defaultGateway)
701775
expectReconciled()
702-
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch(ControllerVersion))
776+
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch())
703777
assert.ChannelIsEmpty(t, writes)
704778
// Test fake doesn't actual do Apply, so manually do this
705779
defaultGateway.Annotations = map[string]string{ControllerVersionAnnotation: fmt.Sprint(ControllerVersion)}
@@ -712,7 +786,7 @@ func TestVersionManagement(t *testing.T) {
712786
defaultGateway.Annotations = map[string]string{ControllerVersionAnnotation: fmt.Sprint(1)}
713787
gws.Update(defaultGateway)
714788
expectReconciled()
715-
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch(ControllerVersion))
789+
assert.Equal(t, assert.ChannelHasItem(t, writes), buildPatch())
716790
assert.ChannelIsEmpty(t, writes)
717791
// Test fake doesn't actual do Apply, so manually do this
718792
defaultGateway.Annotations = map[string]string{ControllerVersionAnnotation: fmt.Sprint(ControllerVersion)}
@@ -995,7 +1069,7 @@ func TestHandlerEnqueueFunction(t *testing.T) {
9951069
client.Kube().Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = &kubeVersion.Info{Major: "1", Minor: "28"}
9961070

9971071
tw := revisions.NewTagWatcher(client, "")
998-
env := model.NewEnvironment()
1072+
env := newTestEnv()
9991073
go tw.Run(stop)
10001074

10011075
d := NewDeploymentController(client, cluster.ID(features.ClusterName), env, dummyWebHookInjectFn, func(fn func()) {
@@ -1075,11 +1149,22 @@ global:
10751149
return injConfig
10761150
}
10771151

1078-
func buildPatch(version int) string {
1152+
// buildPatch used to accept a version
1153+
// but lint flagged that ControllerVersion
1154+
// was always used so the function has been
1155+
// adjusted to hardcode ControllerVersion
1156+
func buildPatch() string {
10791157
return fmt.Sprintf(`apiVersion: gateway.networking.k8s.io/v1beta1
10801158
kind: Gateway
10811159
metadata:
10821160
annotations:
10831161
gateway.istio.io/controller-version: "%d"
1084-
`, version)
1162+
`, ControllerVersion)
1163+
}
1164+
1165+
func newTestEnv() *model.Environment {
1166+
env := model.NewEnvironment()
1167+
env.Watcher = meshwatcher.NewTestWatcher(mesh.DefaultMeshConfig())
1168+
1169+
return env
10851170
}

releasenotes/notes/57890.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: release-notes/v2
2+
kind: bug-fix
3+
area: installation
4+
5+
issue:
6+
- https://github.com/istio/istio/issues/57890
7+
8+
releaseNotes:
9+
- |
10+
**Fixed** missing gateway reconciliation for meshconfig changes

0 commit comments

Comments
 (0)