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
5 changes: 4 additions & 1 deletion server/internal/resource/migrations/1_0_0_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ func TestVersion_1_0_0(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
state := resource.NewState()
state := &resource.State{
Version: resource.StateVersion_1_0_0,
Resources: map[resource.Type]map[string]*resource.ResourceData{},
}
state.Add(tc.in...)

migration := &migrations.Version_1_0_0{}
Expand Down
39 changes: 39 additions & 0 deletions server/internal/resource/migrations/1_1_0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package migrations

import (
"github.com/pgEdge/control-plane/server/internal/ds"
"github.com/pgEdge/control-plane/server/internal/resource"
)

var _ resource.StateMigration = (*Version_1_1_0)(nil)

// Version_1_1_0 removes swarm.service_user_role resources and scrubs
// references to them from all other resources' dependency lists.
// Services now use connect_as to reference database_users directly.
type Version_1_1_0 struct{}

func (v *Version_1_1_0) Version() *ds.Version {
return resource.StateVersion_1_1_0
}

func (v *Version_1_1_0) Run(state *resource.State) error {
const serviceUserRoleType resource.Type = "swarm.service_user_role"

// 1. Delete all service_user_role resources from state
delete(state.Resources, serviceUserRoleType)

// 2. Remove service_user_role from all other resources' dependency lists
for _, resources := range state.Resources {
for _, data := range resources {
filtered := data.Dependencies[:0]
for _, dep := range data.Dependencies {
if dep.Type != serviceUserRoleType {
filtered = append(filtered, dep)
}
}
data.Dependencies = filtered
}
Comment thread
rshoemaker marked this conversation as resolved.
}

return nil
}
168 changes: 168 additions & 0 deletions server/internal/resource/migrations/1_1_0_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package migrations_test

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/pgEdge/control-plane/server/internal/resource"
"github.com/pgEdge/control-plane/server/internal/resource/migrations"
)

func TestVersion_1_1_0(t *testing.T) {
serviceUserRoleType := resource.Type("swarm.service_user_role")
mcpConfigType := resource.Type("swarm.mcp_config")
serviceInstanceSpecType := resource.Type("swarm.service_instance_spec")
networkType := resource.Type("swarm.network")
dirType := resource.Type("swarm.dir")

svcRoleRO := resource.Identifier{ID: "appmcp-ro", Type: serviceUserRoleType}
svcRoleRW := resource.Identifier{ID: "appmcp-rw", Type: serviceUserRoleType}
networkDep := resource.Identifier{ID: "db-network", Type: networkType}
dirDep := resource.Identifier{ID: "data-dir", Type: dirType}

t.Run("removes service_user_role resources", func(t *testing.T) {
state := &resource.State{
Version: resource.StateVersion_1_0_0.Clone(),
Resources: map[resource.Type]map[string]*resource.ResourceData{
serviceUserRoleType: {
"appmcp-ro": {Identifier: svcRoleRO},
"appmcp-rw": {Identifier: svcRoleRW},
},
mcpConfigType: {
"mcp-cfg": {
Identifier: resource.Identifier{ID: "mcp-cfg", Type: mcpConfigType},
Dependencies: []resource.Identifier{dirDep, svcRoleRO, svcRoleRW},
},
},
serviceInstanceSpecType: {
"svc-spec": {
Identifier: resource.Identifier{ID: "svc-spec", Type: serviceInstanceSpecType},
Dependencies: []resource.Identifier{networkDep, svcRoleRO, svcRoleRW},
},
},
},
}

migration := &migrations.Version_1_1_0{}
err := migration.Run(state)
require.NoError(t, err)

// service_user_role resources should be gone
_, exists := state.Resources[serviceUserRoleType]
assert.False(t, exists, "service_user_role resources should be deleted")

// mcp_config should have service_user_role deps removed
mcpCfg := state.Resources[mcpConfigType]["mcp-cfg"]
require.NotNil(t, mcpCfg)
assert.Equal(t, []resource.Identifier{dirDep}, mcpCfg.Dependencies)

// service_instance_spec should have service_user_role deps removed
svcSpec := state.Resources[serviceInstanceSpecType]["svc-spec"]
require.NotNil(t, svcSpec)
assert.Equal(t, []resource.Identifier{networkDep}, svcSpec.Dependencies)
})

t.Run("removes service_user_role resources across all service types", func(t *testing.T) {
ragConfigType := resource.Type("swarm.rag_config")
postgrestConfigType := resource.Type("swarm.postgrest_config")

mcpRO := resource.Identifier{ID: "appmcp-ro", Type: serviceUserRoleType}
mcpRW := resource.Identifier{ID: "appmcp-rw", Type: serviceUserRoleType}
ragRO := resource.Identifier{ID: "apprag-ro", Type: serviceUserRoleType}
prstRO := resource.Identifier{ID: "appprst-ro", Type: serviceUserRoleType}
prstRW := resource.Identifier{ID: "appprst-rw", Type: serviceUserRoleType}

state := &resource.State{
Version: resource.StateVersion_1_0_0.Clone(),
Resources: map[resource.Type]map[string]*resource.ResourceData{
serviceUserRoleType: {
"appmcp-ro": {Identifier: mcpRO},
"appmcp-rw": {Identifier: mcpRW},
"apprag-ro": {Identifier: ragRO},
"appprst-ro": {Identifier: prstRO},
"appprst-rw": {Identifier: prstRW},
},
mcpConfigType: {
"mcp-cfg": {
Identifier: resource.Identifier{ID: "mcp-cfg", Type: mcpConfigType},
Dependencies: []resource.Identifier{dirDep, mcpRO, mcpRW},
},
},
ragConfigType: {
"rag-cfg": {
Identifier: resource.Identifier{ID: "rag-cfg", Type: ragConfigType},
Dependencies: []resource.Identifier{dirDep, ragRO},
},
},
postgrestConfigType: {
"prst-cfg": {
Identifier: resource.Identifier{ID: "prst-cfg", Type: postgrestConfigType},
Dependencies: []resource.Identifier{dirDep, prstRO, prstRW},
},
},
serviceInstanceSpecType: {
"svc-spec": {
Identifier: resource.Identifier{ID: "svc-spec", Type: serviceInstanceSpecType},
Dependencies: []resource.Identifier{networkDep, mcpRO, mcpRW, ragRO, prstRO, prstRW},
},
},
},
}

migration := &migrations.Version_1_1_0{}
err := migration.Run(state)
require.NoError(t, err)

// All service_user_role resources should be gone
_, exists := state.Resources[serviceUserRoleType]
assert.False(t, exists, "service_user_role resources should be deleted")

// MCP config: only dirDep remains
assert.Equal(t, []resource.Identifier{dirDep}, state.Resources[mcpConfigType]["mcp-cfg"].Dependencies)

// RAG config: only dirDep remains
assert.Equal(t, []resource.Identifier{dirDep}, state.Resources[ragConfigType]["rag-cfg"].Dependencies)

// PostgREST config: only dirDep remains
assert.Equal(t, []resource.Identifier{dirDep}, state.Resources[postgrestConfigType]["prst-cfg"].Dependencies)

// service_instance_spec: only networkDep remains
assert.Equal(t, []resource.Identifier{networkDep}, state.Resources[serviceInstanceSpecType]["svc-spec"].Dependencies)
})

t.Run("no-op when no service_user_role resources exist", func(t *testing.T) {
state := &resource.State{
Version: resource.StateVersion_1_0_0.Clone(),
Resources: map[resource.Type]map[string]*resource.ResourceData{
mcpConfigType: {
"mcp-cfg": {
Identifier: resource.Identifier{ID: "mcp-cfg", Type: mcpConfigType},
Dependencies: []resource.Identifier{dirDep},
},
},
},
}

migration := &migrations.Version_1_1_0{}
err := migration.Run(state)
require.NoError(t, err)

// mcp_config should be untouched
mcpCfg := state.Resources[mcpConfigType]["mcp-cfg"]
require.NotNil(t, mcpCfg)
assert.Equal(t, []resource.Identifier{dirDep}, mcpCfg.Dependencies)
})

t.Run("empty state", func(t *testing.T) {
state := &resource.State{
Version: resource.StateVersion_1_0_0.Clone(),
Resources: map[resource.Type]map[string]*resource.ResourceData{},
}

migration := &migrations.Version_1_1_0{}
err := migration.Run(state)
require.NoError(t, err)
})
}
1 change: 1 addition & 0 deletions server/internal/resource/migrations/provide.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func provideStateMigrations(i *do.Injector) {
do.Provide(i, func(i *do.Injector) (*resource.StateMigrations, error) {
return resource.NewStateMigrations([]resource.StateMigration{
&Version_1_0_0{},
&Version_1_1_0{},
}), nil
})
}
3 changes: 2 additions & 1 deletion server/internal/resource/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import (

var (
StateVersion_1_0_0 = ds.MustParseVersion("1.0.0")
StateVersion_1_1_0 = ds.MustParseVersion("1.1.0")

CurrentVersion = StateVersion_1_0_0
CurrentVersion = StateVersion_1_1_0
)

var (
Expand Down