diff --git a/apiserver/controllers/enterprises.go b/apiserver/controllers/enterprises.go index 9ce278cd..b4b3e528 100644 --- a/apiserver/controllers/enterprises.go +++ b/apiserver/controllers/enterprises.go @@ -66,13 +66,30 @@ func (a *APIController) CreateEnterpriseHandler(w http.ResponseWriter, r *http.R // // List all enterprises. // +// Parameters: +// + name: name +// description: Exact enterprise name to filter by +// type: string +// in: query +// required: false +// +// + name: endpoint +// description: Exact endpoint name to filter by +// type: string +// in: query +// required: false +// // Responses: // 200: Enterprises // default: APIErrorResponse func (a *APIController) ListEnterprisesHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - enterprise, err := a.r.ListEnterprises(ctx) + filter := runnerParams.EnterpriseFilter{ + Name: r.URL.Query().Get("name"), + Endpoint: r.URL.Query().Get("endpoint"), + } + enterprise, err := a.r.ListEnterprises(ctx, filter) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing enterprise") handleError(ctx, w, err) diff --git a/apiserver/controllers/organizations.go b/apiserver/controllers/organizations.go index 86f3c5d6..9089f440 100644 --- a/apiserver/controllers/organizations.go +++ b/apiserver/controllers/organizations.go @@ -67,13 +67,30 @@ func (a *APIController) CreateOrgHandler(w http.ResponseWriter, r *http.Request) // // List organizations. // +// Parameters: +// + name: name +// description: Exact organization name to filter by +// type: string +// in: query +// required: false +// +// + name: endpoint +// description: Exact endpoint name to filter by +// type: string +// in: query +// required: false +// // Responses: // 200: Organizations // default: APIErrorResponse func (a *APIController) ListOrgsHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - orgs, err := a.r.ListOrganizations(ctx) + filter := runnerParams.OrganizationFilter{ + Name: r.URL.Query().Get("name"), + Endpoint: r.URL.Query().Get("endpoint"), + } + orgs, err := a.r.ListOrganizations(ctx, filter) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing orgs") handleError(ctx, w, err) diff --git a/apiserver/controllers/repositories.go b/apiserver/controllers/repositories.go index 2eea0001..f3675790 100644 --- a/apiserver/controllers/repositories.go +++ b/apiserver/controllers/repositories.go @@ -67,13 +67,37 @@ func (a *APIController) CreateRepoHandler(w http.ResponseWriter, r *http.Request // // List repositories. // +// Parameters: +// + name: owner +// description: Exact owner name to filter by +// type: string +// in: query +// required: false +// +// + name: name +// description: Exact repository name to filter by +// type: string +// in: query +// required: false +// +// + name: endpoint +// description: Exact endpoint name to filter by +// type: string +// in: query +// required: false +// // Responses: // 200: Repositories // default: APIErrorResponse func (a *APIController) ListReposHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - repos, err := a.r.ListRepositories(ctx) + filter := runnerParams.RepositoryFilter{ + Name: r.URL.Query().Get("name"), + Owner: r.URL.Query().Get("owner"), + Endpoint: r.URL.Query().Get("endpoint"), + } + repos, err := a.r.ListRepositories(ctx, filter) if err != nil { slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing repositories") handleError(ctx, w, err) diff --git a/apiserver/swagger.yaml b/apiserver/swagger.yaml index 66e7a655..bf02a2d7 100644 --- a/apiserver/swagger.yaml +++ b/apiserver/swagger.yaml @@ -418,6 +418,15 @@ paths: /enterprises: get: operationId: ListEnterprises + parameters: + - description: Exact enterprise name to filter by + in: query + name: name + type: string + - description: Exact endpoint name to filter by + in: query + name: endpoint + type: string responses: "200": description: Enterprises @@ -1254,6 +1263,15 @@ paths: /organizations: get: operationId: ListOrgs + parameters: + - description: Exact organization name to filter by + in: query + name: name + type: string + - description: Exact endpoint name to filter by + in: query + name: endpoint + type: string responses: "200": description: Organizations @@ -1754,6 +1772,19 @@ paths: /repositories: get: operationId: ListRepos + parameters: + - description: Exact owner name to filter by + in: query + name: owner + type: string + - description: Exact repository name to filter by + in: query + name: name + type: string + - description: Exact endpoint name to filter by + in: query + name: endpoint + type: string responses: "200": description: Repositories diff --git a/client/enterprises/list_enterprises_parameters.go b/client/enterprises/list_enterprises_parameters.go index 83291c5f..44ba108b 100644 --- a/client/enterprises/list_enterprises_parameters.go +++ b/client/enterprises/list_enterprises_parameters.go @@ -60,6 +60,19 @@ ListEnterprisesParams contains all the parameters to send to the API endpoint Typically these are written to a http.Request. */ type ListEnterprisesParams struct { + + /* Endpoint. + + Exact endpoint name to filter by + */ + Endpoint *string + + /* Name. + + Exact enterprise name to filter by + */ + Name *string + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -113,6 +126,28 @@ func (o *ListEnterprisesParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithEndpoint adds the endpoint to the list enterprises params +func (o *ListEnterprisesParams) WithEndpoint(endpoint *string) *ListEnterprisesParams { + o.SetEndpoint(endpoint) + return o +} + +// SetEndpoint adds the endpoint to the list enterprises params +func (o *ListEnterprisesParams) SetEndpoint(endpoint *string) { + o.Endpoint = endpoint +} + +// WithName adds the name to the list enterprises params +func (o *ListEnterprisesParams) WithName(name *string) *ListEnterprisesParams { + o.SetName(name) + return o +} + +// SetName adds the name to the list enterprises params +func (o *ListEnterprisesParams) SetName(name *string) { + o.Name = name +} + // WriteToRequest writes these params to a swagger request func (o *ListEnterprisesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -121,6 +156,40 @@ func (o *ListEnterprisesParams) WriteToRequest(r runtime.ClientRequest, reg strf } var res []error + if o.Endpoint != nil { + + // query param endpoint + var qrEndpoint string + + if o.Endpoint != nil { + qrEndpoint = *o.Endpoint + } + qEndpoint := qrEndpoint + if qEndpoint != "" { + + if err := r.SetQueryParam("endpoint", qEndpoint); err != nil { + return err + } + } + } + + if o.Name != nil { + + // query param name + var qrName string + + if o.Name != nil { + qrName = *o.Name + } + qName := qrName + if qName != "" { + + if err := r.SetQueryParam("name", qName); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/client/organizations/list_orgs_parameters.go b/client/organizations/list_orgs_parameters.go index 1441722f..af4c19c8 100644 --- a/client/organizations/list_orgs_parameters.go +++ b/client/organizations/list_orgs_parameters.go @@ -60,6 +60,19 @@ ListOrgsParams contains all the parameters to send to the API endpoint Typically these are written to a http.Request. */ type ListOrgsParams struct { + + /* Endpoint. + + Exact endpoint name to filter by + */ + Endpoint *string + + /* Name. + + Exact organization name to filter by + */ + Name *string + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -113,6 +126,28 @@ func (o *ListOrgsParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithEndpoint adds the endpoint to the list orgs params +func (o *ListOrgsParams) WithEndpoint(endpoint *string) *ListOrgsParams { + o.SetEndpoint(endpoint) + return o +} + +// SetEndpoint adds the endpoint to the list orgs params +func (o *ListOrgsParams) SetEndpoint(endpoint *string) { + o.Endpoint = endpoint +} + +// WithName adds the name to the list orgs params +func (o *ListOrgsParams) WithName(name *string) *ListOrgsParams { + o.SetName(name) + return o +} + +// SetName adds the name to the list orgs params +func (o *ListOrgsParams) SetName(name *string) { + o.Name = name +} + // WriteToRequest writes these params to a swagger request func (o *ListOrgsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -121,6 +156,40 @@ func (o *ListOrgsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Regi } var res []error + if o.Endpoint != nil { + + // query param endpoint + var qrEndpoint string + + if o.Endpoint != nil { + qrEndpoint = *o.Endpoint + } + qEndpoint := qrEndpoint + if qEndpoint != "" { + + if err := r.SetQueryParam("endpoint", qEndpoint); err != nil { + return err + } + } + } + + if o.Name != nil { + + // query param name + var qrName string + + if o.Name != nil { + qrName = *o.Name + } + qName := qrName + if qName != "" { + + if err := r.SetQueryParam("name", qName); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/client/repositories/list_repos_parameters.go b/client/repositories/list_repos_parameters.go index f4e17d79..9998a1ba 100644 --- a/client/repositories/list_repos_parameters.go +++ b/client/repositories/list_repos_parameters.go @@ -60,6 +60,25 @@ ListReposParams contains all the parameters to send to the API endpoint Typically these are written to a http.Request. */ type ListReposParams struct { + + /* Endpoint. + + Exact endpoint name to filter by + */ + Endpoint *string + + /* Name. + + Exact repository name to filter by + */ + Name *string + + /* Owner. + + Exact owner name to filter by + */ + Owner *string + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -113,6 +132,39 @@ func (o *ListReposParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithEndpoint adds the endpoint to the list repos params +func (o *ListReposParams) WithEndpoint(endpoint *string) *ListReposParams { + o.SetEndpoint(endpoint) + return o +} + +// SetEndpoint adds the endpoint to the list repos params +func (o *ListReposParams) SetEndpoint(endpoint *string) { + o.Endpoint = endpoint +} + +// WithName adds the name to the list repos params +func (o *ListReposParams) WithName(name *string) *ListReposParams { + o.SetName(name) + return o +} + +// SetName adds the name to the list repos params +func (o *ListReposParams) SetName(name *string) { + o.Name = name +} + +// WithOwner adds the owner to the list repos params +func (o *ListReposParams) WithOwner(owner *string) *ListReposParams { + o.SetOwner(owner) + return o +} + +// SetOwner adds the owner to the list repos params +func (o *ListReposParams) SetOwner(owner *string) { + o.Owner = owner +} + // WriteToRequest writes these params to a swagger request func (o *ListReposParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -121,6 +173,57 @@ func (o *ListReposParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Reg } var res []error + if o.Endpoint != nil { + + // query param endpoint + var qrEndpoint string + + if o.Endpoint != nil { + qrEndpoint = *o.Endpoint + } + qEndpoint := qrEndpoint + if qEndpoint != "" { + + if err := r.SetQueryParam("endpoint", qEndpoint); err != nil { + return err + } + } + } + + if o.Name != nil { + + // query param name + var qrName string + + if o.Name != nil { + qrName = *o.Name + } + qName := qrName + if qName != "" { + + if err := r.SetQueryParam("name", qName); err != nil { + return err + } + } + } + + if o.Owner != nil { + + // query param owner + var qrOwner string + + if o.Owner != nil { + qrOwner = *o.Owner + } + qOwner := qrOwner + if qOwner != "" { + + if err := r.SetQueryParam("owner", qOwner); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/cmd/garm-cli/cmd/enterprise.go b/cmd/garm-cli/cmd/enterprise.go index 1e6c3930..b8850e1b 100644 --- a/cmd/garm-cli/cmd/enterprise.go +++ b/cmd/garm-cli/cmd/enterprise.go @@ -28,6 +28,7 @@ import ( var ( enterpriseName string + enterpriseEndpoint string enterpriseWebhookSecret string enterpriseCreds string ) @@ -85,6 +86,8 @@ var enterpriseListCmd = &cobra.Command{ } listEnterprisesReq := apiClientEnterprises.NewListEnterprisesParams() + listEnterprisesReq.Name = &enterpriseName + listEnterprisesReq.Endpoint = &enterpriseEndpoint response, err := apiCli.Enterprises.ListEnterprises(listEnterprisesReq, authToken) if err != nil { return err @@ -185,6 +188,8 @@ func init() { enterpriseAddCmd.Flags().StringVar(&poolBalancerType, "pool-balancer-type", string(params.PoolBalancerTypeRoundRobin), "The balancing strategy to use when creating runners in pools matching requested labels.") enterpriseListCmd.Flags().BoolVarP(&long, "long", "l", false, "Include additional info.") + enterpriseListCmd.Flags().StringVarP(&enterpriseName, "name", "n", "", "Exact enterprise name to filter by.") + enterpriseListCmd.Flags().StringVarP(&enterpriseEndpoint, "endpoint", "e", "", "Exact endpoint name to filter by.") enterpriseAddCmd.MarkFlagRequired("credentials") //nolint enterpriseAddCmd.MarkFlagRequired("name") //nolint diff --git a/cmd/garm-cli/cmd/organization.go b/cmd/garm-cli/cmd/organization.go index a95f912f..9f23888a 100644 --- a/cmd/garm-cli/cmd/organization.go +++ b/cmd/garm-cli/cmd/organization.go @@ -29,6 +29,7 @@ import ( var ( orgName string + orgEndpoint string orgWebhookSecret string orgCreds string orgRandomWebhookSecret bool @@ -243,6 +244,8 @@ var orgListCmd = &cobra.Command{ } listOrgsReq := apiClientOrgs.NewListOrgsParams() + listOrgsReq.Name = &orgName + listOrgsReq.Endpoint = &orgEndpoint response, err := apiCli.Organizations.ListOrgs(listOrgsReq, authToken) if err != nil { return err @@ -314,7 +317,10 @@ func init() { orgAddCmd.Flags().BoolVar(&installOrgWebhook, "install-webhook", false, "Install the webhook as part of the add operation.") orgAddCmd.MarkFlagsMutuallyExclusive("webhook-secret", "random-webhook-secret") orgAddCmd.MarkFlagsOneRequired("webhook-secret", "random-webhook-secret") + orgListCmd.Flags().BoolVarP(&long, "long", "l", false, "Include additional info.") + orgListCmd.Flags().StringVarP(&orgName, "name", "n", "", "Exact org name to filter by.") + orgListCmd.Flags().StringVarP(&orgEndpoint, "endpoint", "e", "", "Exact endpoint name to filter by.") orgAddCmd.MarkFlagRequired("credentials") //nolint orgAddCmd.MarkFlagRequired("name") //nolint diff --git a/cmd/garm-cli/cmd/repository.go b/cmd/garm-cli/cmd/repository.go index 5bf588c5..91db23ea 100644 --- a/cmd/garm-cli/cmd/repository.go +++ b/cmd/garm-cli/cmd/repository.go @@ -30,6 +30,7 @@ import ( var ( repoOwner string repoName string + repoEndpoint string repoWebhookSecret string repoCreds string forgeType string @@ -213,6 +214,9 @@ var repoListCmd = &cobra.Command{ } listReposReq := apiClientRepos.NewListReposParams() + listReposReq.Name = &repoName + listReposReq.Owner = &repoOwner + listReposReq.Endpoint = &repoEndpoint response, err := apiCli.Repositories.ListRepos(listReposReq, authToken) if err != nil { return err @@ -321,6 +325,9 @@ func init() { repoAddCmd.MarkFlagsOneRequired("webhook-secret", "random-webhook-secret") repoListCmd.Flags().BoolVarP(&long, "long", "l", false, "Include additional info.") + repoListCmd.Flags().StringVarP(&repoName, "name", "n", "", "Exact repo name to filter by.") + repoListCmd.Flags().StringVarP(&repoOwner, "owner", "o", "", "Exact repo owner to filter by.") + repoListCmd.Flags().StringVarP(&repoEndpoint, "endpoint", "e", "", "Exact endpoint name to filter by.") repoAddCmd.MarkFlagRequired("credentials") //nolint repoAddCmd.MarkFlagRequired("owner") //nolint diff --git a/database/common/mocks/Store.go b/database/common/mocks/Store.go index 97da1c06..ec107854 100644 --- a/database/common/mocks/Store.go +++ b/database/common/mocks/Store.go @@ -1524,9 +1524,9 @@ func (_m *Store) ListAllScaleSets(ctx context.Context) ([]params.ScaleSet, error 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) +// ListEnterprises provides a mock function with given fields: ctx, filter +func (_m *Store) ListEnterprises(ctx context.Context, filter params.EnterpriseFilter) ([]params.Enterprise, error) { + ret := _m.Called(ctx, filter) if len(ret) == 0 { panic("no return value specified for ListEnterprises") @@ -1534,19 +1534,19 @@ func (_m *Store) ListEnterprises(ctx context.Context) ([]params.Enterprise, erro var r0 []params.Enterprise var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]params.Enterprise, error)); ok { - return rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, params.EnterpriseFilter) ([]params.Enterprise, error)); ok { + return rf(ctx, filter) } - if rf, ok := ret.Get(0).(func(context.Context) []params.Enterprise); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, params.EnterpriseFilter) []params.Enterprise); ok { + r0 = rf(ctx, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]params.Enterprise) } } - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, params.EnterpriseFilter) error); ok { + r1 = rf(ctx, filter) } else { r1 = ret.Error(1) } @@ -1824,9 +1824,9 @@ func (_m *Store) ListJobsByStatus(ctx context.Context, status params.JobStatus) 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) +// ListOrganizations provides a mock function with given fields: ctx, filter +func (_m *Store) ListOrganizations(ctx context.Context, filter params.OrganizationFilter) ([]params.Organization, error) { + ret := _m.Called(ctx, filter) if len(ret) == 0 { panic("no return value specified for ListOrganizations") @@ -1834,19 +1834,19 @@ func (_m *Store) ListOrganizations(ctx context.Context) ([]params.Organization, var r0 []params.Organization var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]params.Organization, error)); ok { - return rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, params.OrganizationFilter) ([]params.Organization, error)); ok { + return rf(ctx, filter) } - if rf, ok := ret.Get(0).(func(context.Context) []params.Organization); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, params.OrganizationFilter) []params.Organization); ok { + r0 = rf(ctx, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]params.Organization) } } - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, params.OrganizationFilter) error); ok { + r1 = rf(ctx, filter) } else { r1 = ret.Error(1) } @@ -1884,9 +1884,9 @@ func (_m *Store) ListPoolInstances(ctx context.Context, poolID string) ([]params 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) +// ListRepositories provides a mock function with given fields: ctx, filter +func (_m *Store) ListRepositories(ctx context.Context, filter params.RepositoryFilter) ([]params.Repository, error) { + ret := _m.Called(ctx, filter) if len(ret) == 0 { panic("no return value specified for ListRepositories") @@ -1894,19 +1894,19 @@ func (_m *Store) ListRepositories(ctx context.Context) ([]params.Repository, err var r0 []params.Repository var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]params.Repository, error)); ok { - return rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, params.RepositoryFilter) ([]params.Repository, error)); ok { + return rf(ctx, filter) } - if rf, ok := ret.Get(0).(func(context.Context) []params.Repository); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, params.RepositoryFilter) []params.Repository); ok { + r0 = rf(ctx, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]params.Repository) } } - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, params.RepositoryFilter) error); ok { + r1 = rf(ctx, filter) } else { r1 = ret.Error(1) } diff --git a/database/common/store.go b/database/common/store.go index db5fbb04..8b3c4f7c 100644 --- a/database/common/store.go +++ b/database/common/store.go @@ -41,7 +41,7 @@ type RepoStore interface { CreateRepository(ctx context.Context, owner, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (param params.Repository, err error) GetRepository(ctx context.Context, owner, name, endpointName string) (params.Repository, error) GetRepositoryByID(ctx context.Context, repoID string) (params.Repository, error) - ListRepositories(ctx context.Context) ([]params.Repository, error) + ListRepositories(ctx context.Context, filter params.RepositoryFilter) ([]params.Repository, error) DeleteRepository(ctx context.Context, repoID string) error UpdateRepository(ctx context.Context, repoID string, param params.UpdateEntityParams) (params.Repository, error) } @@ -50,7 +50,7 @@ type OrgStore interface { CreateOrganization(ctx context.Context, name string, credentials params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (org params.Organization, err error) GetOrganization(ctx context.Context, name, endpointName string) (params.Organization, error) GetOrganizationByID(ctx context.Context, orgID string) (params.Organization, error) - ListOrganizations(ctx context.Context) ([]params.Organization, error) + ListOrganizations(ctx context.Context, filter params.OrganizationFilter) ([]params.Organization, error) DeleteOrganization(ctx context.Context, orgID string) error UpdateOrganization(ctx context.Context, orgID string, param params.UpdateEntityParams) (params.Organization, error) } @@ -59,7 +59,7 @@ type EnterpriseStore interface { CreateEnterprise(ctx context.Context, name string, credentialsName params.ForgeCredentials, webhookSecret string, poolBalancerType params.PoolBalancerType) (params.Enterprise, error) GetEnterprise(ctx context.Context, name, endpointName string) (params.Enterprise, error) GetEnterpriseByID(ctx context.Context, enterpriseID string) (params.Enterprise, error) - ListEnterprises(ctx context.Context) ([]params.Enterprise, error) + ListEnterprises(ctx context.Context, filter params.EnterpriseFilter) ([]params.Enterprise, error) DeleteEnterprise(ctx context.Context, enterpriseID string) error UpdateEnterprise(ctx context.Context, enterpriseID string, param params.UpdateEntityParams) (params.Enterprise, error) } diff --git a/database/sql/enterprise.go b/database/sql/enterprise.go index 41d95b26..fc273165 100644 --- a/database/sql/enterprise.go +++ b/database/sql/enterprise.go @@ -111,13 +111,19 @@ func (s *sqlDatabase) GetEnterpriseByID(ctx context.Context, enterpriseID string return param, nil } -func (s *sqlDatabase) ListEnterprises(_ context.Context) ([]params.Enterprise, error) { +func (s *sqlDatabase) ListEnterprises(_ context.Context, filter params.EnterpriseFilter) ([]params.Enterprise, error) { var enterprises []Enterprise q := s.conn. Preload("Credentials"). Preload("Credentials.Endpoint"). - Preload("Endpoint"). - Find(&enterprises) + Preload("Endpoint") + if filter.Name != "" { + q = q.Where("name = ?", filter.Name) + } + if filter.Endpoint != "" { + q = q.Where("endpoint_name = ?", filter.Endpoint) + } + q = q.Find(&enterprises) if q.Error != nil { return []params.Enterprise{}, errors.Wrap(q.Error, "fetching enterprises") } diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 79b298d5..056bb7fa 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -54,8 +54,10 @@ type EnterpriseTestSuite struct { adminUserID string testCreds params.ForgeCredentials + ghesCreds params.ForgeCredentials secondaryTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint + ghesEndpoint params.ForgeEndpoint } func (s *EnterpriseTestSuite) equalInstancesByName(expected, actual []params.Instance) { @@ -90,7 +92,9 @@ func (s *EnterpriseTestSuite) SetupTest() { s.Require().NotEmpty(s.adminUserID) s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.ghesEndpoint = garmTesting.CreateGHESEndpoint(adminCtx, db, s.T()) s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint) + s.ghesCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "ghes-creds", db, s.T(), s.ghesEndpoint) s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) // create some enterprise objects in the database, for testing purposes @@ -272,18 +276,68 @@ func (s *EnterpriseTestSuite) TestGetEnterpriseDBDecryptingErr() { } func (s *EnterpriseTestSuite) TestListEnterprises() { - enterprises, err := s.Store.ListEnterprises(s.adminCtx) + enterprises, err := s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{}) s.Require().Nil(err) garmTesting.EqualDBEntityByName(s.T(), s.Fixtures.Enterprises, enterprises) } +func (s *EnterpriseTestSuite) TestListEnterprisesWithFilter() { + enterprise, err := s.Store.CreateEnterprise( + s.adminCtx, + "test-enterprise", + s.ghesCreds, + "test-secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + enterprise2, err := s.Store.CreateEnterprise( + s.adminCtx, + "test-enterprise", + s.testCreds, + "test-secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + enterprise3, err := s.Store.CreateEnterprise( + s.adminCtx, + "test-enterprise2", + s.testCreds, + "test-secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + enterprises, err := s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{ + Name: "test-enterprise", + }) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise, enterprise2}, enterprises) + + enterprises, err = s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{ + Name: "test-enterprise", + Endpoint: s.ghesEndpoint.Name, + }) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise}, enterprises) + + enterprises, err = s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{ + Name: "test-enterprise2", + }) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise3}, enterprises) +} + func (s *EnterpriseTestSuite) TestListEnterprisesDBFetchErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE `enterprises`.`deleted_at` IS NULL")). WillReturnError(fmt.Errorf("fetching user from database mock error")) - _, err := s.StoreSQLMocked.ListEnterprises(s.adminCtx) + _, err := s.StoreSQLMocked.ListEnterprises(s.adminCtx, params.EnterpriseFilter{}) s.assertSQLMockExpectations() s.Require().NotNil(err) diff --git a/database/sql/organizations.go b/database/sql/organizations.go index 73456362..3b1a05fa 100644 --- a/database/sql/organizations.go +++ b/database/sql/organizations.go @@ -92,15 +92,23 @@ func (s *sqlDatabase) GetOrganization(ctx context.Context, name, endpointName st return param, nil } -func (s *sqlDatabase) ListOrganizations(_ context.Context) ([]params.Organization, error) { +func (s *sqlDatabase) ListOrganizations(_ context.Context, filter params.OrganizationFilter) ([]params.Organization, error) { var orgs []Organization q := s.conn. Preload("Credentials"). Preload("GiteaCredentials"). Preload("Credentials.Endpoint"). Preload("GiteaCredentials.Endpoint"). - Preload("Endpoint"). - Find(&orgs) + Preload("Endpoint") + + if filter.Name != "" { + q = q.Where("name = ?", filter.Name) + } + + if filter.Endpoint != "" { + q = q.Where("endpoint_name = ?", filter.Endpoint) + } + q = q.Find(&orgs) if q.Error != nil { return []params.Organization{}, errors.Wrap(q.Error, "fetching org from database") } diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index 5c053cec..df876ba1 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -333,18 +333,74 @@ func (s *OrgTestSuite) TestGetOrganizationDBDecryptingErr() { } func (s *OrgTestSuite) TestListOrganizations() { - orgs, err := s.Store.ListOrganizations(s.adminCtx) + orgs, err := s.Store.ListOrganizations(s.adminCtx, params.OrganizationFilter{}) s.Require().Nil(err) garmTesting.EqualDBEntityByName(s.T(), s.Fixtures.Orgs, orgs) } +func (s *OrgTestSuite) TestListOrganizationsWithFilters() { + org, err := s.Store.CreateOrganization( + s.adminCtx, + "test-org", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + org2, err := s.Store.CreateOrganization( + s.adminCtx, + "test-org", + s.testCredsGitea, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + org3, err := s.Store.CreateOrganization( + s.adminCtx, + "test-org2", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + orgs, err := s.Store.ListOrganizations( + s.adminCtx, + params.OrganizationFilter{ + Name: "test-org", + }) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org, org2}, orgs) + + orgs, err = s.Store.ListOrganizations( + s.adminCtx, + params.OrganizationFilter{ + Name: "test-org", + Endpoint: s.giteaEndpoint.Name, + }) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org2}, orgs) + + orgs, err = s.Store.ListOrganizations( + s.adminCtx, + params.OrganizationFilter{ + Name: "test-org2", + }) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org3}, orgs) +} + func (s *OrgTestSuite) TestListOrganizationsDBFetchErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE `organizations`.`deleted_at` IS NULL")). WillReturnError(fmt.Errorf("fetching user from database mock error")) - _, err := s.StoreSQLMocked.ListOrganizations(s.adminCtx) + _, err := s.StoreSQLMocked.ListOrganizations(s.adminCtx, params.OrganizationFilter{}) s.assertSQLMockExpectations() s.Require().NotNil(err) diff --git a/database/sql/repositories.go b/database/sql/repositories.go index 03452df6..a18eb001 100644 --- a/database/sql/repositories.go +++ b/database/sql/repositories.go @@ -93,15 +93,24 @@ func (s *sqlDatabase) GetRepository(ctx context.Context, owner, name, endpointNa return param, nil } -func (s *sqlDatabase) ListRepositories(_ context.Context) ([]params.Repository, error) { +func (s *sqlDatabase) ListRepositories(_ context.Context, filter params.RepositoryFilter) ([]params.Repository, error) { var repos []Repository q := s.conn. Preload("Credentials"). Preload("GiteaCredentials"). Preload("Credentials.Endpoint"). Preload("GiteaCredentials.Endpoint"). - Preload("Endpoint"). - Find(&repos) + Preload("Endpoint") + if filter.Owner != "" { + q = q.Where("owner = ?", filter.Owner) + } + if filter.Name != "" { + q = q.Where("name = ?", filter.Name) + } + if filter.Endpoint != "" { + q = q.Where("endpoint_name = ?", filter.Endpoint) + } + q = q.Find(&repos) if q.Error != nil { return []params.Repository{}, errors.Wrap(q.Error, "fetching user from database") } diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index f593ddce..4609a357 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -376,18 +376,99 @@ func (s *RepoTestSuite) TestGetRepositoryDBDecryptingErr() { } func (s *RepoTestSuite) TestListRepositories() { - repos, err := s.Store.ListRepositories(s.adminCtx) + repos, err := s.Store.ListRepositories(s.adminCtx, params.RepositoryFilter{}) s.Require().Nil(err) s.equalReposByName(s.Fixtures.Repos, repos) } +func (s *RepoTestSuite) TestListRepositoriesWithFilters() { + repo, err := s.Store.CreateRepository( + s.adminCtx, + "test-owner", + "test-repo", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + repo2, err := s.Store.CreateRepository( + s.adminCtx, + "test-owner", + "test-repo", + s.testCredsGitea, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + repo3, err := s.Store.CreateRepository( + s.adminCtx, + "test-owner", + "test-repo2", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + repo4, err := s.Store.CreateRepository( + s.adminCtx, + "test-owner2", + "test-repo", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + + repos, err := s.Store.ListRepositories( + s.adminCtx, + params.RepositoryFilter{ + Name: "test-repo", + }) + + s.Require().Nil(err) + s.equalReposByName([]params.Repository{repo, repo2, repo4}, repos) + + repos, err = s.Store.ListRepositories( + s.adminCtx, + params.RepositoryFilter{ + Name: "test-repo", + Owner: "test-owner", + }) + + s.Require().Nil(err) + s.equalReposByName([]params.Repository{repo, repo2}, repos) + + repos, err = s.Store.ListRepositories( + s.adminCtx, + params.RepositoryFilter{ + Name: "test-repo", + Owner: "test-owner", + Endpoint: s.giteaEndpoint.Name, + }) + + s.Require().Nil(err) + s.equalReposByName([]params.Repository{repo2}, repos) + + repos, err = s.Store.ListRepositories( + s.adminCtx, + params.RepositoryFilter{ + Name: "test-repo2", + }) + + s.Require().Nil(err) + s.equalReposByName([]params.Repository{repo3}, repos) +} + func (s *RepoTestSuite) TestListRepositoriesDBFetchErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE `repositories`.`deleted_at` IS NULL")). WillReturnError(fmt.Errorf("fetching user from database mock error")) - _, err := s.StoreSQLMocked.ListRepositories(s.adminCtx) + _, err := s.StoreSQLMocked.ListRepositories(s.adminCtx, params.RepositoryFilter{}) s.Require().NotNil(err) s.Require().Equal("fetching user from database: fetching user from database mock error", err.Error()) @@ -401,7 +482,7 @@ func (s *RepoTestSuite) TestListRepositoriesDBDecryptingErr() { ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE `repositories`.`deleted_at` IS NULL")). WillReturnRows(sqlmock.NewRows([]string{"id", "webhook_secret"}).AddRow(s.Fixtures.Repos[0].ID, s.Fixtures.Repos[0].WebhookSecret)) - _, err := s.StoreSQLMocked.ListRepositories(s.adminCtx) + _, err := s.StoreSQLMocked.ListRepositories(s.adminCtx, params.RepositoryFilter{}) s.Require().NotNil(err) s.Require().Equal("fetching repositories: decrypting secret: invalid passphrase length (expected length 32 characters)", err.Error()) diff --git a/internal/testing/testing.go b/internal/testing/testing.go index 84b4d48c..98bfd34c 100644 --- a/internal/testing/testing.go +++ b/internal/testing/testing.go @@ -85,6 +85,31 @@ func CreateGARMTestUser(ctx context.Context, username string, db common.Store, s return user } +func CreateGHESEndpoint(ctx context.Context, db common.Store, s *testing.T) params.ForgeEndpoint { + endpointParams := params.CreateGithubEndpointParams{ + Name: "ghes.example.com", + Description: "GHES endpoint", + APIBaseURL: "https://ghes.example.com", + UploadBaseURL: "https://upload.ghes.example.com/", + BaseURL: "https://ghes.example.com", + } + + ep, err := db.GetGithubEndpoint(ctx, endpointParams.Name) + if err != nil { + if !errors.Is(err, runnerErrors.ErrNotFound) { + s.Fatalf("failed to get database object (%s): %v", endpointParams.Name, err) + } + ep, err = db.CreateGithubEndpoint(ctx, endpointParams) + if err != nil { + if !errors.Is(err, runnerErrors.ErrDuplicateEntity) { + s.Fatalf("failed to create database object (%s): %v", endpointParams.Name, err) + } + } + } + + return ep +} + func CreateDefaultGithubEndpoint(ctx context.Context, db common.Store, s *testing.T) params.ForgeEndpoint { endpointParams := params.CreateGithubEndpointParams{ Name: "github.com", diff --git a/params/params.go b/params/params.go index 2a7fdef9..c9d4fb94 100644 --- a/params/params.go +++ b/params/params.go @@ -1192,3 +1192,19 @@ type ForgeEndpoint struct { EndpointType EndpointType `json:"endpoint_type,omitempty"` } + +type RepositoryFilter struct { + Owner string + Name string + Endpoint string +} + +type OrganizationFilter struct { + Name string + Endpoint string +} + +type EnterpriseFilter struct { + Name string + Endpoint string +} diff --git a/runner/enterprises.go b/runner/enterprises.go index f192c7cd..341cf5b9 100644 --- a/runner/enterprises.go +++ b/runner/enterprises.go @@ -86,12 +86,12 @@ func (r *Runner) CreateEnterprise(ctx context.Context, param params.CreateEnterp return enterprise, nil } -func (r *Runner) ListEnterprises(ctx context.Context) ([]params.Enterprise, error) { +func (r *Runner) ListEnterprises(ctx context.Context, filter params.EnterpriseFilter) ([]params.Enterprise, error) { if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - enterprises, err := r.store.ListEnterprises(ctx) + enterprises, err := r.store.ListEnterprises(ctx, filter) if err != nil { return nil, errors.Wrap(err, "listing enterprises") } diff --git a/runner/enterprises_test.go b/runner/enterprises_test.go index d5eef463..ce791e55 100644 --- a/runner/enterprises_test.go +++ b/runner/enterprises_test.go @@ -59,6 +59,8 @@ type EnterpriseTestSuite struct { testCreds params.ForgeCredentials secondaryTestCreds params.ForgeCredentials forgeEndpoint params.ForgeEndpoint + ghesEndpoint params.ForgeEndpoint + ghesCreds params.ForgeCredentials } func (s *EnterpriseTestSuite) SetupTest() { @@ -71,8 +73,10 @@ func (s *EnterpriseTestSuite) SetupTest() { adminCtx := garmTesting.ImpersonateAdminContext(context.Background(), db, s.T()) s.forgeEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.ghesEndpoint = garmTesting.CreateGHESEndpoint(adminCtx, db, s.T()) s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.forgeEndpoint) s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.forgeEndpoint) + s.ghesCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "ghes-creds", db, s.T(), s.ghesEndpoint) // create some organization objects in the database, for testing purposes enterprises := map[string]params.Enterprise{} @@ -224,14 +228,74 @@ func (s *EnterpriseTestSuite) TestCreateEnterpriseStartPoolMgrFailed() { func (s *EnterpriseTestSuite) TestListEnterprises() { s.Fixtures.PoolMgrCtrlMock.On("GetEnterprisePoolManager", mock.AnythingOfType("params.Enterprise")).Return(s.Fixtures.PoolMgrMock, nil) s.Fixtures.PoolMgrMock.On("Status").Return(params.PoolManagerStatus{IsRunning: true}, nil) - orgs, err := s.Runner.ListEnterprises(s.Fixtures.AdminContext) + orgs, err := s.Runner.ListEnterprises(s.Fixtures.AdminContext, params.EnterpriseFilter{}) s.Require().Nil(err) garmTesting.EqualDBEntityByName(s.T(), garmTesting.DBEntityMapToSlice(s.Fixtures.StoreEnterprises), orgs) } +func (s *EnterpriseTestSuite) TestListEnterprisesWithFilters() { + s.Fixtures.PoolMgrCtrlMock.On("GetEnterprisePoolManager", mock.AnythingOfType("params.Enterprise")).Return(s.Fixtures.PoolMgrMock, nil) + s.Fixtures.PoolMgrMock.On("Status").Return(params.PoolManagerStatus{IsRunning: true}, nil) + + enterprise, err := s.Fixtures.Store.CreateEnterprise( + s.Fixtures.AdminContext, + "test-enterprise", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + enterprise2, err := s.Fixtures.Store.CreateEnterprise( + s.Fixtures.AdminContext, + "test-enterprise2", + s.testCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + enterprise3, err := s.Fixtures.Store.CreateEnterprise( + s.Fixtures.AdminContext, + "test-enterprise", + s.ghesCreds, + "super secret", + params.PoolBalancerTypeRoundRobin, + ) + s.Require().NoError(err) + orgs, err := s.Runner.ListEnterprises( + s.Fixtures.AdminContext, + params.EnterpriseFilter{ + Name: "test-enterprise", + }, + ) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise, enterprise3}, orgs) + + orgs, err = s.Runner.ListEnterprises( + s.Fixtures.AdminContext, + params.EnterpriseFilter{ + Name: "test-enterprise", + Endpoint: s.ghesEndpoint.Name, + }, + ) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise3}, orgs) + + orgs, err = s.Runner.ListEnterprises( + s.Fixtures.AdminContext, + params.EnterpriseFilter{ + Name: "test-enterprise2", + }, + ) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise2}, orgs) +} + func (s *EnterpriseTestSuite) TestListEnterprisesErrUnauthorized() { - _, err := s.Runner.ListEnterprises(context.Background()) + _, err := s.Runner.ListEnterprises(context.Background(), params.EnterpriseFilter{}) s.Require().Equal(runnerErrors.ErrUnauthorized, err) } diff --git a/runner/metrics/enterprise.go b/runner/metrics/enterprise.go index 3ab9003c..be6eba66 100644 --- a/runner/metrics/enterprise.go +++ b/runner/metrics/enterprise.go @@ -19,6 +19,7 @@ import ( "strconv" "github.com/cloudbase/garm/metrics" + "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner" //nolint:typecheck ) @@ -28,7 +29,7 @@ func CollectEnterpriseMetric(ctx context.Context, r *runner.Runner) error { metrics.EnterpriseInfo.Reset() metrics.EnterprisePoolManagerStatus.Reset() - enterprises, err := r.ListEnterprises(ctx) + enterprises, err := r.ListEnterprises(ctx, params.EnterpriseFilter{}) if err != nil { return err } diff --git a/runner/metrics/organization.go b/runner/metrics/organization.go index 3716cca1..6bf6d9e5 100644 --- a/runner/metrics/organization.go +++ b/runner/metrics/organization.go @@ -19,6 +19,7 @@ import ( "strconv" "github.com/cloudbase/garm/metrics" + "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner" ) @@ -28,7 +29,7 @@ func CollectOrganizationMetric(ctx context.Context, r *runner.Runner) error { metrics.OrganizationInfo.Reset() metrics.OrganizationPoolManagerStatus.Reset() - organizations, err := r.ListOrganizations(ctx) + organizations, err := r.ListOrganizations(ctx, params.OrganizationFilter{}) if err != nil { return err } diff --git a/runner/metrics/repository.go b/runner/metrics/repository.go index 36e07bf0..a2e8fa57 100644 --- a/runner/metrics/repository.go +++ b/runner/metrics/repository.go @@ -19,6 +19,7 @@ import ( "strconv" "github.com/cloudbase/garm/metrics" + "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner" ) @@ -27,7 +28,7 @@ func CollectRepositoryMetric(ctx context.Context, r *runner.Runner) error { metrics.EnterpriseInfo.Reset() metrics.EnterprisePoolManagerStatus.Reset() - repositories, err := r.ListRepositories(ctx) + repositories, err := r.ListRepositories(ctx, params.RepositoryFilter{}) if err != nil { return err } diff --git a/runner/organizations.go b/runner/organizations.go index 26d4f6e9..0ec4bfa2 100644 --- a/runner/organizations.go +++ b/runner/organizations.go @@ -95,12 +95,12 @@ func (r *Runner) CreateOrganization(ctx context.Context, param params.CreateOrgP return org, nil } -func (r *Runner) ListOrganizations(ctx context.Context) ([]params.Organization, error) { +func (r *Runner) ListOrganizations(ctx context.Context, filter params.OrganizationFilter) ([]params.Organization, error) { if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - orgs, err := r.store.ListOrganizations(ctx) + orgs, err := r.store.ListOrganizations(ctx, filter) if err != nil { return nil, errors.Wrap(err, "listing organizations") } diff --git a/runner/organizations_test.go b/runner/organizations_test.go index 90075c87..9de6d2b4 100644 --- a/runner/organizations_test.go +++ b/runner/organizations_test.go @@ -58,7 +58,9 @@ type OrgTestSuite struct { testCreds params.ForgeCredentials secondaryTestCreds params.ForgeCredentials + giteaTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint + giteaEndpoint params.ForgeEndpoint } func (s *OrgTestSuite) SetupTest() { @@ -72,7 +74,9 @@ func (s *OrgTestSuite) SetupTest() { adminCtx := garmTesting.ImpersonateAdminContext(context.Background(), db, s.T()) s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.giteaEndpoint = garmTesting.CreateDefaultGiteaEndpoint(adminCtx, db, s.T()) s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint) + s.giteaTestCreds = garmTesting.CreateTestGiteaCredentials(adminCtx, "gitea-creds", db, s.T(), s.giteaEndpoint) s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) // create some organization objects in the database, for testing purposes @@ -238,14 +242,74 @@ func (s *OrgTestSuite) TestCreateOrganizationStartPoolMgrFailed() { func (s *OrgTestSuite) TestListOrganizations() { s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) s.Fixtures.PoolMgrMock.On("Status").Return(params.PoolManagerStatus{IsRunning: true}, nil) - orgs, err := s.Runner.ListOrganizations(s.Fixtures.AdminContext) + orgs, err := s.Runner.ListOrganizations(s.Fixtures.AdminContext, params.OrganizationFilter{}) s.Require().Nil(err) garmTesting.EqualDBEntityByName(s.T(), garmTesting.DBEntityMapToSlice(s.Fixtures.StoreOrgs), orgs) } +func (s *OrgTestSuite) TestListOrganizationsWithFilter() { + s.Fixtures.PoolMgrCtrlMock.On("GetOrgPoolManager", mock.AnythingOfType("params.Organization")).Return(s.Fixtures.PoolMgrMock, nil) + s.Fixtures.PoolMgrMock.On("Status").Return(params.PoolManagerStatus{IsRunning: true}, nil) + + org, err := s.Fixtures.Store.CreateOrganization( + s.Fixtures.AdminContext, + "test-org", + s.testCreds, + "super-secret", + params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + + org2, err := s.Fixtures.Store.CreateOrganization( + s.Fixtures.AdminContext, + "test-org", + s.giteaTestCreds, + "super-secret", + params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + + org3, err := s.Fixtures.Store.CreateOrganization( + s.Fixtures.AdminContext, + "test-org2", + s.giteaTestCreds, + "super-secret", + params.PoolBalancerTypeRoundRobin) + s.Require().NoError(err) + + orgs, err := s.Runner.ListOrganizations( + s.Fixtures.AdminContext, + params.OrganizationFilter{ + Name: "test-org", + }, + ) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org, org2}, orgs) + + orgs, err = s.Runner.ListOrganizations( + s.Fixtures.AdminContext, + params.OrganizationFilter{ + Name: "test-org", + Endpoint: s.giteaEndpoint.Name, + }, + ) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org2}, orgs) + + orgs, err = s.Runner.ListOrganizations( + s.Fixtures.AdminContext, + params.OrganizationFilter{ + Name: "test-org2", + }, + ) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org3}, orgs) +} + func (s *OrgTestSuite) TestListOrganizationsErrUnauthorized() { - _, err := s.Runner.ListOrganizations(context.Background()) + _, err := s.Runner.ListOrganizations(context.Background(), params.OrganizationFilter{}) s.Require().Equal(runnerErrors.ErrUnauthorized, err) } diff --git a/runner/repositories.go b/runner/repositories.go index d5118e96..24beaa07 100644 --- a/runner/repositories.go +++ b/runner/repositories.go @@ -93,12 +93,12 @@ func (r *Runner) CreateRepository(ctx context.Context, param params.CreateRepoPa return repo, nil } -func (r *Runner) ListRepositories(ctx context.Context) ([]params.Repository, error) { +func (r *Runner) ListRepositories(ctx context.Context, filter params.RepositoryFilter) ([]params.Repository, error) { if !auth.IsAdmin(ctx) { return nil, runnerErrors.ErrUnauthorized } - repos, err := r.store.ListRepositories(ctx) + repos, err := r.store.ListRepositories(ctx, filter) if err != nil { return nil, errors.Wrap(err, "listing repositories") } diff --git a/runner/repositories_test.go b/runner/repositories_test.go index 0adf40d7..53fe5869 100644 --- a/runner/repositories_test.go +++ b/runner/repositories_test.go @@ -62,7 +62,9 @@ type RepoTestSuite struct { testCreds params.ForgeCredentials secondaryTestCreds params.ForgeCredentials + giteaTestCreds params.ForgeCredentials githubEndpoint params.ForgeEndpoint + giteaEndpoint params.ForgeEndpoint } func (s *RepoTestSuite) SetupTest() { @@ -75,8 +77,10 @@ func (s *RepoTestSuite) SetupTest() { adminCtx := garmTesting.ImpersonateAdminContext(context.Background(), db, s.T()) s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T()) + s.giteaEndpoint = garmTesting.CreateDefaultGiteaEndpoint(adminCtx, db, s.T()) s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint) s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint) + s.giteaTestCreds = garmTesting.CreateTestGiteaCredentials(adminCtx, "gitea-creds", db, s.T(), s.giteaEndpoint) // create some repository objects in the database, for testing purposes repos := map[string]params.Repository{} @@ -254,14 +258,81 @@ func (s *RepoTestSuite) TestCreateRepositoryStartPoolMgrFailed() { func (s *RepoTestSuite) TestListRepositories() { s.Fixtures.PoolMgrCtrlMock.On("GetRepoPoolManager", mock.AnythingOfType("params.Repository")).Return(s.Fixtures.PoolMgrMock, nil) s.Fixtures.PoolMgrMock.On("Status").Return(params.PoolManagerStatus{IsRunning: true}, nil) - repos, err := s.Runner.ListRepositories(s.Fixtures.AdminContext) + repos, err := s.Runner.ListRepositories(s.Fixtures.AdminContext, params.RepositoryFilter{}) s.Require().Nil(err) garmTesting.EqualDBEntityByName(s.T(), garmTesting.DBEntityMapToSlice(s.Fixtures.StoreRepos), repos) } +func (s *RepoTestSuite) TestListRepositoriesWithFilters() { + s.Fixtures.PoolMgrCtrlMock.On("GetRepoPoolManager", mock.AnythingOfType("params.Repository")).Return(s.Fixtures.PoolMgrMock, nil) + s.Fixtures.PoolMgrMock.On("Status").Return(params.PoolManagerStatus{IsRunning: true}, nil) + + repo, err := s.Fixtures.Store.CreateRepository( + s.Fixtures.AdminContext, + "example-owner", + "example-repo", + s.testCreds, + "test-webhook-secret", + params.PoolBalancerTypeRoundRobin, + ) + if err != nil { + s.FailNow(fmt.Sprintf("failed to create database object (example-repo): %q", err)) + } + + repo2, err := s.Fixtures.Store.CreateRepository( + s.Fixtures.AdminContext, + "another-example-owner", + "example-repo", + s.testCreds, + "test-webhook-secret", + params.PoolBalancerTypeRoundRobin, + ) + if err != nil { + s.FailNow(fmt.Sprintf("failed to create database object (example-repo): %q", err)) + } + + repo3, err := s.Fixtures.Store.CreateRepository( + s.Fixtures.AdminContext, + "example-owner", + "example-repo", + s.giteaTestCreds, + "test-webhook-secret", + params.PoolBalancerTypeRoundRobin, + ) + if err != nil { + s.FailNow(fmt.Sprintf("failed to create database object (example-repo): %q", err)) + } + + repos, err := s.Runner.ListRepositories(s.Fixtures.AdminContext, params.RepositoryFilter{Name: "example-repo"}) + + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Repository{repo, repo2, repo3}, repos) + + repos, err = s.Runner.ListRepositories( + s.Fixtures.AdminContext, + params.RepositoryFilter{ + Name: "example-repo", + Owner: "example-owner", + }, + ) + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Repository{repo, repo3}, repos) + + repos, err = s.Runner.ListRepositories( + s.Fixtures.AdminContext, + params.RepositoryFilter{ + Name: "example-repo", + Owner: "example-owner", + Endpoint: s.giteaEndpoint.Name, + }, + ) + s.Require().Nil(err) + garmTesting.EqualDBEntityByName(s.T(), []params.Repository{repo3}, repos) +} + func (s *RepoTestSuite) TestListRepositoriesErrUnauthorized() { - _, err := s.Runner.ListRepositories(context.Background()) + _, err := s.Runner.ListRepositories(context.Background(), params.RepositoryFilter{}) s.Require().Equal(runnerErrors.ErrUnauthorized, err) } diff --git a/runner/runner.go b/runner/runner.go index aa55ee4f..da3f35ea 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -327,17 +327,17 @@ func (r *Runner) loadReposOrgsAndEnterprises() error { r.mux.Lock() defer r.mux.Unlock() - repos, err := r.store.ListRepositories(r.ctx) + repos, err := r.store.ListRepositories(r.ctx, params.RepositoryFilter{}) if err != nil { return errors.Wrap(err, "fetching repositories") } - orgs, err := r.store.ListOrganizations(r.ctx) + orgs, err := r.store.ListOrganizations(r.ctx, params.OrganizationFilter{}) if err != nil { return errors.Wrap(err, "fetching organizations") } - enterprises, err := r.store.ListEnterprises(r.ctx) + enterprises, err := r.store.ListEnterprises(r.ctx, params.EnterpriseFilter{}) if err != nil { return errors.Wrap(err, "fetching enterprises") } diff --git a/workers/cache/cache.go b/workers/cache/cache.go index a00c7667..3f589edd 100644 --- a/workers/cache/cache.go +++ b/workers/cache/cache.go @@ -96,17 +96,17 @@ func (w *Worker) loadAllEntities() error { return fmt.Errorf("listing scale sets: %w", err) } - repos, err := w.store.ListRepositories(w.ctx) + repos, err := w.store.ListRepositories(w.ctx, params.RepositoryFilter{}) if err != nil { return fmt.Errorf("listing repositories: %w", err) } - orgs, err := w.store.ListOrganizations(w.ctx) + orgs, err := w.store.ListOrganizations(w.ctx, params.OrganizationFilter{}) if err != nil { return fmt.Errorf("listing organizations: %w", err) } - enterprises, err := w.store.ListEnterprises(w.ctx) + enterprises, err := w.store.ListEnterprises(w.ctx, params.EnterpriseFilter{}) if err != nil { return fmt.Errorf("listing enterprises: %w", err) } diff --git a/workers/entity/controller.go b/workers/entity/controller.go index 99618194..3ad52108 100644 --- a/workers/entity/controller.go +++ b/workers/entity/controller.go @@ -24,6 +24,7 @@ import ( "github.com/cloudbase/garm/auth" dbCommon "github.com/cloudbase/garm/database/common" "github.com/cloudbase/garm/database/watcher" + "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" garmUtil "github.com/cloudbase/garm/util" ) @@ -63,7 +64,7 @@ type Controller struct { func (c *Controller) loadAllRepositories() error { c.mux.Lock() defer c.mux.Unlock() - repos, err := c.store.ListRepositories(c.ctx) + repos, err := c.store.ListRepositories(c.ctx, params.RepositoryFilter{}) if err != nil { return fmt.Errorf("fetching repositories: %w", err) } @@ -95,7 +96,7 @@ func (c *Controller) loadAllRepositories() error { func (c *Controller) loadAllOrganizations() error { c.mux.Lock() defer c.mux.Unlock() - orgs, err := c.store.ListOrganizations(c.ctx) + orgs, err := c.store.ListOrganizations(c.ctx, params.OrganizationFilter{}) if err != nil { return fmt.Errorf("fetching organizations: %w", err) } @@ -127,7 +128,7 @@ func (c *Controller) loadAllOrganizations() error { func (c *Controller) loadAllEnterprises() error { c.mux.Lock() defer c.mux.Unlock() - enterprises, err := c.store.ListEnterprises(c.ctx) + enterprises, err := c.store.ListEnterprises(c.ctx, params.EnterpriseFilter{}) if err != nil { return fmt.Errorf("fetching enterprises: %w", err) }