Skip to content

Commit c854da7

Browse files
committed
Add nginx config hash; remove API group cache
Compute a SHA-256 of the generated nginx.conf and annotate the reverse-proxy Deployment with typesense-operator/nginx-config-hash so changes to ingress directives cause a rollout. Removed the previous configMapUpdated/restart-timestamp logic. Updated getNodes call to pass the resizeOp flag. Removed apiGroupsCache, its mutex and caching logic from IsApiGroupDeployed so discovery is queried directly. Added tests to verify the nginx config hash changes when ingress directives change and remains stable on no-op reconciles. Cleaned up related imports and unused fields.
1 parent 64995dd commit c854da7

5 files changed

Lines changed: 64 additions & 34 deletions

File tree

internal/controller/typesensecluster_configmap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (r *TypesenseClusterReconciler) updateConfigMap(ctx context.Context, ts *ts
119119
}
120120
}
121121

122-
nodes, err := r.getNodes(ctx, ts, *replicas, false)
122+
nodes, err := r.getNodes(ctx, ts, *replicas, resizeOp)
123123
if err != nil {
124124
return false, err
125125
}

internal/controller/typesensecluster_controller.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"fmt"
2323
"net/http"
2424
"strings"
25-
"sync"
2625
"time"
2726

2827
"github.com/go-logr/logr"
@@ -64,9 +63,7 @@ type TypesenseClusterReconciler struct {
6463
InCluster bool
6564
HttpClient *http.Client
6665

67-
apiGroupsCache map[string]bool
68-
apiGroupsMutex sync.RWMutex
69-
serverVersion string
66+
serverVersion string
7067
}
7168

7269
type TypesenseClusterReconciliationPhase struct {

internal/controller/typesensecluster_controller_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"k8s.io/client-go/kubernetes"
3434
"k8s.io/client-go/rest"
3535
"k8s.io/client-go/tools/record"
36+
"k8s.io/utils/ptr"
3637
"sigs.k8s.io/controller-runtime/pkg/client"
3738
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3839

@@ -238,6 +239,58 @@ var _ = Describe("TypesenseCluster Controller", func() {
238239
}, resolverSvc)).To(Succeed())
239240
Expect(resolverSvc.Spec.Ports).To(HaveLen(2))
240241
})
242+
243+
It("should update the reverse proxy deployment when ingress configuration changes", func(ctx context.Context) {
244+
tsResource := &tsv1alpha1.TypesenseCluster{}
245+
Expect(k8sClient.Get(ctx, typeNamespacedName, tsResource)).To(Succeed())
246+
247+
// Enable Ingress with initial directives
248+
tsResource.Spec.Ingress = &tsv1alpha1.IngressSpec{
249+
Host: "ts.example.com",
250+
HttpDirectives: ptr.To("client_max_body_size 10m;"),
251+
IngressClassName: "nginx",
252+
}
253+
Expect(k8sClient.Update(ctx, tsResource)).To(Succeed())
254+
255+
controllerReconciler := newControllerReconciler()
256+
runReconcile(ctx, controllerReconciler, reconcile.Request{NamespacedName: typeNamespacedName})
257+
258+
deploy := &appsv1.Deployment{}
259+
Expect(k8sClient.Get(ctx, types.NamespacedName{
260+
Name: fmt.Sprintf(ClusterReverseProxy, resourceName),
261+
Namespace: defaultNamespace,
262+
}, deploy)).To(Succeed())
263+
264+
initialHash := deploy.Spec.Template.Annotations[NginxConfigHashAnnotationKey]
265+
Expect(initialHash).ToNot(BeEmpty())
266+
267+
// Update Ingress directives
268+
Expect(k8sClient.Get(ctx, typeNamespacedName, tsResource)).To(Succeed())
269+
tsResource.Spec.Ingress.HttpDirectives = ptr.To("client_max_body_size 20m;")
270+
Expect(k8sClient.Update(ctx, tsResource)).To(Succeed())
271+
272+
runReconcile(ctx, controllerReconciler, reconcile.Request{NamespacedName: typeNamespacedName})
273+
274+
Expect(k8sClient.Get(ctx, types.NamespacedName{
275+
Name: fmt.Sprintf(ClusterReverseProxy, resourceName),
276+
Namespace: defaultNamespace,
277+
}, deploy)).To(Succeed())
278+
279+
newHash := deploy.Spec.Template.Annotations[NginxConfigHashAnnotationKey]
280+
Expect(newHash).ToNot(BeEmpty())
281+
Expect(newHash).ToNot(Equal(initialHash))
282+
283+
// Re-reconcile without changes and check that the hash remains the same
284+
runReconcile(ctx, controllerReconciler, reconcile.Request{NamespacedName: typeNamespacedName})
285+
286+
Expect(k8sClient.Get(ctx, types.NamespacedName{
287+
Name: fmt.Sprintf(ClusterReverseProxy, resourceName),
288+
Namespace: defaultNamespace,
289+
}, deploy)).To(Succeed())
290+
291+
finalHash := deploy.Spec.Template.Annotations[NginxConfigHashAnnotationKey]
292+
Expect(finalHash).To(Equal(newHash))
293+
})
241294
})
242295

243296
Context("When reconciling a resource in a custom namespace", func() {

internal/controller/typesensecluster_helpers.go

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,38 +52,16 @@ func (r *TypesenseClusterReconciler) IsFeatureSupported(minimum string) (bool, s
5252
}
5353

5454
func (r *TypesenseClusterReconciler) IsApiGroupDeployed(apiGroup string) (bool, error) {
55-
r.apiGroupsMutex.RLock()
56-
if supported, exists := r.apiGroupsCache[apiGroup]; exists {
57-
r.apiGroupsMutex.RUnlock()
58-
if supported {
59-
return true, nil
60-
}
61-
} else {
62-
r.apiGroupsMutex.RUnlock()
63-
}
64-
6555
apiGroupList, err := r.DiscoveryClient.ServerGroups()
6656
if err != nil {
6757
return false, err
6858
}
6959

7060
for _, ag := range apiGroupList.Groups {
7161
if ag.Name == apiGroup {
72-
r.apiGroupsMutex.Lock()
73-
if r.apiGroupsCache == nil {
74-
r.apiGroupsCache = make(map[string]bool)
75-
}
76-
r.apiGroupsCache[apiGroup] = true
77-
r.apiGroupsMutex.Unlock()
7862
return true, nil
7963
}
8064
}
8165

82-
r.apiGroupsMutex.Lock()
83-
if r.apiGroupsCache == nil {
84-
r.apiGroupsCache = make(map[string]bool)
85-
}
86-
r.apiGroupsCache[apiGroup] = false
87-
r.apiGroupsMutex.Unlock()
8866
return false, nil
8967
}

internal/controller/typesensecluster_ingress.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ package controller
33
import (
44
"bytes"
55
"context"
6+
"crypto/sha256"
67
"fmt"
78
"maps"
89
"strconv"
910
"strings"
1011
"text/template"
11-
"time"
1212

1313
tsv1alpha1 "github.com/akyriako/typesense-operator/api/v1alpha1"
1414
appsv1 "k8s.io/api/apps/v1"
@@ -76,6 +76,8 @@ http {
7676
const (
7777
clusterIssuerAnnotationKey = "cert-manager.io/cluster-issuer"
7878
nginxConfKey = "nginx.conf"
79+
// NginxConfigHashAnnotationKey is the annotation key for the nginx config hash.
80+
NginxConfigHashAnnotationKey = "typesense-operator/nginx-config-hash"
7981
)
8082

8183
//nolint:gocyclo // ReconcileIngress coordinates multiple child resources
@@ -158,7 +160,6 @@ func (r *TypesenseClusterReconciler) ReconcileIngress(ctx context.Context, ts *t
158160
}
159161
}
160162

161-
configMapUpdated := false
162163
if !configMapExists {
163164
r.logger.V(debugLevel).Info("creating ingress config map", "configmap", configMapObjectKey.Name)
164165

@@ -180,8 +181,6 @@ func (r *TypesenseClusterReconciler) ReconcileIngress(ctx context.Context, ts *t
180181
if err != nil {
181182
return err
182183
}
183-
184-
configMapUpdated = true
185184
}
186185
}
187186

@@ -207,11 +206,14 @@ func (r *TypesenseClusterReconciler) ReconcileIngress(ctx context.Context, ts *t
207206
replicas = *ts.Spec.Ingress.Replicas
208207
}
209208

210-
podAnnotations := make(map[string]string)
211-
if configMapUpdated {
212-
podAnnotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
209+
nginxConf, err := r.getIngressNginxConf(ts)
210+
if err != nil {
211+
return err
213212
}
214213

214+
podAnnotations := make(map[string]string)
215+
podAnnotations[NginxConfigHashAnnotationKey] = fmt.Sprintf("%x", sha256.Sum256([]byte(nginxConf)))
216+
215217
desiredDeployment := &appsv1.Deployment{
216218
TypeMeta: metav1.TypeMeta{
217219
APIVersion: appsv1.SchemeGroupVersion.String(),

0 commit comments

Comments
 (0)