Add execution environment to external provider
The execution package is a common package that can be used by external providers to load environment variables and stdin, in a coherent struct that can be consumed by the various commands that need to execute as part of the provider. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
e67c08b1d4
commit
882d07a0da
4 changed files with 133 additions and 70 deletions
13
runner/providers/external/execution/commands.go
vendored
Normal file
13
runner/providers/external/execution/commands.go
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package execution
|
||||
|
||||
type ExecutionCommand string
|
||||
|
||||
const (
|
||||
CreateInstanceCommand ExecutionCommand = "CreateInstance"
|
||||
DeleteInstanceCommand ExecutionCommand = "DeleteInstance"
|
||||
GetInstanceCommand ExecutionCommand = "GetInstance"
|
||||
ListInstancesCommand ExecutionCommand = "ListInstances"
|
||||
StartInstanceCommand ExecutionCommand = "StartInstance"
|
||||
StopInstanceCommand ExecutionCommand = "StopInstance"
|
||||
RemoveAllInstancesCommand ExecutionCommand = "RemoveAllInstances"
|
||||
)
|
||||
101
runner/providers/external/execution/execution.go
vendored
Normal file
101
runner/providers/external/execution/execution.go
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package execution
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/cloudbase/garm/params"
|
||||
)
|
||||
|
||||
func GetEnvironment() (Environment, error) {
|
||||
env := Environment{
|
||||
Command: ExecutionCommand(os.Getenv("GARM_COMMAND")),
|
||||
ControllerID: os.Getenv("GARM_CONTROLLER_ID"),
|
||||
PoolID: os.Getenv("GARM_POOL_ID"),
|
||||
ProviderConfigFile: os.Getenv("GARM_PROVIDER_CONFIG_FILE"),
|
||||
InstanceID: os.Getenv("GARM_INSTANCE_ID"),
|
||||
}
|
||||
|
||||
if env.Command == CreateInstanceCommand {
|
||||
// We need to get the bootstrap params from stdin
|
||||
info, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
return Environment{}, fmt.Errorf("failed to get stdin: %w", err)
|
||||
}
|
||||
if info.Size() == 0 {
|
||||
return Environment{}, fmt.Errorf("no data found on stdin")
|
||||
}
|
||||
|
||||
var data bytes.Buffer
|
||||
if _, err := io.Copy(&data, os.Stdin); err != nil {
|
||||
return Environment{}, fmt.Errorf("failed to copy bootstrap params")
|
||||
}
|
||||
|
||||
var bootstrapParams params.BootstrapInstance
|
||||
if err := json.Unmarshal(data.Bytes(), &bootstrapParams); err != nil {
|
||||
return Environment{}, fmt.Errorf("failed to decode instance params: %w", err)
|
||||
}
|
||||
env.BootstrapParams = bootstrapParams
|
||||
}
|
||||
|
||||
if err := env.Validate(); err != nil {
|
||||
return Environment{}, fmt.Errorf("failed to validate execution environment: %w", err)
|
||||
}
|
||||
|
||||
return env, nil
|
||||
}
|
||||
|
||||
type Environment struct {
|
||||
Command ExecutionCommand
|
||||
ControllerID string
|
||||
PoolID string
|
||||
ProviderConfigFile string
|
||||
InstanceID string
|
||||
BootstrapParams params.BootstrapInstance
|
||||
}
|
||||
|
||||
func (e Environment) Validate() error {
|
||||
if e.Command == "" {
|
||||
return fmt.Errorf("missing GARM_COMMAND")
|
||||
}
|
||||
|
||||
if e.ProviderConfigFile == "" {
|
||||
return fmt.Errorf("missing GARM_PROVIDER_CONFIG_FILE")
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(e.ProviderConfigFile); err != nil {
|
||||
return fmt.Errorf("error accessing config file: %w", err)
|
||||
}
|
||||
|
||||
switch e.Command {
|
||||
case CreateInstanceCommand:
|
||||
if e.BootstrapParams.Name == "" {
|
||||
return fmt.Errorf("missing bootstrap params")
|
||||
}
|
||||
if e.ControllerID == "" {
|
||||
return fmt.Errorf("missing controller ID")
|
||||
}
|
||||
if e.PoolID == "" {
|
||||
return fmt.Errorf("missing pool ID")
|
||||
}
|
||||
case DeleteInstanceCommand, GetInstanceCommand,
|
||||
StartInstanceCommand, StopInstanceCommand:
|
||||
if e.InstanceID == "" {
|
||||
return fmt.Errorf("missing instance ID")
|
||||
}
|
||||
case ListInstancesCommand:
|
||||
if e.PoolID == "" {
|
||||
return fmt.Errorf("missing pool ID")
|
||||
}
|
||||
case RemoveAllInstancesCommand:
|
||||
if e.ControllerID == "" {
|
||||
return fmt.Errorf("missing controller ID")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown GARM_COMMAND: %s", e.Command)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
40
runner/providers/external/external.go
vendored
40
runner/providers/external/external.go
vendored
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/cloudbase/garm/params"
|
||||
"github.com/cloudbase/garm/runner/common"
|
||||
providerCommon "github.com/cloudbase/garm/runner/providers/common"
|
||||
"github.com/cloudbase/garm/runner/providers/external/execution"
|
||||
"github.com/cloudbase/garm/util/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
|
@ -42,10 +43,6 @@ type external struct {
|
|||
execPath string
|
||||
}
|
||||
|
||||
func (e *external) configEnvVar() string {
|
||||
return fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile)
|
||||
}
|
||||
|
||||
func (e *external) validateCreateResult(inst params.Instance, bootstrapParams params.BootstrapInstance) error {
|
||||
if inst.ProviderID == "" {
|
||||
return garmErrors.NewProviderError("missing provider ID after create call")
|
||||
|
|
@ -68,11 +65,12 @@ func (e *external) validateCreateResult(inst params.Instance, bootstrapParams pa
|
|||
|
||||
// CreateInstance creates a new compute instance in the provider.
|
||||
func (e *external) CreateInstance(ctx context.Context, bootstrapParams params.BootstrapInstance) (params.Instance, error) {
|
||||
asEnv := bootstrapParamsToEnv(bootstrapParams)
|
||||
asEnv = append(asEnv, createInstanceCommand)
|
||||
asEnv = append(asEnv, fmt.Sprintf("GARM_CONTROLLER_ID=%s", e.controllerID))
|
||||
asEnv = append(asEnv, fmt.Sprintf("GARM_POOL_ID=%s", bootstrapParams.PoolID))
|
||||
asEnv = append(asEnv, e.configEnvVar())
|
||||
asEnv := []string{
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.CreateInstanceCommand),
|
||||
fmt.Sprintf("GARM_CONTROLLER_ID=%s", e.controllerID),
|
||||
fmt.Sprintf("GARM_POOL_ID=%s", bootstrapParams.PoolID),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
|
||||
asJs, err := json.Marshal(bootstrapParams)
|
||||
if err != nil {
|
||||
|
|
@ -101,9 +99,9 @@ func (e *external) CreateInstance(ctx context.Context, bootstrapParams params.Bo
|
|||
// Delete instance will delete the instance in a provider.
|
||||
func (e *external) DeleteInstance(ctx context.Context, instance string) error {
|
||||
asEnv := []string{
|
||||
deleteInstanceCommand,
|
||||
e.configEnvVar(),
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.DeleteInstanceCommand),
|
||||
fmt.Sprintf("GARM_INSTANCE_ID=%s", instance),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
|
||||
_, err := exec.Exec(ctx, e.execPath, nil, asEnv)
|
||||
|
|
@ -116,9 +114,9 @@ func (e *external) DeleteInstance(ctx context.Context, instance string) error {
|
|||
// GetInstance will return details about one instance.
|
||||
func (e *external) GetInstance(ctx context.Context, instance string) (params.Instance, error) {
|
||||
asEnv := []string{
|
||||
getInstanceCommand,
|
||||
e.configEnvVar(),
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.GetInstanceCommand),
|
||||
fmt.Sprintf("GARM_INSTANCE_ID=%s", instance),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
|
||||
// TODO(gabriel-samfira): handle error types. Of particular insterest is to
|
||||
|
|
@ -138,9 +136,9 @@ func (e *external) GetInstance(ctx context.Context, instance string) (params.Ins
|
|||
// ListInstances will list all instances for a provider.
|
||||
func (e *external) ListInstances(ctx context.Context, poolID string) ([]params.Instance, error) {
|
||||
asEnv := []string{
|
||||
listInstancesCommand,
|
||||
e.configEnvVar(),
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.ListInstancesCommand),
|
||||
fmt.Sprintf("GARM_POOL_ID=%s", poolID),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
|
||||
out, err := exec.Exec(ctx, e.execPath, nil, asEnv)
|
||||
|
|
@ -158,9 +156,9 @@ func (e *external) ListInstances(ctx context.Context, poolID string) ([]params.I
|
|||
// RemoveAllInstances will remove all instances created by this provider.
|
||||
func (e *external) RemoveAllInstances(ctx context.Context) error {
|
||||
asEnv := []string{
|
||||
removeAllInstancesCommand,
|
||||
e.configEnvVar(),
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.RemoveAllInstancesCommand),
|
||||
fmt.Sprintf("GARM_CONTROLLER_ID=%s", e.controllerID),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
_, err := exec.Exec(ctx, e.execPath, nil, asEnv)
|
||||
if err != nil {
|
||||
|
|
@ -172,9 +170,9 @@ func (e *external) RemoveAllInstances(ctx context.Context) error {
|
|||
// Stop shuts down the instance.
|
||||
func (e *external) Stop(ctx context.Context, instance string, force bool) error {
|
||||
asEnv := []string{
|
||||
stopInstanceCommand,
|
||||
e.configEnvVar(),
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.StopInstanceCommand),
|
||||
fmt.Sprintf("GARM_INSTANCE_ID=%s", instance),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
_, err := exec.Exec(ctx, e.execPath, nil, asEnv)
|
||||
if err != nil {
|
||||
|
|
@ -186,9 +184,9 @@ func (e *external) Stop(ctx context.Context, instance string, force bool) error
|
|||
// Start boots up an instance.
|
||||
func (e *external) Start(ctx context.Context, instance string) error {
|
||||
asEnv := []string{
|
||||
startInstanceCommand,
|
||||
e.configEnvVar(),
|
||||
fmt.Sprintf("GARM_COMMAND=%s", execution.StartInstanceCommand),
|
||||
fmt.Sprintf("GARM_INSTANCE_ID=%s", instance),
|
||||
fmt.Sprintf("GARM_PROVIDER_CONFIG_FILE=%s", e.cfg.External.ConfigFile),
|
||||
}
|
||||
_, err := exec.Exec(ctx, e.execPath, nil, asEnv)
|
||||
if err != nil {
|
||||
|
|
|
|||
49
runner/providers/external/util.go
vendored
49
runner/providers/external/util.go
vendored
|
|
@ -1,49 +0,0 @@
|
|||
package external
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudbase/garm/params"
|
||||
)
|
||||
|
||||
const (
|
||||
envPrefix = "GARM"
|
||||
|
||||
createInstanceCommand = "GARM_COMMAND=CreateInstance"
|
||||
deleteInstanceCommand = "GARM_COMMAND=DeleteInstance"
|
||||
getInstanceCommand = "GARM_COMMAND=GetInstance"
|
||||
listInstancesCommand = "GARM_COMMAND=ListInstances"
|
||||
startInstanceCommand = "GARM_COMMAND=StartInstance"
|
||||
stopInstanceCommand = "GARM_COMMAND=StopInstance"
|
||||
removeAllInstancesCommand = "GARM_COMMAND=RemoveAllInstances"
|
||||
)
|
||||
|
||||
func bootstrapParamsToEnv(param params.BootstrapInstance) []string {
|
||||
ret := []string{
|
||||
fmt.Sprintf("%s_BOOTSTRAP_NAME='%s'", envPrefix, param.Name),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_OS_ARCH='%s'", envPrefix, param.OSArch),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_FLAVOR='%s'", envPrefix, param.Flavor),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_IMAGE='%s'", envPrefix, param.Image),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_POOL_ID='%s'", envPrefix, param.PoolID),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_INSTANCE_TOKEN='%s'", envPrefix, param.InstanceToken),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_CALLBACK_URL='%s'", envPrefix, param.CallbackURL),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_REPO_URL='%s'", envPrefix, param.RepoURL),
|
||||
fmt.Sprintf("%s_BOOTSTRAP_LABELS='%s'", envPrefix, strings.Join(param.Labels, ",")),
|
||||
}
|
||||
|
||||
for idx, tool := range param.Tools {
|
||||
ret = append(ret, fmt.Sprintf("%s_BOOTSTRAP_TOOLS_DOWNLOAD_URL_%d='%s'", envPrefix, idx, *tool.DownloadURL))
|
||||
ret = append(ret, fmt.Sprintf("%s_BOOTSTRAP_TOOLS_ARCH_%d='%s'", envPrefix, idx, *tool.Architecture))
|
||||
ret = append(ret, fmt.Sprintf("%s_BOOTSTRAP_TOOLS_OS_%d='%s'", envPrefix, idx, *tool.OS))
|
||||
ret = append(ret, fmt.Sprintf("%s_BOOTSTRAP_TOOLS_FILENAME_%d='%s'", envPrefix, idx, *tool.Filename))
|
||||
ret = append(ret, fmt.Sprintf("%s_BOOTSTRAP_TOOLS_SHA256_%d='%s'", envPrefix, idx, *tool.SHA256Checksum))
|
||||
}
|
||||
|
||||
for idx, sshKey := range param.SSHKeys {
|
||||
ret = append(ret, fmt.Sprintf("%s_BOOTSTRAP_SSH_KEY_%d='%s'", envPrefix, idx, sshKey))
|
||||
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue