Skip to content

Commit

Permalink
cli tests + fixes for the way the process handles failures
Browse files Browse the repository at this point in the history
  • Loading branch information
NorseGaud committed Dec 5, 2024
1 parent 483fd68 commit 1bdf680
Show file tree
Hide file tree
Showing 24 changed files with 769 additions and 247 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,10 @@ For example, the `github` plugin will update the metrics for the plugin it is ru

But metrics.UpdateService can also update things like `LastSuccess`, and `LastFailure`. See `metrics.UpdateService` for more information.

## FAQs

- Can I guarantee that the logs for Anklet will contain the `anklet (and all plugins) shut down` message?
- No, there is no guarantee an error, not thrown from inside of a plugin, will do a graceful shutdown.

## Copyright

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.9.4
0.10.0
111 changes: 87 additions & 24 deletions internal/anka/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ type Cli struct {
RegistryPullMutex sync.Mutex
}

func GetAnkaCLIFromContext(pluginCtx context.Context) *Cli {
func GetAnkaCLIFromContext(pluginCtx context.Context) (*Cli, error) {
ankaCLI, ok := pluginCtx.Value(config.ContextKey("ankacli")).(*Cli)
if !ok {
panic("function GetAnkaCLIFromContext failed")
return nil, fmt.Errorf("GetAnkaCLIFromContext failed")
}
return ankaCLI
return ankaCLI, nil
}

func NewCLI(pluginCtx context.Context) (*Cli, error) {
Expand Down Expand Up @@ -82,7 +82,10 @@ func NewCLI(pluginCtx context.Context) (*Cli, error) {
}

func (cli *Cli) Execute(pluginCtx context.Context, args ...string) ([]byte, int, error) {
logger := logging.GetLoggerFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return nil, 0, err
}
if args[2] != "list" { // hide spammy list command
logger.DebugContext(pluginCtx, "executing", "command", strings.Join(args, " "))
}
Expand Down Expand Up @@ -157,12 +160,18 @@ func (cli *Cli) ExecuteAndParseJsonOnError(pluginCtx context.Context, args ...st
}

func (cli *Cli) AnkaRun(pluginCtx context.Context, args ...string) error {
vm := GetAnkaVmFromContext(pluginCtx)
vm, err := GetAnkaVmFromContext(pluginCtx)
if err != nil {
return err
}
runOutput, exitCode, err := cli.Execute(pluginCtx, "anka", "-j", "run", vm.Name, "bash", "-c", strings.Join(args, " "))
if exitCode != 0 || err != nil {
return fmt.Errorf("command execution failed with code %d: %s %s", exitCode, string(runOutput), err)
}
logger := logging.GetLoggerFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return err
}
logger.DebugContext(pluginCtx, "command executed successfully", "stdout", string(runOutput))
return nil
}
Expand All @@ -171,8 +180,14 @@ func (cli *Cli) AnkaRegistryPull(workerCtx context.Context, pluginCtx context.Co
if pluginCtx.Err() != nil {
return nil, fmt.Errorf("context canceled before AnkaRegistryPull")
}
logger := logging.GetLoggerFromContext(pluginCtx)
ctxPlugin := config.GetPluginFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return nil, err
}
ctxPlugin, err := config.GetPluginFromContext(pluginCtx)
if err != nil {
return nil, err
}
var registryExtra []string
if ctxPlugin.RegistryURL != "" {
registryExtra = []string{"--remote", ctxPlugin.RegistryURL}
Expand All @@ -187,7 +202,10 @@ func (cli *Cli) AnkaRegistryPull(workerCtx context.Context, pluginCtx context.Co
}
logger.DebugContext(pluginCtx, "pulling template to host")

metricsData := metrics.GetMetricsDataFromContext(workerCtx)
metricsData, err := metrics.GetMetricsDataFromContext(workerCtx)
if err != nil {
return nil, err
}

defer metricsData.SetStatus(pluginCtx, logger, "running")

Expand All @@ -205,7 +223,10 @@ func (cli *Cli) AnkaRegistryPull(workerCtx context.Context, pluginCtx context.Co
}

func (cli *Cli) AnkaDelete(workerCtx context.Context, pluginCtx context.Context, vm *VM) error {
logger := logging.GetLoggerFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return err
}
deleteOutput, err := cli.ExecuteParseJson(pluginCtx, "anka", "-j", "delete", "--yes", vm.Name)
if err != nil {
logger.ErrorContext(pluginCtx, "error executing anka delete", "err", err)
Expand All @@ -214,7 +235,10 @@ func (cli *Cli) AnkaDelete(workerCtx context.Context, pluginCtx context.Context,
}
logger.DebugContext(pluginCtx, "successfully deleted vm", "std", deleteOutput.Message)
// decrement total running VMs
metricsData := metrics.GetMetricsDataFromContext(workerCtx)
metricsData, err := metrics.GetMetricsDataFromContext(workerCtx)
if err != nil {
return err
}
metricsData.DecrementTotalRunningVMs()
return nil
}
Expand All @@ -223,7 +247,10 @@ func (cli *Cli) ObtainAnkaVM(workerCtx context.Context, pluginCtx context.Contex
if pluginCtx.Err() != nil {
return pluginCtx, nil, fmt.Errorf("context canceled before ObtainAnkaVMAndName")
}
logger := logging.GetLoggerFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return pluginCtx, nil, err
}
vmID, err := uuid.NewRandom()
if err != nil {
logger.ErrorContext(pluginCtx, "error creating uuid for vm name", "err", err)
Expand All @@ -245,7 +272,10 @@ func (cli *Cli) ObtainAnkaVM(workerCtx context.Context, pluginCtx context.Contex
return pluginCtx, vm, err
}
// increment total running VMs
metricsData := metrics.GetMetricsDataFromContext(workerCtx)
metricsData, err := metrics.GetMetricsDataFromContext(workerCtx)
if err != nil {
return pluginCtx, vm, err
}
metricsData.IncrementTotalRunningVMs()
return pluginCtx, vm, nil
}
Expand All @@ -254,8 +284,14 @@ func (cli *Cli) AnkaClone(pluginCtx context.Context, template string) error {
if pluginCtx.Err() != nil {
return fmt.Errorf("context canceled before AnkaClone")
}
logger := logging.GetLoggerFromContext(pluginCtx)
vm := GetAnkaVmFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return err
}
vm, err := GetAnkaVmFromContext(pluginCtx)
if err != nil {
return err
}
cloneOutput, err := cli.ExecuteParseJson(pluginCtx, "anka", "-j", "clone", template, vm.Name)
if err != nil {
return err
Expand All @@ -271,8 +307,14 @@ func (cli *Cli) AnkaStart(pluginCtx context.Context) error {
if pluginCtx.Err() != nil {
return fmt.Errorf("context canceled before AnkaStart")
}
logger := logging.GetLoggerFromContext(pluginCtx)
vm := GetAnkaVmFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return err
}
vm, err := GetAnkaVmFromContext(pluginCtx)
if err != nil {
return err
}
startOutput, err := cli.ExecuteParseJson(pluginCtx, "anka", "-j", "start", vm.Name)
if err != nil {
return err
Expand All @@ -288,8 +330,14 @@ func (cli *Cli) AnkaCopy(pluginCtx context.Context, filesToCopyIn ...string) err
if pluginCtx.Err() != nil {
return fmt.Errorf("context canceled before AnkaCopy")
}
logger := logging.GetLoggerFromContext(pluginCtx)
vm := GetAnkaVmFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return err
}
vm, err := GetAnkaVmFromContext(pluginCtx)
if err != nil {
return err
}
for _, hostLevelFile := range filesToCopyIn {
// handle symlinks
realPath, err := filepath.EvalSymlinks(hostLevelFile)
Expand All @@ -312,8 +360,14 @@ func (cli *Cli) AnkaCopy(pluginCtx context.Context, filesToCopyIn ...string) err
}

func HostHasVmCapacity(pluginCtx context.Context) bool {
logger := logging.GetLoggerFromContext(pluginCtx)
ankaCLI := GetAnkaCLIFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return false
}
ankaCLI, err := GetAnkaCLIFromContext(pluginCtx)
if err != nil {
return false
}
// check if there are already two VMS running or not
runningVMsList, err := ankaCLI.ExecuteParseJson(pluginCtx, "anka", "-j", "list", "-r")
if err != nil {
Expand All @@ -339,9 +393,18 @@ func HostHasVmCapacity(pluginCtx context.Context) bool {
}

func (cli *Cli) EnsureVMTemplateExists(workerCtx context.Context, pluginCtx context.Context, targetTemplate string, targetTag string) (error, error) {
logger := logging.GetLoggerFromContext(pluginCtx)
ankaCLI := GetAnkaCLIFromContext(pluginCtx)
globals := config.GetGlobalsFromContext(pluginCtx)
logger, err := logging.GetLoggerFromContext(pluginCtx)
if err != nil {
return nil, err
}
ankaCLI, err := GetAnkaCLIFromContext(pluginCtx)
if err != nil {
return nil, err
}
globals, err := config.GetGlobalsFromContext(pluginCtx)
if err != nil {
return nil, err
}
pullTemplate := false
list, err := ankaCLI.ExecuteParseJson(pluginCtx, "anka", "-j", "list", targetTemplate)
if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions internal/anka/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package anka

import (
"context"
"fmt"

"github.com/veertuinc/anklet/internal/config"
)
Expand All @@ -10,10 +11,10 @@ type VM struct {
Name string
}

func GetAnkaVmFromContext(ctx context.Context) *VM {
func GetAnkaVmFromContext(ctx context.Context) (*VM, error) {
ankaVm, ok := ctx.Value(config.ContextKey("ankavm")).(*VM)
if !ok {
panic("function GetAnkaVmFromContext failed")
return nil, fmt.Errorf("GetAnkaVmFromContext failed")
}
return ankaVm
return ankaVm, nil
}
31 changes: 16 additions & 15 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"fmt"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -191,12 +192,12 @@ func LoadInEnvs(config Config) (Config, error) {
return config, nil
}

func GetPluginFromContext(ctx context.Context) Plugin {
func GetPluginFromContext(ctx context.Context) (Plugin, error) {
plugin, ok := ctx.Value(ContextKey("plugin")).(Plugin)
if !ok {
panic("GetPluginFromContext failed")
return Plugin{}, fmt.Errorf("GetPluginFromContext failed")
}
return plugin
return plugin, nil
}

type Globals struct {
Expand All @@ -206,34 +207,34 @@ type Globals struct {
DebugEnabled bool
}

func GetGlobalsFromContext(ctx context.Context) Globals {
func GetGlobalsFromContext(ctx context.Context) (Globals, error) {
globals, ok := ctx.Value(ContextKey("globals")).(Globals)
if !ok {
panic("GetGlobalsFromContext failed")
return Globals{}, fmt.Errorf("GetGlobalsFromContext failed")
}
return globals
return globals, nil
}

func GetLoadedConfigFromContext(ctx context.Context) *Config {
func GetLoadedConfigFromContext(ctx context.Context) (*Config, error) {
config, ok := ctx.Value(ContextKey("config")).(*Config)
if !ok {
panic("GetLoadedConfigFromContext failed")
return nil, fmt.Errorf("GetLoadedConfigFromContext failed")
}
return config
return config, nil
}

func GetIsRepoSetFromContext(ctx context.Context) bool {
func GetIsRepoSetFromContext(ctx context.Context) (bool, error) {
isRepoSet, ok := ctx.Value(ContextKey("isRepoSet")).(bool)
if !ok {
panic("GetIsRepoSetFromContext failed")
return false, fmt.Errorf("GetIsRepoSetFromContext failed")
}
return isRepoSet
return isRepoSet, nil
}

func GetConfigFileNameFromContext(ctx context.Context) string {
func GetConfigFileNameFromContext(ctx context.Context) (string, error) {
configFileName, ok := ctx.Value(ContextKey("configFileName")).(string)
if !ok {
panic("GetConfigFileNameFromContext failed")
return "", fmt.Errorf("GetConfigFileNameFromContext failed")
}
return configFileName
return configFileName, nil
}
18 changes: 11 additions & 7 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,28 +57,32 @@ func GetDatabaseFromContext(ctx context.Context) (*Database, error) {
return database, nil
}

func UpdateUniqueRunKey(ctx context.Context, key string) context.Context {
func UpdateUniqueRunKey(ctx context.Context, key string) (context.Context, error) {
database, ok := ctx.Value(config.ContextKey("database")).(Database)
if !ok {
panic("database not found in context")
return ctx, fmt.Errorf("database not found in context")
}
database.UniqueRunKey = key
ctx = logging.AppendCtx(ctx, slog.String("uniqueRunKey", key))
return context.WithValue(ctx, config.ContextKey("database"), database)
return context.WithValue(ctx, config.ContextKey("database"), database), nil
}

func RemoveUniqueKeyFromDB(ctx context.Context) {
func RemoveUniqueKeyFromDB(ctx context.Context) (context.Context, error) {
database, ok := ctx.Value(config.ContextKey("database")).(Database)
if !ok {
panic("database not found in context")
return ctx, fmt.Errorf("database not found in context")
}
logging, err := logging.GetLoggerFromContext(ctx)
if err != nil {
return ctx, err
}
logging := logging.GetLoggerFromContext(ctx)
// we don't use ctx for the database deletion so we avoid getting the cancelled context state, which fails when Del runs
deletion, err := database.Client.Del(context.Background(), database.UniqueRunKey).Result()
if err != nil {
panic(err)
return nil, err
}
logging.DebugContext(ctx, fmt.Sprintf("removal of unique key %s from database returned %d (1 is success, 0 failed)", database.UniqueRunKey, deletion))
return ctx, nil
}

func CheckIfKeyExists(ctx context.Context, key string) (bool, error) {
Expand Down
Loading

0 comments on commit 1bdf680

Please sign in to comment.