Skip to content

Commit be2ddf1

Browse files
Merge pull request #843 from lmiccini/rabbitmq_nil_check
Fix nil pointer dereferences in NotificationsBus handling
2 parents fe8dc27 + 32c6499 commit be2ddf1

6 files changed

Lines changed: 150 additions & 2 deletions

File tree

api/v1beta1/autoscaling_webhook.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,17 @@ func (spec *AodhCore) Default() {
9090

9191
// getDeprecatedFields returns the centralized list of deprecated fields for AodhCore
9292
func (spec *AodhCore) getDeprecatedFields(old *AodhCore) []common_webhook.DeprecatedFieldUpdate {
93+
var newValue *string
94+
if spec.NotificationsBus != nil {
95+
newValue = &spec.NotificationsBus.Cluster
96+
}
97+
9398
deprecatedFields := []common_webhook.DeprecatedFieldUpdate{
9499
{
95100
DeprecatedFieldName: "rabbitMqClusterName",
96101
NewFieldPath: []string{"notificationsBus", "cluster"},
97102
NewDeprecatedValue: &spec.RabbitMqClusterName,
98-
NewValue: &spec.NotificationsBus.Cluster,
103+
NewValue: newValue,
99104
},
100105
}
101106

api/v1beta1/ceilometer_webhook.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,17 @@ func (spec *CeilometerSpecCore) Default() {
9898

9999
// getDeprecatedFields returns the centralized list of deprecated fields for CeilometerSpecCore
100100
func (spec *CeilometerSpecCore) getDeprecatedFields(old *CeilometerSpecCore) []common_webhook.DeprecatedFieldUpdate {
101+
var newValue *string
102+
if spec.NotificationsBus != nil {
103+
newValue = &spec.NotificationsBus.Cluster
104+
}
105+
101106
deprecatedFields := []common_webhook.DeprecatedFieldUpdate{
102107
{
103108
DeprecatedFieldName: "rabbitMqClusterName",
104109
NewFieldPath: []string{"notificationsBus", "cluster"},
105110
NewDeprecatedValue: &spec.RabbitMqClusterName,
106-
NewValue: &spec.NotificationsBus.Cluster,
111+
NewValue: newValue,
107112
},
108113
}
109114

api/v1beta1/telemetry_webhook_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,65 @@ func TestCloudKittySpecBaseDefault(t *testing.T) {
9393
g.Expect(spec.MessagingBus.Cluster).To(Equal("rabbitmq"),
9494
"CloudKitty messagingBus.cluster should be defaulted to rabbitmq")
9595
}
96+
97+
// TestAodhCoreDefaultWithNilNotificationsBus tests that Default() doesn't panic with nil NotificationsBus
98+
func TestAodhCoreDefaultWithNilNotificationsBus(t *testing.T) {
99+
g := NewWithT(t)
100+
101+
spec := &AodhCore{}
102+
// NotificationsBus is nil (not set)
103+
104+
// Call Default() - should not panic
105+
spec.Default()
106+
107+
// NotificationsBus should remain nil
108+
g.Expect(spec.NotificationsBus).To(BeNil(),
109+
"Aodh notificationsBus should remain nil when not configured")
110+
}
111+
112+
// TestCeilometerSpecCoreDefaultWithNilNotificationsBus tests that Default() doesn't panic with nil NotificationsBus
113+
func TestCeilometerSpecCoreDefaultWithNilNotificationsBus(t *testing.T) {
114+
g := NewWithT(t)
115+
116+
spec := &CeilometerSpecCore{}
117+
// NotificationsBus is nil (not set)
118+
119+
// Call Default() - should not panic
120+
spec.Default()
121+
122+
// NotificationsBus should remain nil
123+
g.Expect(spec.NotificationsBus).To(BeNil(),
124+
"Ceilometer notificationsBus should remain nil when not configured")
125+
}
126+
127+
// TestAodhCoreValidateCreateWithNilNotificationsBus tests validation with nil NotificationsBus
128+
func TestAodhCoreValidateCreateWithNilNotificationsBus(t *testing.T) {
129+
g := NewWithT(t)
130+
131+
spec := &AodhCore{}
132+
// NotificationsBus is nil, but we have the deprecated field set
133+
spec.RabbitMqClusterName = "old-rabbitmq"
134+
135+
// Call ValidateCreate - should not panic even with nil NotificationsBus
136+
warns, errs := spec.ValidateCreate(nil, "default")
137+
138+
// Should have a warning about deprecated field
139+
g.Expect(warns).ToNot(BeEmpty(), "Should have warning about deprecated rabbitMqClusterName field")
140+
g.Expect(errs).To(BeEmpty(), "Should not have errors")
141+
}
142+
143+
// TestCeilometerSpecCoreValidateCreateWithNilNotificationsBus tests validation with nil NotificationsBus
144+
func TestCeilometerSpecCoreValidateCreateWithNilNotificationsBus(t *testing.T) {
145+
g := NewWithT(t)
146+
147+
spec := &CeilometerSpecCore{}
148+
// NotificationsBus is nil, but we have the deprecated field set
149+
spec.RabbitMqClusterName = "old-rabbitmq"
150+
151+
// Call ValidateCreate - should not panic even with nil NotificationsBus
152+
warns, errs := spec.ValidateCreate(nil, "default")
153+
154+
// Should have a warning about deprecated field
155+
g.Expect(warns).ToNot(BeEmpty(), "Should have warning about deprecated rabbitMqClusterName field")
156+
g.Expect(errs).To(BeEmpty(), "Should not have errors")
157+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/go-logr/logr v1.4.3
1010
github.com/grafana/loki/operator/api/loki v0.0.0-20250910094332-a082b8a061ba
1111
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7
12+
github.com/onsi/gomega v1.39.0
1213
github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20260126095359-39e97ca5471e
1314
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260126091827-7758173fbb09
1415
github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260126094240-81b572e8f653
@@ -103,6 +104,7 @@ require (
103104
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
104105
google.golang.org/grpc v1.71.1 // indirect
105106
google.golang.org/protobuf v1.36.7 // indirect
107+
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
106108
gopkg.in/inf.v0 v0.9.1 // indirect
107109
gopkg.in/yaml.v2 v2.4.0 // indirect
108110
gopkg.in/yaml.v3 v3.0.1 // indirect

internal/controller/autoscaling_controller.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ import (
7171
// ErrNotificationsURLSecretNotSet is returned when NotificationsURLSecret is not set in the instance status
7272
var ErrNotificationsURLSecretNotSet = errors.New("NotificationsURLSecret is not set")
7373

74+
// ErrRabbitMQConfigNil is returned when rabbitmqConfig parameter is nil
75+
var ErrRabbitMQConfigNil = errors.New("rabbitmqConfig is nil - NotificationsBus must be configured")
76+
7477
// AutoscalingReconciler reconciles a Autoscaling object
7578
type AutoscalingReconciler struct {
7679
client.Client
@@ -903,6 +906,11 @@ func (r *AutoscalingReconciler) transportURLCreateOrUpdate(
903906
) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) {
904907
// Aodh only uses NotificationsBus TransportURL
905908
rmqName := fmt.Sprintf("%s-notifications-transport", autoscaling.ServiceName)
909+
910+
if rabbitmqConfig == nil {
911+
return nil, controllerutil.OperationResultNone, ErrRabbitMQConfigNil
912+
}
913+
906914
config := rabbitmqConfig
907915

908916
// Prepare the spec values before CreateOrUpdate so webhooks see them during CREATE
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/onsi/gomega"
8+
telemetryv1 "github.com/openstack-k8s-operators/telemetry-operator/api/v1beta1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
12+
)
13+
14+
// TestTransportURLCreateOrUpdateWithNilConfig tests that transportURLCreateOrUpdate
15+
// handles nil rabbitmqConfig gracefully without panicking
16+
func TestTransportURLCreateOrUpdateWithNilConfig(t *testing.T) {
17+
g := gomega.NewWithT(t)
18+
19+
// Create a fake client
20+
scheme := runtime.NewScheme()
21+
_ = telemetryv1.AddToScheme(scheme)
22+
23+
client := fake.NewClientBuilder().WithScheme(scheme).Build()
24+
25+
reconciler := &AutoscalingReconciler{
26+
Client: client,
27+
Scheme: scheme,
28+
}
29+
30+
instance := &telemetryv1.Autoscaling{
31+
ObjectMeta: metav1.ObjectMeta{
32+
Name: "test-autoscaling",
33+
Namespace: "test-namespace",
34+
},
35+
Spec: telemetryv1.AutoscalingSpec{
36+
Aodh: telemetryv1.Aodh{
37+
AodhCore: telemetryv1.AodhCore{
38+
// NotificationsBus is nil
39+
NotificationsBus: nil,
40+
},
41+
},
42+
},
43+
}
44+
45+
serviceLabels := map[string]string{
46+
"app": "test",
47+
}
48+
49+
// Call transportURLCreateOrUpdate with nil rabbitmqConfig
50+
// This should return an error, not panic
51+
transportURL, op, err := reconciler.transportURLCreateOrUpdate(
52+
context.Background(),
53+
instance,
54+
serviceLabels,
55+
nil, // nil rabbitmqConfig
56+
)
57+
58+
// Should return an error
59+
g.Expect(err).To(gomega.HaveOccurred(), "Should return error when rabbitmqConfig is nil")
60+
g.Expect(err.Error()).To(gomega.ContainSubstring("rabbitmqConfig is nil"),
61+
"Error message should indicate rabbitmqConfig is nil")
62+
63+
// Should return nil transportURL and OperationResultNone
64+
g.Expect(transportURL).To(gomega.BeNil(), "TransportURL should be nil when config is nil")
65+
g.Expect(string(op)).To(gomega.Equal("unchanged"), "Operation should be OperationResultNone when config is nil")
66+
}

0 commit comments

Comments
 (0)