-// fakeCloud provides an exec method that can be used as a
-// test.StubExecFunc. It calls the provided makeVM func when called
-// with a previously unseen instance ID. Calls to exec are passed on
-// to the *fakeVM for the appropriate instance ID.
-type fakeCloud struct {
- queue *test.Queue
- makeVM func(cloud.Instance) *fakeVM
- onComplete func(string)
- onCancel func(string)
- vms map[cloud.InstanceID]*fakeVM
- sync.Mutex
-}
-
-func (fc *fakeCloud) exec(inst cloud.Instance, command string, stdin io.Reader, stdout, stderr io.Writer) uint32 {
- fc.Lock()
- fvm, ok := fc.vms[inst.ID()]
- if !ok {
- if fc.vms == nil {
- fc.vms = make(map[cloud.InstanceID]*fakeVM)
- }
- fvm = fc.makeVM(inst)
- fc.vms[inst.ID()] = fvm
- }
- fc.Unlock()
- return fvm.exec(fc.queue, fc.onComplete, fc.onCancel, command, stdin, stdout, stderr)
-}
-
-// fakeVM is a fake VM with configurable delays and failure modes.
-type fakeVM struct {
- boot time.Time
- broken time.Time
- crunchRunMissing bool
- crunchRunCrashRate float64
- crunchRunDetachDelay time.Duration
- ctrExit int
- running map[string]bool
- completed []string
- sync.Mutex
-}
-
-func (fvm *fakeVM) exec(queue *test.Queue, onComplete, onCancel func(uuid string), command string, stdin io.Reader, stdout, stderr io.Writer) uint32 {
- uuid := regexp.MustCompile(`.{5}-dz642-.{15}`).FindString(command)
- if eta := fvm.boot.Sub(time.Now()); eta > 0 {
- fmt.Fprintf(stderr, "stub is booting, ETA %s\n", eta)
- return 1
- }
- if !fvm.broken.IsZero() && fvm.broken.Before(time.Now()) {
- fmt.Fprintf(stderr, "cannot fork\n")
- return 2
- }
- if fvm.crunchRunMissing && strings.Contains(command, "crunch-run") {
- fmt.Fprint(stderr, "crunch-run: command not found\n")
- return 1
- }
- if strings.HasPrefix(command, "crunch-run --detach ") {
- fvm.Lock()
- if fvm.running == nil {
- fvm.running = map[string]bool{}
- }
- fvm.running[uuid] = true
- fvm.Unlock()
- time.Sleep(fvm.crunchRunDetachDelay)
- fmt.Fprintf(stderr, "starting %s\n", uuid)
- logger := logrus.WithField("ContainerUUID", uuid)
- logger.Printf("[test] starting crunch-run stub")
- go func() {
- crashluck := rand.Float64()
- ctr, ok := queue.Get(uuid)
- if !ok {
- logger.Print("[test] container not in queue")
- return
- }
- if crashluck > fvm.crunchRunCrashRate/2 {
- time.Sleep(time.Duration(rand.Float64()*20) * time.Millisecond)
- ctr.State = arvados.ContainerStateRunning
- queue.Notify(ctr)
- }
-
- time.Sleep(time.Duration(rand.Float64()*20) * time.Millisecond)
- fvm.Lock()
- _, running := fvm.running[uuid]
- fvm.Unlock()
- if !running {
- logger.Print("[test] container was killed")
- return
- }
- // TODO: Check whether the stub instance has
- // been destroyed, and if so, don't call
- // onComplete. Then "container finished twice"
- // can be classified as a bug.
- if crashluck < fvm.crunchRunCrashRate {
- logger.Print("[test] crashing crunch-run stub")
- if onCancel != nil && ctr.State == arvados.ContainerStateRunning {
- onCancel(uuid)
- }
- } else {
- ctr.State = arvados.ContainerStateComplete
- ctr.ExitCode = fvm.ctrExit
- queue.Notify(ctr)
- if onComplete != nil {
- onComplete(uuid)
- }
- }
- logger.Print("[test] exiting crunch-run stub")
- fvm.Lock()
- defer fvm.Unlock()
- delete(fvm.running, uuid)
- }()
- return 0
- }
- if command == "crunch-run --list" {
- fvm.Lock()
- defer fvm.Unlock()
- for uuid := range fvm.running {
- fmt.Fprintf(stdout, "%s\n", uuid)
- }
- return 0
- }
- if strings.HasPrefix(command, "crunch-run --kill ") {
- fvm.Lock()
- defer fvm.Unlock()
- if fvm.running[uuid] {
- delete(fvm.running, uuid)
- } else {
- fmt.Fprintf(stderr, "%s: container is not running\n", uuid)
- }
- return 0
- }
- if command == "true" {
- return 0
- }
- fmt.Fprintf(stderr, "%q: command not found", command)
- return 1
-}
-