// 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 ( "bytes" "context" "encoding/json" "fmt" "net/http" runnerErrors "github.com/cloudbase/garm-provider-common/errors" "github.com/cloudbase/garm/params" ) type scaleSetJitRunnerConfig struct { Name string `json:"name"` WorkFolder string `json:"workFolder"` } func (s *ScaleSetClient) GenerateJitRunnerConfig(ctx context.Context, runnerName string, scaleSetID int) (params.RunnerScaleSetJitRunnerConfig, error) { runnerSettings := scaleSetJitRunnerConfig{ Name: runnerName, WorkFolder: "_work", } body, err := json.Marshal(runnerSettings) if err != nil { return params.RunnerScaleSetJitRunnerConfig{}, err } if err := s.ensureAdminInfo(ctx); err != nil { return params.RunnerScaleSetJitRunnerConfig{}, fmt.Errorf("failed to ensure admin info: %w", err) } jitConfigPath := fmt.Sprintf("%s/%d/generatejitconfig", scaleSetEndpoint, scaleSetID) req, err := s.newActionsRequest(ctx, http.MethodPost, jitConfigPath, bytes.NewBuffer(body)) if err != nil { return params.RunnerScaleSetJitRunnerConfig{}, fmt.Errorf("failed to create request: %w", err) } resp, err := s.Do(req) if err != nil { return params.RunnerScaleSetJitRunnerConfig{}, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) } defer resp.Body.Close() var runnerJitConfig params.RunnerScaleSetJitRunnerConfig if err := json.NewDecoder(resp.Body).Decode(&runnerJitConfig); err != nil { return params.RunnerScaleSetJitRunnerConfig{}, fmt.Errorf("failed to decode response: %w", err) } return runnerJitConfig, nil } func (s *ScaleSetClient) GetRunner(ctx context.Context, runnerID int64) (params.RunnerReference, error) { path := fmt.Sprintf("%s/%d", runnerEndpoint, runnerID) req, err := s.newActionsRequest(ctx, http.MethodGet, path, nil) if err != nil { return params.RunnerReference{}, fmt.Errorf("failed to construct request: %w", err) } resp, err := s.Do(req) if err != nil { return params.RunnerReference{}, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) } defer resp.Body.Close() var runnerReference params.RunnerReference if err := json.NewDecoder(resp.Body).Decode(&runnerReference); err != nil { return params.RunnerReference{}, fmt.Errorf("failed to decode response: %w", err) } return runnerReference, nil } func (s *ScaleSetClient) ListAllRunners(ctx context.Context) (params.RunnerReferenceList, error) { req, err := s.newActionsRequest(ctx, http.MethodGet, runnerEndpoint, nil) if err != nil { return params.RunnerReferenceList{}, fmt.Errorf("failed to construct request: %w", err) } resp, err := s.Do(req) if err != nil { return params.RunnerReferenceList{}, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) } defer resp.Body.Close() var runnerList params.RunnerReferenceList if err := json.NewDecoder(resp.Body).Decode(&runnerList); err != nil { return params.RunnerReferenceList{}, fmt.Errorf("failed to decode response: %w", err) } return runnerList, nil } func (s *ScaleSetClient) GetRunnerByName(ctx context.Context, runnerName string) (params.RunnerReference, error) { path := fmt.Sprintf("%s?agentName=%s", runnerEndpoint, runnerName) req, err := s.newActionsRequest(ctx, http.MethodGet, path, nil) if err != nil { return params.RunnerReference{}, fmt.Errorf("failed to construct request: %w", err) } resp, err := s.Do(req) if err != nil { return params.RunnerReference{}, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) } defer resp.Body.Close() var runnerList params.RunnerReferenceList if err := json.NewDecoder(resp.Body).Decode(&runnerList); err != nil { return params.RunnerReference{}, fmt.Errorf("failed to decode response: %w", err) } if runnerList.Count == 0 { return params.RunnerReference{}, fmt.Errorf("could not find runner with name %q: %w", runnerName, runnerErrors.ErrNotFound) } if runnerList.Count > 1 { return params.RunnerReference{}, fmt.Errorf("failed to decode response: %w", err) } return runnerList.RunnerReferences[0], nil } func (s *ScaleSetClient) RemoveRunner(ctx context.Context, runnerID int64) error { path := fmt.Sprintf("%s/%d", runnerEndpoint, runnerID) req, err := s.newActionsRequest(ctx, http.MethodDelete, path, nil) if err != nil { return fmt.Errorf("failed to construct request: %w", err) } resp, err := s.Do(req) if err != nil { return fmt.Errorf("request failed for %s: %w", req.URL.String(), err) } resp.Body.Close() return nil }