Skip to content

Clone credentials #416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 18, 2023
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
95 changes: 79 additions & 16 deletions controllers/postgres_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,10 @@ func (r *PostgresReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
}
}

// Make sure the standby secrets exist, if neccessary
if err := r.ensureStandbySecrets(ctx, instance); err != nil {
r.recorder.Eventf(instance, "Warning", "Error", "failed to create standby secrets: %v", err)
return ctrl.Result{}, fmt.Errorf("error while creating standby secrets: %w", err)
// Make sure the postgres secrets exist, if neccessary
if err := r.ensurePostgresSecrets(ctx, instance); err != nil {
r.recorder.Eventf(instance, "Warning", "Error", "failed to create postgres secrets: %v", err)
return ctrl.Result{}, fmt.Errorf("error while creating postgres secrets: %w", err)
}

if instance.IsReplicationPrimary() {
Expand Down Expand Up @@ -714,6 +714,20 @@ func (r *PostgresReconciler) getZPostgresqlByLabels(ctx context.Context, matchin
return zpl.Items, nil
}

func (r *PostgresReconciler) ensurePostgresSecrets(ctx context.Context, instance *pg.Postgres) error {

if err := r.ensureStandbySecrets(ctx, instance); err != nil {
return err
}

if err := r.ensureCloneSecrets(ctx, instance); err != nil {
return err
}

return nil

}

func (r *PostgresReconciler) ensureStandbySecrets(ctx context.Context, instance *pg.Postgres) error {
if instance.IsReplicationPrimary() {
// nothing is configured, or we are the leader. nothing to do.
Expand All @@ -726,7 +740,7 @@ func (r *PostgresReconciler) ensureStandbySecrets(ctx context.Context, instance
}

// Check if secrets exist local in SERVICE Cluster
localStandbySecretName := "standby." + instance.ToPeripheralResourceName() + ".credentials"
localStandbySecretName := operatormanager.PostgresConfigReplicationUsername + "." + instance.ToPeripheralResourceName() + ".credentials"
localSecretNamespace := instance.ToPeripheralResourceNamespace()
localStandbySecret := &corev1.Secret{}
r.Log.Info("checking for local standby secret", "namespace", localSecretNamespace, "name", localStandbySecretName)
Expand All @@ -744,26 +758,77 @@ func (r *PostgresReconciler) ensureStandbySecrets(ctx context.Context, instance

r.Log.Info("no local standby secret found, continuing to create one")

remoteSecretNamespacedName := types.NamespacedName{
Namespace: instance.ObjectMeta.Namespace,
Name: instance.Spec.PostgresConnection.ConnectionSecretName,
}
return r.copySecrets(ctx, remoteSecretNamespacedName, instance, false)

}

func (r *PostgresReconciler) ensureCloneSecrets(ctx context.Context, instance *pg.Postgres) error {
if instance.Spec.PostgresRestore == nil {
// not a clone. nothing to do.
return nil
}

// Check if instance.Spec.PostgresConnectionInfo.ConnectionSecretName is defined
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is outdated

if instance.Spec.PostgresRestore.SourcePostgresID == "" {
return errors.New("SourcePostgresID not configured")
}

// Check if secrets exist local in SERVICE Cluster
localStandbySecretName := operatormanager.PostresConfigSuperUsername + "." + instance.ToPeripheralResourceName() + ".credentials"
localSecretNamespace := instance.ToPeripheralResourceNamespace()
localStandbySecret := &corev1.Secret{}
r.Log.Info("checking for local postgres secret", "namespace", localSecretNamespace, "name", localStandbySecretName)
err := r.SvcClient.Get(ctx, types.NamespacedName{Namespace: localSecretNamespace, Name: localStandbySecretName}, localStandbySecret)

if err == nil {
r.Log.Info("local postgres secret found, no action needed")
return nil
}

// we got an error other than not found, so we cannot continue!
if !apierrors.IsNotFound(err) {
return fmt.Errorf("error while fetching local stadnby secret from service cluster: %w", err)
}

r.Log.Info("no local postgres secret found, continuing to create one")

remoteSecretName := strings.Replace(instance.ToUserPasswordsSecretName(), instance.Name, instance.Spec.PostgresRestore.SourcePostgresID, 1) // TODO this is hacky-wacky...
remoteSecretNamespacedName := types.NamespacedName{
Namespace: instance.ObjectMeta.Namespace,
Name: remoteSecretName,
}
return r.copySecrets(ctx, remoteSecretNamespacedName, instance, true)

}

func (r *PostgresReconciler) copySecrets(ctx context.Context, sourceSecret types.NamespacedName, targetInstance *pg.Postgres, ignoreStandbyUser bool) error {
// Check if secrets exist in remote CONTROL Cluster
remoteSecretName := instance.Spec.PostgresConnection.ConnectionSecretName
remoteSecretNamespace := instance.ObjectMeta.Namespace
remoteSecret := &corev1.Secret{}
r.Log.Info("fetching remote standby secret", "namespace", remoteSecretNamespace, "name", remoteSecretName)
if err := r.CtrlClient.Get(ctx, types.NamespacedName{Namespace: remoteSecretNamespace, Name: remoteSecretName}, remoteSecret); err != nil {
r.Log.Info("fetching remote postgres secret", "namespace", sourceSecret.Namespace, "name", sourceSecret.Name)
if err := r.CtrlClient.Get(ctx, sourceSecret, remoteSecret); err != nil {
// we cannot read the secret given in the configuration, so we cannot continue!
return fmt.Errorf("error while fetching remote standby secret from control plane: %w", err)
return fmt.Errorf("error while fetching remote postgres secret from control plane: %w", err)
}

// copy ALL secrets...
// copy all but the standby secrets...
for username := range remoteSecret.Data {
// check if we skip the standby user (e.g. to prevent old standby intances from connecting once a clone took over its sources ip/port)
if ignoreStandbyUser && username == operatormanager.PostgresConfigReplicationUsername {
continue
}

r.Log.Info("creating local secret", "username", username)

currentSecretName := strings.ReplaceAll(username, "_", "-") + "." + instance.ToPeripheralResourceName() + ".credentials"
currentSecretName := strings.ReplaceAll(username, "_", "-") + "." + targetInstance.ToPeripheralResourceName() + ".credentials"
postgresSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: currentSecretName,
Namespace: localSecretNamespace,
Labels: map[string]string(instance.ToZalandoPostgresqlMatchingLabels()),
Namespace: targetInstance.ToPeripheralResourceNamespace(),
Labels: map[string]string(targetInstance.ToZalandoPostgresqlMatchingLabels()),
},
Data: map[string][]byte{
"username": []byte(username),
Expand All @@ -774,11 +839,9 @@ func (r *PostgresReconciler) ensureStandbySecrets(ctx context.Context, instance
if err := r.SvcClient.Create(ctx, postgresSecret); err != nil {
return fmt.Errorf("error while creating local secrets in service cluster: %w", err)
}
r.Log.Info("created local secret", "secret", postgresSecret)
}

return nil

}

func (r *PostgresReconciler) updatePatroniConfig(ctx context.Context, instance *pg.Postgres) error {
Expand Down
7 changes: 5 additions & 2 deletions pkg/operatormanager/operatormanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const (
SidecarsCMExporterQueriesKey string = "queries.yaml"

localSidecarsCMName = "postgres-sidecars-configmap"

PostresConfigSuperUsername = "postgres"
PostgresConfigReplicationUsername = "standby"
)

// operatorPodMatchingLabels is for listing operator pods
Expand Down Expand Up @@ -423,8 +426,8 @@ func (m *OperatorManager) editConfigMap(cm *corev1.ConfigMap, namespace string,
cm.Data["major_version_upgrade_mode"] = options.MajorVersionUpgradeMode

// we specifically refer to those two users in the cloud-api, so we hardcode them here as well to be on the safe side.
cm.Data["super_username"] = "postgres"
cm.Data["replication_username"] = "standby"
cm.Data["super_username"] = PostresConfigSuperUsername
cm.Data["replication_username"] = PostgresConfigReplicationUsername

cm.Data["enable_pod_antiaffinity"] = strconv.FormatBool(options.PodAntiaffinity)
}
Expand Down