diff --git a/server/internal/orchestrator/swarm/available_upgrades_test.go b/server/internal/orchestrator/swarm/available_upgrades_test.go index b30863bd..133d7a17 100644 --- a/server/internal/orchestrator/swarm/available_upgrades_test.go +++ b/server/internal/orchestrator/swarm/available_upgrades_test.go @@ -15,7 +15,7 @@ func TestVersions_AvailableUpgrades(t *testing.T) { ImageRepositoryHost: "ghcr.io/pgedge", }, } - v := NewVersions(cfg) + v := newTestVersions(t, cfg) t.Run("returns newer entries in same bucket", func(t *testing.T) { current := ds.MustParsePgEdgeVersion("17.9", "5") diff --git a/server/internal/orchestrator/swarm/find_upgrade_test.go b/server/internal/orchestrator/swarm/find_upgrade_test.go index 57f2504f..d8db11ad 100644 --- a/server/internal/orchestrator/swarm/find_upgrade_test.go +++ b/server/internal/orchestrator/swarm/find_upgrade_test.go @@ -14,7 +14,7 @@ import ( func testVersions(t *testing.T) *Versions { t.Helper() - return NewVersions(config.Config{ + return newTestVersions(t, config.Config{ DockerSwarm: config.DockerSwarm{ ImageRepositoryHost: "ghcr.io/pgedge", }, diff --git a/server/internal/orchestrator/swarm/images.go b/server/internal/orchestrator/swarm/images.go index 554ac87f..d29e1114 100644 --- a/server/internal/orchestrator/swarm/images.go +++ b/server/internal/orchestrator/swarm/images.go @@ -21,67 +21,6 @@ type Versions struct { images map[string]map[string]*Images } -func NewVersions(cfg config.Config) *Versions { - versions := &Versions{ - cfg: cfg, - images: make(map[string]map[string]*Images), - } - - // pg16 - versions.addImage(ds.MustParsePgEdgeVersion("16.10", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "16.10-spock5.0.4-standard-3"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("16.11", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "16.11-spock5.0.4-standard-4"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("16.12", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "16.12-spock5.0.5-standard-1"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("16.13", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "16.13-spock5.0.6-standard-2"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("16.14", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "16.14-spock5.0.9-standard-1"), - }) - - // pg17 - versions.addImage(ds.MustParsePgEdgeVersion("17.6", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "17.6-spock5.0.4-standard-3"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("17.7", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "17.7-spock5.0.4-standard-4"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("17.8", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "17.8-spock5.0.5-standard-1"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("17.9", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "17.9-spock5.0.6-standard-2"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("17.10", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "17.10-spock5.0.9-standard-1"), - }) - - // pg18 - versions.addImage(ds.MustParsePgEdgeVersion("18.0", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "18.0-spock5.0.4-standard-3"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("18.1", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "18.1-spock5.0.4-standard-4"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("18.2", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "18.2-spock5.0.5-standard-1"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("18.3", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "18.3-spock5.0.6-standard-2"), - }) - versions.addImage(ds.MustParsePgEdgeVersion("18.4", "5"), &Images{ - PgEdgeImage: imageTag(cfg, "18.4-spock5.0.9-standard-1"), - }) - - versions.defaultVersion = ds.MustParsePgEdgeVersion("18.4", "5") - - return versions -} func (v Versions) Supported() []*ds.PgEdgeVersion { return v.supportedVersions @@ -120,10 +59,6 @@ func (v Versions) GetImages(version *ds.PgEdgeVersion) (*Images, error) { return images, nil } -func imageTag(cfg config.Config, tag string) string { - return fmt.Sprintf("%s/pgedge-postgres:%s", cfg.DockerSwarm.ImageRepositoryHost, tag) -} - // FindByImage returns the PgEdgeVersion and Images for the manifest entry // whose PgEdgeImage matches image exactly. Returns (nil, nil, false) when no // entry matches. diff --git a/server/internal/orchestrator/swarm/images_test.go b/server/internal/orchestrator/swarm/images_test.go index 837f89de..dfcf2b81 100644 --- a/server/internal/orchestrator/swarm/images_test.go +++ b/server/internal/orchestrator/swarm/images_test.go @@ -1,24 +1,20 @@ -package swarm_test +package swarm import ( "strings" "testing" "github.com/pgEdge/control-plane/server/internal/config" - "github.com/pgEdge/control-plane/server/internal/orchestrator/swarm" "github.com/stretchr/testify/assert" ) func TestVersions(t *testing.T) { - // Versions is a collection of constant values that are determined at - // startup. These tests validate that these constants are internally - // consistent and match expectations, but they don't enforce specific values - // so that the values can change without updating these tests. - versions := swarm.NewVersions(config.Config{ + cfg := config.Config{ DockerSwarm: config.DockerSwarm{ ImageRepositoryHost: "127.0.0.1:5000/pgedge", }, - }) + } + versions := newTestVersions(t, cfg) t.Run("Supported", func(t *testing.T) { supported := versions.Supported() diff --git a/server/internal/orchestrator/swarm/manifest_loader_test.go b/server/internal/orchestrator/swarm/manifest_loader_test.go index 36260f03..963bfa1e 100644 --- a/server/internal/orchestrator/swarm/manifest_loader_test.go +++ b/server/internal/orchestrator/swarm/manifest_loader_test.go @@ -467,97 +467,6 @@ func TestManifestLoader_RefreshFailure(t *testing.T) { } } -// TestBuildVersions_MatchesNewVersions verifies that buildVersions produces -// the same set of supported versions as the hardcoded NewVersions function -// when given the embedded manifest. -func TestBuildVersions_MatchesNewVersions(t *testing.T) { - cfg := config.Config{ - DockerSwarm: config.DockerSwarm{ - ImageRepositoryHost: "ghcr.io/pgedge", - }, - } - - var mf versionManifest - if err := json.Unmarshal(embeddedManifest, &mf); err != nil { - t.Fatalf("unmarshal embedded manifest: %v", err) - } - - got, err := buildVersions(cfg, &mf) - if err != nil { - t.Fatalf("buildVersions: %v", err) - } - - want := NewVersions(cfg) - - if len(got.Supported()) != len(want.Supported()) { - t.Errorf("Supported() len = %d, want %d", len(got.Supported()), len(want.Supported())) - } - - for _, wv := range want.Supported() { - imgs, err := got.GetImages(wv) - if err != nil { - t.Errorf("GetImages(%s) not found in manifest-built Versions: %v", wv, err) - continue - } - wantImgs, _ := want.GetImages(wv) - if imgs.PgEdgeImage != wantImgs.PgEdgeImage { - t.Errorf("GetImages(%s).PgEdgeImage = %q, want %q", wv, imgs.PgEdgeImage, wantImgs.PgEdgeImage) - } - } - - if got.Default().PostgresVersion.String() != want.Default().PostgresVersion.String() { - t.Errorf("Default() = %s, want %s", got.Default().PostgresVersion, want.Default().PostgresVersion) - } -} - -// TestBuildServiceVersions_MatchesNewServiceVersions verifies that -// buildServiceVersions produces the same registrations as NewServiceVersions -// for the embedded manifest. -func TestBuildServiceVersions_MatchesNewServiceVersions(t *testing.T) { - cfg := config.Config{ - DockerSwarm: config.DockerSwarm{ - ImageRepositoryHost: "ghcr.io/pgedge", - }, - } - - var mf versionManifest - if err := json.Unmarshal(embeddedManifest, &mf); err != nil { - t.Fatalf("unmarshal embedded manifest: %v", err) - } - - got, err := buildServiceVersions(cfg, &mf) - if err != nil { - t.Fatalf("buildServiceVersions: %v", err) - } - - want := NewServiceVersions(cfg) - - serviceTypes := []string{"mcp", "postgrest", "rag"} - for _, svc := range serviceTypes { - gotVers, err := got.SupportedServiceVersions(svc) - if err != nil { - t.Errorf("SupportedServiceVersions(%q) error: %v", svc, err) - continue - } - wantVers, _ := want.SupportedServiceVersions(svc) - if len(gotVers) != len(wantVers) { - t.Errorf("SupportedServiceVersions(%q) len = %d, want %d", svc, len(gotVers), len(wantVers)) - } - - for _, ver := range wantVers { - gotImg, err := got.GetServiceImage(svc, ver) - if err != nil { - t.Errorf("GetServiceImage(%q, %q) not found: %v", svc, ver, err) - continue - } - wantImg, _ := want.GetServiceImage(svc, ver) - if gotImg.Tag != wantImg.Tag { - t.Errorf("GetServiceImage(%q, %q).Tag = %q, want %q", svc, ver, gotImg.Tag, wantImg.Tag) - } - } - } -} - // TestManifestLoader_RealURL exercises the loader against a real HTTP server. // Run with a local file server already serving version-manifest.json: // diff --git a/server/internal/orchestrator/swarm/orchestrator.go b/server/internal/orchestrator/swarm/orchestrator.go index 592e7bfd..03a73e4d 100644 --- a/server/internal/orchestrator/swarm/orchestrator.go +++ b/server/internal/orchestrator/swarm/orchestrator.go @@ -56,8 +56,9 @@ const ( type Orchestrator struct { cfg config.Config - versions *Versions - serviceVersions *ServiceVersions + loader *ManifestLoader + versions *Versions // non-nil only in tests (overrides loader) + serviceVersions *ServiceVersions // non-nil only in tests (overrides loader) docker *docker.Docker logger zerolog.Logger dbNetworkAllocator Allocator @@ -68,11 +69,28 @@ type Orchestrator struct { controlAvailable bool } +func (o *Orchestrator) getVersions() *Versions { + if o.versions != nil { + return o.versions + } + v := o.loader.Versions() + return &v +} + +func (o *Orchestrator) getServiceVersions() *ServiceVersions { + if o.serviceVersions != nil { + return o.serviceVersions + } + sv := o.loader.ServiceVersions() + return &sv +} + func NewOrchestrator( ctx context.Context, cfg config.Config, d *docker.Docker, logger zerolog.Logger, + loader *ManifestLoader, ) (*Orchestrator, error) { info, err := d.Info(ctx) if err != nil { @@ -101,11 +119,10 @@ func NewOrchestrator( } return &Orchestrator{ - cfg: cfg, - versions: NewVersions(cfg), - serviceVersions: NewServiceVersions(cfg), - docker: d, - logger: logger, + cfg: cfg, + loader: loader, + docker: d, + logger: logger, dbNetworkAllocator: Allocator{ Prefix: dbNetworkPrefix, Bits: cfg.DockerSwarm.DatabaseNetworksSubnetBits, @@ -130,8 +147,9 @@ func (o *Orchestrator) PopulateHost(ctx context.Context, h *host.Host) error { MemberID: o.swarmNodeID, ControlAvailable: o.controlAvailable, } - h.DefaultPgEdgeVersion = o.versions.Default() - h.SupportedPgEdgeVersions = o.versions.Supported() + v := o.getVersions() + h.DefaultPgEdgeVersion = v.Default() + h.SupportedPgEdgeVersions = v.Supported() return nil } @@ -197,7 +215,7 @@ func (o *Orchestrator) resolveInstanceImages(spec *database.InstanceSpec) (*Imag case swarmOpts != nil && swarmOpts.ResolvedImage != "": return &Images{PgEdgeImage: swarmOpts.ResolvedImage}, nil default: - manifested, err := o.versions.GetImages(spec.PgEdgeVersion) + manifested, err := o.getVersions().GetImages(spec.PgEdgeVersion) if err != nil { return nil, fmt.Errorf("failed to get images: %w", err) } @@ -261,7 +279,7 @@ func (o *Orchestrator) resolveServiceImage(spec *database.ServiceInstanceSpec) ( case swarmOpts != nil && swarmOpts.ResolvedImage != "": return &ServiceImage{Tag: swarmOpts.ResolvedImage}, nil default: - manifested, err := o.serviceVersions.GetServiceImage(spec.ServiceSpec.ServiceType, spec.ServiceSpec.Version) + manifested, err := o.getServiceVersions().GetServiceImage(spec.ServiceSpec.ServiceType, spec.ServiceSpec.Version) if err != nil { return nil, fmt.Errorf("failed to get service image: %w", err) } @@ -307,11 +325,11 @@ func (o *Orchestrator) ReconcileServiceInstanceSpec(old, new *database.ServiceIn } func (o *Orchestrator) AvailableUpgrades(current *ds.PgEdgeVersion) []*database.AvailableUpgrade { - return o.versions.AvailableUpgrades(current) + return o.getVersions().AvailableUpgrades(current) } func (o *Orchestrator) FindUpgrade(current *ds.PgEdgeVersion, targetImage string) (*database.AvailableUpgrade, error) { - ver, img, ok := o.versions.FindByImage(targetImage) + ver, img, ok := o.getVersions().FindByImage(targetImage) if !ok { return nil, fmt.Errorf("%w: image not found in manifest: %s", database.ErrUpgradeNotAvailable, targetImage) } @@ -1270,12 +1288,13 @@ func (o *Orchestrator) validateInstanceSpec(ctx context.Context, spec *database. } specVersion := spec.PgEdgeVersion + vers := o.getVersions() if specVersion == nil { o.logger.Warn().Msg("PostgresVersion not provided, using default version") - specVersion = o.versions.defaultVersion + specVersion = vers.defaultVersion } - images, err := o.versions.GetImages(specVersion) + images, err := vers.GetImages(specVersion) if err != nil { return fmt.Errorf("image fetch error: %w", err) } @@ -1357,8 +1376,9 @@ func (o *Orchestrator) validatePortAvailable(ctx context.Context, nodeName strin return nil } - specVersion := o.versions.defaultVersion - images, err := o.versions.GetImages(specVersion) + v := o.getVersions() + specVersion := v.defaultVersion + images, err := v.GetImages(specVersion) if err != nil { return fmt.Errorf("image fetch error: %w", err) } @@ -1384,8 +1404,9 @@ func (o *Orchestrator) validateVolumes(ctx context.Context, nodeName string, vol targets = append(targets, v.DestinationPath) } - specVersion := o.versions.defaultVersion - images, err := o.versions.GetImages(specVersion) + v := o.getVersions() + specVersion := v.defaultVersion + images, err := v.GetImages(specVersion) if err != nil { return fmt.Errorf("image fetch error: %w", err) } diff --git a/server/internal/orchestrator/swarm/provide.go b/server/internal/orchestrator/swarm/provide.go index 7aa087f8..65c1d405 100644 --- a/server/internal/orchestrator/swarm/provide.go +++ b/server/internal/orchestrator/swarm/provide.go @@ -9,6 +9,7 @@ import ( "github.com/pgEdge/control-plane/server/internal/config" "github.com/pgEdge/control-plane/server/internal/docker" + "github.com/pgEdge/control-plane/server/internal/logging" ) func Provide(i *do.Injector) { @@ -29,6 +30,14 @@ func provideOrchestrator(i *do.Injector) { if err != nil { return nil, fmt.Errorf("failed to get logger: %w", err) } - return NewOrchestrator(context.Background(), cfg, dockerClient, logger) + loggerFactory, err := do.Invoke[*logging.Factory](i) + if err != nil { + return nil, fmt.Errorf("failed to get logger factory: %w", err) + } + loader, err := NewManifestLoader(context.Background(), cfg, loggerFactory) + if err != nil { + return nil, fmt.Errorf("failed to create manifest loader: %w", err) + } + return NewOrchestrator(context.Background(), cfg, dockerClient, logger, loader) }) } diff --git a/server/internal/orchestrator/swarm/rag_instance_resources_test.go b/server/internal/orchestrator/swarm/rag_instance_resources_test.go index 383d07b2..1474a582 100644 --- a/server/internal/orchestrator/swarm/rag_instance_resources_test.go +++ b/server/internal/orchestrator/swarm/rag_instance_resources_test.go @@ -15,9 +15,10 @@ import ( // newTestOrchestrator returns an Orchestrator with serviceVersions initialised // from a minimal config, suitable for unit tests that call generateRAGInstanceResources. -func newTestOrchestrator() *Orchestrator { +func newTestOrchestrator(t *testing.T) *Orchestrator { + t.Helper() return &Orchestrator{ - serviceVersions: NewServiceVersions(config.Config{}), + serviceVersions: newTestServiceVersions(t, config.Config{}), } } @@ -50,7 +51,7 @@ func minimalRAGConfig() map[string]any { } func TestGenerateRAGInstanceResources_ResourceList(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) spec := &database.ServiceInstanceSpec{ ServiceInstanceID: "storefront-rag-host1", ServiceSpec: &database.ServiceSpec{ @@ -88,7 +89,7 @@ func TestGenerateRAGInstanceResources_ResourceList(t *testing.T) { } func TestGenerateRAGInstanceResources_MultiNode(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) spec := &database.ServiceInstanceSpec{ ServiceInstanceID: "storefront-rag-host1", ServiceSpec: &database.ServiceSpec{ @@ -126,7 +127,7 @@ func TestGenerateRAGInstanceResources_MultiNode(t *testing.T) { } func TestGenerateServiceInstanceResources_RAGDispatch(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) spec := &database.ServiceInstanceSpec{ ServiceInstanceID: "db1-rag-host1", ServiceSpec: &database.ServiceSpec{ @@ -150,7 +151,7 @@ func TestGenerateServiceInstanceResources_RAGDispatch(t *testing.T) { } func TestGenerateServiceInstanceResources_UnknownTypeReturnsError(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) spec := &database.ServiceInstanceSpec{ ServiceInstanceID: "db1-unknown-host1", ServiceSpec: &database.ServiceSpec{ @@ -169,7 +170,7 @@ func TestGenerateServiceInstanceResources_UnknownTypeReturnsError(t *testing.T) } func TestGenerateRAGInstanceResources_ConnectAs_CredentialsPopulated(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) spec := &database.ServiceInstanceSpec{ ServiceInstanceID: "storefront-rag-host1", ServiceSpec: &database.ServiceSpec{ @@ -203,7 +204,7 @@ func TestGenerateRAGInstanceResources_ConnectAs_CredentialsPopulated(t *testing. } func TestGenerateRAGInstanceResources_IncompatibleVersion(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) // Override the "rag/latest" image with a constraint requiring PG >= 18. o.serviceVersions.addServiceImage("rag", "latest", &ServiceImage{ Tag: "rag-server:latest", diff --git a/server/internal/orchestrator/swarm/rag_service_keys_resource_test.go b/server/internal/orchestrator/swarm/rag_service_keys_resource_test.go index b6479141..f091e970 100644 --- a/server/internal/orchestrator/swarm/rag_service_keys_resource_test.go +++ b/server/internal/orchestrator/swarm/rag_service_keys_resource_test.go @@ -234,7 +234,7 @@ func TestExtractRAGAPIKeys_MultiPipeline(t *testing.T) { } func TestGenerateRAGInstanceResources_IncludesKeysResource(t *testing.T) { - o := newTestOrchestrator() + o := newTestOrchestrator(t) spec := &database.ServiceInstanceSpec{ ServiceInstanceID: "storefront-rag-host1", ServiceSpec: &database.ServiceSpec{ diff --git a/server/internal/orchestrator/swarm/reconcile_instance_spec_test.go b/server/internal/orchestrator/swarm/reconcile_instance_spec_test.go index 845f0183..069faa91 100644 --- a/server/internal/orchestrator/swarm/reconcile_instance_spec_test.go +++ b/server/internal/orchestrator/swarm/reconcile_instance_spec_test.go @@ -13,7 +13,7 @@ import ( func TestReconcileInstanceSpec(t *testing.T) { o := &Orchestrator{ - versions: NewVersions(config.Config{ + versions: newTestVersions(t, config.Config{ DockerSwarm: config.DockerSwarm{ ImageRepositoryHost: "registry.example.com/pgedge", }, diff --git a/server/internal/orchestrator/swarm/resolve_instance_images_test.go b/server/internal/orchestrator/swarm/resolve_instance_images_test.go index 1eabe184..0dcabf7b 100644 --- a/server/internal/orchestrator/swarm/resolve_instance_images_test.go +++ b/server/internal/orchestrator/swarm/resolve_instance_images_test.go @@ -13,7 +13,7 @@ import ( func TestResolveInstanceImages(t *testing.T) { o := &Orchestrator{ - versions: NewVersions(config.Config{ + versions: newTestVersions(t, config.Config{ DockerSwarm: config.DockerSwarm{ ImageRepositoryHost: "registry.example.com/pgedge", }, diff --git a/server/internal/orchestrator/swarm/resolve_service_image_test.go b/server/internal/orchestrator/swarm/resolve_service_image_test.go index c3008c40..7f3d1020 100644 --- a/server/internal/orchestrator/swarm/resolve_service_image_test.go +++ b/server/internal/orchestrator/swarm/resolve_service_image_test.go @@ -10,9 +10,10 @@ import ( "github.com/pgEdge/control-plane/server/internal/database" ) -func newTestServiceOrchestrator() *Orchestrator { +func newTestServiceOrchestrator(t *testing.T) *Orchestrator { + t.Helper() return &Orchestrator{ - serviceVersions: NewServiceVersions(config.Config{ + serviceVersions: newTestServiceVersions(t, config.Config{ DockerSwarm: config.DockerSwarm{ ImageRepositoryHost: "registry.example.com/pgedge", }, @@ -32,7 +33,7 @@ func serviceSpecWith(serviceType, version string, swarm *database.SwarmOpts) *da } func TestResolveServiceImage(t *testing.T) { - o := newTestServiceOrchestrator() + o := newTestServiceOrchestrator(t) manifestImage, err := o.serviceVersions.GetServiceImage("mcp", "latest") require.NoError(t, err) @@ -106,7 +107,7 @@ func TestResolveServiceImage(t *testing.T) { } func TestReconcileServiceInstanceSpec(t *testing.T) { - o := newTestServiceOrchestrator() + o := newTestServiceOrchestrator(t) manifestImage, err := o.serviceVersions.GetServiceImage("mcp", "latest") require.NoError(t, err) diff --git a/server/internal/orchestrator/swarm/service_images.go b/server/internal/orchestrator/swarm/service_images.go index 82fb3adc..7fa25b4a 100644 --- a/server/internal/orchestrator/swarm/service_images.go +++ b/server/internal/orchestrator/swarm/service_images.go @@ -36,54 +36,6 @@ type ServiceVersions struct { images map[string]map[string]*ServiceImage } -func NewServiceVersions(cfg config.Config) *ServiceVersions { - versions := &ServiceVersions{ - cfg: cfg, - images: make(map[string]map[string]*ServiceImage), - } - - // MCP service versions - // TODO: Register semver versions when official releases are published. - versions.addServiceImage("mcp", "latest", &ServiceImage{ - Tag: serviceImageTag(cfg, "postgres-mcp:latest"), - // No constraints — MCP works with all PG/Spock versions. - }) - - // PostgREST service versions. - // Images are published to the pgEdge registry under ghcr.io/pgedge/postgrest. - // The bare ref (no registry prefix) lets serviceImageTag prepend the - // configured ImageRepositoryHost (e.g. ghcr.io/pgedge). - versions.addServiceImage("postgrest", "latest", &ServiceImage{ - Tag: serviceImageTag(cfg, "postgrest:latest"), - }) - versions.addServiceImage("postgrest", "14.5", &ServiceImage{ - Tag: serviceImageTag(cfg, "postgrest:14.5"), - }) - - // RAG service versions - // TODO: Register semver versions when official releases are published. - versions.addServiceImage("rag", "latest", &ServiceImage{ - Tag: serviceImageTag(cfg, "rag-server:latest"), - // No constraints — RAG works with all PG/Spock versions. - }) - - // Example of a service image with version constraints (nil = no restriction): - // - // acme-service:1.0.0 requires PG 14-17 and Spock >= 4.0.0 - // - // versions.addServiceImage("acme", "1.0.0", &ServiceImage{ - // Tag: serviceImageTag(cfg, "acme-service:1.0.0"), - // PostgresConstraint: &ds.VersionConstraint{ - // Min: ds.MustParseVersion("14"), - // Max: ds.MustParseVersion("17"), - // }, - // SpockConstraint: &ds.VersionConstraint{ - // Min: ds.MustParseVersion("4.0.0"), - // }, - // }) - - return versions -} func (sv *ServiceVersions) addServiceImage(serviceType string, version string, image *ServiceImage) { if _, ok := sv.images[serviceType]; !ok { diff --git a/server/internal/orchestrator/swarm/service_images_test.go b/server/internal/orchestrator/swarm/service_images_test.go index 1cf40cad..e69253d7 100644 --- a/server/internal/orchestrator/swarm/service_images_test.go +++ b/server/internal/orchestrator/swarm/service_images_test.go @@ -14,7 +14,7 @@ func TestGetServiceImage(t *testing.T) { ImageRepositoryHost: "ghcr.io/pgedge", }, } - sv := NewServiceVersions(cfg) + sv := newTestServiceVersions(t, cfg) tests := []struct { name string @@ -86,7 +86,7 @@ func TestSupportedServiceVersions(t *testing.T) { ImageRepositoryHost: "ghcr.io/pgedge", }, } - sv := NewServiceVersions(cfg) + sv := newTestServiceVersions(t, cfg) tests := []struct { name string @@ -195,7 +195,7 @@ func TestGetServiceImage_ConstraintsPopulated(t *testing.T) { ImageRepositoryHost: "ghcr.io/pgedge", }, } - sv := NewServiceVersions(cfg) + sv := newTestServiceVersions(t, cfg) t.Run("mcp has no constraints", func(t *testing.T) { img, err := sv.GetServiceImage("mcp", "latest") diff --git a/server/internal/orchestrator/swarm/testhelpers_test.go b/server/internal/orchestrator/swarm/testhelpers_test.go new file mode 100644 index 00000000..03a10cb5 --- /dev/null +++ b/server/internal/orchestrator/swarm/testhelpers_test.go @@ -0,0 +1,39 @@ +package swarm + +import ( + "encoding/json" + "testing" + + "github.com/pgEdge/control-plane/server/internal/config" +) + +func parseEmbeddedManifest(t *testing.T) *versionManifest { + t.Helper() + var mf versionManifest + if err := json.Unmarshal(embeddedManifest, &mf); err != nil { + t.Fatalf("unmarshal embedded manifest: %v", err) + } + return &mf +} + +// newTestVersions builds a *Versions from the embedded manifest. +// Use this in tests instead of the deleted NewVersions constructor. +func newTestVersions(t *testing.T, cfg config.Config) *Versions { + t.Helper() + v, err := buildVersions(cfg, parseEmbeddedManifest(t)) + if err != nil { + t.Fatalf("buildVersions: %v", err) + } + return v +} + +// newTestServiceVersions builds a *ServiceVersions from the embedded manifest. +// Use this in tests instead of the deleted NewServiceVersions constructor. +func newTestServiceVersions(t *testing.T, cfg config.Config) *ServiceVersions { + t.Helper() + sv, err := buildServiceVersions(cfg, parseEmbeddedManifest(t)) + if err != nil { + t.Fatalf("buildServiceVersions: %v", err) + } + return sv +} diff --git a/server/internal/orchestrator/swarm/validate_instance_specs_test.go b/server/internal/orchestrator/swarm/validate_instance_specs_test.go index 0e8008b4..d2d206c3 100644 --- a/server/internal/orchestrator/swarm/validate_instance_specs_test.go +++ b/server/internal/orchestrator/swarm/validate_instance_specs_test.go @@ -14,7 +14,7 @@ import ( func TestValidateInstanceSpecs_ImageValidation(t *testing.T) { o := &Orchestrator{ - versions: NewVersions(config.Config{ + versions: newTestVersions(t, config.Config{ DockerSwarm: config.DockerSwarm{ ImageRepositoryHost: "ghcr.io/pgedge", },