github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/cmd/chore/e2e/run/run.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"reflect"
     9  	"runtime"
    10  	"strings"
    11  	"time"
    12  
    13  	"gopkg.in/yaml.v3"
    14  
    15  	"github.com/afiskon/promtail-client/promtail"
    16  )
    17  
    18  var _ runFunc = run
    19  
    20  func run(ctx context.Context, settings programSettings) error {
    21  
    22  	newOrbctl, err := buildOrbctl(ctx, settings)
    23  	if err != nil {
    24  		return err
    25  	}
    26  
    27  	if settings.cleanup {
    28  		defer func() {
    29  			// context is probably cancelled as program is terminating so we create a new one here
    30  			destroyCtx, destroyCancel := context.WithTimeout(context.Background(), 10*time.Minute)
    31  			defer destroyCancel()
    32  			if cuErr := destroy(&testSpecs{}, settings, zeroConditions())(destroyCtx, 99, newOrbctl); cuErr != nil {
    33  
    34  				original := ""
    35  				if err != nil {
    36  					original = fmt.Sprintf(": %s", err.Error())
    37  				}
    38  
    39  				err = fmt.Errorf("cleaning up after end-to-end test failed: %w%s", cuErr, original)
    40  			}
    41  		}()
    42  	}
    43  
    44  	kubeconfig, err := ioutil.TempFile("", "kubeconfig-*")
    45  	if err != nil {
    46  		return err
    47  	}
    48  	if err := kubeconfig.Close(); err != nil {
    49  		return err
    50  	}
    51  
    52  	readKubeconfig, deleteKubeconfig := downloadKubeconfigFunc(settings, kubeconfig.Name())
    53  	defer deleteKubeconfig()
    54  
    55  	return seq(ctx, testSpecs{
    56  		DesireORBITERState: struct {
    57  			InitialMasters int
    58  			InitialWorkers int
    59  		}{
    60  			InitialMasters: 3,
    61  			InitialWorkers: 3,
    62  		},
    63  	}, settings, newOrbctl, configureKubectl(kubeconfig.Name()), readKubeconfig,
    64  		/*  1 */ desireORBITERState,
    65  		/*  2 */ destroy,
    66  		/*  3 */ desireORBITERState,
    67  		/*  4 */ bootstrap,
    68  		/*  5 */ desireBOOMState(true),
    69  		/*  6 */ downscale,
    70  		/*  7 */ reboot,
    71  		/*  8 */ replace,
    72  		/*  9 */ upgrade("v1.21.0"),
    73  	)
    74  }
    75  
    76  var _ fmt.Stringer = (*programSettings)(nil)
    77  
    78  type programSettings struct {
    79  	ctx                          context.Context
    80  	logger                       promtail.Client
    81  	orbID, branch, orbconfig     string
    82  	from                         uint8
    83  	cleanup, debugOrbctlCommands bool
    84  	cache                        struct {
    85  		artifactsVersion string
    86  	}
    87  }
    88  
    89  func (s programSettings) artifactsVersion() string {
    90  	if s.cache.artifactsVersion != "" {
    91  		return s.cache.artifactsVersion
    92  	}
    93  	branchParts := strings.Split(s.branch, "/")
    94  	s.cache.artifactsVersion = branchParts[len(branchParts)-1:][0] + "-dev"
    95  	return s.cache.artifactsVersion
    96  }
    97  
    98  func (p *programSettings) String() string {
    99  	return fmt.Sprintf(`orbconfig=%s
   100  orb=%s
   101  branch=%s
   102  from=%d
   103  cleanup=%t`,
   104  		p.orbconfig,
   105  		p.orbID,
   106  		p.branch,
   107  		p.from,
   108  		p.cleanup,
   109  	)
   110  }
   111  
   112  type testSpecs struct {
   113  	DesireORBITERState struct {
   114  		InitialMasters int
   115  		InitialWorkers int
   116  	}
   117  	DesireBOOMState struct {
   118  		Stateless bool
   119  	}
   120  }
   121  
   122  type testFunc func(*testSpecs, programSettings, *conditions) interactFunc
   123  
   124  type interactFunc func(context.Context, uint8, newOrbctlCommandFunc) (err error)
   125  
   126  func seq(
   127  	ctx context.Context,
   128  	defaultSpecs testSpecs,
   129  	settings programSettings,
   130  	newOrbctl newOrbctlCommandFunc,
   131  	newKubectl newKubectlCommandFunc,
   132  	downloadKubeconfigFunc downloadKubeconfig,
   133  	fns ...testFunc,
   134  ) error {
   135  
   136  	conditions := zeroConditions()
   137  
   138  	e2eSpecBuf := new(bytes.Buffer)
   139  	defer e2eSpecBuf.Reset()
   140  
   141  	if err := runCommand(settings, nil, e2eSpecBuf, nil, newOrbctl(ctx), "--gitops", "file", "print", "e2e.yml"); err != nil {
   142  		return err
   143  	}
   144  
   145  	if err := yaml.Unmarshal(e2eSpecBuf.Bytes(), &defaultSpecs); err != nil {
   146  		return err
   147  	}
   148  
   149  	var at uint8
   150  	for _, fn := range fns {
   151  		at++
   152  
   153  		// must be called before continue so we keep having an idempotent desired state
   154  		interactFn := fn(&defaultSpecs, settings, conditions)
   155  
   156  		if at < settings.from {
   157  			settings.logger.Infof("\033[1;32m%s: Skipping step %d\033[0m\n", settings.orbID, at)
   158  			continue
   159  		}
   160  
   161  		if err := runTest(ctx, settings, interactFn, newOrbctl, newKubectl, downloadKubeconfigFunc, at, conditions); err != nil {
   162  			return err
   163  		}
   164  	}
   165  	return nil
   166  }
   167  
   168  func runTest(
   169  	ctx context.Context,
   170  	settings programSettings,
   171  	fn interactFunc,
   172  	orbctl newOrbctlCommandFunc,
   173  	kubectl newKubectlCommandFunc,
   174  	downloadKubeconfigFunc downloadKubeconfig,
   175  	step uint8,
   176  	conditions *conditions,
   177  ) (err error) {
   178  	fnName := fmt.Sprintf("%s (%d. in stack)", runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name(), step)
   179  
   180  	defer func() {
   181  		if err != nil {
   182  			err = fmt.Errorf("%s failed: %w", fnName, err)
   183  		} else {
   184  			settings.logger.Infof("\033[1;32m%s: %s succeeded\033[0m\n", settings.orbID, fnName)
   185  		}
   186  	}()
   187  
   188  	testContext, testCancel := context.WithCancel(ctx)
   189  	defer testCancel()
   190  
   191  	if err := fn(testContext, step, orbctl); err != nil {
   192  		return err
   193  	}
   194  
   195  	var awaitFuncs []func() error
   196  
   197  	appendAwaitFunc := func(condition *condition) {
   198  		if condition != nil {
   199  			awaitFuncs = append(awaitFuncs, func() error {
   200  				return awaitCondition(testContext, settings, orbctl, kubectl, downloadKubeconfigFunc, step, condition)
   201  			})
   202  		}
   203  	}
   204  
   205  	appendAwaitFunc(conditions.testCase)
   206  	appendAwaitFunc(conditions.orbiter)
   207  	appendAwaitFunc(conditions.boom)
   208  
   209  	for _, awaitFunc := range awaitFuncs {
   210  		if err := awaitFunc(); err != nil {
   211  			return err
   212  		}
   213  	}
   214  
   215  	return nil
   216  }