Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move URLs from default section of config to DB #256

Merged
merged 4 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ default: build
build-static: ## Build garm statically
@echo Building garm
docker build --tag $(IMAGE_TAG) -f Dockerfile.build-static .
mkdir -p build
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
@echo Binaries are available in $(PWD)/build

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The goal of ```GARM``` is to be simple to set up, simple to configure and simple

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.

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.
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.

: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)

Expand Down
39 changes: 39 additions & 0 deletions apiserver/controllers/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,42 @@ func (a *APIController) ControllerInfoHandler(w http.ResponseWriter, r *http.Req
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
}
}

// swagger:route PUT /controller controller UpdateController
//
// Update controller.
//
// Parameters:
// + name: Body
// description: Parameters used when updating the controller.
// type: UpdateControllerParams
// in: body
// required: true
//
// Responses:
// 200: ControllerInfo
// 400: APIErrorResponse
func (a *APIController) UpdateControllerHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var updateParams runnerParams.UpdateControllerParams
if err := json.NewDecoder(r.Body).Decode(&updateParams); err != nil {
handleError(ctx, w, gErrors.ErrBadRequest)
return
}

if err := updateParams.Validate(); err != nil {
handleError(ctx, w, err)
return
}

info, err := a.r.UpdateController(ctx, updateParams)
if err != nil {
handleError(ctx, w, err)
return
}

w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(info); err != nil {
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
}
}
5 changes: 5 additions & 0 deletions apiserver/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ var (
Error: "init_required",
Details: "Missing superuser",
}
// URLsRequired is returned if the controller does not have the required URLs
URLsRequired = APIErrorResponse{
Error: "urls_required",
Details: "Missing required URLs. Make sure you update the metadata, callback and webhook URLs",
}
)
33 changes: 28 additions & 5 deletions apiserver/routers/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func requestLogger(h http.Handler) http.Handler {
})
}

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

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

//////////////////////////
// Controller endpoints //
//////////////////////////
controllerRouter := apiSubRouter.PathPrefix("/controller").Subrouter()
// The controller endpoints allow us to get information about the controller and update the URL endpoints.
// This endpoint must not be guarded by the urlsRequiredMiddleware as that would prevent the user from
// updating the URLs.
controllerRouter.Use(initMiddleware.Middleware)
controllerRouter.Use(authMiddleware.Middleware)
controllerRouter.Use(auth.AdminRequiredMiddleware)
// Get controller info
controllerRouter.Handle("/", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
controllerRouter.Handle("", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
// Update controller
controllerRouter.Handle("/", http.HandlerFunc(han.UpdateControllerHandler)).Methods("PUT", "OPTIONS")
controllerRouter.Handle("", http.HandlerFunc(han.UpdateControllerHandler)).Methods("PUT", "OPTIONS")

////////////////////////////////////
// API router for everything else //
////////////////////////////////////
apiRouter := apiSubRouter.PathPrefix("").Subrouter()
apiRouter.Use(initMiddleware.Middleware)
// all endpoints except the controller endpoint should return an error
// if the required metadata, callback and webhook URLs are not set.
apiRouter.Use(urlsRequiredMiddleware.Middleware)
apiRouter.Use(authMiddleware.Middleware)
apiRouter.Use(auth.AdminRequiredMiddleware)

// Legacy controller path
apiRouter.Handle("/controller-info/", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
apiRouter.Handle("/controller-info", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")

// Metrics Token
apiRouter.Handle("/metrics-token/", http.HandlerFunc(han.MetricsTokenHandler)).Methods("GET", "OPTIONS")
apiRouter.Handle("/metrics-token", http.HandlerFunc(han.MetricsTokenHandler)).Methods("GET", "OPTIONS")
Expand Down Expand Up @@ -343,10 +370,6 @@ func NewAPIRouter(han *controllers.APIController, authMiddleware, initMiddleware
apiRouter.Handle("/providers/", http.HandlerFunc(han.ListProviders)).Methods("GET", "OPTIONS")
apiRouter.Handle("/providers", http.HandlerFunc(han.ListProviders)).Methods("GET", "OPTIONS")

// Controller info
apiRouter.Handle("/controller-info/", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")
apiRouter.Handle("/controller-info", http.HandlerFunc(han.ControllerInfoHandler)).Methods("GET", "OPTIONS")

//////////////////////
// Github Endpoints //
//////////////////////
Expand Down
7 changes: 7 additions & 0 deletions apiserver/swagger-models.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,10 @@ definitions:
import:
package: github.com/cloudbase/garm/params
alias: garm_params
UpdateControllerParams:
type: object
x-go-type:
type: UpdateControllerParams
import:
package: github.com/cloudbase/garm/params
alias: garm_params
31 changes: 31 additions & 0 deletions apiserver/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ definitions:
alias: garm_params
package: github.com/cloudbase/garm/params
type: Repository
UpdateControllerParams:
type: object
x-go-type:
import:
alias: garm_params
package: github.com/cloudbase/garm/params
type: UpdateControllerParams
UpdateEntityParams:
type: object
x-go-type:
Expand Down Expand Up @@ -311,6 +318,30 @@ paths:
summary: Logs in a user and returns a JWT token.
tags:
- login
/controller:
put:
operationId: UpdateController
parameters:
- description: Parameters used when updating the controller.
in: body
name: Body
required: true
schema:
$ref: '#/definitions/UpdateControllerParams'
description: Parameters used when updating the controller.
type: object
responses:
"200":
description: ControllerInfo
schema:
$ref: '#/definitions/ControllerInfo'
"400":
description: APIErrorResponse
schema:
$ref: '#/definitions/APIErrorResponse'
summary: Update controller.
tags:
- controller
/controller-info:
get:
operationId: ControllerInfo
Expand Down
27 changes: 27 additions & 0 deletions auth/init_required.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,30 @@ func (i *initRequired) Middleware(next http.Handler) http.Handler {
next.ServeHTTP(w, r.WithContext(ctx))
})
}

func NewUrlsRequiredMiddleware(store common.Store) (Middleware, error) {
return &urlsRequired{
store: store,
}, nil
}

type urlsRequired struct {
store common.Store
}

func (u *urlsRequired) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctrlInfo, err := u.store.ControllerInfo()
if err != nil || ctrlInfo.WebhookURL == "" || ctrlInfo.MetadataURL == "" || ctrlInfo.CallbackURL == "" {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusConflict)
if err := json.NewEncoder(w).Encode(params.URLsRequired); err != nil {
slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to encode response")
}
return
}

next.ServeHTTP(w, r.WithContext(ctx))
})
}
106 changes: 106 additions & 0 deletions client/controller/controller_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading