diff --git a/helper/github/client.go b/helper/github/client.go index 348b092..67d4aaf 100644 --- a/helper/github/client.go +++ b/helper/github/client.go @@ -1,6 +1,7 @@ package github import ( + "crypto/tls" "net/http" "github.com/bradleyfalzon/ghinstallation" @@ -10,26 +11,62 @@ import ( // Client is a wrapper around GitHub client that supports GitHub App authentication for multiple installations. type Client struct { *github.Client - - transport *ghinstallation.AppsTransport + transport *ghinstallation.AppsTransport + skipTLSVerify bool } -// NewClient creates a new Client. -func NewClient(appID int64, appPrivateKey string) (*Client, error) { - transport, err := ghinstallation.NewAppsTransport(http.DefaultTransport, appID, []byte(appPrivateKey)) +// NewClient creates a new GitHub App client that supports both GitHub.com and GitHub Enterprise API URLs. +// githubEnterpriseApiUrl should be a full API base URL (e.g. https://api.githubenterprise.example.com/). +func NewClient(appID int64, appPrivateKey string, githubEnterpriseApiUrl string, skipTLSVerify bool) (*Client, error) { + baseTransport := http.DefaultTransport.(*http.Transport).Clone() + if skipTLSVerify { + if baseTransport.TLSClientConfig == nil { + baseTransport.TLSClientConfig = &tls.Config{} + } + baseTransport.TLSClientConfig.InsecureSkipVerify = true + // Prefer TLS 1.2+ even when skipping verification. + baseTransport.TLSClientConfig.MinVersion = tls.VersionTLS12 + } + + transport, err := ghinstallation.NewAppsTransport(baseTransport, appID, []byte(appPrivateKey)) if err != nil { return nil, err } - client := &Client{ - Client: github.NewClient(&http.Client{Transport: transport}), - transport: transport, + httpClient := &http.Client{Transport: transport} + ghClient := github.NewClient(httpClient) + + if githubEnterpriseApiUrl != "" { + ghClient, err = ghClient.WithEnterpriseURLs(githubEnterpriseApiUrl, "") + if err != nil { + return nil, err + } + + transport.BaseURL = githubEnterpriseApiUrl } - return client, nil + return &Client{ + Client: ghClient, + transport: transport, + skipTLSVerify: skipTLSVerify, + }, nil } -// Installation returns a new GitHub client for the given installation ID. func (c *Client) Installation(installationID int64) *github.Client { - return github.NewClient(&http.Client{Transport: ghinstallation.NewFromAppsTransport(c.transport, installationID)}) + installationTransport := ghinstallation.NewFromAppsTransport(c.transport, installationID) + httpClient := &http.Client{Transport: installationTransport} + + installationClient := github.NewClient(httpClient) + + // Detect if we're using GitHub Enterprise (since DefaultBaseURL is no longer exported) + if c.Client.BaseURL != nil && c.Client.BaseURL.String() != "https://api.github.com/" { + if enterpriseClient, err := installationClient.WithEnterpriseURLs( + c.Client.BaseURL.String(), + c.Client.UploadURL.String(), + ); err == nil { + return enterpriseClient + } + } + + return installationClient } diff --git a/helper/github/client_test.go b/helper/github/client_test.go index 3e77d69..978756d 100644 --- a/helper/github/client_test.go +++ b/helper/github/client_test.go @@ -7,19 +7,67 @@ import ( "github.com/stretchr/testify/assert" ) -func TestNewClient_Success(t *testing.T) { +func TestNewClient_Success_GitHubCom(t *testing.T) { key, err := os.ReadFile("testdata/test.key") if err != nil { t.Fatal(err) } - client, err := NewClient(12345, string(key)) + client, err := NewClient(12345, string(key), "", false) assert.NoError(t, err) assert.NotNil(t, client) + assert.NotNil(t, client.Client) + assert.NotNil(t, client.transport) + + // Should default to GitHub.com + assert.Contains(t, client.Client.BaseURL.String(), "https://api.github.com/") +} + +func TestNewClient_Success_GitHubCom_TLS(t *testing.T) { + key, err := os.ReadFile("testdata/test.key") + if err != nil { + t.Fatal(err) + } + + client, err := NewClient(12345, string(key), "", true) + assert.NoError(t, err) + assert.NotNil(t, client) +} + +func TestNewClient_Success_Enterprise(t *testing.T) { + key, err := os.ReadFile("testdata/test.key") + if err != nil { + t.Fatal(err) + } + + fakeEnterpriseURL := "https://api.githubenterprise.example.com/" + + client, err := NewClient(12345, string(key), fakeEnterpriseURL, false) + assert.NoError(t, err) + assert.NotNil(t, client) + assert.NotNil(t, client.transport) + + // Ensure the Enterprise base URL was applied + assert.Contains(t, client.Client.BaseURL.String(), fakeEnterpriseURL) + assert.Equal(t, client.transport.BaseURL, fakeEnterpriseURL) +} + +func TestNewClient_Success_Enterprise_TLS(t *testing.T) { + key, err := os.ReadFile("testdata/test.key") + if err != nil { + t.Fatal(err) + } + + fakeEnterpriseURL := "https://api.githubenterprise.example.com/" + + client, err := NewClient(12345, string(key), fakeEnterpriseURL, true) + assert.NoError(t, err) + assert.NotNil(t, client) + assert.Contains(t, client.Client.BaseURL.String(), fakeEnterpriseURL) } func TestNewClient_Failure(t *testing.T) { - client, err := NewClient(12345, "") + client, err := NewClient(12345, "", "", false) assert.Error(t, err) assert.Nil(t, client) } @@ -30,9 +78,32 @@ func TestClientInstallation(t *testing.T) { t.Fatal(err) } - client, err := NewClient(12345, string(key)) + client, err := NewClient(12345, string(key), "", false) assert.NoError(t, err) + assert.NotNil(t, client) installation := client.Installation(12345) assert.NotNil(t, installation) + + // The installation client should still have a valid BaseURL + assert.Contains(t, installation.BaseURL.String(), "https://api.github.com/") +} + +func TestClientInstallation_Enterprise(t *testing.T) { + key, err := os.ReadFile("testdata/test.key") + if err != nil { + t.Fatal(err) + } + + fakeEnterpriseURL := "https://api.githubenterprise.example.com/" + + client, err := NewClient(12345, string(key), fakeEnterpriseURL, true) + assert.NoError(t, err) + assert.NotNil(t, client) + + installation := client.Installation(12345) + assert.NotNil(t, installation) + + // Should retain enterprise base URL + assert.Contains(t, installation.BaseURL.String(), fakeEnterpriseURL) } diff --git a/install.sh b/install.sh index e0a07ed..59051a5 100755 --- a/install.sh +++ b/install.sh @@ -8,9 +8,9 @@ export DEBIAN_FRONTEND=noninteractive export FIREACTIONS_VERSION=0.2.5 export FIRECRACKER_VERSION=1.4.1 export KERNEL_VERSION=5.10 +export GITHUB_SKIP_TLS_VERIFY=false -usage() -{ +usage() { echo "This script installs Fireactions on a Linux machine." echo echo "Usage: $0 [options]" @@ -18,6 +18,8 @@ usage() echo "Options:" echo " --github-app-id Sepcify the ID of the GitHub App (required)" echo " --github-app-key-file Specify the path to the GitHub App private key file (required)" + echo " --github-enterprise-api-url Specify the enterprise GitHub api url (optional default: https://api.github.com)" + echo " --github-skip-tls-verify Specify whether or not to verify GitHub certificate (optional default: false)" echo " --github-organization Specify the name of the GitHub organization (required)" echo " --fireactions-version Specify the Fireactions version to install (default: $FIREACTIONS_VERSION)" echo " --firecracker-version Specify the Firecracker version to install (default: $FIRECRACKER_VERSION)" @@ -29,37 +31,32 @@ usage() echo } -has_yum() -{ +has_yum() { [ -n "$(command -v yum)" ] } -has_apt() -{ +has_apt() { [ -n "$(command -v apt-get)" ] } -print_error() -{ +print_error() { echo -e "\033[31mERROR:\033[0m $1" } -check_kvm() -{ +check_kvm() { if [[ ! -e /dev/kvm ]]; then print_error "Virtualization is not available on this machine, /dev/kvm is missing. Enable virtualization and try again." exit 1 fi } -install_dependencies() -{ +install_dependencies() { if has_apt; then apt-get update -qq -y apt-get install -qq -y \ - curl \ + curl \ gnupg \ - lvm2 \ + lvm2 \ tar elif has_yum; then yum install -q -y \ @@ -72,8 +69,7 @@ install_dependencies() fi } -install_firecracker() -{ +install_firecracker() { if [[ -e /usr/local/bin/firecracker ]]; then return fi @@ -91,8 +87,7 @@ install_firecracker() rm -rf "$TEMP_DIR" } -install_containerd() -{ +install_containerd() { if [[ -e /usr/local/bin/containerd ]]; then return fi @@ -101,13 +96,13 @@ install_containerd() curl -sL -o "$TEMP_DIR/containerd-$CONTAINERD_VERSION-linux-$ARCH.tar.gz" \ "https://github.com/containerd/containerd/releases/download/v$CONTAINERD_VERSION/containerd-$CONTAINERD_VERSION-linux-$ARCH.tar.gz" - + tar -zxf "$TEMP_DIR/containerd-$CONTAINERD_VERSION-linux-$ARCH.tar.gz" -C "$TEMP_DIR" mv "$TEMP_DIR/bin/containerd" /usr/local/bin/containerd mv "$TEMP_DIR/bin/ctr" /usr/local/bin/ctr - cat < /etc/systemd/system/containerd.service + cat </etc/systemd/system/containerd.service [Unit] Description=containerd container runtime Documentation=https://containerd.io @@ -136,7 +131,7 @@ WantedBy=multi-user.target EOF mkdir -p /etc/containerd - cat < /etc/containerd/config.toml + cat </etc/containerd/config.toml version = 2 root = "/var/lib/containerd" @@ -174,8 +169,7 @@ EOF rm -rf "$TEMP_DIR" } -install_cni() -{ +install_cni() { if [[ -e /opt/cni/bin/bridge ]]; then return fi @@ -184,7 +178,7 @@ install_cni() curl -sL -o "$TEMP_DIR/cni-plugins-linux-$ARCH-v$CNI_VERSION.tgz" \ "https://github.com/containernetworking/plugins/releases/download/v$CNI_VERSION/cni-plugins-linux-$ARCH-v$CNI_VERSION.tgz" - + mkdir -p /opt/cni/bin tar -zxf "$TEMP_DIR/cni-plugins-linux-$ARCH-v$CNI_VERSION.tgz" -C /opt/cni/bin @@ -193,7 +187,7 @@ install_cni() chmod +x /opt/cni/bin/tc-redirect-tap mkdir -p /etc/cni/net.d - cat < /etc/cni/net.d/10-fireactions.conflist + cat </etc/cni/net.d/10-fireactions.conflist { "cniVersion": "0.4.0", "name": "fireactions", @@ -226,8 +220,7 @@ EOF rm -rf "$TEMP_DIR" } -install_fireactions() -{ +install_fireactions() { if [[ -e /usr/local/bin/fireactions ]]; then return fi @@ -236,20 +229,20 @@ install_fireactions() curl -sL -o "$TEMP_DIR/fireactions-v$FIREACTIONS_VERSION.tar.gz" \ "https://github.com/hostinger/fireactions/releases/download/v$FIREACTIONS_VERSION/fireactions-v$FIREACTIONS_VERSION-linux-$ARCH.tar.gz" - + tar -zxf "$TEMP_DIR/fireactions-v$FIREACTIONS_VERSION.tar.gz" -C "$TEMP_DIR" mv "$TEMP_DIR/fireactions" /usr/local/bin/fireactions chmod +x /usr/local/bin/fireactions - cat < /etc/sysctl.d/99-fireactions.conf + cat </etc/sysctl.d/99-fireactions.conf net.ipv4.conf.all.forwarding=1 net.ipv4.ip_forward=1 EOF - sysctl -p /etc/sysctl.d/99-fireactions.conf > /dev/null + sysctl -p /etc/sysctl.d/99-fireactions.conf >/dev/null mkdir -p /etc/fireactions - cat < /etc/fireactions/config.yaml + cat </etc/fireactions/config.yaml bind_address: 127.0.0.1:8080 metrics: @@ -258,6 +251,8 @@ metrics: github: app_id: $GITHUB_APP_ID + enterprise_api_url: $GITHUB_ENTERPRISE_API_URL + skip_tls_verify: $GITHUB_SKIP_TLS_VERIFY app_private_key: | $GITHUB_APP_PRIVATE_KEY @@ -288,7 +283,7 @@ pools: log_level: debug EOF - cat < /etc/systemd/system/fireactions.service + cat </etc/systemd/system/fireactions.service [Unit] Description=Fireactions Documentation=https://github.com/hostinger/fireactions @@ -314,8 +309,7 @@ EOF rm -rf "$TEMP_DIR" } -install_kernel() -{ +install_kernel() { if [[ -e /var/lib/fireactions/kernels/$KERNEL_VERSION/vmlinux ]]; then return fi @@ -326,8 +320,7 @@ install_kernel() "https://storage.googleapis.com/fireactions/kernels/$ARCH/$KERNEL_VERSION/vmlinux" } -main() -{ +main() { if [ "$#" -eq 0 ]; then usage exit 1 @@ -335,76 +328,91 @@ main() while [ "$1" != "" ]; do case $1 in - --github-app-id ) - shift - GITHUB_APP_ID=$1 - ;; - --github-app-id=* ) - GITHUB_APP_ID="${1#*=}" - ;; - --github-app-key-file ) - shift - GITHUB_APP_PRIVATE_KEY_FILE=$1 - ;; - --github-app-key-file=* ) - GITHUB_APP_PRIVATE_KEY_FILE="${1#*=}" - ;; - --github-organization ) - shift - GITHUB_ORGANIZATION=$1 - ;; - --github-organization=* ) - GITHUB_ORGANIZATION="${1#*=}" - ;; - --fireactions-version ) - shift - FIREACTIONS_VERSION=$1 - ;; - --fireactions-version=* ) - FIREACTIONS_VERSION="${1#*=}" - ;; - --firecracker-version ) - shift - FIRECRACKER_VERSION=$1 - ;; - --firecracker-version=* ) - FIRECRACKER_VERSION="${1#*=}" - ;; - --kernel-version ) - shift - KERNEL_VERSION=$1 - ;; - --kernel-version=* ) - KERNEL_VERSION="${1#*=}" - ;; - --containerd-snapshotter-device ) - shift - CONTAINERD_SNAPSHOTTER_DEVICE=$1 - ;; - --containerd-snapshotter-device=* ) - export CONTAINERD_SNAPSHOTTER_DEVICE="${1#*=}" - ;; - --containerd-version ) - shift - CONTAINERD_VERSION=$1 - ;; - --containerd-version=* ) - CONTAINERD_VERSION="${1#*=}" - ;; - --cni-version ) - shift - CNI_VERSION=$1 - ;; - --cni-version=* ) - CNI_VERSION="${1#*=}" - ;; - -h | --help ) - usage - exit 0 - ;; - * ) - usage - exit 1 + --github-app-id) + shift + GITHUB_APP_ID=$1 + ;; + --github-app-id=*) + GITHUB_APP_ID="${1#*=}" + ;; + --github-app-key-file) + shift + GITHUB_APP_PRIVATE_KEY_FILE=$1 + ;; + --github-app-key-file=*) + GITHUB_APP_PRIVATE_KEY_FILE="${1#*=}" + ;; + --github-enterprise-api-url) + shift + GITHUB_ENTERPRISE_API_URL=$1 + ;; + --github-enterprise-api-url=*) + GITHUB_ENTERPRISE_API_URL="${1#*=}" + ;; + --github-skip-tls-verify) + shift + GITHUB_SKIP_TLS_VERIFY=$1 + ;; + --github-skip-tls-verify=*) + GITHUB_SKIP_TLS_VERIFY="${1#*=}" + ;; + --github-organization) + shift + GITHUB_ORGANIZATION=$1 + ;; + --github-organization=*) + GITHUB_ORGANIZATION="${1#*=}" + ;; + --fireactions-version) + shift + FIREACTIONS_VERSION=$1 + ;; + --fireactions-version=*) + FIREACTIONS_VERSION="${1#*=}" + ;; + --firecracker-version) + shift + FIRECRACKER_VERSION=$1 + ;; + --firecracker-version=*) + FIRECRACKER_VERSION="${1#*=}" + ;; + --kernel-version) + shift + KERNEL_VERSION=$1 + ;; + --kernel-version=*) + KERNEL_VERSION="${1#*=}" + ;; + --containerd-snapshotter-device) + shift + CONTAINERD_SNAPSHOTTER_DEVICE=$1 + ;; + --containerd-snapshotter-device=*) + export CONTAINERD_SNAPSHOTTER_DEVICE="${1#*=}" + ;; + --containerd-version) + shift + CONTAINERD_VERSION=$1 + ;; + --containerd-version=*) + CONTAINERD_VERSION="${1#*=}" + ;; + --cni-version) + shift + CNI_VERSION=$1 + ;; + --cni-version=*) + CNI_VERSION="${1#*=}" + ;; + -h | --help) + usage + exit 0 + ;; + *) + usage + exit 1 + ;; esac shift done @@ -445,15 +453,16 @@ main() fi case $(uname -m) in - x86_64) - export ARCH=amd64 - ;; - aarch64) - export ARCH=arm64 - ;; - *) - print_error "Unsupported architecture: $(uname -m)" - exit 1 + x86_64) + export ARCH=amd64 + ;; + aarch64) + export ARCH=arm64 + ;; + *) + print_error "Unsupported architecture: $(uname -m)" + exit 1 + ;; esac if [[ -e /usr/local/bin/fireactions ]]; then diff --git a/server/config.go b/server/config.go index 0cceb29..cf11ebb 100644 --- a/server/config.go +++ b/server/config.go @@ -28,13 +28,15 @@ type MetricsConfig struct { } type GitHubConfig struct { - AppPrivateKey string `yaml:"app_private_key" validate:"required"` - AppID int64 `yaml:"app_id" validate:"required"` + AppPrivateKey string `yaml:"app_private_key" validate:"required"` + AppID int64 `yaml:"app_id" validate:"required"` + EnterpriseApiUrl string `yaml:"enterprise_api_url" validate:"omitempty,url"` + SkipTLSVerify bool `yaml:"skip_tls_verify" validate:"skiptls_if_enterprise"` } type RunnerConfig struct { Name string `yaml:"name" validate:"required"` - ImagePullPolicy string `yaml:"image_pull_policy" validate:"required,oneof=always never ifnotpresent"` + ImagePullPolicy string `yaml:"image_pull_policy" validate:"required,oneof=Always Never IfNotPresent"` Image string `yaml:"image" validate:"required"` Organization string `yaml:"organization" validate:"required"` GroupID int64 `yaml:"group_id" validate:"required"` @@ -42,7 +44,7 @@ type RunnerConfig struct { } type FirecrackerConfig struct { - BinaryPath string `yaml:"binary_path" ` + BinaryPath string `yaml:"binary_path"` KernelImagePath string `yaml:"kernel_image_path"` KernelArgs string `yaml:"kernel_args"` MachineConfig FirecrackerMachineConfig `yaml:"machine_config"` @@ -56,18 +58,21 @@ type FirecrackerMachineConfig struct { // DefaultConfig creates a new Config with default values. func DefaultConfig() *Config { - c := &Config{ + return &Config{ BindAddress: ":8080", Metrics: &MetricsConfig{Enabled: true, Address: ":8081"}, BasicAuthEnabled: false, BasicAuthUsers: map[string]string{}, - GitHub: &GitHubConfig{AppPrivateKey: "", AppID: 0}, - Pools: []*PoolConfig{}, - LogLevel: "debug", - Debug: false, + GitHub: &GitHubConfig{ + AppPrivateKey: "", + AppID: 0, + EnterpriseApiUrl: "", // empty = GitHub.com + SkipTLSVerify: false, // default: do not skip TLS + }, + Pools: []*PoolConfig{}, + LogLevel: "debug", + Debug: false, } - - return c } // NewConfigFromFile creates a new Config from a file. @@ -75,13 +80,11 @@ func NewConfig(path string) (*Config, error) { c := DefaultConfig() c.path = path - err := c.Load() - if err != nil { + if err := c.Load(); err != nil { return nil, err } - err = c.Validate() - if err != nil { + if err := c.Validate(); err != nil { return nil, fmt.Errorf("validate: %w", err) } @@ -94,7 +97,6 @@ func (c *Config) Load() error { if err != nil { return fmt.Errorf("open file: %w", err) } - defer file.Close() return yaml.NewDecoder(file).Decode(c) @@ -102,5 +104,17 @@ func (c *Config) Load() error { // Validate validates the configuration. func (c *Config) Validate() error { - return validator.New().Struct(c) + v := validator.New() + + // Custom validation: SkipTLSVerify can only be true if EnterpriseApiUrl is set + _ = v.RegisterValidation("skiptls_if_enterprise", func(fl validator.FieldLevel) bool { + cfg := fl.Parent().Interface().(GitHubConfig) + if cfg.SkipTLSVerify && cfg.EnterpriseApiUrl == "" { + return false + } + return true + }) + + // Apply struct validation + return v.Struct(c) } diff --git a/server/config_test.go b/server/config_test.go index f2b3ae7..da4b963 100644 --- a/server/config_test.go +++ b/server/config_test.go @@ -9,8 +9,15 @@ import ( func TestNewConfig(t *testing.T) { config, err := NewConfig("testdata/config1.yaml") if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } assert.Equal(t, "testdata/config1.yaml", config.path) + + // Check GitHub config values + assert.NotNil(t, config.GitHub) + assert.NotEmpty(t, config.GitHub.AppPrivateKey) + assert.NotZero(t, config.GitHub.AppID) + assert.Equal(t, "https://api.githubenterprise.example.com/api/v3", config.GitHub.EnterpriseApiUrl) + assert.True(t, config.GitHub.SkipTLSVerify) } diff --git a/server/server.go b/server/server.go index 19eaff1..e30a405 100644 --- a/server/server.go +++ b/server/server.go @@ -34,21 +34,25 @@ type Opt func(s *Server) // WithLogger sets the logger for the Server. func WithLogger(logger *zerolog.Logger) Opt { - f := func(s *Server) { + return func(s *Server) { s.logger = logger } - - return f } // New creates a new Server. func New(config *Config, opts ...Opt) (*Server, error) { - err := config.Validate() - if err != nil { + // Validate config + if err := config.Validate(); err != nil { return nil, fmt.Errorf("config: %w", err) } - github, err := github.NewClient(config.GitHub.AppID, config.GitHub.AppPrivateKey) + // Create GitHub client (supports GitHub.com and Enterprise) + gclient, err := github.NewClient( + config.GitHub.AppID, + config.GitHub.AppPrivateKey, + config.GitHub.EnterpriseApiUrl, + config.GitHub.SkipTLSVerify, + ) if err != nil { return nil, fmt.Errorf("creating github client: %w", err) } @@ -58,6 +62,7 @@ func New(config *Config, opts ...Opt) (*Server, error) { handler.Use(requestid.New(requestid.WithCustomHeaderStrKey("X-Request-ID"))) handler.Use(gin.Recovery()) + // HTTP server server := &http.Server{ Addr: config.BindAddress, Handler: handler, @@ -71,7 +76,7 @@ func New(config *Config, opts ...Opt) (*Server, error) { config: config, server: server, pools: make(map[string]*Pool), - github: github, + github: gclient, l: &sync.Mutex{}, logger: &logger, } @@ -90,7 +95,6 @@ func New(config *Config, opts ...Opt) (*Server, error) { WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } - s.metricsServer = metricsServer } @@ -123,7 +127,12 @@ func New(config *Config, opts ...Opt) (*Server, error) { // Run starts the server and blocks until the context is canceled. func (s *Server) Run(ctx context.Context) error { - s.logger.Info().Str("version", fireactions.Version).Str("date", fireactions.Date).Str("commit", fireactions.Commit).Msgf("Starting server on %s", s.config.BindAddress) + s.logger.Info(). + Str("version", fireactions.Version). + Str("date", fireactions.Date). + Str("commit", fireactions.Commit). + Msgf("Starting server on %s", s.config.BindAddress) + if s.config.Debug { s.logger.Warn().Msg("Debug mode enabled") } @@ -139,7 +148,6 @@ func (s *Server) Run(ctx context.Context) error { if err != nil { return fmt.Errorf("creating pool: %w", err) } - s.pools[poolConfig.Name] = pool go pool.Start() s.logger.Info().Msgf("Pool %s started", poolConfig.Name) @@ -147,19 +155,18 @@ func (s *Server) Run(ctx context.Context) error { errGroup := &errgroup.Group{} errGroup.Go(func() error { return s.server.Serve(listener) }) + if s.metricsServer != nil { metricsListener, err := net.Listen("tcp", s.config.Metrics.Address) if err != nil { return fmt.Errorf("failed to start metrics server: %w", err) } - errGroup.Go(func() error { return s.metricsServer.Serve(metricsListener) }) } go func() { <-ctx.Done() fmt.Println() - s.logger.Info().Msg("Shutting down server") wg := sync.WaitGroup{} @@ -170,7 +177,6 @@ func (s *Server) Run(ctx context.Context) error { wg.Done() }(pool) } - wg.Wait() cancelCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second) @@ -205,7 +211,6 @@ func (s *Server) GetPool(ctx context.Context, id string) (*Pool, error) { if !ok { return nil, fireactions.ErrPoolNotFound } - return pool, nil } @@ -218,7 +223,6 @@ func (s *Server) ListPools(ctx context.Context) ([]*Pool, error) { for _, pool := range s.pools { pools = append(pools, pool) } - return pools, nil } @@ -258,13 +262,13 @@ func (s *Server) ResumePool(ctx context.Context, id string) error { return nil } +// Reload reloads server configuration and restarts pools as needed. func (s *Server) Reload(ctx context.Context) error { s.l.Lock() defer s.l.Unlock() s.logger.Info().Msgf("Reloading server configuration") - err := s.config.Load() - if err != nil { + if err := s.config.Load(); err != nil { return fmt.Errorf("loading config: %w", err) } @@ -276,7 +280,7 @@ func (s *Server) Reload(ctx context.Context) error { continue } - pool, err = NewPool(s.logger, poolConfig, s.github) + pool, err := NewPool(s.logger, poolConfig, s.github) if err != nil { return fmt.Errorf("creating pool: %w", err) } diff --git a/server/testdata/config1.yaml b/server/testdata/config1.yaml index 7f16200..0b09627 100644 --- a/server/testdata/config1.yaml +++ b/server/testdata/config1.yaml @@ -16,51 +16,53 @@ github: app_private_key: | -----BEGIN RSA PRIVATE KEY----- app_id: 12345 + enterprise_api_url: "https://api.githubenterprise.example.com/api/v3" # optional, empty = github.com, otherwise i.e. https://example.com/api/v3, or https://api.example.com + skip_tls_verify: true pools: -- name: fireactions-2vcpu-2gb - max_runners: 20 - min_runners: 10 - runner: - name: fireactions-2vcpu-2gb - image: ghcr.io/hostinger/fireactions/runner:ubuntu-20.04-x64-2.310.2 - image_pull_policy: IfNotPresent - group_id: 1 - organization: hostinger - labels: - - self-hosted - - fireactions-2vcpu-2gb - - fireactions - firecracker: - binary_path: firecracker - kernel_image_path: /var/lib/fireactions/vmlinux - kernel_args: "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw" - machine_config: - mem_size_mib: 2048 - vcpu_count: 2 - metadata: - example1: value1 -- name: fireactions-2vcpu-4gb - max_runners: 20 - min_runners: 10 - runner: - name: fireactions-2vcpu-4gb - image: ghcr.io/hostinger/fireactions/runner:ubuntu-20.04-x64-2.310.2 - image_pull_policy: IfNotPresent - group_id: 1 - organization: hostinger - labels: - - self-hosted - - fireactions-2vcpu-2gb - - fireactions - firecracker: - binary_path: firecracker - kernel_image_path: /var/lib/fireactions/vmlinux - kernel_args: "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw" - machine_config: - mem_size_mib: 4096 - vcpu_count: 2 - metadata: - example1: value1 + - name: fireactions-2vcpu-2gb + max_runners: 20 + min_runners: 10 + runner: + name: fireactions-2vcpu-2gb + image: ghcr.io/hostinger/fireactions/runner:ubuntu-20.04-x64-2.310.2 + image_pull_policy: IfNotPresent + group_id: 1 + organization: hostinger + labels: + - self-hosted + - fireactions-2vcpu-2gb + - fireactions + firecracker: + binary_path: firecracker + kernel_image_path: /var/lib/fireactions/vmlinux + kernel_args: "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw" + machine_config: + mem_size_mib: 2048 + vcpu_count: 2 + metadata: + example1: value1 + - name: fireactions-2vcpu-4gb + max_runners: 20 + min_runners: 10 + runner: + name: fireactions-2vcpu-4gb + image: ghcr.io/hostinger/fireactions/runner:ubuntu-20.04-x64-2.310.2 + image_pull_policy: IfNotPresent + group_id: 1 + organization: hostinger + labels: + - self-hosted + - fireactions-2vcpu-2gb + - fireactions + firecracker: + binary_path: firecracker + kernel_image_path: /var/lib/fireactions/vmlinux + kernel_args: "console=ttyS0 noapic reboot=k panic=1 pci=off nomodules rw" + machine_config: + mem_size_mib: 4096 + vcpu_count: 2 + metadata: + example1: value1 log_level: debug