Define a metadata subrouter
Define a metadata subrouter and move the token endpoint there. We may end up needing multiple endpoints for various purposes in the future. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
a078645ab2
commit
0869073906
19 changed files with 63 additions and 30 deletions
|
|
@ -41,13 +41,16 @@ func NewAPIRouter(han *controllers.APIController, logWriter io.Writer, authMiddl
|
|||
firstRunRouter := apiSubRouter.PathPrefix("/first-run").Subrouter()
|
||||
firstRunRouter.Handle("/", log(logWriter, http.HandlerFunc(han.FirstRunHandler))).Methods("POST", "OPTIONS")
|
||||
|
||||
// Instance callback
|
||||
// Instance URLs
|
||||
callbackRouter := apiSubRouter.PathPrefix("/callbacks").Subrouter()
|
||||
callbackRouter.Handle("/status/", log(logWriter, http.HandlerFunc(han.InstanceStatusMessageHandler))).Methods("POST", "OPTIONS")
|
||||
callbackRouter.Handle("/status", log(logWriter, http.HandlerFunc(han.InstanceStatusMessageHandler))).Methods("POST", "OPTIONS")
|
||||
callbackRouter.Handle("/token/", log(logWriter, http.HandlerFunc(han.InstanceGithubRegistrationTokenHandler))).Methods("GET", "OPTIONS")
|
||||
callbackRouter.Handle("/token", log(logWriter, http.HandlerFunc(han.InstanceGithubRegistrationTokenHandler))).Methods("GET", "OPTIONS")
|
||||
callbackRouter.Use(instanceMiddleware.Middleware)
|
||||
|
||||
metadataRouter := apiSubRouter.PathPrefix("/metadata").Subrouter()
|
||||
metadataRouter.Handle("/token/", log(logWriter, http.HandlerFunc(han.InstanceGithubRegistrationTokenHandler))).Methods("GET", "OPTIONS")
|
||||
metadataRouter.Handle("/token", log(logWriter, http.HandlerFunc(han.InstanceGithubRegistrationTokenHandler))).Methods("GET", "OPTIONS")
|
||||
metadataRouter.Use(instanceMiddleware.Middleware)
|
||||
// Login
|
||||
authRouter := apiSubRouter.PathPrefix("/auth").Subrouter()
|
||||
authRouter.Handle("/{login:login\\/?}", log(logWriter, http.HandlerFunc(han.LoginHandler))).Methods("POST", "OPTIONS")
|
||||
|
|
|
|||
|
|
@ -23,25 +23,25 @@ import (
|
|||
|
||||
var CloudConfigTemplate = `#!/bin/bash
|
||||
|
||||
set -ex
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
CALLBACK_URL="{{ .CallbackURL }}"
|
||||
TOKEN_URL="{{ .TokenURL }}"
|
||||
METADATA_URL="{{ .MetadataURL }}"
|
||||
BEARER_TOKEN="{{ .CallbackToken }}"
|
||||
GITHUB_TOKEN="{{ .GithubToken }}"
|
||||
|
||||
if [ -z "$GITHUB_TOKEN" ];then
|
||||
if [ -z "$TOKEN_URL" ];then
|
||||
echo "no token is available and TOKEN_URL is not set"
|
||||
if [ -z "$METADATA_URL" ];then
|
||||
echo "no token is available and METADATA_URL is not set"
|
||||
exit 1
|
||||
fi
|
||||
GITHUB_TOKEN=$(curl -s -X GET -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "${TOKEN_URL}")
|
||||
GITHUB_TOKEN=$(curl --fail -s -X GET -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "${METADATA_URL}/token")
|
||||
fi
|
||||
|
||||
function call() {
|
||||
PAYLOAD="$1"
|
||||
curl -s -X POST -d "${PAYLOAD}" -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "${CALLBACK_URL}" || echo "failed to call home: exit code ($?)"
|
||||
curl --fail -s -X POST -d "${PAYLOAD}" -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "${CALLBACK_URL}" || echo "failed to call home: exit code ($?)"
|
||||
}
|
||||
|
||||
function sendStatus() {
|
||||
|
|
@ -107,7 +107,7 @@ type InstallRunnerParams struct {
|
|||
RunnerGroup string
|
||||
RepoURL string
|
||||
GithubToken string
|
||||
TokenURL string
|
||||
MetadataURL string
|
||||
RunnerName string
|
||||
RunnerLabels string
|
||||
CallbackURL string
|
||||
|
|
|
|||
|
|
@ -173,8 +173,9 @@ type Default struct {
|
|||
ConfigDir string `toml:"config_dir,omitempty" json:"config-dir,omitempty"`
|
||||
// CallbackURL is the URL where the instances can send back status reports.
|
||||
CallbackURL string `toml:"callback_url" json:"callback-url"`
|
||||
// TokenURL is the URL where instances can fetch a github runner registration token.
|
||||
TokenURL string `toml:"token_url" json:"token-url"`
|
||||
// MetadataURL is the URL where instances can fetch information they may need
|
||||
// to set themselves up.
|
||||
MetadataURL string `toml:"metadata_url" json:"metadata-url"`
|
||||
// LogFile is the location of the log file.
|
||||
LogFile string `toml:"log_file,omitempty" json:"log-file"`
|
||||
EnableLogStreamer bool `toml:"enable_log_streamer"`
|
||||
|
|
@ -190,9 +191,9 @@ func (d *Default) Validate() error {
|
|||
return errors.Wrap(err, "validating callback_url")
|
||||
}
|
||||
|
||||
if d.TokenURL != "" {
|
||||
if _, err := url.Parse(d.TokenURL); err != nil {
|
||||
return errors.Wrap(err, "validating token_url")
|
||||
if d.MetadataURL != "" {
|
||||
if _, err := url.Parse(d.MetadataURL); err != nil {
|
||||
return errors.Wrap(err, "validating metadata_url")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
CALLBACK_URL="GARM_CALLBACK_URL"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
CALLBACK_URL="GARM_CALLBACK_URL"
|
||||
|
|
|
|||
|
|
@ -154,9 +154,9 @@ function tempDownloadToken() {
|
|||
}
|
||||
|
||||
function runnerTokenURL() {
|
||||
TOKEN_URL=$(echo "$INPUT" | jq -c -r '."token-url"')
|
||||
checkValNotNull "${TOKEN_URL}" "token-url" || return $?
|
||||
echo "${TOKEN_URL}"
|
||||
METADATA_URL=$(echo "$INPUT" | jq -c -r '."metadata-url"')
|
||||
checkValNotNull "${METADATA_URL}" "metadata-url" || return $?
|
||||
echo "${METADATA_URL}/token/"
|
||||
}
|
||||
|
||||
function downloadFilename() {
|
||||
|
|
@ -194,7 +194,7 @@ function repoURL() {
|
|||
function getRegistrationTokenFromAPI() {
|
||||
TOKEN_URL=$(runnerTokenURL)
|
||||
BEARER_TOKEN=$(callbackToken)
|
||||
TOKEN=$(curl -s -X GET -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "${TOKEN_URL}")
|
||||
TOKEN=$(curl --fail -s -X GET -H 'Accept: application/json' -H "Authorization: Bearer ${BEARER_TOKEN}" "${TOKEN_URL}")
|
||||
checkValNotNull "${TOKEN}" "repo_url" || return $?
|
||||
echo "${TOKEN}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ func (s *sqlDatabase) CreateInstance(ctx context.Context, poolID string, param p
|
|||
OSType: param.OSType,
|
||||
OSArch: param.OSArch,
|
||||
CallbackURL: param.CallbackURL,
|
||||
MetadataURL: param.MetadataURL,
|
||||
GithubRegistrationToken: ghToken,
|
||||
}
|
||||
q := s.conn.Create(&newInstance)
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ type Instance struct {
|
|||
Status common.InstanceStatus
|
||||
RunnerStatus common.RunnerStatus
|
||||
CallbackURL string
|
||||
MetadataURL string
|
||||
ProviderFault []byte `gorm:"type:longblob"`
|
||||
CreateAttempt int
|
||||
GithubRegistrationToken []byte `gorm:"type:longblob"`
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ func (s *sqlDatabase) sqlToParamsInstance(instance Instance) params.Instance {
|
|||
RunnerStatus: instance.RunnerStatus,
|
||||
PoolID: instance.PoolID.String(),
|
||||
CallbackURL: instance.CallbackURL,
|
||||
MetadataURL: instance.MetadataURL,
|
||||
StatusMessages: []params.StatusMessage{},
|
||||
CreateAttempt: instance.CreateAttempt,
|
||||
UpdatedAt: instance.UpdatedAt,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ type Instance struct {
|
|||
|
||||
// Do not serialize sensitive info.
|
||||
CallbackURL string `json:"-"`
|
||||
MetadataURL string `json:"-"`
|
||||
CreateAttempt int `json:"-"`
|
||||
GithubRegistrationToken []byte `json:"-"`
|
||||
}
|
||||
|
|
@ -92,8 +93,8 @@ type BootstrapInstance struct {
|
|||
// CallbackUrl is the URL where the instance can send a post, signaling
|
||||
// progress or status.
|
||||
CallbackURL string `json:"callback-url"`
|
||||
// TokenURL is the URL where instances can fetch github runner registratin tokens.
|
||||
TokenURL string `json:"token-url"`
|
||||
// MetadataURL is the URL where instances can fetch information needed to set themselves up.
|
||||
MetadataURL string `json:"metadata-url"`
|
||||
// InstanceToken is the token that needs to be set by the instance in the headers
|
||||
// in order to send updated back to the garm via CallbackURL.
|
||||
InstanceToken string `json:"instance-token"`
|
||||
|
|
@ -147,6 +148,7 @@ type Internal struct {
|
|||
OAuth2Token string `json:"oauth2"`
|
||||
ControllerID string `json:"controller_id"`
|
||||
InstanceCallbackURL string `json:"instance_callback_url"`
|
||||
InstanceMetadataURL string `json:"instance_metadata_url"`
|
||||
JWTSecret string `json:"jwt_secret"`
|
||||
// GithubCredentialsDetails contains all info about the credentials, except the
|
||||
// token, which is added above.
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ type CreateInstanceParams struct {
|
|||
Status common.InstanceStatus
|
||||
RunnerStatus common.RunnerStatus
|
||||
CallbackURL string
|
||||
MetadataURL string
|
||||
CreateAttempt int `json:"-"`
|
||||
GithubRegistrationToken []byte `json:"-"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
// test that we implement PoolManager
|
||||
var _ poolHelper = &organization{}
|
||||
var _ poolHelper = &enterprise{}
|
||||
|
||||
func NewEnterprisePoolManager(ctx context.Context, cfg params.Enterprise, cfgInternal params.Internal, providers map[string]common.Provider, store dbCommon.Store) (common.PoolManager, error) {
|
||||
ghc, ghEnterpriseClient, err := util.GithubClient(ctx, cfgInternal.OAuth2Token, cfgInternal.GithubCredentialsDetails)
|
||||
|
|
@ -179,6 +179,10 @@ func (r *enterprise) GetCallbackURL() string {
|
|||
return r.cfgInternal.InstanceCallbackURL
|
||||
}
|
||||
|
||||
func (r *enterprise) GetMetadataURL() string {
|
||||
return r.cfgInternal.InstanceMetadataURL
|
||||
}
|
||||
|
||||
func (r *enterprise) FindPoolByTags(labels []string) (params.Pool, error) {
|
||||
pool, err := r.store.FindEnterprisePoolByTags(r.ctx, r.id, labels)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ type poolHelper interface {
|
|||
JwtToken() string
|
||||
String() string
|
||||
GetCallbackURL() string
|
||||
GetMetadataURL() string
|
||||
FindPoolByTags(labels []string) (params.Pool, error)
|
||||
GetPoolByID(poolID string) (params.Pool, error)
|
||||
ValidateOwner(job params.WorkflowJob) error
|
||||
|
|
|
|||
|
|
@ -192,6 +192,10 @@ func (r *organization) GetCallbackURL() string {
|
|||
return r.cfgInternal.InstanceCallbackURL
|
||||
}
|
||||
|
||||
func (r *organization) GetMetadataURL() string {
|
||||
return r.cfgInternal.InstanceMetadataURL
|
||||
}
|
||||
|
||||
func (r *organization) FindPoolByTags(labels []string) (params.Pool, error) {
|
||||
pool, err := r.store.FindOrganizationPoolByTags(r.ctx, r.id, labels)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -362,6 +362,7 @@ func (r *basePoolManager) AddRunner(ctx context.Context, poolID string) error {
|
|||
OSType: pool.OSType,
|
||||
GithubRegistrationToken: []byte(tk),
|
||||
CallbackURL: r.helper.GetCallbackURL(),
|
||||
MetadataURL: r.helper.GetMetadataURL(),
|
||||
CreateAttempt: 1,
|
||||
}
|
||||
|
||||
|
|
@ -551,6 +552,7 @@ func (r *basePoolManager) addInstanceToProvider(instance params.Instance) error
|
|||
Tools: r.tools,
|
||||
RepoURL: r.helper.GithubURL(),
|
||||
GithubRunnerAccessToken: string(instance.GithubRegistrationToken),
|
||||
MetadataURL: instance.MetadataURL,
|
||||
CallbackURL: instance.CallbackURL,
|
||||
InstanceToken: jwtToken,
|
||||
OSArch: pool.OSArch,
|
||||
|
|
|
|||
|
|
@ -193,6 +193,10 @@ func (r *repository) GetCallbackURL() string {
|
|||
return r.cfgInternal.InstanceCallbackURL
|
||||
}
|
||||
|
||||
func (r *repository) GetMetadataURL() string {
|
||||
return r.cfgInternal.InstanceMetadataURL
|
||||
}
|
||||
|
||||
func (r *repository) FindPoolByTags(labels []string) (params.Pool, error) {
|
||||
pool, err := r.store.FindRepositoryPoolByTags(r.ctx, r.id, labels)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ func (p *poolManagerCtrl) getInternalConfig(credsName string) (params.Internal,
|
|||
OAuth2Token: creds.OAuth2Token,
|
||||
ControllerID: p.controllerID,
|
||||
InstanceCallbackURL: p.config.Default.CallbackURL,
|
||||
InstanceMetadataURL: p.config.Default.MetadataURL,
|
||||
JWTSecret: p.config.JWTAuth.Secret,
|
||||
GithubCredentialsDetails: params.GithubCredentials{
|
||||
Name: creds.Name,
|
||||
|
|
|
|||
11
testdata/config.toml
vendored
11
testdata/config.toml
vendored
|
|
@ -5,13 +5,16 @@
|
|||
# runner status in garm.
|
||||
callback_url = "https://garm.example.com/api/v1/callbacks/status"
|
||||
|
||||
# This URL is used to retrieve a github runner registration token for a particular
|
||||
# instance. Once the instance transitions to "installed", this endpoint should
|
||||
# no longer be accessible.
|
||||
token_url = "https://garm.example.com/api/v1/callbacks/tokens"
|
||||
# This URL is used by instances to retrieve information they need to set themselves
|
||||
# up. Access to this URL is granted using the same JWT token used to send back
|
||||
# status updates. Once the instance transitions to "installed" or "failed" state,
|
||||
# access to both the status and metadata endpoints is disabled.
|
||||
metadata_url = "https://garm.example.com/api/v1/metadata"
|
||||
|
||||
# This folder is defined here for future use. Right now, we create a SSH
|
||||
# public/private key-pair.
|
||||
config_dir = "/etc/garm"
|
||||
|
||||
# Uncomment this line if you'd like to log to a file instead of standard output.
|
||||
# log_file = "/tmp/runner-manager.log"
|
||||
|
||||
|
|
|
|||
|
|
@ -212,8 +212,7 @@ func GetCloudConfig(bootstrapParams params.BootstrapInstance, tools github.Runne
|
|||
FileName: *tools.Filename,
|
||||
DownloadURL: *tools.DownloadURL,
|
||||
TempDownloadToken: tempToken,
|
||||
GithubToken: bootstrapParams.GithubRunnerAccessToken,
|
||||
TokenURL: bootstrapParams.TokenURL,
|
||||
MetadataURL: bootstrapParams.MetadataURL,
|
||||
RunnerUsername: config.DefaultUser,
|
||||
RunnerGroup: config.DefaultUser,
|
||||
RepoURL: bootstrapParams.RepoURL,
|
||||
|
|
@ -223,6 +222,11 @@ func GetCloudConfig(bootstrapParams params.BootstrapInstance, tools github.Runne
|
|||
CallbackToken: bootstrapParams.InstanceToken,
|
||||
}
|
||||
|
||||
if bootstrapParams.MetadataURL == "" {
|
||||
// Token URL is not set. Add the GH runner registration token to userdata.
|
||||
installRunnerParams.GithubToken = bootstrapParams.GithubRunnerAccessToken
|
||||
}
|
||||
|
||||
installScript, err := cloudconfig.InstallRunnerScript(installRunnerParams)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "generating script")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue