diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 68f34c301..55d25a5f1 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -360,7 +360,11 @@ func main() { // Initialize commitments API for LIQUID interface (Postgres-backed usage reporting). commitmentsConfig := conf.GetConfigOrDie[commitments.Config]() - commitmentsAPI := commitments.NewAPIWithConfig(multiclusterClient, commitmentsConfig, nil) + var commitmentsUsageDB commitments.UsageDBClient + if commitmentsConfig.DatasourceName != "" { + commitmentsUsageDB = commitments.NewDBUsageClient(multiclusterClient, commitmentsConfig.DatasourceName) + } + commitmentsAPI := commitments.NewAPIWithConfig(multiclusterClient, commitmentsConfig, commitmentsUsageDB) commitmentsAPI.Init(mux, metrics.Registry, ctrl.Log.WithName("commitments-api")) if slices.Contains(mainConfig.EnabledControllers, "nova-pipeline-controllers") { diff --git a/helm/bundles/cortex-nova/values.yaml b/helm/bundles/cortex-nova/values.yaml index d30d89a4f..2c896a4cc 100644 --- a/helm/bundles/cortex-nova/values.yaml +++ b/helm/bundles/cortex-nova/values.yaml @@ -99,9 +99,7 @@ cortex: &cortex keystoneSecretRef: name: cortex-nova-openstack-keystone namespace: default - databaseSecretRef: - name: cortex-nova-postgres - namespace: default + # ssoSecretRef: # name: cortex-nova-openstack-sso # namespace: default diff --git a/internal/scheduling/reservations/commitments/api.go b/internal/scheduling/reservations/commitments/api.go index 3c43d0a67..e61026b68 100644 --- a/internal/scheduling/reservations/commitments/api.go +++ b/internal/scheduling/reservations/commitments/api.go @@ -9,7 +9,6 @@ import ( "strings" "sync" - "github.com/cobaltcore-dev/cortex/internal/scheduling/external" "github.com/go-logr/logr" "github.com/prometheus/client_golang/prometheus" "sigs.k8s.io/controller-runtime/pkg/client" @@ -53,16 +52,8 @@ func NewAPI(client client.Client) *HTTPAPI { return NewAPIWithConfig(client, DefaultConfig(), nil) } -// NewAPIWithConfig creates an HTTPAPI. If usageDB is nil and config.DatabaseSecretRef -// is set, a lazy-connecting PostgresReader-backed client is created automatically. +// NewAPIWithConfig creates an HTTPAPI with the given config and optional usageDB client. func NewAPIWithConfig(k8sClient client.Client, config Config, usageDB UsageDBClient) *HTTPAPI { - if usageDB == nil && config.DatabaseSecretRef != nil { - reader := &external.PostgresReader{ - Client: k8sClient, - DatabaseSecretRef: *config.DatabaseSecretRef, - } - usageDB = NewDBUsageClient(reader) - } return &HTTPAPI{ client: k8sClient, config: config, diff --git a/internal/scheduling/reservations/commitments/config.go b/internal/scheduling/reservations/commitments/config.go index 937449e34..888d37018 100644 --- a/internal/scheduling/reservations/commitments/config.go +++ b/internal/scheduling/reservations/commitments/config.go @@ -5,8 +5,6 @@ package commitments import ( "time" - - corev1 "k8s.io/api/core/v1" ) type Config struct { @@ -28,8 +26,9 @@ type Config struct { // SchedulerURL is the endpoint of the nova external scheduler SchedulerURL string `json:"schedulerURL"` - // Secret ref to the database credentials for querying VM state. - DatabaseSecretRef *corev1.SecretReference `json:"databaseSecretRef,omitempty"` + // DatasourceName is the name of the Datasource CRD that provides database connection info. + // Used to query VM state for report-usage. If empty, report-usage returns an error. + DatasourceName string `json:"datasourceName,omitempty"` // FlavorGroupPipelines maps flavor group names to pipeline names. // Example: {"2152": "kvm-hana-bin-packing", "2101": "kvm-general-purpose-load-balancing", "*": "kvm-general-purpose-load-balancing"} diff --git a/internal/scheduling/reservations/commitments/controller.go b/internal/scheduling/reservations/commitments/controller.go index 09cb00bda..a4ae6877b 100644 --- a/internal/scheduling/reservations/commitments/controller.go +++ b/internal/scheduling/reservations/commitments/controller.go @@ -25,7 +25,6 @@ import ( schedulerdelegationapi "github.com/cobaltcore-dev/cortex/api/external/nova" "github.com/cobaltcore-dev/cortex/api/v1alpha1" - "github.com/cobaltcore-dev/cortex/internal/knowledge/db" "github.com/cobaltcore-dev/cortex/internal/knowledge/extractor/plugins/compute" "github.com/cobaltcore-dev/cortex/internal/scheduling/reservations" "github.com/cobaltcore-dev/cortex/pkg/multicluster" @@ -41,8 +40,6 @@ type CommitmentReservationController struct { Scheme *runtime.Scheme // Configuration for the controller. Conf Config - // Database connection for querying VM state from Knowledge cache. - DB *db.DB // SchedulerClient for making scheduler API calls. SchedulerClient *reservations.SchedulerClient } @@ -516,16 +513,6 @@ func (r *CommitmentReservationController) hypervisorToReservations(ctx context.C // Init initializes the reconciler with required clients and DB connection. func (r *CommitmentReservationController) Init(ctx context.Context, client client.Client, conf Config) error { - // Initialize database connection if DatabaseSecretRef is provided. - if conf.DatabaseSecretRef != nil { - var err error - r.DB, err = db.Connector{Client: client}.FromSecretRef(ctx, *conf.DatabaseSecretRef) - if err != nil { - return fmt.Errorf("failed to initialize database connection: %w", err) - } - logf.FromContext(ctx).Info("database connection initialized for commitment reservation controller") - } - // Initialize scheduler client r.SchedulerClient = reservations.NewSchedulerClient(conf.SchedulerURL) logf.FromContext(ctx).Info("scheduler client initialized for commitment reservation controller", "url", conf.SchedulerURL) diff --git a/internal/scheduling/reservations/commitments/usage_db.go b/internal/scheduling/reservations/commitments/usage_db.go index 6d411ae5b..51d0d2299 100644 --- a/internal/scheduling/reservations/commitments/usage_db.go +++ b/internal/scheduling/reservations/commitments/usage_db.go @@ -6,19 +6,38 @@ package commitments import ( "context" "fmt" + "sync" "github.com/cobaltcore-dev/cortex/internal/knowledge/datasources/plugins/openstack/nova" "github.com/cobaltcore-dev/cortex/internal/scheduling/external" + "sigs.k8s.io/controller-runtime/pkg/client" ) -// dbUsageClient implements UsageDBClient using a PostgresReader for lazy connection. +// dbUsageClient implements UsageDBClient using a lazy-connecting PostgresReader. type dbUsageClient struct { - reader *external.PostgresReader + k8sClient client.Client + datasourceName string + mu sync.Mutex + reader *external.PostgresReader } -// NewDBUsageClient creates a UsageDBClient backed by the given PostgresReader. -func NewDBUsageClient(reader *external.PostgresReader) UsageDBClient { - return &dbUsageClient{reader: reader} +// NewDBUsageClient creates a UsageDBClient that lazily connects to Postgres via the named Datasource CRD. +func NewDBUsageClient(k8sClient client.Client, datasourceName string) UsageDBClient { + return &dbUsageClient{k8sClient: k8sClient, datasourceName: datasourceName} +} + +func (c *dbUsageClient) getReader(ctx context.Context) (*external.PostgresReader, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.reader != nil { + return c.reader, nil + } + reader, err := external.NewPostgresReader(ctx, c.k8sClient, c.datasourceName) + if err != nil { + return nil, fmt.Errorf("failed to connect to datasource %s: %w", c.datasourceName, err) + } + c.reader = reader + return reader, nil } // vmQueryRow is the scan target for the server+flavor JOIN query. @@ -38,6 +57,11 @@ type vmQueryRow struct { // ListProjectVMs returns all VMs for a project joined with their flavor data from Postgres. func (c *dbUsageClient) ListProjectVMs(ctx context.Context, projectID string) ([]VMRow, error) { + reader, err := c.getReader(ctx) + if err != nil { + return nil, err + } + query := ` SELECT s.id, s.name, s.status, s.created, @@ -53,7 +77,7 @@ func (c *dbUsageClient) ListProjectVMs(ctx context.Context, projectID string) ([ WHERE s.tenant_id = $1` var rows []vmQueryRow - if err := c.reader.Select(ctx, &rows, query, projectID); err != nil { + if err := reader.Select(ctx, &rows, query, projectID); err != nil { return nil, fmt.Errorf("failed to query VMs for project %s: %w", projectID, err) }