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
10 changes: 10 additions & 0 deletions cmd/postgres-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/percona/percona-postgresql-operator/v2/internal/naming"
"github.com/percona/percona-postgresql-operator/v2/internal/upgradecheck"
"github.com/percona/percona-postgresql-operator/v2/percona/certmanager"
"github.com/percona/percona-postgresql-operator/v2/percona/config/images"
perconaController "github.com/percona/percona-postgresql-operator/v2/percona/controller"
"github.com/percona/percona-postgresql-operator/v2/percona/controller/pgbackup"
"github.com/percona/percona-postgresql-operator/v2/percona/controller/pgcluster"
Expand Down Expand Up @@ -124,6 +125,15 @@ func main() {
)
assertNoError(err)

// Initialize image configuration
log.Info("Initializing image configuration")
if err := images.InitializeGlobalConfig(ctx, mgr.GetClient()); err != nil {
log.Error(err, "Failed to initialize image configuration, using embedded defaults")
images.SetGlobalConfig(images.DefaultConfig)
} else {
log.Info("Image configuration initialized successfully")
}

// Add Percona custom resource types to scheme
assertNoError(v2.AddToScheme(mgr.GetScheme()))

Expand Down
97 changes: 89 additions & 8 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

"github.com/pkg/errors"

"github.com/percona/percona-postgresql-operator/v2/percona/config/images"
pNaming "github.com/percona/percona-postgresql-operator/v2/percona/naming"
"github.com/percona/percona-postgresql-operator/v2/pkg/apis/upstream.pgv2.percona.com/v1beta1"
)

Expand All @@ -21,6 +23,30 @@ func defaultFromEnv(value, key string) string {
return value
}

// getImageFromOperatorConfig attempts to get image from operator-wide configuration
func getImageFromOperatorConfig(cluster *v1beta1.PostgresCluster, component string) string {
crVersion := getCRVersionForCluster(cluster)
if crVersion == "" {
return ""
}

pgVersion := fmt.Sprintf("%d", cluster.Spec.PostgresVersion)

return images.GetImageForCluster(crVersion, component, pgVersion)
Comment thread
Kajot-dev marked this conversation as resolved.
}
Comment thread
Kajot-dev marked this conversation as resolved.

// getCRVersionForCluster retrieves the CR version for a cluster
// The Percona operator stores the CR version in the LabelOperatorVersion label
func getCRVersionForCluster(cluster *v1beta1.PostgresCluster) string {
// Check the standard Percona version label
if cluster.Labels != nil {
if version, ok := cluster.Labels[pNaming.LabelOperatorVersion]; ok {
return version
}
}
return ""
}

// FetchKeyCommand returns the fetch_key_cmd value stored in the encryption_key_command
// variable used to enable TDE.
func FetchKeyCommand(spec *v1beta1.PostgresClusterSpec) string {
Expand Down Expand Up @@ -53,7 +79,17 @@ func FetchKeyCommand(spec *v1beta1.PostgresClusterSpec) string {
func PGBackRestContainerImage(cluster *v1beta1.PostgresCluster) string {
image := cluster.Spec.Backups.PGBackRest.Image

return defaultFromEnv(image, "RELATED_IMAGE_PGBACKREST")
// Try operator-wide config first
if image == "" {
image = getImageFromOperatorConfig(cluster, "pgbackrest")
}

// Fallback to env var
if image == "" {
image = defaultFromEnv(image, "RELATED_IMAGE_PGBACKREST")
}

return image
}

// PGAdminContainerImage returns the container image to use for pgAdmin.
Expand All @@ -64,7 +100,17 @@ func PGAdminContainerImage(cluster *v1beta1.PostgresCluster) string {
image = cluster.Spec.UserInterface.PGAdmin.Image
}

return defaultFromEnv(image, "RELATED_IMAGE_PGADMIN")
// Try operator-wide config first
if image == "" {
image = getImageFromOperatorConfig(cluster, "pgadmin")
}

// Fallback to env var
if image == "" {
image = defaultFromEnv(image, "RELATED_IMAGE_PGADMIN")
}

return image
}

// StandalonePGAdminContainerImage returns the container image to use for pgAdmin.
Expand All @@ -74,7 +120,12 @@ func StandalonePGAdminContainerImage(pgadmin *v1beta1.PGAdmin) string {
image = *pgadmin.Spec.Image
}

return defaultFromEnv(image, "RELATED_IMAGE_STANDALONE_PGADMIN")
// Fallback to env var
if image == "" {
image = defaultFromEnv(image, "RELATED_IMAGE_STANDALONE_PGADMIN")
}

return image
}

// PGBouncerContainerImage returns the container image to use for pgBouncer.
Expand All @@ -85,7 +136,17 @@ func PGBouncerContainerImage(cluster *v1beta1.PostgresCluster) string {
image = cluster.Spec.Proxy.PGBouncer.Image
}

return defaultFromEnv(image, "RELATED_IMAGE_PGBOUNCER")
// Try operator-wide config first
if image == "" {
image = getImageFromOperatorConfig(cluster, "pgbouncer")
}

// Fallback to env var
if image == "" {
image = defaultFromEnv(image, "RELATED_IMAGE_PGBOUNCER")
}

return image
}

// PGExporterContainerImage returns the container image to use for the
Expand All @@ -98,7 +159,12 @@ func PGExporterContainerImage(cluster *v1beta1.PostgresCluster) string {
image = cluster.Spec.Monitoring.PGMonitor.Exporter.Image
}

return defaultFromEnv(image, "RELATED_IMAGE_PGEXPORTER")
// Fallback to env var
if image == "" {
image = defaultFromEnv(image, "RELATED_IMAGE_PGEXPORTER")
}

return image
}

// PostgresContainerImageString returns the container image to use for PostgreSQL (from string params).
Expand All @@ -117,10 +183,25 @@ func PostgresContainerImageString(image string, postgresVersion int, postGISVers
// Made as a wrapper of PostgresContainerImageString for compat reasons
func PostgresContainerImage(cluster *v1beta1.PostgresCluster) string {
image := cluster.Spec.Image
postgresVersion := cluster.Spec.PostgresVersion
postGISVersion := cluster.Spec.PostGISVersion

return PostgresContainerImageString(image, postgresVersion, postGISVersion)
// Try operator-wide config first
if image == "" {
// Use postgresGIS component if PostGIS is enabled
component := "postgres"
if cluster.Spec.PostGISVersion != "" {
component = "postgresGIS"
}
image = getImageFromOperatorConfig(cluster, component)
}

// Fallback to env var
if image == "" {
postgresVersion := cluster.Spec.PostgresVersion
postGISVersion := cluster.Spec.PostGISVersion
image = PostgresContainerImageString("", postgresVersion, postGISVersion)
}

return image
}

// PGONamespace returns the namespace where the PGO is running,
Expand Down
31 changes: 31 additions & 0 deletions percona/config/images/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2021 - 2026 Percona, LLC
//
// SPDX-License-Identifier: Apache-2.0

package images

import (
_ "embed"

"github.com/pkg/errors"
"sigs.k8s.io/yaml"
)

//go:embed default-images.yaml
var defaultImagesYAML []byte

// DefaultConfig holds the default image configuration loaded from embedded YAML
var DefaultConfig *DefaultImagesConfig

// mustLoadDefault parses the embedded default-images.yaml and returns the configuration
func mustLoadDefault() *DefaultImagesConfig {
var cfg DefaultImagesConfig
if err := yaml.Unmarshal(defaultImagesYAML, &cfg); err != nil {
panic(errors.Wrap(err, "failed to parse embedded default images"))
}
return &cfg
}

func init() {
DefaultConfig = mustLoadDefault()
}
98 changes: 98 additions & 0 deletions percona/config/images/default-images.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Default image configuration for Percona PostgreSQL Operator
# This file is embedded into the operator binary at build time
# Users can override these values via ConfigMap or environment variable

# Global registry for all images
registry: docker.io

# Versions define repositories and tags per CR version
versions:
- crVersion: "3.0.0"
repositories:
postgres: percona/percona-distribution-postgresql
postgresGIS: percona/percona-distribution-postgresql-with-postgis
pgbackrest: percona/percona-pgbackrest
pgbouncer: percona/percona-pgbouncer
tags:
postgres:
"14": "14.23-1"
"15": "15.18-1"
"16": "16.14-1"
"17": "17.10-1"
"18": "18.4-1"
postgresGIS:
"14": "14.23-2"
"15": "15.18-2"
"16": "16.14-2"
"17": "17.10-2"
"18": "18.4-2"
pgbackrest: "2.58.0-2"
pgbouncer: "1.25.2-1"

- crVersion: "2.9.0"
repositories:
postgres: percona/percona-distribution-postgresql
postgresGIS: percona/percona-distribution-postgresql-with-postgis
pgbackrest: percona/percona-pgbackrest
pgbouncer: percona/percona-pgbouncer
tags:
postgres:
"14": "14.22-1"
"15": "15.17-1"
"16": "16.13-1"
"17": "17.9-1"
"18": "18.3-1"
postgresGIS:
"14": "14.22-1"
"15": "15.17-1"
"16": "16.13-1"
"17": "17.9-1"
"18": "18.3-1"
pgbackrest: "2.58.0-1"
pgbouncer: "1.25.1-1"

- crVersion: "2.8.1"
repositories:
postgres: percona/percona-distribution-postgresql
postgresGIS: percona/percona-postgresql-operator
pgbackrest: percona/percona-pgbackrest
pgbouncer: percona/percona-pgbouncer
tags:
postgres:
"13": "13.23-1"
"14": "14.20-1"
"15": "15.15-1"
"16": "16.11-1"
"17": "17.7-1"
"18": "18.1-1"
postgresGIS:
"13": "2.8.1-ppg13.23-postgres-gis3.3.8"
"14": "2.8.1-ppg14.20-postgres-gis3.3.8"
"15": "2.8.1-ppg15.15-postgres-gis3.3.8"
"16": "2.8.1-ppg16.11-postgres-gis3.3.8"
"17": "2.8.1-ppg17.7-postgres-gis3.5.4"
"18": "2.8.1-ppg18.1-postgres-gis3.5.4"
pgbackrest: "2.57.0-1"
pgbouncer: "1.25.0-1"

- crVersion: "2.8.0"
repositories:
postgres: percona/percona-distribution-postgresql
postgresGIS: percona/percona-postgresql-operator
pgbackrest: percona/percona-pgbackrest
pgbouncer: percona/percona-pgbouncer
tags:
postgres:
"13": "13.22-1"
"14": "14.19-1"
"15": "15.14-1"
"16": "16.10-1"
"17": "17.6-1"
postgresGIS:
"13": "2.8.0-ppg13.22-postgres-gis3.3.8"
"14": "2.8.0-ppg14.19-postgres-gis3.3.8"
"15": "2.8.0-ppg15.14-postgres-gis3.3.8"
"16": "2.8.0-ppg16.10-postgres-gis3.3.8"
"17": "2.8.0-ppg17.6-postgres-gis3.3.8"
pgbackrest: "2.56.0-1"
pgbouncer: "1.24.1-1"
Loading
Loading