go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/spantest/spantest_docker.go (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build !linux 16 // +build !linux 17 18 package spantest 19 20 import ( 21 "context" 22 "fmt" 23 "math/rand" 24 "os" 25 "os/exec" 26 "time" 27 28 "go.chromium.org/luci/common/errors" 29 30 "go.chromium.org/luci/common/system/filesystem" 31 "go.chromium.org/luci/common/system/port" 32 ) 33 34 // StartEmulator starts a Cloud Spanner Emulator instance. 35 func StartEmulator(ctx context.Context) (*Emulator, error) { 36 rand.Seed(time.Now().UTC().UnixNano()) 37 38 p, err := port.PickUnusedPort() 39 if err != nil { 40 return nil, errors.Annotate(err, "picking port").Err() 41 } 42 43 hostport := fmt.Sprintf("localhost:%d", p) 44 e := &Emulator{ 45 hostport: hostport, 46 containerName: fmt.Sprintf("%d", rand.Int63()), 47 } 48 49 if e.cfgDir, err = e.createSpannerEmulatorConfig(); err != nil { 50 return nil, err 51 } 52 53 ctx, e.cancel = context.WithCancel(ctx) 54 e.cmd = exec.CommandContext( 55 ctx, "docker", "run", "-p", fmt.Sprintf("127.0.0.1:%d:9010", p), "--name", e.containerName, "gcr.io/cloud-spanner-emulator/emulator") 56 e.cmd.Env = append(e.cmd.Env, 57 fmt.Sprintf("SPANNER_EMULATOR_HOST=%s", e.hostport), 58 fmt.Sprintf("CLOUDSDK_CONFIG=%s", e.cfgDir), 59 fmt.Sprintf("PATH=%s", os.Getenv("PATH"))) 60 61 e.cmd.Stdout = os.Stdout 62 e.cmd.Stderr = os.Stderr 63 64 if err = e.cmd.Start(); err != nil { 65 return nil, err 66 } 67 68 return e, nil 69 } 70 71 // Stop kills the emulator process and removes the temporary gcloud config directory. 72 func (e *Emulator) Stop() error { 73 if e.cmd != nil { 74 // if err := e.cmd.Process.Kill(); err != nil { 75 // return err 76 // } 77 e.cancel() 78 e.cmd = nil 79 80 // Try to explicitly stop the container, because killing `docker run` 81 // via context cancellation may not do it. 82 stopCmd := exec.Command("docker", "stop", e.containerName) 83 stopCmd.Start() 84 return stopCmd.Wait() 85 } 86 87 if e.cfgDir != "" { 88 if err := filesystem.RemoveAll(e.cfgDir); err == nil { 89 return errors.Annotate(err, "failed to remove the temporary config directory").Err() 90 } 91 e.cfgDir = "" 92 } 93 return nil 94 }