From d544c041796574b5b3689e659e3b5bf1ef04f24b Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 1 Mar 2023 17:14:34 -0600 Subject: [PATCH 01/14] initial work --- api/register.go | 43 +++++++++++++++++++++++++++++++++ cmd/vela-worker/flags.go | 6 +++++ cmd/vela-worker/operate.go | 24 +++++++++++++----- cmd/vela-worker/register.go | 20 +++++++++++++-- cmd/vela-worker/run.go | 6 +++++ cmd/vela-worker/server.go | 1 + cmd/vela-worker/worker.go | 1 + docker-compose.yml | 10 +++++--- go.mod | 2 ++ router/middleware/auth_token.go | 18 ++++++++++++++ router/router.go | 3 +++ 11 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 api/register.go create mode 100644 router/middleware/auth_token.go diff --git a/api/register.go b/api/register.go new file mode 100644 index 00000000..051fbf7e --- /dev/null +++ b/api/register.go @@ -0,0 +1,43 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/go-vela/worker/router/middleware/token" +) + +func Register(c *gin.Context) { + // extract the deadloop channel that was packed into gin context + v, ok := c.Get("auth-token") + if !ok { + c.JSON(http.StatusInternalServerError, "no deadloop channel in the context") + return + } + + // make sure we configured it properly + authChannel, ok := v.(chan string) + if !ok { + c.JSON(http.StatusInternalServerError, "deadloop channel in the context is the wrong type") + return + } + + if len(authChannel) > 0 { + c.JSON(http.StatusOK, "worker already registered") + return + } + + // this is a fake token, we would fetch this from the JSON body + token, err := token.Retrieve(c.Request) + if err != nil { + c.JSON(http.StatusInternalServerError, "no deadloop channel in the context") + return + } + + // send the token + authChannel <- token + + // somehow we need to make sure the registration worked + // maybe a second channel for registration results? + c.JSON(http.StatusOK, "successfully registered the worker") +} diff --git a/cmd/vela-worker/flags.go b/cmd/vela-worker/flags.go index bce7284c..b749a3c9 100644 --- a/cmd/vela-worker/flags.go +++ b/cmd/vela-worker/flags.go @@ -33,6 +33,12 @@ func flags() []cli.Flag { Value: 15 * time.Minute, }, + &cli.StringFlag{ + EnvVars: []string{"WORKER_REGISTRATION_TOKEN", "VELA_REGISTRATION_TOKEN", "REGISTRATION_TOKEN"}, + Name: "worker.token", + Usage: "seed worker registration token on start up to check in with server", + }, + // Build Flags &cli.IntFlag{ diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index bbbe2e7a..8128d8c3 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -22,12 +22,6 @@ import ( func (w *Worker) operate(ctx context.Context) error { var err error - // setup the vela client with the server - w.VelaClient, err = setupClient(w.Config.Server, w.Config.Server.Secret) - if err != nil { - return err - } - // create the errgroup for managing operator subprocesses // // https://pkg.go.dev/golang.org/x/sync/errgroup?tab=doc#Group @@ -43,6 +37,21 @@ func (w *Worker) operate(ctx context.Context) error { registryWorker.SetLastCheckedIn(time.Now().UTC().Unix()) registryWorker.SetBuildLimit(int64(w.Config.Build.Limit)) + // run the deadloop + logrus.Info("ranging over the deadloop channel, halting operation") + + // wait for registration token + token := <-w.AuthToken + + // continue operation like normal + logrus.Info("deadloop channel received token, continuing operation") + + // setup the vela client with the server + w.VelaClient, err = setupClient(w.Config.Server, token) + if err != nil { + return err + } + // spawn goroutine for phoning home executors.Go(func() error { for { @@ -72,6 +81,9 @@ func (w *Worker) operate(ctx context.Context) error { } }) + // ensure worker is registered before beginning to pull from queue. + _ = <-w.AuthToken + // setup the queue // // https://pkg.go.dev/github.com/go-vela/server/queue?tab=doc#New diff --git a/cmd/vela-worker/register.go b/cmd/vela-worker/register.go index 5de52721..416ea284 100644 --- a/cmd/vela-worker/register.go +++ b/cmd/vela-worker/register.go @@ -34,11 +34,19 @@ func (w *Worker) checkIn(config *library.Worker) error { // if we were able to GET the worker, update it logrus.Infof("checking worker %s into the server", config.GetHostname()) - _, _, err = w.VelaClient.Worker.Update(config.GetHostname(), config) + wrkCheckIn, _, err := w.VelaClient.Worker.Update(config.GetHostname(), config) if err != nil { return fmt.Errorf("unable to update worker %s on the server: %w", config.GetHostname(), err) } + w.VelaClient.Authentication.SetTokenAuth(wrkCheckIn.Token.GetToken()) + + if len(w.AuthToken) > 0 { + <-w.AuthToken + } + + w.AuthToken <- wrkCheckIn.Token.GetToken() + return nil } @@ -46,12 +54,20 @@ func (w *Worker) checkIn(config *library.Worker) error { func (w *Worker) register(config *library.Worker) error { logrus.Infof("worker %s not found, registering it with the server", config.GetHostname()) - _, _, err := w.VelaClient.Worker.Add(config) + tkn, _, err := w.VelaClient.Worker.Add(config) if err != nil { // log the error instead of returning so the operation doesn't block worker deployment return fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) } + w.VelaClient.Authentication.SetTokenAuth(tkn.GetToken()) + + if len(w.AuthToken) > 0 { + <-w.AuthToken + } + + w.AuthToken <- tkn.GetToken() + // successfully added the worker so return nil return nil } diff --git a/cmd/vela-worker/run.go b/cmd/vela-worker/run.go index 482911e0..66efd36f 100644 --- a/cmd/vela-worker/run.go +++ b/cmd/vela-worker/run.go @@ -136,6 +136,8 @@ func run(c *cli.Context) error { TLSMinVersion: c.String("server.tls-min-version"), }, Executors: make(map[int]executor.Engine), + + AuthToken: make(chan string, 1), } // set the worker address if no flag was provided @@ -143,6 +145,10 @@ func run(c *cli.Context) error { w.Config.API.Address, _ = url.Parse(fmt.Sprintf("http://%s", hostname)) } + if len(c.String("worker.token")) > 0 { + w.AuthToken <- c.String("worker-token") + } + // validate the worker err = w.Validate() if err != nil { diff --git a/cmd/vela-worker/server.go b/cmd/vela-worker/server.go index cecd9210..70524261 100644 --- a/cmd/vela-worker/server.go +++ b/cmd/vela-worker/server.go @@ -33,6 +33,7 @@ func (w *Worker) server() (http.Handler, *tls.Config) { middleware.Executors(w.Executors), middleware.Secret(w.Config.Server.Secret), middleware.Logger(logrus.StandardLogger(), time.RFC3339, true), + middleware.AuthToken(w.AuthToken), ) // log a message indicating the start of serving traffic diff --git a/cmd/vela-worker/worker.go b/cmd/vela-worker/worker.go index 1d21e75c..6dcbf5b5 100644 --- a/cmd/vela-worker/worker.go +++ b/cmd/vela-worker/worker.go @@ -67,5 +67,6 @@ type ( Queue queue.Service Runtime runtime.Engine VelaClient *vela.Client + AuthToken chan string } ) diff --git a/docker-compose.yml b/docker-compose.yml index 67cad74c..efb4d904 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,7 @@ services: VELA_SERVER_ADDR: 'http://server:8080' VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' WORKER_ADDR: 'http://worker:8080' - WORKER_CHECK_IN: 5m + WORKER_CHECK_IN: 2m restart: always ports: - "8081:8080" @@ -51,7 +51,7 @@ services: # https://go-vela.github.io/docs/administration/server/ server: container_name: server - image: target/vela-server:latest + image: server:local networks: - vela environment: @@ -70,8 +70,10 @@ services: VELA_WEBUI_ADDR: 'http://localhost:8888' VELA_LOG_LEVEL: trace VELA_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' - VELA_REFRESH_TOKEN_DURATION: 90m - VELA_ACCESS_TOKEN_DURATION: 60m + VELA_SERVER_PRIVATE_KEY: 'F534FF2A080E45F38E05DC70752E6787' + VELA_USER_REFRESH_TOKEN_DURATION: 90m + VELA_USER_ACCESS_TOKEN_DURATION: 60m + VELA_WORKER_AUTH_TOKEN_DURATION: 3m VELA_DISABLE_WEBHOOK_VALIDATION: 'true' VELA_ENABLE_SECURE_COOKIE: 'false' VELA_REPO_ALLOWLIST: '*' diff --git a/go.mod b/go.mod index 032411b5..cfaa8a49 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/go-vela/worker go 1.19 +replace github.com/go-vela/sdk-go => ../sdk-go + require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/docker/distribution v2.8.1+incompatible diff --git a/router/middleware/auth_token.go b/router/middleware/auth_token.go new file mode 100644 index 00000000..d37a100e --- /dev/null +++ b/router/middleware/auth_token.go @@ -0,0 +1,18 @@ +// Copyright (c) 2022 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package middleware + +import ( + "github.com/gin-gonic/gin" +) + +// AuthToken is a middleware function that attaches the +// auth-token channel to the context of every http.Request. +func AuthToken(r chan string) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("auth-token", r) + c.Next() + } +} diff --git a/router/router.go b/router/router.go index 222029df..1c1f183a 100644 --- a/router/router.go +++ b/router/router.go @@ -108,5 +108,8 @@ func Load(options ...gin.HandlerFunc) *gin.Engine { ExecutorHandlers(baseAPI) } + // endpoint for passing a new registration token to the deadloop running operate.go + r.GET("/register", api.Register) + return r } From ec2a113bfebfbb7395048ba06a9ce6412474dd5f Mon Sep 17 00:00:00 2001 From: ecrupper Date: Thu, 2 Mar 2023 15:52:13 -0600 Subject: [PATCH 02/14] more work --- api/register.go | 36 +++++++++++++++++++++++------ cmd/vela-worker/operate.go | 46 ++++++++++++++++++------------------- cmd/vela-worker/register.go | 30 ++++++++---------------- cmd/vela-worker/worker.go | 1 + router/router.go | 2 +- 5 files changed, 63 insertions(+), 52 deletions(-) diff --git a/api/register.go b/api/register.go index 051fbf7e..7b9c22de 100644 --- a/api/register.go +++ b/api/register.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + package api import ( @@ -7,34 +11,52 @@ import ( "github.com/go-vela/worker/router/middleware/token" ) +// swagger:operation POST /register system Register +// +// Register the worker with the Vela server +// +// --- +// produces: +// - application/json +// parameters: +// security: +// - ApiKeyAuth: [] +// responses: +// '200': +// description: Successfully registered worker +// schema: +// type: string + +// Health check the status of the application. func Register(c *gin.Context) { - // extract the deadloop channel that was packed into gin context + // extract the auth token channel that was packed into gin context v, ok := c.Get("auth-token") if !ok { - c.JSON(http.StatusInternalServerError, "no deadloop channel in the context") + c.JSON(http.StatusInternalServerError, "no auth token channel in the context") return } - // make sure we configured it properly + // make sure we configured the channel properly authChannel, ok := v.(chan string) if !ok { - c.JSON(http.StatusInternalServerError, "deadloop channel in the context is the wrong type") + c.JSON(http.StatusInternalServerError, "auth token channel in the context is the wrong type") return } + // if auth token is present in the channel, deny registration if len(authChannel) > 0 { c.JSON(http.StatusOK, "worker already registered") return } - // this is a fake token, we would fetch this from the JSON body + // retrieve auth token from header token, err := token.Retrieve(c.Request) if err != nil { - c.JSON(http.StatusInternalServerError, "no deadloop channel in the context") + c.JSON(http.StatusInternalServerError, err) return } - // send the token + // write registration token to auth token channel authChannel <- token // somehow we need to make sure the registration worked diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index 8128d8c3..3adeedcb 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -37,24 +37,22 @@ func (w *Worker) operate(ctx context.Context) error { registryWorker.SetLastCheckedIn(time.Now().UTC().Unix()) registryWorker.SetBuildLimit(int64(w.Config.Build.Limit)) - // run the deadloop - logrus.Info("ranging over the deadloop channel, halting operation") - - // wait for registration token - token := <-w.AuthToken - - // continue operation like normal - logrus.Info("deadloop channel received token, continuing operation") - - // setup the vela client with the server - w.VelaClient, err = setupClient(w.Config.Server, token) - if err != nil { - return err - } - // spawn goroutine for phoning home executors.Go(func() error { for { + logrus.Info("verifying token is present in channel") + // wait for token + token := <-w.AuthToken + + // continue operation like normal + logrus.Info("token present, continuing operation") + + // setup the vela client with the token + w.VelaClient, err = setupClient(w.Config.Server, token) + if err != nil { + return err + } + select { case <-gctx.Done(): logrus.Info("Completed looping on worker registration") @@ -65,14 +63,12 @@ func (w *Worker) operate(ctx context.Context) error { // register or update the worker //nolint:contextcheck // ignore passing context - err = w.checkIn(registryWorker) - if err != nil { - logrus.Error(err) - } - - // if unable to update the worker, log the error but allow the worker to continue running + w.CheckedIn, err = w.checkIn(registryWorker) if err != nil { logrus.Errorf("unable to update worker %s on the server: %v", registryWorker.GetHostname(), err) + logrus.Info("waiting for registration token") + + continue } // sleep for the configured time @@ -81,9 +77,6 @@ func (w *Worker) operate(ctx context.Context) error { } }) - // ensure worker is registered before beginning to pull from queue. - _ = <-w.AuthToken - // setup the queue // // https://pkg.go.dev/github.com/go-vela/server/queue?tab=doc#New @@ -111,6 +104,11 @@ func (w *Worker) operate(ctx context.Context) error { executors.Go(func() error { // create an infinite loop to poll for builds for { + if !w.CheckedIn { + time.Sleep(5 * time.Second) + logrus.Info("worker not checked in, skipping queue read") + continue + } select { case <-gctx.Done(): logrus.WithFields(logrus.Fields{ diff --git a/cmd/vela-worker/register.go b/cmd/vela-worker/register.go index 416ea284..103ca05e 100644 --- a/cmd/vela-worker/register.go +++ b/cmd/vela-worker/register.go @@ -13,7 +13,7 @@ import ( ) // checkIn is a helper function to phone home to the server. -func (w *Worker) checkIn(config *library.Worker) error { +func (w *Worker) checkIn(config *library.Worker) (bool, error) { // check to see if the worker already exists in the database logrus.Infof("retrieving worker %s from the server", config.GetHostname()) @@ -21,14 +21,14 @@ func (w *Worker) checkIn(config *library.Worker) error { if err != nil { respErr := fmt.Errorf("unable to retrieve worker %s from the server: %w", config.GetHostname(), err) if resp == nil { - return respErr + return false, respErr } // if we receive a 404 the worker needs to be registered if resp.StatusCode == http.StatusNotFound { return w.register(config) } - return respErr + return false, respErr } // if we were able to GET the worker, update it @@ -36,38 +36,28 @@ func (w *Worker) checkIn(config *library.Worker) error { wrkCheckIn, _, err := w.VelaClient.Worker.Update(config.GetHostname(), config) if err != nil { - return fmt.Errorf("unable to update worker %s on the server: %w", config.GetHostname(), err) - } - - w.VelaClient.Authentication.SetTokenAuth(wrkCheckIn.Token.GetToken()) - - if len(w.AuthToken) > 0 { - <-w.AuthToken + return false, fmt.Errorf("unable to update worker %s on the server: %w", config.GetHostname(), err) } + // write the token to the auth token channel w.AuthToken <- wrkCheckIn.Token.GetToken() - return nil + return true, nil } // register is a helper function to register the worker with the server. -func (w *Worker) register(config *library.Worker) error { +func (w *Worker) register(config *library.Worker) (bool, error) { logrus.Infof("worker %s not found, registering it with the server", config.GetHostname()) tkn, _, err := w.VelaClient.Worker.Add(config) if err != nil { // log the error instead of returning so the operation doesn't block worker deployment - return fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) - } - - w.VelaClient.Authentication.SetTokenAuth(tkn.GetToken()) - - if len(w.AuthToken) > 0 { - <-w.AuthToken + return false, fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) } + // write the token to the auth token channel w.AuthToken <- tkn.GetToken() // successfully added the worker so return nil - return nil + return true, nil } diff --git a/cmd/vela-worker/worker.go b/cmd/vela-worker/worker.go index 6dcbf5b5..bbdd0cd1 100644 --- a/cmd/vela-worker/worker.go +++ b/cmd/vela-worker/worker.go @@ -68,5 +68,6 @@ type ( Runtime runtime.Engine VelaClient *vela.Client AuthToken chan string + CheckedIn bool } ) diff --git a/router/router.go b/router/router.go index 1c1f183a..07b165f2 100644 --- a/router/router.go +++ b/router/router.go @@ -109,7 +109,7 @@ func Load(options ...gin.HandlerFunc) *gin.Engine { } // endpoint for passing a new registration token to the deadloop running operate.go - r.GET("/register", api.Register) + r.POST("/register", api.Register) return r } From 0e126e73519f694ba5f977b6890a77844856fced Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 22 Mar 2023 13:51:19 -0500 Subject: [PATCH 03/14] backwards compatibility with comments --- cmd/vela-worker/flags.go | 1 + cmd/vela-worker/operate.go | 54 ++++++++++++++++++++------------ cmd/vela-worker/register.go | 2 ++ cmd/vela-worker/run.go | 4 +-- cmd/vela-worker/server.go | 1 + go.sum | 2 -- router/middleware/secret.go | 6 +++- router/middleware/secret_test.go | 2 +- router/middleware/server.go | 18 +++++++++++ router/middleware/server_test.go | 46 +++++++++++++++++++++++++++ router/middleware/user/user.go | 54 +++++++++++++++++++++++++++++--- 11 files changed, 159 insertions(+), 31 deletions(-) create mode 100644 router/middleware/server.go create mode 100644 router/middleware/server_test.go diff --git a/cmd/vela-worker/flags.go b/cmd/vela-worker/flags.go index b749a3c9..ba09ff69 100644 --- a/cmd/vela-worker/flags.go +++ b/cmd/vela-worker/flags.go @@ -80,6 +80,7 @@ func flags() []cli.Flag { EnvVars: []string{"WORKER_SERVER_SECRET", "VELA_SERVER_SECRET", "SERVER_SECRET"}, Name: "server.secret", Usage: "secret used for server <-> worker communication", + Value: "", }, &cli.StringFlag{ EnvVars: []string{"WORKER_SERVER_CERT", "VELA_SERVER_CERT", "SERVER_CERT"}, diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index 3adeedcb..c5cdf86b 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -37,22 +37,18 @@ func (w *Worker) operate(ctx context.Context) error { registryWorker.SetLastCheckedIn(time.Now().UTC().Unix()) registryWorker.SetBuildLimit(int64(w.Config.Build.Limit)) + // pull token from configuration if provided; wait if not + token := <-w.AuthToken + + // setup the vela client with the token + w.VelaClient, err = setupClient(w.Config.Server, token) + if err != nil { + return err + } + // spawn goroutine for phoning home executors.Go(func() error { for { - logrus.Info("verifying token is present in channel") - // wait for token - token := <-w.AuthToken - - // continue operation like normal - logrus.Info("token present, continuing operation") - - // setup the vela client with the token - w.VelaClient, err = setupClient(w.Config.Server, token) - if err != nil { - return err - } - select { case <-gctx.Done(): logrus.Info("Completed looping on worker registration") @@ -61,14 +57,31 @@ func (w *Worker) operate(ctx context.Context) error { // set checking time to now and call the server registryWorker.SetLastCheckedIn(time.Now().UTC().Unix()) - // register or update the worker - //nolint:contextcheck // ignore passing context - w.CheckedIn, err = w.checkIn(registryWorker) - if err != nil { - logrus.Errorf("unable to update worker %s on the server: %v", registryWorker.GetHostname(), err) - logrus.Info("waiting for registration token") + // check in attempt loop + for { + // register or update the worker + //nolint:contextcheck // ignore passing context + w.CheckedIn, err = w.checkIn(registryWorker) + if err != nil { + logrus.Errorf("unable to update worker %s on the server: %v", registryWorker.GetHostname(), err) + logrus.Info("retrying...") - continue + time.Sleep(3 * time.Second) + + continue + } + + // successful check in breaks the loop + break + } + + // pull token retrieved from check in from server + token := <-w.AuthToken + + // setup the vela client with the token + w.VelaClient, err = setupClient(w.Config.Server, token) + if err != nil { + return err } // sleep for the configured time @@ -104,6 +117,7 @@ func (w *Worker) operate(ctx context.Context) error { executors.Go(func() error { // create an infinite loop to poll for builds for { + // do not pull from queue unless worker is checked in with server if !w.CheckedIn { time.Sleep(5 * time.Second) logrus.Info("worker not checked in, skipping queue read") diff --git a/cmd/vela-worker/register.go b/cmd/vela-worker/register.go index 103ca05e..e7881eb9 100644 --- a/cmd/vela-worker/register.go +++ b/cmd/vela-worker/register.go @@ -55,6 +55,8 @@ func (w *Worker) register(config *library.Worker) (bool, error) { return false, fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) } + logrus.Infof("register function TOKEN from add worker: %s", tkn) + // write the token to the auth token channel w.AuthToken <- tkn.GetToken() diff --git a/cmd/vela-worker/run.go b/cmd/vela-worker/run.go index 66efd36f..736d6db9 100644 --- a/cmd/vela-worker/run.go +++ b/cmd/vela-worker/run.go @@ -145,8 +145,8 @@ func run(c *cli.Context) error { w.Config.API.Address, _ = url.Parse(fmt.Sprintf("http://%s", hostname)) } - if len(c.String("worker.token")) > 0 { - w.AuthToken <- c.String("worker-token") + if len(c.String("server.secret")) > 0 { + w.AuthToken <- c.String("server.secret") } // validate the worker diff --git a/cmd/vela-worker/server.go b/cmd/vela-worker/server.go index 70524261..566d92e7 100644 --- a/cmd/vela-worker/server.go +++ b/cmd/vela-worker/server.go @@ -31,6 +31,7 @@ func (w *Worker) server() (http.Handler, *tls.Config) { _server := router.Load( middleware.RequestVersion, middleware.Executors(w.Executors), + middleware.Server(w.Config.Server.Address), middleware.Secret(w.Config.Server.Secret), middleware.Logger(logrus.StandardLogger(), time.RFC3339, true), middleware.AuthToken(w.AuthToken), diff --git a/go.sum b/go.sum index 849d7f28..bf06d2c1 100644 --- a/go.sum +++ b/go.sum @@ -156,8 +156,6 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-vela/sdk-go v0.18.0-rc2 h1:LR4Lt/ELT7SAQYv8fjQnfx8MjjtkLVcIGhNvlImqAdM= -github.com/go-vela/sdk-go v0.18.0-rc2/go.mod h1:Z3YFC+6vvghCvEfwlb/PKLW0Tx8BBDz4q4KQ3mYhUK0= github.com/go-vela/server v0.18.0-rc2 h1:/0GGtSG6q8hdYevLT8XkUijdbwzccGHY/d2jRTxDQug= github.com/go-vela/server v0.18.0-rc2/go.mod h1:VQpgbboMvXK3vP9Hwr837BA5dNC1SqRpJYgl64Vz5HI= github.com/go-vela/types v0.18.0-rc1 h1:q93g+A/GOP56vmMi8AH1BKtgmqZy3gj5PD66Wqx8ej4= diff --git a/router/middleware/secret.go b/router/middleware/secret.go index c880a425..2e83c13d 100644 --- a/router/middleware/secret.go +++ b/router/middleware/secret.go @@ -8,10 +8,14 @@ import ( "github.com/gin-gonic/gin" ) -// Secret is a middleware function that attaches the secret used for +// Secret is a middleware function that attaches the vela server secret used for // server <-> agent communication to the context of every http.Request. func Secret(secret string) gin.HandlerFunc { return func(c *gin.Context) { + if len(secret) == 0 { + return + } + c.Set("secret", secret) c.Next() } diff --git a/router/middleware/secret_test.go b/router/middleware/secret_test.go index 1f37a084..2e532ef6 100644 --- a/router/middleware/secret_test.go +++ b/router/middleware/secret_test.go @@ -26,7 +26,7 @@ func TestMiddleware_Secret(t *testing.T) { context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) // setup mock server - engine.Use(Secret(want)) + engine.Use(Server(want)) engine.GET("/health", func(c *gin.Context) { got = c.Value("secret").(string) diff --git a/router/middleware/server.go b/router/middleware/server.go new file mode 100644 index 00000000..969faced --- /dev/null +++ b/router/middleware/server.go @@ -0,0 +1,18 @@ +// Copyright (c) 2022 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package middleware + +import ( + "github.com/gin-gonic/gin" +) + +// Server is a middleware function that attaches the vela server address used for +// server <-> agent communication to the context of every http.Request. +func Server(server string) gin.HandlerFunc { + return func(c *gin.Context) { + c.Set("server", server) + c.Next() + } +} diff --git a/router/middleware/server_test.go b/router/middleware/server_test.go new file mode 100644 index 00000000..45cc0616 --- /dev/null +++ b/router/middleware/server_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2022 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package middleware + +import ( + "net/http" + "net/http/httptest" + "reflect" + "testing" + + "github.com/gin-gonic/gin" +) + +func TestMiddleware_Server(t *testing.T) { + // setup types + got := "" + want := "foobar" + + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) + + // setup mock server + engine.Use(Server(want)) + engine.GET("/health", func(c *gin.Context) { + got = c.Value("server").(string) + + c.Status(http.StatusOK) + }) + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("Server returned %v, want %v", resp.Code, http.StatusOK) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("Server is %v, want %v", got, want) + } +} diff --git a/router/middleware/user/user.go b/router/middleware/user/user.go index 817aa0e9..639b5a6d 100644 --- a/router/middleware/user/user.go +++ b/router/middleware/user/user.go @@ -5,10 +5,15 @@ package user import ( + "context" + "fmt" "net/http" "strings" + "time" + "github.com/go-vela/server/util" "github.com/go-vela/worker/router/middleware/token" + "github.com/sirupsen/logrus" "github.com/go-vela/types/library" @@ -31,12 +36,51 @@ func Establish() gin.HandlerFunc { return } - secret := c.MustGet("secret").(string) - if strings.EqualFold(t, secret) { - u.SetName("vela-server") - u.SetActive(true) - u.SetAdmin(true) + if secret, ok := c.Value("secret").(string); ok { + if strings.EqualFold(t, secret) { + u.SetName("vela-server") + u.SetActive(true) + u.SetAdmin(true) + + ToContext(c, u) + c.Next() + + return + } + } + + // prepare the request to the worker + client := http.DefaultClient + client.Timeout = 30 * time.Second + + // set the API endpoint path we send the request to + url := fmt.Sprintf("%s/validate-token", c.MustGet("server")) + + req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil) + if err != nil { + retErr := fmt.Errorf("unable to form a request to %s: %w", u, err) + util.HandleError(c, http.StatusBadRequest, retErr) + + return + } + + // add the token to authenticate to the worker + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t)) + + // perform the request to the server + resp, err := client.Do(req) + if err != nil { + logrus.Debug("token validation for server token failed, adding nil user to context") + ToContext(c, u) + c.Next() + + return } + defer resp.Body.Close() + + u.SetName("vela-server") + u.SetActive(true) + u.SetAdmin(true) ToContext(c, u) c.Next() From 3c26bd6fab87d7fc0155642c769493fc51223567 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Thu, 23 Mar 2023 14:11:14 -0500 Subject: [PATCH 04/14] adjust middleware to only use validate-token for server tokens --- cmd/vela-worker/server.go | 1 - router/middleware/secret.go | 22 --------------- router/middleware/secret_test.go | 46 -------------------------------- router/middleware/user/user.go | 18 ++----------- 4 files changed, 2 insertions(+), 85 deletions(-) delete mode 100644 router/middleware/secret.go delete mode 100644 router/middleware/secret_test.go diff --git a/cmd/vela-worker/server.go b/cmd/vela-worker/server.go index 566d92e7..c4dbca1f 100644 --- a/cmd/vela-worker/server.go +++ b/cmd/vela-worker/server.go @@ -32,7 +32,6 @@ func (w *Worker) server() (http.Handler, *tls.Config) { middleware.RequestVersion, middleware.Executors(w.Executors), middleware.Server(w.Config.Server.Address), - middleware.Secret(w.Config.Server.Secret), middleware.Logger(logrus.StandardLogger(), time.RFC3339, true), middleware.AuthToken(w.AuthToken), ) diff --git a/router/middleware/secret.go b/router/middleware/secret.go deleted file mode 100644 index 2e83c13d..00000000 --- a/router/middleware/secret.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package middleware - -import ( - "github.com/gin-gonic/gin" -) - -// Secret is a middleware function that attaches the vela server secret used for -// server <-> agent communication to the context of every http.Request. -func Secret(secret string) gin.HandlerFunc { - return func(c *gin.Context) { - if len(secret) == 0 { - return - } - - c.Set("secret", secret) - c.Next() - } -} diff --git a/router/middleware/secret_test.go b/router/middleware/secret_test.go deleted file mode 100644 index 2e532ef6..00000000 --- a/router/middleware/secret_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package middleware - -import ( - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/gin-gonic/gin" -) - -func TestMiddleware_Secret(t *testing.T) { - // setup types - got := "" - want := "foobar" - - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) - - // setup mock server - engine.Use(Server(want)) - engine.GET("/health", func(c *gin.Context) { - got = c.Value("secret").(string) - - c.Status(http.StatusOK) - }) - - // run test - engine.ServeHTTP(context.Writer, context.Request) - - if resp.Code != http.StatusOK { - t.Errorf("Secret returned %v, want %v", resp.Code, http.StatusOK) - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("Secret is %v, want %v", got, want) - } -} diff --git a/router/middleware/user/user.go b/router/middleware/user/user.go index 639b5a6d..fa3e2f2a 100644 --- a/router/middleware/user/user.go +++ b/router/middleware/user/user.go @@ -8,7 +8,6 @@ import ( "context" "fmt" "net/http" - "strings" "time" "github.com/go-vela/server/util" @@ -36,19 +35,6 @@ func Establish() gin.HandlerFunc { return } - if secret, ok := c.Value("secret").(string); ok { - if strings.EqualFold(t, secret) { - u.SetName("vela-server") - u.SetActive(true) - u.SetAdmin(true) - - ToContext(c, u) - c.Next() - - return - } - } - // prepare the request to the worker client := http.DefaultClient client.Timeout = 30 * time.Second @@ -68,8 +54,8 @@ func Establish() gin.HandlerFunc { req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t)) // perform the request to the server - resp, err := client.Do(req) - if err != nil { + resp, _ := client.Do(req) + if resp.StatusCode != http.StatusOK { logrus.Debug("token validation for server token failed, adding nil user to context") ToContext(c, u) c.Next() From 3cfd06355557fffc21a34210470e8188fe0ad3f4 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 28 Mar 2023 12:43:37 -0500 Subject: [PATCH 05/14] more work --- cmd/vela-worker/operate.go | 29 ++- cmd/vela-worker/register.go | 24 +- cmd/vela-worker/validate.go | 5 - go.mod | 4 +- go.sum | 8 +- router/middleware/perm/perm.go | 66 ++++- router/middleware/perm/perm_test.go | 344 +++++++++++++++++++++---- router/middleware/server.go | 2 +- router/middleware/user/context.go | 39 --- router/middleware/user/context_test.go | 90 ------- router/middleware/user/doc.go | 12 - router/middleware/user/user.go | 74 ------ router/middleware/user/user_test.go | 162 ------------ router/router.go | 3 +- 14 files changed, 390 insertions(+), 472 deletions(-) delete mode 100644 router/middleware/user/context.go delete mode 100644 router/middleware/user/context_test.go delete mode 100644 router/middleware/user/doc.go delete mode 100644 router/middleware/user/user.go delete mode 100644 router/middleware/user/user_test.go diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index c5cdf86b..252ffbea 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -34,7 +34,6 @@ func (w *Worker) operate(ctx context.Context) error { registryWorker.SetAddress(w.Config.API.Address.String()) registryWorker.SetRoutes(w.Config.Queue.Routes) registryWorker.SetActive(true) - registryWorker.SetLastCheckedIn(time.Now().UTC().Unix()) registryWorker.SetBuildLimit(int64(w.Config.Build.Limit)) // pull token from configuration if provided; wait if not @@ -54,19 +53,32 @@ func (w *Worker) operate(ctx context.Context) error { logrus.Info("Completed looping on worker registration") return nil default: - // set checking time to now and call the server - registryWorker.SetLastCheckedIn(time.Now().UTC().Unix()) - // check in attempt loop for { // register or update the worker //nolint:contextcheck // ignore passing context - w.CheckedIn, err = w.checkIn(registryWorker) + w.CheckedIn, token, err = w.checkIn(registryWorker) + // check in failed if err != nil { - logrus.Errorf("unable to update worker %s on the server: %v", registryWorker.GetHostname(), err) + // token has expired + if w.VelaClient.Authentication.IsTokenAuthExpired() && len(w.Config.Server.Secret) == 0 { + // wait on new registration token, return to check in attempt + token = <-w.AuthToken + + // setup the vela client with the token + w.VelaClient, err = setupClient(w.Config.Server, token) + if err != nil { + return err + } + + continue + } + + // check in failed, token is still valid, retry + logrus.Errorf("unable to check-in worker %s on the server: %v", registryWorker.GetHostname(), err) logrus.Info("retrying...") - time.Sleep(3 * time.Second) + time.Sleep(5 * time.Second) continue } @@ -75,9 +87,6 @@ func (w *Worker) operate(ctx context.Context) error { break } - // pull token retrieved from check in from server - token := <-w.AuthToken - // setup the vela client with the token w.VelaClient, err = setupClient(w.Config.Server, token) if err != nil { diff --git a/cmd/vela-worker/register.go b/cmd/vela-worker/register.go index e7881eb9..7f7a86d4 100644 --- a/cmd/vela-worker/register.go +++ b/cmd/vela-worker/register.go @@ -13,7 +13,7 @@ import ( ) // checkIn is a helper function to phone home to the server. -func (w *Worker) checkIn(config *library.Worker) (bool, error) { +func (w *Worker) checkIn(config *library.Worker) (bool, string, error) { // check to see if the worker already exists in the database logrus.Infof("retrieving worker %s from the server", config.GetHostname()) @@ -21,45 +21,39 @@ func (w *Worker) checkIn(config *library.Worker) (bool, error) { if err != nil { respErr := fmt.Errorf("unable to retrieve worker %s from the server: %w", config.GetHostname(), err) if resp == nil { - return false, respErr + return false, "", respErr } // if we receive a 404 the worker needs to be registered if resp.StatusCode == http.StatusNotFound { return w.register(config) } - return false, respErr + return false, "", respErr } // if we were able to GET the worker, update it logrus.Infof("checking worker %s into the server", config.GetHostname()) - wrkCheckIn, _, err := w.VelaClient.Worker.Update(config.GetHostname(), config) + tkn, _, err := w.VelaClient.Worker.RefreshAuth(config.GetHostname()) if err != nil { - return false, fmt.Errorf("unable to update worker %s on the server: %w", config.GetHostname(), err) + return false, "", fmt.Errorf("unable to refresh auth for worker %s on the server: %w", config.GetHostname(), err) } - // write the token to the auth token channel - w.AuthToken <- wrkCheckIn.Token.GetToken() - - return true, nil + return true, tkn.GetToken(), nil } // register is a helper function to register the worker with the server. -func (w *Worker) register(config *library.Worker) (bool, error) { +func (w *Worker) register(config *library.Worker) (bool, string, error) { logrus.Infof("worker %s not found, registering it with the server", config.GetHostname()) tkn, _, err := w.VelaClient.Worker.Add(config) if err != nil { // log the error instead of returning so the operation doesn't block worker deployment - return false, fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) + return false, "", fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) } logrus.Infof("register function TOKEN from add worker: %s", tkn) - // write the token to the auth token channel - w.AuthToken <- tkn.GetToken() - // successfully added the worker so return nil - return true, nil + return true, tkn.GetToken(), nil } diff --git a/cmd/vela-worker/validate.go b/cmd/vela-worker/validate.go index 84db82b1..82d1b79a 100644 --- a/cmd/vela-worker/validate.go +++ b/cmd/vela-worker/validate.go @@ -52,11 +52,6 @@ func (w *Worker) Validate() error { return fmt.Errorf("no worker server address provided") } - // verify a server secret was provided - if len(w.Config.Server.Secret) == 0 { - return fmt.Errorf("no worker server secret provided") - } - // verify an executor driver was provided if len(w.Config.Executor.Driver) == 0 { return fmt.Errorf("no worker executor driver provided") diff --git a/go.mod b/go.mod index cfaa8a49..ab2b4754 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/docker/go-units v0.5.0 github.com/gin-gonic/gin v1.9.0 github.com/go-vela/sdk-go v0.18.0-rc2 - github.com/go-vela/server v0.18.0-rc2 - github.com/go-vela/types v0.18.0-rc1 + github.com/go-vela/server v0.18.2-0.20230323213747-5ffbe819aa29 + github.com/go-vela/types v0.18.2-0.20230321015315-6c723879639c github.com/google/go-cmp v0.5.9 github.com/joho/godotenv v1.5.1 github.com/opencontainers/image-spec v1.0.2 diff --git a/go.sum b/go.sum index bf06d2c1..ef5d1c58 100644 --- a/go.sum +++ b/go.sum @@ -156,10 +156,10 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-vela/server v0.18.0-rc2 h1:/0GGtSG6q8hdYevLT8XkUijdbwzccGHY/d2jRTxDQug= -github.com/go-vela/server v0.18.0-rc2/go.mod h1:VQpgbboMvXK3vP9Hwr837BA5dNC1SqRpJYgl64Vz5HI= -github.com/go-vela/types v0.18.0-rc1 h1:q93g+A/GOP56vmMi8AH1BKtgmqZy3gj5PD66Wqx8ej4= -github.com/go-vela/types v0.18.0-rc1/go.mod h1:6MzMhLaXKSZ9wiJveieqnBd2+4ZMS7yv7+POGSITyS8= +github.com/go-vela/server v0.18.2-0.20230323213747-5ffbe819aa29 h1:DSbHMwxvI65vXAumZ1V9o82Q8W3J7Cce+grG8aqPPEw= +github.com/go-vela/server v0.18.2-0.20230323213747-5ffbe819aa29/go.mod h1:b+7XeGHO4ynIinY9mpWb6ye9psdwHpsAqMWy5oC+zJ0= +github.com/go-vela/types v0.18.2-0.20230321015315-6c723879639c h1:lnCL1knUGvgZQG4YBHSs/CZnxNBfqFUBlGhyq9LO9uk= +github.com/go-vela/types v0.18.2-0.20230321015315-6c723879639c/go.mod h1:6MzMhLaXKSZ9wiJveieqnBd2+4ZMS7yv7+POGSITyS8= github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/router/middleware/perm/perm.go b/router/middleware/perm/perm.go index 135d452c..85dd60d8 100644 --- a/router/middleware/perm/perm.go +++ b/router/middleware/perm/perm.go @@ -9,29 +9,79 @@ import ( "net/http" "strings" + "github.com/go-vela/sdk-go/vela" "github.com/go-vela/types" - "github.com/go-vela/worker/router/middleware/user" + "github.com/go-vela/worker/router/middleware/token" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) -// MustServer ensures the user is the vela server. +// MustServer ensures the caller is the vela server. func MustServer() gin.HandlerFunc { return func(c *gin.Context) { - u := user.Retrieve(c) + // retrieve the callers token from the request headers + tkn, err := token.Retrieve(c.Request) + if err != nil { + msg := fmt.Sprintf("error parsing token: %v", err) + + logrus.Error(msg) + + c.AbortWithStatusJSON(http.StatusBadRequest, types.Error{Message: &msg}) + + return + } + + // retrieve the configured server address from the context + addr := c.MustGet("server-address").(string) + + // create a temporary client to validate the incoming request + vela, err := vela.NewClient(addr, "vela-worker", nil) + if err != nil { + msg := fmt.Sprintf("error creating vela client: %s", err) + + logrus.Error(msg) + + c.AbortWithStatusJSON(http.StatusInternalServerError, types.Error{Message: &msg}) + + return + } + + // validate a token was provided + if strings.EqualFold(tkn, "") { + msg := "missing token" + + logrus.Error(msg) + + c.AbortWithStatusJSON(http.StatusBadRequest, types.Error{Message: &msg}) - if strings.EqualFold(u.GetName(), "vela-server") { return } - msg := fmt.Sprintf("User %s is not a platform admin", u.GetName()) + // set the token auth provided in the callers request header + vela.Authentication.SetTokenAuth(tkn) - err := c.Error(fmt.Errorf(msg)) + // validate the token with the configured vela server + resp, err := vela.Authentication.ValidateToken() if err != nil { - logrus.Error(err) + msg := fmt.Sprintf("error validating token: %s", err) + + logrus.Error(msg) + + c.AbortWithStatusJSON(http.StatusInternalServerError, types.Error{Message: &msg}) + + return } - c.AbortWithStatusJSON(http.StatusUnauthorized, types.Error{Message: &msg}) + // if ValidateToken returned anything other than 200 consider the token invalid + if resp.StatusCode != http.StatusOK { + msg := "unable to validate token" + + logrus.Error(msg) + + c.AbortWithStatusJSON(http.StatusUnauthorized, types.Error{Message: &msg}) + + return + } } } diff --git a/router/middleware/perm/perm_test.go b/router/middleware/perm/perm_test.go index 64eb967c..c0e634a0 100644 --- a/router/middleware/perm/perm_test.go +++ b/router/middleware/perm/perm_test.go @@ -10,85 +10,333 @@ import ( "net/http/httptest" "testing" - "github.com/go-vela/worker/router/middleware/user" - - "github.com/go-vela/types/library" - "github.com/gin-gonic/gin" ) -func TestPerm_MustServer_success(t *testing.T) { +func TestPerm_MustServer_ValidateToken200(t *testing.T) { + // setup types + tkn := "superSecret" + + // setup context + gin.SetMode(gin.TestMode) + + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + workerCtx.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn)) + + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + + // mocked token validation endpoint used in MustServer + serverEngine.GET("/validate-token", func(c *gin.Context) { + // token is not expired and matches server token + c.Status(http.StatusOK) + }) + + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() + + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", serverMock.URL) }) + + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() + + // run test + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) + + if workerResp.Code != http.StatusOK { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusOK) + } +} + +func TestPerm_MustServer_ValidateToken401(t *testing.T) { + // setup types + tkn := "superSecret" + + // setup context + gin.SetMode(gin.TestMode) + + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + workerCtx.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn)) + + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + + // mocked token validation endpoint used in MustServer + serverEngine.GET("/validate-token", func(c *gin.Context) { + // test that validate-token returning a 401 works as expected + c.Status(http.StatusUnauthorized) + }) + + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() + + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", serverMock.URL) }) + + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() + + // run test + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) + + if workerResp.Code != http.StatusUnauthorized { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusUnauthorized) + } +} + +func TestPerm_MustServer_ValidateToken404(t *testing.T) { + // setup types + tkn := "superSecret" + + // setup context + gin.SetMode(gin.TestMode) + + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + workerCtx.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn)) + + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + + // skip mocked token validation endpoint used in MustServer + // test that validate-token returning a 404 works as expected + + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() + + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", serverMock.URL) }) + + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() + + // run test + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) + + if workerResp.Code != http.StatusUnauthorized { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusUnauthorized) + } +} + +func TestPerm_MustServer_ValidateToken500(t *testing.T) { // setup types - secret := "superSecret" + tkn := "superSecret" + + // setup context + gin.SetMode(gin.TestMode) + + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + workerCtx.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn)) + + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + + // mocked token validation endpoint used in MustServer + serverEngine.GET("/validate-token", func(c *gin.Context) { + // validate-token returning a server error + c.Status(http.StatusInternalServerError) + }) + + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() - u := new(library.User) - u.SetID(1) - u.SetName("vela-server") - u.SetToken("bar") - u.SetHash("baz") - u.SetAdmin(true) + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", serverMock.URL) }) + + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() + + // run test + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) + + if workerResp.Code != http.StatusUnauthorized { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusUnauthorized) + } +} + +func TestPerm_MustServer_BadServerAddress(t *testing.T) { + // setup types + tkn := "superSecret" + badServerAddress := "test.example.com" // setup context gin.SetMode(gin.TestMode) - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/server/users", nil) - context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", secret)) + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + workerCtx.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn)) - // setup vela mock server - engine.Use(func(c *gin.Context) { c.Set("secret", secret) }) - engine.Use(user.Establish()) - engine.Use(MustServer()) - engine.GET("/server/users", func(c *gin.Context) { + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + + // mocked token validation endpoint used in MustServer + serverEngine.GET("/validate-token", func(c *gin.Context) { c.Status(http.StatusOK) }) - s1 := httptest.NewServer(engine) - defer s1.Close() + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() + + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", badServerAddress) }) + + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() // run test - engine.ServeHTTP(context.Writer, context.Request) + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) - if resp.Code != http.StatusOK { - t.Errorf("MustServer returned %v, want %v", resp.Code, http.StatusOK) + if workerResp.Code != http.StatusInternalServerError { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusInternalServerError) } } -func TestPerm_MustServer_failure(t *testing.T) { +func TestPerm_MustServer_NoToken(t *testing.T) { // setup types - secret := "foo" + tkn := "" + + // setup context + gin.SetMode(gin.TestMode) + + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + workerCtx.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tkn)) - u := new(library.User) - u.SetID(1) - u.SetName("not-vela-server") - u.SetToken("bar") - u.SetHash("baz") - u.SetAdmin(true) + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + // mocked token validation endpoint used in MustServer + serverEngine.GET("/validate-token", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() + + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", serverMock.URL) }) + + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() + + // run test + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) + + if workerResp.Code != http.StatusBadRequest { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusBadRequest) + } +} + +func TestPerm_MustServer_NoAuth(t *testing.T) { // setup context gin.SetMode(gin.TestMode) - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/server/users", nil) - context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", secret)) + // setup mock worker router + workerResp := httptest.NewRecorder() + workerCtx, workerEngine := gin.CreateTestContext(workerResp) + + // fake request made to the worker router + workerCtx.Request, _ = http.NewRequest(http.MethodGet, "/build/cancel", nil) + // test that skipping adding an authorization header is handled properly + + // setup mock server router + // the URL of the mock server router is injected into the mock worker router + serverResp := httptest.NewRecorder() + _, serverEngine := gin.CreateTestContext(serverResp) + + // mocked token validation endpoint used in MustServer + serverEngine.GET("/validate-token", func(c *gin.Context) { + c.Status(http.StatusOK) + }) + + serverMock := httptest.NewServer(serverEngine) + defer serverMock.Close() + + workerEngine.Use(func(c *gin.Context) { c.Set("server-address", serverMock.URL) }) - // setup vela mock server - engine.Use(func(c *gin.Context) { c.Set("secret", secret) }) - engine.Use(func(c *gin.Context) { c.Set("user", u) }) - engine.Use(MustServer()) - engine.GET("/server/users", func(c *gin.Context) { + // attach perm middleware that we are testing + workerEngine.Use(MustServer()) + workerEngine.GET("/build/cancel", func(c *gin.Context) { c.Status(http.StatusOK) }) - s1 := httptest.NewServer(engine) - defer s1.Close() + workerMock := httptest.NewServer(workerEngine) + defer workerMock.Close() // run test - engine.ServeHTTP(context.Writer, context.Request) + workerEngine.ServeHTTP(workerCtx.Writer, workerCtx.Request) - if resp.Code != http.StatusUnauthorized { - t.Errorf("MustServer returned %v, want %v", resp.Code, http.StatusUnauthorized) + if workerResp.Code != http.StatusBadRequest { + t.Errorf("MustServer returned %v, want %v", workerResp.Code, http.StatusBadRequest) } } diff --git a/router/middleware/server.go b/router/middleware/server.go index 969faced..45280360 100644 --- a/router/middleware/server.go +++ b/router/middleware/server.go @@ -12,7 +12,7 @@ import ( // server <-> agent communication to the context of every http.Request. func Server(server string) gin.HandlerFunc { return func(c *gin.Context) { - c.Set("server", server) + c.Set("server-address", server) c.Next() } } diff --git a/router/middleware/user/context.go b/router/middleware/user/context.go deleted file mode 100644 index 944a1f3e..00000000 --- a/router/middleware/user/context.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package user - -import ( - "context" - - "github.com/go-vela/types/library" -) - -const key = "user" - -// Setter defines a context that enables setting values. -type Setter interface { - Set(string, interface{}) -} - -// FromContext returns the User associated with this context. -func FromContext(c context.Context) *library.User { - value := c.Value(key) - if value == nil { - return nil - } - - u, ok := value.(*library.User) - if !ok { - return nil - } - - return u -} - -// ToContext adds the User to this context if it supports -// the Setter interface. -func ToContext(c Setter, u *library.User) { - c.Set(key, u) -} diff --git a/router/middleware/user/context_test.go b/router/middleware/user/context_test.go deleted file mode 100644 index 6cfe188a..00000000 --- a/router/middleware/user/context_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package user - -import ( - "testing" - - "github.com/go-vela/types/library" - - "github.com/gin-gonic/gin" -) - -func TestUser_FromContext(t *testing.T) { - // setup types - uID := int64(1) - want := &library.User{ID: &uID} - - // setup context - gin.SetMode(gin.TestMode) - context, _ := gin.CreateTestContext(nil) - context.Set(key, want) - - // run test - got := FromContext(context) - - if got != want { - t.Errorf("FromContext is %v, want %v", got, want) - } -} - -func TestUser_FromContext_Bad(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - context, _ := gin.CreateTestContext(nil) - context.Set(key, nil) - - // run test - got := FromContext(context) - - if got != nil { - t.Errorf("FromContext is %v, want nil", got) - } -} - -func TestUser_FromContext_WrongType(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - context, _ := gin.CreateTestContext(nil) - context.Set(key, 1) - - // run test - got := FromContext(context) - - if got != nil { - t.Errorf("FromContext is %v, want nil", got) - } -} - -func TestUser_FromContext_Empty(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - context, _ := gin.CreateTestContext(nil) - - // run test - got := FromContext(context) - - if got != nil { - t.Errorf("FromContext is %v, want nil", got) - } -} - -func TestUser_ToContext(t *testing.T) { - // setup types - uID := int64(1) - want := &library.User{ID: &uID} - - // setup context - gin.SetMode(gin.TestMode) - context, _ := gin.CreateTestContext(nil) - ToContext(context, want) - - // run test - got := context.Value(key) - - if got != want { - t.Errorf("ToContext is %v, want %v", got, want) - } -} diff --git a/router/middleware/user/doc.go b/router/middleware/user/doc.go deleted file mode 100644 index 7ba0a485..00000000 --- a/router/middleware/user/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -// Package user provides the ability for inserting -// Vela user resources into or extracting Vela user -// resources from the middleware chain for the API. -// -// Usage: -// -// import "github.com/go-vela/worker/router/middleware/user" -package user diff --git a/router/middleware/user/user.go b/router/middleware/user/user.go deleted file mode 100644 index fa3e2f2a..00000000 --- a/router/middleware/user/user.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package user - -import ( - "context" - "fmt" - "net/http" - "time" - - "github.com/go-vela/server/util" - "github.com/go-vela/worker/router/middleware/token" - "github.com/sirupsen/logrus" - - "github.com/go-vela/types/library" - - "github.com/gin-gonic/gin" -) - -// Retrieve gets the user in the given context. -func Retrieve(c *gin.Context) *library.User { - return FromContext(c) -} - -// Establish sets the user in the given context. -func Establish() gin.HandlerFunc { - return func(c *gin.Context) { - u := new(library.User) - - t, err := token.Retrieve(c.Request) - if err != nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, err.Error()) - return - } - - // prepare the request to the worker - client := http.DefaultClient - client.Timeout = 30 * time.Second - - // set the API endpoint path we send the request to - url := fmt.Sprintf("%s/validate-token", c.MustGet("server")) - - req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil) - if err != nil { - retErr := fmt.Errorf("unable to form a request to %s: %w", u, err) - util.HandleError(c, http.StatusBadRequest, retErr) - - return - } - - // add the token to authenticate to the worker - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t)) - - // perform the request to the server - resp, _ := client.Do(req) - if resp.StatusCode != http.StatusOK { - logrus.Debug("token validation for server token failed, adding nil user to context") - ToContext(c, u) - c.Next() - - return - } - defer resp.Body.Close() - - u.SetName("vela-server") - u.SetActive(true) - u.SetAdmin(true) - - ToContext(c, u) - c.Next() - } -} diff --git a/router/middleware/user/user_test.go b/router/middleware/user/user_test.go deleted file mode 100644 index b4f8b52f..00000000 --- a/router/middleware/user/user_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package user - -import ( - "fmt" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/go-vela/types/library" - - "github.com/gin-gonic/gin" -) - -func TestUser_Retrieve(t *testing.T) { - // setup types - want := new(library.User) - want.SetID(1) - - // setup context - gin.SetMode(gin.TestMode) - - context, _ := gin.CreateTestContext(nil) - ToContext(context, want) - - // run test - got := Retrieve(context) - - if got != want { - t.Errorf("Retrieve is %v, want %v", got, want) - } -} - -func TestUser_Establish(t *testing.T) { - // setup types - secret := "superSecret" - got := new(library.User) - want := new(library.User) - want.SetName("vela-server") - want.SetActive(true) - want.SetAdmin(true) - - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/users/vela-server", nil) - context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", secret)) - - // setup vela mock server - engine.Use(func(c *gin.Context) { c.Set("secret", secret) }) - engine.Use(Establish()) - engine.GET("/users/:user", func(c *gin.Context) { - got = Retrieve(c) - - c.Status(http.StatusOK) - }) - - s1 := httptest.NewServer(engine) - defer s1.Close() - - // run test - engine.ServeHTTP(context.Writer, context.Request) - - if resp.Code != http.StatusOK { - t.Errorf("Establish returned %v, want %v", resp.Code, http.StatusOK) - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("Establish is %v, want %v", got, want) - } -} - -func TestUser_Establish_NoToken(t *testing.T) { - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/users/foo", nil) - - // setup mock server - engine.Use(Establish()) - - // run test - engine.ServeHTTP(context.Writer, context.Request) - - if resp.Code != http.StatusUnauthorized { - t.Errorf("Establish returned %v, want %v", resp.Code, http.StatusUnauthorized) - } -} - -func TestUser_Establish_SecretValid(t *testing.T) { - // setup types - secret := "superSecret" - - want := new(library.User) - want.SetName("vela-server") - want.SetActive(true) - want.SetAdmin(true) - - got := new(library.User) - - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/users/vela-server", nil) - context.Request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", secret)) - - // setup vela mock server - engine.Use(func(c *gin.Context) { c.Set("secret", secret) }) - engine.Use(Establish()) - engine.GET("/users/:user", func(c *gin.Context) { - got = Retrieve(c) - - c.Status(http.StatusOK) - }) - - s := httptest.NewServer(engine) - defer s.Close() - - // run test - engine.ServeHTTP(context.Writer, context.Request) - - if resp.Code != http.StatusOK { - t.Errorf("Establish returned %v, want %v", resp.Code, http.StatusOK) - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("Establish is %v, want %v", got, want) - } -} - -func TestUser_Establish_NoAuthorizeUser(t *testing.T) { - // setup types - secret := "superSecret" - - // setup context - gin.SetMode(gin.TestMode) - - resp := httptest.NewRecorder() - context, engine := gin.CreateTestContext(resp) - context.Request, _ = http.NewRequest(http.MethodGet, "/users/foo?access_token=bar", nil) - - // setup vela mock server - engine.Use(func(c *gin.Context) { c.Set("secret", secret) }) - engine.Use(Establish()) - - // run test - engine.ServeHTTP(context.Writer, context.Request) - - if resp.Code != http.StatusUnauthorized { - t.Errorf("Establish returned %v, want %v", resp.Code, http.StatusUnauthorized) - } -} diff --git a/router/router.go b/router/router.go index 07b165f2..7de750f4 100644 --- a/router/router.go +++ b/router/router.go @@ -31,7 +31,6 @@ import ( "github.com/go-vela/worker/api" "github.com/go-vela/worker/router/middleware" "github.com/go-vela/worker/router/middleware/perm" - "github.com/go-vela/worker/router/middleware/user" ) const ( @@ -95,7 +94,7 @@ func Load(options ...gin.HandlerFunc) *gin.Engine { // add a collection of endpoints for handling API related requests // // https://pkg.go.dev/github.com/gin-gonic/gin?tab=doc#RouterGroup.Group - baseAPI := r.Group(base, user.Establish(), perm.MustServer()) + baseAPI := r.Group(base, perm.MustServer()) { // add an endpoint for shutting down the worker // From fda7052c32a3f8084ba0e18399b5b493e859b1af Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 28 Mar 2023 13:20:54 -0500 Subject: [PATCH 06/14] rename auth token channel to RegisterToken --- cmd/vela-worker/flags.go | 6 ------ cmd/vela-worker/operate.go | 6 +++--- cmd/vela-worker/run.go | 5 +++-- cmd/vela-worker/server.go | 2 +- cmd/vela-worker/worker.go | 14 +++++++------- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/cmd/vela-worker/flags.go b/cmd/vela-worker/flags.go index ba09ff69..23d7b025 100644 --- a/cmd/vela-worker/flags.go +++ b/cmd/vela-worker/flags.go @@ -33,12 +33,6 @@ func flags() []cli.Flag { Value: 15 * time.Minute, }, - &cli.StringFlag{ - EnvVars: []string{"WORKER_REGISTRATION_TOKEN", "VELA_REGISTRATION_TOKEN", "REGISTRATION_TOKEN"}, - Name: "worker.token", - Usage: "seed worker registration token on start up to check in with server", - }, - // Build Flags &cli.IntFlag{ diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index 252ffbea..fbb1e8f5 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -36,8 +36,8 @@ func (w *Worker) operate(ctx context.Context) error { registryWorker.SetActive(true) registryWorker.SetBuildLimit(int64(w.Config.Build.Limit)) - // pull token from configuration if provided; wait if not - token := <-w.AuthToken + // pull registration token from configuration if provided; wait if not + token := <-w.RegisterToken // setup the vela client with the token w.VelaClient, err = setupClient(w.Config.Server, token) @@ -63,7 +63,7 @@ func (w *Worker) operate(ctx context.Context) error { // token has expired if w.VelaClient.Authentication.IsTokenAuthExpired() && len(w.Config.Server.Secret) == 0 { // wait on new registration token, return to check in attempt - token = <-w.AuthToken + token = <-w.RegisterToken // setup the vela client with the token w.VelaClient, err = setupClient(w.Config.Server, token) diff --git a/cmd/vela-worker/run.go b/cmd/vela-worker/run.go index 736d6db9..131822a0 100644 --- a/cmd/vela-worker/run.go +++ b/cmd/vela-worker/run.go @@ -137,7 +137,7 @@ func run(c *cli.Context) error { }, Executors: make(map[int]executor.Engine), - AuthToken: make(chan string, 1), + RegisterToken: make(chan string, 1), } // set the worker address if no flag was provided @@ -145,8 +145,9 @@ func run(c *cli.Context) error { w.Config.API.Address, _ = url.Parse(fmt.Sprintf("http://%s", hostname)) } + // if server secret is provided, use as register token on start up if len(c.String("server.secret")) > 0 { - w.AuthToken <- c.String("server.secret") + w.RegisterToken <- c.String("server.secret") } // validate the worker diff --git a/cmd/vela-worker/server.go b/cmd/vela-worker/server.go index c4dbca1f..7a13904a 100644 --- a/cmd/vela-worker/server.go +++ b/cmd/vela-worker/server.go @@ -33,7 +33,7 @@ func (w *Worker) server() (http.Handler, *tls.Config) { middleware.Executors(w.Executors), middleware.Server(w.Config.Server.Address), middleware.Logger(logrus.StandardLogger(), time.RFC3339, true), - middleware.AuthToken(w.AuthToken), + middleware.AuthToken(w.RegisterToken), ) // log a message indicating the start of serving traffic diff --git a/cmd/vela-worker/worker.go b/cmd/vela-worker/worker.go index bbdd0cd1..895fdd19 100644 --- a/cmd/vela-worker/worker.go +++ b/cmd/vela-worker/worker.go @@ -62,12 +62,12 @@ type ( // Worker represents all configuration and // system processes for the worker. Worker struct { - Config *Config - Executors map[int]executor.Engine - Queue queue.Service - Runtime runtime.Engine - VelaClient *vela.Client - AuthToken chan string - CheckedIn bool + Config *Config + Executors map[int]executor.Engine + Queue queue.Service + Runtime runtime.Engine + VelaClient *vela.Client + RegisterToken chan string + CheckedIn bool } ) From afcfbe4490b6faa9a239034f7cd83edb457d4347 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 28 Mar 2023 14:34:15 -0500 Subject: [PATCH 07/14] rename token channel and update api comments --- api/register.go | 24 ++++++------ cmd/vela-worker/server.go | 2 +- docker-compose.yml | 8 ++-- router/middleware/auth_token.go | 8 ++-- router/middleware/register_token_test.go | 48 ++++++++++++++++++++++++ router/middleware/server_test.go | 2 +- 6 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 router/middleware/register_token_test.go diff --git a/api/register.go b/api/register.go index 7b9c22de..1961b632 100644 --- a/api/register.go +++ b/api/register.go @@ -13,7 +13,7 @@ import ( // swagger:operation POST /register system Register // -// Register the worker with the Vela server +// Fill registration token channel in worker to continue operation // // --- // produces: @@ -23,28 +23,30 @@ import ( // - ApiKeyAuth: [] // responses: // '200': -// description: Successfully registered worker +// description: Successfully passed token to worker // schema: // type: string -// Health check the status of the application. +// Register will pass the token given in the request header to the register token +// channel of the worker. This will unblock operation if the worker has not been +// registered and the provided registration token is valid. func Register(c *gin.Context) { - // extract the auth token channel that was packed into gin context - v, ok := c.Get("auth-token") + // extract the register token channel that was packed into gin context + v, ok := c.Get("register-token") if !ok { c.JSON(http.StatusInternalServerError, "no auth token channel in the context") return } // make sure we configured the channel properly - authChannel, ok := v.(chan string) + rChan, ok := v.(chan string) if !ok { - c.JSON(http.StatusInternalServerError, "auth token channel in the context is the wrong type") + c.JSON(http.StatusInternalServerError, "register token channel in the context is the wrong type") return } // if auth token is present in the channel, deny registration - if len(authChannel) > 0 { + if len(rChan) > 0 { c.JSON(http.StatusOK, "worker already registered") return } @@ -57,9 +59,7 @@ func Register(c *gin.Context) { } // write registration token to auth token channel - authChannel <- token + rChan <- token - // somehow we need to make sure the registration worked - // maybe a second channel for registration results? - c.JSON(http.StatusOK, "successfully registered the worker") + c.JSON(http.StatusOK, "successfully passed token to worker") } diff --git a/cmd/vela-worker/server.go b/cmd/vela-worker/server.go index fe5cb925..7961fe73 100644 --- a/cmd/vela-worker/server.go +++ b/cmd/vela-worker/server.go @@ -33,7 +33,7 @@ func (w *Worker) server() (http.Handler, *tls.Config) { middleware.ServerAddress(w.Config.Server.Address), middleware.Executors(w.Executors), middleware.Logger(logrus.StandardLogger(), time.RFC3339, true), - middleware.AuthToken(w.RegisterToken), + middleware.RegisterToken(w.RegisterToken), ) // log a message indicating the start of serving traffic diff --git a/docker-compose.yml b/docker-compose.yml index 177b8f53..a39851ae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,8 @@ services: VELA_RUNTIME_PRIVILEGED_IMAGES: 'target/vela-docker' VELA_EXECUTOR_ENFORCE_TRUSTED_REPOS: 'true' VELA_SERVER_ADDR: 'http://server:8080' - VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' + # uncomment this line to use symmetric token (no registration) + # VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' WORKER_ADDR: 'http://worker:8080' WORKER_CHECK_IN: 2m restart: always @@ -50,7 +51,7 @@ services: # https://go-vela.github.io/docs/administration/server/ server: container_name: server - image: server:local + image: target/vela-server:latest networks: - vela environment: @@ -68,7 +69,8 @@ services: VELA_ADDR: 'http://localhost:8080' VELA_WEBUI_ADDR: 'http://localhost:8888' VELA_LOG_LEVEL: trace - VELA_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' + # uncomment this line to use symmetric token (no registration) + # VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' VELA_SERVER_PRIVATE_KEY: 'F534FF2A080E45F38E05DC70752E6787' VELA_USER_REFRESH_TOKEN_DURATION: 90m VELA_USER_ACCESS_TOKEN_DURATION: 60m diff --git a/router/middleware/auth_token.go b/router/middleware/auth_token.go index d37a100e..177fd3f2 100644 --- a/router/middleware/auth_token.go +++ b/router/middleware/auth_token.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. // // Use of this source code is governed by the LICENSE file in this repository. @@ -8,11 +8,11 @@ import ( "github.com/gin-gonic/gin" ) -// AuthToken is a middleware function that attaches the +// RegisterToken is a middleware function that attaches the // auth-token channel to the context of every http.Request. -func AuthToken(r chan string) gin.HandlerFunc { +func RegisterToken(r chan string) gin.HandlerFunc { return func(c *gin.Context) { - c.Set("auth-token", r) + c.Set("register-token", r) c.Next() } } diff --git a/router/middleware/register_token_test.go b/router/middleware/register_token_test.go new file mode 100644 index 00000000..afe8241d --- /dev/null +++ b/router/middleware/register_token_test.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package middleware + +import ( + "net/http" + "net/http/httptest" + "reflect" + "testing" + + "github.com/gin-gonic/gin" +) + +func TestMiddleware_RegisterToken(t *testing.T) { + // setup types + want := make(chan string, 1) + got := make(chan string, 1) + + want <- "foo" + + // setup context + gin.SetMode(gin.TestMode) + + resp := httptest.NewRecorder() + context, engine := gin.CreateTestContext(resp) + context.Request, _ = http.NewRequest(http.MethodGet, "/health", nil) + + // setup mock server + engine.Use(RegisterToken(want)) + engine.GET("/health", func(c *gin.Context) { + got = c.Value("register-token").(chan string) + + c.Status(http.StatusOK) + }) + + // run test + engine.ServeHTTP(context.Writer, context.Request) + + if resp.Code != http.StatusOK { + t.Errorf("RegisterToken returned %v, want %v", resp.Code, http.StatusOK) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("RegisterToken is %v, want foo", got) + } +} diff --git a/router/middleware/server_test.go b/router/middleware/server_test.go index 2deeb3d8..a1221287 100644 --- a/router/middleware/server_test.go +++ b/router/middleware/server_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. // // Use of this source code is governed by the LICENSE file in this repository. From ca12afc4c32eedb214a5d784c7966a42e20daa6f Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 28 Mar 2023 15:14:48 -0500 Subject: [PATCH 08/14] updating some comments and not loggin token --- api/register.go | 5 +++-- cmd/vela-worker/register.go | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/register.go b/api/register.go index 1961b632..6ea770b1 100644 --- a/api/register.go +++ b/api/register.go @@ -34,7 +34,7 @@ func Register(c *gin.Context) { // extract the register token channel that was packed into gin context v, ok := c.Get("register-token") if !ok { - c.JSON(http.StatusInternalServerError, "no auth token channel in the context") + c.JSON(http.StatusInternalServerError, "no register token channel in the context") return } @@ -45,7 +45,8 @@ func Register(c *gin.Context) { return } - // if auth token is present in the channel, deny registration + // if token is present in the channel, deny registration + // this will likely never happen as the channel is offloaded immediately if len(rChan) > 0 { c.JSON(http.StatusOK, "worker already registered") return diff --git a/cmd/vela-worker/register.go b/cmd/vela-worker/register.go index 7f7a86d4..e922c745 100644 --- a/cmd/vela-worker/register.go +++ b/cmd/vela-worker/register.go @@ -52,8 +52,6 @@ func (w *Worker) register(config *library.Worker) (bool, string, error) { return false, "", fmt.Errorf("unable to register worker %s with the server: %w", config.GetHostname(), err) } - logrus.Infof("register function TOKEN from add worker: %s", tkn) - // successfully added the worker so return nil return true, tkn.GetToken(), nil } From e2a0d16fec94bb4bc575676ebd47370ad50c4209 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 28 Mar 2023 15:26:24 -0500 Subject: [PATCH 09/14] fix docker compose --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index a39851ae..be674eff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -70,7 +70,7 @@ services: VELA_WEBUI_ADDR: 'http://localhost:8888' VELA_LOG_LEVEL: trace # uncomment this line to use symmetric token (no registration) - # VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' + # VELA_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' VELA_SERVER_PRIVATE_KEY: 'F534FF2A080E45F38E05DC70752E6787' VELA_USER_REFRESH_TOKEN_DURATION: 90m VELA_USER_ACCESS_TOKEN_DURATION: 60m From 792e3e6a576843cf6ee492e002bc5014dd985fc4 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Tue, 28 Mar 2023 15:30:40 -0500 Subject: [PATCH 10/14] fix local replace --- go.mod | 2 -- go.sum | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d5e8ff8f..cc15e3ba 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,6 @@ module github.com/go-vela/worker go 1.19 -replace github.com/go-vela/sdk-go => ../sdk-go - require ( github.com/Masterminds/semver/v3 v3.2.0 github.com/docker/distribution v2.8.1+incompatible diff --git a/go.sum b/go.sum index a4d122fa..6d05d5c5 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-vela/sdk-go v0.18.2-0.20230327141933-e8d38c73b1bb h1:JXEolOu+HFktExoDFcGYIdWS9LfPAQnQMIB4Rm48WS0= +github.com/go-vela/sdk-go v0.18.2-0.20230327141933-e8d38c73b1bb/go.mod h1:N8qFPxB0RsHrSYr01GVwgOOowtSfhvjXtJ1cRBaeTc4= github.com/go-vela/server v0.18.2-0.20230324155739-73f83fcfd004 h1:yJis1sso5c0ZoeZLfZ/lYsjfxU7H9cYP/VJXssRxDa8= github.com/go-vela/server v0.18.2-0.20230324155739-73f83fcfd004/go.mod h1:b+7XeGHO4ynIinY9mpWb6ye9psdwHpsAqMWy5oC+zJ0= github.com/go-vela/types v0.18.2-0.20230321015315-6c723879639c h1:lnCL1knUGvgZQG4YBHSs/CZnxNBfqFUBlGhyq9LO9uk= From 6785fac4460c7d152ff67b5d86de02b8bc981dba Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 29 Mar 2023 12:50:49 -0500 Subject: [PATCH 11/14] token expiration func has two return vals --- cmd/vela-worker/operate.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cmd/vela-worker/operate.go b/cmd/vela-worker/operate.go index fbb1e8f5..8a8909a5 100644 --- a/cmd/vela-worker/operate.go +++ b/cmd/vela-worker/operate.go @@ -60,8 +60,15 @@ func (w *Worker) operate(ctx context.Context) error { w.CheckedIn, token, err = w.checkIn(registryWorker) // check in failed if err != nil { + // check if token is expired + expired, err := w.VelaClient.Authentication.IsTokenAuthExpired() + if err != nil { + logrus.Error("unable to check token expiration") + return err + } + // token has expired - if w.VelaClient.Authentication.IsTokenAuthExpired() && len(w.Config.Server.Secret) == 0 { + if expired && len(w.Config.Server.Secret) == 0 { // wait on new registration token, return to check in attempt token = <-w.RegisterToken From cf5a4bb6907feec9fb87e58f8fa4b12567e602d9 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 29 Mar 2023 14:23:20 -0500 Subject: [PATCH 12/14] docker compose no register --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index be674eff..8fde3ef5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,8 +30,8 @@ services: VELA_RUNTIME_PRIVILEGED_IMAGES: 'target/vela-docker' VELA_EXECUTOR_ENFORCE_TRUSTED_REPOS: 'true' VELA_SERVER_ADDR: 'http://server:8080' - # uncomment this line to use symmetric token (no registration) - # VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' + # comment the line below to use registration flow + VELA_SERVER_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' WORKER_ADDR: 'http://worker:8080' WORKER_CHECK_IN: 2m restart: always @@ -69,8 +69,8 @@ services: VELA_ADDR: 'http://localhost:8080' VELA_WEBUI_ADDR: 'http://localhost:8888' VELA_LOG_LEVEL: trace - # uncomment this line to use symmetric token (no registration) - # VELA_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' + # comment the line below to use registration flow + VELA_SECRET: 'zB7mrKDTZqNeNTD8z47yG4DHywspAh' VELA_SERVER_PRIVATE_KEY: 'F534FF2A080E45F38E05DC70752E6787' VELA_USER_REFRESH_TOKEN_DURATION: 90m VELA_USER_ACCESS_TOKEN_DURATION: 60m From e30d1052254791554ccd5e1ad69f115992e5aa65 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 29 Mar 2023 14:36:30 -0500 Subject: [PATCH 13/14] name register token middleware file correctly --- router/middleware/{auth_token.go => register_token.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename router/middleware/{auth_token.go => register_token.go} (100%) diff --git a/router/middleware/auth_token.go b/router/middleware/register_token.go similarity index 100% rename from router/middleware/auth_token.go rename to router/middleware/register_token.go From b37660a9300f308ada5f12efd5c80f2a61b395a1 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Wed, 29 Mar 2023 15:02:27 -0500 Subject: [PATCH 14/14] update swagger for register --- api/register.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/register.go b/api/register.go index 6ea770b1..33829dff 100644 --- a/api/register.go +++ b/api/register.go @@ -26,6 +26,10 @@ import ( // description: Successfully passed token to worker // schema: // type: string +// '500': +// description: Unable to pass token to worker +// schema: +// "$ref": "#/definitions/Error" // Register will pass the token given in the request header to the register token // channel of the worker. This will unblock operation if the worker has not been