[ARVADOS] created: 1.3.0-2114-ge20ed15ea
Git user
git at public.arvados.org
Tue Jan 28 19:36:28 UTC 2020
at e20ed15ea23390163ea262b38eff8403b7c9edf5 (commit)
commit e20ed15ea23390163ea262b38eff8403b7c9edf5
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jan 28 14:35:47 2020 -0500
15954: Fix missing return.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index d0a566d6b..6b4200a72 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -388,11 +388,11 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
}()
}
wg.Wait()
- return nil
} else {
// Just run one
boot.RunProgram(ctx, boot.tempdir, nil, nil, basename)
}
+ return nil
}
if cmpt.runFunc != nil {
return cmpt.runFunc(ctx, boot, stdout, stderr)
commit 0359ad44cb9bece93d0a9b1f131ec1fac7916d63
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jan 28 11:54:17 2020 -0500
15954: Avoid modifying caller's global environment.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index cfa6b4258..d0a566d6b 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -44,7 +44,9 @@ type bootCommand struct {
stdout io.Writer
stderr io.Writer
- tempdir string
+ tempdir string
+ configfile string
+ environ []string // for child processes
setupRubyOnce sync.Once
setupRubyErr error
@@ -127,9 +129,12 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
if err != nil {
return 1
}
- os.Setenv("ARVADOS_CONFIG", conffile.Name())
- arvados.DefaultConfigFile = conffile.Name()
- os.Setenv("RAILS_ENV", boot.clusterType)
+ boot.configfile = conffile.Name()
+
+ boot.environ = os.Environ()
+ boot.setEnv("ARVADOS_CONFIG", boot.configfile)
+ boot.setEnv("RAILS_ENV", boot.clusterType)
+ boot.prependEnv("PATH", filepath.Join(boot.libPath, "bin")+":")
// Now that we have the config, replace the bootstrap logger
// with a new one according to the logging config.
@@ -146,7 +151,7 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
defer cancel()
ch := make(chan os.Signal)
- signal.Notify(ch, syscall.SIGINT)
+ signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
go func() {
for sig := range ch {
logger.WithField("signal", sig).Info("caught signal")
@@ -164,12 +169,14 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
return 1
}
}
- os.Setenv("PATH", filepath.Join(boot.libPath, "bin")+":"+os.Getenv("PATH"))
-
err = boot.installGoProgram(ctx, "cmd/arvados-server")
if err != nil {
return 1
}
+ err = boot.setupRubyEnv()
+ if err != nil {
+ return 1
+ }
var wg sync.WaitGroup
for _, cmpt := range []component{
@@ -228,6 +235,26 @@ func (boot *bootCommand) waitUntilReady(ctx context.Context) bool {
return true
}
+func (boot *bootCommand) prependEnv(key, prepend string) {
+ for i, s := range boot.environ {
+ if strings.HasPrefix(s, key+"=") {
+ boot.environ[i] = key + "=" + prepend + s[len(key)+1:]
+ return
+ }
+ }
+ boot.environ = append(boot.environ, key+"="+prepend)
+}
+
+func (boot *bootCommand) setEnv(key, val string) {
+ for i, s := range boot.environ {
+ if strings.HasPrefix(s, key+"=") {
+ boot.environ[i] = key + "=" + val
+ return
+ }
+ }
+ boot.environ = append(boot.environ, key+"="+val)
+}
+
func (boot *bootCommand) installGoProgram(ctx context.Context, srcpath string) error {
boot.goMutex.Lock()
defer boot.goMutex.Unlock()
@@ -235,17 +262,29 @@ func (boot *bootCommand) installGoProgram(ctx context.Context, srcpath string) e
}
func (boot *bootCommand) setupRubyEnv() error {
- boot.setupRubyOnce.Do(func() {
- buf, err := exec.Command("gem", "env", "gempath").Output() // /var/lib/arvados/.gem/ruby/2.5.0/bin:...
- if err != nil || len(buf) == 0 {
- boot.setupRubyErr = fmt.Errorf("gem env gempath: %v", err)
+ buf, err := exec.Command("gem", "env", "gempath").Output() // /var/lib/arvados/.gem/ruby/2.5.0/bin:...
+ if err != nil || len(buf) == 0 {
+ return fmt.Errorf("gem env gempath: %v", err)
+ }
+ gempath := string(bytes.Split(buf, []byte{':'})[0])
+ boot.prependEnv("PATH", gempath+"/bin:")
+ boot.setEnv("GEM_HOME", gempath)
+ boot.setEnv("GEM_PATH", gempath)
+ return nil
+}
+
+func (boot *bootCommand) lookPath(prog string) string {
+ for _, val := range boot.environ {
+ if strings.HasPrefix(val, "PATH=") {
+ for _, dir := range filepath.SplitList(val[5:]) {
+ path := filepath.Join(dir, prog)
+ if fi, err := os.Stat(path); err == nil && fi.Mode()&0111 != 0 {
+ return path
+ }
+ }
}
- gempath := string(bytes.Split(buf, []byte{':'})[0])
- os.Setenv("PATH", gempath+"/bin:"+os.Getenv("PATH"))
- os.Setenv("GEM_HOME", gempath)
- os.Setenv("GEM_PATH", gempath)
- })
- return boot.setupRubyErr
+ }
+ return prog
}
// Run prog with args, using dir as working directory. If ctx is
@@ -259,7 +298,7 @@ func (boot *bootCommand) setupRubyEnv() error {
func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.Writer, env []string, prog string, args ...string) error {
cmdline := fmt.Sprintf("%s", append([]string{prog}, args...))
fmt.Fprintf(boot.stderr, "%s executing in %s\n", cmdline, dir)
- cmd := exec.Command(prog, args...)
+ cmd := exec.Command(boot.lookPath(prog), args...)
if output == nil {
cmd.Stdout = boot.stderr
} else {
@@ -271,9 +310,7 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
} else {
cmd.Dir = filepath.Join(boot.sourcePath, dir)
}
- if env != nil {
- cmd.Env = append(env, os.Environ()...)
- }
+ cmd.Env = append(env, boot.environ...)
go func() {
<-ctx.Done()
log := ctxlog.FromContext(ctx).WithFields(logrus.Fields{"dir": dir, "cmdline": cmdline})
@@ -318,7 +355,7 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
errs := make(chan error, 1)
go func() {
defer close(errs)
- exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, nil, bytes.NewBuffer(nil), stdout, stderr)
+ exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, []string{"-config", boot.configfile}, bytes.NewBuffer(nil), stdout, stderr)
if exitcode != 0 {
errs <- fmt.Errorf("exit code %d", exitcode)
}
@@ -365,10 +402,6 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
if err != nil {
return fmt.Errorf("bug: no InternalURLs for component %q: %v", cmpt.name, cmpt.svc.InternalURLs)
}
- err = boot.setupRubyEnv()
- if err != nil {
- return err
- }
var buf bytes.Buffer
err = boot.RunProgram(ctx, cmpt.railsApp, &buf, nil, "gem", "list", "--details", "bundler")
if err != nil {
commit 071208be8ec20519c8fb05fe7ac0563e55e55b5b
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jan 28 11:03:08 2020 -0500
15954: Write controller URL to stdout when cluster is ready.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 992c7c6e5..cfa6b4258 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -29,6 +29,7 @@ import (
"git.arvados.org/arvados.git/lib/dispatchcloud"
"git.arvados.org/arvados.git/sdk/go/arvados"
"git.arvados.org/arvados.git/sdk/go/ctxlog"
+ "git.arvados.org/arvados.git/sdk/go/health"
"github.com/sirupsen/logrus"
)
@@ -196,11 +197,37 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
}
}()
}
+ if boot.waitUntilReady(ctx) {
+ fmt.Fprintln(stdout, boot.cluster.Services.Controller.ExternalURL)
+ }
<-ctx.Done()
wg.Wait()
return 0
}
+func (boot *bootCommand) waitUntilReady(ctx context.Context) bool {
+ agg := health.Aggregator{Cluster: boot.cluster}
+ for waiting := true; waiting; {
+ time.Sleep(time.Second)
+ if ctx.Err() != nil {
+ return false
+ }
+ resp := agg.ClusterHealth()
+ // The overall health check (resp.Health=="OK") might
+ // never pass due to missing components (like
+ // arvados-dispatch-cloud in a test cluster), so
+ // instead we wait for all configured components to
+ // pass.
+ waiting = false
+ for _, check := range resp.Checks {
+ if check.Health != "OK" {
+ waiting = true
+ }
+ }
+ }
+ return true
+}
+
func (boot *bootCommand) installGoProgram(ctx context.Context, srcpath string) error {
boot.goMutex.Lock()
defer boot.goMutex.Unlock()
diff --git a/sdk/go/health/aggregator.go b/sdk/go/health/aggregator.go
index 90823b38b..a0284e8f2 100644
--- a/sdk/go/health/aggregator.go
+++ b/sdk/go/health/aggregator.go
@@ -106,6 +106,7 @@ type ServiceHealth struct {
}
func (agg *Aggregator) ClusterHealth() ClusterHealthResponse {
+ agg.setupOnce.Do(agg.setup)
resp := ClusterHealthResponse{
Health: "OK",
Checks: make(map[string]CheckResult),
commit 3d18d409e0bfe9ed4571ae6f43191c215a692dab
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jan 28 10:56:17 2020 -0500
15954: Add comments.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 7f513310d..992c7c6e5 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -221,6 +221,14 @@ func (boot *bootCommand) setupRubyEnv() error {
return boot.setupRubyErr
}
+// Run prog with args, using dir as working directory. If ctx is
+// cancelled while the child is running, RunProgram terminates the
+// child, waits for it to exit, then returns.
+//
+// Child's environment will have our env vars, plus any given in env.
+//
+// Child's stdout will be written to output if non-nil, otherwise the
+// boot command's stderr.
func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.Writer, env []string, prog string, args ...string) error {
cmdline := fmt.Sprintf("%s", append([]string{prog}, args...))
fmt.Fprintf(boot.stderr, "%s executing in %s\n", cmdline, dir)
@@ -243,6 +251,7 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
<-ctx.Done()
log := ctxlog.FromContext(ctx).WithFields(logrus.Fields{"dir": dir, "cmdline": cmdline})
for cmd.ProcessState == nil {
+ // Child hasn't exited yet
if cmd.Process == nil {
log.Infof("waiting for child process to start")
time.Sleep(time.Second)
@@ -431,6 +440,7 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
cluster.TLS.Insecure = true
}
if boot.clusterType == "test" {
+ // Add a second keepstore process.
port++
cluster.Services.Keepstore.InternalURLs[arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", port)}] = arvados.ServiceInstance{}
commit d81316e711dd9966e62ac6b5444e8d2aa12edfaa
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jan 28 10:51:55 2020 -0500
15954: Fix test-mode volume config.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 633dbdb0b..7f513310d 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -434,23 +434,23 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
port++
cluster.Services.Keepstore.InternalURLs[arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", port)}] = arvados.ServiceInstance{}
- n := -1
+ // Create a directory-backed volume for each keepstore
+ // process.
+ cluster.Volumes = map[string]arvados.Volume{}
for url := range cluster.Services.Keepstore.InternalURLs {
- n++
- datadir := fmt.Sprintf("%s/keep%d.data", boot.tempdir, n)
+ volnum := len(cluster.Volumes)
+ datadir := fmt.Sprintf("%s/keep%d.data", boot.tempdir, volnum)
if _, err = os.Stat(datadir + "/."); err == nil {
} else if !os.IsNotExist(err) {
return err
} else if err = os.Mkdir(datadir, 0777); err != nil {
return err
}
- cluster.Volumes = map[string]arvados.Volume{
- fmt.Sprintf("zzzzz-nyw5e-%015d", n): arvados.Volume{
- Driver: "Directory",
- DriverParameters: json.RawMessage(fmt.Sprintf(`{"Root":%q}`, datadir)),
- AccessViaHosts: map[arvados.URL]arvados.VolumeAccess{
- url: {},
- },
+ cluster.Volumes[fmt.Sprintf("zzzzz-nyw5e-%015d", volnum)] = arvados.Volume{
+ Driver: "Directory",
+ DriverParameters: json.RawMessage(fmt.Sprintf(`{"Root":%q}`, datadir)),
+ AccessViaHosts: map[arvados.URL]arvados.VolumeAccess{
+ url: {},
},
}
}
commit f7a22b6b70839d2fd4b49ef1ad1d96701f30ff6c
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jan 27 16:10:52 2020 -0500
15954: Eliminate intermediate go process.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index e0e357e1e..633dbdb0b 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -247,8 +247,9 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
log.Infof("waiting for child process to start")
time.Sleep(time.Second)
} else {
+ log.WithField("PID", cmd.Process.Pid).Info("sending SIGTERM")
cmd.Process.Signal(syscall.SIGTERM)
- log.WithField("PID", cmd.Process.Pid).Infof("waiting for child process to exit after SIGTERM")
+ log.WithField("PID", cmd.Process.Pid).Info("waiting for child process to exit after SIGTERM")
time.Sleep(5 * time.Second)
}
}
@@ -297,6 +298,11 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
}
}
if cmpt.goProg != "" {
+ boot.RunProgram(ctx, cmpt.goProg, nil, nil, "go", "install")
+ if ctx.Err() != nil {
+ return nil
+ }
+ _, basename := filepath.Split(cmpt.goProg)
if len(cmpt.svc.InternalURLs) > 0 {
// Run one for each URL
var wg sync.WaitGroup
@@ -305,14 +311,14 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
wg.Add(1)
go func() {
defer wg.Done()
- boot.RunProgram(ctx, cmpt.goProg, nil, []string{"ARVADOS_SERVICE_INTERNAL_URL=" + u.String()}, "go", "run", ".")
+ boot.RunProgram(ctx, boot.tempdir, nil, []string{"ARVADOS_SERVICE_INTERNAL_URL=" + u.String()}, basename)
}()
}
wg.Wait()
return nil
} else {
// Just run one
- return boot.RunProgram(ctx, cmpt.goProg, nil, nil, "go", "run", ".")
+ boot.RunProgram(ctx, boot.tempdir, nil, nil, basename)
}
}
if cmpt.runFunc != nil {
commit 570a9f8e5504d518b118952098b95f11761dda18
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jan 27 16:10:22 2020 -0500
15954: Start websocket and keepstore.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 361f64505..e0e357e1e 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -179,8 +179,10 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
{name: "health", goProg: "services/health"},
{name: "keep-balance", goProg: "services/keep-balance", notIfTest: true},
{name: "keepproxy", goProg: "services/keepproxy"},
+ {name: "keepstore", goProg: "services/keepstore", svc: boot.cluster.Services.Keepstore},
{name: "keep-web", goProg: "services/keep-web"},
{name: "railsAPI", svc: boot.cluster.Services.RailsAPI, railsApp: "services/api"},
+ {name: "ws", goProg: "services/ws"},
} {
cmpt := cmpt
wg.Add(1)
@@ -202,8 +204,7 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
func (boot *bootCommand) installGoProgram(ctx context.Context, srcpath string) error {
boot.goMutex.Lock()
defer boot.goMutex.Unlock()
- env := append([]string{"GOPATH=" + boot.libPath}, os.Environ()...)
- return boot.RunProgram(ctx, filepath.Join(boot.sourcePath, srcpath), nil, env, "go", "install")
+ return boot.RunProgram(ctx, filepath.Join(boot.sourcePath, srcpath), nil, []string{"GOPATH=" + boot.libPath}, "go", "install")
}
func (boot *bootCommand) setupRubyEnv() error {
@@ -236,7 +237,7 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
cmd.Dir = filepath.Join(boot.sourcePath, dir)
}
if env != nil {
- cmd.Env = env
+ cmd.Env = append(env, os.Environ()...)
}
go func() {
<-ctx.Done()
@@ -296,7 +297,23 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
}
}
if cmpt.goProg != "" {
- return boot.RunProgram(ctx, cmpt.goProg, nil, nil, "go", "run", ".")
+ if len(cmpt.svc.InternalURLs) > 0 {
+ // Run one for each URL
+ var wg sync.WaitGroup
+ for u := range cmpt.svc.InternalURLs {
+ u := u
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ boot.RunProgram(ctx, cmpt.goProg, nil, []string{"ARVADOS_SERVICE_INTERNAL_URL=" + u.String()}, "go", "run", ".")
+ }()
+ }
+ wg.Wait()
+ return nil
+ } else {
+ // Just run one
+ return boot.RunProgram(ctx, cmpt.goProg, nil, nil, "go", "run", ".")
+ }
}
if cmpt.runFunc != nil {
return cmpt.runFunc(ctx, boot, stdout, stderr)
@@ -407,12 +424,29 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
if boot.clusterType != "production" {
cluster.TLS.Insecure = true
}
- if boot.clusterType == "test" && len(cluster.Volumes) == 0 {
- cluster.Volumes = map[string]arvados.Volume{
- "zzzzz-nyw5e-000000000000000": arvados.Volume{
- Driver: "Directory",
- DriverParameters: json.RawMessage(fmt.Sprintf(`{"Root":%q}`, boot.tempdir+"/keep0")),
- },
+ if boot.clusterType == "test" {
+ port++
+ cluster.Services.Keepstore.InternalURLs[arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", port)}] = arvados.ServiceInstance{}
+
+ n := -1
+ for url := range cluster.Services.Keepstore.InternalURLs {
+ n++
+ datadir := fmt.Sprintf("%s/keep%d.data", boot.tempdir, n)
+ if _, err = os.Stat(datadir + "/."); err == nil {
+ } else if !os.IsNotExist(err) {
+ return err
+ } else if err = os.Mkdir(datadir, 0777); err != nil {
+ return err
+ }
+ cluster.Volumes = map[string]arvados.Volume{
+ fmt.Sprintf("zzzzz-nyw5e-%015d", n): arvados.Volume{
+ Driver: "Directory",
+ DriverParameters: json.RawMessage(fmt.Sprintf(`{"Root":%q}`, datadir)),
+ AccessViaHosts: map[arvados.URL]arvados.VolumeAccess{
+ url: {},
+ },
+ },
+ }
}
}
cfg.Clusters[cluster.ClusterID] = *cluster
diff --git a/lib/service/cmd.go b/lib/service/cmd.go
index f1f3fd91d..48912b889 100644
--- a/lib/service/cmd.go
+++ b/lib/service/cmd.go
@@ -12,6 +12,7 @@ import (
"io"
"net"
"net/http"
+ "net/url"
"os"
"strings"
@@ -164,6 +165,14 @@ func getListenAddr(svcs arvados.Services, prog arvados.ServiceName, log logrus.F
if !ok {
return arvados.URL{}, fmt.Errorf("unknown service name %q", prog)
}
+
+ if want := os.Getenv("ARVADOS_SERVICE_INTERNAL_URL"); want == "" {
+ } else if url, err := url.Parse(want); err != nil {
+ return arvados.URL{}, fmt.Errorf("$ARVADOS_SERVICE_INTERNAL_URL (%q): %s", want, err)
+ } else {
+ return arvados.URL(*url), nil
+ }
+
errors := []string{}
for url := range svc.InternalURLs {
listener, err := net.Listen("tcp", url.Host)
commit 59b55da81d54d287a39e24aa7d5187371bf0001b
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jan 27 11:39:34 2020 -0500
15954: Add ping check for health aggregator.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/sdk/go/health/aggregator.go b/sdk/go/health/aggregator.go
index a1ef5e0be..90823b38b 100644
--- a/sdk/go/health/aggregator.go
+++ b/sdk/go/health/aggregator.go
@@ -62,11 +62,14 @@ func (agg *Aggregator) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
sendErr(http.StatusUnauthorized, errUnauthorized)
return
}
- if req.URL.Path != "/_health/all" {
+ if req.URL.Path == "/_health/all" {
+ json.NewEncoder(resp).Encode(agg.ClusterHealth())
+ } else if req.URL.Path == "/_health/ping" {
+ resp.Write(healthyBody)
+ } else {
sendErr(http.StatusNotFound, errNotFound)
return
}
- json.NewEncoder(resp).Encode(agg.ClusterHealth())
if agg.Log != nil {
agg.Log(req, nil)
}
commit 1cb783ed951c9160f22680b2620c6f32b581582f
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jan 27 10:22:02 2020 -0500
15954: Don't assign ports for dispatchcloud in test mode.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 8d20e5f6c..361f64505 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -366,6 +366,9 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
&cluster.Services.WebDAVDownload,
&cluster.Services.Websocket,
} {
+ if svc == &cluster.Services.DispatchCloud && boot.clusterType == "test" {
+ continue
+ }
if len(svc.InternalURLs) == 0 {
port++
svc.InternalURLs = map[arvados.URL]arvados.ServiceInstance{
commit 455040905b4e1ccbb107a1e06766043a9540b871
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jan 27 10:21:06 2020 -0500
15954: Fix wrong token expected at keepproxy health check endpoint.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/services/keepproxy/keepproxy.go b/services/keepproxy/keepproxy.go
index 58e4a8534..2b15d7994 100644
--- a/services/keepproxy/keepproxy.go
+++ b/services/keepproxy/keepproxy.go
@@ -157,7 +157,7 @@ func run(logger log.FieldLogger, cluster *arvados.Cluster) error {
signal.Notify(term, syscall.SIGINT)
// Start serving requests.
- router = MakeRESTRouter(kc, time.Duration(cluster.API.KeepServiceRequestTimeout), cluster.SystemRootToken)
+ router = MakeRESTRouter(kc, time.Duration(cluster.API.KeepServiceRequestTimeout), cluster.ManagementToken)
return http.Serve(listener, httpserver.AddRequestIDs(httpserver.LogRequests(router)))
}
commit 10f4f6d203e7d7cacfe2d6620fa4515c2354c556
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Jan 24 15:28:28 2020 -0500
15954: Add health/proxy/dav services.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 4ac6d4a21..8d20e5f6c 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -39,8 +39,11 @@ type bootCommand struct {
libPath string // e.g., /var/lib/arvados
clusterType string // e.g., production
- stdout io.Writer
- stderr io.Writer
+ cluster *arvados.Cluster
+ stdout io.Writer
+ stderr io.Writer
+
+ tempdir string
setupRubyOnce sync.Once
setupRubyErr error
@@ -98,11 +101,11 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
return 1
}
- tempdir, err := ioutil.TempDir("", "arvados-server-boot-")
+ boot.tempdir, err = ioutil.TempDir("", "arvados-server-boot-")
if err != nil {
return 1
}
- defer os.RemoveAll(tempdir)
+ defer os.RemoveAll(boot.tempdir)
// Fill in any missing config keys, and write the resulting
// config in the temp dir for child services to use.
@@ -110,7 +113,7 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
if err != nil {
return 1
}
- conffile, err := os.OpenFile(filepath.Join(tempdir, "config.yml"), os.O_CREATE|os.O_WRONLY, 0777)
+ conffile, err := os.OpenFile(filepath.Join(boot.tempdir, "config.yml"), os.O_CREATE|os.O_WRONLY, 0777)
if err != nil {
return 1
}
@@ -124,16 +127,16 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
return 1
}
os.Setenv("ARVADOS_CONFIG", conffile.Name())
-
+ arvados.DefaultConfigFile = conffile.Name()
os.Setenv("RAILS_ENV", boot.clusterType)
// Now that we have the config, replace the bootstrap logger
// with a new one according to the logging config.
- cluster, err := cfg.GetCluster("")
+ boot.cluster, err = cfg.GetCluster("")
if err != nil {
return 1
}
- log = ctxlog.New(stderr, cluster.SystemLogs.Format, cluster.SystemLogs.LogLevel)
+ log = ctxlog.New(stderr, boot.cluster.SystemLogs.Format, boot.cluster.SystemLogs.LogLevel)
logger := log.WithFields(logrus.Fields{
"PID": os.Getpid(),
})
@@ -169,10 +172,15 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
var wg sync.WaitGroup
for _, cmpt := range []component{
+ {name: "nginx", runFunc: runNginx},
{name: "controller", cmdHandler: controller.Command},
{name: "dispatchcloud", cmdHandler: dispatchcloud.Command, notIfTest: true},
+ {name: "git-httpd", goProg: "services/arv-git-httpd"},
+ {name: "health", goProg: "services/health"},
+ {name: "keep-balance", goProg: "services/keep-balance", notIfTest: true},
{name: "keepproxy", goProg: "services/keepproxy"},
- {name: "railsAPI", svc: cluster.Services.RailsAPI, railsApp: "services/api"},
+ {name: "keep-web", goProg: "services/keep-web"},
+ {name: "railsAPI", svc: boot.cluster.Services.RailsAPI, railsApp: "services/api"},
} {
cmpt := cmpt
wg.Add(1)
@@ -233,13 +241,13 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
go func() {
<-ctx.Done()
log := ctxlog.FromContext(ctx).WithFields(logrus.Fields{"dir": dir, "cmdline": cmdline})
- for cmd.ProcessState != nil {
+ 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")
+ cmd.Process.Signal(syscall.SIGTERM)
+ log.WithField("PID", cmd.Process.Pid).Infof("waiting for child process to exit after SIGTERM")
time.Sleep(5 * time.Second)
}
}
@@ -255,6 +263,7 @@ type component struct {
name string
svc arvados.Service
cmdHandler cmd.Handler
+ runFunc func(ctx context.Context, boot *bootCommand, stdout, stderr io.Writer) error
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
@@ -262,7 +271,7 @@ type component struct {
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)
+ fmt.Fprintf(stderr, "skipping component %q in %s mode\n", cmpt.name, boot.clusterType)
<-ctx.Done()
return nil
}
@@ -289,25 +298,15 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
if cmpt.goProg != "" {
return boot.RunProgram(ctx, cmpt.goProg, nil, nil, "go", "run", ".")
}
+ if cmpt.runFunc != nil {
+ return cmpt.runFunc(ctx, boot, stdout, stderr)
+ }
if cmpt.railsApp != "" {
- port := "-"
- for u := range cmpt.svc.InternalURLs {
- if _, p, err := net.SplitHostPort(u.Host); err != nil {
- return err
- } else if p != "" {
- port = p
- } else if u.Scheme == "https" {
- port = "443"
- } else {
- port = "80"
- }
- break
- }
- if port == "-" {
+ port, err := internalPort(cmpt.svc)
+ if err != nil {
return fmt.Errorf("bug: no InternalURLs for component %q: %v", cmpt.name, cmpt.svc.InternalURLs)
}
-
- err := boot.setupRubyEnv()
+ err = boot.setupRubyEnv()
if err != nil {
return err
}
@@ -358,7 +357,14 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
for _, svc := range []*arvados.Service{
&cluster.Services.Controller,
&cluster.Services.DispatchCloud,
+ &cluster.Services.GitHTTP,
+ &cluster.Services.Health,
+ &cluster.Services.Keepproxy,
+ &cluster.Services.Keepstore,
&cluster.Services.RailsAPI,
+ &cluster.Services.WebDAV,
+ &cluster.Services.WebDAVDownload,
+ &cluster.Services.Websocket,
} {
if len(svc.InternalURLs) == 0 {
port++
@@ -366,15 +372,22 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", port)}: arvados.ServiceInstance{},
}
}
- }
- if cluster.Services.Controller.ExternalURL.Host == "" {
- for k := range cluster.Services.Controller.InternalURLs {
- cluster.Services.Controller.ExternalURL = k
+ if svc.ExternalURL.Host == "" && (svc == &cluster.Services.Controller ||
+ svc == &cluster.Services.GitHTTP ||
+ svc == &cluster.Services.Keepproxy ||
+ svc == &cluster.Services.WebDAV ||
+ svc == &cluster.Services.WebDAVDownload ||
+ svc == &cluster.Services.Websocket) {
+ port++
+ svc.ExternalURL = arvados.URL{Scheme: "https", Host: fmt.Sprintf("localhost:%d", port)}
}
}
if cluster.SystemRootToken == "" {
cluster.SystemRootToken = randomHexString(64)
}
+ if cluster.ManagementToken == "" {
+ cluster.ManagementToken = randomHexString(64)
+ }
if cluster.API.RailsSessionSecretToken == "" {
cluster.API.RailsSessionSecretToken = randomHexString(64)
}
@@ -388,6 +401,17 @@ func (boot *bootCommand) autofillConfig(cfg *arvados.Config, log logrus.FieldLog
}
cluster.Containers.DispatchPrivateKey = string(buf)
}
+ if boot.clusterType != "production" {
+ cluster.TLS.Insecure = true
+ }
+ if boot.clusterType == "test" && len(cluster.Volumes) == 0 {
+ cluster.Volumes = map[string]arvados.Volume{
+ "zzzzz-nyw5e-000000000000000": arvados.Volume{
+ Driver: "Directory",
+ DriverParameters: json.RawMessage(fmt.Sprintf(`{"Root":%q}`, boot.tempdir+"/keep0")),
+ },
+ }
+ }
cfg.Clusters[cluster.ClusterID] = *cluster
return nil
}
@@ -400,3 +424,30 @@ func randomHexString(chars int) string {
}
return fmt.Sprintf("%x", b)
}
+
+func internalPort(svc arvados.Service) (string, error) {
+ for u := range svc.InternalURLs {
+ if _, p, err := net.SplitHostPort(u.Host); err != nil {
+ return "", err
+ } else if p != "" {
+ return p, nil
+ } else if u.Scheme == "https" {
+ return "443", nil
+ } else {
+ return "80", nil
+ }
+ }
+ return "", fmt.Errorf("service has no InternalURLs")
+}
+
+func externalPort(svc arvados.Service) (string, error) {
+ if _, p, err := net.SplitHostPort(svc.ExternalURL.Host); err != nil {
+ return "", err
+ } else if p != "" {
+ return p, nil
+ } else if svc.ExternalURL.Scheme == "https" {
+ return "443", nil
+ } else {
+ return "80", nil
+ }
+}
diff --git a/lib/boot/nginx.go b/lib/boot/nginx.go
new file mode 100644
index 000000000..11d823fc0
--- /dev/null
+++ b/lib/boot/nginx.go
@@ -0,0 +1,77 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+)
+
+func runNginx(ctx context.Context, boot *bootCommand, stdout, stderr io.Writer) error {
+ vars := map[string]string{
+ "SSLCERT": filepath.Join(boot.sourcePath, "services", "api", "tmp", "self-signed.pem"), // TODO: root ca
+ "SSLKEY": filepath.Join(boot.sourcePath, "services", "api", "tmp", "self-signed.key"), // TODO: root ca
+ "ACCESSLOG": filepath.Join(boot.tempdir, "nginx_access.log"),
+ "ERRORLOG": filepath.Join(boot.tempdir, "nginx_error.log"),
+ "TMPDIR": boot.tempdir,
+ }
+ var err error
+ for _, cmpt := range []struct {
+ varname string
+ svc arvados.Service
+ }{
+ {"CONTROLLER", boot.cluster.Services.Controller},
+ {"KEEPWEB", boot.cluster.Services.WebDAV},
+ {"KEEPWEBDL", boot.cluster.Services.WebDAVDownload},
+ {"KEEPPROXY", boot.cluster.Services.Keepproxy},
+ {"GIT", boot.cluster.Services.GitHTTP},
+ {"WS", boot.cluster.Services.Websocket},
+ } {
+ vars[cmpt.varname+"PORT"], err = internalPort(cmpt.svc)
+ if err != nil {
+ return fmt.Errorf("%s internal port: %s (%v)", cmpt.varname, err, cmpt.svc)
+ }
+ vars[cmpt.varname+"SSLPORT"], err = externalPort(cmpt.svc)
+ if err != nil {
+ return fmt.Errorf("%s external port: %s (%v)", cmpt.varname, err, cmpt.svc)
+ }
+ }
+ tmpl, err := ioutil.ReadFile(filepath.Join(boot.sourcePath, "sdk", "python", "tests", "nginx.conf"))
+ if err != nil {
+ return err
+ }
+ conf := regexp.MustCompile(`{{.*?}}`).ReplaceAllStringFunc(string(tmpl), func(src string) string {
+ if len(src) < 4 {
+ return src
+ }
+ return vars[src[2:len(src)-2]]
+ })
+ conffile := filepath.Join(boot.tempdir, "nginx.conf")
+ err = ioutil.WriteFile(conffile, []byte(conf), 0755)
+ if err != nil {
+ return err
+ }
+ nginx := "nginx"
+ if _, err := exec.LookPath(nginx); err != nil {
+ for _, dir := range []string{"/sbin", "/usr/sbin", "/usr/local/sbin"} {
+ if _, err = os.Stat(dir + "/nginx"); err == nil {
+ nginx = dir + "/nginx"
+ break
+ }
+ }
+ }
+ return boot.RunProgram(ctx, ".", nil, nil, nginx,
+ "-g", "error_log stderr info;",
+ "-g", "pid "+filepath.Join(boot.tempdir, "nginx.pid")+";",
+ "-c", conffile)
+}
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index 6010ee4bf..e9be12235 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -92,7 +92,7 @@ http {
server localhost:{{WSPORT}};
}
server {
- listen *:{{WSSPORT}} ssl default_server;
+ listen *:{{WSSSLPORT}} ssl default_server;
server_name websocket;
ssl_certificate "{{SSLCERT}}";
ssl_certificate_key "{{SSLKEY}}";
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index 9e9b12f98..5c05c124c 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -616,7 +616,7 @@ def run_nginx():
nginxconf['GITPORT'] = internal_port_from_config("GitHTTP")
nginxconf['GITSSLPORT'] = external_port_from_config("GitHTTP")
nginxconf['WSPORT'] = internal_port_from_config("Websocket")
- nginxconf['WSSPORT'] = external_port_from_config("Websocket")
+ nginxconf['WSSSLPORT'] = external_port_from_config("Websocket")
nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem')
nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key')
nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
commit 11c6d08cdb0bb78a7144d229ee6b884ae0618b15
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jan 27 10:10:31 2020 -0500
15954: Use test suite secrets in test config.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/doc/examples/config/zzzzz.yml b/doc/examples/config/zzzzz.yml
index 16d449cf4..9e3d718ed 100644
--- a/doc/examples/config/zzzzz.yml
+++ b/doc/examples/config/zzzzz.yml
@@ -7,3 +7,13 @@ Clusters:
dbname: arvados_test
user: arvados
password: insecure_arvados_test
+ ManagementToken: e687950a23c3a9bceec28c6223a06c79
+ SystemRootToken: systemusertesttoken1234567890aoeuidhtnsqjkxbmwvzpy
+ API:
+ RequestTimeout: 30s
+ TLS:
+ Insecure: true
+ Collections:
+ BlobSigningKey: zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc
+ TrustAllContent: true
+ ForwardSlashNameSubstitution: /
commit b532d3105faf4325b47e7bc8ccefd384f960574e
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Jan 24 15:07:32 2020 -0500
15954: Remove unused cmdArgs.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index a8aa114de..4ac6d4a21 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -169,8 +169,8 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
var wg sync.WaitGroup
for _, cmpt := range []component{
- {name: "controller", cmdArgs: []string{"-config", conffile.Name()}, cmdHandler: controller.Command},
- {name: "dispatchcloud", cmdArgs: []string{"-config", conffile.Name()}, cmdHandler: dispatchcloud.Command, notIfTest: true},
+ {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"},
} {
@@ -255,7 +255,6 @@ type component struct {
name string
svc arvados.Service
cmdHandler cmd.Handler
- cmdArgs []string
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
@@ -272,7 +271,7 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
errs := make(chan error, 1)
go func() {
defer close(errs)
- exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, cmpt.cmdArgs, bytes.NewBuffer(nil), stdout, stderr)
+ exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, nil, bytes.NewBuffer(nil), stdout, stderr)
if exitcode != 0 {
errs <- fmt.Errorf("exit code %d", exitcode)
}
commit 56cc681c50a1463d0128e9e07b5e55265727b567
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Jan 24 15:06:29 2020 -0500
15954: Fix process start/stop race.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index de46ee674..a8aa114de 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -232,13 +232,16 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
}
go func() {
<-ctx.Done()
- cmd.Process.Signal(syscall.SIGINT)
- for range time.Tick(5 * time.Second) {
- if cmd.ProcessState != nil {
- break
+ 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)
}
- ctxlog.FromContext(ctx).WithField("process", cmd.Process).Infof("waiting for child process to exit after SIGINT")
- cmd.Process.Signal(syscall.SIGINT)
}
}()
err := cmd.Run()
commit 0d97999fe3429b31031a09b563e39bf83fefad16
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Jan 24 14:45:35 2020 -0500
15954: Run dispatchcloud and keepproxy.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 6762446b1..de46ee674 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -26,6 +26,7 @@ import (
"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"
@@ -105,7 +106,10 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
// 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
@@ -165,9 +169,10 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
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", cmdArgs: []string{"-config", conffile.Name()}, cmdHandler: controller.Command},
+ {name: "dispatchcloud", cmdArgs: []string{"-config", conffile.Name()}, cmdHandler: dispatchcloud.Command, notIfTest: true},
+ {name: "keepproxy", goProg: "services/keepproxy"},
+ {name: "railsAPI", svc: cluster.Services.RailsAPI, railsApp: "services/api"},
} {
cmpt := cmpt
wg.Add(1)
@@ -248,10 +253,17 @@ type component struct {
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 {
errs := make(chan error, 1)
@@ -272,7 +284,10 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
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 {
@@ -295,36 +310,36 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
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
}
@@ -332,10 +347,10 @@ func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stder
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{
@@ -364,7 +379,15 @@ func autofillConfig(cfg *arvados.Config, log logrus.FieldLogger) {
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 {
diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml
index 50fee746b..91bb8550e 100644
--- a/lib/config/config.default.yml
+++ b/lib/config/config.default.yml
@@ -623,7 +623,7 @@ Clusters:
# (experimental) cloud dispatcher for executing containers on
# worker VMs. Begins with "-----BEGIN RSA PRIVATE KEY-----\n"
# and ends with "\n-----END RSA PRIVATE KEY-----\n".
- DispatchPrivateKey: none
+ DispatchPrivateKey: ""
# Maximum time to wait for workers to come up before abandoning
# stale locks from a previous dispatch process.
diff --git a/lib/config/generated_config.go b/lib/config/generated_config.go
index 2ee602507..eb5cc47f2 100644
--- a/lib/config/generated_config.go
+++ b/lib/config/generated_config.go
@@ -629,7 +629,7 @@ Clusters:
# (experimental) cloud dispatcher for executing containers on
# worker VMs. Begins with "-----BEGIN RSA PRIVATE KEY-----\n"
# and ends with "\n-----END RSA PRIVATE KEY-----\n".
- DispatchPrivateKey: none
+ DispatchPrivateKey: ""
# Maximum time to wait for workers to come up before abandoning
# stale locks from a previous dispatch process.
commit b4bc18e9418dc02d126ea949e4405f9f40bf043a
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Jan 24 14:02:00 2020 -0500
15954: Try harder to shut down children before exiting.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index f2805ecf0..6762446b1 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -21,6 +21,7 @@ import (
"strings"
"sync"
"syscall"
+ "time"
"git.arvados.org/arvados.git/lib/cmd"
"git.arvados.org/arvados.git/lib/config"
@@ -161,22 +162,27 @@ func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader,
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"},
} {
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
}
@@ -222,6 +228,13 @@ func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.W
go func() {
<-ctx.Done()
cmd.Process.Signal(syscall.SIGINT)
+ for range time.Tick(5 * time.Second) {
+ if cmd.ProcessState != nil {
+ break
+ }
+ ctxlog.FromContext(ctx).WithField("process", cmd.Process).Infof("waiting for child process to exit after SIGINT")
+ cmd.Process.Signal(syscall.SIGINT)
+ }
}()
err := cmd.Run()
if err != nil {
@@ -241,11 +254,23 @@ type component struct {
func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stderr io.Writer) error {
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, cmpt.cmdArgs, 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 != "" {
port := "-"
commit d0991b80b5b0b9a2169624a225ae6892ebe496d4
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Jan 24 13:37:07 2020 -0500
15954: boot RailsAPI and controller.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/cmd/arvados-server/cmd.go b/cmd/arvados-server/cmd.go
index d93a8e78f..a9d927d87 100644
--- a/cmd/arvados-server/cmd.go
+++ b/cmd/arvados-server/cmd.go
@@ -7,6 +7,7 @@ package main
import (
"os"
+ "git.arvados.org/arvados.git/lib/boot"
"git.arvados.org/arvados.git/lib/cloud/cloudtest"
"git.arvados.org/arvados.git/lib/cmd"
"git.arvados.org/arvados.git/lib/config"
@@ -21,6 +22,7 @@ var (
"-version": cmd.Version,
"--version": cmd.Version,
+ "boot": boot.Command,
"cloudtest": cloudtest.Command,
"config-check": config.CheckCommand,
"config-dump": config.DumpCommand,
diff --git a/doc/examples/config/zzzzz.yml b/doc/examples/config/zzzzz.yml
new file mode 100644
index 000000000..16d449cf4
--- /dev/null
+++ b/doc/examples/config/zzzzz.yml
@@ -0,0 +1,9 @@
+Clusters:
+ zzzzz:
+ PostgreSQL:
+ Connection:
+ client_encoding: utf8
+ host: localhost
+ dbname: arvados_test
+ user: arvados
+ password: insecure_arvados_test
diff --git a/go.mod b/go.mod
index 033723d23..2e16e5a0f 100644
--- a/go.mod
+++ b/go.mod
@@ -50,7 +50,7 @@ require (
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
- golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd // indirect
+ golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd
google.golang.org/api v0.13.0
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405
gopkg.in/square/go-jose.v2 v2.3.1
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
new file mode 100644
index 000000000..f2805ecf0
--- /dev/null
+++ b/lib/boot/cmd.go
@@ -0,0 +1,352 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "bytes"
+ "context"
+ "crypto/rand"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "strings"
+ "sync"
+ "syscall"
+
+ "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/sdk/go/arvados"
+ "git.arvados.org/arvados.git/sdk/go/ctxlog"
+ "github.com/sirupsen/logrus"
+)
+
+var Command cmd.Handler = &bootCommand{}
+
+type bootCommand struct {
+ sourcePath string // e.g., /home/username/src/arvados
+ libPath string // e.g., /var/lib/arvados
+ clusterType string // e.g., production
+
+ stdout io.Writer
+ stderr io.Writer
+
+ setupRubyOnce sync.Once
+ setupRubyErr error
+ goMutex sync.Mutex
+}
+
+func (boot *bootCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ boot.stdout = stdout
+ boot.stderr = stderr
+ log := ctxlog.New(stderr, "json", "info")
+
+ var err error
+ defer func() {
+ if err != nil {
+ log.WithError(err).Info("exiting")
+ }
+ }()
+
+ flags := flag.NewFlagSet(prog, flag.ContinueOnError)
+ flags.SetOutput(stderr)
+ loader := config.NewLoader(stdin, log)
+ loader.SetupFlags(flags)
+ versionFlag := flags.Bool("version", false, "Write version information to stdout and exit 0")
+ flags.StringVar(&boot.sourcePath, "source", ".", "arvados source tree `directory`")
+ flags.StringVar(&boot.libPath, "lib", "/var/lib/arvados", "`directory` to install dependencies and library files")
+ flags.StringVar(&boot.clusterType, "type", "production", "cluster `type`: development, test, or production")
+ err = flags.Parse(args)
+ if err == flag.ErrHelp {
+ err = nil
+ return 0
+ } else if err != nil {
+ return 2
+ } else if *versionFlag {
+ return cmd.Version.RunCommand(prog, args, stdin, stdout, stderr)
+ } else if boot.clusterType != "development" && boot.clusterType != "test" && boot.clusterType != "production" {
+ err = fmt.Errorf("cluster type must be 'development', 'test', or 'production'")
+ return 2
+ }
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ return 1
+ }
+ if !strings.HasPrefix(boot.sourcePath, "/") {
+ boot.sourcePath = filepath.Join(cwd, boot.sourcePath)
+ }
+ boot.sourcePath, err = filepath.EvalSymlinks(boot.sourcePath)
+ if err != nil {
+ return 1
+ }
+
+ loader.SkipAPICalls = true
+ cfg, err := loader.Load()
+ if err != nil {
+ return 1
+ }
+
+ tempdir, err := ioutil.TempDir("", "arvados-server-boot-")
+ if err != nil {
+ return 1
+ }
+ defer os.RemoveAll(tempdir)
+
+ // Fill in any missing config keys, and write the resulting
+ // config in the temp dir for child services to use.
+ autofillConfig(cfg, log)
+ conffile, err := os.OpenFile(filepath.Join(tempdir, "config.yml"), os.O_CREATE|os.O_WRONLY, 0777)
+ if err != nil {
+ return 1
+ }
+ defer conffile.Close()
+ err = json.NewEncoder(conffile).Encode(cfg)
+ if err != nil {
+ return 1
+ }
+ err = conffile.Close()
+ if err != nil {
+ return 1
+ }
+ os.Setenv("ARVADOS_CONFIG", conffile.Name())
+
+ os.Setenv("RAILS_ENV", boot.clusterType)
+
+ // Now that we have the config, replace the bootstrap logger
+ // with a new one according to the logging config.
+ cluster, err := cfg.GetCluster("")
+ if err != nil {
+ return 1
+ }
+ log = ctxlog.New(stderr, cluster.SystemLogs.Format, cluster.SystemLogs.LogLevel)
+ logger := log.WithFields(logrus.Fields{
+ "PID": os.Getpid(),
+ })
+ ctx := ctxlog.Context(context.Background(), logger)
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ ch := make(chan os.Signal)
+ signal.Notify(ch, syscall.SIGINT)
+ go func() {
+ for sig := range ch {
+ logger.WithField("signal", sig).Info("caught signal")
+ cancel()
+ }
+ }()
+
+ for _, dir := range []string{boot.libPath, filepath.Join(boot.libPath, "bin")} {
+ if _, err = os.Stat(filepath.Join(dir, ".")); os.IsNotExist(err) {
+ err = os.Mkdir(dir, 0755)
+ if err != nil {
+ return 1
+ }
+ } else if err != nil {
+ return 1
+ }
+ }
+ os.Setenv("PATH", filepath.Join(boot.libPath, "bin")+":"+os.Getenv("PATH"))
+
+ err = boot.installGoProgram(ctx, "cmd/arvados-server")
+ if err != nil {
+ return 1
+ }
+ 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"},
+ } {
+ cmpt := cmpt
+ go func() {
+ 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()
+ return 0
+}
+
+func (boot *bootCommand) installGoProgram(ctx context.Context, srcpath string) error {
+ boot.goMutex.Lock()
+ defer boot.goMutex.Unlock()
+ env := append([]string{"GOPATH=" + boot.libPath}, os.Environ()...)
+ return boot.RunProgram(ctx, filepath.Join(boot.sourcePath, srcpath), nil, env, "go", "install")
+}
+
+func (boot *bootCommand) setupRubyEnv() error {
+ boot.setupRubyOnce.Do(func() {
+ buf, err := exec.Command("gem", "env", "gempath").Output() // /var/lib/arvados/.gem/ruby/2.5.0/bin:...
+ if err != nil || len(buf) == 0 {
+ boot.setupRubyErr = fmt.Errorf("gem env gempath: %v", err)
+ }
+ gempath := string(bytes.Split(buf, []byte{':'})[0])
+ os.Setenv("PATH", gempath+"/bin:"+os.Getenv("PATH"))
+ os.Setenv("GEM_HOME", gempath)
+ os.Setenv("GEM_PATH", gempath)
+ })
+ return boot.setupRubyErr
+}
+
+func (boot *bootCommand) RunProgram(ctx context.Context, dir string, output io.Writer, env []string, prog string, args ...string) error {
+ cmdline := fmt.Sprintf("%s", append([]string{prog}, args...))
+ fmt.Fprintf(boot.stderr, "%s executing in %s\n", cmdline, dir)
+ cmd := exec.Command(prog, args...)
+ if output == nil {
+ cmd.Stdout = boot.stderr
+ } else {
+ cmd.Stdout = output
+ }
+ cmd.Stderr = boot.stderr
+ if strings.HasPrefix(dir, "/") {
+ cmd.Dir = dir
+ } else {
+ cmd.Dir = filepath.Join(boot.sourcePath, dir)
+ }
+ if env != nil {
+ cmd.Env = env
+ }
+ go func() {
+ <-ctx.Done()
+ cmd.Process.Signal(syscall.SIGINT)
+ }()
+ err := cmd.Run()
+ if err != nil {
+ return fmt.Errorf("%s: error: %v", cmdline, err)
+ }
+ return nil
+}
+
+type component struct {
+ name string
+ svc arvados.Service
+ cmdHandler cmd.Handler
+ cmdArgs []string
+ src string // source dir in arvados tree, e.g., "services/keepstore"
+}
+
+func (cmpt *component) Run(ctx context.Context, boot *bootCommand, stdout, stderr io.Writer) error {
+ 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)
+ }
+ return nil
+ }
+ if cmpt.src != "" {
+ port := "-"
+ for u := range cmpt.svc.InternalURLs {
+ if _, p, err := net.SplitHostPort(u.Host); err != nil {
+ return err
+ } else if p != "" {
+ port = p
+ } else if u.Scheme == "https" {
+ port = "443"
+ } else {
+ port = "80"
+ }
+ break
+ }
+ if port == "-" {
+ return fmt.Errorf("bug: no InternalURLs for component %q: %v", cmpt.name, cmpt.svc.InternalURLs)
+ }
+
+ err := boot.setupRubyEnv()
+ if err != nil {
+ return err
+ }
+ var buf bytes.Buffer
+ err = boot.RunProgram(ctx, cmpt.src, &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")
+ 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"))
+ if err != nil {
+ return err
+ }
+ err = boot.RunProgram(ctx, cmpt.src, 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")
+ if err != nil {
+ return err
+ }
+ err = boot.RunProgram(ctx, cmpt.src, 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)
+ 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) {
+ cluster, err := cfg.GetCluster("")
+ if err != nil {
+ panic(err)
+ }
+ port := 9000
+ for _, svc := range []*arvados.Service{
+ &cluster.Services.Controller,
+ &cluster.Services.DispatchCloud,
+ &cluster.Services.RailsAPI,
+ } {
+ if len(svc.InternalURLs) == 0 {
+ port++
+ svc.InternalURLs = map[arvados.URL]arvados.ServiceInstance{
+ arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%d", port)}: arvados.ServiceInstance{},
+ }
+ }
+ }
+ if cluster.Services.Controller.ExternalURL.Host == "" {
+ for k := range cluster.Services.Controller.InternalURLs {
+ cluster.Services.Controller.ExternalURL = k
+ }
+ }
+ if cluster.SystemRootToken == "" {
+ cluster.SystemRootToken = randomHexString(64)
+ }
+ if cluster.API.RailsSessionSecretToken == "" {
+ cluster.API.RailsSessionSecretToken = randomHexString(64)
+ }
+ if cluster.Collections.BlobSigningKey == "" {
+ cluster.Collections.BlobSigningKey = randomHexString(64)
+ }
+ cfg.Clusters[cluster.ClusterID] = *cluster
+}
+
+func randomHexString(chars int) string {
+ b := make([]byte, chars/2)
+ _, err := rand.Read(b)
+ if err != nil {
+ panic(err)
+ }
+ return fmt.Sprintf("%x", b)
+}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list