Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add jit config #163

Merged
merged 14 commits into from
Sep 24, 2023
72 changes: 72 additions & 0 deletions apiserver/controllers/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ package controllers

import (
"encoding/json"
"fmt"
"log"
"net/http"

"github.com/cloudbase/garm/apiserver/params"
"github.com/gorilla/mux"
)

func (a *APIController) InstanceGithubRegistrationTokenHandler(w http.ResponseWriter, r *http.Request) {
Expand All @@ -36,6 +40,74 @@ func (a *APIController) InstanceGithubRegistrationTokenHandler(w http.ResponseWr
}
}

func (a *APIController) JITCredentialsFileHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)
fileName, ok := vars["fileName"]
if !ok {
w.WriteHeader(http.StatusNotFound)
if err := json.NewEncoder(w).Encode(params.APIErrorResponse{
Error: "Not Found",
Details: "Not Found",
}); err != nil {
log.Printf("failed to encode response: %q", err)
}
return
}

dotFileName := fmt.Sprintf(".%s", fileName)

data, err := a.r.GetJITConfigFile(ctx, dotFileName)
if err != nil {
log.Printf("getting JIT config file: %s", err)
handleError(w, err)
return
}

// Note the leading dot in the filename
name := fmt.Sprintf("attachment; filename=%s", dotFileName)
w.Header().Set("Content-Disposition", name)
w.Header().Set("Content-Type", "octet-stream")
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
w.WriteHeader(http.StatusOK)
if _, err := w.Write(data); err != nil {
log.Printf("failed to encode response: %q", err)
}
}

func (a *APIController) SystemdServiceNameHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

serviceName, err := a.r.GetRunnerServiceName(ctx)
if err != nil {
handleError(w, err)
return
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte(serviceName)); err != nil {
log.Printf("failed to encode response: %q", err)
}
}

func (a *APIController) SystemdUnitFileHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
runAsUser := r.URL.Query().Get("runAsUser")

data, err := a.r.GenerateSystemdUnitFile(ctx, runAsUser)
if err != nil {
handleError(w, err)
return
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
if _, err := w.Write(data); err != nil {
log.Printf("failed to encode response: %q", err)
}
}

func (a *APIController) RootCertificateBundleHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

Expand Down
8 changes: 8 additions & 0 deletions apiserver/routers/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ func NewAPIRouter(han *controllers.APIController, logWriter io.Writer, authMiddl
// Registration token
metadataRouter.Handle("/runner-registration-token/", http.HandlerFunc(han.InstanceGithubRegistrationTokenHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/runner-registration-token", http.HandlerFunc(han.InstanceGithubRegistrationTokenHandler)).Methods("GET", "OPTIONS")
// JIT credential files
metadataRouter.Handle("/credentials/{fileName}/", http.HandlerFunc(han.JITCredentialsFileHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/credentials/{fileName}", http.HandlerFunc(han.JITCredentialsFileHandler)).Methods("GET", "OPTIONS")
// Systemd files
metadataRouter.Handle("/system/service-name/", http.HandlerFunc(han.SystemdServiceNameHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/system/service-name", http.HandlerFunc(han.SystemdServiceNameHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/systemd/unit-file/", http.HandlerFunc(han.SystemdUnitFileHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/systemd/unit-file", http.HandlerFunc(han.SystemdUnitFileHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/system/cert-bundle/", http.HandlerFunc(han.RootCertificateBundleHandler)).Methods("GET", "OPTIONS")
metadataRouter.Handle("/system/cert-bundle", http.HandlerFunc(han.RootCertificateBundleHandler)).Methods("GET", "OPTIONS")

Expand Down
14 changes: 14 additions & 0 deletions auth/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
instanceEntityKey contextFlags = "entity"
instanceRunnerStatus contextFlags = "status"
instanceTokenFetched contextFlags = "tokenFetched"
instanceHasJITConfig contextFlags = "hasJITConfig"
instanceParams contextFlags = "instanceParams"
)

Expand Down Expand Up @@ -66,6 +67,18 @@ func InstanceTokenFetched(ctx context.Context) bool {
return elem.(bool)
}

func SetInstanceHasJITConfig(ctx context.Context, cfg map[string]string) context.Context {
return context.WithValue(ctx, instanceHasJITConfig, len(cfg) > 0)
}

func InstanceHasJITConfig(ctx context.Context) bool {
elem := ctx.Value(instanceHasJITConfig)
if elem == nil {
return false
}
return elem.(bool)
}

func SetInstanceParams(ctx context.Context, instance params.Instance) context.Context {
return context.WithValue(ctx, instanceParams, instance)
}
Expand Down Expand Up @@ -149,6 +162,7 @@ func PopulateInstanceContext(ctx context.Context, instance params.Instance) cont
ctx = SetInstancePoolID(ctx, instance.PoolID)
ctx = SetInstanceRunnerStatus(ctx, instance.RunnerStatus)
ctx = SetInstanceTokenFetched(ctx, instance.TokenFetched)
ctx = SetInstanceHasJITConfig(ctx, instance.JitConfiguration)
ctx = SetInstanceParams(ctx, instance)
return ctx
}
Expand Down
38 changes: 38 additions & 0 deletions database/sql/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"encoding/json"

runnerErrors "github.com/cloudbase/garm-provider-common/errors"
"github.com/cloudbase/garm-provider-common/util"
"github.com/cloudbase/garm/params"

"github.com/google/uuid"
Expand All @@ -28,6 +29,25 @@ import (
"gorm.io/gorm/clause"
)

func (s *sqlDatabase) marshalAndSeal(data interface{}) ([]byte, error) {
enc, err := json.Marshal(data)
if err != nil {
return nil, errors.Wrap(err, "marshalling data")
}
return util.Seal(enc, []byte(s.cfg.Passphrase))
}

func (s *sqlDatabase) unsealAndUnmarshal(data []byte, target interface{}) error {
decrypted, err := util.Unseal(data, []byte(s.cfg.Passphrase))
if err != nil {
return errors.Wrap(err, "decrypting data")
}
if err := json.Unmarshal(decrypted, target); err != nil {
return errors.Wrap(err, "unmarshalling data")
}
return nil
}

func (s *sqlDatabase) CreateInstance(ctx context.Context, poolID string, param params.CreateInstanceParams) (params.Instance, error) {
pool, err := s.getPoolByID(ctx, poolID)
if err != nil {
Expand All @@ -42,6 +62,14 @@ func (s *sqlDatabase) CreateInstance(ctx context.Context, poolID string, param p
}
}

var secret []byte
if len(param.JitConfiguration) > 0 {
secret, err = s.marshalAndSeal(param.JitConfiguration)
if err != nil {
return params.Instance{}, errors.Wrap(err, "marshalling jit config")
}
}

newInstance := Instance{
Pool: pool,
Name: param.Name,
Expand All @@ -52,7 +80,9 @@ func (s *sqlDatabase) CreateInstance(ctx context.Context, poolID string, param p
CallbackURL: param.CallbackURL,
MetadataURL: param.MetadataURL,
GitHubRunnerGroup: param.GitHubRunnerGroup,
JitConfiguration: secret,
AditionalLabels: labels,
AgentID: param.AgentID,
}
q := s.conn.Create(&newInstance)
if q.Error != nil {
Expand Down Expand Up @@ -235,6 +265,14 @@ func (s *sqlDatabase) UpdateInstance(ctx context.Context, instanceID string, par
instance.TokenFetched = *param.TokenFetched
}

if param.JitConfiguration != nil {
secret, err := s.marshalAndSeal(param.JitConfiguration)
if err != nil {
return params.Instance{}, errors.Wrap(err, "marshalling jit config")
}
instance.JitConfiguration = secret
}

instance.ProviderFault = param.ProviderFault

q := s.conn.Save(&instance)
Expand Down
1 change: 1 addition & 0 deletions database/sql/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ type Instance struct {
ProviderFault []byte `gorm:"type:longblob"`
CreateAttempt int
TokenFetched bool
JitConfiguration []byte `gorm:"type:longblob"`
GitHubRunnerGroup string
AditionalLabels datatypes.JSON

Expand Down
7 changes: 7 additions & 0 deletions database/sql/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ func (s *sqlDatabase) sqlToParamsInstance(instance Instance) (params.Instance, e
}
}

var jitConfig map[string]string
if len(instance.JitConfiguration) > 0 {
if err := s.unsealAndUnmarshal(instance.JitConfiguration, &jitConfig); err != nil {
return params.Instance{}, errors.Wrap(err, "unmarshalling jit configuration")
}
}
ret := params.Instance{
ID: instance.ID.String(),
ProviderID: id,
Expand All @@ -59,6 +65,7 @@ func (s *sqlDatabase) sqlToParamsInstance(instance Instance) (params.Instance, e
CreateAttempt: instance.CreateAttempt,
UpdatedAt: instance.UpdatedAt,
TokenFetched: instance.TokenFetched,
JitConfiguration: jitConfig,
GitHubRunnerGroup: instance.GitHubRunnerGroup,
AditionalLabels: labels,
}
Expand Down
11 changes: 6 additions & 5 deletions params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,12 @@ type Instance struct {
GitHubRunnerGroup string `json:"github-runner-group"`

// Do not serialize sensitive info.
CallbackURL string `json:"-"`
MetadataURL string `json:"-"`
CreateAttempt int `json:"-"`
TokenFetched bool `json:"-"`
AditionalLabels []string `json:"-"`
CallbackURL string `json:"-"`
MetadataURL string `json:"-"`
CreateAttempt int `json:"-"`
TokenFetched bool `json:"-"`
AditionalLabels []string `json:"-"`
JitConfiguration map[string]string `json:"-"`
}

func (i Instance) GetName() string {
Expand Down
17 changes: 10 additions & 7 deletions params/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ type CreateInstanceParams struct {
// GithubRunnerGroup is the github runner group to which the runner belongs.
// The runner group must be created by someone with access to the enterprise.
GitHubRunnerGroup string
CreateAttempt int `json:"-"`
CreateAttempt int `json:"-"`
AgentID int64 `json:"-"`
AditionalLabels []string
JitConfiguration map[string]string
}

type CreatePoolParams struct {
Expand Down Expand Up @@ -198,12 +200,13 @@ type UpdateInstanceParams struct {
// for this instance.
Addresses []commonParams.Address `json:"addresses,omitempty"`
// Status is the status of the instance inside the provider (eg: running, stopped, etc)
Status commonParams.InstanceStatus `json:"status,omitempty"`
RunnerStatus RunnerStatus `json:"runner_status,omitempty"`
ProviderFault []byte `json:"provider_fault,omitempty"`
AgentID int64 `json:"-"`
CreateAttempt int `json:"-"`
TokenFetched *bool `json:"-"`
Status commonParams.InstanceStatus `json:"status,omitempty"`
RunnerStatus RunnerStatus `json:"runner_status,omitempty"`
ProviderFault []byte `json:"provider_fault,omitempty"`
AgentID int64 `json:"-"`
CreateAttempt int `json:"-"`
TokenFetched *bool `json:"-"`
JitConfiguration map[string]string `json:"-"`
}

type UpdateUserParams struct {
Expand Down
Loading