From 043bb7e8a8a4a3c4622690c12d936b855702fe24 Mon Sep 17 00:00:00 2001 From: Rohil Surana Date: Fri, 17 Jul 2026 13:34:40 +0530 Subject: [PATCH 1/2] chore(server): remove the resources_config_path boot loader flow --- cmd/serve.go | 21 +-------- config/sample.config.yaml | 14 ------ internal/bootstrap/service.go | 44 +++++------------- internal/store/blob/schema_repository.go | 58 ------------------------ pkg/server/config.go | 8 ---- 5 files changed, 12 insertions(+), 133 deletions(-) delete mode 100644 internal/store/blob/schema_repository.go diff --git a/cmd/serve.go b/cmd/serve.go index ae2ac1304..a8cc1e2a2 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -134,22 +134,6 @@ func StartServer(logger *slog.Logger, cfg *config.Frontier) error { } }() - // load resource config - if cfg.App.ResourcesConfigPath != "" { - logger.Warn("app.resources_config_path is deprecated and will be removed after a deprecation window; " + - "manage permissions and roles with 'frontier reconcile' instead") - } - resourceBlobFS, err := blob.NewStore(ctx, cfg.App.ResourcesConfigPath, cfg.App.ResourcesConfigPathSecret) - if err != nil { - return err - } - defer func() { - logger.Debug("cleaning up resource blob") - if err := resourceBlobFS.Close(); err != nil { - logger.Warn("resource blob cleanup failed", "err", err) - } - }() - // load billing plans billingBlobFS, err := blob.NewStore(ctx, cfg.Billing.PlansPath, "") if err != nil { @@ -182,7 +166,7 @@ func StartServer(logger *slog.Logger, cfg *config.Frontier) error { return err } - deps, err := buildAPIDependencies(logger, cfg, dbClient, spiceDBClient, resourceBlobFS, billingPlanRepository) + deps, err := buildAPIDependencies(logger, cfg, dbClient, spiceDBClient, billingPlanRepository) if err != nil { return err } @@ -361,7 +345,6 @@ func buildAPIDependencies( cfg *config.Frontier, dbc *db.Client, sdb *spicedb.SpiceDB, - resourceBlobBucket blob.Bucket, planBlobRepository *blob.PlanRepository, ) (api.Deps, error) { // Load additional traits from config file if specified @@ -594,11 +577,9 @@ func buildAPIDependencies( usageService := usage.NewService(creditService) - resourceSchemaRepository := blob.NewSchemaConfigRepository(resourceBlobBucket) bootstrapService := bootstrap.NewBootstrapService( logger, cfg.App.Admin, - resourceSchemaRepository, namespaceService, roleService, permissionService, diff --git a/config/sample.config.yaml b/config/sample.config.yaml index 78c772067..3e8ae07f1 100644 --- a/config/sample.config.yaml +++ b/config/sample.config.yaml @@ -63,20 +63,6 @@ app: host: "127.0.0.1" # WARNING: identity_proxy_header bypass all authorization checks and shouldn't be used in production identity_proxy_header: X-Frontier-Email - # DEPRECATED: will be removed after a deprecation window. Manage permissions - # and roles with 'frontier reconcile' instead (see docs/rfcs/0001-declarative-reconcile.md). - # full path prefixed with scheme where resources config yaml files are kept - # e.g.: - # local storage file "file:///tmp/resources_config" - # GCS Bucket "gs://frontier/resources_config" - resources_config_path: file:///tmp/resources_config\ - # secret required to access resources config - # e.g.: - # system environment variable "env://TEST_RULESET_SECRET" - # local file "file:///opt/auth.json" - # secret string "val://user:password" - # optional - resources_config_path_secret: env://TEST_RESOURCE_CONFIG_SECRET # cross-origin resource sharing configuration cors: diff --git a/internal/bootstrap/service.go b/internal/bootstrap/service.go index a4d9dd26b..0b297551b 100644 --- a/internal/bootstrap/service.go +++ b/internal/bootstrap/service.go @@ -42,10 +42,6 @@ type RelationService interface { Delete(ctx context.Context, rel relation.Relation) error } -type FileService interface { - GetDefinition(ctx context.Context) (*schema.ServiceDefinition, error) -} - type AuthzEngine interface { WriteSchema(ctx context.Context, schema string) error } @@ -87,7 +83,6 @@ type AdminConfig struct { type Service struct { logger *slog.Logger adminConfig AdminConfig - schemaConfig FileService namespaceService NamespaceService roleService RoleService permissionService PermissionService @@ -108,7 +103,6 @@ type Service struct { func NewBootstrapService( logger *slog.Logger, config AdminConfig, - schemaConfig FileService, namespaceService NamespaceService, roleService RoleService, actionService PermissionService, @@ -126,7 +120,6 @@ func NewBootstrapService( return &Service{ logger: logger, adminConfig: config, - schemaConfig: schemaConfig, namespaceService: namespaceService, roleService: roleService, permissionService: actionService, @@ -144,25 +137,18 @@ func NewBootstrapService( } func (s Service) MigrateSchema(ctx context.Context) error { - customServiceDefinition, err := s.schemaConfig.GetDefinition(ctx) - if err != nil { - return err - } - - return s.AppendSchema(ctx, *customServiceDefinition) + // Custom permissions are managed through the reconcile flow now. Boot only + // re-applies the base schema merged with the permissions already in the + // database (AppendSchema keeps existing ones), so no config file is read. + return s.AppendSchema(ctx, schema.ServiceDefinition{}) } -// BuiltinPermissions returns the permissions that come from the base schema and -// the config files — the ones bootstrap recreates on every boot. It looks only -// at the base schema and config, not at the permissions already in the database. +// BuiltinPermissions returns the permissions that come from the base schema — +// the ones bootstrap recreates on every boot and that cannot be deleted through +// the API. It looks only at the base schema, not at the permissions already in +// the database. func (s Service) BuiltinPermissions(ctx context.Context) (map[string]struct{}, error) { - custom, err := s.schemaConfig.GetDefinition(ctx) - if err != nil { - return nil, err - } - custom.Permissions = filterDefaultAppNamespacePermissions(custom.Permissions) - - defs, err := ApplyServiceDefinitionOverAZSchema(custom, GetBaseAZSchema()) + defs, err := ApplyServiceDefinitionOverAZSchema(&schema.ServiceDefinition{}, GetBaseAZSchema()) if err != nil { return nil, err } @@ -269,16 +255,8 @@ func (s Service) MigrateRoles(ctx context.Context) error { } } - // migrate user defined roles to org - serviceDefinition, err := s.schemaConfig.GetDefinition(ctx) - if err != nil { - return err - } - for _, defRole := range serviceDefinition.Roles { - if err = s.migrateRole(ctx, defaultOrgID, defRole); err != nil { - return err - } - } + // Custom roles are managed through the reconcile flow now; boot no longer + // creates them from a config file. // backfill PAT wildcard tuples for all existing roles if err = s.migratePATRelations(ctx); err != nil { diff --git a/internal/store/blob/schema_repository.go b/internal/store/blob/schema_repository.go deleted file mode 100644 index 9ab8de9b9..000000000 --- a/internal/store/blob/schema_repository.go +++ /dev/null @@ -1,58 +0,0 @@ -package blob - -import ( - "context" - "fmt" - "io" - "strings" - - "github.com/raystack/frontier/internal/bootstrap/schema" - - "github.com/pkg/errors" - "gocloud.dev/blob" - "gopkg.in/yaml.v3" -) - -type SchemaRepository struct { - bucket Bucket -} - -func NewSchemaConfigRepository(b Bucket) *SchemaRepository { - return &SchemaRepository{bucket: b} -} - -// GetDefinition returns the service definition from the bucket -func (s *SchemaRepository) GetDefinition(ctx context.Context) (*schema.ServiceDefinition, error) { - var definitions []schema.ServiceDefinition - - // iterate over bucket files, only read .yml & .yaml files - it := s.bucket.List(&blob.ListOptions{}) - for { - obj, err := it.Next(ctx) - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - - if obj.IsDir { - continue - } - if !(strings.HasSuffix(obj.Key, ".yaml") || strings.HasSuffix(obj.Key, ".yml")) { - continue - } - fileBytes, err := s.bucket.ReadAll(ctx, obj.Key) - if err != nil { - return nil, fmt.Errorf("%s: %s", "error in reading bucket object", err.Error()) - } - - var def schema.ServiceDefinition - if err := yaml.Unmarshal(fileBytes, &def); err != nil { - return nil, errors.Wrap(err, "GetDefinitions: yaml.Unmarshal: "+obj.Key) - } - definitions = append(definitions, def) - } - - return schema.MergeServiceDefinitions(definitions...), nil -} diff --git a/pkg/server/config.go b/pkg/server/config.go index c8e6124b2..6f20ba39f 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -70,14 +70,6 @@ type Config struct { // Headers which will have user's email id IdentityProxyHeader string `yaml:"identity_proxy_header" mapstructure:"identity_proxy_header" default:""` - // ResourcesPath is a directory path where resources is defined - // that this service should implement - ResourcesConfigPath string `yaml:"resources_config_path" mapstructure:"resources_config_path"` - - // ResourcesPathSecretSecret could be an env name, file path or actual value required - // to access ResourcesPathSecretPath files - ResourcesConfigPathSecret string `yaml:"resources_config_path_secret" mapstructure:"resources_config_path_secret"` - Authentication authenticate.Config `yaml:"authentication" mapstructure:"authentication"` // Cors configuration setup origin value from where we want to allow cors From 16283c93f7af3e38d95d0c60359e3c7ee3eb02a3 Mon Sep 17 00:00:00 2001 From: Rohil Surana Date: Fri, 17 Jul 2026 14:27:38 +0530 Subject: [PATCH 2/2] test(e2e): seed custom compute resources via admin API instead of the removed boot loader --- internal/store/blob/schema_repository_test.go | 100 ------------------ test/e2e/regression/api_test.go | 16 ++- test/e2e/regression/authentication_test.go | 7 -- test/e2e/regression/billing_test.go | 5 +- test/e2e/regression/onboarding_test.go | 16 ++- test/e2e/regression/pat_test.go | 11 +- .../regression/service_registration_test.go | 11 +- test/e2e/regression/serviceusers_test.go | 11 +- .../regression/testdata/resource/compute.yml | 33 ------ test/e2e/smoke/ping_test.go | 15 +-- test/e2e/smoke/testdata/resource/potato.yaml | 7 -- test/e2e/testbench/helper.go | 41 +++++++ 12 files changed, 65 insertions(+), 208 deletions(-) delete mode 100644 internal/store/blob/schema_repository_test.go delete mode 100644 test/e2e/regression/testdata/resource/compute.yml delete mode 100644 test/e2e/smoke/testdata/resource/potato.yaml diff --git a/internal/store/blob/schema_repository_test.go b/internal/store/blob/schema_repository_test.go deleted file mode 100644 index 26df92a56..000000000 --- a/internal/store/blob/schema_repository_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package blob - -import ( - "context" - "sort" - "testing" - - "github.com/raystack/frontier/internal/bootstrap/schema" - - "github.com/stretchr/testify/assert" -) - -func TestGetSchema(t *testing.T) { - testBucket, err := NewStore(context.Background(), "file://testdata", "") - assert.NoError(t, err) - - s := SchemaRepository{ - bucket: testBucket, - } - def, err := s.GetDefinition(context.Background()) - assert.NoError(t, err) - - expectedMap := &schema.ServiceDefinition{ - Roles: []schema.RoleDefinition{ - { - Name: "compute_order_manager", - Permissions: []string{ - "compute/order:delete", - "compute/order:update", - "compute/order:get", - "compute/order:list", - "compute/order:create", - }, - }, - { - Name: "compute_order_viewer", - Permissions: []string{ - "compute/order:list", - "compute/order:get", - }, - }, - { - Name: "compute_order_owner", - Permissions: []string{ - "compute/order:delete", - "compute/order:update", - "compute/order:get", - "compute/order:create", - }, - }, - }, - Permissions: []schema.ResourcePermission{ - { - Name: "delete", - Namespace: "compute/order", - }, - { - Name: "update", - Namespace: "compute/order", - }, - { - Name: "get", - Namespace: "compute/order", - }, - { - Name: "list", - Namespace: "compute/order", - }, - { - Name: "create", - Namespace: "compute/order", - }, - { - Name: "delete", - Namespace: "database/order", - }, - { - Name: "update", - Namespace: "database/order", - }, - { - Name: "get", - Namespace: "database/order", - }, - }, - } - sort.Slice(def.Roles, func(i, j int) bool { - return def.Roles[i].Name < def.Roles[j].Name - }) - sort.Slice(expectedMap.Roles, func(i, j int) bool { - return expectedMap.Roles[i].Name < expectedMap.Roles[j].Name - }) - sort.Slice(def.Permissions, func(i, j int) bool { - return schema.FQPermissionNameFromNamespace(def.Permissions[i].Namespace, def.Permissions[i].Name) < schema.FQPermissionNameFromNamespace(def.Permissions[j].Namespace, def.Permissions[j].Name) - }) - sort.Slice(expectedMap.Permissions, func(i, j int) bool { - return schema.FQPermissionNameFromNamespace(expectedMap.Permissions[i].Namespace, expectedMap.Permissions[i].Name) < schema.FQPermissionNameFromNamespace(expectedMap.Permissions[j].Namespace, expectedMap.Permissions[j].Name) - }) - assert.Equal(t, expectedMap, def) -} diff --git a/test/e2e/regression/api_test.go b/test/e2e/regression/api_test.go index 7f686c54e..2e799885e 100644 --- a/test/e2e/regression/api_test.go +++ b/test/e2e/regression/api_test.go @@ -6,8 +6,6 @@ import ( "io" "net/http" "net/http/httptest" - "os" - "path" "strings" "testing" "time" @@ -69,10 +67,6 @@ type APIRegressionTestSuite struct { } func (s *APIRegressionTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Require().Nil(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Require().NoError(err) @@ -82,9 +76,8 @@ func (s *APIRegressionTestSuite) SetupSuite() { AuditEvents: "db", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", @@ -106,6 +99,11 @@ func (s *APIRegressionTestSuite) SetupSuite() { ctx := context.Background() + // Seed the custom "compute" permissions and roles the tests below rely on, + // via the admin API. This replaces the removed boot-time resources_config + // loader — tests now seed custom resources the same way operators do. + s.Require().NoError(testbench.SeedComputeResources(ctx, s.testBench.AdminClient)) + adminCookie, err := testbench.AuthenticateUser(ctx, s.testBench.Client, testbench.OrgAdminEmail) s.Require().NoError(err) s.adminCookie = adminCookie diff --git a/test/e2e/regression/authentication_test.go b/test/e2e/regression/authentication_test.go index 4b0661810..e3496941d 100644 --- a/test/e2e/regression/authentication_test.go +++ b/test/e2e/regression/authentication_test.go @@ -6,8 +6,6 @@ import ( "fmt" "net/http" "net/url" - "os" - "path" "strings" "testing" "time" @@ -44,10 +42,6 @@ type AuthenticationRegressionTestSuite struct { } func (s *AuthenticationRegressionTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Require().Nil(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Require().NoError(err) s.connectPort = connectPort @@ -82,7 +76,6 @@ func (s *AuthenticationRegressionTestSuite) SetupSuite() { Connect: server.ConnectConfig{ Port: connectPort, }, - ResourcesConfigPath: path.Join(testDataPath, "resource"), Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", diff --git a/test/e2e/regression/billing_test.go b/test/e2e/regression/billing_test.go index ab4e5ca13..83350b6df 100644 --- a/test/e2e/regression/billing_test.go +++ b/test/e2e/regression/billing_test.go @@ -51,9 +51,8 @@ func (s *BillingRegressionTestSuite) SetupSuite() { AuditEvents: "db", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", diff --git a/test/e2e/regression/onboarding_test.go b/test/e2e/regression/onboarding_test.go index e4713d2d4..129df136d 100644 --- a/test/e2e/regression/onboarding_test.go +++ b/test/e2e/regression/onboarding_test.go @@ -2,8 +2,6 @@ package e2e_test import ( "context" - "os" - "path" "testing" "time" @@ -30,10 +28,6 @@ type OnboardingRegressionTestSuite struct { } func (s *OnboardingRegressionTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Require().Nil(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Require().NoError(err) @@ -42,9 +36,8 @@ func (s *OnboardingRegressionTestSuite) SetupSuite() { Level: "error", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", @@ -66,6 +59,11 @@ func (s *OnboardingRegressionTestSuite) SetupSuite() { ctx := context.Background() + // Seed the custom "compute" permissions and roles the tests below rely on, + // via the admin API. This replaces the removed boot-time resources_config + // loader — tests now seed custom resources the same way operators do. + s.Require().NoError(testbench.SeedComputeResources(ctx, s.testBench.AdminClient)) + adminCookie, err := testbench.AuthenticateUser(ctx, s.testBench.Client, testbench.OrgAdminEmail) s.Require().NoError(err) s.adminCookie = adminCookie diff --git a/test/e2e/regression/pat_test.go b/test/e2e/regression/pat_test.go index e372702ab..b34fe6b82 100644 --- a/test/e2e/regression/pat_test.go +++ b/test/e2e/regression/pat_test.go @@ -3,8 +3,6 @@ package e2e_test import ( "context" "fmt" - "os" - "path" "testing" "time" @@ -31,10 +29,6 @@ type PATRegressionTestSuite struct { } func (s *PATRegressionTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Require().Nil(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Require().NoError(err) @@ -43,9 +37,8 @@ func (s *PATRegressionTestSuite) SetupSuite() { Level: "error", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", diff --git a/test/e2e/regression/service_registration_test.go b/test/e2e/regression/service_registration_test.go index 685700f3a..276b7dfa1 100644 --- a/test/e2e/regression/service_registration_test.go +++ b/test/e2e/regression/service_registration_test.go @@ -2,8 +2,6 @@ package e2e_test import ( "context" - "os" - "path" "testing" "time" @@ -29,10 +27,6 @@ type ServiceRegistrationRegressionTestSuite struct { } func (s *ServiceRegistrationRegressionTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Require().Nil(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Require().NoError(err) @@ -41,9 +35,8 @@ func (s *ServiceRegistrationRegressionTestSuite) SetupSuite() { Level: "error", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", diff --git a/test/e2e/regression/serviceusers_test.go b/test/e2e/regression/serviceusers_test.go index c4a74d609..7b7f1d081 100644 --- a/test/e2e/regression/serviceusers_test.go +++ b/test/e2e/regression/serviceusers_test.go @@ -7,8 +7,6 @@ import ( "encoding/base64" "fmt" "net/http" - "os" - "path" "testing" "time" @@ -39,10 +37,6 @@ type ServiceUsersRegressionTestSuite struct { } func (s *ServiceUsersRegressionTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Require().Nil(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Require().NoError(err) s.connectPort = connectPort @@ -52,9 +46,8 @@ func (s *ServiceUsersRegressionTestSuite) SetupSuite() { Level: "error", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, Authentication: authenticate.Config{ Session: authenticate.SessionConfig{ HashSecretKey: "hash-secret-should-be-32-chars--", diff --git a/test/e2e/regression/testdata/resource/compute.yml b/test/e2e/regression/testdata/resource/compute.yml deleted file mode 100644 index 8ade548e0..000000000 --- a/test/e2e/regression/testdata/resource/compute.yml +++ /dev/null @@ -1,33 +0,0 @@ -permissions: - - name: delete - namespace: compute/order - - name: update - namespace: compute/order - - name: get - namespace: compute/order - - name: create - namespace: compute/order - - key: compute.order.configure - - name: get - namespace: compute/disk - - name: create - namespace: compute/disk - - name: delete - namespace: compute/disk -roles: - - name: compute_order_manager - permissions: - - compute_order_delete - - compute_order_update - - compute_order_get - - compute_order_create - - name: compute_order_viewer - permissions: - - compute_order_get - - name: compute_order_owner - permissions: - - compute_order_delete - - compute_order_update - - compute_order_get - - compute_order_create - diff --git a/test/e2e/smoke/ping_test.go b/test/e2e/smoke/ping_test.go index 701d81393..5c8b67b1e 100644 --- a/test/e2e/smoke/ping_test.go +++ b/test/e2e/smoke/ping_test.go @@ -4,8 +4,6 @@ import ( "fmt" "io" "net/http" - "os" - "path" "testing" "github.com/raystack/frontier/pkg/server" @@ -16,10 +14,6 @@ import ( "github.com/stretchr/testify/suite" ) -const ( - fixturesDir = "testdata" -) - type PingSmokeTestSuite struct { suite.Suite @@ -28,10 +22,6 @@ type PingSmokeTestSuite struct { } func (s *PingSmokeTestSuite) SetupSuite() { - wd, err := os.Getwd() - s.Assert().NoError(err) - testDataPath := path.Join("file://", wd, fixturesDir) - connectPort, err := testbench.GetFreePort() s.Assert().NoError(err) s.connectPort = connectPort @@ -41,9 +31,8 @@ func (s *PingSmokeTestSuite) SetupSuite() { Level: "fatal", }, App: server.Config{ - Host: "localhost", - Connect: server.ConnectConfig{Port: connectPort}, - ResourcesConfigPath: path.Join(testDataPath, "resource"), + Host: "localhost", + Connect: server.ConnectConfig{Port: connectPort}, }, } diff --git a/test/e2e/smoke/testdata/resource/potato.yaml b/test/e2e/smoke/testdata/resource/potato.yaml deleted file mode 100644 index a2b4e42cf..000000000 --- a/test/e2e/smoke/testdata/resource/potato.yaml +++ /dev/null @@ -1,7 +0,0 @@ -permissions: - - name: delete - namespace: potato/cart - - name: update - namespace: potato/cart - - name: get - namespace: potato/cart \ No newline at end of file diff --git a/test/e2e/testbench/helper.go b/test/e2e/testbench/helper.go index 1d6e279c7..bbd187fca 100644 --- a/test/e2e/testbench/helper.go +++ b/test/e2e/testbench/helper.go @@ -69,6 +69,47 @@ func PromoteBootstrapAdmin(ctx context.Context, ad frontierv1beta1connect.AdminS return fmt.Errorf("promote bootstrap admin (server unavailable after retries): %w", lastErr) } +// SeedComputeResources creates the custom "compute" permissions and roles the +// regression suites rely on, through the admin API. This replaces the removed +// boot-time resources_config loader: tests now seed custom resources the same +// way operators do, via reconcile/the admin API. +func SeedComputeResources(ctx context.Context, ad frontierv1beta1connect.AdminServiceClient) error { + basic := base64.StdEncoding.EncodeToString([]byte(BootstrapClientID + ":" + BootstrapClientSecret)) + authCtx := ContextWithHeaders(ctx, map[string]string{"Authorization": "Basic " + basic}) + + permKeys := []string{ + "compute.order.delete", "compute.order.update", "compute.order.get", + "compute.order.create", "compute.order.configure", + "compute.disk.get", "compute.disk.create", "compute.disk.delete", + } + bodies := make([]*frontierv1beta1.PermissionRequestBody, 0, len(permKeys)) + for _, key := range permKeys { + bodies = append(bodies, &frontierv1beta1.PermissionRequestBody{Key: key}) + } + if _, err := ad.CreatePermission(authCtx, connect.NewRequest(&frontierv1beta1.CreatePermissionRequest{ + Bodies: bodies, + })); err != nil { + return fmt.Errorf("seed compute permissions: %w", err) + } + + roles := []struct { + name string + perms []string + }{ + {"compute_order_manager", []string{"compute_order_delete", "compute_order_update", "compute_order_get", "compute_order_create"}}, + {"compute_order_viewer", []string{"compute_order_get"}}, + {"compute_order_owner", []string{"compute_order_delete", "compute_order_update", "compute_order_get", "compute_order_create"}}, + } + for _, r := range roles { + if _, err := ad.CreateRole(authCtx, connect.NewRequest(&frontierv1beta1.CreateRoleRequest{ + Body: &frontierv1beta1.RoleRequestBody{Name: r.name, Permissions: r.perms}, + })); err != nil { + return fmt.Errorf("seed compute role %s: %w", r.name, err) + } + } + return nil +} + // headersKey is the context key for storing headers to be sent with ConnectRPC requests. type headersKey struct{}