Skip to content
Draft
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
1 change: 0 additions & 1 deletion api/v1alpha1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const (
ConditionReasonMajorUpdateAvailable ConditionReason = "MajorUpdateAvailable"
ConditionReasonVersionOutdated ConditionReason = "VersionOutdated"
ConditionReasonUpgradeCheckFailed ConditionReason = "UpgradeCheckFailed"

// ConditionTypeReady indicates that cluster is ready to serve client requests.
ConditionTypeReady ConditionType = "Ready"
ClickHouseConditionAllShardsReady ConditionReason = "AllShardsReady"
Expand Down
28 changes: 28 additions & 0 deletions internal/controller/clickhouse/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ func (cmd *commander) Version(ctx context.Context, id v1.ClickHouseReplicaID) (s
return version, nil
}

// ReloadConfig executes SYSTEM RELOAD CONFIG.
func (cmd *commander) ReloadConfig(ctx context.Context, id v1.ClickHouseReplicaID) error {
conn, err := cmd.getConn(id)
if err != nil {
return fmt.Errorf("failed to get connection for replica %s: %w", id, err)
}

if err := conn.Exec(ctx, "SYSTEM RELOAD CONFIG"); err != nil {
return fmt.Errorf("reload config on replica %s: %w", id, err)
}

return nil
}

// ReloadUsers executes SYSTEM RELOAD USERS.
func (cmd *commander) ReloadUsers(ctx context.Context, id v1.ClickHouseReplicaID) error {
conn, err := cmd.getConn(id)
if err != nil {
return fmt.Errorf("failed to get connection for replica %s: %w", id, err)
}

if err := conn.Exec(ctx, "SYSTEM RELOAD USERS"); err != nil {
return fmt.Errorf("reload users on replica %s: %w", id, err)
}

return nil
}

func (cmd *commander) Databases(ctx context.Context, id v1.ClickHouseReplicaID) (map[string]databaseDescriptor, error) {
conn, err := cmd.getConn(id)
if err != nil {
Expand Down
18 changes: 17 additions & 1 deletion internal/controllerutil/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const (
AnnotationConfigHash = "checksum/configuration"
AnnotationRestartedAt = "kubectl.kubernetes.io/restartedAt"

AnnotationStatefulSetVersion = "clickhouse.com/statefulset-version"
AnnotationStatefulSetVersion = "clickhouse.com/statefulset-version"
AnnotationRestartRequiredConfigHash = "checksum/restart-required-config"
)

// AddHashWithKeyToAnnotations adds given spec hash to object's annotations with given key.
Expand Down Expand Up @@ -48,3 +49,18 @@ func GetConfigHashFromObject(found client.Object) string {
func AddObjectConfigHash(obj client.Object, hash string) {
AddHashWithKeyToAnnotations(obj, AnnotationConfigHash, hash)
}

// GetRestartRequiredConfigHashFromObject retrieves restart-required config hash from object's annotations.
func GetRestartRequiredConfigHashFromObject(found client.Object) string {
annotations := found.GetAnnotations()
if annotations == nil || annotations[AnnotationRestartRequiredConfigHash] == "" {
return ""
}

return annotations[AnnotationRestartRequiredConfigHash]
}

// AddObjectRestartRequiredConfigHash adds restart-required config hash to object's annotations.
func AddObjectRestartRequiredConfigHash(obj client.Object, hash string) {
AddHashWithKeyToAnnotations(obj, AnnotationRestartRequiredConfigHash, hash)
}
78 changes: 78 additions & 0 deletions test/e2e/clickhouse_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,84 @@ var _ = Describe("ClickHouse controller", Label("clickhouse"), func() {
Entry("scale up to 2 replicas", v1.ClickHouseClusterSpec{Replicas: ptr.To[int32](2)}),
)

It("should not restart pods when only ExtraUsersConfig changes", func(ctx context.Context) {
cr := v1.ClickHouseCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespace,
Name: fmt.Sprintf("test-%d", rand.Uint32()), //nolint:gosec
},
Spec: v1.ClickHouseClusterSpec{
Replicas: ptr.To[int32](1),
ContainerTemplate: v1.ContainerTemplateSpec{
Image: v1.ContainerImage{Tag: ClickHouseBaseVersion},
},
DataVolumeClaimSpec: &defaultStorage,
KeeperClusterRef: &corev1.LocalObjectReference{Name: keeper.Name},
Settings: v1.ClickHouseSettings{
ExtraUsersConfig: runtime.RawExtension{Raw: []byte(`{}`)},
},
},
}
checks := 0

By("creating cluster CR")
Expect(k8sClient.Create(ctx, &cr)).To(Succeed())
DeferCleanup(func(ctx context.Context) {
Expect(k8sClient.Delete(ctx, &cr)).To(Succeed())
})
WaitClickHouseUpdatedAndReady(ctx, &cr, time.Minute, false)
ClickHouseRWChecks(ctx, &cr, &checks)

By("recording pod UIDs before config change")

var podsBefore corev1.PodList

Expect(k8sClient.List(ctx, &podsBefore, client.InNamespace(testNamespace),
client.MatchingLabels{controllerutil.LabelAppKey: cr.SpecificName()})).To(Succeed())
Expect(podsBefore.Items).NotTo(BeEmpty())

uidByPodName := make(map[string]types.UID)
for _, p := range podsBefore.Items {
uidByPodName[p.Name] = p.UID
}

By("updating ExtraUsersConfig (reloadable, should not trigger restart)")
Expect(k8sClient.Get(ctx, cr.NamespacedName(), &cr)).To(Succeed())
cr.Spec.Settings.ExtraUsersConfig = runtime.RawExtension{
Raw: []byte(`{"users": {"e2e_test_user": {"password": "test", "profile": "default"}}}`),
}
Expect(k8sClient.Update(ctx, &cr)).To(Succeed())

By("waiting for configuration to sync")
EventuallyWithOffset(1, func() bool {
var cluster v1.ClickHouseCluster

ExpectWithOffset(1, k8sClient.Get(ctx, cr.NamespacedName(), &cluster)).To(Succeed())

for _, cond := range cluster.Status.Conditions {
if cond.Type == string(v1.ConditionTypeConfigurationInSync) && cond.Status == metav1.ConditionTrue {
return true
}
}

return false
}, 2*time.Minute).Should(BeTrue())
Comment on lines +163 to +176

By("verifying pods were not restarted (same UIDs)")

var podsAfter corev1.PodList

Expect(k8sClient.List(ctx, &podsAfter, client.InNamespace(testNamespace),
client.MatchingLabels{controllerutil.LabelAppKey: cr.SpecificName()})).To(Succeed())

for _, p := range podsAfter.Items {
Expect(p.UID).To(Equal(uidByPodName[p.Name]),
"pod %s was restarted (UID changed)", p.Name)
}

ClickHouseRWChecks(ctx, &cr, &checks)
})

DescribeTable("ClickHouse cluster updates", func(
ctx context.Context,
baseReplicas int,
Expand Down
Loading