Use separate endpoints to list tools
Users and instances now have different endpoint for listing tools. Moreover, users can now use a flag to see what tools are available upstream if sync is off: garm-cli controller tools list --upstream Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
9a9080c180
commit
61b4b4cadd
12 changed files with 828 additions and 440 deletions
|
|
@ -85,7 +85,65 @@ func (a *APIController) InstanceGARMToolsHandler(w http.ResponseWriter, r *http.
|
|||
}
|
||||
}
|
||||
|
||||
tools, err := a.r.GetGARMTools(ctx, uint64(pageLocation), uint64(pageSize))
|
||||
tools, err := a.r.GetAgentGARMTools(ctx, uint64(pageLocation), uint64(pageSize))
|
||||
if err != nil {
|
||||
handleError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(tools); err != nil {
|
||||
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
|
||||
}
|
||||
}
|
||||
|
||||
// swagger:route GET /tools/garm-agent tools AdminGarmAgentList
|
||||
//
|
||||
// List GARM agent tools for admin users.
|
||||
//
|
||||
// Parameters:
|
||||
// + name: page
|
||||
// description: The page at which to list.
|
||||
// type: integer
|
||||
// in: query
|
||||
// required: false
|
||||
// + name: pageSize
|
||||
// description: Number of items per page.
|
||||
// type: integer
|
||||
// in: query
|
||||
// required: false
|
||||
// + name: upstream
|
||||
// description: If true, list tools from the upstream cached release instead of the local object store.
|
||||
// type: boolean
|
||||
// in: query
|
||||
// required: false
|
||||
//
|
||||
// Responses:
|
||||
// 200: GARMAgentToolsPaginatedResponse
|
||||
// 400: APIErrorResponse
|
||||
func (a *APIController) AdminGARMToolsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
var pageLocation int64
|
||||
var pageSize int64 = 25
|
||||
pageArg := r.URL.Query().Get("page")
|
||||
pageSizeArg := r.URL.Query().Get("pageSize")
|
||||
|
||||
if pageArg != "" {
|
||||
pageInt, err := strconv.ParseInt(pageArg, 10, 64)
|
||||
if err == nil && pageInt >= 0 {
|
||||
pageLocation = pageInt
|
||||
}
|
||||
}
|
||||
if pageSizeArg != "" {
|
||||
pageSizeInt, err := strconv.ParseInt(pageSizeArg, 10, 64)
|
||||
if err == nil && pageSizeInt >= 0 {
|
||||
pageSize = pageSizeInt
|
||||
}
|
||||
}
|
||||
|
||||
upstream := r.URL.Query().Get("upstream") == "true"
|
||||
tools, err := a.r.GetGARMTools(ctx, uint64(pageLocation), uint64(pageSize), upstream)
|
||||
if err != nil {
|
||||
handleError(ctx, w, err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -267,8 +267,8 @@ func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware
|
|||
///////////////////////////////////////////////////////
|
||||
// Tools URLs (garm agent, cached gitea runner, etc) //
|
||||
///////////////////////////////////////////////////////
|
||||
apiRouter.Handle("/tools/garm-agent/", http.HandlerFunc(han.InstanceGARMToolsHandler)).Methods("GET", "OPTIONS")
|
||||
apiRouter.Handle("/tools/garm-agent", http.HandlerFunc(han.InstanceGARMToolsHandler)).Methods("GET", "OPTIONS")
|
||||
apiRouter.Handle("/tools/garm-agent/", http.HandlerFunc(han.AdminGARMToolsHandler)).Methods("GET", "OPTIONS")
|
||||
apiRouter.Handle("/tools/garm-agent", http.HandlerFunc(han.AdminGARMToolsHandler)).Methods("GET", "OPTIONS")
|
||||
// Upload garm agent tool
|
||||
apiRouter.Handle("/tools/garm-agent/", http.HandlerFunc(han.UploadGARMAgentToolHandler)).Methods("POST", "OPTIONS")
|
||||
apiRouter.Handle("/tools/garm-agent", http.HandlerFunc(han.UploadGARMAgentToolHandler)).Methods("POST", "OPTIONS")
|
||||
|
|
|
|||
|
|
@ -2603,7 +2603,7 @@ paths:
|
|||
- templates
|
||||
/tools/garm-agent:
|
||||
get:
|
||||
operationId: GarmAgentList
|
||||
operationId: AdminGarmAgentList
|
||||
parameters:
|
||||
- description: The page at which to list.
|
||||
in: query
|
||||
|
|
@ -2613,6 +2613,10 @@ paths:
|
|||
in: query
|
||||
name: pageSize
|
||||
type: integer
|
||||
- description: If true, list tools from the upstream cached release instead of the local object store.
|
||||
in: query
|
||||
name: upstream
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: GARMAgentToolsPaginatedResponse
|
||||
|
|
@ -2622,7 +2626,7 @@ paths:
|
|||
description: APIErrorResponse
|
||||
schema:
|
||||
$ref: '#/definitions/APIErrorResponse'
|
||||
summary: List GARM agent tools.
|
||||
summary: List GARM agent tools for admin users.
|
||||
tags:
|
||||
- tools
|
||||
post:
|
||||
|
|
|
|||
232
client/tools/admin_garm_agent_list_parameters.go
Normal file
232
client/tools/admin_garm_agent_list_parameters.go
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package tools
|
||||
|
||||
// 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"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// NewAdminGarmAgentListParams creates a new AdminGarmAgentListParams 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 NewAdminGarmAgentListParams() *AdminGarmAgentListParams {
|
||||
return &AdminGarmAgentListParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdminGarmAgentListParamsWithTimeout creates a new AdminGarmAgentListParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewAdminGarmAgentListParamsWithTimeout(timeout time.Duration) *AdminGarmAgentListParams {
|
||||
return &AdminGarmAgentListParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdminGarmAgentListParamsWithContext creates a new AdminGarmAgentListParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewAdminGarmAgentListParamsWithContext(ctx context.Context) *AdminGarmAgentListParams {
|
||||
return &AdminGarmAgentListParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdminGarmAgentListParamsWithHTTPClient creates a new AdminGarmAgentListParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewAdminGarmAgentListParamsWithHTTPClient(client *http.Client) *AdminGarmAgentListParams {
|
||||
return &AdminGarmAgentListParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
AdminGarmAgentListParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the admin garm agent list operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type AdminGarmAgentListParams struct {
|
||||
|
||||
/* Page.
|
||||
|
||||
The page at which to list.
|
||||
*/
|
||||
Page *int64
|
||||
|
||||
/* PageSize.
|
||||
|
||||
Number of items per page.
|
||||
*/
|
||||
PageSize *int64
|
||||
|
||||
/* Upstream.
|
||||
|
||||
If true, list tools from the upstream cached release instead of the local object store.
|
||||
*/
|
||||
Upstream *bool
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the admin garm agent list params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *AdminGarmAgentListParams) WithDefaults() *AdminGarmAgentListParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the admin garm agent list params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *AdminGarmAgentListParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) WithTimeout(timeout time.Duration) *AdminGarmAgentListParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) WithContext(ctx context.Context) *AdminGarmAgentListParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) WithHTTPClient(client *http.Client) *AdminGarmAgentListParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithPage adds the page to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) WithPage(page *int64) *AdminGarmAgentListParams {
|
||||
o.SetPage(page)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPage adds the page to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) SetPage(page *int64) {
|
||||
o.Page = page
|
||||
}
|
||||
|
||||
// WithPageSize adds the pageSize to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) WithPageSize(pageSize *int64) *AdminGarmAgentListParams {
|
||||
o.SetPageSize(pageSize)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPageSize adds the pageSize to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) SetPageSize(pageSize *int64) {
|
||||
o.PageSize = pageSize
|
||||
}
|
||||
|
||||
// WithUpstream adds the upstream to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) WithUpstream(upstream *bool) *AdminGarmAgentListParams {
|
||||
o.SetUpstream(upstream)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetUpstream adds the upstream to the admin garm agent list params
|
||||
func (o *AdminGarmAgentListParams) SetUpstream(upstream *bool) {
|
||||
o.Upstream = upstream
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *AdminGarmAgentListParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
|
||||
if o.Page != nil {
|
||||
|
||||
// query param page
|
||||
var qrPage int64
|
||||
|
||||
if o.Page != nil {
|
||||
qrPage = *o.Page
|
||||
}
|
||||
qPage := swag.FormatInt64(qrPage)
|
||||
if qPage != "" {
|
||||
|
||||
if err := r.SetQueryParam("page", qPage); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.PageSize != nil {
|
||||
|
||||
// query param pageSize
|
||||
var qrPageSize int64
|
||||
|
||||
if o.PageSize != nil {
|
||||
qrPageSize = *o.PageSize
|
||||
}
|
||||
qPageSize := swag.FormatInt64(qrPageSize)
|
||||
if qPageSize != "" {
|
||||
|
||||
if err := r.SetQueryParam("pageSize", qPageSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.Upstream != nil {
|
||||
|
||||
// query param upstream
|
||||
var qrUpstream bool
|
||||
|
||||
if o.Upstream != nil {
|
||||
qrUpstream = *o.Upstream
|
||||
}
|
||||
qUpstream := swag.FormatBool(qrUpstream)
|
||||
if qUpstream != "" {
|
||||
|
||||
if err := r.SetQueryParam("upstream", qUpstream); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
179
client/tools/admin_garm_agent_list_responses.go
Normal file
179
client/tools/admin_garm_agent_list_responses.go
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package tools
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
// AdminGarmAgentListReader is a Reader for the AdminGarmAgentList structure.
|
||||
type AdminGarmAgentListReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *AdminGarmAgentListReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewAdminGarmAgentListOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
case 400:
|
||||
result := NewAdminGarmAgentListBadRequest()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, result
|
||||
default:
|
||||
return nil, runtime.NewAPIError("[GET /tools/garm-agent] AdminGarmAgentList", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdminGarmAgentListOK creates a AdminGarmAgentListOK with default headers values
|
||||
func NewAdminGarmAgentListOK() *AdminGarmAgentListOK {
|
||||
return &AdminGarmAgentListOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
AdminGarmAgentListOK describes a response with status code 200, with default header values.
|
||||
|
||||
GARMAgentToolsPaginatedResponse
|
||||
*/
|
||||
type AdminGarmAgentListOK struct {
|
||||
Payload garm_params.GARMAgentToolsPaginatedResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this admin garm agent list o k response has a 2xx status code
|
||||
func (o *AdminGarmAgentListOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this admin garm agent list o k response has a 3xx status code
|
||||
func (o *AdminGarmAgentListOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this admin garm agent list o k response has a 4xx status code
|
||||
func (o *AdminGarmAgentListOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this admin garm agent list o k response has a 5xx status code
|
||||
func (o *AdminGarmAgentListOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this admin garm agent list o k response a status code equal to that given
|
||||
func (o *AdminGarmAgentListOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the admin garm agent list o k response
|
||||
func (o *AdminGarmAgentListOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] adminGarmAgentListOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] adminGarmAgentListOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListOK) GetPayload() garm_params.GARMAgentToolsPaginatedResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListOK) 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
|
||||
}
|
||||
|
||||
// NewAdminGarmAgentListBadRequest creates a AdminGarmAgentListBadRequest with default headers values
|
||||
func NewAdminGarmAgentListBadRequest() *AdminGarmAgentListBadRequest {
|
||||
return &AdminGarmAgentListBadRequest{}
|
||||
}
|
||||
|
||||
/*
|
||||
AdminGarmAgentListBadRequest describes a response with status code 400, with default header values.
|
||||
|
||||
APIErrorResponse
|
||||
*/
|
||||
type AdminGarmAgentListBadRequest struct {
|
||||
Payload apiserver_params.APIErrorResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this admin garm agent list bad request response has a 2xx status code
|
||||
func (o *AdminGarmAgentListBadRequest) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this admin garm agent list bad request response has a 3xx status code
|
||||
func (o *AdminGarmAgentListBadRequest) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this admin garm agent list bad request response has a 4xx status code
|
||||
func (o *AdminGarmAgentListBadRequest) IsClientError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsServerError returns true when this admin garm agent list bad request response has a 5xx status code
|
||||
func (o *AdminGarmAgentListBadRequest) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this admin garm agent list bad request response a status code equal to that given
|
||||
func (o *AdminGarmAgentListBadRequest) IsCode(code int) bool {
|
||||
return code == 400
|
||||
}
|
||||
|
||||
// Code gets the status code for the admin garm agent list bad request response
|
||||
func (o *AdminGarmAgentListBadRequest) Code() int {
|
||||
return 400
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListBadRequest) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] adminGarmAgentListBadRequest %s", 400, payload)
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListBadRequest) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] adminGarmAgentListBadRequest %s", 400, payload)
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListBadRequest) GetPayload() apiserver_params.APIErrorResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *AdminGarmAgentListBadRequest) 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
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package tools
|
||||
|
||||
// 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"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// NewGarmAgentListParams creates a new GarmAgentListParams 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 NewGarmAgentListParams() *GarmAgentListParams {
|
||||
return &GarmAgentListParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGarmAgentListParamsWithTimeout creates a new GarmAgentListParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewGarmAgentListParamsWithTimeout(timeout time.Duration) *GarmAgentListParams {
|
||||
return &GarmAgentListParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGarmAgentListParamsWithContext creates a new GarmAgentListParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewGarmAgentListParamsWithContext(ctx context.Context) *GarmAgentListParams {
|
||||
return &GarmAgentListParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGarmAgentListParamsWithHTTPClient creates a new GarmAgentListParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewGarmAgentListParamsWithHTTPClient(client *http.Client) *GarmAgentListParams {
|
||||
return &GarmAgentListParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GarmAgentListParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the garm agent list operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type GarmAgentListParams struct {
|
||||
|
||||
/* Page.
|
||||
|
||||
The page at which to list.
|
||||
*/
|
||||
Page *int64
|
||||
|
||||
/* PageSize.
|
||||
|
||||
Number of items per page.
|
||||
*/
|
||||
PageSize *int64
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the garm agent list params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *GarmAgentListParams) WithDefaults() *GarmAgentListParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the garm agent list params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *GarmAgentListParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the garm agent list params
|
||||
func (o *GarmAgentListParams) WithTimeout(timeout time.Duration) *GarmAgentListParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the garm agent list params
|
||||
func (o *GarmAgentListParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the garm agent list params
|
||||
func (o *GarmAgentListParams) WithContext(ctx context.Context) *GarmAgentListParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the garm agent list params
|
||||
func (o *GarmAgentListParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the garm agent list params
|
||||
func (o *GarmAgentListParams) WithHTTPClient(client *http.Client) *GarmAgentListParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the garm agent list params
|
||||
func (o *GarmAgentListParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithPage adds the page to the garm agent list params
|
||||
func (o *GarmAgentListParams) WithPage(page *int64) *GarmAgentListParams {
|
||||
o.SetPage(page)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPage adds the page to the garm agent list params
|
||||
func (o *GarmAgentListParams) SetPage(page *int64) {
|
||||
o.Page = page
|
||||
}
|
||||
|
||||
// WithPageSize adds the pageSize to the garm agent list params
|
||||
func (o *GarmAgentListParams) WithPageSize(pageSize *int64) *GarmAgentListParams {
|
||||
o.SetPageSize(pageSize)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPageSize adds the pageSize to the garm agent list params
|
||||
func (o *GarmAgentListParams) SetPageSize(pageSize *int64) {
|
||||
o.PageSize = pageSize
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *GarmAgentListParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
|
||||
if o.Page != nil {
|
||||
|
||||
// query param page
|
||||
var qrPage int64
|
||||
|
||||
if o.Page != nil {
|
||||
qrPage = *o.Page
|
||||
}
|
||||
qPage := swag.FormatInt64(qrPage)
|
||||
if qPage != "" {
|
||||
|
||||
if err := r.SetQueryParam("page", qPage); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.PageSize != nil {
|
||||
|
||||
// query param pageSize
|
||||
var qrPageSize int64
|
||||
|
||||
if o.PageSize != nil {
|
||||
qrPageSize = *o.PageSize
|
||||
}
|
||||
qPageSize := swag.FormatInt64(qrPageSize)
|
||||
if qPageSize != "" {
|
||||
|
||||
if err := r.SetQueryParam("pageSize", qPageSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package tools
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
// GarmAgentListReader is a Reader for the GarmAgentList structure.
|
||||
type GarmAgentListReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *GarmAgentListReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewGarmAgentListOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
case 400:
|
||||
result := NewGarmAgentListBadRequest()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, result
|
||||
default:
|
||||
return nil, runtime.NewAPIError("[GET /tools/garm-agent] GarmAgentList", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
// NewGarmAgentListOK creates a GarmAgentListOK with default headers values
|
||||
func NewGarmAgentListOK() *GarmAgentListOK {
|
||||
return &GarmAgentListOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
GarmAgentListOK describes a response with status code 200, with default header values.
|
||||
|
||||
GARMAgentToolsPaginatedResponse
|
||||
*/
|
||||
type GarmAgentListOK struct {
|
||||
Payload garm_params.GARMAgentToolsPaginatedResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this garm agent list o k response has a 2xx status code
|
||||
func (o *GarmAgentListOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this garm agent list o k response has a 3xx status code
|
||||
func (o *GarmAgentListOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this garm agent list o k response has a 4xx status code
|
||||
func (o *GarmAgentListOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this garm agent list o k response has a 5xx status code
|
||||
func (o *GarmAgentListOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this garm agent list o k response a status code equal to that given
|
||||
func (o *GarmAgentListOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
// Code gets the status code for the garm agent list o k response
|
||||
func (o *GarmAgentListOK) Code() int {
|
||||
return 200
|
||||
}
|
||||
|
||||
func (o *GarmAgentListOK) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] garmAgentListOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *GarmAgentListOK) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] garmAgentListOK %s", 200, payload)
|
||||
}
|
||||
|
||||
func (o *GarmAgentListOK) GetPayload() garm_params.GARMAgentToolsPaginatedResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *GarmAgentListOK) 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
|
||||
}
|
||||
|
||||
// NewGarmAgentListBadRequest creates a GarmAgentListBadRequest with default headers values
|
||||
func NewGarmAgentListBadRequest() *GarmAgentListBadRequest {
|
||||
return &GarmAgentListBadRequest{}
|
||||
}
|
||||
|
||||
/*
|
||||
GarmAgentListBadRequest describes a response with status code 400, with default header values.
|
||||
|
||||
APIErrorResponse
|
||||
*/
|
||||
type GarmAgentListBadRequest struct {
|
||||
Payload apiserver_params.APIErrorResponse
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this garm agent list bad request response has a 2xx status code
|
||||
func (o *GarmAgentListBadRequest) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this garm agent list bad request response has a 3xx status code
|
||||
func (o *GarmAgentListBadRequest) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this garm agent list bad request response has a 4xx status code
|
||||
func (o *GarmAgentListBadRequest) IsClientError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsServerError returns true when this garm agent list bad request response has a 5xx status code
|
||||
func (o *GarmAgentListBadRequest) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this garm agent list bad request response a status code equal to that given
|
||||
func (o *GarmAgentListBadRequest) IsCode(code int) bool {
|
||||
return code == 400
|
||||
}
|
||||
|
||||
// Code gets the status code for the garm agent list bad request response
|
||||
func (o *GarmAgentListBadRequest) Code() int {
|
||||
return 400
|
||||
}
|
||||
|
||||
func (o *GarmAgentListBadRequest) Error() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] garmAgentListBadRequest %s", 400, payload)
|
||||
}
|
||||
|
||||
func (o *GarmAgentListBadRequest) String() string {
|
||||
payload, _ := json.Marshal(o.Payload)
|
||||
return fmt.Sprintf("[GET /tools/garm-agent][%d] garmAgentListBadRequest %s", 400, payload)
|
||||
}
|
||||
|
||||
func (o *GarmAgentListBadRequest) GetPayload() apiserver_params.APIErrorResponse {
|
||||
return o.Payload
|
||||
}
|
||||
|
||||
func (o *GarmAgentListBadRequest) 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
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ type ClientOption func(*runtime.ClientOperation)
|
|||
|
||||
// ClientService is the interface for Client methods
|
||||
type ClientService interface {
|
||||
GarmAgentList(params *GarmAgentListParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GarmAgentListOK, error)
|
||||
AdminGarmAgentList(params *AdminGarmAgentListParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*AdminGarmAgentListOK, error)
|
||||
|
||||
UploadGARMAgentTool(params *UploadGARMAgentToolParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*UploadGARMAgentToolOK, error)
|
||||
|
||||
|
|
@ -64,22 +64,22 @@ type ClientService interface {
|
|||
}
|
||||
|
||||
/*
|
||||
GarmAgentList lists g a r m agent tools
|
||||
AdminGarmAgentList lists g a r m agent tools for admin users
|
||||
*/
|
||||
func (a *Client) GarmAgentList(params *GarmAgentListParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*GarmAgentListOK, error) {
|
||||
func (a *Client) AdminGarmAgentList(params *AdminGarmAgentListParams, authInfo runtime.ClientAuthInfoWriter, opts ...ClientOption) (*AdminGarmAgentListOK, error) {
|
||||
// TODO: Validate the params before sending
|
||||
if params == nil {
|
||||
params = NewGarmAgentListParams()
|
||||
params = NewAdminGarmAgentListParams()
|
||||
}
|
||||
op := &runtime.ClientOperation{
|
||||
ID: "GarmAgentList",
|
||||
ID: "AdminGarmAgentList",
|
||||
Method: "GET",
|
||||
PathPattern: "/tools/garm-agent",
|
||||
ProducesMediaTypes: []string{"application/json"},
|
||||
ConsumesMediaTypes: []string{"application/json"},
|
||||
Schemes: []string{"http"},
|
||||
Params: params,
|
||||
Reader: &GarmAgentListReader{formats: a.formats},
|
||||
Reader: &AdminGarmAgentListReader{formats: a.formats},
|
||||
AuthInfo: authInfo,
|
||||
Context: params.Context,
|
||||
Client: params.HTTPClient,
|
||||
|
|
@ -92,13 +92,13 @@ func (a *Client) GarmAgentList(params *GarmAgentListParams, authInfo runtime.Cli
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
success, ok := result.(*GarmAgentListOK)
|
||||
success, ok := result.(*AdminGarmAgentListOK)
|
||||
if ok {
|
||||
return success, nil
|
||||
}
|
||||
// unexpected success response
|
||||
// safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue
|
||||
msg := fmt.Sprintf("unexpected success response for GarmAgentList: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
msg := fmt.Sprintf("unexpected success response for AdminGarmAgentList: API contract not enforced by server. Client expected to get an error, but got: %T", result)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,25 +197,29 @@ var controllerToolsListCmd = &cobra.Command{
|
|||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List GARM agent tools",
|
||||
Long: `List all GARM agent tools available in the controller.`,
|
||||
Long: `List all GARM agent tools available in the controller. Use --upstream to list tools from the upstream cached release.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if needsInit {
|
||||
return errNeedsInitError
|
||||
}
|
||||
|
||||
showTools := apiClientTools.NewGarmAgentListParams()
|
||||
showTools := apiClientTools.NewAdminGarmAgentListParams()
|
||||
if cmd.Flags().Changed("page") {
|
||||
showTools.Page = &fileObjPage
|
||||
}
|
||||
if cmd.Flags().Changed("page-size") {
|
||||
showTools.PageSize = &fileObjPageSize
|
||||
}
|
||||
response, err := apiCli.Tools.GarmAgentList(showTools, authToken)
|
||||
if cmd.Flags().Changed("upstream") {
|
||||
upstreamVal := true
|
||||
showTools.Upstream = &upstreamVal
|
||||
}
|
||||
response, err := apiCli.Tools.AdminGarmAgentList(showTools, authToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatGARMToolsList(response.Payload)
|
||||
formatGARMToolsList(response.Payload, cmd.Flags().Changed("upstream"))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
@ -521,6 +525,7 @@ func init() {
|
|||
|
||||
controllerToolsListCmd.Flags().Int64Var(&fileObjPage, "page", 0, "The tools page to display")
|
||||
controllerToolsListCmd.Flags().Int64Var(&fileObjPageSize, "page-size", 25, "Total number of results per page")
|
||||
controllerToolsListCmd.Flags().Bool("upstream", false, "List tools from the upstream cached release instead of the local object store")
|
||||
|
||||
controllerToolsUploadCmd.Flags().StringVar(&toolFilePath, "file", "", "Path to the garm-agent binary file (required)")
|
||||
controllerToolsUploadCmd.Flags().StringVar(&toolOSType, "os", "", "Operating system: linux or windows (required)")
|
||||
|
|
@ -550,17 +555,25 @@ func init() {
|
|||
rootCmd.AddCommand(controllerCmd)
|
||||
}
|
||||
|
||||
func formatGARMToolsList(files params.GARMAgentToolsPaginatedResponse) {
|
||||
func formatGARMToolsList(files params.GARMAgentToolsPaginatedResponse, upstream bool) {
|
||||
if outputFormat == common.OutputFormatJSON {
|
||||
printAsJSON(files)
|
||||
return
|
||||
}
|
||||
t := table.NewWriter()
|
||||
// Define column count
|
||||
numCols := 8
|
||||
t.Style().Options.SeparateHeader = true
|
||||
t.Style().Options.SeparateRows = true
|
||||
|
||||
var numCols int
|
||||
var header table.Row
|
||||
if upstream {
|
||||
numCols = 6
|
||||
header = table.Row{"Name", "Size", "Version", "OS Type", "OS Architecture", "Download URL"}
|
||||
} else {
|
||||
numCols = 8
|
||||
header = table.Row{"ID", "Name", "Size", "Version", "OS Type", "OS Architecture", "Created", "Updated"}
|
||||
}
|
||||
|
||||
// Page header - fill all columns with the same text
|
||||
pageHeaderText := fmt.Sprintf("Page %d of %d", files.CurrentPage, files.Pages)
|
||||
pageHeader := make(table.Row, numCols)
|
||||
|
|
@ -571,18 +584,23 @@ func formatGARMToolsList(files params.GARMAgentToolsPaginatedResponse) {
|
|||
AutoMerge: true,
|
||||
AutoMergeAlign: text.AlignCenter,
|
||||
})
|
||||
// Column headers
|
||||
header := table.Row{"ID", "Name", "Size", "Version", "OS Type", "OS Architecture", "Created", "Updated"}
|
||||
t.AppendHeader(header)
|
||||
// Right-align numeric columns
|
||||
t.SetColumnConfigs([]table.ColumnConfig{
|
||||
{Number: 1, Align: text.AlignRight},
|
||||
{Number: 3, Align: text.AlignRight},
|
||||
})
|
||||
|
||||
for _, val := range files.Results {
|
||||
row := table.Row{val.ID, val.Name, formatSize(val.Size), val.Version, val.OSType, val.OSArch, val.CreatedAt.Format("2006-01-02 15:04:05"), val.UpdatedAt.Format("2006-01-02 15:04:05")}
|
||||
t.AppendRow(row)
|
||||
if upstream {
|
||||
t.SetColumnConfigs([]table.ColumnConfig{
|
||||
{Number: 2, Align: text.AlignRight},
|
||||
})
|
||||
for _, val := range files.Results {
|
||||
t.AppendRow(table.Row{val.Name, formatSize(val.Size), val.Version, val.OSType, val.OSArch, val.DownloadURL})
|
||||
}
|
||||
} else {
|
||||
t.SetColumnConfigs([]table.ColumnConfig{
|
||||
{Number: 1, Align: text.AlignRight},
|
||||
{Number: 3, Align: text.AlignRight},
|
||||
})
|
||||
for _, val := range files.Results {
|
||||
t.AppendRow(table.Row{val.ID, val.Name, formatSize(val.Size), val.Version, val.OSType, val.OSArch, val.CreatedAt, val.UpdatedAt})
|
||||
}
|
||||
}
|
||||
fmt.Println(t.Render())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1488,6 +1488,14 @@ type PaginatedResponse[T any] struct {
|
|||
// swagger:model FileObjectPaginatedResponse
|
||||
type FileObjectPaginatedResponse = PaginatedResponse[FileObject]
|
||||
|
||||
const (
|
||||
// ToolSourceLocal indicates the tool is stored in the internal object store.
|
||||
ToolSourceLocal = "local"
|
||||
// ToolSourceUpstream indicates the tool is only available from the upstream
|
||||
// cached release and has not been downloaded locally.
|
||||
ToolSourceUpstream = "upstream"
|
||||
)
|
||||
|
||||
// swagger:model GARMAgentTool
|
||||
type GARMAgentTool struct {
|
||||
ID uint `json:"id"`
|
||||
|
|
@ -1507,6 +1515,11 @@ type GARMAgentTool struct {
|
|||
// from a release URL, this will hold the release URL where the tools
|
||||
// originated from.
|
||||
Origin string `json:"origin"`
|
||||
// Source indicates where this tool is currently stored.
|
||||
// "local" means the tool is stored in the internal object store.
|
||||
// "upstream" means the tool is only available from the upstream
|
||||
// cached release and has not been downloaded locally.
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
// swagger:model GARMAgentToolsPaginatedResponse
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"log/slog"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudbase/garm-provider-common/cloudconfig"
|
||||
"github.com/cloudbase/garm-provider-common/defaults"
|
||||
|
|
@ -173,30 +174,73 @@ func (r *Runner) getRunnerInstallTemplateContext(instance params.Instance, entit
|
|||
return installRunnerParams, nil
|
||||
}
|
||||
|
||||
// getAgentTool retrieves the GARM agent tool for the given OS type and architecture.
|
||||
// Priority:
|
||||
// 1. Tools from object store (manual uploads or synced)
|
||||
// 2. Tools from cached upstream release (GitHub release API data)
|
||||
// Returns nil if no tools are available from any source.
|
||||
func (r *Runner) getAgentTool(ctx context.Context, osType commonParams.OSType, osArch commonParams.OSArch) *params.GARMAgentTool {
|
||||
// getUpstreamToolsForGaps returns upstream cached tools for os/arch combinations
|
||||
// not already covered by object store tools. If osType and osArch are non-nil,
|
||||
// only the matching platform is considered (instance-scoped). If nil, all
|
||||
// platforms are considered (admin-scoped).
|
||||
func getUpstreamToolsForGaps(objectStoreTools []params.GARMAgentTool, osType *commonParams.OSType, osArch *commonParams.OSArch) []params.GARMAgentTool {
|
||||
ctrlInfo := cache.ControllerInfo()
|
||||
|
||||
// Check object store first (for both manual uploads and synced tools)
|
||||
agentTools, err := r.GetGARMTools(ctx, 0, 100)
|
||||
if err != nil && !errors.Is(err, runnerErrors.ErrNotFound) {
|
||||
slog.WarnContext(ctx, "failed to query garm agent tools", "error", err)
|
||||
// Check staleness - don't use cached data older than 24 hours
|
||||
if ctrlInfo.CachedGARMAgentReleaseFetchedAt == nil {
|
||||
return nil
|
||||
}
|
||||
if time.Since(*ctrlInfo.CachedGARMAgentReleaseFetchedAt) > 24*time.Hour {
|
||||
return nil
|
||||
}
|
||||
if ctrlInfo.CachedGARMAgentTools == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter results to match the requested OS type and architecture
|
||||
for _, tool := range agentTools.Results {
|
||||
if tool.OSType == osType && tool.OSArch == osArch {
|
||||
return &tool
|
||||
// Build set of os_type/os_arch keys already covered by object store tools
|
||||
covered := make(map[string]bool, len(objectStoreTools))
|
||||
for _, tool := range objectStoreTools {
|
||||
covered[string(tool.OSType)+"/"+string(tool.OSArch)] = true
|
||||
}
|
||||
|
||||
var result []params.GARMAgentTool
|
||||
for key, tool := range ctrlInfo.CachedGARMAgentTools {
|
||||
if covered[key] {
|
||||
continue
|
||||
}
|
||||
// For instance-scoped calls, only include matching os/arch
|
||||
if osType != nil && osArch != nil {
|
||||
if tool.OSType != *osType || tool.OSArch != *osArch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tool.Source = params.ToolSourceUpstream
|
||||
result = append(result, tool)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getInstanceAgentTool retrieves the GARM agent tool for the given instance.
|
||||
// It first checks the local object store, then falls back to upstream cached tools.
|
||||
// Returns nil if no tools are available from any source.
|
||||
func (r *Runner) getInstanceAgentTool(instance params.Instance) *params.GARMAgentTool {
|
||||
tags := []string{
|
||||
garmAgentFileTag,
|
||||
"os_type=" + string(instance.OSType),
|
||||
"os_arch=" + string(instance.OSArch),
|
||||
}
|
||||
files, err := r.store.SearchFileObjectByTags(r.ctx, tags, 0, 1)
|
||||
if err == nil && files.TotalCount > 0 {
|
||||
downloadURL, err := url.JoinPath(instance.MetadataURL, "tools/garm-agent", fmt.Sprintf("%d", files.Results[0].ID), "download")
|
||||
if err == nil {
|
||||
tool, err := fileObjectToGARMTool(files.Results[0], downloadURL)
|
||||
if err == nil {
|
||||
return &tool
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No tools in object store - fall back to cached upstream release data
|
||||
tool := ctrlInfo.GetCachedAgentTool(string(osType), string(osArch))
|
||||
return tool
|
||||
// Fall back to upstream cached tools
|
||||
upstreamTools := getUpstreamToolsForGaps(nil, &instance.OSType, &instance.OSArch)
|
||||
if len(upstreamTools) > 0 {
|
||||
return &upstreamTools[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) GetInstanceMetadata(ctx context.Context) (params.InstanceMetadata, error) {
|
||||
|
|
@ -267,7 +311,7 @@ func (r *Runner) GetInstanceMetadata(ctx context.Context) (params.InstanceMetada
|
|||
}
|
||||
|
||||
if dbEntity.AgentMode {
|
||||
agentTool := r.getAgentTool(ctx, instance.OSType, instance.OSArch)
|
||||
agentTool := r.getInstanceAgentTool(instance)
|
||||
|
||||
// If we have tools, set agent metadata
|
||||
if agentTool != nil {
|
||||
|
|
@ -588,22 +632,81 @@ func fileObjectToGARMTool(obj params.FileObject, downloadURL string) (params.GAR
|
|||
DownloadURL: downloadURL,
|
||||
Version: version,
|
||||
Origin: origin,
|
||||
Source: params.ToolSourceLocal,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *Runner) GetGARMTools(ctx context.Context, page, pageSize uint64) (params.GARMAgentToolsPaginatedResponse, error) {
|
||||
// GetGARMTools lists GARM agent tools available to admin users.
|
||||
// When upstream is false, it lists tools from the local object store.
|
||||
// When upstream is true, it lists tools from the cached upstream release JSON.
|
||||
func (r *Runner) GetGARMTools(ctx context.Context, page, pageSize uint64, upstream bool) (params.GARMAgentToolsPaginatedResponse, error) {
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return params.GARMAgentToolsPaginatedResponse{}, runnerErrors.ErrUnauthorized
|
||||
}
|
||||
|
||||
if upstream {
|
||||
return r.getUpstreamGARMTools()
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
garmAgentFileTag,
|
||||
}
|
||||
metadataURL := cache.ControllerInfo().MetadataURL
|
||||
|
||||
files, err := r.store.SearchFileObjectByTags(r.ctx, tags, page, pageSize)
|
||||
if err != nil {
|
||||
return params.GARMAgentToolsPaginatedResponse{}, fmt.Errorf("failed to list files: %w", err)
|
||||
}
|
||||
|
||||
var tools []params.GARMAgentTool
|
||||
for _, val := range files.Results {
|
||||
objectIDAsString := fmt.Sprintf("%d", val.ID)
|
||||
downloadURL, err := url.JoinPath(metadataURL, "tools/garm-agent", objectIDAsString, "download")
|
||||
if err != nil {
|
||||
return params.GARMAgentToolsPaginatedResponse{}, fmt.Errorf("failed to construct agent tools download URL: %w", err)
|
||||
}
|
||||
res, err := fileObjectToGARMTool(val, downloadURL)
|
||||
if err != nil {
|
||||
return params.GARMAgentToolsPaginatedResponse{}, fmt.Errorf("failed parse tools object: %w", err)
|
||||
}
|
||||
tools = append(tools, res)
|
||||
}
|
||||
|
||||
return params.GARMAgentToolsPaginatedResponse{
|
||||
TotalCount: files.TotalCount,
|
||||
Pages: files.Pages,
|
||||
CurrentPage: files.CurrentPage,
|
||||
NextPage: files.NextPage,
|
||||
PreviousPage: files.PreviousPage,
|
||||
Results: tools,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getUpstreamGARMTools returns tools from the cached upstream release JSON.
|
||||
func (r *Runner) getUpstreamGARMTools() (params.GARMAgentToolsPaginatedResponse, error) {
|
||||
upstreamTools := getUpstreamToolsForGaps(nil, nil, nil)
|
||||
return params.GARMAgentToolsPaginatedResponse{
|
||||
TotalCount: uint64(len(upstreamTools)),
|
||||
Pages: 1,
|
||||
CurrentPage: 0,
|
||||
Results: upstreamTools,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAgentGARMTools lists GARM agent tools available for an instance, filtered by
|
||||
// the instance's OS type and architecture. On the last page, upstream cached tools
|
||||
// are merged to fill gaps for os/arch combos not present in the local object store.
|
||||
func (r *Runner) GetAgentGARMTools(ctx context.Context, page, pageSize uint64) (params.GARMAgentToolsPaginatedResponse, error) {
|
||||
instance, err := validateInstanceState(ctx)
|
||||
if err != nil {
|
||||
if !auth.IsAdmin(ctx) {
|
||||
return params.GARMAgentToolsPaginatedResponse{}, runnerErrors.ErrUnauthorized
|
||||
}
|
||||
} else {
|
||||
tags = append(tags, "os_type="+string(instance.OSType))
|
||||
tags = append(tags, "os_arch="+string(instance.OSArch))
|
||||
return params.GARMAgentToolsPaginatedResponse{}, runnerErrors.ErrUnauthorized
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
garmAgentFileTag,
|
||||
"os_type=" + string(instance.OSType),
|
||||
"os_arch=" + string(instance.OSArch),
|
||||
}
|
||||
|
||||
files, err := r.store.SearchFileObjectByTags(r.ctx, tags, page, pageSize)
|
||||
|
|
@ -624,6 +727,14 @@ func (r *Runner) GetGARMTools(ctx context.Context, page, pageSize uint64) (param
|
|||
}
|
||||
tools = append(tools, res)
|
||||
}
|
||||
|
||||
// On the last page, merge upstream cached tools that fill gaps
|
||||
// (os/arch combos not present in the object store).
|
||||
if files.NextPage == nil {
|
||||
upstreamTools := getUpstreamToolsForGaps(tools, &instance.OSType, &instance.OSArch)
|
||||
tools = append(tools, upstreamTools...)
|
||||
}
|
||||
|
||||
return params.GARMAgentToolsPaginatedResponse{
|
||||
TotalCount: files.TotalCount,
|
||||
Pages: files.Pages,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
|
@ -739,6 +740,11 @@ func (s *MetadataTestSuite) TestGetInstanceMetadataBasicFields() {
|
|||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetInstanceMetadataWithAgentModeNoTools() {
|
||||
// Clear any upstream tools from controller cache (may be set by other tests)
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
})
|
||||
|
||||
// Set up runner tools cache
|
||||
tools := []commonParams.RunnerApplicationDownload{
|
||||
{
|
||||
|
|
@ -789,6 +795,11 @@ func (s *MetadataTestSuite) TestGetInstanceMetadataAgentModeDisabledByDefault()
|
|||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetInstanceMetadataWithAgentModeToolsCountZero() {
|
||||
// Clear any upstream tools from controller cache (may be set by other tests)
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
})
|
||||
|
||||
// Set up runner tools cache
|
||||
tools := []commonParams.RunnerApplicationDownload{
|
||||
{
|
||||
|
|
@ -820,6 +831,11 @@ func (s *MetadataTestSuite) TestGetInstanceMetadataWithAgentModeToolsCountZero()
|
|||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetInstanceMetadataWithAgentModeGetToolsReturnsNotFoundError() {
|
||||
// Clear any upstream tools from controller cache (may be set by other tests)
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
})
|
||||
|
||||
// Set up runner tools cache
|
||||
tools := []commonParams.RunnerApplicationDownload{
|
||||
{
|
||||
|
|
@ -980,27 +996,161 @@ func (s *MetadataTestSuite) TestFileObjectToGARMTool() {
|
|||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetGARMTools() {
|
||||
// GetGARMTools requires file objects in database
|
||||
// This is tested in file_store_test.go and garm_tools_test.go
|
||||
// Here we just test the authorization paths
|
||||
_, err := s.Runner.GetGARMTools(s.instanceCtx, 0, 25)
|
||||
// GetGARMTools is admin-only, should succeed for admin context
|
||||
_, err := s.Runner.GetGARMTools(s.adminCtx, 0, 25, false)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
// Should not error on authorization (might have no results)
|
||||
func (s *MetadataTestSuite) TestGetAgentGARMTools() {
|
||||
// GetAgentGARMTools is instance-only, should succeed for instance context
|
||||
_, err := s.Runner.GetAgentGARMTools(s.instanceCtx, 0, 25)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetGARMToolsUnauthorized() {
|
||||
_, err := s.Runner.GetGARMTools(s.unauthorizedCtx, 0, 25)
|
||||
_, err := s.Runner.GetGARMTools(s.unauthorizedCtx, 0, 25, false)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetGARMToolsInvalidState() {
|
||||
_, err := s.Runner.GetGARMTools(s.invalidInstanceCtx, 0, 25)
|
||||
func (s *MetadataTestSuite) TestGetGARMToolsInstanceUnauthorized() {
|
||||
// GetGARMTools is admin-only, instance context should fail
|
||||
_, err := s.Runner.GetGARMTools(s.instanceCtx, 0, 25, false)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetAgentGARMToolsUnauthorized() {
|
||||
_, err := s.Runner.GetAgentGARMTools(s.unauthorizedCtx, 0, 25)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetAgentGARMToolsInvalidState() {
|
||||
_, err := s.Runner.GetAgentGARMTools(s.invalidInstanceCtx, 0, 25)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetAgentGARMToolsMergesUpstreamTools() {
|
||||
// Set up controller cache with upstream tools but no object store tools
|
||||
now := time.Now()
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
CachedGARMAgentReleaseFetchedAt: &now,
|
||||
CachedGARMAgentTools: map[string]params.GARMAgentTool{
|
||||
"linux/amd64": {
|
||||
Name: "garm-agent-linux-amd64",
|
||||
Version: "v0.1.0",
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Amd64,
|
||||
DownloadURL: "https://github.com/example/releases/download/v0.1.0/garm-agent-linux-amd64",
|
||||
},
|
||||
"linux/arm64": {
|
||||
Name: "garm-agent-linux-arm64",
|
||||
Version: "v0.1.0",
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Arm64,
|
||||
DownloadURL: "https://github.com/example/releases/download/v0.1.0/garm-agent-linux-arm64",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Instance context filters to linux/amd64 - should get the upstream tool
|
||||
result, err := s.Runner.GetAgentGARMTools(s.instanceCtx, 0, 25)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(result.Results, 1)
|
||||
s.Require().Equal("garm-agent-linux-amd64", result.Results[0].Name)
|
||||
s.Require().Equal(params.ToolSourceUpstream, result.Results[0].Source)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetAgentGARMToolsObjectStoreOverridesUpstream() {
|
||||
// Set up controller cache with upstream tools
|
||||
now := time.Now()
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
CachedGARMAgentReleaseFetchedAt: &now,
|
||||
CachedGARMAgentTools: map[string]params.GARMAgentTool{
|
||||
"linux/amd64": {
|
||||
Name: "garm-agent-linux-amd64",
|
||||
Version: "v0.1.0",
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Amd64,
|
||||
DownloadURL: "https://github.com/example/releases/download/v0.1.0/garm-agent-linux-amd64",
|
||||
},
|
||||
"linux/arm64": {
|
||||
Name: "garm-agent-linux-arm64",
|
||||
Version: "v0.1.0",
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Arm64,
|
||||
DownloadURL: "https://github.com/example/releases/download/v0.1.0/garm-agent-linux-arm64",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Create object store tool for linux/amd64
|
||||
agentBinary := []byte("fake garm agent binary")
|
||||
_, err := s.Runner.CreateGARMTool(s.adminCtx, params.CreateGARMToolParams{
|
||||
Name: "garm-agent-linux-amd64",
|
||||
Size: int64(len(agentBinary)),
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Amd64,
|
||||
Version: "v1.0.0",
|
||||
}, bytes.NewReader(agentBinary))
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Instance is linux/amd64 - should get local tool, not upstream
|
||||
result, err := s.Runner.GetAgentGARMTools(s.instanceCtx, 0, 25)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(result.Results, 1)
|
||||
s.Require().Equal(params.ToolSourceLocal, result.Results[0].Source)
|
||||
s.Require().Equal("v1.0.0", result.Results[0].Version)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetAgentGARMToolsStaleUpstreamNotIncluded() {
|
||||
// Set up controller cache with stale upstream tools (>24h old)
|
||||
staleTime := time.Now().Add(-25 * time.Hour)
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
CachedGARMAgentReleaseFetchedAt: &staleTime,
|
||||
CachedGARMAgentTools: map[string]params.GARMAgentTool{
|
||||
"linux/amd64": {
|
||||
Name: "garm-agent-linux-amd64",
|
||||
Version: "v0.1.0",
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Amd64,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Instance context - stale upstream tools should not be included
|
||||
result, err := s.Runner.GetAgentGARMTools(s.instanceCtx, 0, 25)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Empty(result.Results)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestGetGARMToolsAdminNoUpstream() {
|
||||
// Set up controller cache with upstream tools but no object store tools
|
||||
now := time.Now()
|
||||
cache.SetControllerCache(params.ControllerInfo{
|
||||
MetadataURL: "http://metadata.example.com",
|
||||
CachedGARMAgentReleaseFetchedAt: &now,
|
||||
CachedGARMAgentTools: map[string]params.GARMAgentTool{
|
||||
"linux/amd64": {
|
||||
Name: "garm-agent-linux-amd64",
|
||||
Version: "v0.1.0",
|
||||
OSType: commonParams.Linux,
|
||||
OSArch: commonParams.Amd64,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Admin context should NOT include upstream tools
|
||||
result, err := s.Runner.GetGARMTools(s.adminCtx, 0, 25, false)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Empty(result.Results)
|
||||
}
|
||||
|
||||
func (s *MetadataTestSuite) TestShowGARMToolsUnauthorized() {
|
||||
_, err := s.Runner.ShowGARMTools(s.unauthorizedCtx, 1)
|
||||
s.Require().NotNil(err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue