Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions internal/controller/provisioner/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,28 @@ func (p *NginxProvisioner) buildNginxResourceObjects(
ports[int32(listener.Port)] = struct{}{}
}

service, err := buildNginxService(objectMeta, nProxyCfg, ports, selectorLabels)
// Create separate copies of objectMeta for service and deployment to avoid shared map references
serviceObjectMeta := metav1.ObjectMeta{
Name: objectMeta.Name,
Namespace: objectMeta.Namespace,
Labels: maps.Clone(objectMeta.Labels),
Annotations: maps.Clone(objectMeta.Annotations),
}

deploymentObjectMeta := metav1.ObjectMeta{
Name: objectMeta.Name,
Namespace: objectMeta.Namespace,
Labels: maps.Clone(objectMeta.Labels),
Annotations: maps.Clone(objectMeta.Annotations),
}

service, err := buildNginxService(serviceObjectMeta, nProxyCfg, ports, selectorLabels)
if err != nil {
errs = append(errs, err)
}

deployment, err := p.buildNginxDeployment(
objectMeta,
deploymentObjectMeta,
nProxyCfg,
ngxIncludesConfigMapName,
ngxAgentConfigMapName,
Expand Down
58 changes: 58 additions & 0 deletions internal/controller/provisioner/objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1696,4 +1696,62 @@ func TestBuildNginxResourceObjects_Patches(t *testing.T) {
}
g.Expect(svc).ToNot(BeNil())
g.Expect(svc.Labels).ToNot(HaveKey("patched")) // Should not have patch-related labels

// Test that Service patches don't affect Deployment labels and vice versa (cross-contamination)
nProxyCfg = &graph.EffectiveNginxProxy{
Kubernetes: &ngfAPIv1alpha2.KubernetesSpec{
Service: &ngfAPIv1alpha2.ServiceSpec{
Patches: []ngfAPIv1alpha2.Patch{
{
Type: helpers.GetPointer(ngfAPIv1alpha2.PatchTypeStrategicMerge),
Value: &apiextv1.JSON{
Raw: []byte(`{"metadata":{"labels":{"service-only":"true"}}}`),
},
},
},
},
Deployment: &ngfAPIv1alpha2.DeploymentSpec{
Patches: []ngfAPIv1alpha2.Patch{
{
Type: helpers.GetPointer(ngfAPIv1alpha2.PatchTypeStrategicMerge),
Value: &apiextv1.JSON{
Raw: []byte(`{"metadata":{"labels":{"deployment-only":"true"}}}`),
},
},
},
},
},
}

objects, err = provisioner.buildNginxResourceObjects("gw-nginx", gateway, nProxyCfg)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(objects).To(HaveLen(6))

// Find and validate service - should only have service-specific labels
svc = nil
for _, obj := range objects {
if s, ok := obj.(*corev1.Service); ok {
svc = s
break
}
}
g.Expect(svc).ToNot(BeNil())
g.Expect(svc.Labels).To(HaveKeyWithValue("service-only", "true"))
g.Expect(svc.Labels).ToNot(HaveKey("deployment-only"))

// Find and validate deployment - should only have deployment-specific labels
dep = nil
for _, obj := range objects {
if d, ok := obj.(*appsv1.Deployment); ok {
dep = d
break
}
}
g.Expect(dep).ToNot(BeNil())
g.Expect(dep.Labels).To(HaveKeyWithValue("deployment-only", "true"))
g.Expect(dep.Labels).ToNot(HaveKey("service-only"))

// Both should still have the common base labels
g.Expect(svc.Labels).To(HaveKeyWithValue("app", "nginx"))
g.Expect(dep.Labels).To(HaveKeyWithValue("app", "nginx"))
}
Loading