From 4f74d0cd59cc0e5a3b1d64dd10a9cc90336a3699 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Mon, 19 Jun 2023 13:59:59 +0200 Subject: [PATCH] Add generate-jitconfig API for self-hosted runners (#2801) --- github/actions_runners.go | 54 ++++++++++++++++++++ github/actions_runners_test.go | 89 +++++++++++++++++++++++++++++++++ github/github-accessors.go | 16 ++++++ github/github-accessors_test.go | 20 ++++++++ 4 files changed, 179 insertions(+) diff --git a/github/actions_runners.go b/github/actions_runners.go index 40c6be3a92..3990a5a90f 100644 --- a/github/actions_runners.go +++ b/github/actions_runners.go @@ -45,6 +45,60 @@ func (s *ActionsService) ListRunnerApplicationDownloads(ctx context.Context, own return rads, resp, nil } +// GenerateJITConfigRequest specifies body parameters to GenerateRepoJITConfig. +type GenerateJITConfigRequest struct { + Name string `json:"name"` + RunnerGroupID int64 `json:"runner_group_id"` + WorkFolder *string `json:"work_folder,omitempty"` + + // Labels represents the names of the custom labels to add to the runner. + // Minimum items: 1. Maximum items: 100. + Labels []string `json:"labels"` +} + +// JITRunnerConfig represents encoded JIT configuration that can be used to bootstrap a self-hosted runner. +type JITRunnerConfig struct { + EncodedJITConfig *string `json:"encoded_jit_config,omitempty"` +} + +// GenerateOrgJITConfig generate a just-in-time configuration for an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-configuration-for-a-just-in-time-runner-for-an-organization +func (s *ActionsService) GenerateOrgJITConfig(ctx context.Context, owner string, request *GenerateJITConfigRequest) (*JITRunnerConfig, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/generate-jitconfig", owner) + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + jitConfig := new(JITRunnerConfig) + resp, err := s.client.Do(ctx, req, jitConfig) + if err != nil { + return nil, resp, err + } + + return jitConfig, resp, nil +} + +// GenerateRepoJITConfig generates a just-in-time configuration for a repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-configuration-for-a-just-in-time-runner-for-a-repository +func (s *ActionsService) GenerateRepoJITConfig(ctx context.Context, owner, repo string, request *GenerateJITConfigRequest) (*JITRunnerConfig, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/generate-jitconfig", owner, repo) + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + jitConfig := new(JITRunnerConfig) + resp, err := s.client.Do(ctx, req, jitConfig) + if err != nil { + return nil, resp, err + } + + return jitConfig, resp, nil +} + // RegistrationToken represents a token that can be used to add a self-hosted runner to a repository. type RegistrationToken struct { Token *string `json:"token,omitempty"` diff --git a/github/actions_runners_test.go b/github/actions_runners_test.go index 9b338641ea..8063152f93 100644 --- a/github/actions_runners_test.go +++ b/github/actions_runners_test.go @@ -7,6 +7,7 @@ package github import ( "context" + "encoding/json" "fmt" "net/http" "testing" @@ -56,6 +57,94 @@ func TestActionsService_ListRunnerApplicationDownloads(t *testing.T) { }) } +func TestActionsService_GenerateOrgJITConfig(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &GenerateJITConfigRequest{Name: "test", RunnerGroupID: 1, Labels: []string{"one", "two"}} + + mux.HandleFunc("/orgs/o/actions/runners/generate-jitconfig", func(w http.ResponseWriter, r *http.Request) { + v := new(GenerateJITConfigRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"encoded_jit_config":"foo"}`) + }) + + ctx := context.Background() + jitConfig, _, err := client.Actions.GenerateOrgJITConfig(ctx, "o", input) + if err != nil { + t.Errorf("Actions.GenerateOrgJITConfig returned error: %v", err) + } + + want := &JITRunnerConfig{EncodedJITConfig: String("foo")} + if !cmp.Equal(jitConfig, want) { + t.Errorf("Actions.GenerateOrgJITConfig returned %+v, want %+v", jitConfig, want) + } + + const methodName = "GenerateOrgJITConfig" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GenerateOrgJITConfig(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GenerateOrgJITConfig(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GenerateRepoJITConfig(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &GenerateJITConfigRequest{Name: "test", RunnerGroupID: 1, Labels: []string{"one", "two"}} + + mux.HandleFunc("/repos/o/r/actions/runners/generate-jitconfig", func(w http.ResponseWriter, r *http.Request) { + v := new(GenerateJITConfigRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `{"encoded_jit_config":"foo"}`) + }) + + ctx := context.Background() + jitConfig, _, err := client.Actions.GenerateRepoJITConfig(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.GenerateRepoJITConfig returned error: %v", err) + } + + want := &JITRunnerConfig{EncodedJITConfig: String("foo")} + if !cmp.Equal(jitConfig, want) { + t.Errorf("Actions.GenerateRepoJITConfig returned %+v, want %+v", jitConfig, want) + } + + const methodName = "GenerateRepoJITConfig" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GenerateRepoJITConfig(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GenerateRepoJITConfig(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestActionsService_CreateRegistrationToken(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/github/github-accessors.go b/github/github-accessors.go index 3504a87514..2f35ed7a90 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -6350,6 +6350,14 @@ func (f *ForkEvent) GetSender() *User { return f.Sender } +// GetWorkFolder returns the WorkFolder field if it's non-nil, zero value otherwise. +func (g *GenerateJITConfigRequest) GetWorkFolder() string { + if g == nil || g.WorkFolder == nil { + return "" + } + return *g.WorkFolder +} + // GetPreviousTagName returns the PreviousTagName field if it's non-nil, zero value otherwise. func (g *GenerateNotesOptions) GetPreviousTagName() string { if g == nil || g.PreviousTagName == nil { @@ -8974,6 +8982,14 @@ func (i *IssueStats) GetTotalIssues() int { return *i.TotalIssues } +// GetEncodedJITConfig returns the EncodedJITConfig field if it's non-nil, zero value otherwise. +func (j *JITRunnerConfig) GetEncodedJITConfig() string { + if j == nil || j.EncodedJITConfig == nil { + return "" + } + return *j.EncodedJITConfig +} + // GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. func (j *Jobs) GetTotalCount() int { if j == nil || j.TotalCount == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index f9950350fe..f83f47381f 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -7413,6 +7413,16 @@ func TestForkEvent_GetSender(tt *testing.T) { f.GetSender() } +func TestGenerateJITConfigRequest_GetWorkFolder(tt *testing.T) { + var zeroValue string + g := &GenerateJITConfigRequest{WorkFolder: &zeroValue} + g.GetWorkFolder() + g = &GenerateJITConfigRequest{} + g.GetWorkFolder() + g = nil + g.GetWorkFolder() +} + func TestGenerateNotesOptions_GetPreviousTagName(tt *testing.T) { var zeroValue string g := &GenerateNotesOptions{PreviousTagName: &zeroValue} @@ -10507,6 +10517,16 @@ func TestIssueStats_GetTotalIssues(tt *testing.T) { i.GetTotalIssues() } +func TestJITRunnerConfig_GetEncodedJITConfig(tt *testing.T) { + var zeroValue string + j := &JITRunnerConfig{EncodedJITConfig: &zeroValue} + j.GetEncodedJITConfig() + j = &JITRunnerConfig{} + j.GetEncodedJITConfig() + j = nil + j.GetEncodedJITConfig() +} + func TestJobs_GetTotalCount(tt *testing.T) { var zeroValue int j := &Jobs{TotalCount: &zeroValue}