From aa71655f58068954815815d74398998ef47ea763 Mon Sep 17 00:00:00 2001 From: Kian Parvin Date: Mon, 26 Aug 2024 10:24:22 +0200 Subject: [PATCH 1/2] add default cloud functionality --- internal/jimm/cloud.go | 22 ++++ internal/jimm/cloud_test.go | 58 ++++++++++ internal/jimm/model.go | 36 +++++-- internal/jimm/model_test.go | 27 ++++- internal/jimmtest/jimm_mock.go | 147 +++---------------------- internal/jimmtest/mocks/model.go | 166 +++++++++++++++++++++++++++++ internal/jujuapi/controllerroot.go | 22 +--- internal/jujuapi/modelmanager.go | 30 +++++- 8 files changed, 342 insertions(+), 166 deletions(-) create mode 100644 internal/jimmtest/mocks/model.go diff --git a/internal/jimm/cloud.go b/internal/jimm/cloud.go index ba0259747..4a2f4eef9 100644 --- a/internal/jimm/cloud.go +++ b/internal/jimm/cloud.go @@ -69,6 +69,28 @@ func (j *JIMM) GetCloud(ctx context.Context, user *openfga.User, tag names.Cloud } } +// DefaultCloud returns the default cloud to use for user requests. +// If the user has access to 0 or more than 1 cloud an error will be returned. +func (j *JIMM) DefaultCloud(ctx context.Context, user *openfga.User) (names.CloudTag, error) { + const op = errors.Op("jimm.DefaultCloud") + var userClouds []*dbmodel.Cloud + err := j.ForEachUserCloud(ctx, user, func(c *dbmodel.Cloud) error { + userClouds = append(userClouds, c) + return nil + }) + if err != nil { + return names.CloudTag{}, errors.E(op, err) + } + switch len(userClouds) { + case 0: + return names.CloudTag{}, errors.E(op, "no clouds available") + case 1: + return userClouds[0].ResourceTag(), nil + default: + return names.CloudTag{}, errors.E(op, "multiple clouds available; please specify one") + } +} + // ForEachUserCloud iterates through all of the clouds a user has access to // calling the given function for each cloud. If the user has admin level // access to the cloud then the provided cloud will include all user diff --git a/internal/jimm/cloud_test.go b/internal/jimm/cloud_test.go index 36e1119c6..6b62a26d1 100644 --- a/internal/jimm/cloud_test.go +++ b/internal/jimm/cloud_test.go @@ -156,6 +156,64 @@ func TestGetCloud(t *testing.T) { }) } +func TestDefaultCloud(t *testing.T) { + c := qt.New(t) + + client, _, _, err := jimmtest.SetupTestOFGAClient(c.Name()) + c.Assert(err, qt.IsNil) + + ctx := context.Background() + now := time.Now().UTC().Round(time.Millisecond) + j := &jimm.JIMM{ + UUID: uuid.NewString(), + OpenFGAClient: client, + Database: db.Database{ + DB: jimmtest.PostgresDB(c, func() time.Time { return now }), + }, + } + + err = j.Database.Migrate(ctx, false) + c.Assert(err, qt.IsNil) + + aliceIdentity, err := dbmodel.NewIdentity("alice@canonical.com") + c.Assert(err, qt.IsNil) + alice := openfga.NewUser( + aliceIdentity, + client, + ) + + cloud := &dbmodel.Cloud{ + Name: "test-cloud-1", + } + err = j.Database.AddCloud(ctx, cloud) + c.Assert(err, qt.IsNil) + + cloud2 := &dbmodel.Cloud{ + Name: "test-cloud-2", + } + err = j.Database.AddCloud(ctx, cloud2) + c.Assert(err, qt.IsNil) + + // Test access to 0 clouds. + _, err = j.DefaultCloud(ctx, alice) + c.Assert(err, qt.ErrorMatches, "no clouds available") + + // Test access to 1 cloud. + err = alice.SetCloudAccess(context.Background(), cloud.ResourceTag(), ofganames.AdministratorRelation) + c.Assert(err, qt.IsNil) + + defaultCloud, err := j.DefaultCloud(ctx, alice) + c.Assert(err, qt.IsNil) + c.Assert(defaultCloud.String(), qt.Equals, "cloud-test-cloud-1") + + // Test access to more than 1 cloud. + err = alice.SetCloudAccess(context.Background(), cloud2.ResourceTag(), ofganames.AdministratorRelation) + c.Assert(err, qt.IsNil) + + _, err = j.DefaultCloud(ctx, alice) + c.Assert(err, qt.ErrorMatches, "multiple clouds available; please specify one") +} + func TestForEachCloud(t *testing.T) { c := qt.New(t) diff --git a/internal/jimm/model.go b/internal/jimm/model.go index 1d2ee823a..a8d73d54c 100644 --- a/internal/jimm/model.go +++ b/internal/jimm/model.go @@ -47,22 +47,21 @@ type ModelCreateArgs struct { CloudCredential names.CloudCredentialTag } +type ModelCreateDefaults struct { + DefaultCloud func() (names.CloudTag, error) +} + // FromJujuModelCreateArgs converts jujuparams.ModelCreateArgs into AddModelArgs. -func (a *ModelCreateArgs) FromJujuModelCreateArgs(args *jujuparams.ModelCreateArgs) error { +func (a *ModelCreateArgs) FromJujuModelCreateArgs(args *jujuparams.ModelCreateArgs, opts ModelCreateDefaults) error { if args.Name == "" { return errors.E("name not specified") } a.Name = args.Name a.Config = args.Config a.CloudRegion = args.CloudRegion - if args.CloudTag == "" { - return errors.E("no cloud specified for model; please specify one") - } - ct, err := names.ParseCloudTag(args.CloudTag) - if err != nil { - return errors.E(err, errors.CodeBadRequest) + if err := a.parseCloud(args.CloudTag, opts.DefaultCloud); err != nil { + return err } - a.Cloud = ct if args.OwnerTag == "" { return errors.E("owner tag not specified") @@ -87,6 +86,25 @@ func (a *ModelCreateArgs) FromJujuModelCreateArgs(args *jujuparams.ModelCreateAr return nil } +func (a *ModelCreateArgs) parseCloud(cloudTag string, defaultCloudTag func() (names.CloudTag, error)) error { + var err error + if cloudTag != "" { + a.Cloud, err = names.ParseCloudTag(cloudTag) + if err != nil { + return errors.E(err, errors.CodeBadRequest) + } + return nil + } + if defaultCloudTag != nil { + a.Cloud, err = defaultCloudTag() + if err != nil { + return err + } + return nil + } + return errors.E("no cloud specified for model; please specify one") +} + func newModelBuilder(ctx context.Context, j *JIMM) *modelBuilder { return &modelBuilder{ ctx: ctx, @@ -299,7 +317,7 @@ func (b *modelBuilder) CreateDatabaseModel() *modelBuilder { if b.credential == nil { // try to select a valid credential if err := b.selectCloudCredentials(); err != nil { - b.err = errors.E(err, "could not select cloud credentials") + b.err = errors.E(fmt.Sprintf("could not select cloud credentials: %s", err)) return b } } diff --git a/internal/jimm/model_test.go b/internal/jimm/model_test.go index 57b7cbcc0..f8decbf26 100644 --- a/internal/jimm/model_test.go +++ b/internal/jimm/model_test.go @@ -36,6 +36,7 @@ func TestModelCreateArgs(t *testing.T) { tests := []struct { about string args jujuparams.ModelCreateArgs + defaults jimm.ModelCreateDefaults expectedArgs jimm.ModelCreateArgs expectedError string }{{ @@ -105,13 +106,30 @@ func TestModelCreateArgs(t *testing.T) { }, expectedError: "owner tag not specified", }, { - about: "cloud tag not specified", + about: "cloud tag not specified and no default configured", args: jujuparams.ModelCreateArgs{ Name: "test-model", OwnerTag: names.NewUserTag("alice@canonical.com").String(), CloudCredentialTag: names.NewCloudCredentialTag("test-cloud/alice/test-credential-1").String(), }, expectedError: "no cloud specified for model; please specify one", + }, { + about: "cloud tag not specified but default is available", + args: jujuparams.ModelCreateArgs{ + Name: "test-model", + OwnerTag: names.NewUserTag("alice@canonical.com").String(), + CloudCredentialTag: names.NewCloudCredentialTag("test-cloud/alice/test-credential-1").String(), + }, + defaults: jimm.ModelCreateDefaults{ + DefaultCloud: func() (names.CloudTag, error) { + return names.NewCloudTag("test-cloud"), nil + }}, + expectedArgs: jimm.ModelCreateArgs{ + Name: "test-model", + Owner: names.NewUserTag("alice@canonical.com"), + Cloud: names.NewCloudTag("test-cloud"), + CloudCredential: names.NewCloudCredentialTag("test-cloud/alice/test-credential-1"), + }, }} opts := []cmp.Option{ @@ -128,7 +146,7 @@ func TestModelCreateArgs(t *testing.T) { for _, test := range tests { c.Run(test.about, func(c *qt.C) { var a jimm.ModelCreateArgs - err := a.FromJujuModelCreateArgs(&test.args) + err := a.FromJujuModelCreateArgs(&test.args, test.defaults) if test.expectedError == "" { c.Assert(err, qt.IsNil) c.Assert(a, qt.CmpEquals(opts...), test.expectedArgs) @@ -924,7 +942,8 @@ func TestAddModel(t *testing.T) { user.JimmAdmin = test.jimmAdmin args := jimm.ModelCreateArgs{} - err = args.FromJujuModelCreateArgs(&test.args) + defaults := jimm.ModelCreateDefaults{} + err = args.FromJujuModelCreateArgs(&test.args, defaults) c.Assert(err, qt.IsNil) _, err = j.AddModel(context.Background(), user, &args) @@ -3626,7 +3645,7 @@ controllers: CloudTag: names.NewCloudTag("test-cloud").String(), CloudRegion: "test-region-1", CloudCredentialTag: names.NewCloudCredentialTag("test-cloud/alice@canonical.com/test-credential-1").String(), - }) + }, jimm.ModelCreateDefaults{}) c.Assert(err, qt.IsNil) // According to controller priority for test-region-1, we would diff --git a/internal/jimmtest/jimm_mock.go b/internal/jimmtest/jimm_mock.go index 5eff89a5e..7d64c86c1 100644 --- a/internal/jimmtest/jimm_mock.go +++ b/internal/jimmtest/jimm_mock.go @@ -21,7 +21,6 @@ import ( "github.com/canonical/jimm/v3/internal/openfga" ofganames "github.com/canonical/jimm/v3/internal/openfga/names" "github.com/canonical/jimm/v3/internal/pubsub" - "github.com/canonical/jimm/v3/pkg/api/params" jimmnames "github.com/canonical/jimm/v3/pkg/names" ) @@ -31,32 +30,26 @@ import ( // a NotImplemented error. type JIMM struct { mocks.LoginService + mocks.ModelManager AddAuditLogEntry_ func(ale *dbmodel.AuditLogEntry) AddCloudToController_ func(ctx context.Context, user *openfga.User, controllerName string, tag names.CloudTag, cloud jujuparams.Cloud, force bool) error AddController_ func(ctx context.Context, u *openfga.User, ctl *dbmodel.Controller) error AddGroup_ func(ctx context.Context, user *openfga.User, name string) (*dbmodel.GroupEntry, error) AddHostedCloud_ func(ctx context.Context, user *openfga.User, tag names.CloudTag, cloud jujuparams.Cloud, force bool) error - AddModel_ func(ctx context.Context, u *openfga.User, args *jimm.ModelCreateArgs) (*jujuparams.ModelInfo, error) AddServiceAccount_ func(ctx context.Context, u *openfga.User, clientId string) error Authenticate_ func(ctx context.Context, req *jujuparams.LoginRequest) (*openfga.User, error) AuthorizationClient_ func() *openfga.OFGAClient - ChangeModelCredential_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, cloudCredentialTag names.CloudCredentialTag) error CheckPermission_ func(ctx context.Context, user *openfga.User, cachedPerms map[string]string, desiredPerms map[string]interface{}) (map[string]string, error) CopyServiceAccountCredential_ func(ctx context.Context, u *openfga.User, svcAcc *openfga.User, cloudCredentialTag names.CloudCredentialTag) (names.CloudCredentialTag, []jujuparams.UpdateCredentialModelResult, error) DB_ func() *db.Database - DestroyModel_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, destroyStorage *bool, force *bool, maxWait *time.Duration, timeout *time.Duration) error + DefaultCloud_ func(ctx context.Context, u *openfga.User) (names.CloudTag, error) DestroyOffer_ func(ctx context.Context, user *openfga.User, offerURL string, force bool) error - DumpModel_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, simplified bool) (string, error) - DumpModelDB_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (map[string]interface{}, error) EarliestControllerVersion_ func(ctx context.Context) (version.Number, error) FindApplicationOffers_ func(ctx context.Context, user *openfga.User, filters ...jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) FindAuditEvents_ func(ctx context.Context, user *openfga.User, filter db.AuditLogFilter) ([]dbmodel.AuditLogEntry, error) ForEachCloud_ func(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error - ForEachModel_ func(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error ForEachUserCloud_ func(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error ForEachUserCloudCredential_ func(ctx context.Context, u *dbmodel.Identity, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error - ForEachUserModel_ func(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error - FullModelStatus_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) GetApplicationOffer_ func(ctx context.Context, user *openfga.User, offerURL string) (*jujuparams.ApplicationOfferAdminDetailsV5, error) GetApplicationOfferConsumeDetails_ func(ctx context.Context, user *openfga.User, details *jujuparams.ConsumeOfferDetails, v bakery.Version) error GetCloud_ func(ctx context.Context, u *openfga.User, tag names.CloudTag) (dbmodel.Cloud, error) @@ -73,22 +66,16 @@ type JIMM struct { GrantModelAccess_ func(ctx context.Context, user *openfga.User, mt names.ModelTag, ut names.UserTag, access jujuparams.UserAccessPermission) error GrantOfferAccess_ func(ctx context.Context, u *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) error GrantServiceAccountAccess_ func(ctx context.Context, u *openfga.User, svcAccTag jimmnames.ServiceAccountTag, entities []string) error - ImportModel_ func(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error - IdentityModelDefaults_ func(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) InitiateMigration_ func(ctx context.Context, user *openfga.User, spec jujuparams.MigrationSpec) (jujuparams.InitiateMigrationResult, error) InitiateInternalMigration_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetController string) (jujuparams.InitiateMigrationResult, error) ListApplicationOffers_ func(ctx context.Context, user *openfga.User, filters ...jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) ListControllers_ func(ctx context.Context, user *openfga.User) ([]dbmodel.Controller, error) ListGroups_ func(ctx context.Context, user *openfga.User) ([]dbmodel.GroupEntry, error) - ModelDefaultsForCloud_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) - ModelInfo_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) - ModelStatus_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) Offer_ func(ctx context.Context, user *openfga.User, offer jimm.AddApplicationOfferParams) error OAuthAuthenticationService_ func() jimm.OAuthAuthenticator ParseTag_ func(ctx context.Context, key string) (*ofganames.Tag, error) PubSubHub_ func() *pubsub.Hub PurgeLogs_ func(ctx context.Context, user *openfga.User, before time.Time) (int64, error) - QueryModelsJq_ func(ctx context.Context, models []dbmodel.Model, jqQuery string) (params.CrossModelQueryResponse, error) RemoveCloud_ func(ctx context.Context, u *openfga.User, ct names.CloudTag) error RemoveCloudFromController_ func(ctx context.Context, u *openfga.User, controllerName string, ct names.CloudTag) error RemoveController_ func(ctx context.Context, user *openfga.User, controllerName string, force bool) error @@ -102,17 +89,12 @@ type JIMM struct { RevokeOfferAccess_ func(ctx context.Context, user *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) (err error) SetControllerConfig_ func(ctx context.Context, u *openfga.User, args jujuparams.ControllerConfigSet) error SetControllerDeprecated_ func(ctx context.Context, user *openfga.User, controllerName string, deprecated bool) error - SetModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error SetIdentityModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, configs map[string]interface{}) error ToJAASTag_ func(ctx context.Context, tag *ofganames.Tag, resolveUUIDs bool) (string, error) - UnsetModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error UpdateApplicationOffer_ func(ctx context.Context, controller *dbmodel.Controller, offerUUID string, removed bool) error UpdateCloud_ func(ctx context.Context, u *openfga.User, ct names.CloudTag, cloud jujuparams.Cloud) error UpdateCloudCredential_ func(ctx context.Context, u *openfga.User, args jimm.UpdateCloudCredentialArgs) ([]jujuparams.UpdateCredentialModelResult, error) - UpdateMigratedModel_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error UserLogin_ func(ctx context.Context, identityName string) (*openfga.User, error) - ValidateModelUpgrade_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error - WatchAllModelSummaries_ func(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) } func (j *JIMM) AddAuditLogEntry(ale *dbmodel.AuditLogEntry) { @@ -145,12 +127,6 @@ func (j *JIMM) AddHostedCloud(ctx context.Context, user *openfga.User, tag names } return j.AddHostedCloud_(ctx, user, tag, cloud, force) } -func (j *JIMM) AddModel(ctx context.Context, u *openfga.User, args *jimm.ModelCreateArgs) (_ *jujuparams.ModelInfo, err error) { - if j.AddModel_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.AddModel_(ctx, u, args) -} func (j *JIMM) AddServiceAccount(ctx context.Context, u *openfga.User, clientId string) error { if j.AddServiceAccount_ == nil { @@ -178,12 +154,7 @@ func (j *JIMM) AuthorizationClient() *openfga.OFGAClient { } return j.AuthorizationClient_() } -func (j *JIMM) ChangeModelCredential(ctx context.Context, user *openfga.User, modelTag names.ModelTag, cloudCredentialTag names.CloudCredentialTag) error { - if j.ChangeModelCredential_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.ChangeModelCredential_(ctx, user, modelTag, cloudCredentialTag) -} + func (j *JIMM) CheckPermission(ctx context.Context, user *openfga.User, cachedPerms map[string]string, desiredPerms map[string]interface{}) (map[string]string, error) { if j.CheckPermission_ == nil { return nil, errors.E(errors.CodeNotImplemented) @@ -196,11 +167,11 @@ func (j *JIMM) DB() *db.Database { } return j.DB_() } -func (j *JIMM) DestroyModel(ctx context.Context, u *openfga.User, mt names.ModelTag, destroyStorage *bool, force *bool, maxWait *time.Duration, timeout *time.Duration) error { - if j.DestroyModel_ == nil { - return errors.E(errors.CodeNotImplemented) +func (j *JIMM) DefaultCloud(ctx context.Context, u *openfga.User) (names.CloudTag, error) { + if j.DefaultCloud_ == nil { + return names.CloudTag{}, errors.E(errors.CodeNotImplemented) } - return j.DestroyModel_(ctx, u, mt, destroyStorage, force, maxWait, timeout) + return j.DefaultCloud_(ctx, u) } func (j *JIMM) DestroyOffer(ctx context.Context, user *openfga.User, offerURL string, force bool) error { if j.DestroyOffer_ == nil { @@ -208,18 +179,7 @@ func (j *JIMM) DestroyOffer(ctx context.Context, user *openfga.User, offerURL st } return j.DestroyOffer_(ctx, user, offerURL, force) } -func (j *JIMM) DumpModel(ctx context.Context, u *openfga.User, mt names.ModelTag, simplified bool) (string, error) { - if j.DumpModel_ == nil { - return "", errors.E(errors.CodeNotImplemented) - } - return j.DumpModel_(ctx, u, mt, simplified) -} -func (j *JIMM) DumpModelDB(ctx context.Context, u *openfga.User, mt names.ModelTag) (map[string]interface{}, error) { - if j.DumpModelDB_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.DumpModelDB_(ctx, u, mt) -} + func (j *JIMM) EarliestControllerVersion(ctx context.Context) (version.Number, error) { if j.EarliestControllerVersion_ == nil { return version.Number{}, errors.E(errors.CodeNotImplemented) @@ -244,12 +204,7 @@ func (j *JIMM) ForEachCloud(ctx context.Context, user *openfga.User, f func(*dbm } return j.ForEachCloud_(ctx, user, f) } -func (j *JIMM) ForEachModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error { - if j.ForEachModel_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.ForEachModel_(ctx, u, f) -} + func (j *JIMM) ForEachUserCloud(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error { if j.ForEachUserCloud_ == nil { return errors.E(errors.CodeNotImplemented) @@ -262,18 +217,7 @@ func (j *JIMM) ForEachUserCloudCredential(ctx context.Context, u *dbmodel.Identi } return j.ForEachUserCloudCredential_(ctx, u, ct, f) } -func (j *JIMM) ForEachUserModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error { - if j.ForEachUserModel_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.ForEachUserModel_(ctx, u, f) -} -func (j *JIMM) FullModelStatus(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) { - if j.FullModelStatus_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.FullModelStatus_(ctx, user, modelTag, patterns) -} + func (j *JIMM) GetApplicationOffer(ctx context.Context, user *openfga.User, offerURL string) (*jujuparams.ApplicationOfferAdminDetailsV5, error) { if j.GetApplicationOffer_ == nil { return nil, errors.E(errors.CodeNotImplemented) @@ -372,12 +316,6 @@ func (j *JIMM) GrantServiceAccountAccess(ctx context.Context, u *openfga.User, s return j.GrantServiceAccountAccess_(ctx, u, svcAccTag, entities) } -func (j *JIMM) ImportModel(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error { - if j.ImportModel_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.ImportModel_(ctx, user, controllerName, modelTag, newOwner) -} func (j *JIMM) InitiateMigration(ctx context.Context, user *openfga.User, spec jujuparams.MigrationSpec) (jujuparams.InitiateMigrationResult, error) { if j.InitiateMigration_ == nil { return jujuparams.InitiateMigrationResult{}, errors.E(errors.CodeNotImplemented) @@ -408,24 +346,7 @@ func (j *JIMM) ListGroups(ctx context.Context, user *openfga.User) ([]dbmodel.Gr } return j.ListGroups_(ctx, user) } -func (j *JIMM) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) { - if j.ModelDefaultsForCloud_ == nil { - return jujuparams.ModelDefaultsResult{}, errors.E(errors.CodeNotImplemented) - } - return j.ModelDefaultsForCloud_(ctx, user, cloudTag) -} -func (j *JIMM) ModelInfo(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) { - if j.ModelInfo_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.ModelInfo_(ctx, u, mt) -} -func (j *JIMM) ModelStatus(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) { - if j.ModelStatus_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.ModelStatus_(ctx, u, mt) -} + func (j *JIMM) Offer(ctx context.Context, user *openfga.User, offer jimm.AddApplicationOfferParams) error { if j.Offer_ == nil { return errors.E(errors.CodeNotImplemented) @@ -456,12 +377,7 @@ func (j *JIMM) PurgeLogs(ctx context.Context, user *openfga.User, before time.Ti } return j.PurgeLogs_(ctx, user, before) } -func (j *JIMM) QueryModelsJq(ctx context.Context, models []dbmodel.Model, jqQuery string) (params.CrossModelQueryResponse, error) { - if j.QueryModelsJq_ == nil { - return params.CrossModelQueryResponse{}, errors.E(errors.CodeNotImplemented) - } - return j.QueryModelsJq_(ctx, models, jqQuery) -} + func (j *JIMM) RemoveCloud(ctx context.Context, u *openfga.User, ct names.CloudTag) error { if j.RemoveCloud_ == nil { return errors.E(errors.CodeNotImplemented) @@ -540,12 +456,7 @@ func (j *JIMM) SetControllerDeprecated(ctx context.Context, user *openfga.User, } return j.SetControllerDeprecated_(ctx, user, controllerName, deprecated) } -func (j *JIMM) SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error { - if j.SetModelDefaults_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.SetModelDefaults_(ctx, user, cloudTag, region, configs) -} + func (j *JIMM) SetIdentityModelDefaults(ctx context.Context, user *dbmodel.Identity, configs map[string]interface{}) error { if j.SetIdentityModelDefaults_ == nil { return errors.E(errors.CodeNotImplemented) @@ -558,12 +469,7 @@ func (j *JIMM) ToJAASTag(ctx context.Context, tag *ofganames.Tag, resolveUUIDs b } return j.ToJAASTag_(ctx, tag, resolveUUIDs) } -func (j *JIMM) UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error { - if j.UnsetModelDefaults_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.UnsetModelDefaults_(ctx, user, cloudTag, region, keys) -} + func (j *JIMM) UpdateApplicationOffer(ctx context.Context, controller *dbmodel.Controller, offerUUID string, removed bool) error { if j.UpdateApplicationOffer_ == nil { return errors.E(errors.CodeNotImplemented) @@ -582,33 +488,10 @@ func (j *JIMM) UpdateCloudCredential(ctx context.Context, u *openfga.User, args } return j.UpdateCloudCredential_(ctx, u, args) } -func (j *JIMM) UpdateMigratedModel(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error { - if j.UpdateMigratedModel_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.UpdateMigratedModel_(ctx, user, modelTag, targetControllerName) -} + func (j *JIMM) UserLogin(ctx context.Context, identityName string) (*openfga.User, error) { if j.UserLogin_ == nil { return nil, errors.E(errors.CodeNotImplemented) } return j.UserLogin_(ctx, identityName) } -func (j *JIMM) IdentityModelDefaults(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) { - if j.IdentityModelDefaults_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.IdentityModelDefaults_(ctx, user) -} -func (j *JIMM) ValidateModelUpgrade(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error { - if j.ValidateModelUpgrade_ == nil { - return errors.E(errors.CodeNotImplemented) - } - return j.ValidateModelUpgrade_(ctx, u, mt, force) -} -func (j *JIMM) WatchAllModelSummaries(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) { - if j.WatchAllModelSummaries_ == nil { - return nil, errors.E(errors.CodeNotImplemented) - } - return j.WatchAllModelSummaries_(ctx, controller) -} diff --git a/internal/jimmtest/mocks/model.go b/internal/jimmtest/mocks/model.go new file mode 100644 index 000000000..ae070047c --- /dev/null +++ b/internal/jimmtest/mocks/model.go @@ -0,0 +1,166 @@ +// Copyright 2024 Canonical. +package mocks + +import ( + "context" + "time" + + jujuparams "github.com/juju/juju/rpc/params" + "github.com/juju/names/v5" + + "github.com/canonical/jimm/v3/internal/dbmodel" + "github.com/canonical/jimm/v3/internal/errors" + "github.com/canonical/jimm/v3/internal/jimm" + "github.com/canonical/jimm/v3/internal/openfga" + "github.com/canonical/jimm/v3/pkg/api/params" +) + +type ModelManager struct { + AddModel_ func(ctx context.Context, u *openfga.User, args *jimm.ModelCreateArgs) (*jujuparams.ModelInfo, error) + ChangeModelCredential_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, cloudCredentialTag names.CloudCredentialTag) error + DestroyModel_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, destroyStorage *bool, force *bool, maxWait *time.Duration, timeout *time.Duration) error + DumpModel_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, simplified bool) (string, error) + DumpModelDB_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (map[string]interface{}, error) + ForEachModel_ func(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error + ForEachUserModel_ func(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error + FullModelStatus_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) + ImportModel_ func(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error + IdentityModelDefaults_ func(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) + ModelDefaultsForCloud_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) + ModelInfo_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) + ModelStatus_ func(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) + QueryModelsJq_ func(ctx context.Context, models []dbmodel.Model, jqQuery string) (params.CrossModelQueryResponse, error) + SetModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error + UnsetModelDefaults_ func(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error + UpdateMigratedModel_ func(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error + ValidateModelUpgrade_ func(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error + WatchAllModelSummaries_ func(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) +} + +func (j *ModelManager) AddModel(ctx context.Context, u *openfga.User, args *jimm.ModelCreateArgs) (_ *jujuparams.ModelInfo, err error) { + if j.AddModel_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.AddModel_(ctx, u, args) +} + +func (j *ModelManager) ChangeModelCredential(ctx context.Context, user *openfga.User, modelTag names.ModelTag, cloudCredentialTag names.CloudCredentialTag) error { + if j.ChangeModelCredential_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.ChangeModelCredential_(ctx, user, modelTag, cloudCredentialTag) +} + +func (j *ModelManager) DestroyModel(ctx context.Context, u *openfga.User, mt names.ModelTag, destroyStorage *bool, force *bool, maxWait *time.Duration, timeout *time.Duration) error { + if j.DestroyModel_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.DestroyModel_(ctx, u, mt, destroyStorage, force, maxWait, timeout) +} + +func (j *ModelManager) DumpModel(ctx context.Context, u *openfga.User, mt names.ModelTag, simplified bool) (string, error) { + if j.DumpModel_ == nil { + return "", errors.E(errors.CodeNotImplemented) + } + return j.DumpModel_(ctx, u, mt, simplified) +} +func (j *ModelManager) DumpModelDB(ctx context.Context, u *openfga.User, mt names.ModelTag) (map[string]interface{}, error) { + if j.DumpModelDB_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.DumpModelDB_(ctx, u, mt) +} + +func (j *ModelManager) ForEachModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error { + if j.ForEachModel_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.ForEachModel_(ctx, u, f) +} + +func (j *ModelManager) ForEachUserModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error { + if j.ForEachUserModel_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.ForEachUserModel_(ctx, u, f) +} + +func (j *ModelManager) FullModelStatus(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) { + if j.FullModelStatus_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.FullModelStatus_(ctx, user, modelTag, patterns) +} + +func (j *ModelManager) ImportModel(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error { + if j.ImportModel_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.ImportModel_(ctx, user, controllerName, modelTag, newOwner) +} + +func (j *ModelManager) ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) { + if j.ModelDefaultsForCloud_ == nil { + return jujuparams.ModelDefaultsResult{}, errors.E(errors.CodeNotImplemented) + } + return j.ModelDefaultsForCloud_(ctx, user, cloudTag) +} + +func (j *ModelManager) ModelInfo(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) { + if j.ModelInfo_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.ModelInfo_(ctx, u, mt) +} +func (j *ModelManager) ModelStatus(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) { + if j.ModelStatus_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.ModelStatus_(ctx, u, mt) +} + +func (j *ModelManager) QueryModelsJq(ctx context.Context, models []dbmodel.Model, jqQuery string) (params.CrossModelQueryResponse, error) { + if j.QueryModelsJq_ == nil { + return params.CrossModelQueryResponse{}, errors.E(errors.CodeNotImplemented) + } + return j.QueryModelsJq_(ctx, models, jqQuery) +} + +func (j *ModelManager) SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error { + if j.SetModelDefaults_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.SetModelDefaults_(ctx, user, cloudTag, region, configs) +} + +func (j *ModelManager) UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error { + if j.UnsetModelDefaults_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.UnsetModelDefaults_(ctx, user, cloudTag, region, keys) +} + +func (j *ModelManager) UpdateMigratedModel(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error { + if j.UpdateMigratedModel_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.UpdateMigratedModel_(ctx, user, modelTag, targetControllerName) +} +func (j *ModelManager) IdentityModelDefaults(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) { + if j.IdentityModelDefaults_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.IdentityModelDefaults_(ctx, user) +} +func (j *ModelManager) ValidateModelUpgrade(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error { + if j.ValidateModelUpgrade_ == nil { + return errors.E(errors.CodeNotImplemented) + } + return j.ValidateModelUpgrade_(ctx, u, mt, force) +} +func (j *ModelManager) WatchAllModelSummaries(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) { + if j.WatchAllModelSummaries_ == nil { + return nil, errors.E(errors.CodeNotImplemented) + } + return j.WatchAllModelSummaries_(ctx, controller) +} diff --git a/internal/jujuapi/controllerroot.go b/internal/jujuapi/controllerroot.go index f0e620d1e..c9b66dc01 100644 --- a/internal/jujuapi/controllerroot.go +++ b/internal/jujuapi/controllerroot.go @@ -24,36 +24,29 @@ import ( "github.com/canonical/jimm/v3/internal/openfga" ofganames "github.com/canonical/jimm/v3/internal/openfga/names" "github.com/canonical/jimm/v3/internal/pubsub" - "github.com/canonical/jimm/v3/pkg/api/params" jimmnames "github.com/canonical/jimm/v3/pkg/names" ) type JIMM interface { LoginService + ModelManager AddAuditLogEntry(ale *dbmodel.AuditLogEntry) AddCloudToController(ctx context.Context, user *openfga.User, controllerName string, tag names.CloudTag, cloud jujuparams.Cloud, force bool) error AddController(ctx context.Context, u *openfga.User, ctl *dbmodel.Controller) error AddHostedCloud(ctx context.Context, user *openfga.User, tag names.CloudTag, cloud jujuparams.Cloud, force bool) error AddGroup(ctx context.Context, user *openfga.User, name string) (*dbmodel.GroupEntry, error) - AddModel(ctx context.Context, u *openfga.User, args *jimm.ModelCreateArgs) (_ *jujuparams.ModelInfo, err error) AddServiceAccount(ctx context.Context, u *openfga.User, clientId string) error AuthorizationClient() *openfga.OFGAClient - ChangeModelCredential(ctx context.Context, user *openfga.User, modelTag names.ModelTag, cloudCredentialTag names.CloudCredentialTag) error CopyServiceAccountCredential(ctx context.Context, u *openfga.User, svcAcc *openfga.User, cloudCredentialTag names.CloudCredentialTag) (names.CloudCredentialTag, []jujuparams.UpdateCredentialModelResult, error) DB() *db.Database - DestroyModel(ctx context.Context, u *openfga.User, mt names.ModelTag, destroyStorage *bool, force *bool, maxWait *time.Duration, timeout *time.Duration) error + DefaultCloud(ctx context.Context, u *openfga.User) (names.CloudTag, error) DestroyOffer(ctx context.Context, user *openfga.User, offerURL string, force bool) error - DumpModel(ctx context.Context, u *openfga.User, mt names.ModelTag, simplified bool) (string, error) - DumpModelDB(ctx context.Context, u *openfga.User, mt names.ModelTag) (map[string]interface{}, error) EarliestControllerVersion(ctx context.Context) (version.Number, error) FindApplicationOffers(ctx context.Context, user *openfga.User, filters ...jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) FindAuditEvents(ctx context.Context, user *openfga.User, filter db.AuditLogFilter) ([]dbmodel.AuditLogEntry, error) ForEachCloud(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error - ForEachModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error ForEachUserCloud(ctx context.Context, user *openfga.User, f func(*dbmodel.Cloud) error) error ForEachUserCloudCredential(ctx context.Context, u *dbmodel.Identity, ct names.CloudTag, f func(cred *dbmodel.CloudCredential) error) error - ForEachUserModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error - FullModelStatus(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) GetApplicationOffer(ctx context.Context, user *openfga.User, offerURL string) (*jujuparams.ApplicationOfferAdminDetailsV5, error) GetApplicationOfferConsumeDetails(ctx context.Context, user *openfga.User, details *jujuparams.ConsumeOfferDetails, v bakery.Version) error GetCloud(ctx context.Context, u *openfga.User, tag names.CloudTag) (dbmodel.Cloud, error) @@ -70,20 +63,14 @@ type JIMM interface { GrantModelAccess(ctx context.Context, user *openfga.User, mt names.ModelTag, ut names.UserTag, access jujuparams.UserAccessPermission) error GrantOfferAccess(ctx context.Context, u *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) error GrantServiceAccountAccess(ctx context.Context, u *openfga.User, svcAccTag jimmnames.ServiceAccountTag, tags []string) error - IdentityModelDefaults(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) - ImportModel(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error InitiateInternalMigration(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetController string) (jujuparams.InitiateMigrationResult, error) InitiateMigration(ctx context.Context, user *openfga.User, spec jujuparams.MigrationSpec) (jujuparams.InitiateMigrationResult, error) ListApplicationOffers(ctx context.Context, user *openfga.User, filters ...jujuparams.OfferFilter) ([]jujuparams.ApplicationOfferAdminDetailsV5, error) ListGroups(ctx context.Context, user *openfga.User) ([]dbmodel.GroupEntry, error) - ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) - ModelInfo(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) - ModelStatus(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) Offer(ctx context.Context, user *openfga.User, offer jimm.AddApplicationOfferParams) error ParseTag(ctx context.Context, key string) (*ofganames.Tag, error) PubSubHub() *pubsub.Hub PurgeLogs(ctx context.Context, user *openfga.User, before time.Time) (int64, error) - QueryModelsJq(ctx context.Context, models []dbmodel.Model, jqQuery string) (params.CrossModelQueryResponse, error) RenameGroup(ctx context.Context, user *openfga.User, oldName, newName string) error RemoveCloud(ctx context.Context, u *openfga.User, ct names.CloudTag) error RemoveCloudFromController(ctx context.Context, u *openfga.User, controllerName string, ct names.CloudTag) error @@ -97,16 +84,11 @@ type JIMM interface { RevokeOfferAccess(ctx context.Context, user *openfga.User, offerURL string, ut names.UserTag, access jujuparams.OfferAccessPermission) (err error) SetControllerConfig(ctx context.Context, u *openfga.User, args jujuparams.ControllerConfigSet) error SetControllerDeprecated(ctx context.Context, user *openfga.User, controllerName string, deprecated bool) error - SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error ToJAASTag(ctx context.Context, tag *ofganames.Tag, resolveUUIDs bool) (string, error) - UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error UpdateApplicationOffer(ctx context.Context, controller *dbmodel.Controller, offerUUID string, removed bool) error UpdateCloud(ctx context.Context, u *openfga.User, ct names.CloudTag, cloud jujuparams.Cloud) error UpdateCloudCredential(ctx context.Context, u *openfga.User, args jimm.UpdateCloudCredentialArgs) ([]jujuparams.UpdateCredentialModelResult, error) - UpdateMigratedModel(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error UserLogin(ctx context.Context, identityName string) (*openfga.User, error) - ValidateModelUpgrade(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error - WatchAllModelSummaries(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) } // controllerRoot is the root for endpoints served on controller connections. diff --git a/internal/jujuapi/modelmanager.go b/internal/jujuapi/modelmanager.go index a79d19790..8e7278fe0 100644 --- a/internal/jujuapi/modelmanager.go +++ b/internal/jujuapi/modelmanager.go @@ -5,6 +5,7 @@ package jujuapi import ( "context" "fmt" + "time" jujuparams "github.com/juju/juju/rpc/params" "github.com/juju/names/v5" @@ -13,7 +14,9 @@ import ( "github.com/canonical/jimm/v3/internal/errors" "github.com/canonical/jimm/v3/internal/jimm" "github.com/canonical/jimm/v3/internal/jujuapi/rpc" + "github.com/canonical/jimm/v3/internal/openfga" "github.com/canonical/jimm/v3/internal/servermon" + "github.com/canonical/jimm/v3/pkg/api/params" ) func init() { @@ -52,6 +55,28 @@ func init() { } } +type ModelManager interface { + AddModel(ctx context.Context, u *openfga.User, args *jimm.ModelCreateArgs) (_ *jujuparams.ModelInfo, err error) + ChangeModelCredential(ctx context.Context, user *openfga.User, modelTag names.ModelTag, cloudCredentialTag names.CloudCredentialTag) error + DestroyModel(ctx context.Context, u *openfga.User, mt names.ModelTag, destroyStorage *bool, force *bool, maxWait *time.Duration, timeout *time.Duration) error + DumpModel(ctx context.Context, u *openfga.User, mt names.ModelTag, simplified bool) (string, error) + DumpModelDB(ctx context.Context, u *openfga.User, mt names.ModelTag) (map[string]interface{}, error) + ForEachModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error + ForEachUserModel(ctx context.Context, u *openfga.User, f func(*dbmodel.Model, jujuparams.UserAccessPermission) error) error + FullModelStatus(ctx context.Context, user *openfga.User, modelTag names.ModelTag, patterns []string) (*jujuparams.FullStatus, error) + IdentityModelDefaults(ctx context.Context, user *dbmodel.Identity) (map[string]interface{}, error) + ImportModel(ctx context.Context, user *openfga.User, controllerName string, modelTag names.ModelTag, newOwner string) error + ModelDefaultsForCloud(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag) (jujuparams.ModelDefaultsResult, error) + ModelInfo(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelInfo, error) + ModelStatus(ctx context.Context, u *openfga.User, mt names.ModelTag) (*jujuparams.ModelStatus, error) + QueryModelsJq(ctx context.Context, models []dbmodel.Model, jqQuery string) (params.CrossModelQueryResponse, error) + SetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, configs map[string]interface{}) error + UnsetModelDefaults(ctx context.Context, user *dbmodel.Identity, cloudTag names.CloudTag, region string, keys []string) error + UpdateMigratedModel(ctx context.Context, user *openfga.User, modelTag names.ModelTag, targetControllerName string) error + ValidateModelUpgrade(ctx context.Context, u *openfga.User, mt names.ModelTag, force bool) error + WatchAllModelSummaries(ctx context.Context, controller *dbmodel.Controller) (_ func() error, err error) +} + // DumpModels implements the DumpModels method of the modelmanager (version // 3 onwards) facade. The model dump is passed back as-is from the // controller without any changes from JIMM. @@ -147,7 +172,10 @@ func (r *controllerRoot) CreateModel(ctx context.Context, args jujuparams.ModelC defer cancel() var mca jimm.ModelCreateArgs - if err := mca.FromJujuModelCreateArgs(&args); err != nil { + mcd := jimm.ModelCreateDefaults{ + DefaultCloud: func() (names.CloudTag, error) { return r.jimm.DefaultCloud(ctx, r.user) }, + } + if err := mca.FromJujuModelCreateArgs(&args, mcd); err != nil { return jujuparams.ModelInfo{}, errors.E(op, err) } info, err := r.jimm.AddModel(ctx, r.user, &mca) From cc30b07b7cb651a2fa336cdd3163e74ba36251c1 Mon Sep 17 00:00:00 2001 From: Kian Parvin Date: Mon, 26 Aug 2024 12:36:01 +0200 Subject: [PATCH 2/2] update api test --- internal/jujuapi/modelmanager_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/internal/jujuapi/modelmanager_test.go b/internal/jujuapi/modelmanager_test.go index 4e2c915dd..6092b3f25 100644 --- a/internal/jujuapi/modelmanager_test.go +++ b/internal/jujuapi/modelmanager_test.go @@ -804,6 +804,12 @@ var createModelTests = []struct { ownerTag: names.NewUserTag("bob@canonical.com").String(), cloudTag: names.NewCloudTag(jimmtest.TestCloudName).String(), credentialTag: "cloudcred-" + jimmtest.TestCloudName + "_bob@canonical.com_cred", +}, { + about: "no cloud tag uses default cloud", + name: "model-0", + ownerTag: names.NewUserTag("bob@canonical.com").String(), + cloudTag: "", + credentialTag: "cloudcred-" + jimmtest.TestCloudName + "_bob@canonical.com_cred", }, { about: "unauthorized user", name: "model-2", @@ -860,13 +866,6 @@ var createModelTests = []struct { cloudTag: "not-a-cloud-tag", credentialTag: "cloudcred-" + jimmtest.TestCloudName + "_bob@canonical.com_cred1", expectError: `"not-a-cloud-tag" is not a valid tag \(bad request\)`, -}, { - about: "no cloud tag", - name: "model-8", - ownerTag: names.NewUserTag("bob@canonical.com").String(), - cloudTag: "", - credentialTag: "cloudcred-" + jimmtest.TestCloudName + "_bob@canonical.com_cred1", - expectError: `no cloud specified for model; please specify one`, }, { about: "no credential tag selects unambigous creds", name: "model-8",