Repository: xanzy/go-gitlab
Pull request #1769 (2023-08-04 02:27:55)

Add new runner api support
This MR adds support for the new user-based Gitlab Runner API, documented here: https://docs.gitlab.com/ee/api/users.html#create-a-runner

It's worth noting that it's in the `users.go` file even though it's creating a runner, because it's in the `/user` api space. I could be convinced to move it to the `runners.go` file pretty easily, but figured the default stance was to align to the upstream API space.

It's also worth noting that there is no `/users/runner` DELETE api, because it uses the existing APIs within `runners.go` to delete by ID.

Let me know if you have any questions! I'd appreciate cutting a new version after merging this MR so that I can start working on the tf provider resource :)


Patch:
diff --git a/users.go b/users.go
index a77852e..d3eff76 100644
--- a/users.go
+++ b/users.go
@@ -1408,3 +1408,50 @@ func (s *UsersService) DisableTwoFactor(user int, options ...RequestOptionFunc)
 		return fmt.Errorf("Received unexpected result code: %d", resp.StatusCode)
 	}
 }
+
+// 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 UserRunner struct {
+	ID             int        `json:"id"`
+	Token          string     `json:"token"`
+	TokenExpiresAt *time.Time `json:"token_expires_at"`
+}
+
+// CreateUserRunnerOptions represents the available CreateUserRunner() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/users.html#create-a-runner
+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 runner linked to the current user.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/users.html#create-a-runner
+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
+	}
+
+	r := new(UserRunner)
+	resp, err := s.client.Do(req, r)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return r, resp, nil
+}


Test patch:
diff --git a/users_test.go b/users_test.go
index 565aacd..4ee6887 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)
@@ -653,3 +652,33 @@ 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,
+      "token": "glrt-1234567890ABCD",
+      "token_expires_at":null
+    }`))
+	})
+
+	createRunnerOpts := &CreateUserRunnerOptions{
+		ProjectID:  Int(1),
+		RunnerType: String("project_type"),
+	}
+
+	response, _, err := client.Users.CreateUserRunner(createRunnerOpts)
+	if err != nil {
+		t.Errorf("Users.CreateUserRunner returned an error: %v", err)
+	}
+
+	require.Equal(t, 1234, response.ID)
+	require.Equal(t, "glrt-1234567890ABCD", response.Token)
+	require.Equal(t, (*time.Time)(nil), response.TokenExpiresAt)
+}