Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion server/internal/orchestrator/swarm/find_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
Expand Down
65 changes: 0 additions & 65 deletions server/internal/orchestrator/swarm/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
12 changes: 4 additions & 8 deletions server/internal/orchestrator/swarm/images_test.go
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
91 changes: 0 additions & 91 deletions server/internal/orchestrator/swarm/manifest_loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
Expand Down
59 changes: 40 additions & 19 deletions server/internal/orchestrator/swarm/orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down
11 changes: 10 additions & 1 deletion server/internal/orchestrator/swarm/provide.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
})
}
Loading