From 7e1a83c79ab16322d25d18a0b2f843d473f00652 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Fri, 11 Apr 2025 10:42:31 +0000 Subject: [PATCH] Add API endpoint for some scaleset ops Signed-off-by: Gabriel Adrian Samfira --- apiserver/controllers/instances.go | 48 +++ apiserver/controllers/scalesets.go | 211 ++++++++++++ apiserver/routers/routers.go | 19 ++ apiserver/swagger-models.yaml | 30 ++ apiserver/swagger.yaml | 131 ++++++++ client/garm_api_client.go | 5 + client/instances/instances_client.go | 40 +++ .../list_scale_set_instances_parameters.go | 151 +++++++++ .../list_scale_set_instances_responses.go | 184 +++++++++++ .../scalesets/delete_scale_set_parameters.go | 151 +++++++++ .../scalesets/delete_scale_set_responses.go | 106 ++++++ client/scalesets/get_scale_set_parameters.go | 151 +++++++++ client/scalesets/get_scale_set_responses.go | 184 +++++++++++ client/scalesets/list_scalesets_parameters.go | 128 ++++++++ client/scalesets/list_scalesets_responses.go | 184 +++++++++++ client/scalesets/scalesets_client.go | 217 +++++++++++++ .../scalesets/update_scale_set_parameters.go | 173 ++++++++++ .../scalesets/update_scale_set_responses.go | 184 +++++++++++ database/common/store.go | 12 + database/sql/instances.go | 12 +- database/sql/pools.go | 5 +- database/sql/util.go | 42 ++- database/watcher/filters.go | 21 ++ params/interfaces.go | 7 + params/params.go | 15 + runner/enterprises.go | 9 + runner/organizations.go | 9 + runner/pool/pool.go | 47 +-- runner/repositories.go | 9 + runner/scalesets.go | 306 ++++++++++++++++++ util/github/scalesets/message_sessions.go | 2 + 31 files changed, 2768 insertions(+), 25 deletions(-) create mode 100644 apiserver/controllers/scalesets.go create mode 100644 client/instances/list_scale_set_instances_parameters.go create mode 100644 client/instances/list_scale_set_instances_responses.go create mode 100644 client/scalesets/delete_scale_set_parameters.go create mode 100644 client/scalesets/delete_scale_set_responses.go create mode 100644 client/scalesets/get_scale_set_parameters.go create mode 100644 client/scalesets/get_scale_set_responses.go create mode 100644 client/scalesets/list_scalesets_parameters.go create mode 100644 client/scalesets/list_scalesets_responses.go create mode 100644 client/scalesets/scalesets_client.go create mode 100644 client/scalesets/update_scale_set_parameters.go create mode 100644 client/scalesets/update_scale_set_responses.go create mode 100644 params/interfaces.go create mode 100644 runner/scalesets.go diff --git a/apiserver/controllers/instances.go b/apiserver/controllers/instances.go index 962264f9..fd6d2c45 100644 --- a/apiserver/controllers/instances.go +++ b/apiserver/controllers/instances.go @@ -69,6 +69,54 @@ func (a *APIController) ListPoolInstancesHandler(w http.ResponseWriter, r *http. } } +// swagger:route GET /scalesets/{scalesetID}/instances instances ListScaleSetInstances +// +// List runner instances in a scale set. +// +// Parameters: +// + name: scalesetID +// description: Runner scale set ID. +// type: string +// in: path +// required: true +// +// Responses: +// 200: Instances +// default: APIErrorResponse +func (a *APIController) ListScaleSetInstancesHandler(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + vars := mux.Vars(r) + scalesetID, ok := vars["scalesetID"] + if !ok { + w.WriteHeader(http.StatusBadRequest) + if err := json.NewEncoder(w).Encode(params.APIErrorResponse{ + Error: "Bad Request", + Details: "No pool ID specified", + }); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } + return + } + id, err := strconv.ParseUint(scalesetID, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + instances, err := a.r.ListScaleSetInstances(ctx, uint(id)) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing pool instances") + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(instances); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + // swagger:route GET /instances/{instanceName} instances GetInstance // // Get runner instance by name. diff --git a/apiserver/controllers/scalesets.go b/apiserver/controllers/scalesets.go new file mode 100644 index 00000000..d12928f0 --- /dev/null +++ b/apiserver/controllers/scalesets.go @@ -0,0 +1,211 @@ +// Copyright 2022 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 controllers + +import ( + "encoding/json" + "log/slog" + "net/http" + "strconv" + + "github.com/gorilla/mux" + + gErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/apiserver/params" + runnerParams "github.com/cloudbase/garm/params" +) + +// swagger:route GET /scalesets scalesets ListScalesets +// +// List all scalesets. +// +// Responses: +// 200: ScaleSets +// default: APIErrorResponse +func (a *APIController) ListAllScaleSetsHandler(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + scalesets, err := a.r.ListAllScaleSets(ctx) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "listing scale sets") + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(scalesets); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route GET /scalesets/{scalesetID} scalesets GetScaleSet +// +// Get scale set by ID. +// +// Parameters: +// + name: scalesetID +// description: ID of the scale set to fetch. +// type: string +// in: path +// required: true +// +// Responses: +// 200: ScaleSet +// default: APIErrorResponse +func (a *APIController) GetScaleSetByIDHandler(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + scaleSetID, ok := vars["scalesetID"] + if !ok { + w.WriteHeader(http.StatusBadRequest) + if err := json.NewEncoder(w).Encode(params.APIErrorResponse{ + Error: "Bad Request", + Details: "No scale set ID specified", + }); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } + return + } + id, err := strconv.ParseUint(scaleSetID, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + scaleSet, err := a.r.GetScaleSetByID(ctx, uint(id)) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "fetching scale set") + handleError(ctx, w, err) + return + } + + scaleSet.RunnerBootstrapTimeout = scaleSet.RunnerTimeout() + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(scaleSet); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} + +// swagger:route DELETE /scalesets/{scalesetID} scalesets DeleteScaleSet +// +// Delete scale set by ID. +// +// Parameters: +// + name: scalesetID +// description: ID of the scale set to delete. +// type: string +// in: path +// required: true +// +// Responses: +// default: APIErrorResponse +func (a *APIController) DeleteScaleSetByIDHandler(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + scalesetID, ok := vars["scalesetID"] + if !ok { + w.WriteHeader(http.StatusBadRequest) + if err := json.NewEncoder(w).Encode(params.APIErrorResponse{ + Error: "Bad Request", + Details: "No scale set ID specified", + }); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } + return + } + + id, err := strconv.ParseUint(scalesetID, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + if err := a.r.DeleteScaleSetByID(ctx, uint(id)); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "removing scale set") + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) +} + +// swagger:route PUT /scalesets/{scalesetID} scalesets UpdateScaleSet +// +// Update scale set by ID. +// +// Parameters: +// + name: scalesetID +// description: ID of the scale set to update. +// type: string +// in: path +// required: true +// +// + name: Body +// description: Parameters to update the scale set with. +// type: UpdateScaleSetParams +// in: body +// required: true +// +// Responses: +// 200: ScaleSet +// default: APIErrorResponse +func (a *APIController) UpdateScaleSetByIDHandler(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + vars := mux.Vars(r) + scalesetID, ok := vars["scalesetID"] + if !ok { + w.WriteHeader(http.StatusBadRequest) + if err := json.NewEncoder(w).Encode(params.APIErrorResponse{ + Error: "Bad Request", + Details: "No scale set ID specified", + }); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } + return + } + + id, err := strconv.ParseUint(scalesetID, 10, 64) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to parse id") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + var scaleSetData runnerParams.UpdateScaleSetParams + if err := json.NewDecoder(r.Body).Decode(&scaleSetData); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to decode") + handleError(ctx, w, gErrors.ErrBadRequest) + return + } + + scaleSet, err := a.r.UpdateScaleSetByID(ctx, uint(id), scaleSetData) + if err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "updating scale set") + handleError(ctx, w, err) + return + } + + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(scaleSet); err != nil { + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response") + } +} diff --git a/apiserver/routers/routers.go b/apiserver/routers/routers.go index 8c0434bc..13c9a2f9 100644 --- a/apiserver/routers/routers.go +++ b/apiserver/routers/routers.go @@ -214,6 +214,25 @@ func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware apiRouter.Handle("/pools/{poolID}/instances/", http.HandlerFunc(han.ListPoolInstancesHandler)).Methods("GET", "OPTIONS") apiRouter.Handle("/pools/{poolID}/instances", http.HandlerFunc(han.ListPoolInstancesHandler)).Methods("GET", "OPTIONS") + //////////////// + // Scale sets // + //////////////// + // List all pools + apiRouter.Handle("/scalesets/", http.HandlerFunc(han.ListAllScaleSetsHandler)).Methods("GET", "OPTIONS") + apiRouter.Handle("/scalesets", http.HandlerFunc(han.ListAllScaleSetsHandler)).Methods("GET", "OPTIONS") + // Get one pool + apiRouter.Handle("/scalesets/{scalesetID}/", http.HandlerFunc(han.GetScaleSetByIDHandler)).Methods("GET", "OPTIONS") + apiRouter.Handle("/scalesets/{scalesetID}", http.HandlerFunc(han.GetScaleSetByIDHandler)).Methods("GET", "OPTIONS") + // Delete one pool + apiRouter.Handle("/scalesets/{scalesetID}/", http.HandlerFunc(han.DeleteScaleSetByIDHandler)).Methods("DELETE", "OPTIONS") + apiRouter.Handle("/scalesets/{scalesetID}", http.HandlerFunc(han.DeleteScaleSetByIDHandler)).Methods("DELETE", "OPTIONS") + // Update one pool + apiRouter.Handle("/scalesets/{scalesetID}/", http.HandlerFunc(han.UpdateScaleSetByIDHandler)).Methods("PUT", "OPTIONS") + apiRouter.Handle("/scalesets/{scalesetID}", http.HandlerFunc(han.UpdateScaleSetByIDHandler)).Methods("PUT", "OPTIONS") + // List pool instances + apiRouter.Handle("/scalesets/{scalesetID}/instances/", http.HandlerFunc(han.ListScaleSetInstancesHandler)).Methods("GET", "OPTIONS") + apiRouter.Handle("/scalesets/{scalesetID}/instances", http.HandlerFunc(han.ListScaleSetInstancesHandler)).Methods("GET", "OPTIONS") + ///////////// // Runners // ///////////// diff --git a/apiserver/swagger-models.yaml b/apiserver/swagger-models.yaml index 88c6bd8d..ad83d6c8 100644 --- a/apiserver/swagger-models.yaml +++ b/apiserver/swagger-models.yaml @@ -130,6 +130,22 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params + ScaleSets: + type: array + x-go-type: + type: ScaleSets + import: + package: github.com/cloudbase/garm/params + alias: garm_params + items: + $ref: '#/definitions/ScaleSet' + ScaleSet: + type: object + x-go-type: + type: ScaleSet + import: + package: github.com/cloudbase/garm/params + alias: garm_params Repositories: type: array x-go-type: @@ -213,6 +229,13 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params + CreateScaleSetParams: + type: object + x-go-type: + type: CreateScaleSetParams + import: + package: github.com/cloudbase/garm/params + alias: garm_params UpdatePoolParams: type: object x-go-type: @@ -220,6 +243,13 @@ definitions: import: package: github.com/cloudbase/garm/params alias: garm_params + UpdateScaleSetParams: + type: object + x-go-type: + type: UpdateScaleSetParams + import: + package: github.com/cloudbase/garm/params + alias: garm_params APIErrorResponse: type: object x-go-type: diff --git a/apiserver/swagger.yaml b/apiserver/swagger.yaml index 42c573f0..cf4287e9 100644 --- a/apiserver/swagger.yaml +++ b/apiserver/swagger.yaml @@ -65,6 +65,13 @@ definitions: alias: garm_params package: github.com/cloudbase/garm/params type: CreateRepoParams + CreateScaleSetParams: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: CreateScaleSetParams Credentials: items: $ref: '#/definitions/GithubCredentials' @@ -244,6 +251,22 @@ definitions: alias: garm_params package: github.com/cloudbase/garm/params type: Repository + ScaleSet: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: ScaleSet + ScaleSets: + items: + $ref: '#/definitions/ScaleSet' + type: array + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: ScaleSets UpdateControllerParams: type: object x-go-type: @@ -279,6 +302,13 @@ definitions: alias: garm_params package: github.com/cloudbase/garm/params type: UpdatePoolParams + UpdateScaleSetParams: + type: object + x-go-type: + import: + alias: garm_params + package: github.com/cloudbase/garm/params + type: UpdateScaleSetParams User: type: object x-go-type: @@ -1718,6 +1748,107 @@ paths: tags: - repositories - hooks + /scalesets: + get: + operationId: ListScalesets + responses: + "200": + description: ScaleSets + schema: + $ref: '#/definitions/ScaleSets' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: List all scalesets. + tags: + - scalesets + /scalesets/{scalesetID}: + delete: + operationId: DeleteScaleSet + parameters: + - description: ID of the scale set to delete. + in: path + name: scalesetID + required: true + type: string + responses: + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Delete scale set by ID. + tags: + - scalesets + get: + operationId: GetScaleSet + parameters: + - description: ID of the scale set to fetch. + in: path + name: scalesetID + required: true + type: string + responses: + "200": + description: ScaleSet + schema: + $ref: '#/definitions/ScaleSet' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Get scale set by ID. + tags: + - scalesets + put: + operationId: UpdateScaleSet + parameters: + - description: ID of the scale set to update. + in: path + name: scalesetID + required: true + type: string + - description: Parameters to update the scale set with. + in: body + name: Body + required: true + schema: + $ref: '#/definitions/UpdateScaleSetParams' + description: Parameters to update the scale set with. + type: object + responses: + "200": + description: ScaleSet + schema: + $ref: '#/definitions/ScaleSet' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: Update scale set by ID. + tags: + - scalesets + /scalesets/{scalesetID}/instances: + get: + operationId: ListScaleSetInstances + parameters: + - description: Runner scale set ID. + in: path + name: scalesetID + required: true + type: string + responses: + "200": + description: Instances + schema: + $ref: '#/definitions/Instances' + default: + description: APIErrorResponse + schema: + $ref: '#/definitions/APIErrorResponse' + summary: List runner instances in a scale set. + tags: + - instances produces: - application/json security: diff --git a/client/garm_api_client.go b/client/garm_api_client.go index cbc65dfc..f5bc51b2 100644 --- a/client/garm_api_client.go +++ b/client/garm_api_client.go @@ -24,6 +24,7 @@ import ( "github.com/cloudbase/garm/client/pools" "github.com/cloudbase/garm/client/providers" "github.com/cloudbase/garm/client/repositories" + "github.com/cloudbase/garm/client/scalesets" ) // Default garm API HTTP client. @@ -82,6 +83,7 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *GarmAPI { cli.Pools = pools.New(transport, formats) cli.Providers = providers.New(transport, formats) cli.Repositories = repositories.New(transport, formats) + cli.Scalesets = scalesets.New(transport, formats) return cli } @@ -154,6 +156,8 @@ type GarmAPI struct { Repositories repositories.ClientService + Scalesets scalesets.ClientService + Transport runtime.ClientTransport } @@ -174,4 +178,5 @@ func (c *GarmAPI) SetTransport(transport runtime.ClientTransport) { c.Pools.SetTransport(transport) c.Providers.SetTransport(transport) c.Repositories.SetTransport(transport) + c.Scalesets.SetTransport(transport) } diff --git a/client/instances/instances_client.go b/client/instances/instances_client.go index 5b6af6f3..2c41f919 100644 --- a/client/instances/instances_client.go +++ b/client/instances/instances_client.go @@ -62,6 +62,8 @@ type ClientService interface { ListPoolInstances(params *ListPoolInstancesParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListPoolInstancesOK, error) + ListScaleSetInstances(params *ListScaleSetInstancesParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListScaleSetInstancesOK, error) + SetTransport(transport runtime.ClientTransport) } @@ -211,6 +213,44 @@ func (a *Client) ListPoolInstances(params *ListPoolInstancesParams, authInfo run return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } +/* +ListScaleSetInstances lists runner instances in a scale set +*/ +func (a *Client) ListScaleSetInstances(params *ListScaleSetInstancesParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListScaleSetInstancesOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewListScaleSetInstancesParams() + } + op := &runtime.ClientOperation{ + ID: "ListScaleSetInstances", + Method: "GET", + PathPattern: "/scalesets/{scalesetID}/instances", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &ListScaleSetInstancesReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*ListScaleSetInstancesOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*ListScaleSetInstancesDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport diff --git a/client/instances/list_scale_set_instances_parameters.go b/client/instances/list_scale_set_instances_parameters.go new file mode 100644 index 00000000..7b38ef82 --- /dev/null +++ b/client/instances/list_scale_set_instances_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package instances + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewListScaleSetInstancesParams creates a new ListScaleSetInstancesParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewListScaleSetInstancesParams() *ListScaleSetInstancesParams { + return &ListScaleSetInstancesParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewListScaleSetInstancesParamsWithTimeout creates a new ListScaleSetInstancesParams object +// with the ability to set a timeout on a request. +func NewListScaleSetInstancesParamsWithTimeout(timeout time.Duration) *ListScaleSetInstancesParams { + return &ListScaleSetInstancesParams{ + timeout: timeout, + } +} + +// NewListScaleSetInstancesParamsWithContext creates a new ListScaleSetInstancesParams object +// with the ability to set a context for a request. +func NewListScaleSetInstancesParamsWithContext(ctx context.Context) *ListScaleSetInstancesParams { + return &ListScaleSetInstancesParams{ + Context: ctx, + } +} + +// NewListScaleSetInstancesParamsWithHTTPClient creates a new ListScaleSetInstancesParams object +// with the ability to set a custom HTTPClient for a request. +func NewListScaleSetInstancesParamsWithHTTPClient(client *http.Client) *ListScaleSetInstancesParams { + return &ListScaleSetInstancesParams{ + HTTPClient: client, + } +} + +/* +ListScaleSetInstancesParams contains all the parameters to send to the API endpoint + + for the list scale set instances operation. + + Typically these are written to a http.Request. +*/ +type ListScaleSetInstancesParams struct { + + /* ScalesetID. + + Runner scale set ID. + */ + ScalesetID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the list scale set instances params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListScaleSetInstancesParams) WithDefaults() *ListScaleSetInstancesParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the list scale set instances params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListScaleSetInstancesParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the list scale set instances params +func (o *ListScaleSetInstancesParams) WithTimeout(timeout time.Duration) *ListScaleSetInstancesParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the list scale set instances params +func (o *ListScaleSetInstancesParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the list scale set instances params +func (o *ListScaleSetInstancesParams) WithContext(ctx context.Context) *ListScaleSetInstancesParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the list scale set instances params +func (o *ListScaleSetInstancesParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the list scale set instances params +func (o *ListScaleSetInstancesParams) WithHTTPClient(client *http.Client) *ListScaleSetInstancesParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the list scale set instances params +func (o *ListScaleSetInstancesParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithScalesetID adds the scalesetID to the list scale set instances params +func (o *ListScaleSetInstancesParams) WithScalesetID(scalesetID string) *ListScaleSetInstancesParams { + o.SetScalesetID(scalesetID) + return o +} + +// SetScalesetID adds the scalesetId to the list scale set instances params +func (o *ListScaleSetInstancesParams) SetScalesetID(scalesetID string) { + o.ScalesetID = scalesetID +} + +// WriteToRequest writes these params to a swagger request +func (o *ListScaleSetInstancesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param scalesetID + if err := r.SetPathParam("scalesetID", o.ScalesetID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/instances/list_scale_set_instances_responses.go b/client/instances/list_scale_set_instances_responses.go new file mode 100644 index 00000000..a966a9e7 --- /dev/null +++ b/client/instances/list_scale_set_instances_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package instances + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// ListScaleSetInstancesReader is a Reader for the ListScaleSetInstances structure. +type ListScaleSetInstancesReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ListScaleSetInstancesReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewListScaleSetInstancesOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewListScaleSetInstancesDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewListScaleSetInstancesOK creates a ListScaleSetInstancesOK with default headers values +func NewListScaleSetInstancesOK() *ListScaleSetInstancesOK { + return &ListScaleSetInstancesOK{} +} + +/* +ListScaleSetInstancesOK describes a response with status code 200, with default header values. + +Instances +*/ +type ListScaleSetInstancesOK struct { + Payload garm_params.Instances +} + +// IsSuccess returns true when this list scale set instances o k response has a 2xx status code +func (o *ListScaleSetInstancesOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this list scale set instances o k response has a 3xx status code +func (o *ListScaleSetInstancesOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list scale set instances o k response has a 4xx status code +func (o *ListScaleSetInstancesOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this list scale set instances o k response has a 5xx status code +func (o *ListScaleSetInstancesOK) IsServerError() bool { + return false +} + +// IsCode returns true when this list scale set instances o k response a status code equal to that given +func (o *ListScaleSetInstancesOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the list scale set instances o k response +func (o *ListScaleSetInstancesOK) Code() int { + return 200 +} + +func (o *ListScaleSetInstancesOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}/instances][%d] listScaleSetInstancesOK %s", 200, payload) +} + +func (o *ListScaleSetInstancesOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}/instances][%d] listScaleSetInstancesOK %s", 200, payload) +} + +func (o *ListScaleSetInstancesOK) GetPayload() garm_params.Instances { + return o.Payload +} + +func (o *ListScaleSetInstancesOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewListScaleSetInstancesDefault creates a ListScaleSetInstancesDefault with default headers values +func NewListScaleSetInstancesDefault(code int) *ListScaleSetInstancesDefault { + return &ListScaleSetInstancesDefault{ + _statusCode: code, + } +} + +/* +ListScaleSetInstancesDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type ListScaleSetInstancesDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this list scale set instances default response has a 2xx status code +func (o *ListScaleSetInstancesDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this list scale set instances default response has a 3xx status code +func (o *ListScaleSetInstancesDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this list scale set instances default response has a 4xx status code +func (o *ListScaleSetInstancesDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this list scale set instances default response has a 5xx status code +func (o *ListScaleSetInstancesDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this list scale set instances default response a status code equal to that given +func (o *ListScaleSetInstancesDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the list scale set instances default response +func (o *ListScaleSetInstancesDefault) Code() int { + return o._statusCode +} + +func (o *ListScaleSetInstancesDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}/instances][%d] ListScaleSetInstances default %s", o._statusCode, payload) +} + +func (o *ListScaleSetInstancesDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}/instances][%d] ListScaleSetInstances default %s", o._statusCode, payload) +} + +func (o *ListScaleSetInstancesDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *ListScaleSetInstancesDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/scalesets/delete_scale_set_parameters.go b/client/scalesets/delete_scale_set_parameters.go new file mode 100644 index 00000000..640f95a8 --- /dev/null +++ b/client/scalesets/delete_scale_set_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewDeleteScaleSetParams creates a new DeleteScaleSetParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewDeleteScaleSetParams() *DeleteScaleSetParams { + return &DeleteScaleSetParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewDeleteScaleSetParamsWithTimeout creates a new DeleteScaleSetParams object +// with the ability to set a timeout on a request. +func NewDeleteScaleSetParamsWithTimeout(timeout time.Duration) *DeleteScaleSetParams { + return &DeleteScaleSetParams{ + timeout: timeout, + } +} + +// NewDeleteScaleSetParamsWithContext creates a new DeleteScaleSetParams object +// with the ability to set a context for a request. +func NewDeleteScaleSetParamsWithContext(ctx context.Context) *DeleteScaleSetParams { + return &DeleteScaleSetParams{ + Context: ctx, + } +} + +// NewDeleteScaleSetParamsWithHTTPClient creates a new DeleteScaleSetParams object +// with the ability to set a custom HTTPClient for a request. +func NewDeleteScaleSetParamsWithHTTPClient(client *http.Client) *DeleteScaleSetParams { + return &DeleteScaleSetParams{ + HTTPClient: client, + } +} + +/* +DeleteScaleSetParams contains all the parameters to send to the API endpoint + + for the delete scale set operation. + + Typically these are written to a http.Request. +*/ +type DeleteScaleSetParams struct { + + /* ScalesetID. + + ID of the scale set to delete. + */ + ScalesetID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the delete scale set params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteScaleSetParams) WithDefaults() *DeleteScaleSetParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the delete scale set params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *DeleteScaleSetParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the delete scale set params +func (o *DeleteScaleSetParams) WithTimeout(timeout time.Duration) *DeleteScaleSetParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the delete scale set params +func (o *DeleteScaleSetParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the delete scale set params +func (o *DeleteScaleSetParams) WithContext(ctx context.Context) *DeleteScaleSetParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the delete scale set params +func (o *DeleteScaleSetParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the delete scale set params +func (o *DeleteScaleSetParams) WithHTTPClient(client *http.Client) *DeleteScaleSetParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the delete scale set params +func (o *DeleteScaleSetParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithScalesetID adds the scalesetID to the delete scale set params +func (o *DeleteScaleSetParams) WithScalesetID(scalesetID string) *DeleteScaleSetParams { + o.SetScalesetID(scalesetID) + return o +} + +// SetScalesetID adds the scalesetId to the delete scale set params +func (o *DeleteScaleSetParams) SetScalesetID(scalesetID string) { + o.ScalesetID = scalesetID +} + +// WriteToRequest writes these params to a swagger request +func (o *DeleteScaleSetParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param scalesetID + if err := r.SetPathParam("scalesetID", o.ScalesetID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/scalesets/delete_scale_set_responses.go b/client/scalesets/delete_scale_set_responses.go new file mode 100644 index 00000000..dd0f7334 --- /dev/null +++ b/client/scalesets/delete_scale_set_responses.go @@ -0,0 +1,106 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" +) + +// DeleteScaleSetReader is a Reader for the DeleteScaleSet structure. +type DeleteScaleSetReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *DeleteScaleSetReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + result := NewDeleteScaleSetDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result +} + +// NewDeleteScaleSetDefault creates a DeleteScaleSetDefault with default headers values +func NewDeleteScaleSetDefault(code int) *DeleteScaleSetDefault { + return &DeleteScaleSetDefault{ + _statusCode: code, + } +} + +/* +DeleteScaleSetDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type DeleteScaleSetDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this delete scale set default response has a 2xx status code +func (o *DeleteScaleSetDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this delete scale set default response has a 3xx status code +func (o *DeleteScaleSetDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this delete scale set default response has a 4xx status code +func (o *DeleteScaleSetDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this delete scale set default response has a 5xx status code +func (o *DeleteScaleSetDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this delete scale set default response a status code equal to that given +func (o *DeleteScaleSetDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the delete scale set default response +func (o *DeleteScaleSetDefault) Code() int { + return o._statusCode +} + +func (o *DeleteScaleSetDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[DELETE /scalesets/{scalesetID}][%d] DeleteScaleSet default %s", o._statusCode, payload) +} + +func (o *DeleteScaleSetDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[DELETE /scalesets/{scalesetID}][%d] DeleteScaleSet default %s", o._statusCode, payload) +} + +func (o *DeleteScaleSetDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *DeleteScaleSetDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/scalesets/get_scale_set_parameters.go b/client/scalesets/get_scale_set_parameters.go new file mode 100644 index 00000000..9e31b46e --- /dev/null +++ b/client/scalesets/get_scale_set_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetScaleSetParams creates a new GetScaleSetParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetScaleSetParams() *GetScaleSetParams { + return &GetScaleSetParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetScaleSetParamsWithTimeout creates a new GetScaleSetParams object +// with the ability to set a timeout on a request. +func NewGetScaleSetParamsWithTimeout(timeout time.Duration) *GetScaleSetParams { + return &GetScaleSetParams{ + timeout: timeout, + } +} + +// NewGetScaleSetParamsWithContext creates a new GetScaleSetParams object +// with the ability to set a context for a request. +func NewGetScaleSetParamsWithContext(ctx context.Context) *GetScaleSetParams { + return &GetScaleSetParams{ + Context: ctx, + } +} + +// NewGetScaleSetParamsWithHTTPClient creates a new GetScaleSetParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetScaleSetParamsWithHTTPClient(client *http.Client) *GetScaleSetParams { + return &GetScaleSetParams{ + HTTPClient: client, + } +} + +/* +GetScaleSetParams contains all the parameters to send to the API endpoint + + for the get scale set operation. + + Typically these are written to a http.Request. +*/ +type GetScaleSetParams struct { + + /* ScalesetID. + + ID of the scale set to fetch. + */ + ScalesetID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get scale set params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetScaleSetParams) WithDefaults() *GetScaleSetParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get scale set params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetScaleSetParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get scale set params +func (o *GetScaleSetParams) WithTimeout(timeout time.Duration) *GetScaleSetParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get scale set params +func (o *GetScaleSetParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get scale set params +func (o *GetScaleSetParams) WithContext(ctx context.Context) *GetScaleSetParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get scale set params +func (o *GetScaleSetParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get scale set params +func (o *GetScaleSetParams) WithHTTPClient(client *http.Client) *GetScaleSetParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get scale set params +func (o *GetScaleSetParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithScalesetID adds the scalesetID to the get scale set params +func (o *GetScaleSetParams) WithScalesetID(scalesetID string) *GetScaleSetParams { + o.SetScalesetID(scalesetID) + return o +} + +// SetScalesetID adds the scalesetId to the get scale set params +func (o *GetScaleSetParams) SetScalesetID(scalesetID string) { + o.ScalesetID = scalesetID +} + +// WriteToRequest writes these params to a swagger request +func (o *GetScaleSetParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param scalesetID + if err := r.SetPathParam("scalesetID", o.ScalesetID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/scalesets/get_scale_set_responses.go b/client/scalesets/get_scale_set_responses.go new file mode 100644 index 00000000..5b30e16f --- /dev/null +++ b/client/scalesets/get_scale_set_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// GetScaleSetReader is a Reader for the GetScaleSet structure. +type GetScaleSetReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetScaleSetReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetScaleSetOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewGetScaleSetDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewGetScaleSetOK creates a GetScaleSetOK with default headers values +func NewGetScaleSetOK() *GetScaleSetOK { + return &GetScaleSetOK{} +} + +/* +GetScaleSetOK describes a response with status code 200, with default header values. + +ScaleSet +*/ +type GetScaleSetOK struct { + Payload garm_params.ScaleSet +} + +// IsSuccess returns true when this get scale set o k response has a 2xx status code +func (o *GetScaleSetOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get scale set o k response has a 3xx status code +func (o *GetScaleSetOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get scale set o k response has a 4xx status code +func (o *GetScaleSetOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get scale set o k response has a 5xx status code +func (o *GetScaleSetOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get scale set o k response a status code equal to that given +func (o *GetScaleSetOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the get scale set o k response +func (o *GetScaleSetOK) Code() int { + return 200 +} + +func (o *GetScaleSetOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}][%d] getScaleSetOK %s", 200, payload) +} + +func (o *GetScaleSetOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}][%d] getScaleSetOK %s", 200, payload) +} + +func (o *GetScaleSetOK) GetPayload() garm_params.ScaleSet { + return o.Payload +} + +func (o *GetScaleSetOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetScaleSetDefault creates a GetScaleSetDefault with default headers values +func NewGetScaleSetDefault(code int) *GetScaleSetDefault { + return &GetScaleSetDefault{ + _statusCode: code, + } +} + +/* +GetScaleSetDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type GetScaleSetDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this get scale set default response has a 2xx status code +func (o *GetScaleSetDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this get scale set default response has a 3xx status code +func (o *GetScaleSetDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this get scale set default response has a 4xx status code +func (o *GetScaleSetDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this get scale set default response has a 5xx status code +func (o *GetScaleSetDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this get scale set default response a status code equal to that given +func (o *GetScaleSetDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the get scale set default response +func (o *GetScaleSetDefault) Code() int { + return o._statusCode +} + +func (o *GetScaleSetDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}][%d] GetScaleSet default %s", o._statusCode, payload) +} + +func (o *GetScaleSetDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets/{scalesetID}][%d] GetScaleSet default %s", o._statusCode, payload) +} + +func (o *GetScaleSetDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *GetScaleSetDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/scalesets/list_scalesets_parameters.go b/client/scalesets/list_scalesets_parameters.go new file mode 100644 index 00000000..b6fd1ccb --- /dev/null +++ b/client/scalesets/list_scalesets_parameters.go @@ -0,0 +1,128 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewListScalesetsParams creates a new ListScalesetsParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewListScalesetsParams() *ListScalesetsParams { + return &ListScalesetsParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewListScalesetsParamsWithTimeout creates a new ListScalesetsParams object +// with the ability to set a timeout on a request. +func NewListScalesetsParamsWithTimeout(timeout time.Duration) *ListScalesetsParams { + return &ListScalesetsParams{ + timeout: timeout, + } +} + +// NewListScalesetsParamsWithContext creates a new ListScalesetsParams object +// with the ability to set a context for a request. +func NewListScalesetsParamsWithContext(ctx context.Context) *ListScalesetsParams { + return &ListScalesetsParams{ + Context: ctx, + } +} + +// NewListScalesetsParamsWithHTTPClient creates a new ListScalesetsParams object +// with the ability to set a custom HTTPClient for a request. +func NewListScalesetsParamsWithHTTPClient(client *http.Client) *ListScalesetsParams { + return &ListScalesetsParams{ + HTTPClient: client, + } +} + +/* +ListScalesetsParams contains all the parameters to send to the API endpoint + + for the list scalesets operation. + + Typically these are written to a http.Request. +*/ +type ListScalesetsParams struct { + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the list scalesets params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListScalesetsParams) WithDefaults() *ListScalesetsParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the list scalesets params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *ListScalesetsParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the list scalesets params +func (o *ListScalesetsParams) WithTimeout(timeout time.Duration) *ListScalesetsParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the list scalesets params +func (o *ListScalesetsParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the list scalesets params +func (o *ListScalesetsParams) WithContext(ctx context.Context) *ListScalesetsParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the list scalesets params +func (o *ListScalesetsParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the list scalesets params +func (o *ListScalesetsParams) WithHTTPClient(client *http.Client) *ListScalesetsParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the list scalesets params +func (o *ListScalesetsParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WriteToRequest writes these params to a swagger request +func (o *ListScalesetsParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/scalesets/list_scalesets_responses.go b/client/scalesets/list_scalesets_responses.go new file mode 100644 index 00000000..05064308 --- /dev/null +++ b/client/scalesets/list_scalesets_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// ListScalesetsReader is a Reader for the ListScalesets structure. +type ListScalesetsReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *ListScalesetsReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewListScalesetsOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewListScalesetsDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewListScalesetsOK creates a ListScalesetsOK with default headers values +func NewListScalesetsOK() *ListScalesetsOK { + return &ListScalesetsOK{} +} + +/* +ListScalesetsOK describes a response with status code 200, with default header values. + +ScaleSets +*/ +type ListScalesetsOK struct { + Payload garm_params.ScaleSets +} + +// IsSuccess returns true when this list scalesets o k response has a 2xx status code +func (o *ListScalesetsOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this list scalesets o k response has a 3xx status code +func (o *ListScalesetsOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list scalesets o k response has a 4xx status code +func (o *ListScalesetsOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this list scalesets o k response has a 5xx status code +func (o *ListScalesetsOK) IsServerError() bool { + return false +} + +// IsCode returns true when this list scalesets o k response a status code equal to that given +func (o *ListScalesetsOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the list scalesets o k response +func (o *ListScalesetsOK) Code() int { + return 200 +} + +func (o *ListScalesetsOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets][%d] listScalesetsOK %s", 200, payload) +} + +func (o *ListScalesetsOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets][%d] listScalesetsOK %s", 200, payload) +} + +func (o *ListScalesetsOK) GetPayload() garm_params.ScaleSets { + return o.Payload +} + +func (o *ListScalesetsOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewListScalesetsDefault creates a ListScalesetsDefault with default headers values +func NewListScalesetsDefault(code int) *ListScalesetsDefault { + return &ListScalesetsDefault{ + _statusCode: code, + } +} + +/* +ListScalesetsDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type ListScalesetsDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this list scalesets default response has a 2xx status code +func (o *ListScalesetsDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this list scalesets default response has a 3xx status code +func (o *ListScalesetsDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this list scalesets default response has a 4xx status code +func (o *ListScalesetsDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this list scalesets default response has a 5xx status code +func (o *ListScalesetsDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this list scalesets default response a status code equal to that given +func (o *ListScalesetsDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the list scalesets default response +func (o *ListScalesetsDefault) Code() int { + return o._statusCode +} + +func (o *ListScalesetsDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets][%d] ListScalesets default %s", o._statusCode, payload) +} + +func (o *ListScalesetsDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[GET /scalesets][%d] ListScalesets default %s", o._statusCode, payload) +} + +func (o *ListScalesetsDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *ListScalesetsDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/client/scalesets/scalesets_client.go b/client/scalesets/scalesets_client.go new file mode 100644 index 00000000..5375750d --- /dev/null +++ b/client/scalesets/scalesets_client.go @@ -0,0 +1,217 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "github.com/go-openapi/runtime" + httptransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// New creates a new scalesets API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +// New creates a new scalesets API client with basic auth credentials. +// It takes the following parameters: +// - host: http host (github.com). +// - basePath: any base path for the API client ("/v1", "/v3"). +// - scheme: http scheme ("http", "https"). +// - user: user for basic authentication header. +// - password: password for basic authentication header. +func NewClientWithBasicAuth(host, basePath, scheme, user, password string) ClientService { + transport := httptransport.New(host, basePath, []string{scheme}) + transport.DefaultAuthentication = httptransport.BasicAuth(user, password) + return &Client{transport: transport, formats: strfmt.Default} +} + +// New creates a new scalesets API client with a bearer token for authentication. +// It takes the following parameters: +// - host: http host (github.com). +// - basePath: any base path for the API client ("/v1", "/v3"). +// - scheme: http scheme ("http", "https"). +// - bearerToken: bearer token for Bearer authentication header. +func NewClientWithBearerToken(host, basePath, scheme, bearerToken string) ClientService { + transport := httptransport.New(host, basePath, []string{scheme}) + transport.DefaultAuthentication = httptransport.BearerToken(bearerToken) + return &Client{transport: transport, formats: strfmt.Default} +} + +/* +Client for scalesets API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption may be used to customize the behavior of Client methods. +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + DeleteScaleSet(params *DeleteScaleSetParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error + + GetScaleSet(params *GetScaleSetParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetScaleSetOK, error) + + ListScalesets(params *ListScalesetsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListScalesetsOK, error) + + UpdateScaleSet(params *UpdateScaleSetParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateScaleSetOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +DeleteScaleSet deletes scale set by ID +*/ +func (a *Client) DeleteScaleSet(params *DeleteScaleSetParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) error { + // TODO: Validate the params before sending + if params == nil { + params = NewDeleteScaleSetParams() + } + op := &runtime.ClientOperation{ + ID: "DeleteScaleSet", + Method: "DELETE", + PathPattern: "/scalesets/{scalesetID}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &DeleteScaleSetReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + _, err := a.transport.Submit(op) + if err != nil { + return err + } + return nil +} + +/* +GetScaleSet gets scale set by ID +*/ +func (a *Client) GetScaleSet(params *GetScaleSetParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GetScaleSetOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetScaleSetParams() + } + op := &runtime.ClientOperation{ + ID: "GetScaleSet", + Method: "GET", + PathPattern: "/scalesets/{scalesetID}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetScaleSetReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetScaleSetOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*GetScaleSetDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +ListScalesets lists all scalesets +*/ +func (a *Client) ListScalesets(params *ListScalesetsParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*ListScalesetsOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewListScalesetsParams() + } + op := &runtime.ClientOperation{ + ID: "ListScalesets", + Method: "GET", + PathPattern: "/scalesets", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &ListScalesetsReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*ListScalesetsOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*ListScalesetsDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +/* +UpdateScaleSet updates scale set by ID +*/ +func (a *Client) UpdateScaleSet(params *UpdateScaleSetParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UpdateScaleSetOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewUpdateScaleSetParams() + } + op := &runtime.ClientOperation{ + ID: "UpdateScaleSet", + Method: "PUT", + PathPattern: "/scalesets/{scalesetID}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &UpdateScaleSetReader{formats: a.formats}, + AuthInfo: authInfo, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*UpdateScaleSetOK) + if ok { + return success, nil + } + // unexpected success response + unexpectedSuccess := result.(*UpdateScaleSetDefault) + return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/client/scalesets/update_scale_set_parameters.go b/client/scalesets/update_scale_set_parameters.go new file mode 100644 index 00000000..39668e9b --- /dev/null +++ b/client/scalesets/update_scale_set_parameters.go @@ -0,0 +1,173 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + garm_params "github.com/cloudbase/garm/params" +) + +// NewUpdateScaleSetParams creates a new UpdateScaleSetParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewUpdateScaleSetParams() *UpdateScaleSetParams { + return &UpdateScaleSetParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewUpdateScaleSetParamsWithTimeout creates a new UpdateScaleSetParams object +// with the ability to set a timeout on a request. +func NewUpdateScaleSetParamsWithTimeout(timeout time.Duration) *UpdateScaleSetParams { + return &UpdateScaleSetParams{ + timeout: timeout, + } +} + +// NewUpdateScaleSetParamsWithContext creates a new UpdateScaleSetParams object +// with the ability to set a context for a request. +func NewUpdateScaleSetParamsWithContext(ctx context.Context) *UpdateScaleSetParams { + return &UpdateScaleSetParams{ + Context: ctx, + } +} + +// NewUpdateScaleSetParamsWithHTTPClient creates a new UpdateScaleSetParams object +// with the ability to set a custom HTTPClient for a request. +func NewUpdateScaleSetParamsWithHTTPClient(client *http.Client) *UpdateScaleSetParams { + return &UpdateScaleSetParams{ + HTTPClient: client, + } +} + +/* +UpdateScaleSetParams contains all the parameters to send to the API endpoint + + for the update scale set operation. + + Typically these are written to a http.Request. +*/ +type UpdateScaleSetParams struct { + + /* Body. + + Parameters to update the scale set with. + */ + Body garm_params.UpdateScaleSetParams + + /* ScalesetID. + + ID of the scale set to update. + */ + ScalesetID string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the update scale set params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UpdateScaleSetParams) WithDefaults() *UpdateScaleSetParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the update scale set params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *UpdateScaleSetParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the update scale set params +func (o *UpdateScaleSetParams) WithTimeout(timeout time.Duration) *UpdateScaleSetParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the update scale set params +func (o *UpdateScaleSetParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the update scale set params +func (o *UpdateScaleSetParams) WithContext(ctx context.Context) *UpdateScaleSetParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the update scale set params +func (o *UpdateScaleSetParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the update scale set params +func (o *UpdateScaleSetParams) WithHTTPClient(client *http.Client) *UpdateScaleSetParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the update scale set params +func (o *UpdateScaleSetParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the update scale set params +func (o *UpdateScaleSetParams) WithBody(body garm_params.UpdateScaleSetParams) *UpdateScaleSetParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the update scale set params +func (o *UpdateScaleSetParams) SetBody(body garm_params.UpdateScaleSetParams) { + o.Body = body +} + +// WithScalesetID adds the scalesetID to the update scale set params +func (o *UpdateScaleSetParams) WithScalesetID(scalesetID string) *UpdateScaleSetParams { + o.SetScalesetID(scalesetID) + return o +} + +// SetScalesetID adds the scalesetId to the update scale set params +func (o *UpdateScaleSetParams) SetScalesetID(scalesetID string) { + o.ScalesetID = scalesetID +} + +// WriteToRequest writes these params to a swagger request +func (o *UpdateScaleSetParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + + // path param scalesetID + if err := r.SetPathParam("scalesetID", o.ScalesetID); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/client/scalesets/update_scale_set_responses.go b/client/scalesets/update_scale_set_responses.go new file mode 100644 index 00000000..666e8256 --- /dev/null +++ b/client/scalesets/update_scale_set_responses.go @@ -0,0 +1,184 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package scalesets + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "encoding/json" + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + apiserver_params "github.com/cloudbase/garm/apiserver/params" + garm_params "github.com/cloudbase/garm/params" +) + +// UpdateScaleSetReader is a Reader for the UpdateScaleSet structure. +type UpdateScaleSetReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *UpdateScaleSetReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewUpdateScaleSetOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + default: + result := NewUpdateScaleSetDefault(response.Code()) + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + if response.Code()/100 == 2 { + return result, nil + } + return nil, result + } +} + +// NewUpdateScaleSetOK creates a UpdateScaleSetOK with default headers values +func NewUpdateScaleSetOK() *UpdateScaleSetOK { + return &UpdateScaleSetOK{} +} + +/* +UpdateScaleSetOK describes a response with status code 200, with default header values. + +ScaleSet +*/ +type UpdateScaleSetOK struct { + Payload garm_params.ScaleSet +} + +// IsSuccess returns true when this update scale set o k response has a 2xx status code +func (o *UpdateScaleSetOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this update scale set o k response has a 3xx status code +func (o *UpdateScaleSetOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this update scale set o k response has a 4xx status code +func (o *UpdateScaleSetOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this update scale set o k response has a 5xx status code +func (o *UpdateScaleSetOK) IsServerError() bool { + return false +} + +// IsCode returns true when this update scale set o k response a status code equal to that given +func (o *UpdateScaleSetOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the update scale set o k response +func (o *UpdateScaleSetOK) Code() int { + return 200 +} + +func (o *UpdateScaleSetOK) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /scalesets/{scalesetID}][%d] updateScaleSetOK %s", 200, payload) +} + +func (o *UpdateScaleSetOK) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /scalesets/{scalesetID}][%d] updateScaleSetOK %s", 200, payload) +} + +func (o *UpdateScaleSetOK) GetPayload() garm_params.ScaleSet { + return o.Payload +} + +func (o *UpdateScaleSetOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewUpdateScaleSetDefault creates a UpdateScaleSetDefault with default headers values +func NewUpdateScaleSetDefault(code int) *UpdateScaleSetDefault { + return &UpdateScaleSetDefault{ + _statusCode: code, + } +} + +/* +UpdateScaleSetDefault describes a response with status code -1, with default header values. + +APIErrorResponse +*/ +type UpdateScaleSetDefault struct { + _statusCode int + + Payload apiserver_params.APIErrorResponse +} + +// IsSuccess returns true when this update scale set default response has a 2xx status code +func (o *UpdateScaleSetDefault) IsSuccess() bool { + return o._statusCode/100 == 2 +} + +// IsRedirect returns true when this update scale set default response has a 3xx status code +func (o *UpdateScaleSetDefault) IsRedirect() bool { + return o._statusCode/100 == 3 +} + +// IsClientError returns true when this update scale set default response has a 4xx status code +func (o *UpdateScaleSetDefault) IsClientError() bool { + return o._statusCode/100 == 4 +} + +// IsServerError returns true when this update scale set default response has a 5xx status code +func (o *UpdateScaleSetDefault) IsServerError() bool { + return o._statusCode/100 == 5 +} + +// IsCode returns true when this update scale set default response a status code equal to that given +func (o *UpdateScaleSetDefault) IsCode(code int) bool { + return o._statusCode == code +} + +// Code gets the status code for the update scale set default response +func (o *UpdateScaleSetDefault) Code() int { + return o._statusCode +} + +func (o *UpdateScaleSetDefault) Error() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /scalesets/{scalesetID}][%d] UpdateScaleSet default %s", o._statusCode, payload) +} + +func (o *UpdateScaleSetDefault) String() string { + payload, _ := json.Marshal(o.Payload) + return fmt.Sprintf("[PUT /scalesets/{scalesetID}][%d] UpdateScaleSet default %s", o._statusCode, payload) +} + +func (o *UpdateScaleSetDefault) GetPayload() apiserver_params.APIErrorResponse { + return o.Payload +} + +func (o *UpdateScaleSetDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/database/common/store.go b/database/common/store.go index 4d91e6cd..860ed8ac 100644 --- a/database/common/store.go +++ b/database/common/store.go @@ -135,6 +135,16 @@ type ControllerStore interface { UpdateController(info params.UpdateControllerParams) (params.ControllerInfo, error) } +type ScaleSetsStore interface { + ListAllScaleSets(ctx context.Context) ([]params.ScaleSet, error) + CreateEntityScaleSet(_ context.Context, entity params.GithubEntity, param params.CreateScaleSetParams) (scaleSet params.ScaleSet, err error) + ListEntityScaleSets(_ context.Context, entity params.GithubEntity) ([]params.ScaleSet, error) + UpdateEntityScaleSet(_ context.Context, entity params.GithubEntity, scaleSetID uint, param params.UpdateScaleSetParams, callback func(old, new params.ScaleSet) error) (updatedScaleSet params.ScaleSet, err error) + GetScaleSetByID(ctx context.Context, scaleSet uint) (params.ScaleSet, error) + DeleteScaleSetByID(ctx context.Context, scaleSetID uint) (err error) + ListScaleSetInstances(_ context.Context, scalesetID uint) ([]params.Instance, error) +} + //go:generate mockery --name=Store type Store interface { RepoStore @@ -148,7 +158,9 @@ type Store interface { GithubCredentialsStore ControllerStore EntityPoolStore + ScaleSetsStore ControllerInfo() (params.ControllerInfo, error) InitController() (params.ControllerInfo, error) + GetGithubEntity(_ context.Context, entityType params.GithubEntityType, entityID string) (params.GithubEntity, error) } diff --git a/database/sql/instances.go b/database/sql/instances.go index 65cf0dba..d4bfd019 100644 --- a/database/sql/instances.go +++ b/database/sql/instances.go @@ -97,6 +97,8 @@ func (s *sqlDatabase) getPoolInstanceByName(poolID string, instanceName string) } return Instance{}, errors.Wrap(q.Error, "fetching pool instance by name") } + + instance.Pool = pool return instance, nil } @@ -134,7 +136,7 @@ func (s *sqlDatabase) GetPoolInstanceByName(_ context.Context, poolID string, in } func (s *sqlDatabase) GetInstanceByName(ctx context.Context, instanceName string) (params.Instance, error) { - instance, err := s.getInstanceByName(ctx, instanceName, "StatusMessages") + instance, err := s.getInstanceByName(ctx, instanceName, "StatusMessages", "Pool") if err != nil { return params.Instance{}, errors.Wrap(err, "fetching instance") } @@ -194,7 +196,7 @@ func (s *sqlDatabase) AddInstanceEvent(ctx context.Context, instanceName string, } func (s *sqlDatabase) UpdateInstance(ctx context.Context, instanceName string, param params.UpdateInstanceParams) (params.Instance, error) { - instance, err := s.getInstanceByName(ctx, instanceName) + instance, err := s.getInstanceByName(ctx, instanceName, "Pool") if err != nil { return params.Instance{}, errors.Wrap(err, "updating instance") } @@ -272,7 +274,7 @@ func (s *sqlDatabase) ListPoolInstances(_ context.Context, poolID string) ([]par } var instances []Instance - query := s.conn.Model(&Instance{}).Preload("Job").Where("pool_id = ?", u) + query := s.conn.Model(&Instance{}).Preload("Job", "Pool").Where("pool_id = ?", u) if err := query.Find(&instances); err.Error != nil { return nil, errors.Wrap(err.Error, "fetching instances") @@ -290,7 +292,7 @@ func (s *sqlDatabase) ListPoolInstances(_ context.Context, poolID string) ([]par func (s *sqlDatabase) ListScaleSetInstances(_ context.Context, scalesetID uint) ([]params.Instance, error) { var instances []Instance - query := s.conn.Model(&Instance{}).Preload("Job").Where("scale_set_id = ?", scalesetID) + query := s.conn.Model(&Instance{}).Preload("Job", "ScaleSet").Where("scale_set_fk_id = ?", scalesetID) if err := query.Find(&instances); err.Error != nil { return nil, errors.Wrap(err.Error, "fetching instances") @@ -310,7 +312,7 @@ func (s *sqlDatabase) ListScaleSetInstances(_ context.Context, scalesetID uint) func (s *sqlDatabase) ListAllInstances(_ context.Context) ([]params.Instance, error) { var instances []Instance - q := s.conn.Model(&Instance{}).Preload("Job").Find(&instances) + q := s.conn.Model(&Instance{}).Preload("Job", "Pool", "ScaleSet").Find(&instances) if q.Error != nil { return nil, errors.Wrap(q.Error, "fetching instances") } diff --git a/database/sql/pools.go b/database/sql/pools.go index fdcf3f5a..7454b1ef 100644 --- a/database/sql/pools.go +++ b/database/sql/pools.go @@ -427,7 +427,10 @@ func (s *sqlDatabase) ListEntityInstances(_ context.Context, entity params.Githu } ret := []params.Instance{} for _, pool := range pools { - for _, instance := range pool.Instances { + instances := pool.Instances + pool.Instances = nil + for _, instance := range instances { + instance.Pool = pool paramsInstance, err := s.sqlToParamsInstance(instance) if err != nil { return nil, errors.Wrap(err, "fetching instance") diff --git a/database/sql/util.go b/database/sql/util.go index c5e412a9..fb627814 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -15,6 +15,7 @@ package sql import ( + "context" "encoding/json" "fmt" @@ -60,7 +61,6 @@ func (s *sqlDatabase) sqlToParamsInstance(instance Instance) (params.Instance, e OSArch: instance.OSArch, Status: instance.Status, RunnerStatus: instance.RunnerStatus, - PoolID: instance.PoolID.String(), CallbackURL: instance.CallbackURL, MetadataURL: instance.MetadataURL, StatusMessages: []params.StatusMessage{}, @@ -75,6 +75,22 @@ func (s *sqlDatabase) sqlToParamsInstance(instance Instance) (params.Instance, e if instance.ScaleSetFkID != nil { ret.ScaleSetID = *instance.ScaleSetFkID + ret.ProviderName = instance.ScaleSet.ProviderName + ret.RunnerBootstrapTimeout = instance.ScaleSet.RunnerBootstrapTimeout + } + + if instance.PoolID != uuid.Nil { + ret.PoolID = instance.PoolID.String() + ret.ProviderName = instance.Pool.ProviderName + ret.RunnerBootstrapTimeout = instance.Pool.RunnerBootstrapTimeout + } + + if ret.ScaleSetID == 0 && ret.PoolID == "" { + return params.Instance{}, errors.New("missing pool or scale set id") + } + + if ret.ScaleSetID != 0 && ret.PoolID != "" { + return params.Instance{}, errors.New("both pool and scale set ids are set") } if instance.Job != nil { @@ -591,3 +607,27 @@ func (s *sqlDatabase) sendNotify(entityType dbCommon.DatabaseEntityType, op dbCo } return s.producer.Notify(message) } + +func (s *sqlDatabase) GetGithubEntity(_ context.Context, entityType params.GithubEntityType, entityID string) (params.GithubEntity, error) { + var ghEntity params.EntityGetter + var err error + switch entityType { + case params.GithubEntityTypeEnterprise: + ghEntity, err = s.GetEnterpriseByID(s.ctx, entityID) + case params.GithubEntityTypeOrganization: + ghEntity, err = s.GetOrganizationByID(s.ctx, entityID) + case params.GithubEntityTypeRepository: + ghEntity, err = s.GetRepositoryByID(s.ctx, entityID) + default: + return params.GithubEntity{}, errors.Wrap(runnerErrors.ErrBadRequest, "invalid entity type") + } + if err != nil { + return params.GithubEntity{}, errors.Wrap(err, "failed to get ") + } + + entity, err := ghEntity.GetEntity() + if err != nil { + return params.GithubEntity{}, errors.Wrap(err, "failed to get entity") + } + return entity, nil +} diff --git a/database/watcher/filters.go b/database/watcher/filters.go index aa5131b1..0c259bce 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -260,3 +260,24 @@ func WithScaleSetInstanceFilter(scaleset params.ScaleSet) dbCommon.PayloadFilter return instance.ScaleSetID == scaleset.ID } } + +// EntityTypeCallbackFilter is a callback function that takes a ChangePayload and returns a boolean. +// This callback type is used in the WithEntityTypeAndCallbackFilter (and potentially others) when +// a filter needs to delegate logic to a specific callback function. +type EntityTypeCallbackFilter func(payload dbCommon.ChangePayload) (bool, error) + +// WithEntityTypeAndCallbackFilter returns a filter function that filters payloads by entity type and the +// result of a callback function. +func WithEntityTypeAndCallbackFilter(entityType dbCommon.DatabaseEntityType, callback EntityTypeCallbackFilter) dbCommon.PayloadFilterFunc { + return func(payload dbCommon.ChangePayload) bool { + if payload.EntityType != entityType { + return false + } + + ok, err := callback(payload) + if err != nil { + return false + } + return ok + } +} diff --git a/params/interfaces.go b/params/interfaces.go new file mode 100644 index 00000000..95f02a9a --- /dev/null +++ b/params/interfaces.go @@ -0,0 +1,7 @@ +package params + +// EntityGetter is implemented by all github entities (repositories, organizations and enterprises). +// It defines the GetEntity() function which returns a github entity. +type EntityGetter interface { + GetEntity() (GithubEntity, error) +} diff --git a/params/params.go b/params/params.go index 68227dd2..b0a6492e 100644 --- a/params/params.go +++ b/params/params.go @@ -153,6 +153,10 @@ type Instance struct { // instance in the provider. ProviderID string `json:"provider_id,omitempty"` + // ProviderName is the name of the IaaS where the instance was + // created. + ProviderName string `json:"provider_name"` + // AgentID is the github runner agent ID. AgentID int64 `json:"agent_id,omitempty"` @@ -212,6 +216,10 @@ type Instance struct { // Job is the current job that is being serviced by this runner. Job *Job `json:"job,omitempty"` + // RunnerBootstrapTimeout is the timeout in minutes after which the runner deployment + // will be considered failed. This value is caried over from the pool or scale set. + RunnerBootstrapTimeout uint `json:"runner_bootstrap_timeout,omitempty"` + // Do not serialize sensitive info. CallbackURL string `json:"-"` MetadataURL string `json:"-"` @@ -229,6 +237,13 @@ func (i Instance) GetID() string { return i.ID } +func (i Instance) RunnerTimeout() uint { + if i.RunnerBootstrapTimeout == 0 { + return appdefaults.DefaultRunnerBootstrapTimeout + } + return i.RunnerBootstrapTimeout +} + // used by swagger client generated code type Instances []Instance diff --git a/runner/enterprises.go b/runner/enterprises.go index 3e9e3b8c..fb3f528b 100644 --- a/runner/enterprises.go +++ b/runner/enterprises.go @@ -145,6 +145,15 @@ func (r *Runner) DeleteEnterprise(ctx context.Context, enterpriseID string) erro return runnerErrors.NewBadRequestError("enterprise has pools defined (%s)", strings.Join(poolIDs, ", ")) } + scaleSets, err := r.store.ListEntityScaleSets(ctx, entity) + if err != nil { + return errors.Wrap(err, "fetching enterprise scale sets") + } + + if len(scaleSets) > 0 { + return runnerErrors.NewBadRequestError("enterprise has scale sets defined; delete them first") + } + if err := r.poolManagerCtrl.DeleteEnterprisePoolManager(enterprise); err != nil { return errors.Wrap(err, "deleting enterprise pool manager") } diff --git a/runner/organizations.go b/runner/organizations.go index 39aa788b..4b5e3fd7 100644 --- a/runner/organizations.go +++ b/runner/organizations.go @@ -159,6 +159,15 @@ func (r *Runner) DeleteOrganization(ctx context.Context, orgID string, keepWebho return runnerErrors.NewBadRequestError("org has pools defined (%s)", strings.Join(poolIDs, ", ")) } + scaleSets, err := r.store.ListEntityScaleSets(ctx, entity) + if err != nil { + return errors.Wrap(err, "fetching organization scale sets") + } + + if len(scaleSets) > 0 { + return runnerErrors.NewBadRequestError("organization has scale sets defined; delete them first") + } + if !keepWebhook && r.config.Default.EnableWebhookManagement { poolMgr, err := r.poolManagerCtrl.GetOrgPoolManager(org) if err != nil { diff --git a/runner/pool/pool.go b/runner/pool/pool.go index f6c97633..3ec72dad 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -415,6 +415,11 @@ func (r *basePoolManager) cleanupOrphanedProviderRunners(runners []*github.Runne } for _, instance := range dbInstances { + if instance.ScaleSetID != 0 { + // ignore scale set instances. + continue + } + lockAcquired, err := locking.TryLock(instance.Name) if !lockAcquired || err != nil { slog.DebugContext( @@ -433,14 +438,9 @@ func (r *basePoolManager) cleanupOrphanedProviderRunners(runners []*github.Runne continue } - pool, err := r.store.GetEntityPool(r.ctx, r.entity, instance.PoolID) - if err != nil { - return errors.Wrap(err, "fetching instance pool info") - } - switch instance.RunnerStatus { case params.RunnerPending, params.RunnerInstalling: - if time.Since(instance.UpdatedAt).Minutes() < float64(pool.RunnerTimeout()) { + if time.Since(instance.UpdatedAt).Minutes() < float64(instance.RunnerTimeout()) { // runner is still installing. We give it a chance to finish. slog.DebugContext( r.ctx, "runner is still installing, give it a chance to finish", @@ -491,6 +491,11 @@ func (r *basePoolManager) reapTimedOutRunners(runners []*github.Runner) error { } for _, instance := range dbInstances { + if instance.ScaleSetID != 0 { + // ignore scale set instances. + continue + } + slog.DebugContext( r.ctx, "attempting to lock instance", "runner_name", instance.Name) @@ -503,11 +508,7 @@ func (r *basePoolManager) reapTimedOutRunners(runners []*github.Runner) error { } defer locking.Unlock(instance.Name, false) - pool, err := r.store.GetEntityPool(r.ctx, r.entity, instance.PoolID) - if err != nil { - return errors.Wrap(err, "fetching instance pool info") - } - if time.Since(instance.UpdatedAt).Minutes() < float64(pool.RunnerTimeout()) { + if time.Since(instance.UpdatedAt).Minutes() < float64(instance.RunnerTimeout()) { continue } @@ -602,13 +603,13 @@ func (r *basePoolManager) cleanupOrphanedGithubRunners(runners []*github.Runner) } // check if the provider still has the instance. - provider, ok := r.providers[pool.ProviderName] + provider, ok := r.providers[dbInstance.ProviderName] if !ok { - return fmt.Errorf("unknown provider %s for pool %s", pool.ProviderName, pool.ID) + return fmt.Errorf("unknown provider %s for pool %s", dbInstance.ProviderName, dbInstance.PoolID) } var poolInstances []commonParams.ProviderInstance - poolInstances, ok = poolInstanceCache[pool.ID] + poolInstances, ok = poolInstanceCache[dbInstance.PoolID] if !ok { slog.DebugContext( r.ctx, "updating instances cache for pool", @@ -620,9 +621,9 @@ func (r *basePoolManager) cleanupOrphanedGithubRunners(runners []*github.Runner) } poolInstances, err = provider.ListInstances(r.ctx, pool.ID, listInstancesParams) if err != nil { - return errors.Wrapf(err, "fetching instances for pool %s", pool.ID) + return errors.Wrapf(err, "fetching instances for pool %s", dbInstance.PoolID) } - poolInstanceCache[pool.ID] = poolInstances + poolInstanceCache[dbInstance.PoolID] = poolInstances } lockAcquired, err := locking.TryLock(dbInstance.Name) @@ -1348,9 +1349,9 @@ func (r *basePoolManager) deleteInstanceFromProvider(ctx context.Context, instan return errors.Wrap(err, "fetching pool") } - provider, ok := r.providers[pool.ProviderName] + provider, ok := r.providers[instance.ProviderName] if !ok { - return fmt.Errorf("unknown provider %s for pool %s", pool.ProviderName, pool.ID) + return fmt.Errorf("unknown provider %s for pool %s", instance.ProviderName, instance.PoolID) } identifier := instance.ProviderID @@ -1386,6 +1387,11 @@ func (r *basePoolManager) deletePendingInstances() error { slog.DebugContext( r.ctx, "removing instances in pending_delete") for _, instance := range instances { + if instance.ScaleSetID != 0 { + // instance is part of a scale set. Skip. + continue + } + if instance.Status != commonParams.InstancePendingDelete && instance.Status != commonParams.InstancePendingForceDelete { // not in pending_delete status. Skip. continue @@ -1493,6 +1499,11 @@ func (r *basePoolManager) addPendingInstances() error { return fmt.Errorf("failed to fetch instances from store: %w", err) } for _, instance := range instances { + if instance.ScaleSetID != 0 { + // instance is part of a scale set. Skip. + continue + } + if instance.Status != commonParams.InstancePendingCreate { // not in pending_create status. Skip. continue diff --git a/runner/repositories.go b/runner/repositories.go index 5edff6ff..ab4f8e90 100644 --- a/runner/repositories.go +++ b/runner/repositories.go @@ -158,6 +158,15 @@ func (r *Runner) DeleteRepository(ctx context.Context, repoID string, keepWebhoo return runnerErrors.NewBadRequestError("repo has pools defined (%s)", strings.Join(poolIDs, ", ")) } + scaleSets, err := r.store.ListEntityScaleSets(ctx, entity) + if err != nil { + return errors.Wrap(err, "fetching repo scale sets") + } + + if len(scaleSets) > 0 { + return runnerErrors.NewBadRequestError("repo has scale sets defined; delete them first") + } + if !keepWebhook && r.config.Default.EnableWebhookManagement { poolMgr, err := r.poolManagerCtrl.GetRepoPoolManager(repo) if err != nil { diff --git a/runner/scalesets.go b/runner/scalesets.go new file mode 100644 index 00000000..5e8123b0 --- /dev/null +++ b/runner/scalesets.go @@ -0,0 +1,306 @@ +// Copyright 2025 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 runner + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + + runnerErrors "github.com/cloudbase/garm-provider-common/errors" + "github.com/cloudbase/garm/auth" + "github.com/cloudbase/garm/params" + "github.com/cloudbase/garm/util/appdefaults" + "github.com/cloudbase/garm/util/github" + "github.com/cloudbase/garm/util/github/scalesets" + "github.com/pkg/errors" +) + +func (r *Runner) ListAllScaleSets(ctx context.Context) ([]params.ScaleSet, error) { + if !auth.IsAdmin(ctx) { + return []params.ScaleSet{}, runnerErrors.ErrUnauthorized + } + + scalesets, err := r.store.ListAllScaleSets(ctx) + if err != nil { + return nil, errors.Wrap(err, "fetching pools") + } + return scalesets, nil +} + +func (r *Runner) GetScaleSetByID(ctx context.Context, scaleSet uint) (params.ScaleSet, error) { + if !auth.IsAdmin(ctx) { + return params.ScaleSet{}, runnerErrors.ErrUnauthorized + } + + set, err := r.store.GetScaleSetByID(ctx, scaleSet) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "fetching scale set") + } + return set, nil +} + +func (r *Runner) DeleteScaleSetByID(ctx context.Context, scaleSetID uint) error { + if !auth.IsAdmin(ctx) { + return runnerErrors.ErrUnauthorized + } + + scaleSet, err := r.store.GetScaleSetByID(ctx, scaleSetID) + if err != nil { + if !errors.Is(err, runnerErrors.ErrNotFound) { + return errors.Wrap(err, "fetching scale set") + } + return nil + } + + if len(scaleSet.Instances) > 0 { + return runnerErrors.NewBadRequestError("scale set has runners") + } + + if scaleSet.Enabled { + return runnerErrors.NewBadRequestError("scale set is enabled; disable it first") + } + + paramEntity, err := scaleSet.GithubEntity() + if err != nil { + return errors.Wrap(err, "getting entity") + } + + entity, err := r.store.GetGithubEntity(ctx, paramEntity.EntityType, paramEntity.ID) + if err != nil { + return errors.Wrap(err, "getting entity") + } + + ghCli, err := github.Client(ctx, entity) + if err != nil { + return errors.Wrap(err, "creating github client") + } + + scalesetCli, err := scalesets.NewClient(ghCli) + if err != nil { + return errors.Wrap(err, "getting scaleset client") + } + + if err := scalesetCli.DeleteRunnerScaleSet(ctx, scaleSet.ScaleSetID); err != nil { + if !errors.Is(err, runnerErrors.ErrNotFound) { + slog.InfoContext(ctx, "scale set not found", "scale_set_id", scaleSet.ScaleSetID) + return nil + } + slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to delete scale set from github") + return errors.Wrap(err, "deleting scale set from github") + } + if err := r.store.DeleteScaleSetByID(ctx, scaleSetID); err != nil { + return errors.Wrap(err, "deleting scale set") + } + return nil +} + +func (r *Runner) UpdateScaleSetByID(ctx context.Context, scaleSetID uint, param params.UpdateScaleSetParams) (params.ScaleSet, error) { + if !auth.IsAdmin(ctx) { + return params.ScaleSet{}, runnerErrors.ErrUnauthorized + } + + scaleSet, err := r.store.GetScaleSetByID(ctx, scaleSetID) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "fetching scale set") + } + + maxRunners := scaleSet.MaxRunners + minIdleRunners := scaleSet.MinIdleRunners + + if param.MaxRunners != nil { + maxRunners = *param.MaxRunners + } + if param.MinIdleRunners != nil { + minIdleRunners = *param.MinIdleRunners + } + + if param.RunnerBootstrapTimeout != nil && *param.RunnerBootstrapTimeout == 0 { + return params.ScaleSet{}, runnerErrors.NewBadRequestError("runner_bootstrap_timeout cannot be 0") + } + + if minIdleRunners > maxRunners { + return params.ScaleSet{}, runnerErrors.NewBadRequestError("min_idle_runners cannot be larger than max_runners") + } + + paramEntity, err := scaleSet.GithubEntity() + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "getting entity") + } + + entity, err := r.store.GetGithubEntity(ctx, paramEntity.EntityType, paramEntity.ID) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "getting entity") + } + + ghCli, err := github.Client(ctx, entity) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "creating github client") + } + + callback := func(old, new params.ScaleSet) error { + scalesetCli, err := scalesets.NewClient(ghCli) + if err != nil { + return errors.Wrap(err, "getting scaleset client") + } + + updateParams := params.RunnerScaleSet{} + hasUpdates := false + if old.Name != new.Name { + updateParams.Name = new.Name + hasUpdates = true + } + + if old.Enabled != new.Enabled { + updateParams.Enabled = &new.Enabled + hasUpdates = true + } + + if old.GitHubRunnerGroup != new.GitHubRunnerGroup { + runnerGroup, err := scalesetCli.GetRunnerGroupByName(ctx, new.GitHubRunnerGroup) + if err != nil { + return fmt.Errorf("error fetching runner group from github: %w", err) + } + updateParams.RunnerGroupID = int(runnerGroup.ID) + hasUpdates = true + } + + if old.DisableUpdate != new.DisableUpdate { + updateParams.RunnerSetting.DisableUpdate = new.DisableUpdate + hasUpdates = true + } + + if hasUpdates { + result, err := scalesetCli.UpdateRunnerScaleSet(ctx, new.ScaleSetID, updateParams) + if err != nil { + return fmt.Errorf("failed to update scaleset in github: %w", err) + } + asJs, _ := json.MarshalIndent(result, "", " ") + slog.Info("update result", "data", string(asJs)) + } + return nil + } + + newScaleSet, err := r.store.UpdateEntityScaleSet(ctx, entity, scaleSetID, param, callback) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "updating pool") + } + return newScaleSet, nil +} + +func (r *Runner) CreateEntityScaleSet(ctx context.Context, entityType params.GithubEntityType, entityID string, param params.CreateScaleSetParams) (scaleSetRet params.ScaleSet, err error) { + if !auth.IsAdmin(ctx) { + return params.ScaleSet{}, runnerErrors.ErrUnauthorized + } + + if param.RunnerBootstrapTimeout == 0 { + param.RunnerBootstrapTimeout = appdefaults.DefaultRunnerBootstrapTimeout + } + + if param.GitHubRunnerGroup == "" { + param.GitHubRunnerGroup = "Default" + } + + entity, err := r.store.GetGithubEntity(ctx, entityType, entityID) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "getting entity") + } + + ghCli, err := github.Client(ctx, entity) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "creating github client") + } + + scalesetCli, err := scalesets.NewClient(ghCli) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "getting scaleset client") + } + var runnerGroupID int = 1 + if param.GitHubRunnerGroup != "Default" { + runnerGroup, err := scalesetCli.GetRunnerGroupByName(ctx, param.GitHubRunnerGroup) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "getting runner group") + } + runnerGroupID = int(runnerGroup.ID) + } + + createParam := ¶ms.RunnerScaleSet{ + Name: param.Name, + RunnerGroupID: runnerGroupID, + Labels: []params.Label{ + { + Name: param.Name, + Type: "System", + }, + }, + RunnerSetting: params.RunnerSetting{ + Ephemeral: true, + DisableUpdate: param.DisableUpdate, + }, + Enabled: ¶m.Enabled, + } + + runnerScaleSet, err := scalesetCli.CreateRunnerScaleSet(ctx, createParam) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "creating runner scale set") + } + + asJs, _ := json.MarshalIndent(runnerScaleSet, "", " ") + slog.InfoContext(ctx, "scale set", "data", string(asJs)) + + defer func() { + if err != nil { + if innerErr := scalesetCli.DeleteRunnerScaleSet(ctx, runnerScaleSet.ID); innerErr != nil { + slog.With(slog.Any("error", innerErr)).ErrorContext(ctx, "failed to cleanup scale set") + } + } + }() + param.ScaleSetID = runnerScaleSet.ID + + scaleSet, err := r.store.CreateEntityScaleSet(ctx, entity, param) + if err != nil { + return params.ScaleSet{}, errors.Wrap(err, "creating scale set") + } + + return scaleSet, nil +} + +func (r *Runner) ListScaleSetInstances(ctx context.Context, scalesetID uint) ([]params.Instance, error) { + if !auth.IsAdmin(ctx) { + return nil, runnerErrors.ErrUnauthorized + } + + instances, err := r.store.ListScaleSetInstances(ctx, scalesetID) + if err != nil { + return []params.Instance{}, errors.Wrap(err, "fetching instances") + } + return instances, nil +} + +func (r *Runner) ListEntityScaleSets(ctx context.Context, entityType params.GithubEntityType, entityID string) ([]params.ScaleSet, error) { + if !auth.IsAdmin(ctx) { + return []params.ScaleSet{}, runnerErrors.ErrUnauthorized + } + entity := params.GithubEntity{ + ID: entityID, + EntityType: entityType, + } + scaleSets, err := r.store.ListEntityScaleSets(ctx, entity) + if err != nil { + return nil, errors.Wrap(err, "fetching scale sets") + } + return scaleSets, nil +} diff --git a/util/github/scalesets/message_sessions.go b/util/github/scalesets/message_sessions.go index 5ecdd94d..e4152e08 100644 --- a/util/github/scalesets/message_sessions.go +++ b/util/github/scalesets/message_sessions.go @@ -80,7 +80,9 @@ func (m *MessageSession) loop() { // work, if it's credentials issues, users can update them. slog.With(slog.Any("error", err)).ErrorContext(m.ctx, "failed to refresh message queue token") m.lastErr = err + continue } + m.lastErr = nil } } }