diff --git a/database/common/common.go b/database/common/common.go index ab546844..023a2057 100644 --- a/database/common/common.go +++ b/database/common/common.go @@ -27,14 +27,6 @@ type RepoStore interface { ListRepositories(ctx context.Context) ([]params.Repository, error) DeleteRepository(ctx context.Context, repoID string) error UpdateRepository(ctx context.Context, repoID string, param params.UpdateEntityParams) (params.Repository, error) - - // CreateRepositoryPool(ctx context.Context, repoID string, param params.CreatePoolParams) (params.Pool, error) - // GetRepositoryPool(ctx context.Context, repoID, poolID string) (params.Pool, error) - // DeleteRepositoryPool(ctx context.Context, repoID, poolID string) error - UpdateRepositoryPool(ctx context.Context, repoID, poolID string, param params.UpdatePoolParams) (params.Pool, error) - - ListRepoPools(ctx context.Context, repoID string) ([]params.Pool, error) - ListRepoInstances(ctx context.Context, repoID string) ([]params.Instance, error) } type OrgStore interface { @@ -44,14 +36,6 @@ type OrgStore interface { ListOrganizations(ctx context.Context) ([]params.Organization, error) DeleteOrganization(ctx context.Context, orgID string) error UpdateOrganization(ctx context.Context, orgID string, param params.UpdateEntityParams) (params.Organization, error) - - // CreateOrganizationPool(ctx context.Context, orgID string, param params.CreatePoolParams) (params.Pool, error) - // GetOrganizationPool(ctx context.Context, orgID, poolID string) (params.Pool, error) - // DeleteOrganizationPool(ctx context.Context, orgID, poolID string) error - UpdateOrganizationPool(ctx context.Context, orgID, poolID string, param params.UpdatePoolParams) (params.Pool, error) - - ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, error) - ListOrgInstances(ctx context.Context, orgID string) ([]params.Instance, error) } type EnterpriseStore interface { @@ -61,14 +45,6 @@ type EnterpriseStore interface { ListEnterprises(ctx context.Context) ([]params.Enterprise, error) DeleteEnterprise(ctx context.Context, enterpriseID string) error UpdateEnterprise(ctx context.Context, enterpriseID string, param params.UpdateEntityParams) (params.Enterprise, error) - - // CreateEnterprisePool(ctx context.Context, enterpriseID string, param params.CreatePoolParams) (params.Pool, error) - // GetEnterprisePool(ctx context.Context, enterpriseID, poolID string) (params.Pool, error) - // DeleteEnterprisePool(ctx context.Context, enterpriseID, poolID string) error - UpdateEnterprisePool(ctx context.Context, enterpriseID, poolID string, param params.UpdatePoolParams) (params.Pool, error) - - ListEnterprisePools(ctx context.Context, enterpriseID string) ([]params.Pool, error) - ListEnterpriseInstances(ctx context.Context, enterpriseID string) ([]params.Instance, error) } type PoolStore interface { diff --git a/database/common/mocks/Store.go b/database/common/mocks/Store.go index 73eef2c3..219057e4 100644 --- a/database/common/mocks/Store.go +++ b/database/common/mocks/Store.go @@ -310,24 +310,6 @@ func (_m *Store) DeleteEnterprise(ctx context.Context, enterpriseID string) erro return r0 } -// DeleteEnterprisePool provides a mock function with given fields: ctx, enterpriseID, poolID -func (_m *Store) DeleteEnterprisePool(ctx context.Context, enterpriseID string, poolID string) error { - ret := _m.Called(ctx, enterpriseID, poolID) - - if len(ret) == 0 { - panic("no return value specified for DeleteEnterprisePool") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, enterpriseID, poolID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // DeleteEntityPool provides a mock function with given fields: ctx, entity, poolID func (_m *Store) DeleteEntityPool(ctx context.Context, entity params.GithubEntity, poolID string) error { ret := _m.Called(ctx, entity, poolID) @@ -400,24 +382,6 @@ func (_m *Store) DeleteOrganization(ctx context.Context, orgID string) error { return r0 } -// DeleteOrganizationPool provides a mock function with given fields: ctx, orgID, poolID -func (_m *Store) DeleteOrganizationPool(ctx context.Context, orgID string, poolID string) error { - ret := _m.Called(ctx, orgID, poolID) - - if len(ret) == 0 { - panic("no return value specified for DeleteOrganizationPool") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, orgID, poolID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // DeletePoolByID provides a mock function with given fields: ctx, poolID func (_m *Store) DeletePoolByID(ctx context.Context, poolID string) error { ret := _m.Called(ctx, poolID) @@ -454,24 +418,6 @@ func (_m *Store) DeleteRepository(ctx context.Context, repoID string) error { return r0 } -// DeleteRepositoryPool provides a mock function with given fields: ctx, repoID, poolID -func (_m *Store) DeleteRepositoryPool(ctx context.Context, repoID string, poolID string) error { - ret := _m.Called(ctx, repoID, poolID) - - if len(ret) == 0 { - panic("no return value specified for DeleteRepositoryPool") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, repoID, poolID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - // FindPoolsMatchingAllTags provides a mock function with given fields: ctx, entityType, entityID, tags func (_m *Store) FindPoolsMatchingAllTags(ctx context.Context, entityType params.GithubEntityType, entityID string, tags []string) ([]params.Pool, error) { ret := _m.Called(ctx, entityType, entityID, tags) @@ -1002,66 +948,6 @@ func (_m *Store) ListAllPools(ctx context.Context) ([]params.Pool, error) { return r0, r1 } -// ListEnterpriseInstances provides a mock function with given fields: ctx, enterpriseID -func (_m *Store) ListEnterpriseInstances(ctx context.Context, enterpriseID string) ([]params.Instance, error) { - ret := _m.Called(ctx, enterpriseID) - - if len(ret) == 0 { - panic("no return value specified for ListEnterpriseInstances") - } - - var r0 []params.Instance - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]params.Instance, error)); ok { - return rf(ctx, enterpriseID) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []params.Instance); ok { - r0 = rf(ctx, enterpriseID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.Instance) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, enterpriseID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListEnterprisePools provides a mock function with given fields: ctx, enterpriseID -func (_m *Store) ListEnterprisePools(ctx context.Context, enterpriseID string) ([]params.Pool, error) { - ret := _m.Called(ctx, enterpriseID) - - if len(ret) == 0 { - panic("no return value specified for ListEnterprisePools") - } - - var r0 []params.Pool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]params.Pool, error)); ok { - return rf(ctx, enterpriseID) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []params.Pool); ok { - r0 = rf(ctx, enterpriseID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.Pool) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, enterpriseID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ListEnterprises provides a mock function with given fields: ctx func (_m *Store) ListEnterprises(ctx context.Context) ([]params.Enterprise, error) { ret := _m.Called(ctx) @@ -1242,66 +1128,6 @@ func (_m *Store) ListJobsByStatus(ctx context.Context, status params.JobStatus) return r0, r1 } -// ListOrgInstances provides a mock function with given fields: ctx, orgID -func (_m *Store) ListOrgInstances(ctx context.Context, orgID string) ([]params.Instance, error) { - ret := _m.Called(ctx, orgID) - - if len(ret) == 0 { - panic("no return value specified for ListOrgInstances") - } - - var r0 []params.Instance - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]params.Instance, error)); ok { - return rf(ctx, orgID) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []params.Instance); ok { - r0 = rf(ctx, orgID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.Instance) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, orgID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListOrgPools provides a mock function with given fields: ctx, orgID -func (_m *Store) ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, error) { - ret := _m.Called(ctx, orgID) - - if len(ret) == 0 { - panic("no return value specified for ListOrgPools") - } - - var r0 []params.Pool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]params.Pool, error)); ok { - return rf(ctx, orgID) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []params.Pool); ok { - r0 = rf(ctx, orgID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.Pool) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, orgID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ListOrganizations provides a mock function with given fields: ctx func (_m *Store) ListOrganizations(ctx context.Context) ([]params.Organization, error) { ret := _m.Called(ctx) @@ -1362,66 +1188,6 @@ func (_m *Store) ListPoolInstances(ctx context.Context, poolID string) ([]params return r0, r1 } -// ListRepoInstances provides a mock function with given fields: ctx, repoID -func (_m *Store) ListRepoInstances(ctx context.Context, repoID string) ([]params.Instance, error) { - ret := _m.Called(ctx, repoID) - - if len(ret) == 0 { - panic("no return value specified for ListRepoInstances") - } - - var r0 []params.Instance - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]params.Instance, error)); ok { - return rf(ctx, repoID) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []params.Instance); ok { - r0 = rf(ctx, repoID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.Instance) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, repoID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ListRepoPools provides a mock function with given fields: ctx, repoID -func (_m *Store) ListRepoPools(ctx context.Context, repoID string) ([]params.Pool, error) { - ret := _m.Called(ctx, repoID) - - if len(ret) == 0 { - panic("no return value specified for ListRepoPools") - } - - var r0 []params.Pool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]params.Pool, error)); ok { - return rf(ctx, repoID) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []params.Pool); ok { - r0 = rf(ctx, repoID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]params.Pool) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, repoID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // ListRepositories provides a mock function with given fields: ctx func (_m *Store) ListRepositories(ctx context.Context) ([]params.Repository, error) { ret := _m.Called(ctx) @@ -1544,34 +1310,6 @@ func (_m *Store) UpdateEnterprise(ctx context.Context, enterpriseID string, para return r0, r1 } -// UpdateEnterprisePool provides a mock function with given fields: ctx, enterpriseID, poolID, param -func (_m *Store) UpdateEnterprisePool(ctx context.Context, enterpriseID string, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - ret := _m.Called(ctx, enterpriseID, poolID, param) - - if len(ret) == 0 { - panic("no return value specified for UpdateEnterprisePool") - } - - var r0 params.Pool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, params.UpdatePoolParams) (params.Pool, error)); ok { - return rf(ctx, enterpriseID, poolID, param) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, params.UpdatePoolParams) params.Pool); ok { - r0 = rf(ctx, enterpriseID, poolID, param) - } else { - r0 = ret.Get(0).(params.Pool) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, params.UpdatePoolParams) error); ok { - r1 = rf(ctx, enterpriseID, poolID, param) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // UpdateEntityPool provides a mock function with given fields: ctx, entity, poolID, param func (_m *Store) UpdateEntityPool(ctx context.Context, entity params.GithubEntity, poolID string, param params.UpdatePoolParams) (params.Pool, error) { ret := _m.Called(ctx, entity, poolID, param) @@ -1656,34 +1394,6 @@ func (_m *Store) UpdateOrganization(ctx context.Context, orgID string, param par return r0, r1 } -// UpdateOrganizationPool provides a mock function with given fields: ctx, orgID, poolID, param -func (_m *Store) UpdateOrganizationPool(ctx context.Context, orgID string, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - ret := _m.Called(ctx, orgID, poolID, param) - - if len(ret) == 0 { - panic("no return value specified for UpdateOrganizationPool") - } - - var r0 params.Pool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, params.UpdatePoolParams) (params.Pool, error)); ok { - return rf(ctx, orgID, poolID, param) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, params.UpdatePoolParams) params.Pool); ok { - r0 = rf(ctx, orgID, poolID, param) - } else { - r0 = ret.Get(0).(params.Pool) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, params.UpdatePoolParams) error); ok { - r1 = rf(ctx, orgID, poolID, param) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // UpdateRepository provides a mock function with given fields: ctx, repoID, param func (_m *Store) UpdateRepository(ctx context.Context, repoID string, param params.UpdateEntityParams) (params.Repository, error) { ret := _m.Called(ctx, repoID, param) @@ -1712,34 +1422,6 @@ func (_m *Store) UpdateRepository(ctx context.Context, repoID string, param para return r0, r1 } -// UpdateRepositoryPool provides a mock function with given fields: ctx, repoID, poolID, param -func (_m *Store) UpdateRepositoryPool(ctx context.Context, repoID string, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - ret := _m.Called(ctx, repoID, poolID, param) - - if len(ret) == 0 { - panic("no return value specified for UpdateRepositoryPool") - } - - var r0 params.Pool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, params.UpdatePoolParams) (params.Pool, error)); ok { - return rf(ctx, repoID, poolID, param) - } - if rf, ok := ret.Get(0).(func(context.Context, string, string, params.UpdatePoolParams) params.Pool); ok { - r0 = rf(ctx, repoID, poolID, param) - } else { - r0 = ret.Get(0).(params.Pool) - } - - if rf, ok := ret.Get(1).(func(context.Context, string, string, params.UpdatePoolParams) error); ok { - r1 = rf(ctx, repoID, poolID, param) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // UpdateUser provides a mock function with given fields: ctx, user, param func (_m *Store) UpdateUser(ctx context.Context, user string, param params.UpdateUserParams) (params.User, error) { ret := _m.Called(ctx, user, param) diff --git a/database/sql/enterprise.go b/database/sql/enterprise.go index c3bf5d69..3eb53b9e 100644 --- a/database/sql/enterprise.go +++ b/database/sql/enterprise.go @@ -5,7 +5,6 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "gorm.io/datatypes" "gorm.io/gorm" runnerErrors "github.com/cloudbase/garm-provider-common/errors" @@ -134,137 +133,6 @@ func (s *sqlDatabase) UpdateEnterprise(ctx context.Context, enterpriseID string, return newParams, nil } -func (s *sqlDatabase) CreateEnterprisePool(ctx context.Context, enterpriseID string, param params.CreatePoolParams) (params.Pool, error) { - if len(param.Tags) == 0 { - return params.Pool{}, runnerErrors.NewBadRequestError("no tags specified") - } - - enterprise, err := s.getEnterpriseByID(ctx, enterpriseID) - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching enterprise") - } - - newPool := Pool{ - ProviderName: param.ProviderName, - MaxRunners: param.MaxRunners, - MinIdleRunners: param.MinIdleRunners, - RunnerPrefix: param.GetRunnerPrefix(), - Image: param.Image, - Flavor: param.Flavor, - OSType: param.OSType, - OSArch: param.OSArch, - EnterpriseID: &enterprise.ID, - Enabled: param.Enabled, - RunnerBootstrapTimeout: param.RunnerBootstrapTimeout, - GitHubRunnerGroup: param.GitHubRunnerGroup, - Priority: param.Priority, - } - - if len(param.ExtraSpecs) > 0 { - newPool.ExtraSpecs = datatypes.JSON(param.ExtraSpecs) - } - - _, err = s.getEnterprisePoolByUniqueFields(ctx, enterpriseID, newPool.ProviderName, newPool.Image, newPool.Flavor) - if err != nil { - if !errors.Is(err, runnerErrors.ErrNotFound) { - return params.Pool{}, errors.Wrap(err, "creating pool") - } - } else { - return params.Pool{}, runnerErrors.NewConflictError("pool with the same image and flavor already exists on this provider") - } - - tags := []Tag{} - for _, val := range param.Tags { - t, err := s.getOrCreateTag(s.conn, val) - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching tag") - } - tags = append(tags, t) - } - - q := s.conn.Create(&newPool) - if q.Error != nil { - return params.Pool{}, errors.Wrap(q.Error, "adding pool") - } - - for i := range tags { - if err := s.conn.Model(&newPool).Association("Tags").Append(&tags[i]); err != nil { - return params.Pool{}, errors.Wrap(err, "saving tag") - } - } - - pool, err := s.getPoolByID(s.conn, newPool.ID.String(), "Tags", "Instances", "Enterprise", "Organization", "Repository") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - - return s.sqlToCommonPool(pool) -} - -func (s *sqlDatabase) GetEnterprisePool(_ context.Context, enterpriseID, poolID string) (params.Pool, error) { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeEnterprise, enterpriseID, poolID, "Tags", "Instances") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - return s.sqlToCommonPool(pool) -} - -func (s *sqlDatabase) DeleteEnterprisePool(_ context.Context, enterpriseID, poolID string) error { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeEnterprise, enterpriseID, poolID) - if err != nil { - return errors.Wrap(err, "looking up enterprise pool") - } - q := s.conn.Unscoped().Delete(&pool) - if q.Error != nil && !errors.Is(q.Error, gorm.ErrRecordNotFound) { - return errors.Wrap(q.Error, "deleting pool") - } - return nil -} - -func (s *sqlDatabase) UpdateEnterprisePool(_ context.Context, enterpriseID, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeEnterprise, enterpriseID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - - return s.updatePool(s.conn, pool, param) -} - -func (s *sqlDatabase) ListEnterprisePools(ctx context.Context, enterpriseID string) ([]params.Pool, error) { - pools, err := s.listEntityPools(ctx, params.GithubEntityTypeEnterprise, enterpriseID, "Tags", "Instances", "Enterprise") - if err != nil { - return nil, errors.Wrap(err, "fetching pools") - } - - ret := make([]params.Pool, len(pools)) - for idx, pool := range pools { - ret[idx], err = s.sqlToCommonPool(pool) - if err != nil { - return nil, errors.Wrap(err, "fetching pools") - } - } - - return ret, nil -} - -func (s *sqlDatabase) ListEnterpriseInstances(ctx context.Context, enterpriseID string) ([]params.Instance, error) { - pools, err := s.listEntityPools(ctx, params.GithubEntityTypeEnterprise, enterpriseID, "Instances", "Tags", "Instances.Job") - if err != nil { - return nil, errors.Wrap(err, "fetching enterprise") - } - ret := []params.Instance{} - for _, pool := range pools { - for _, instance := range pool.Instances { - paramsInstance, err := s.sqlToParamsInstance(instance) - if err != nil { - return nil, errors.Wrap(err, "fetching instance") - } - ret = append(ret, paramsInstance) - } - } - return ret, nil -} - func (s *sqlDatabase) getEnterprise(_ context.Context, name string) (Enterprise, error) { var enterprise Enterprise @@ -302,22 +170,3 @@ func (s *sqlDatabase) getEnterpriseByID(_ context.Context, id string, preload .. } return enterprise, nil } - -func (s *sqlDatabase) getEnterprisePoolByUniqueFields(ctx context.Context, enterpriseID string, provider, image, flavor string) (Pool, error) { - enterprise, err := s.getEnterpriseByID(ctx, enterpriseID) - if err != nil { - return Pool{}, errors.Wrap(err, "fetching enterprise") - } - - q := s.conn - var pool []Pool - err = q.Model(&enterprise).Association("Pools").Find(&pool, "provider_name = ? and image = ? and flavor = ?", provider, image, flavor) - if err != nil { - return Pool{}, errors.Wrap(err, "fetching pool") - } - if len(pool) == 0 { - return Pool{}, runnerErrors.ErrNotFound - } - - return pool[0], nil -} diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 4f4a7da2..f77ae3d5 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -405,10 +405,8 @@ func (s *EnterpriseTestSuite) TestGetEnterpriseByIDDBDecryptingErr() { } func (s *EnterpriseTestSuite) TestCreateEnterprisePool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().Nil(err) @@ -426,11 +424,9 @@ func (s *EnterpriseTestSuite) TestCreateEnterprisePool() { func (s *EnterpriseTestSuite) TestCreateEnterprisePoolMissingTags() { s.Fixtures.CreatePoolParams.Tags = []string{} - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } - _, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().NotNil(err) s.Require().Equal("no tags specified", err.Error()) @@ -448,41 +444,37 @@ func (s *EnterpriseTestSuite) TestCreateEnterprisePoolInvalidEnterpriseID() { } func (s *EnterpriseTestSuite) TestCreateEnterprisePoolDBCreateErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`enterprise_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WillReturnError(fmt.Errorf("mocked creating pool error")) - _, err := s.StoreSQLMocked.CreateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("creating pool: fetching pool: mocked creating pool error", err.Error()) + s.Require().Equal("checking pool existence: mocked creating pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestCreateEnterpriseDBPoolAlreadyExistErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`enterprise_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"enterprise_id", "provider_name", "image", "flavor"}). AddRow( s.Fixtures.Enterprises[0].ID, @@ -490,159 +482,141 @@ func (s *EnterpriseTestSuite) TestCreateEnterpriseDBPoolAlreadyExistErr() { s.Fixtures.CreatePoolParams.Image, s.Fixtures.CreatePoolParams.Flavor)) - _, err := s.StoreSQLMocked.CreateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) s.Require().Equal(runnerErrors.NewConflictError("pool with the same image and flavor already exists on this provider"), err) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestCreateEnterprisePoolDBFetchTagErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`enterprise_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"enterprise_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnError(fmt.Errorf("mocked fetching tag error")) - _, err := s.StoreSQLMocked.CreateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("fetching tag: fetching tag from database: mocked fetching tag error", err.Error()) + s.Require().Equal("creating tag: fetching tag from database: mocked fetching tag error", err.Error()) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestCreateEnterprisePoolDBAddingPoolErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} - + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`enterprise_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"enterprise_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnError(fmt.Errorf("mocked adding pool error")) s.Fixtures.SQLMock.ExpectRollback() - _, err := s.StoreSQLMocked.CreateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("adding pool: mocked adding pool error", err.Error()) + s.Require().Equal("creating pool: mocked adding pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestCreateEnterprisePoolDBSaveTagErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`enterprise_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"enterprise_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("UPDATE `pools` SET")). WillReturnError(fmt.Errorf("mocked saving tag error")) s.Fixtures.SQLMock.ExpectRollback() - _, err := s.StoreSQLMocked.CreateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("saving tag: mocked saving tag error", err.Error()) + s.Require().Equal("associating tags: mocked saving tag error", err.Error()) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestCreateEnterprisePoolDBFetchPoolErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`enterprise_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Enterprises[0].ID). WillReturnRows(sqlmock.NewRows([]string{"enterprise_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("UPDATE `pools` SET")). WillReturnResult(sqlmock.NewResult(1, 1)) @@ -657,19 +631,19 @@ func (s *EnterpriseTestSuite) TestCreateEnterprisePoolDBFetchPoolErr() { ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE id = ? AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"id"})) - _, err := s.StoreSQLMocked.CreateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) s.Require().Equal("fetching pool: not found", err.Error()) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestListEnterprisePools() { enterprisePools := []params.Pool{} - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) for i := 1; i <= 2; i++ { s.Fixtures.CreatePoolParams.Flavor = fmt.Sprintf("test-flavor-%v", i) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) @@ -679,24 +653,26 @@ func (s *EnterpriseTestSuite) TestListEnterprisePools() { enterprisePools = append(enterprisePools, pool) } - pools, err := s.Store.ListEnterprisePools(context.Background(), s.Fixtures.Enterprises[0].ID) + pools, err := s.Store.ListEntityPools(context.Background(), entity) s.Require().Nil(err) garmTesting.EqualDBEntityID(s.T(), enterprisePools, pools) } func (s *EnterpriseTestSuite) TestListEnterprisePoolsInvalidEnterpriseID() { - _, err := s.Store.ListEnterprisePools(context.Background(), "dummy-enterprise-id") + entity := params.GithubEntity{ + ID: "dummy-enterprise-id", + EntityType: params.GithubEntityTypeEnterprise, + } + _, err := s.Store.ListEntityPools(context.Background(), entity) s.Require().NotNil(err) s.Require().Equal("fetching pools: parsing id: invalid request", err.Error()) } func (s *EnterpriseTestSuite) TestGetEnterprisePool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create enterprise pool: %v", err)) @@ -720,10 +696,8 @@ func (s *EnterpriseTestSuite) TestGetEnterprisePoolInvalidEnterpriseID() { } func (s *EnterpriseTestSuite) TestDeleteEnterprisePool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create enterprise pool: %v", err)) @@ -748,38 +722,29 @@ func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolInvalidEnterpriseID() { } func (s *EnterpriseTestSuite) TestDeleteEnterprisePoolDBDeleteErr() { - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create enterprise pool: %v", err)) } - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (id = ? and enterprise_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). - WithArgs(pool.ID, s.Fixtures.Enterprises[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"enterprise_id", "id"}).AddRow(s.Fixtures.Enterprises[0].ID, pool.ID)) s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. - ExpectExec(regexp.QuoteMeta("DELETE FROM `pools` WHERE `pools`.`id` = ?")). - WithArgs(pool.ID). + ExpectExec(regexp.QuoteMeta("DELETE FROM `pools` WHERE id = ? and enterprise_id = ?")). + WithArgs(pool.ID, s.Fixtures.Enterprises[0].ID). WillReturnError(fmt.Errorf("mocked deleting pool error")) s.Fixtures.SQLMock.ExpectRollback() - err = s.StoreSQLMocked.DeleteEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, pool.ID) - - s.assertSQLMockExpectations() + err = s.StoreSQLMocked.DeleteEntityPool(context.Background(), entity, pool.ID) s.Require().NotNil(err) - s.Require().Equal("deleting pool: mocked deleting pool error", err.Error()) + s.Require().Equal("removing pool: mocked deleting pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *EnterpriseTestSuite) TestListEnterpriseInstances() { - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create enterprise pool: %v", err)) @@ -794,30 +759,32 @@ func (s *EnterpriseTestSuite) TestListEnterpriseInstances() { poolInstances = append(poolInstances, instance) } - instances, err := s.Store.ListEnterpriseInstances(context.Background(), s.Fixtures.Enterprises[0].ID) + instances, err := s.Store.ListEntityInstances(context.Background(), entity) s.Require().Nil(err) s.equalInstancesByName(poolInstances, instances) } func (s *EnterpriseTestSuite) TestListEnterpriseInstancesInvalidEnterpriseID() { - _, err := s.Store.ListEnterpriseInstances(context.Background(), "dummy-enterprise-id") + entity := params.GithubEntity{ + ID: "dummy-enterprise-id", + EntityType: params.GithubEntityTypeEnterprise, + } + _, err := s.Store.ListEntityInstances(context.Background(), entity) s.Require().NotNil(err) - s.Require().Equal("fetching enterprise: parsing id: invalid request", err.Error()) + s.Require().Equal("fetching entity: parsing id: invalid request", err.Error()) } func (s *EnterpriseTestSuite) TestUpdateEnterprisePool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Enterprises[0].ID, - EntityType: params.GithubEntityTypeEnterprise, - } + entity, err := s.Fixtures.Enterprises[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create enterprise pool: %v", err)) } - pool, err = s.Store.UpdateEnterprisePool(context.Background(), s.Fixtures.Enterprises[0].ID, pool.ID, s.Fixtures.UpdatePoolParams) + pool, err = s.Store.UpdateEntityPool(context.Background(), entity, pool.ID, s.Fixtures.UpdatePoolParams) s.Require().Nil(err) s.Require().Equal(*s.Fixtures.UpdatePoolParams.MaxRunners, pool.MaxRunners) @@ -827,7 +794,11 @@ func (s *EnterpriseTestSuite) TestUpdateEnterprisePool() { } func (s *EnterpriseTestSuite) TestUpdateEnterprisePoolInvalidEnterpriseID() { - _, err := s.Store.UpdateEnterprisePool(context.Background(), "dummy-enterprise-id", "dummy-pool-id", s.Fixtures.UpdatePoolParams) + entity := params.GithubEntity{ + ID: "dummy-enterprise-id", + EntityType: params.GithubEntityTypeEnterprise, + } + _, err := s.Store.UpdateEntityPool(context.Background(), entity, "dummy-pool-id", s.Fixtures.UpdatePoolParams) s.Require().NotNil(err) s.Require().Equal("fetching pool: parsing id: invalid request", err.Error()) diff --git a/database/sql/instances_test.go b/database/sql/instances_test.go index 200c683b..b136c8ae 100644 --- a/database/sql/instances_test.go +++ b/database/sql/instances_test.go @@ -92,10 +92,8 @@ func (s *InstancesTestSuite) SetupTest() { OSType: "linux", Tags: []string{"self-hosted", "amd64", "linux"}, } - entity := params.GithubEntity{ - ID: org.ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := org.GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, createPoolParams) if err != nil { s.FailNow(fmt.Sprintf("failed to create org pool: %s", err)) diff --git a/database/sql/organizations.go b/database/sql/organizations.go index a2b14ae5..24704fd9 100644 --- a/database/sql/organizations.go +++ b/database/sql/organizations.go @@ -20,7 +20,6 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "gorm.io/datatypes" "gorm.io/gorm" runnerErrors "github.com/cloudbase/garm-provider-common/errors" @@ -151,137 +150,6 @@ func (s *sqlDatabase) GetOrganizationByID(ctx context.Context, orgID string) (pa return param, nil } -func (s *sqlDatabase) CreateOrganizationPool(ctx context.Context, orgID string, param params.CreatePoolParams) (params.Pool, error) { - if len(param.Tags) == 0 { - return params.Pool{}, runnerErrors.NewBadRequestError("no tags specified") - } - - org, err := s.getOrgByID(ctx, orgID) - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching org") - } - - newPool := Pool{ - ProviderName: param.ProviderName, - MaxRunners: param.MaxRunners, - MinIdleRunners: param.MinIdleRunners, - RunnerPrefix: param.GetRunnerPrefix(), - Image: param.Image, - Flavor: param.Flavor, - OSType: param.OSType, - OSArch: param.OSArch, - OrgID: &org.ID, - Enabled: param.Enabled, - RunnerBootstrapTimeout: param.RunnerBootstrapTimeout, - GitHubRunnerGroup: param.GitHubRunnerGroup, - Priority: param.Priority, - } - - if len(param.ExtraSpecs) > 0 { - newPool.ExtraSpecs = datatypes.JSON(param.ExtraSpecs) - } - - _, err = s.getOrgPoolByUniqueFields(ctx, orgID, newPool.ProviderName, newPool.Image, newPool.Flavor) - if err != nil { - if !errors.Is(err, runnerErrors.ErrNotFound) { - return params.Pool{}, errors.Wrap(err, "creating pool") - } - } else { - return params.Pool{}, runnerErrors.NewConflictError("pool with the same image and flavor already exists on this provider") - } - - tags := []Tag{} - for _, val := range param.Tags { - t, err := s.getOrCreateTag(s.conn, val) - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching tag") - } - tags = append(tags, t) - } - - q := s.conn.Create(&newPool) - if q.Error != nil { - return params.Pool{}, errors.Wrap(q.Error, "adding pool") - } - - for i := range tags { - if err := s.conn.Model(&newPool).Association("Tags").Append(&tags[i]); err != nil { - return params.Pool{}, errors.Wrap(err, "saving tag") - } - } - - pool, err := s.getPoolByID(s.conn, newPool.ID.String(), "Tags", "Instances", "Enterprise", "Organization", "Repository") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - - return s.sqlToCommonPool(pool) -} - -func (s *sqlDatabase) ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, error) { - pools, err := s.listEntityPools(ctx, params.GithubEntityTypeOrganization, orgID, "Tags", "Instances", "Organization") - if err != nil { - return nil, errors.Wrap(err, "fetching pools") - } - - ret := make([]params.Pool, len(pools)) - for idx, pool := range pools { - ret[idx], err = s.sqlToCommonPool(pool) - if err != nil { - return nil, errors.Wrap(err, "fetching pool") - } - } - - return ret, nil -} - -func (s *sqlDatabase) GetOrganizationPool(_ context.Context, orgID, poolID string) (params.Pool, error) { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeOrganization, orgID, poolID, "Tags", "Instances") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - return s.sqlToCommonPool(pool) -} - -func (s *sqlDatabase) DeleteOrganizationPool(_ context.Context, orgID, poolID string) error { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeOrganization, orgID, poolID) - if err != nil { - return errors.Wrap(err, "looking up org pool") - } - q := s.conn.Unscoped().Delete(&pool) - if q.Error != nil && !errors.Is(q.Error, gorm.ErrRecordNotFound) { - return errors.Wrap(q.Error, "deleting pool") - } - return nil -} - -func (s *sqlDatabase) ListOrgInstances(ctx context.Context, orgID string) ([]params.Instance, error) { - pools, err := s.listEntityPools(ctx, params.GithubEntityTypeOrganization, orgID, "Tags", "Instances", "Instances.Job") - if err != nil { - return nil, errors.Wrap(err, "fetching org") - } - ret := []params.Instance{} - for _, pool := range pools { - for _, instance := range pool.Instances { - paramsInstance, err := s.sqlToParamsInstance(instance) - if err != nil { - return nil, errors.Wrap(err, "fetching instance") - } - ret = append(ret, paramsInstance) - } - } - return ret, nil -} - -func (s *sqlDatabase) UpdateOrganizationPool(_ context.Context, orgID, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeOrganization, orgID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - - return s.updatePool(s.conn, pool, param) -} - func (s *sqlDatabase) getOrgByID(_ context.Context, id string, preload ...string) (Organization, error) { u, err := uuid.Parse(id) if err != nil { @@ -319,22 +187,3 @@ func (s *sqlDatabase) getOrg(_ context.Context, name string) (Organization, erro } return org, nil } - -func (s *sqlDatabase) getOrgPoolByUniqueFields(ctx context.Context, orgID string, provider, image, flavor string) (Pool, error) { - org, err := s.getOrgByID(ctx, orgID) - if err != nil { - return Pool{}, errors.Wrap(err, "fetching org") - } - - q := s.conn - var pool []Pool - err = q.Model(&org).Association("Pools").Find(&pool, "provider_name = ? and image = ? and flavor = ?", provider, image, flavor) - if err != nil { - return Pool{}, errors.Wrap(err, "fetching pool") - } - if len(pool) == 0 { - return Pool{}, runnerErrors.ErrNotFound - } - - return pool[0], nil -} diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index 63b4cebd..86d13d72 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -405,10 +405,8 @@ func (s *OrgTestSuite) TestGetOrganizationByIDDBDecryptingErr() { } func (s *OrgTestSuite) TestCreateOrganizationPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().Nil(err) @@ -426,11 +424,9 @@ func (s *OrgTestSuite) TestCreateOrganizationPool() { func (s *OrgTestSuite) TestCreateOrganizationPoolMissingTags() { s.Fixtures.CreatePoolParams.Tags = []string{} - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } - _, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + _, err = s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().NotNil(err) s.Require().Equal("no tags specified", err.Error()) @@ -448,41 +444,37 @@ func (s *OrgTestSuite) TestCreateOrganizationPoolInvalidOrgID() { } func (s *OrgTestSuite) TestCreateOrganizationPoolDBCreateErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`org_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WillReturnError(fmt.Errorf("mocked creating pool error")) - _, err := s.StoreSQLMocked.CreateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("creating pool: fetching pool: mocked creating pool error", err.Error()) + s.Require().Equal("checking pool existence: mocked creating pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestCreateOrganizationDBPoolAlreadyExistErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`org_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"org_id", "provider_name", "image", "flavor"}). AddRow( s.Fixtures.Orgs[0].ID, @@ -490,159 +482,142 @@ func (s *OrgTestSuite) TestCreateOrganizationDBPoolAlreadyExistErr() { s.Fixtures.CreatePoolParams.Image, s.Fixtures.CreatePoolParams.Flavor)) - _, err := s.StoreSQLMocked.CreateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) s.Require().Equal(runnerErrors.NewConflictError("pool with the same image and flavor already exists on this provider"), err) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestCreateOrganizationPoolDBFetchTagErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`org_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"org_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnError(fmt.Errorf("mocked fetching tag error")) - _, err := s.StoreSQLMocked.CreateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("fetching tag: fetching tag from database: mocked fetching tag error", err.Error()) + s.Require().Equal("creating tag: fetching tag from database: mocked fetching tag error", err.Error()) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestCreateOrganizationPoolDBAddingPoolErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`org_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"org_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnError(fmt.Errorf("mocked adding pool error")) s.Fixtures.SQLMock.ExpectRollback() - _, err := s.StoreSQLMocked.CreateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("adding pool: mocked adding pool error", err.Error()) + s.Require().Equal("creating pool: mocked adding pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestCreateOrganizationPoolDBSaveTagErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`org_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"org_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("UPDATE `pools` SET")). WillReturnError(fmt.Errorf("mocked saving tag error")) s.Fixtures.SQLMock.ExpectRollback() - _, err := s.StoreSQLMocked.CreateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("saving tag: mocked saving tag error", err.Error()) + s.Require().Equal("associating tags: mocked saving tag error", err.Error()) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestCreateOrganizationPoolDBFetchPoolErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`org_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Orgs[0].ID). WillReturnRows(sqlmock.NewRows([]string{"org_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("UPDATE `pools` SET")). WillReturnResult(sqlmock.NewResult(1, 1)) @@ -657,19 +632,20 @@ func (s *OrgTestSuite) TestCreateOrganizationPoolDBFetchPoolErr() { ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE id = ? AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"id"})) - _, err := s.StoreSQLMocked.CreateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) s.Require().Equal("fetching pool: not found", err.Error()) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestListOrgPools() { orgPools := []params.Pool{} - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) for i := 1; i <= 2; i++ { s.Fixtures.CreatePoolParams.Flavor = fmt.Sprintf("test-flavor-%v", i) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) @@ -678,25 +654,26 @@ func (s *OrgTestSuite) TestListOrgPools() { } orgPools = append(orgPools, pool) } - - pools, err := s.Store.ListOrgPools(context.Background(), s.Fixtures.Orgs[0].ID) + pools, err := s.Store.ListEntityPools(context.Background(), entity) s.Require().Nil(err) garmTesting.EqualDBEntityID(s.T(), orgPools, pools) } func (s *OrgTestSuite) TestListOrgPoolsInvalidOrgID() { - _, err := s.Store.ListOrgPools(context.Background(), "dummy-org-id") + entity := params.GithubEntity{ + ID: "dummy-org-id", + EntityType: params.GithubEntityTypeOrganization, + } + _, err := s.Store.ListEntityPools(context.Background(), entity) s.Require().NotNil(err) s.Require().Equal("fetching pools: parsing id: invalid request", err.Error()) } func (s *OrgTestSuite) TestGetOrganizationPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) @@ -720,10 +697,8 @@ func (s *OrgTestSuite) TestGetOrganizationPoolInvalidOrgID() { } func (s *OrgTestSuite) TestDeleteOrganizationPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) @@ -748,38 +723,31 @@ func (s *OrgTestSuite) TestDeleteOrganizationPoolInvalidOrgID() { } func (s *OrgTestSuite) TestDeleteOrganizationPoolDBDeleteErr() { - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) + pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) } - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (id = ? and org_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). - WithArgs(pool.ID, s.Fixtures.Orgs[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"org_id", "id"}).AddRow(s.Fixtures.Orgs[0].ID, pool.ID)) s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. - ExpectExec(regexp.QuoteMeta("DELETE FROM `pools` WHERE `pools`.`id` = ?")). - WithArgs(pool.ID). + ExpectExec(regexp.QuoteMeta("DELETE FROM `pools` WHERE id = ? and org_id = ?")). + WithArgs(pool.ID, s.Fixtures.Orgs[0].ID). WillReturnError(fmt.Errorf("mocked deleting pool error")) s.Fixtures.SQLMock.ExpectRollback() - err = s.StoreSQLMocked.DeleteOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, pool.ID) + err = s.StoreSQLMocked.DeleteEntityPool(context.Background(), entity, pool.ID) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("deleting pool: mocked deleting pool error", err.Error()) + s.Require().Equal("removing pool: mocked deleting pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *OrgTestSuite) TestListOrgInstances() { - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) @@ -794,30 +762,32 @@ func (s *OrgTestSuite) TestListOrgInstances() { poolInstances = append(poolInstances, instance) } - instances, err := s.Store.ListOrgInstances(context.Background(), s.Fixtures.Orgs[0].ID) + instances, err := s.Store.ListEntityInstances(context.Background(), entity) s.Require().Nil(err) s.equalInstancesByName(poolInstances, instances) } func (s *OrgTestSuite) TestListOrgInstancesInvalidOrgID() { - _, err := s.Store.ListOrgInstances(context.Background(), "dummy-org-id") + entity := params.GithubEntity{ + ID: "dummy-org-id", + EntityType: params.GithubEntityTypeOrganization, + } + _, err := s.Store.ListEntityInstances(context.Background(), entity) s.Require().NotNil(err) - s.Require().Equal("fetching org: parsing id: invalid request", err.Error()) + s.Require().Equal("fetching entity: parsing id: invalid request", err.Error()) } func (s *OrgTestSuite) TestUpdateOrganizationPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Orgs[0].ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := s.Fixtures.Orgs[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create org pool: %v", err)) } - pool, err = s.Store.UpdateOrganizationPool(context.Background(), s.Fixtures.Orgs[0].ID, pool.ID, s.Fixtures.UpdatePoolParams) + pool, err = s.Store.UpdateEntityPool(context.Background(), entity, pool.ID, s.Fixtures.UpdatePoolParams) s.Require().Nil(err) s.Require().Equal(*s.Fixtures.UpdatePoolParams.MaxRunners, pool.MaxRunners) @@ -827,7 +797,11 @@ func (s *OrgTestSuite) TestUpdateOrganizationPool() { } func (s *OrgTestSuite) TestUpdateOrganizationPoolInvalidOrgID() { - _, err := s.Store.UpdateOrganizationPool(context.Background(), "dummy-org-id", "dummy-pool-id", s.Fixtures.UpdatePoolParams) + entity := params.GithubEntity{ + ID: "dummy-org-id", + EntityType: params.GithubEntityTypeOrganization, + } + _, err := s.Store.UpdateEntityPool(context.Background(), entity, "dummy-pool-id", s.Fixtures.UpdatePoolParams) s.Require().NotNil(err) s.Require().Equal("fetching pool: parsing id: invalid request", err.Error()) diff --git a/database/sql/pools.go b/database/sql/pools.go index ab892eb2..1dd7e68d 100644 --- a/database/sql/pools.go +++ b/database/sql/pools.go @@ -89,25 +89,30 @@ func (s *sqlDatabase) getEntityPool(tx *gorm.DB, entityType params.GithubEntityT return Pool{}, errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } + var fieldName string + var entityField string + switch entityType { + case params.GithubEntityTypeRepository: + fieldName = entityTypeRepoName + entityField = "Repository" + case params.GithubEntityTypeOrganization: + fieldName = entityTypeOrgName + entityField = "Organization" + case params.GithubEntityTypeEnterprise: + fieldName = entityTypeEnterpriseName + entityField = "Enterprise" + default: + return Pool{}, fmt.Errorf("invalid entityType: %v", entityType) + } + q := tx + q = q.Preload(entityField) if len(preload) > 0 { for _, item := range preload { q = q.Preload(item) } } - var fieldName string - switch entityType { - case params.GithubEntityTypeRepository: - fieldName = entityTypeRepoName - case params.GithubEntityTypeOrganization: - fieldName = entityTypeOrgName - case params.GithubEntityTypeEnterprise: - fieldName = entityTypeEnterpriseName - default: - return Pool{}, fmt.Errorf("invalid entityType: %v", entityType) - } - var pool Pool condition := fmt.Sprintf("id = ? and %s = ?", fieldName) err = q.Model(&Pool{}). @@ -123,30 +128,39 @@ func (s *sqlDatabase) getEntityPool(tx *gorm.DB, entityType params.GithubEntityT return pool, nil } -func (s *sqlDatabase) listEntityPools(_ context.Context, entityType params.GithubEntityType, entityID string, preload ...string) ([]Pool, error) { +func (s *sqlDatabase) listEntityPools(tx *gorm.DB, entityType params.GithubEntityType, entityID string, preload ...string) ([]Pool, error) { if _, err := uuid.Parse(entityID); err != nil { return nil, errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } - q := s.conn - if len(preload) > 0 { - for _, item := range preload { - q = q.Preload(item) - } + if err := s.hasGithubEntity(tx, entityType, entityID); err != nil { + return nil, errors.Wrap(err, "checking entity existence") } + var preloadEntity string var fieldName string switch entityType { case params.GithubEntityTypeRepository: fieldName = entityTypeRepoName + preloadEntity = "Repository" case params.GithubEntityTypeOrganization: fieldName = entityTypeOrgName + preloadEntity = "Organization" case params.GithubEntityTypeEnterprise: fieldName = entityTypeEnterpriseName + preloadEntity = "Enterprise" default: return nil, fmt.Errorf("invalid entityType: %v", entityType) } + q := tx + q = q.Preload(preloadEntity) + if len(preload) > 0 { + for _, item := range preload { + q = q.Preload(item) + } + } + var pools []Pool condition := fmt.Sprintf("%s = ?", fieldName) err := q.Model(&Pool{}). @@ -270,8 +284,7 @@ func (s *sqlDatabase) CreateEntityPool(_ context.Context, entity params.GithubEn newPool.EnterpriseID = &entityID } err = s.conn.Transaction(func(tx *gorm.DB) error { - ok, err := s.hasGithubEntity(tx, entity.EntityType, entity.ID) - if err != nil || !ok { + if err := s.hasGithubEntity(tx, entity.EntityType, entity.ID); err != nil { return errors.Wrap(err, "checking entity existence") } @@ -305,7 +318,7 @@ func (s *sqlDatabase) CreateEntityPool(_ context.Context, entity params.GithubEn return nil }) if err != nil { - return params.Pool{}, errors.Wrap(err, "creating pool") + return params.Pool{}, err } pool, err := s.getPoolByID(s.conn, newPool.ID.String(), "Tags", "Instances", "Enterprise", "Organization", "Repository") @@ -353,16 +366,56 @@ func (s *sqlDatabase) DeleteEntityPool(_ context.Context, entity params.GithubEn } func (s *sqlDatabase) UpdateEntityPool(_ context.Context, entity params.GithubEntity, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - fmt.Printf("UpdateEntityPool: %v %v %v\n", entity, poolID, param) - return params.Pool{}, nil + var updatedPool params.Pool + err := s.conn.Transaction(func(tx *gorm.DB) error { + pool, err := s.getEntityPool(tx, entity.EntityType, entity.ID, poolID, "Tags", "Instances") + if err != nil { + return errors.Wrap(err, "fetching pool") + } + + updatedPool, err = s.updatePool(tx, pool, param) + if err != nil { + return errors.Wrap(err, "updating pool") + } + return nil + }) + if err != nil { + return params.Pool{}, err + } + return updatedPool, nil } func (s *sqlDatabase) ListEntityPools(_ context.Context, entity params.GithubEntity) ([]params.Pool, error) { - fmt.Println(entity) - return nil, nil + pools, err := s.listEntityPools(s.conn, entity.EntityType, entity.ID, "Tags") + if err != nil { + return nil, errors.Wrap(err, "fetching pools") + } + + ret := make([]params.Pool, len(pools)) + for idx, pool := range pools { + ret[idx], err = s.sqlToCommonPool(pool) + if err != nil { + return nil, errors.Wrap(err, "fetching pool") + } + } + + return ret, nil } func (s *sqlDatabase) ListEntityInstances(_ context.Context, entity params.GithubEntity) ([]params.Instance, error) { - fmt.Println(entity) - return nil, nil + pools, err := s.listEntityPools(s.conn, entity.EntityType, entity.ID, "Instances", "Instances.Job") + if err != nil { + return nil, errors.Wrap(err, "fetching entity") + } + ret := []params.Instance{} + for _, pool := range pools { + for _, instance := range pool.Instances { + paramsInstance, err := s.sqlToParamsInstance(instance) + if err != nil { + return nil, errors.Wrap(err, "fetching instance") + } + ret = append(ret, paramsInstance) + } + } + return ret, nil } diff --git a/database/sql/pools_test.go b/database/sql/pools_test.go index aac01f99..c05711cb 100644 --- a/database/sql/pools_test.go +++ b/database/sql/pools_test.go @@ -66,10 +66,8 @@ func (s *PoolsTestSuite) SetupTest() { s.FailNow(fmt.Sprintf("failed to create org: %s", err)) } - entity := params.GithubEntity{ - ID: org.ID, - EntityType: params.GithubEntityTypeOrganization, - } + entity, err := org.GetEntity() + s.Require().Nil(err) // create some pool objects in the database, for testing purposes orgPools := []params.Pool{} for i := 1; i <= 3; i++ { diff --git a/database/sql/repositories.go b/database/sql/repositories.go index 936b796c..164c0197 100644 --- a/database/sql/repositories.go +++ b/database/sql/repositories.go @@ -20,7 +20,6 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "gorm.io/datatypes" "gorm.io/gorm" runnerErrors "github.com/cloudbase/garm-provider-common/errors" @@ -151,138 +150,6 @@ func (s *sqlDatabase) GetRepositoryByID(ctx context.Context, repoID string) (par return param, nil } -func (s *sqlDatabase) CreateRepositoryPool(ctx context.Context, repoID string, param params.CreatePoolParams) (params.Pool, error) { - if len(param.Tags) == 0 { - return params.Pool{}, runnerErrors.NewBadRequestError("no tags specified") - } - - repo, err := s.getRepoByID(ctx, repoID) - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching repo") - } - - newPool := Pool{ - ProviderName: param.ProviderName, - MaxRunners: param.MaxRunners, - MinIdleRunners: param.MinIdleRunners, - RunnerPrefix: param.GetRunnerPrefix(), - Image: param.Image, - Flavor: param.Flavor, - OSType: param.OSType, - OSArch: param.OSArch, - RepoID: &repo.ID, - Enabled: param.Enabled, - RunnerBootstrapTimeout: param.RunnerBootstrapTimeout, - GitHubRunnerGroup: param.GitHubRunnerGroup, - Priority: param.Priority, - } - - if len(param.ExtraSpecs) > 0 { - newPool.ExtraSpecs = datatypes.JSON(param.ExtraSpecs) - } - - _, err = s.getRepoPoolByUniqueFields(ctx, repoID, newPool.ProviderName, newPool.Image, newPool.Flavor) - if err != nil { - if !errors.Is(err, runnerErrors.ErrNotFound) { - return params.Pool{}, errors.Wrap(err, "creating pool") - } - } else { - return params.Pool{}, runnerErrors.NewConflictError("pool with the same image and flavor already exists on this provider") - } - - tags := []Tag{} - for _, val := range param.Tags { - t, err := s.getOrCreateTag(s.conn, val) - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching tag") - } - tags = append(tags, t) - } - - q := s.conn.Create(&newPool) - if q.Error != nil { - return params.Pool{}, errors.Wrap(q.Error, "adding pool") - } - - for i := range tags { - if err := s.conn.Model(&newPool).Association("Tags").Append(&tags[i]); err != nil { - return params.Pool{}, errors.Wrap(err, "saving tag") - } - } - - pool, err := s.getPoolByID(s.conn, newPool.ID.String(), "Tags", "Instances", "Enterprise", "Organization", "Repository") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - - return s.sqlToCommonPool(pool) -} - -func (s *sqlDatabase) ListRepoPools(ctx context.Context, repoID string) ([]params.Pool, error) { - pools, err := s.listEntityPools(ctx, params.GithubEntityTypeRepository, repoID, "Tags", "Instances", "Repository") - if err != nil { - return nil, errors.Wrap(err, "fetching pools") - } - - ret := make([]params.Pool, len(pools)) - for idx, pool := range pools { - ret[idx], err = s.sqlToCommonPool(pool) - if err != nil { - return nil, errors.Wrap(err, "fetching pool") - } - } - - return ret, nil -} - -func (s *sqlDatabase) GetRepositoryPool(_ context.Context, repoID, poolID string) (params.Pool, error) { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeRepository, repoID, poolID, "Tags", "Instances") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - return s.sqlToCommonPool(pool) -} - -func (s *sqlDatabase) DeleteRepositoryPool(_ context.Context, repoID, poolID string) error { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeRepository, repoID, poolID) - if err != nil { - return errors.Wrap(err, "looking up repo pool") - } - q := s.conn.Unscoped().Delete(&pool) - if q.Error != nil && !errors.Is(q.Error, gorm.ErrRecordNotFound) { - return errors.Wrap(q.Error, "deleting pool") - } - return nil -} - -func (s *sqlDatabase) ListRepoInstances(ctx context.Context, repoID string) ([]params.Instance, error) { - pools, err := s.listEntityPools(ctx, params.GithubEntityTypeRepository, repoID, "Tags", "Instances", "Instances.Job") - if err != nil { - return nil, errors.Wrap(err, "fetching repo") - } - - ret := []params.Instance{} - for _, pool := range pools { - for _, instance := range pool.Instances { - paramsInstance, err := s.sqlToParamsInstance(instance) - if err != nil { - return nil, errors.Wrap(err, "fetching instance") - } - ret = append(ret, paramsInstance) - } - } - return ret, nil -} - -func (s *sqlDatabase) UpdateRepositoryPool(_ context.Context, repoID, poolID string, param params.UpdatePoolParams) (params.Pool, error) { - pool, err := s.getEntityPool(s.conn, params.GithubEntityTypeRepository, repoID, poolID, "Tags", "Instances", "Enterprise", "Organization", "Repository") - if err != nil { - return params.Pool{}, errors.Wrap(err, "fetching pool") - } - - return s.updatePool(s.conn, pool, param) -} - func (s *sqlDatabase) getRepo(_ context.Context, owner, name string) (Repository, error) { var repo Repository @@ -325,25 +192,6 @@ func (s *sqlDatabase) getEntityPoolByUniqueFields(tx *gorm.DB, entity params.Git return Pool{}, nil } -func (s *sqlDatabase) getRepoPoolByUniqueFields(ctx context.Context, repoID string, provider, image, flavor string) (Pool, error) { - repo, err := s.getRepoByID(ctx, repoID) - if err != nil { - return Pool{}, errors.Wrap(err, "fetching repo") - } - - q := s.conn - var pool []Pool - err = q.Model(&repo).Association("Pools").Find(&pool, "provider_name = ? and image = ? and flavor = ?", provider, image, flavor) - if err != nil { - return Pool{}, errors.Wrap(err, "fetching pool") - } - if len(pool) == 0 { - return Pool{}, runnerErrors.ErrNotFound - } - - return pool[0], nil -} - func (s *sqlDatabase) getRepoByID(_ context.Context, id string, preload ...string) (Repository, error) { u, err := uuid.Parse(id) if err != nil { diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index ab1f9da5..18126197 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -443,10 +443,8 @@ func (s *RepoTestSuite) TestGetRepositoryByIDDBDecryptingErr() { } func (s *RepoTestSuite) TestCreateRepositoryPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().Nil(err) @@ -463,11 +461,9 @@ func (s *RepoTestSuite) TestCreateRepositoryPool() { func (s *RepoTestSuite) TestCreateRepositoryPoolMissingTags() { s.Fixtures.CreatePoolParams.Tags = []string{} - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } - _, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + _, err = s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().NotNil(err) s.Require().Equal("no tags specified", err.Error()) @@ -485,41 +481,37 @@ func (s *RepoTestSuite) TestCreateRepositoryPoolInvalidRepoID() { } func (s *RepoTestSuite) TestCreateRepositoryPoolDBCreateErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`repo_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WillReturnError(fmt.Errorf("mocked creating pool error")) - _, err := s.StoreSQLMocked.CreateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("creating pool: fetching pool: mocked creating pool error", err.Error()) + s.Require().Equal("checking pool existence: mocked creating pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestCreateRepositoryPoolDBPoolAlreadyExistErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`repo_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"repo_id", "provider_name", "image", "flavor"}). AddRow( s.Fixtures.Repos[0].ID, @@ -527,159 +519,145 @@ func (s *RepoTestSuite) TestCreateRepositoryPoolDBPoolAlreadyExistErr() { s.Fixtures.CreatePoolParams.Image, s.Fixtures.CreatePoolParams.Flavor)) - _, err := s.StoreSQLMocked.CreateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) s.Require().Equal("pool with the same image and flavor already exists on this provider", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestCreateRepositoryPoolDBFetchTagErr() { + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`repo_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"repo_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnError(fmt.Errorf("mocked fetching tag error")) - _, err := s.StoreSQLMocked.CreateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("fetching tag: fetching tag from database: mocked fetching tag error", err.Error()) + s.Require().Equal("creating tag: fetching tag from database: mocked fetching tag error", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestCreateRepositoryPoolDBAddingPoolErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`repo_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"repo_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnError(fmt.Errorf("mocked adding pool error")) s.Fixtures.SQLMock.ExpectRollback() - _, err := s.StoreSQLMocked.CreateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) - s.Require().Equal("adding pool: mocked adding pool error", err.Error()) + s.Require().Equal("creating pool: mocked adding pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestCreateRepositoryPoolDBSaveTagErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`repo_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"repo_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("UPDATE `pools` SET")). WillReturnError(fmt.Errorf("mocked saving tag error")) s.Fixtures.SQLMock.ExpectRollback() - _, err := s.StoreSQLMocked.CreateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) - s.assertSQLMockExpectations() + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) s.Require().NotNil(err) - s.Require().Equal("saving tag: mocked saving tag error", err.Error()) + s.Require().Equal("associating tags: mocked saving tag error", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestCreateRepositoryPoolDBFetchPoolErr() { s.Fixtures.CreatePoolParams.Tags = []string{"linux"} + s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT 1")). - WithArgs(s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE `pools`.`repo_id` = ? AND (provider_name = ? and image = ? and flavor = ?) AND `pools`.`deleted_at` IS NULL")). + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (provider_name = ? and image = ? and flavor = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WithArgs( - s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams.ProviderName, s.Fixtures.CreatePoolParams.Image, - s.Fixtures.CreatePoolParams.Flavor). + s.Fixtures.CreatePoolParams.Flavor, + s.Fixtures.Repos[0].ID). WillReturnRows(sqlmock.NewRows([]string{"repo_id"})) s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `tags` WHERE name = ? AND `tags`.`deleted_at` IS NULL ORDER BY `tags`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"linux"})) - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `tags`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `pools`")). WillReturnResult(sqlmock.NewResult(1, 1)) - s.Fixtures.SQLMock.ExpectCommit() - s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("UPDATE `pools` SET")). WillReturnResult(sqlmock.NewResult(1, 1)) @@ -694,18 +672,19 @@ func (s *RepoTestSuite) TestCreateRepositoryPoolDBFetchPoolErr() { ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE id = ? AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). WillReturnRows(sqlmock.NewRows([]string{"id"})) - _, err := s.StoreSQLMocked.CreateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, s.Fixtures.CreatePoolParams) + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + + _, err = s.StoreSQLMocked.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) - s.assertSQLMockExpectations() s.Require().NotNil(err) s.Require().Equal("fetching pool: not found", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestListRepoPools() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) repoPools := []params.Pool{} for i := 1; i <= 2; i++ { s.Fixtures.CreatePoolParams.Flavor = fmt.Sprintf("test-flavor-%d", i) @@ -716,24 +695,26 @@ func (s *RepoTestSuite) TestListRepoPools() { repoPools = append(repoPools, pool) } - pools, err := s.Store.ListRepoPools(context.Background(), s.Fixtures.Repos[0].ID) + pools, err := s.Store.ListEntityPools(context.Background(), entity) s.Require().Nil(err) garmTesting.EqualDBEntityID(s.T(), repoPools, pools) } func (s *RepoTestSuite) TestListRepoPoolsInvalidRepoID() { - _, err := s.Store.ListRepoPools(context.Background(), "dummy-repo-id") + entity := params.GithubEntity{ + ID: "dummy-repo-id", + EntityType: params.GithubEntityTypeRepository, + } + _, err := s.Store.ListEntityPools(context.Background(), entity) s.Require().NotNil(err) s.Require().Equal("fetching pools: parsing id: invalid request", err.Error()) } func (s *RepoTestSuite) TestGetRepositoryPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create repo pool: %v", err)) @@ -757,10 +738,8 @@ func (s *RepoTestSuite) TestGetRepositoryPoolInvalidRepoID() { } func (s *RepoTestSuite) TestDeleteRepositoryPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create repo pool: %v", err)) @@ -785,38 +764,30 @@ func (s *RepoTestSuite) TestDeleteRepositoryPoolInvalidRepoID() { } func (s *RepoTestSuite) TestDeleteRepositoryPoolDBDeleteErr() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) + pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create repo pool: %v", err)) } - s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `pools` WHERE (id = ? and repo_id = ?) AND `pools`.`deleted_at` IS NULL ORDER BY `pools`.`id` LIMIT 1")). - WithArgs(pool.ID, s.Fixtures.Repos[0].ID). - WillReturnRows(sqlmock.NewRows([]string{"repo_id", "id"}).AddRow(s.Fixtures.Repos[0].ID, pool.ID)) s.Fixtures.SQLMock.ExpectBegin() s.Fixtures.SQLMock. - ExpectExec(regexp.QuoteMeta("DELETE FROM `pools` WHERE `pools`.`id` = ?")). - WithArgs(pool.ID). + ExpectExec(regexp.QuoteMeta("DELETE FROM `pools` WHERE id = ? and repo_id = ?")). + WithArgs(pool.ID, s.Fixtures.Repos[0].ID). WillReturnError(fmt.Errorf("mocked deleting pool error")) s.Fixtures.SQLMock.ExpectRollback() - err = s.StoreSQLMocked.DeleteRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, pool.ID) - - s.assertSQLMockExpectations() + err = s.StoreSQLMocked.DeleteEntityPool(context.Background(), entity, pool.ID) s.Require().NotNil(err) - s.Require().Equal("deleting pool: mocked deleting pool error", err.Error()) + s.Require().Equal("removing pool: mocked deleting pool error", err.Error()) + s.assertSQLMockExpectations() } func (s *RepoTestSuite) TestListRepoInstances() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) pool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create repo pool: %v", err)) @@ -831,30 +802,32 @@ func (s *RepoTestSuite) TestListRepoInstances() { poolInstances = append(poolInstances, instance) } - instances, err := s.Store.ListRepoInstances(context.Background(), s.Fixtures.Repos[0].ID) + instances, err := s.Store.ListEntityInstances(context.Background(), entity) s.Require().Nil(err) s.equalInstancesByID(poolInstances, instances) } func (s *RepoTestSuite) TestListRepoInstancesInvalidRepoID() { - _, err := s.Store.ListRepoInstances(context.Background(), "dummy-repo-id") + entity := params.GithubEntity{ + ID: "dummy-repo-id", + EntityType: params.GithubEntityTypeRepository, + } + _, err := s.Store.ListEntityInstances(context.Background(), entity) s.Require().NotNil(err) - s.Require().Equal("fetching repo: parsing id: invalid request", err.Error()) + s.Require().Equal("fetching entity: parsing id: invalid request", err.Error()) } func (s *RepoTestSuite) TestUpdateRepositoryPool() { - entity := params.GithubEntity{ - ID: s.Fixtures.Repos[0].ID, - EntityType: params.GithubEntityTypeRepository, - } + entity, err := s.Fixtures.Repos[0].GetEntity() + s.Require().Nil(err) repoPool, err := s.Store.CreateEntityPool(context.Background(), entity, s.Fixtures.CreatePoolParams) if err != nil { s.FailNow(fmt.Sprintf("cannot create repo pool: %v", err)) } - pool, err := s.Store.UpdateRepositoryPool(context.Background(), s.Fixtures.Repos[0].ID, repoPool.ID, s.Fixtures.UpdatePoolParams) + pool, err := s.Store.UpdateEntityPool(context.Background(), entity, repoPool.ID, s.Fixtures.UpdatePoolParams) s.Require().Nil(err) s.Require().Equal(*s.Fixtures.UpdatePoolParams.MaxRunners, pool.MaxRunners) @@ -864,7 +837,11 @@ func (s *RepoTestSuite) TestUpdateRepositoryPool() { } func (s *RepoTestSuite) TestUpdateRepositoryPoolInvalidRepoID() { - _, err := s.Store.UpdateRepositoryPool(context.Background(), "dummy-org-id", "dummy-repo-id", s.Fixtures.UpdatePoolParams) + entity := params.GithubEntity{ + ID: "dummy-repo-id", + EntityType: params.GithubEntityTypeRepository, + } + _, err := s.Store.UpdateEntityPool(context.Background(), entity, "dummy-repo-id", s.Fixtures.UpdatePoolParams) s.Require().NotNil(err) s.Require().Equal("fetching pool: parsing id: invalid request", err.Error()) diff --git a/database/sql/util.go b/database/sql/util.go index 2a41050c..aaea31fe 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -290,9 +290,8 @@ func (s *sqlDatabase) getOrCreateTag(tx *gorm.DB, tagName string) (Tag, error) { Name: tagName, } - q = tx.Create(&newTag) - if q.Error != nil { - return Tag{}, errors.Wrap(q.Error, "creating tag") + if err := tx.Create(&newTag).Error; err != nil { + return Tag{}, errors.Wrap(err, "creating tag") } return newTag, nil } @@ -392,10 +391,10 @@ func (s *sqlDatabase) getPoolByID(tx *gorm.DB, poolID string, preload ...string) return pool, nil } -func (s *sqlDatabase) hasGithubEntity(tx *gorm.DB, entityType params.GithubEntityType, entityID string) (bool, error) { +func (s *sqlDatabase) hasGithubEntity(tx *gorm.DB, entityType params.GithubEntityType, entityID string) error { u, err := uuid.Parse(entityID) if err != nil { - return false, errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") + return errors.Wrap(runnerErrors.ErrBadRequest, "parsing id") } var q *gorm.DB switch entityType { @@ -406,15 +405,15 @@ func (s *sqlDatabase) hasGithubEntity(tx *gorm.DB, entityType params.GithubEntit case params.GithubEntityTypeEnterprise: q = tx.Model(&Enterprise{}).Where("id = ?", u) default: - return false, errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") + return errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") } var entity interface{} if err := q.First(entity).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return false, errors.Wrap(runnerErrors.ErrNotFound, "entity not found") + return errors.Wrap(runnerErrors.ErrNotFound, "entity not found") } - return false, errors.Wrap(err, "fetching entity from database") + return errors.Wrap(err, "fetching entity from database") } - return true, nil + return nil } diff --git a/params/params.go b/params/params.go index 2b87a4c5..a2a44222 100644 --- a/params/params.go +++ b/params/params.go @@ -317,6 +317,27 @@ type Pool struct { Priority uint `json:"priority"` } +func (p Pool) GithubEntity() (GithubEntity, error) { + switch p.PoolType() { + case GithubEntityTypeRepository: + return GithubEntity{ + ID: p.RepoID, + EntityType: GithubEntityTypeRepository, + }, nil + case GithubEntityTypeOrganization: + return GithubEntity{ + ID: p.OrgID, + EntityType: GithubEntityTypeOrganization, + }, nil + case GithubEntityTypeEnterprise: + return GithubEntity{ + ID: p.EnterpriseID, + EntityType: GithubEntityTypeEnterprise, + }, nil + } + return GithubEntity{}, fmt.Errorf("pool has no associated entity") +} + func (p Pool) GetID() string { return p.ID } @@ -383,6 +404,18 @@ type Repository struct { WebhookSecret string `json:"-"` } +func (r Repository) GetEntity() (GithubEntity, error) { + if r.ID == "" { + return GithubEntity{}, fmt.Errorf("repository has no ID") + } + return GithubEntity{ + ID: r.ID, + EntityType: GithubEntityTypeRepository, + Owner: r.Owner, + Name: r.Name, + }, nil +} + func (r Repository) GetName() string { return r.Name } @@ -412,6 +445,18 @@ type Organization struct { WebhookSecret string `json:"-"` } +func (o Organization) GetEntity() (GithubEntity, error) { + if o.ID == "" { + return GithubEntity{}, fmt.Errorf("organization has no ID") + } + return GithubEntity{ + ID: o.ID, + EntityType: GithubEntityTypeOrganization, + Owner: o.Name, + WebhookSecret: o.WebhookSecret, + }, nil +} + func (o Organization) GetName() string { return o.Name } @@ -441,6 +486,18 @@ type Enterprise struct { WebhookSecret string `json:"-"` } +func (e Enterprise) GetEntity() (GithubEntity, error) { + if e.ID == "" { + return GithubEntity{}, fmt.Errorf("enterprise has no ID") + } + return GithubEntity{ + ID: e.ID, + EntityType: GithubEntityTypeEnterprise, + Owner: e.Name, + WebhookSecret: e.WebhookSecret, + }, nil +} + func (e Enterprise) GetName() string { return e.Name } diff --git a/runner/enterprises.go b/runner/enterprises.go index 7d6e5b8e..c5274e09 100644 --- a/runner/enterprises.go +++ b/runner/enterprises.go @@ -124,7 +124,12 @@ func (r *Runner) DeleteEnterprise(ctx context.Context, enterpriseID string) erro return errors.Wrap(err, "fetching enterprise") } - pools, err := r.store.ListEnterprisePools(ctx, enterpriseID) + entity, err := enterprise.GetEntity() + if err != nil { + return errors.Wrap(err, "getting entity") + } + + pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { return errors.Wrap(err, "fetching enterprise pools") } @@ -266,7 +271,11 @@ func (r *Runner) ListEnterprisePools(ctx context.Context, enterpriseID string) ( return []params.Pool{}, runnerErrors.ErrUnauthorized } - pools, err := r.store.ListEnterprisePools(ctx, enterpriseID) + entity := params.GithubEntity{ + ID: enterpriseID, + EntityType: params.GithubEntityTypeEnterprise, + } + pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { return nil, errors.Wrap(err, "fetching pools") } @@ -301,7 +310,7 @@ func (r *Runner) UpdateEnterprisePool(ctx context.Context, enterpriseID, poolID return params.Pool{}, runnerErrors.NewBadRequestError("min_idle_runners cannot be larger than max_runners") } - newPool, err := r.store.UpdateEnterprisePool(ctx, enterpriseID, poolID, param) + newPool, err := r.store.UpdateEntityPool(ctx, entity, poolID, param) if err != nil { return params.Pool{}, errors.Wrap(err, "updating pool") } @@ -312,8 +321,11 @@ func (r *Runner) ListEnterpriseInstances(ctx context.Context, enterpriseID strin if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - - instances, err := r.store.ListEnterpriseInstances(ctx, enterpriseID) + entity := params.GithubEntity{ + ID: enterpriseID, + EntityType: params.GithubEntityTypeEnterprise, + } + instances, err := r.store.ListEntityInstances(ctx, entity) if err != nil { return []params.Instance{}, errors.Wrap(err, "fetching instances") } diff --git a/runner/organizations.go b/runner/organizations.go index 258753f0..40847ccf 100644 --- a/runner/organizations.go +++ b/runner/organizations.go @@ -138,7 +138,12 @@ func (r *Runner) DeleteOrganization(ctx context.Context, orgID string, keepWebho return errors.Wrap(err, "fetching org") } - pools, err := r.store.ListOrgPools(ctx, orgID) + entity, err := org.GetEntity() + if err != nil { + return errors.Wrap(err, "getting entity") + } + + pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { return errors.Wrap(err, "fetching org pools") } @@ -300,8 +305,11 @@ func (r *Runner) ListOrgPools(ctx context.Context, orgID string) ([]params.Pool, if !auth.IsAdmin(ctx) { return []params.Pool{}, runnerErrors.ErrUnauthorized } - - pools, err := r.store.ListOrgPools(ctx, orgID) + entity := params.GithubEntity{ + ID: orgID, + EntityType: params.GithubEntityTypeOrganization, + } + pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { return nil, errors.Wrap(err, "fetching pools") } @@ -337,7 +345,7 @@ func (r *Runner) UpdateOrgPool(ctx context.Context, orgID, poolID string, param return params.Pool{}, runnerErrors.NewBadRequestError("min_idle_runners cannot be larger than max_runners") } - newPool, err := r.store.UpdateOrganizationPool(ctx, orgID, poolID, param) + newPool, err := r.store.UpdateEntityPool(ctx, entity, poolID, param) if err != nil { return params.Pool{}, errors.Wrap(err, "updating pool") } @@ -349,7 +357,12 @@ func (r *Runner) ListOrgInstances(ctx context.Context, orgID string) ([]params.I return nil, runnerErrors.ErrUnauthorized } - instances, err := r.store.ListOrgInstances(ctx, orgID) + entity := params.GithubEntity{ + ID: orgID, + EntityType: params.GithubEntityTypeOrganization, + } + + instances, err := r.store.ListEntityInstances(ctx, entity) if err != nil { return []params.Instance{}, errors.Wrap(err, "fetching instances") } diff --git a/runner/pool/pool.go b/runner/pool/pool.go index 5291be6b..bdfc0d3b 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -2173,28 +2173,11 @@ func (r *basePoolManager) GithubURL() string { } func (r *basePoolManager) FetchDbInstances() ([]params.Instance, error) { - switch r.entity.EntityType { - case params.GithubEntityTypeRepository: - return r.store.ListRepoInstances(r.ctx, r.entity.ID) - case params.GithubEntityTypeOrganization: - return r.store.ListOrgInstances(r.ctx, r.entity.ID) - case params.GithubEntityTypeEnterprise: - return r.store.ListEnterpriseInstances(r.ctx, r.entity.ID) - } - return nil, fmt.Errorf("unknown entity type: %s", r.entity.EntityType) + return r.store.ListEntityInstances(r.ctx, r.entity) } func (r *basePoolManager) ListPools() ([]params.Pool, error) { - switch r.entity.EntityType { - case params.GithubEntityTypeRepository: - return r.store.ListRepoPools(r.ctx, r.entity.ID) - case params.GithubEntityTypeOrganization: - return r.store.ListOrgPools(r.ctx, r.entity.ID) - case params.GithubEntityTypeEnterprise: - return r.store.ListEnterprisePools(r.ctx, r.entity.ID) - default: - return nil, fmt.Errorf("unknown entity type: %s", r.entity.EntityType) - } + return r.store.ListEntityPools(r.ctx, r.entity) } func (r *basePoolManager) GetPoolByID(poolID string) (params.Pool, error) { diff --git a/runner/pools.go b/runner/pools.go index 16194f65..aab423ff 100644 --- a/runner/pools.go +++ b/runner/pools.go @@ -16,7 +16,6 @@ package runner import ( "context" - "fmt" "github.com/pkg/errors" @@ -108,19 +107,12 @@ func (r *Runner) UpdatePoolByID(ctx context.Context, poolID string, param params param.Tags = newTags } - var newPool params.Pool - - switch { - case pool.RepoID != "": - newPool, err = r.store.UpdateRepositoryPool(ctx, pool.RepoID, poolID, param) - case pool.OrgID != "": - newPool, err = r.store.UpdateOrganizationPool(ctx, pool.OrgID, poolID, param) - case pool.EnterpriseID != "": - newPool, err = r.store.UpdateEnterprisePool(ctx, pool.EnterpriseID, poolID, param) - default: - return params.Pool{}, fmt.Errorf("pool not found to a repo, org or enterprise") + entity, err := pool.GithubEntity() + if err != nil { + return params.Pool{}, errors.Wrap(err, "getting entity") } + newPool, err := r.store.UpdateEntityPool(ctx, entity, poolID, param) if err != nil { return params.Pool{}, errors.Wrap(err, "updating pool") } diff --git a/runner/repositories.go b/runner/repositories.go index 68ef38f5..f7692b69 100644 --- a/runner/repositories.go +++ b/runner/repositories.go @@ -137,7 +137,12 @@ func (r *Runner) DeleteRepository(ctx context.Context, repoID string, keepWebhoo return errors.Wrap(err, "fetching repo") } - pools, err := r.store.ListRepoPools(ctx, repoID) + entity, err := repo.GetEntity() + if err != nil { + return errors.Wrap(err, "getting entity") + } + + pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { return errors.Wrap(err, "fetching repo pools") } @@ -295,8 +300,11 @@ func (r *Runner) ListRepoPools(ctx context.Context, repoID string) ([]params.Poo if !auth.IsAdmin(ctx) { return []params.Pool{}, runnerErrors.ErrUnauthorized } - - pools, err := r.store.ListRepoPools(ctx, repoID) + entity := params.GithubEntity{ + ID: repoID, + EntityType: params.GithubEntityTypeRepository, + } + pools, err := r.store.ListEntityPools(ctx, entity) if err != nil { return nil, errors.Wrap(err, "fetching pools") } @@ -343,7 +351,7 @@ func (r *Runner) UpdateRepoPool(ctx context.Context, repoID, poolID string, para return params.Pool{}, runnerErrors.NewBadRequestError("min_idle_runners cannot be larger than max_runners") } - newPool, err := r.store.UpdateRepositoryPool(ctx, repoID, poolID, param) + newPool, err := r.store.UpdateEntityPool(ctx, entity, poolID, param) if err != nil { return params.Pool{}, errors.Wrap(err, "updating pool") } @@ -354,8 +362,11 @@ func (r *Runner) ListRepoInstances(ctx context.Context, repoID string) ([]params if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - - instances, err := r.store.ListRepoInstances(ctx, repoID) + entity := params.GithubEntity{ + ID: repoID, + EntityType: params.GithubEntityTypeRepository, + } + instances, err := r.store.ListEntityInstances(ctx, entity) if err != nil { return []params.Instance{}, errors.Wrap(err, "fetching instances") }