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 }