95 lines
2.8 KiB
Go
95 lines
2.8 KiB
Go
// Copyright 2024 Cloudbase Solutions SRL
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package scalesets
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"github.com/google/go-github/v71/github"
|
|
|
|
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
|
"github.com/cloudbase/garm/params"
|
|
"github.com/cloudbase/garm/runner/common"
|
|
)
|
|
|
|
func NewClient(cli common.GithubClient) (*ScaleSetClient, error) {
|
|
return &ScaleSetClient{
|
|
ghCli: cli,
|
|
httpClient: &http.Client{},
|
|
}, nil
|
|
}
|
|
|
|
type ScaleSetClient struct {
|
|
ghCli common.GithubClient
|
|
httpClient *http.Client
|
|
|
|
// scale sets are aparently available through the same security
|
|
// contex that a normal runner would use. We connect to the same
|
|
// API endpoint a runner would connect to, in order to fetch jobs.
|
|
// To do this, we use a runner registration token.
|
|
runnerRegistrationToken *github.RegistrationToken
|
|
// actionsServiceInfo holds the pipeline URL and the JWT token to
|
|
// access it. The pipeline URL is the base URL where we can access
|
|
// the scale set endpoints.
|
|
actionsServiceInfo *params.ActionsServiceAdminInfoResponse
|
|
|
|
mux sync.Mutex
|
|
}
|
|
|
|
func (s *ScaleSetClient) SetGithubClient(cli common.GithubClient) {
|
|
s.mux.Lock()
|
|
defer s.mux.Unlock()
|
|
s.ghCli = cli
|
|
}
|
|
|
|
func (s *ScaleSetClient) Do(req *http.Request) (*http.Response, error) {
|
|
if s.httpClient == nil {
|
|
return nil, fmt.Errorf("http client is not initialized")
|
|
}
|
|
|
|
resp, err := s.httpClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to dispatch HTTP request: %w", err)
|
|
}
|
|
|
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
|
return resp, nil
|
|
}
|
|
|
|
var body []byte
|
|
if resp != nil {
|
|
defer resp.Body.Close()
|
|
body, err = io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read body: %w", err)
|
|
}
|
|
}
|
|
|
|
switch resp.StatusCode {
|
|
case 404:
|
|
return nil, runnerErrors.NewNotFoundError("resource %s not found: %q", req.URL.String(), string(body))
|
|
case 400:
|
|
return nil, runnerErrors.NewBadRequestError("bad request while calling %s: %q", req.URL.String(), string(body))
|
|
case 409:
|
|
return nil, runnerErrors.NewConflictError("conflict while calling %s: %q", req.URL.String(), string(body))
|
|
case 401, 403:
|
|
return nil, runnerErrors.ErrUnauthorized
|
|
default:
|
|
return nil, fmt.Errorf("request to %s failed with status code %d: %q", req.URL.String(), resp.StatusCode, string(body))
|
|
}
|
|
}
|