Skip to content

Commit 214cb05

Browse files
Merge pull request #256 from gabriel-samfira/move-urls-to-db
Move URLs from default section of config to DB
2 parents 7ee235a + aea328b commit 214cb05

30 files changed

+1497
-422
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ default: build
3333
build-static: ## Build garm statically
3434
@echo Building garm
3535
docker build --tag $(IMAGE_TAG) -f Dockerfile.build-static .
36+
mkdir -p build
3637
docker run --rm -e USER_ID=$(USER_ID) -e GARM_REF=$(GARM_REF) -e USER_GROUP=$(USER_GROUP) -v $(PWD)/build:/build/output:z $(IMAGE_TAG) /build-static.sh
3738
@echo Binaries are available in $(PWD)/build
3839

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The goal of ```GARM``` is to be simple to set up, simple to configure and simple
1010

1111
GARM supports creating pools on either GitHub itself or on your own deployment of [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@3.5/admin/overview/about-github-enterprise-server). For instructions on how to use ```GARM``` with GHE, see the [credentials](/doc/github_credentials.md) section of the documentation.
1212

13-
Through the use of providers, `GARM` can create runners in a variety of environments using the same `GARM` instance. Want to create pools of runners in your OpenStack cloud, your Azure cloud and your Kubernetes cluster? No problem! Just install the appropriate providers, configure them in `GARM` and you're good to go. Create zero-runner pools for instances with high costs (large VMs, GPU enabled instances, etc) and have them spin up on demand, or create large pools of k8s backed runners that can be used for your CI/CD pipelines at a moment's notice. You can mix them up and create pools in any combination of providers or resource allocations you want.
13+
Through the use of providers, `GARM` can create runners in a variety of environments using the same `GARM` instance. Whether you want to create pools of runners in your OpenStack cloud, your Azure cloud and your Kubernetes cluster, that is easily achieved by just installing the appropriate providers, configuring them in `GARM` and creating pools that use them. You can create zero-runner pools for instances with high costs (large VMs, GPU enabled instances, etc) and have them spin up on demand, or you can create large pools of k8s backed runners that can be used for your CI/CD pipelines at a moment's notice. You can mix them up and create pools in any combination of providers or resource allocations you want.
1414

1515
:warning: **Important note**: The README and documentation in the `main` branch are relevant to the not yet released code that is present in `main`. Following the documentation from the `main` branch for a stable release of GARM, may lead to errors. To view the documentation for the latest stable release, please switch to the appropriate tag. For information about setting up `v0.1.4`, please refer to the [v0.1.4 tag](https://github.com/cloudbase/garm/tree/v0.1.4)
1616

apiserver/controllers/controllers.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,42 @@ func (a *APIController) ControllerInfoHandler(w http.ResponseWriter, r *http.Req
391391
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
392392
}
393393
}
394+
395+
// swagger:route PUT /controller controller UpdateController
396+
//
397+
// Update controller.
398+
//
399+
// Parameters:
400+
// + name: Body
401+
// description: Parameters used when updating the controller.
402+
// type: UpdateControllerParams
403+
// in: body
404+
// required: true
405+
//
406+
// Responses:
407+
// 200: ControllerInfo
408+
// 400: APIErrorResponse
409+
func (a *APIController) UpdateControllerHandler(w http.ResponseWriter, r *http.Request) {
410+
ctx := r.Context()
411+
var updateParams runnerParams.UpdateControllerParams
412+
if err := json.NewDecoder(r.Body).Decode(&updateParams); err != nil {
413+
handleError(ctx, w, gErrors.ErrBadRequest)
414+
return
415+
}
416+
417+
if err := updateParams.Validate(); err != nil {
418+
handleError(ctx, w, err)
419+
return
420+
}
421+
422+
info, err := a.r.UpdateController(ctx, updateParams)
423+
if err != nil {
424+
handleError(ctx, w, err)
425+
return
426+
}
427+
428+
w.Header().Set("Content-Type", "application/json")
429+
if err := json.NewEncoder(w).Encode(info); err != nil {
430+
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
431+
}
432+
}

apiserver/params/params.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ var (
3636
Error: "init_required",
3737
Details: "Missing superuser",
3838
}
39+
// URLsRequired is returned if the controller does not have the required URLs
40+
URLsRequired = APIErrorResponse{
41+
Error: "urls_required",
42+
Details: "Missing required URLs. Make sure you update the metadata, callback and webhook URLs",
43+
}
3944
)

apiserver/routers/routers.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func requestLogger(h http.Handler) http.Handler {
100100
})
101101
}
102102

103-
func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware, instanceMiddleware auth.Middleware, manageWebhooks bool) *mux.Router {
103+
func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware, urlsRequiredMiddleware, instanceMiddleware auth.Middleware, manageWebhooks bool) *mux.Router {
104104
router := mux.NewRouter()
105105
router.Use(requestLogger)
106106

@@ -152,11 +152,38 @@ func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware
152152
authRouter.Handle("/{login:login\\/?}", http.HandlerFunc(han.LoginHandler)).Methods("POST", "OPTIONS")
153153
authRouter.Use(initMiddleware.Middleware)
154154

155+
//////////////////////////
156+
// Controller endpoints //
157+
//////////////////////////
158+
controllerRouter := apiSubRouter.PathPrefix("/controller").Subrouter()
159+
// The controller endpoints allow us to get information about the controller and update the URL endpoints.
160+
// This endpoint must not be guarded by the urlsRequiredMiddleware as that would prevent the user from
161+
// updating the URLs.
162+
controllerRouter.Use(initMiddleware.Middleware)
163+
controllerRouter.Use(authMiddleware.Middleware)
164+
controllerRouter.Use(auth.AdminRequiredMiddleware)
165+
// Get controller info
166+
controllerRouter.Handle("/", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
167+
controllerRouter.Handle("", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
168+
// Update controller
169+
controllerRouter.Handle("/", http.HandlerFunc(han.UpdateControllerHandler)).Methods("PUT", "OPTIONS")
170+
controllerRouter.Handle("", http.HandlerFunc(han.UpdateControllerHandler)).Methods("PUT", "OPTIONS")
171+
172+
////////////////////////////////////
173+
// API router for everything else //
174+
////////////////////////////////////
155175
apiRouter := apiSubRouter.PathPrefix("").Subrouter()
156176
apiRouter.Use(initMiddleware.Middleware)
177+
// all endpoints except the controller endpoint should return an error
178+
// if the required metadata, callback and webhook URLs are not set.
179+
apiRouter.Use(urlsRequiredMiddleware.Middleware)
157180
apiRouter.Use(authMiddleware.Middleware)
158181
apiRouter.Use(auth.AdminRequiredMiddleware)
159182

183+
// Legacy controller path
184+
apiRouter.Handle("/controller-info/", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
185+
apiRouter.Handle("/controller-info", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
186+
160187
// Metrics Token
161188
apiRouter.Handle("/metrics-token/", http.HandlerFunc(han.MetricsTokenHandler)).Methods("GET", "OPTIONS")
162189
apiRouter.Handle("/metrics-token", http.HandlerFunc(han.MetricsTokenHandler)).Methods("GET", "OPTIONS")
@@ -343,10 +370,6 @@ func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware
343370
apiRouter.Handle("/providers/", http.HandlerFunc(han.ListProviders)).Methods("GET", "OPTIONS")
344371
apiRouter.Handle("/providers", http.HandlerFunc(han.ListProviders)).Methods("GET", "OPTIONS")
345372

346-
// Controller info
347-
apiRouter.Handle("/controller-info/", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
348-
apiRouter.Handle("/controller-info", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
349-
350373
//////////////////////
351374
// Github Endpoints //
352375
//////////////////////

apiserver/swagger-models.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,10 @@ definitions:
278278
import:
279279
package: github.com/cloudbase/garm/params
280280
alias: garm_params
281+
UpdateControllerParams:
282+
type: object
283+
x-go-type:
284+
type: UpdateControllerParams
285+
import:
286+
package: github.com/cloudbase/garm/params
287+
alias: garm_params

apiserver/swagger.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,13 @@ definitions:
244244
alias: garm_params
245245
package: github.com/cloudbase/garm/params
246246
type: Repository
247+
UpdateControllerParams:
248+
type: object
249+
x-go-type:
250+
import:
251+
alias: garm_params
252+
package: github.com/cloudbase/garm/params
253+
type: UpdateControllerParams
247254
UpdateEntityParams:
248255
type: object
249256
x-go-type:
@@ -311,6 +318,30 @@ paths:
311318
summary: Logs in a user and returns a JWT token.
312319
tags:
313320
- login
321+
/controller:
322+
put:
323+
operationId: UpdateController
324+
parameters:
325+
- description: Parameters used when updating the controller.
326+
in: body
327+
name: Body
328+
required: true
329+
schema:
330+
$ref: '#/definitions/UpdateControllerParams'
331+
description: Parameters used when updating the controller.
332+
type: object
333+
responses:
334+
"200":
335+
description: ControllerInfo
336+
schema:
337+
$ref: '#/definitions/ControllerInfo'
338+
"400":
339+
description: APIErrorResponse
340+
schema:
341+
$ref: '#/definitions/APIErrorResponse'
342+
summary: Update controller.
343+
tags:
344+
- controller
314345
/controller-info:
315346
get:
316347
operationId: ControllerInfo

auth/init_required.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,30 @@ func (i *initRequired) Middleware(next http.Handler) http.Handler {
5151
next.ServeHTTP(w, r.WithContext(ctx))
5252
})
5353
}
54+
55+
func NewUrlsRequiredMiddleware(store common.Store) (Middleware, error) {
56+
return &urlsRequired{
57+
store: store,
58+
}, nil
59+
}
60+
61+
type urlsRequired struct {
62+
store common.Store
63+
}
64+
65+
func (u *urlsRequired) Middleware(next http.Handler) http.Handler {
66+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
67+
ctx := r.Context()
68+
ctrlInfo, err := u.store.ControllerInfo()
69+
if err != nil || ctrlInfo.WebhookURL == "" || ctrlInfo.MetadataURL == "" || ctrlInfo.CallbackURL == "" {
70+
w.Header().Add("Content-Type", "application/json")
71+
w.WriteHeader(http.StatusConflict)
72+
if err := json.NewEncoder(w).Encode(params.URLsRequired); err != nil {
73+
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
74+
}
75+
return
76+
}
77+
78+
next.ServeHTTP(w, r.WithContext(ctx))
79+
})
80+
}

client/controller/controller_client.go

Lines changed: 106 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)