From 6069191e14017140f443efb6b0372de0ff1e8fe0 Mon Sep 17 00:00:00 2001 From: Patrick Rice Date: Fri, 4 Aug 2023 02:08:40 +0000 Subject: [PATCH 1/4] Add runner service --- users.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ users_test.go | 23 ++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/users.go b/users.go index a77852ecf..15fa28cf7 100644 --- a/users.go +++ b/users.go @@ -1408,3 +1408,63 @@ func (s *UsersService) DisableTwoFactor(user int, options ...RequestOptionFunc) return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode) } } + +// CreateUserRunnerOptions represents the options available when creating a GitLab Runner +// using the new user-based flow. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/users.html#create-a-runner +type CreateUserRunnerOptions struct { + RunnerType string `json:"runner_type"` + GroupID int `json:"group_id"` + ProjectID int `json:"project_id"` + Description string `json:"description"` + Paused bool `json:"paused"` + Locked bool `json:"locked"` + RunUntagged bool `json:"run_untagged"` + TagList []string `json:"tag_list"` + AccessLevel string `json:"access_level"` + MaximumTimeout int `json:"maximum_timeout"` +} + +// UserRunner represents the a GitLab runner instance created using the user-based flow +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/users.html#create-a-runner +type UserRunner struct { + ID int `json:"id"` + Token string `json:"token"` + TokenExpiresAt string `json:"token_expires_at"` + RunnerType string `json:"runner_type"` + GroupID int `json:"group_id"` + ProjectID int `json:"project_id"` + Description string `json:"description"` + Paused bool `json:"paused"` + Locked bool `json:"locked"` + RunUntagged bool `json:"run_untagged"` + TagList []string `json:"tag_list"` + AccessLevel string `json:"access_level"` + MaximumTimeout int `json:"maximum_timeout"` +} + +// GetUserMemberships retrieves a list of the user's memberships. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/users.html#user-memberships +func (s *UsersService) CreateUserRunner(runnerOpts *CreateUserRunnerOptions, options ...RequestOptionFunc) (*UserRunner, *Response, error) { + // The user who owns the runner comes from the access token used to authorize the request. + u := "user/runners" + + req, err := s.client.NewRequest(http.MethodPost, u, runnerOpts, options) + if err != nil { + return nil, nil, err + } + + var r *UserRunner + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} diff --git a/users_test.go b/users_test.go index 565aacd04..ae65ac2d6 100644 --- a/users_test.go +++ b/users_test.go @@ -653,3 +653,26 @@ func TestDisableUser2FA(t *testing.T) { t.Errorf("Users.DisableTwoFactor returned error: %v", err) } } + +func TestCreateUserRunner(t *testing.T) { + mux, client := setup(t) + + path := fmt.Sprintf("/%suser/runners", apiVersionPath) + mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"id": 1234, "runner_type": "project_type"}`)) + }) + + createRunnerOpts := &CreateUserRunnerOptions{ + RunnerType: "project_type", + ProjectID: 1, + } + + response, _, err := client.Users.CreateUserRunner(createRunnerOpts) + if err != nil { + t.Errorf("Users.CreateUserRunner returned an error: %v", err) + } + + require.Equal(t, "project_type", response.RunnerType) +} From b5d9b2ed31e25eec0903aa6f6d53c70d9017aa05 Mon Sep 17 00:00:00 2001 From: Patrick Rice Date: Fri, 4 Aug 2023 02:08:40 +0000 Subject: [PATCH 2/4] Fix comments --- users.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/users.go b/users.go index 15fa28cf7..249d2ca93 100644 --- a/users.go +++ b/users.go @@ -1447,10 +1447,11 @@ type UserRunner struct { MaximumTimeout int `json:"maximum_timeout"` } -// GetUserMemberships retrieves a list of the user's memberships. +// CreateUserRunner creates a new runner using the user-based flow and returns the authentication +// token. // // GitLab API docs: -// https://docs.gitlab.com/ee/api/users.html#user-memberships +// https://docs.gitlab.com/ee/api/users.html#create-a-runner func (s *UsersService) CreateUserRunner(runnerOpts *CreateUserRunnerOptions, options ...RequestOptionFunc) (*UserRunner, *Response, error) { // The user who owns the runner comes from the access token used to authorize the request. u := "user/runners" From 25e4cfa498e04a3313d2337c6406ad1788bdb106 Mon Sep 17 00:00:00 2001 From: Patrick Rice Date: Fri, 4 Aug 2023 02:24:41 +0000 Subject: [PATCH 3/4] Update the response and options based on new API documentation and examples --- users.go | 37 ++++++++++++++----------------------- users_test.go | 6 ++++-- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/users.go b/users.go index 249d2ca93..b4745522a 100644 --- a/users.go +++ b/users.go @@ -1415,16 +1415,17 @@ func (s *UsersService) DisableTwoFactor(user int, options ...RequestOptionFunc) // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner type CreateUserRunnerOptions struct { - RunnerType string `json:"runner_type"` - GroupID int `json:"group_id"` - ProjectID int `json:"project_id"` - Description string `json:"description"` - Paused bool `json:"paused"` - Locked bool `json:"locked"` - RunUntagged bool `json:"run_untagged"` - TagList []string `json:"tag_list"` - AccessLevel string `json:"access_level"` - MaximumTimeout int `json:"maximum_timeout"` + RunnerType string `json:"runner_type"` + GroupID int `json:"group_id"` + ProjectID int `json:"project_id"` + Description string `json:"description"` + Paused bool `json:"paused"` + Locked bool `json:"locked"` + RunUntagged bool `json:"run_untagged"` + TagList []string `json:"tag_list"` + AccessLevel string `json:"access_level"` + MaximumTimeout int `json:"maximum_timeout"` + MaintenanceNote string `json:"maintenance_note"` } // UserRunner represents the a GitLab runner instance created using the user-based flow @@ -1432,19 +1433,9 @@ type CreateUserRunnerOptions struct { // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner type UserRunner struct { - ID int `json:"id"` - Token string `json:"token"` - TokenExpiresAt string `json:"token_expires_at"` - RunnerType string `json:"runner_type"` - GroupID int `json:"group_id"` - ProjectID int `json:"project_id"` - Description string `json:"description"` - Paused bool `json:"paused"` - Locked bool `json:"locked"` - RunUntagged bool `json:"run_untagged"` - TagList []string `json:"tag_list"` - AccessLevel string `json:"access_level"` - MaximumTimeout int `json:"maximum_timeout"` + ID int `json:"id"` + Token string `json:"token"` + TokenExpiresAt string `json:"token_expires_at"` } // CreateUserRunner creates a new runner using the user-based flow and returns the authentication diff --git a/users_test.go b/users_test.go index ae65ac2d6..7eb44a8fa 100644 --- a/users_test.go +++ b/users_test.go @@ -661,7 +661,7 @@ func TestCreateUserRunner(t *testing.T) { mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id": 1234, "runner_type": "project_type"}`)) + w.Write([]byte(`{"id": 1234, "token": "glrt-1234567890ABCD", "token_expires_at":null}`)) }) createRunnerOpts := &CreateUserRunnerOptions{ @@ -674,5 +674,7 @@ func TestCreateUserRunner(t *testing.T) { t.Errorf("Users.CreateUserRunner returned an error: %v", err) } - require.Equal(t, "project_type", response.RunnerType) + require.Equal(t, 1234, response.ID) + require.Equal(t, "glrt-1234567890ABCD", response.Token) + require.Equal(t, "", response.TokenExpiresAt) } From f4834ae192ae07c9f9b9621d3a95e7fe7049401a Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Fri, 4 Aug 2023 18:21:41 +0200 Subject: [PATCH 4/4] Bring the PR inline with the rest of the package --- users.go | 51 +++++++++++++++++++++++---------------------------- users_test.go | 16 ++++++++++------ 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/users.go b/users.go index b4745522a..d3eff7615 100644 --- a/users.go +++ b/users.go @@ -1409,51 +1409,46 @@ func (s *UsersService) DisableTwoFactor(user int, options ...RequestOptionFunc) } } -// CreateUserRunnerOptions represents the options available when creating a GitLab Runner -// using the new user-based flow. +// UserRunner represents a GitLab runner linked to the current user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner -type CreateUserRunnerOptions struct { - RunnerType string `json:"runner_type"` - GroupID int `json:"group_id"` - ProjectID int `json:"project_id"` - Description string `json:"description"` - Paused bool `json:"paused"` - Locked bool `json:"locked"` - RunUntagged bool `json:"run_untagged"` - TagList []string `json:"tag_list"` - AccessLevel string `json:"access_level"` - MaximumTimeout int `json:"maximum_timeout"` - MaintenanceNote string `json:"maintenance_note"` +type UserRunner struct { + ID int `json:"id"` + Token string `json:"token"` + TokenExpiresAt *time.Time `json:"token_expires_at"` } -// UserRunner represents the a GitLab runner instance created using the user-based flow +// CreateUserRunnerOptions represents the available CreateUserRunner() options. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner -type UserRunner struct { - ID int `json:"id"` - Token string `json:"token"` - TokenExpiresAt string `json:"token_expires_at"` +type CreateUserRunnerOptions struct { + RunnerType *string `url:"runner_type,omitempty" json:"runner_type,omitempty"` + GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"` + ProjectID *int `url:"project_id,omitempty" json:"project_id,omitempty"` + Description *string `url:"description,omitempty" json:"description,omitempty"` + Paused *bool `url:"paused,omitempty" json:"paused,omitempty"` + Locked *bool `url:"locked,omitempty" json:"locked,omitempty"` + RunUntagged *bool `url:"run_untagged,omitempty" json:"run_untagged,omitempty"` + TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"` + AccessLevel *string `url:"access_level,omitempty" json:"access_level,omitempty"` + MaximumTimeout *int `url:"maximum_timeout,omitempty" json:"maximum_timeout,omitempty"` + MaintenanceNote *string `url:"maintenance_note,omitempty" json:"maintenance_note,omitempty"` } -// CreateUserRunner creates a new runner using the user-based flow and returns the authentication -// token. +// CreateUserRunner creates a runner linked to the current user. // // GitLab API docs: // https://docs.gitlab.com/ee/api/users.html#create-a-runner -func (s *UsersService) CreateUserRunner(runnerOpts *CreateUserRunnerOptions, options ...RequestOptionFunc) (*UserRunner, *Response, error) { - // The user who owns the runner comes from the access token used to authorize the request. - u := "user/runners" - - req, err := s.client.NewRequest(http.MethodPost, u, runnerOpts, options) +func (s *UsersService) CreateUserRunner(opts *CreateUserRunnerOptions, options ...RequestOptionFunc) (*UserRunner, *Response, error) { + req, err := s.client.NewRequest(http.MethodPost, "user/runners", opts, options) if err != nil { return nil, nil, err } - var r *UserRunner - resp, err := s.client.Do(req, &r) + r := new(UserRunner) + resp, err := s.client.Do(req, r) if err != nil { return nil, resp, err } diff --git a/users_test.go b/users_test.go index 7eb44a8fa..4ee6887e4 100644 --- a/users_test.go +++ b/users_test.go @@ -616,8 +616,7 @@ func TestGetSingleSSHKeyForUser(t *testing.T) { "title": "Public key", "key": "ssh-rsa AAAA...", "created_at": "2014-08-01T14:47:39.080Z" - } -`) + }`) }) sshKey, _, err := client.Users.GetSSHKeyForUser(1, 1) @@ -661,12 +660,17 @@ func TestCreateUserRunner(t *testing.T) { mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id": 1234, "token": "glrt-1234567890ABCD", "token_expires_at":null}`)) + w.Write([]byte(` + { + "id": 1234, + "token": "glrt-1234567890ABCD", + "token_expires_at":null + }`)) }) createRunnerOpts := &CreateUserRunnerOptions{ - RunnerType: "project_type", - ProjectID: 1, + ProjectID: Int(1), + RunnerType: String("project_type"), } response, _, err := client.Users.CreateUserRunner(createRunnerOpts) @@ -676,5 +680,5 @@ func TestCreateUserRunner(t *testing.T) { require.Equal(t, 1234, response.ID) require.Equal(t, "glrt-1234567890ABCD", response.Token) - require.Equal(t, "", response.TokenExpiresAt) + require.Equal(t, (*time.Time)(nil), response.TokenExpiresAt) }