orchard controller run: create a default bootstrap context (#291)
* orchard controller run: create a default bootstrap context * Dockerfile: correct AS casing * Fix typo in BootstrapContextName
This commit is contained in:
parent
ed8c0771c4
commit
9919117b9b
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:latest as builder
|
||||
FROM golang:latest AS builder
|
||||
|
||||
# Install GoReleaser Pro
|
||||
RUN echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | tee /etc/apt/sources.list.d/goreleaser.list
|
||||
|
|
|
|||
|
|
@ -7,27 +7,39 @@ import (
|
|||
"github.com/cirruslabs/orchard/internal/controller"
|
||||
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sethvargo/go-password/password"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const BootstrapContextName = "bootstrap-context"
|
||||
const BootstrapAdminName = "bootstrap-admin"
|
||||
|
||||
func Bootstrap(controllerInstance *controller.Controller, controllerCert tls.Certificate) error {
|
||||
func Bootstrap(controllerInstance *controller.Controller, controllerCert tls.Certificate) (string, string, error) {
|
||||
// Determine if we need to do anything at all
|
||||
orchardBootstrapAdminToken, orchardBootstrapAdminTokenPresent := os.LookupEnv("ORCHARD_BOOTSTRAP_ADMIN_TOKEN")
|
||||
|
||||
numServiceAccounts, err := controllerInstance.NumServiceAccounts()
|
||||
serviceAccounts, err := controllerInstance.ServiceAccounts()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve the number of service accounts: %w", err)
|
||||
return "", "", fmt.Errorf("failed to retrieve the number of service accounts: %w", err)
|
||||
}
|
||||
|
||||
if numServiceAccounts != 0 && !orchardBootstrapAdminTokenPresent {
|
||||
if len(serviceAccounts) != 0 && !orchardBootstrapAdminTokenPresent {
|
||||
// No bootstrap is needed because there are service accounts
|
||||
// present in the database (so it's not the first start)
|
||||
// and no bootstrap admin token change is requested
|
||||
return nil
|
||||
//
|
||||
// However, if the BootstrapAdminName service account still exists,
|
||||
// return its credentials. We'll use them for updating the
|
||||
// BootstrapContextName context.
|
||||
if serviceAccount, ok := lo.Find(serviceAccounts, func(serviceAccount *v1.ServiceAccount) bool {
|
||||
return serviceAccount.Name == BootstrapAdminName
|
||||
}); ok {
|
||||
return serviceAccount.Name, serviceAccount.Token, nil
|
||||
}
|
||||
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// Generate a bootstrap admin token if not present in the environment variable
|
||||
|
|
@ -48,14 +60,14 @@ func Bootstrap(controllerInstance *controller.Controller, controllerCert tls.Cer
|
|||
}, password.Symbols),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate bootstrap admin token: "+
|
||||
return "", "", fmt.Errorf("failed to generate bootstrap admin token: "+
|
||||
"failed to initialize password generator: %w", err)
|
||||
}
|
||||
|
||||
orchardBootstrapAdminToken, err = passwordGenerator.Generate(32, 10, 10,
|
||||
false, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate bootstrap admin token: %w", err)
|
||||
return "", "", fmt.Errorf("failed to generate bootstrap admin token: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +79,7 @@ func Bootstrap(controllerInstance *controller.Controller, controllerCert tls.Cer
|
|||
Token: orchardBootstrapAdminToken,
|
||||
Roles: v1.AllServiceAccountRoles(),
|
||||
}); err != nil {
|
||||
return err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Report bootstrapping result to the user
|
||||
|
|
@ -98,5 +110,5 @@ func Bootstrap(controllerInstance *controller.Controller, controllerCert tls.Cer
|
|||
|
||||
pterm.Info.Print(messages...)
|
||||
|
||||
return nil
|
||||
return BootstrapAdminName, orchardBootstrapAdminToken, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package controller
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
configpkg "github.com/cirruslabs/orchard/internal/config"
|
||||
"github.com/cirruslabs/orchard/internal/controller"
|
||||
"github.com/cirruslabs/orchard/internal/netconstants"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
|
@ -148,9 +150,64 @@ func runController(cmd *cobra.Command, args []string) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := Bootstrap(controllerInstance, controllerCert); err != nil {
|
||||
// Return bootstrap service account credentials, optionally creating it
|
||||
serviceAccountName, serviceAccountToken, err := Bootstrap(controllerInstance, controllerCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If bootstrap service account still exists, update a context for it,
|
||||
// optionally making this context the default one
|
||||
if serviceAccountName != "" && serviceAccountToken != "" {
|
||||
if err := createBootstrapContext(controllerInstance.Address(), controllerCert,
|
||||
serviceAccountName, serviceAccountToken); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return controllerInstance.Run(cmd.Context())
|
||||
}
|
||||
|
||||
func createBootstrapContext(
|
||||
controllerAddress string,
|
||||
controllerCert tls.Certificate,
|
||||
serviceAccountName string,
|
||||
serviceAccountToken string,
|
||||
) error {
|
||||
configHandle, err := configpkg.NewHandle()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context := configpkg.Context{
|
||||
URL: controllerAddress,
|
||||
ServiceAccountName: serviceAccountName,
|
||||
ServiceAccountToken: serviceAccountToken,
|
||||
}
|
||||
|
||||
if !noTLS {
|
||||
certificatePEMBytes := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: controllerCert.Certificate[0],
|
||||
})
|
||||
|
||||
context.Certificate = certificatePEMBytes
|
||||
}
|
||||
|
||||
if err := configHandle.CreateContext(BootstrapContextName, context, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config, err := configHandle.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.DefaultContext == "" {
|
||||
if err := configHandle.SetDefaultContext(BootstrapContextName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ func New(opts ...Option) (*Controller, error) {
|
|||
return controller, nil
|
||||
}
|
||||
|
||||
func (controller *Controller) NumServiceAccounts() (int, error) {
|
||||
func (controller *Controller) ServiceAccounts() ([]*v1.ServiceAccount, error) {
|
||||
var serviceAccounts []*v1.ServiceAccount
|
||||
var err error
|
||||
|
||||
|
|
@ -197,10 +197,10 @@ func (controller *Controller) NumServiceAccounts() (int, error) {
|
|||
|
||||
return err
|
||||
}); err != nil {
|
||||
return 0, fmt.Errorf("failed to retrieve a list of service accounts: %w", err)
|
||||
return nil, fmt.Errorf("failed to retrieve a list of service accounts: %w", err)
|
||||
}
|
||||
|
||||
return len(serviceAccounts), nil
|
||||
return serviceAccounts, nil
|
||||
}
|
||||
|
||||
func (controller *Controller) EnsureServiceAccount(serviceAccount *v1.ServiceAccount) error {
|
||||
|
|
|
|||
Loading…
Reference in New Issue