"strings"
"sync"
"syscall"
+ "time"
"git.arvados.org/arvados.git/lib/cmd"
"git.arvados.org/arvados.git/lib/config"
"git.arvados.org/arvados.git/lib/controller"
+ "git.arvados.org/arvados.git/lib/dispatchcloud"
"git.arvados.org/arvados.git/sdk/go/arvados"
"git.arvados.org/arvados.git/sdk/go/ctxlog"
"github.com/sirupsen/logrus"
// Fill in any missing config keys, and write the resulting
// config in the temp dir for child services to use.
- autofillConfig(cfg, log)
+ err = boot.autofillConfig(cfg, log)
+ if err != nil {
+ return 1
+ }
conffile, err := os.OpenFile(filepath.Join(tempdir, "config.yml"), os.O_CREATE|os.O_WRONLY, 0777)
if err != nil {
return 1
if err != nil {
return 1
}
+
+ var wg sync.WaitGroup
for _, cmpt := range []component{
- {name: "controller", svc: cluster.Services.Controller, cmdArgs: []string{"-config", conffile.Name()}, cmdHandler: controller.Command},
- // {name: "dispatchcloud", cmdArgs: []string{"-config", conffile.Name()}, cmdHandler: dispatchcloud.Command},
- {name: "railsAPI", svc: cluster.Services.RailsAPI, src: "services/api"},
+ {name: "controller", cmdHandler: controller.Command},
+ {name: "dispatchcloud", cmdHandler: dispatchcloud.Command, notIfTest: true},
+ {name: "keepproxy", goProg: "services/keepproxy"},
+ {name: "railsAPI", svc: cluster.Services.RailsAPI, railsApp: "services/api"},
} {
cmpt := cmpt
+ wg.Add(1)
go func() {
+ defer wg.Done()
+ defer cancel()
logger.WithField("component", cmpt.name).Info("starting")
err := cmpt.Run(ctx, boot, stdout, stderr)
if err != nil {
logger.WithError(err).WithField("component", cmpt.name).Info("exited")
}
- cancel()
}()
}
<-ctx.Done()
+ wg.Wait()
return 0
}
}
go func() {
<-ctx.Done()
- cmd.Process.Signal(syscall.SIGINT)
+ log := ctxlog.FromContext(ctx).WithFields(logrus.Fields{"dir": dir, "cmdline": cmdline})
+ for cmd.ProcessState != nil {
+ if cmd.Process == nil {
+ log.Infof("waiting for child process to start")
+ time.Sleep(time.Second)
+ } else {
+ cmd.Process.Signal(syscall.SIGINT)
+ log.WithField("PID", cmd.Process.Pid).Infof("waiting for child process to exit after SIGINT")
+ time.Sleep(5 * time.Second)
+ }
+ }
}()
err := cmd.Run()
if err != nil {
name string
svc arvados.Service
cmdHandler cmd.Handler
- cmdArgs []string
- src string // source dir in arvados tree, e.g., "services/keepstore"
+ railsApp string // source dir in arvados tree, e.g., "services/api"
+ goProg string // source dir in arvados tree, e.g., "services/keepstore"
+ notIfTest bool // don't run this component on a test cluster
}
func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stderr io.Writer) error {
+ if cmpt.notIfTest && boot.clusterType == "test" {
+ fmt.Fprintf(stderr, "skipping component %q\n", cmpt.name)
+ <-ctx.Done()
+ return nil
+ }
fmt.Fprintf(stderr, "starting component %q\n", cmpt.name)
if cmpt.cmdHandler != nil {
- exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, cmpt.cmdArgs, bytes.NewBuffer(nil), stdout, stderr)
- if exitcode != 0 {
- return fmt.Errorf("exit code %d", exitcode)
+ errs := make(chan error, 1)
+ go func() {
+ defer close(errs)
+ exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, nil, bytes.NewBuffer(nil), stdout, stderr)
+ if exitcode != 0 {
+ errs <- fmt.Errorf("exit code %d", exitcode)
+ }
+ }()
+ select {
+ case err := <-errs:
+ return err
+ case <-ctx.Done():
+ // cmpt.cmdHandler.RunCommand() doesn't have
+ // access to our context, so it won't shut
+ // down by itself. We just abandon it.
+ return nil
}
- return nil
}
- if cmpt.src != "" {
+ if cmpt.goProg != "" {
+ return boot.RunProgram(ctx, cmpt.goProg, nil, nil, "go", "run", ".")
+ }
+ if cmpt.railsApp != "" {
port := "-"
for u := range cmpt.svc.InternalURLs {
if _, p, err := net.SplitHostPort(u.Host); err != nil {
return err
}
var buf bytes.Buffer
- err = boot.RunProgram(ctx, cmpt.src, &buf, nil, "gem", "list", "--details", "bundler")
+ err = boot.RunProgram(ctx, cmpt.railsApp, &buf, nil, "gem", "list", "--details", "bundler")
if err != nil {
return err
}
for _, version := range []string{"1.11.0", "1.17.3", "2.0.2"} {
if !strings.Contains(buf.String(), "("+version+")") {
- err = boot.RunProgram(ctx, cmpt.src, nil, nil, "gem", "install", "--user", "bundler:1.11", "bundler:1.17.3", "bundler:2.0.2")
+ err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "gem", "install", "--user", "bundler:1.11", "bundler:1.17.3", "bundler:2.0.2")
if err != nil {
return err
}
break
}
}
- err = boot.RunProgram(ctx, cmpt.src, nil, nil, "bundle", "install", "--jobs", "4", "--path", filepath.Join(os.Getenv("HOME"), ".gem"))
+ err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "install", "--jobs", "4", "--path", filepath.Join(os.Getenv("HOME"), ".gem"))
if err != nil {
return err
}
- err = boot.RunProgram(ctx, cmpt.src, nil, nil, "bundle", "exec", "passenger-config", "build-native-support")
+ err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "exec", "passenger-config", "build-native-support")
if err != nil {
return err
}
- err = boot.RunProgram(ctx, cmpt.src, nil, nil, "bundle", "exec", "passenger-config", "install-standalone-runtime")
+ err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "exec", "passenger-config", "install-standalone-runtime")
if err != nil {
return err
}
- err = boot.RunProgram(ctx, cmpt.src, nil, nil, "bundle", "exec", "passenger-config", "validate-install")
+ err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "exec", "passenger-config", "validate-install")
if err != nil {
return err
}
- err = boot.RunProgram(ctx, cmpt.src, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
+ err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
if err != nil {
return err
}
return fmt.Errorf("bug: component %q has nothing to run", cmpt.name)
}
-func autofillConfig(cfg *arvados.Config, log logrus.FieldLogger) {
+func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger) error {
cluster, err := cfg.GetCluster("")
if err != nil {
- panic(err)
+ return err
}
port := 9000
for _, svc := range []*arvados.Service{
if cluster.Collections.BlobSigningKey == "" {
cluster.Collections.BlobSigningKey = randomHexString(64)
}
+ if boot.clusterType != "production" && cluster.Containers.DispatchPrivateKey == "" {
+ buf, err := ioutil.ReadFile(filepath.Join(boot.sourcePath, "lib", "dispatchcloud", "test", "sshkey_dispatch"))
+ if err != nil {
+ return err
+ }
+ cluster.Containers.DispatchPrivateKey = string(buf)
+ }
cfg.Clusters[cluster.ClusterID] = *cluster
+ return nil
}
func randomHexString(chars int) string {