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
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ Certificate rotation is automatic and zero-downtime. When a certificate is renew
| **CertManager** | cert-manager auto-renews based on the Certificate CR's `renewBefore` | None |
| **Provided** | You update the Secret contents (manually or via CSI driver sync) | Update the Secret |

!!! note
Changing `spec.tls.gateway.provided.secretName` to point to a **different** Secret triggers a rolling restart of the DocumentDB cluster pods, which causes a brief period of downtime. To rotate certificates without downtime, update the contents of the **existing** Secret instead of changing the Secret name.

### Monitoring Certificate Expiration

```bash
Expand Down
6 changes: 5 additions & 1 deletion operator/src/internal/controller/documentdb_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ func (r *DocumentDBReconciler) Reconcile(ctx context.Context, req ctrl.Request)
if currentCnpgCluster.Annotations == nil {
currentCnpgCluster.Annotations = map[string]string{}
}
currentCnpgCluster.Annotations["documentdb.io/gateway-tls-rev"] = time.Now().Format(time.RFC3339Nano)
// CNPG does not auto-restart pods for plugin parameter changes.
// The gatewayTLSSecret is mounted as a volume by the sidecar injector,
// so pods must be restarted to pick up the new secret name.
// CNPG specifically handles kubectl.kubernetes.io/restartedAt for pod restarts.
currentCnpgCluster.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339Nano)
if err := r.Client.Update(ctx, currentCnpgCluster); err == nil {
logger.Info("Patched CNPG Cluster with TLS settings; requeueing for pod update")
return ctrl.Result{RequeueAfter: RequeueAfterShort}, nil
Expand Down
91 changes: 91 additions & 0 deletions operator/src/internal/controller/documentdb_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3562,6 +3562,97 @@ var _ = Describe("DocumentDB Controller", func() {
Expect(err).ToNot(HaveOccurred())
Expect(result.Requeue).To(BeFalse())
})

It("should add restart annotation when TLS secret name changes", func() {
Expect(rbacv1.AddToScheme(scheme)).To(Succeed())

documentdb := &dbpreview.DocumentDB{
ObjectMeta: metav1.ObjectMeta{
Name: documentDBName,
Namespace: documentDBNamespace,
Finalizers: []string{documentDBFinalizer},
},
Spec: dbpreview.DocumentDBSpec{
InstancesPerNode: 1,
Resource: dbpreview.Resource{
Storage: dbpreview.StorageConfiguration{
PvcSize: "1Gi",
},
},
},
Status: dbpreview.DocumentDBStatus{
TLS: &dbpreview.TLSStatus{
Ready: true,
SecretName: "new-tls-secret",
},
},
}

cnpgCluster := &cnpgv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
Name: documentDBName,
Namespace: documentDBNamespace,
},
Spec: cnpgv1.ClusterSpec{
Instances: 1,
PostgresConfiguration: cnpgv1.PostgresConfiguration{
Extensions: []cnpgv1.ExtensionConfiguration{
{
Name: "documentdb",
ImageVolumeSource: corev1.ImageVolumeSource{
Reference: util.DEFAULT_DOCUMENTDB_IMAGE,
},
},
},
},
Plugins: []cnpgv1.PluginConfiguration{
{
Name: util.DEFAULT_SIDECAR_INJECTOR_PLUGIN,
Parameters: map[string]string{
"gatewayImage": util.DEFAULT_GATEWAY_IMAGE,
"documentDbCredentialSecret": util.DEFAULT_DOCUMENTDB_CREDENTIALS_SECRET,
"gatewayTLSSecret": "old-tls-secret",
},
},
},
},
Status: cnpgv1.ClusterStatus{
CurrentPrimary: documentDBName + "-1",
TargetPrimary: documentDBName + "-1",
InstancesStatus: map[cnpgv1.PodStatus][]string{
cnpgv1.PodHealthy: {documentDBName + "-1"},
},
},
}

fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(documentdb, cnpgCluster).
WithStatusSubresource(&dbpreview.DocumentDB{}).
Build()

reconciler := &DocumentDBReconciler{
Client: fakeClient,
Scheme: scheme,
Recorder: recorder,
}

result, err := reconciler.Reconcile(ctx, ctrl.Request{
NamespacedName: types.NamespacedName{
Name: documentDBName,
Namespace: documentDBNamespace,
},
})

Expect(err).ToNot(HaveOccurred())
Expect(result.RequeueAfter).To(Equal(RequeueAfterShort))

// Verify CNPG cluster was updated with new TLS secret and restart annotation
updatedCluster := &cnpgv1.Cluster{}
Expect(fakeClient.Get(ctx, types.NamespacedName{Name: documentDBName, Namespace: documentDBNamespace}, updatedCluster)).To(Succeed())
Expect(updatedCluster.Spec.Plugins[0].Parameters["gatewayTLSSecret"]).To(Equal("new-tls-secret"))
Expect(updatedCluster.Annotations).To(HaveKey("kubectl.kubernetes.io/restartedAt"))
})
})

Describe("validateK8sVersion", func() {
Expand Down
Loading