-
Notifications
You must be signed in to change notification settings - Fork 62
K8SPG-594 delete custom extensions from installed #967
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
Changes from 32 commits
bdfa932
d1c9434
a265b86
d370c4a
71c00fb
afb1f0c
6f450e1
08695c6
cf29171
4c6bf22
b6a2f72
f232f36
f3999bd
0a2524a
e174f7b
41e0fb4
08e178d
a4c54f2
0dc2bbd
fbe03d3
bf0818c
b6ce78d
bbf10e0
061f592
a74161a
bd04f98
f9ea789
37b3e4e
eb26df5
c688307
d94deba
ed21ea6
75a3372
4f86a07
4f1bac0
7128445
07979c3
a899835
51242e9
05a27e0
10d9c83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
apiVersion: kuttl.dev/v1beta1 | ||
kind: TestAssert | ||
timeout: 30 | ||
--- | ||
kind: ConfigMap | ||
apiVersion: v1 | ||
metadata: | ||
name: 11-check-extensions | ||
data: | ||
data: |2- | ||
pg_stat_monitor | ||
pgaudit |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
apiVersion: kuttl.dev/v1beta1 | ||
kind: TestStep | ||
timeout: 30 | ||
commands: | ||
- script: |- | ||
set -o errexit | ||
set -o xtrace | ||
|
||
source ../../functions | ||
|
||
data=$(kubectl -n ${NAMESPACE} exec $(get_client_pod) -- psql -v ON_ERROR_STOP=1 -t -q postgres://postgres:$(get_psql_user_pass custom-extensions-pguser-postgres)@$(get_psql_user_host custom-extensions-pguser-postgres) -c "\c postgres" -c "select extname from pg_extensions order by extname") | ||
|
||
kubectl create configmap -n "${NAMESPACE}" 11-check-extensions --from-literal=data="${data}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
"context" | ||
"crypto/md5" | ||
"fmt" | ||
"io" | ||
"reflect" | ||
"strings" | ||
"time" | ||
|
@@ -29,13 +30,16 @@ | |
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
"sigs.k8s.io/controller-runtime/pkg/source" | ||
|
||
"github.com/percona/percona-postgresql-operator/internal/controller/runtime" | ||
"github.com/percona/percona-postgresql-operator/internal/logging" | ||
"github.com/percona/percona-postgresql-operator/internal/naming" | ||
"github.com/percona/percona-postgresql-operator/internal/postgres" | ||
perconaController "github.com/percona/percona-postgresql-operator/percona/controller" | ||
"github.com/percona/percona-postgresql-operator/percona/extensions" | ||
"github.com/percona/percona-postgresql-operator/percona/k8s" | ||
pNaming "github.com/percona/percona-postgresql-operator/percona/naming" | ||
"github.com/percona/percona-postgresql-operator/percona/pmm" | ||
common "github.com/percona/percona-postgresql-operator/percona/postgres" | ||
"github.com/percona/percona-postgresql-operator/percona/utils/registry" | ||
"github.com/percona/percona-postgresql-operator/percona/watcher" | ||
v2 "github.com/percona/percona-postgresql-operator/pkg/apis/pgv2.percona.com/v2" | ||
|
@@ -49,8 +53,12 @@ | |
|
||
// Reconciler holds resources for the PerconaPGCluster reconciler | ||
type PGClusterReconciler struct { | ||
Client client.Client | ||
Owner client.FieldOwner | ||
Client client.Client | ||
Owner client.FieldOwner | ||
PodExec func( | ||
ctx context.Context, namespace, pod, container string, | ||
stdin io.Reader, stdout, stderr io.Writer, command ...string, | ||
) error | ||
Recorder record.EventRecorder | ||
Tracer trace.Tracer | ||
Platform string | ||
|
@@ -65,6 +73,13 @@ | |
|
||
// SetupWithManager adds the PerconaPGCluster controller to the provided runtime manager | ||
func (r *PGClusterReconciler) SetupWithManager(mgr manager.Manager) error { | ||
if r.PodExec == nil { | ||
var err error | ||
r.PodExec, err = runtime.NewPodExecutor(mgr.GetConfig()) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
if err := r.CrunchyController.Watch(source.Kind(mgr.GetCache(), &corev1.Secret{}, r.watchSecrets())); err != nil { | ||
return errors.Wrap(err, "unable to watch secrets") | ||
} | ||
|
@@ -241,7 +256,9 @@ | |
return reconcile.Result{}, errors.Wrap(err, "failed to handle monitor user password change") | ||
} | ||
|
||
r.reconcileCustomExtensions(cr) | ||
if err := r.reconcileCustomExtensions(ctx, cr); err != nil { | ||
return reconcile.Result{}, errors.Wrap(err, "reconcile custom extensions") | ||
} | ||
|
||
if err := r.reconcileScheduledBackups(ctx, cr); err != nil { | ||
return reconcile.Result{}, errors.Wrap(err, "reconcile scheduled backups") | ||
|
@@ -524,15 +541,54 @@ | |
return nil | ||
} | ||
|
||
func (r *PGClusterReconciler) reconcileCustomExtensions(cr *v2.PerconaPGCluster) { | ||
func (r *PGClusterReconciler) reconcileCustomExtensions(ctx context.Context, cr *v2.PerconaPGCluster) error { | ||
if cr.Spec.Extensions.Storage.Secret == nil { | ||
return | ||
return nil | ||
} | ||
|
||
extensionKeys := make([]string, 0) | ||
extensionNames := make([]string, 0) | ||
|
||
for _, extension := range cr.Spec.Extensions.Custom { | ||
key := extensions.GetExtensionKey(cr.Spec.PostgresVersion, extension.Name, extension.Version) | ||
extensionKeys = append(extensionKeys, key) | ||
extensionNames = append(extensionNames, extension.Name) | ||
} | ||
|
||
if cr.CompareVersion("2.6.0") >= 0 { | ||
var removedExtension []string | ||
installedExtensions := cr.Status.InstalledCustomExtensions | ||
crExtensions := make(map[string]struct{}) | ||
for _, ext := range extensionNames { | ||
crExtensions[ext] = struct{}{} | ||
} | ||
|
||
// Check for missing entries in crExtensions | ||
for _, ext := range installedExtensions { | ||
// If an object exists in installedExtensions but not in crExtensions, the extension should be deleted. | ||
if _, ok := crExtensions[ext]; !ok { | ||
removedExtension = append(removedExtension, ext) | ||
} | ||
} | ||
|
||
if len(removedExtension) > 0 { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unnecessary empty line |
||
disabled := func(ctx context.Context, exec postgres.Executor) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return errors.WithStack(disableCustomExtensionsInDB(ctx, exec, removedExtension)) | ||
} | ||
|
||
primary, err := common.GetPrimaryPod(ctx, r.Client, cr) | ||
if err != nil { | ||
return errors.New("primary pod not found") | ||
} | ||
|
||
err = disabled(ctx, func(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer, command ...string) error { | ||
return r.PodExec(ctx, primary.Namespace, primary.Name, naming.ContainerDatabase, stdin, stdout, stderr, command...) | ||
}) | ||
if err != nil { | ||
return errors.Wrap(err, "deletion extension from installed") | ||
} | ||
} | ||
} | ||
|
||
for i := 0; i < len(cr.Spec.InstanceSets); i++ { | ||
|
@@ -549,6 +605,31 @@ | |
)) | ||
set.VolumeMounts = append(set.VolumeMounts, extensions.ExtensionVolumeMounts(cr.Spec.PostgresVersion)...) | ||
} | ||
return nil | ||
} | ||
|
||
func disableCustomExtensionsInDB(ctx context.Context, exec postgres.Executor, customExtensionsForDeletion []string) error { | ||
log := logging.FromContext(ctx) | ||
|
||
for _, extensionName := range customExtensionsForDeletion { | ||
sqlCommand := fmt.Sprintf( | ||
`SET client_min_messages = WARNING; DROP EXTENSION IF EXISTS %s;`, | ||
extensionName, | ||
) | ||
_, stderr, err := exec.ExecInAllDatabases(ctx, | ||
sqlCommand, | ||
map[string]string{ | ||
"ON_ERROR_STOP": "on", // Abort when any one command fails. | ||
"QUIET": "on", // Do not print successful commands to stdout. | ||
}, | ||
) | ||
|
||
log.V(1).Info("extension was disabled ", "extensionName", extensionName) | ||
|
||
return errors.Wrapf(err, "custom extension deletion", "stderr: %s", stderr) | ||
Check failure on line 629 in percona/controller/pgcluster/controller.go
|
||
} | ||
|
||
return nil | ||
} | ||
|
||
func isBackupRunning(ctx context.Context, cl client.Reader, cr *v2.PerconaPGCluster) (bool, error) { | ||
|
inelpandzic marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package postgres | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/pkg/errors" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/labels" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
v2 "github.com/percona/percona-postgresql-operator/pkg/apis/pgv2.percona.com/v2" | ||
) | ||
|
||
func GetPrimaryPod(ctx context.Context, cli client.Client, cr *v2.PerconaPGCluster) (*corev1.Pod, error) { | ||
podList := &corev1.PodList{} | ||
err := cli.List(ctx, podList, &client.ListOptions{ | ||
Namespace: cr.Namespace, | ||
LabelSelector: labels.SelectorFromSet(map[string]string{ | ||
"app.kubernetes.io/instance": cr.Name, | ||
"postgres-operator.crunchydata.com/role": "master", | ||
}), | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(podList.Items) == 0 { | ||
return nil, errors.New("no primary pod found") | ||
} | ||
|
||
if len(podList.Items) > 1 { | ||
return nil, errors.New("multiple primary pods found") | ||
} | ||
|
||
return &podList.Items[0], nil | ||
} |
Uh oh!
There was an error while loading. Please reload this page.