[ARVADOS] updated: 1.3.0-2177-g4fa9d36bf
Git user
git at public.arvados.org
Tue Feb 18 20:17:46 UTC 2020
Summary of changes:
.../app/controllers/actions_controller.rb | 2 +-
.../app/controllers/application_controller.rb | 2 +-
apps/workbench/app/models/container_work_unit.rb | 2 +-
.../app/views/users/_virtual_machines.html.erb | 12 +-
.../app/views/virtual_machines/_show_help.html.erb | 27 +-
apps/workbench/fpm-info.sh | 4 +-
build/package-build-dockerfiles/centos7/Dockerfile | 2 +-
.../package-build-dockerfiles/debian10/Dockerfile | 2 +-
build/package-build-dockerfiles/debian9/Dockerfile | 2 +-
.../ubuntu1604/Dockerfile | 2 +-
.../ubuntu1804/Dockerfile | 2 +-
build/rails-package-scripts/postinst.sh | 2 +-
build/run-build-packages.sh | 7 +-
build/run-build-test-packages-one-target.sh | 8 +-
build/run-library.sh | 12 +-
build/run-tests.sh | 2 +-
build/version-at-commit.sh | 39 ++-
cmd/arvados-server/arvados-controller.service | 2 +
cmd/arvados-server/arvados-dispatch-cloud.service | 2 +
doc/_config.yml | 10 +-
doc/_includes/_navbar_top.liquid | 4 +-
doc/_layouts/default.html.liquid | 1 +
.../collection-versioning.html.textile.liquid | 29 +-
doc/admin/config-migration.html.textile.liquid | 6 +-
...controlling-container-reuse.html.textile.liquid | 10 +-
.../logs-table-management.html.textile.liquid | 69 +++--
doc/admin/metrics.html.textile.liquid | 184 ++----------
doc/admin/storage-classes.html.textile.liquid | 22 +-
doc/admin/upgrade-crunch2.html.textile.liquid | 2 +-
doc/admin/upgrading.html.textile.liquid | 205 ++++++++-----
.../workbench2-vocabulary.html.textile.liquid | 4 +-
doc/api/methods.html.textile.liquid | 1 -
doc/api/methods/links.html.textile.liquid | 43 ++-
doc/css/code.css | 2 +
doc/examples/config/zzzzz.yml | 7 -
doc/install/index.html.textile.liquid | 4 +
.../install-dispatch-cloud.html.textile.liquid | 10 +-
.../install-workbench2-app.html.textile.liquid | 4 +-
doc/sdk/index.html.textile.liquid | 2 +-
docker/jobs/Dockerfile | 7 +-
go.mod | 2 +-
go.sum | 2 +
lib/boot/cert.go | 58 ++++
lib/boot/cmd.go | 323 +++++++++++----------
lib/boot/log.go | 32 ++
lib/boot/nginx.go | 31 +-
lib/boot/passenger.go | 93 ++++++
lib/boot/postgresql.go | 100 +++++++
lib/boot/seed.go | 27 ++
lib/boot/service.go | 68 +++++
lib/config/config.default.yml | 9 +
lib/config/export.go | 1 +
lib/config/generated_config.go | 9 +
lib/controller/federation/conn.go | 21 +-
lib/controller/federation/list.go | 51 ++--
lib/controller/federation/list_test.go | 43 +++
sdk/cwl/arvados_cwl/arvcontainer.py | 3 +-
sdk/cwl/arvados_cwl/executor.py | 52 ++--
sdk/cwl/arvados_cwl/fsaccess.py | 5 +-
sdk/cwl/arvados_cwl/runner.py | 8 +-
sdk/cwl/setup.py | 2 +-
sdk/cwl/tests/test_submit.py | 13 +
sdk/cwl/tests/tool/blub.txt.cat | 1 +
.../tool/{submit_tool.cwl => tool_with_sf.cwl} | 6 +-
sdk/cwl/tests/tool/tool_with_sf.yml | 3 +
sdk/go/arvados/api.go | 1 +
sdk/go/arvados/config.go | 1 +
sdk/python/setup.py | 2 +-
sdk/python/tests/nginx.conf | 26 +-
sdk/python/tests/run_test_server.py | 29 +-
.../app/controllers/arvados/v1/users_controller.rb | 6 +-
services/api/app/mailers/user_notifier.rb | 2 +-
services/api/app/models/collection.rb | 6 +-
services/api/config/application.default.yml | 12 -
services/api/lib/config_loader.rb | 11 +-
.../functional/arvados/v1/users_controller_test.rb | 2 +-
services/api/test/unit/user_notifier_test.rb | 2 +-
.../crunch-dispatch-slurm.service | 2 +
services/fuse/arvados_version.py | 17 +-
services/health/arvados-health.service | 2 +
services/keep-balance/keep-balance.service | 2 +
services/keep-web/keep-web.service | 2 +
services/keepproxy/keepproxy.service | 2 +
services/keepstore/keepstore.service | 2 +
services/ws/arvados-ws.service | 2 +
tools/arvbox/bin/arvbox | 6 +-
tools/arvbox/lib/arvbox/docker/common.sh | 1 +
tools/arvbox/lib/arvbox/docker/service/nginx/run | 82 +++---
88 files changed, 1259 insertions(+), 683 deletions(-)
create mode 100644 lib/boot/cert.go
create mode 100644 lib/boot/log.go
create mode 100644 lib/boot/passenger.go
create mode 100644 lib/boot/postgresql.go
create mode 100644 lib/boot/seed.go
create mode 100644 lib/boot/service.go
create mode 100644 sdk/cwl/tests/tool/blub.txt.cat
copy sdk/cwl/tests/tool/{submit_tool.cwl => tool_with_sf.cwl} (87%)
create mode 100644 sdk/cwl/tests/tool/tool_with_sf.yml
via 4fa9d36bff13040b86c60490614b5e124f5a5606 (commit)
via 93a06abafd2d6aacbb5da7bc4f04de558f404177 (commit)
via 6d1d402cfb1a1e53f16668ff76a6fb38c03df94e (commit)
via 44c25614832bd22c931479c38b05c6f3913e3a6f (commit)
via 2fb6eafd2fda2834b0181b908d44a93ff8b6da43 (commit)
via 8a471f18f22f85e996d2ce7110e7848aadfab44b (commit)
via 7a24a37aa9e5ed425550403b68c270316a24d772 (commit)
via 7abc7ca38954acd4eaa53c9280504e06a76b8d71 (commit)
via b9fd7e3f374248a61159e4750a84e38d1c48d5dd (commit)
via b01c4372354d84bbc712d2793b07a8d5d5276b98 (commit)
via 618c29735325bcce9233747f04df1e86a4a3ef69 (commit)
via b8eec9929ab5b7a6c3ab1d3129123a63ba9dd978 (commit)
via 1963a6aaf39f99bfd56264aeded298501eb8c3d4 (commit)
via 04332347f3cca9e3aad62c7e67fe981e818b918c (commit)
via f6abd82e35d6c8f09ce820f1c5774ba1ff613a71 (commit)
via 02dbc92e0a0df291a97edcfcc419319fdbc5f750 (commit)
via 6272cb13e54254ad5ab3da898716daaab3ddae6a (commit)
via af1a79779dad6f9b01fd9deb2197ece416c014ec (commit)
via 8039b0a469df007a282f321bd8349d9e47461a79 (commit)
via a29c0ced4f679e1972488fd2d6074fa1de3bac8d (commit)
via 118908c39c6ffa0ae8b62cddbdb610c51a461b6d (commit)
via d8861941ef704dd729252ee1592e48a082798b65 (commit)
via 1a0dce6698ce30e942877c959138160407b6a1be (commit)
via b742465734fe980578e106e6b035ca9d0aebf02e (commit)
via 14bb7aa24ed7a06ce94e59f64ab32bdb44641168 (commit)
via e7d4393c847d036c2cf4a4d3c865e2c84636b1f2 (commit)
via 4a30eb19ec37e84947f49f2d39b1acae3ebf3847 (commit)
via 36c3c62be3b319a76dae1281206779d9dbecc030 (commit)
via 273e436dbf8736869dde715e9bc04a20c169d63f (commit)
via fa9a17bf1ea10d9130188730a1aa160e89daaa13 (commit)
via ee0c92074b196d9e48ffe4e22bd20fbd58b5b837 (commit)
via 4d969d35d1e1f022bd5ddcebb2de915bedd01334 (commit)
via 73018eed5cd93c9db7467760f5f1484fa2750554 (commit)
via 42ef5a07ca5fd2675f619d48de00c6f10143060c (commit)
via ce4675e0b3447611a0154de25ad89595f488890b (commit)
via 4e450b7c2ec9563dd6d670238d096d1bc9fd158f (commit)
via 74c70bf8c41ac135976f30784a4b5a3ab85dbe47 (commit)
via 63de9493f14d062af48673fa1c4775d9c4e890fb (commit)
via 040db1debca6c5dccda5513fb6d42f14687ae910 (commit)
via 68a488a5a18d214a18016b4ef62137aafab0db8b (commit)
via cba44f28b4e932079f6bad2a478aa58fbcf9c24e (commit)
via c35311c706ed9c8c73ad5aa60285556f4ee266d7 (commit)
via 7c6defa86739291b44683395780559ce5ddc2b88 (commit)
via 405017353d0a74ef28288744957fd362df98c0e2 (commit)
via 9fc2ff5583fc31c9f75f80dcf7258bed63084568 (commit)
via c88bd2141126b4d912ddb7f97774570ca81e6688 (commit)
via 805883d4570e13e2760fa371e1a7714db3d8a0c9 (commit)
via bc4f35573b38ccde0756209057c8bce6947a38bc (commit)
via 6e89a1027ef15b39b5d8b694939f68ff22e10c86 (commit)
via 3c8aecebabdfeea3fd607789b48e38fba9ef5ce7 (commit)
via 6637c4e3656d3e2334a46a7c30eaed91905a0603 (commit)
via 28e50cc9480fdad416404542511a172cdc7253c7 (commit)
via 6275b9ec9584e82f33688674a19225361fefa36b (commit)
via 8b7de058f2d71698513ddbedfd5870a196c579bf (commit)
via 16c01c88acf2a11d2e764278dcb1fb9aaa582559 (commit)
via 18522b9cfaa23f0003094cccebfe127bf82a35f4 (commit)
via 1b275b7390ba414f82241b23098b99654a227bf4 (commit)
via b871183da9ae071b277bfbd362f1cd3b0bde7076 (commit)
via ee38970362aa72e8aeb875ce15028fffd7834a3b (commit)
via a1a1e1b9e28c470c8efb27ea290ba00d48c520f2 (commit)
via b9969745321abe73dd8d2a04dc60c55fe9434ae6 (commit)
from 0446c0a3a433936985d6f46b0eab9b253ed98e80 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 4fa9d36bff13040b86c60490614b5e124f5a5606
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Feb 18 15:16:17 2020 -0500
15954: Tweak bundler sanity check.
"gem install bundler -v 2.0.2 && bundle version" might report a
previously installed version newer than 2.0.2.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index f21861762..2bcfbb84d 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -554,7 +554,7 @@ setup_ruby_environment() {
export HOME=$GEMHOME
("$bundle" version | grep -q 2.0.2) \
|| gem install --user bundler -v 2.0.2
- "$bundle" version | grep 2.0.2
+ "$bundle" version | tee /dev/stderr | grep -q 'version 2'
) || fatal 'install bundler'
fi
}
commit 93a06abafd2d6aacbb5da7bc4f04de558f404177
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Feb 18 15:14:42 2020 -0500
15954: Migrate test server's legacy config.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index 639eb87fd..9519c2b72 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -723,6 +723,9 @@ def setup_config():
"http://%s:%s"%(localhost, keep_web_dl_port): {},
},
},
+ "SSO": {
+ "ExternalURL": "http://localhost:3002",
+ },
}
config = {
@@ -732,6 +735,11 @@ def setup_config():
"SystemRootToken": auth_token('system_user'),
"API": {
"RequestTimeout": "30s",
+ "RailsSessionSecretToken": "e24205c490ac07e028fd5f8a692dcb398bcd654eff1aef5f9fe6891994b18483",
+ },
+ "Login": {
+ "ProviderAppID": "arvados-server",
+ "ProviderAppSecret": "608dbf356a327e2d0d4932b60161e212c2d8d8f5e25690d7b622f850a990cd33",
},
"SystemLogs": {
"LogLevel": ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'),
@@ -745,14 +753,22 @@ def setup_config():
"Services": services,
"Users": {
"AnonymousUserToken": auth_token('anonymous'),
+ "UserProfileNotificationAddress": "arvados at example.com",
},
"Collections": {
"BlobSigningKey": "zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc",
"TrustAllContent": True,
"ForwardSlashNameSubstitution": "/",
+ "TrashSweepInterval": "-1s",
},
"Git": {
- "Repositories": "%s/test" % os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git'),
+ "Repositories": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git', 'test'),
+ },
+ "Containers": {
+ "JobsAPI": {
+ "GitInternalDir": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'internal.git'),
+ },
+ "SupportedDockerImageFormats": {"v1": {}},
},
"Volumes": {
"zzzzz-nyw5e-%015d"%n: {
diff --git a/services/api/config/application.default.yml b/services/api/config/application.default.yml
index 4e1936b77..9fd5368c0 100644
--- a/services/api/config/application.default.yml
+++ b/services/api/config/application.default.yml
@@ -76,15 +76,3 @@ test:
action_controller.allow_forgery_protection: false
action_mailer.delivery_method: :test
active_support.deprecation: :stderr
- uuid_prefix: zzzzz
- sso_app_id: arvados-server
- sso_app_secret: <%= rand(2**512).to_s(36) %>
- sso_provider_url: http://localhost:3002
- secret_token: <%= rand(2**512).to_s(36) %>
- blob_signing_key: zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc
- user_profile_notification_address: arvados at example.com
- workbench_address: https://localhost:3001/
- git_repositories_dir: <%= Rails.root.join 'tmp', 'git', 'test' %>
- git_internal_dir: <%= Rails.root.join 'tmp', 'internal.git' %>
- trash_sweep_interval: -1
- docker_image_formats: ["v1"]
diff --git a/services/api/lib/config_loader.rb b/services/api/lib/config_loader.rb
index 522aa73b0..cf16993ca 100644
--- a/services/api/lib/config_loader.rb
+++ b/services/api/lib/config_loader.rb
@@ -180,8 +180,13 @@ class ConfigLoader
end
end
- def self.parse_duration durstr, cfgkey:
- duration_re = /-?(\d+(\.\d+)?)(s|m|h)/
+ def self.parse_duration(durstr, cfgkey:)
+ sign = 1
+ if durstr[0] == '-'
+ durstr = durstr[1..-1]
+ sign = -1
+ end
+ duration_re = /(\d+(\.\d+)?)(s|m|h)/
dursec = 0
while durstr != ""
mt = duration_re.match durstr
@@ -189,7 +194,7 @@ class ConfigLoader
raise "#{cfgkey} not a valid duration: '#{durstr}', accepted suffixes are s, m, h"
end
multiplier = {s: 1, m: 60, h: 3600}
- dursec += (Float(mt[1]) * multiplier[mt[3].to_sym])
+ dursec += (Float(mt[1]) * multiplier[mt[3].to_sym] * sign)
durstr = durstr[mt[0].length..-1]
end
return dursec.seconds
commit 6d1d402cfb1a1e53f16668ff76a6fb38c03df94e
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Feb 18 11:28:47 2020 -0500
15954: Move pid/log files out of source tree.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/passenger.go b/lib/boot/passenger.go
index 4aada107b..61f97df52 100644
--- a/lib/boot/passenger.go
+++ b/lib/boot/passenger.go
@@ -82,7 +82,11 @@ func (runner runPassenger) Run(ctx context.Context, fail func(error), boot *Boot
return fmt.Errorf("bug: no InternalURLs for component %q: %v", runner, runner.svc.InternalURLs)
}
go func() {
- err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
+ err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec",
+ "passenger", "start",
+ "-p", port,
+ "--log-file", "/dev/null",
+ "--pid-file", filepath.Join(boot.tempdir, "passenger."+strings.Replace(runner.src, "/", "_", -1)+".pid"))
fail(err)
}()
return nil
commit 44c25614832bd22c931479c38b05c6f3913e3a6f
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Feb 18 10:40:12 2020 -0500
15954: Control listen addresses, improve logging.
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 c288bab75..6cf44837c 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -25,8 +25,6 @@ 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"
"git.arvados.org/arvados.git/sdk/go/health"
@@ -81,6 +79,7 @@ func (bootCommand) RunCommand(prog string, args []string, stdin io.Reader, stdou
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")
+ flags.StringVar(&boot.ListenHost, "listen-host", "localhost", "host name or interface address for service listeners")
flags.StringVar(&boot.ControllerAddr, "controller-address", ":0", "desired controller address, `host:port` or `:port`")
flags.BoolVar(&boot.OwnTemporaryDatabase, "own-temporary-database", false, "bring up a postgres server and create a temporary database")
err = flags.Parse(args)
@@ -111,6 +110,7 @@ type Booter struct {
SourcePath string // e.g., /home/username/src/arvados
LibPath string // e.g., /var/lib/arvados
ClusterType string // e.g., production
+ ListenHost string // e.g., localhost
ControllerAddr string // e.g., 127.0.0.1:8000
OwnTemporaryDatabase bool
Stderr io.Writer
@@ -202,7 +202,11 @@ func (boot *Booter) run(loader *config.Loader) error {
}
// Now that we have the config, replace the bootstrap logger
// with a new one according to the logging config.
- boot.logger = ctxlog.New(boot.Stderr, boot.cluster.SystemLogs.Format, boot.cluster.SystemLogs.LogLevel).WithFields(logrus.Fields{
+ loglevel := boot.cluster.SystemLogs.LogLevel
+ if s := os.Getenv("ARVADOS_DEBUG"); s != "" && s != "0" {
+ loglevel = "debug"
+ }
+ boot.logger = ctxlog.New(boot.Stderr, boot.cluster.SystemLogs.Format, loglevel).WithFields(logrus.Fields{
"PID": os.Getpid(),
})
boot.healthChecker = &health.Aggregator{Cluster: boot.cluster}
@@ -230,22 +234,22 @@ func (boot *Booter) run(loader *config.Loader) error {
createCertificates{},
runPostgreSQL{},
runNginx{},
- runServiceCommand{name: "controller", command: controller.Command, depends: []bootTask{runPostgreSQL{}}},
+ runServiceCommand{name: "controller", svc: boot.cluster.Services.Controller, depends: []bootTask{runPostgreSQL{}}},
runGoProgram{src: "services/arv-git-httpd"},
runGoProgram{src: "services/health"},
- runGoProgram{src: "services/keepproxy"},
+ runGoProgram{src: "services/keepproxy", depends: []bootTask{runPassenger{src: "services/api"}}},
runGoProgram{src: "services/keepstore", svc: boot.cluster.Services.Keepstore},
runGoProgram{src: "services/keep-web"},
runGoProgram{src: "services/ws", depends: []bootTask{runPostgreSQL{}}},
installPassenger{src: "services/api"},
runPassenger{src: "services/api", svc: boot.cluster.Services.RailsAPI, depends: []bootTask{createCertificates{}, runPostgreSQL{}, installPassenger{src: "services/api"}}},
- installPassenger{src: "apps/workbench"},
+ installPassenger{src: "apps/workbench", depends: []bootTask{installPassenger{src: "services/api"}}}, // dependency ensures workbench doesn't delay api startup
runPassenger{src: "apps/workbench", svc: boot.cluster.Services.Workbench1, depends: []bootTask{installPassenger{src: "apps/workbench"}}},
seedDatabase{},
}
if boot.ClusterType != "test" {
tasks = append(tasks,
- runServiceCommand{name: "dispatchcloud", command: dispatchcloud.Command},
+ runServiceCommand{name: "dispatch-cloud", svc: boot.cluster.Services.Controller},
runGoProgram{src: "services/keep-balance"},
)
}
@@ -260,10 +264,10 @@ func (boot *Booter) run(loader *config.Loader) error {
return
}
boot.cancel()
- boot.logger.WithField("task", task).WithError(err).Error("task failed")
+ boot.logger.WithField("task", task.String()).WithError(err).Error("task failed")
}
go func() {
- boot.logger.WithField("task", task).Info("starting")
+ boot.logger.WithField("task", task.String()).Info("starting")
err := task.Run(boot.ctx, fail, boot)
if err != nil {
fail(err)
@@ -272,7 +276,12 @@ func (boot *Booter) run(loader *config.Loader) error {
close(boot.tasksReady[task.String()])
}()
}
- return boot.wait(boot.ctx, tasks...)
+ err = boot.wait(boot.ctx, tasks...)
+ if err != nil {
+ return err
+ }
+ <-boot.ctx.Done()
+ return boot.ctx.Err()
}
func (boot *Booter) wait(ctx context.Context, tasks ...bootTask) error {
@@ -281,7 +290,7 @@ func (boot *Booter) wait(ctx context.Context, tasks ...bootTask) error {
if !ok {
return fmt.Errorf("no such task: %s", task)
}
- boot.logger.WithField("task", task).Info("waiting")
+ boot.logger.WithField("task", task.String()).Info("waiting")
select {
case <-ch:
case <-ctx.Done():
@@ -313,9 +322,10 @@ func (boot *Booter) WaitReady() bool {
// instead we wait for all configured components to
// pass.
waiting = false
- for _, check := range resp.Checks {
+ for target, check := range resp.Checks {
if check.Health != "OK" {
waiting = true
+ boot.logger.WithField("target", target).Debug("waiting")
}
}
}
@@ -385,13 +395,23 @@ func (boot *Booter) lookPath(prog string) string {
func (boot *Booter) 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)
+
+ logprefix := prog
+ if prog == "bundle" && len(args) > 2 && args[0] == "exec" {
+ logprefix = args[1]
+ }
+ if !strings.HasPrefix(dir, "/") {
+ logprefix = dir + ": " + logprefix
+ }
+ stderr := &logPrefixer{Writer: boot.Stderr, Prefix: []byte("[" + logprefix + "] ")}
+
cmd := exec.Command(boot.lookPath(prog), args...)
if output == nil {
- cmd.Stdout = boot.Stderr
+ cmd.Stdout = stderr
} else {
cmd.Stdout = output
}
- cmd.Stderr = boot.Stderr
+ cmd.Stderr = stderr
if strings.HasPrefix(dir, "/") {
cmd.Dir = dir
} else {
@@ -452,7 +472,7 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
return err
}
if h == "" {
- h = "localhost"
+ h = boot.ListenHost
}
if p == "0" {
p, err = availablePort(":0")
@@ -486,11 +506,11 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
svc == &cluster.Services.WebDAVDownload ||
svc == &cluster.Services.Websocket ||
svc == &cluster.Services.Workbench1) {
- svc.ExternalURL = arvados.URL{Scheme: "https", Host: fmt.Sprintf("localhost:%s", nextPort())}
+ svc.ExternalURL = arvados.URL{Scheme: "https", Host: fmt.Sprintf("%s:%s", boot.ListenHost, nextPort())}
}
if len(svc.InternalURLs) == 0 {
svc.InternalURLs = map[arvados.URL]arvados.ServiceInstance{
- arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%s", nextPort())}: arvados.ServiceInstance{},
+ arvados.URL{Scheme: "http", Host: fmt.Sprintf("%s:%s", boot.ListenHost, nextPort())}: arvados.ServiceInstance{},
}
}
}
@@ -518,7 +538,7 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
}
if boot.ClusterType == "test" {
// Add a second keepstore process.
- cluster.Services.Keepstore.InternalURLs[arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%s", nextPort())}] = arvados.ServiceInstance{}
+ cluster.Services.Keepstore.InternalURLs[arvados.URL{Scheme: "http", Host: fmt.Sprintf("%s:%s", boot.ListenHost, nextPort())}] = arvados.ServiceInstance{}
// Create a directory-backed volume for each keepstore
// process.
@@ -532,7 +552,7 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
} else if err = os.Mkdir(datadir, 0777); err != nil {
return err
}
- cluster.Volumes[fmt.Sprintf("zzzzz-nyw5e-%015d", volnum)] = arvados.Volume{
+ cluster.Volumes[fmt.Sprintf(cluster.ClusterID+"-nyw5e-%015d", volnum)] = arvados.Volume{
Driver: "Directory",
DriverParameters: json.RawMessage(fmt.Sprintf(`{"Root":%q}`, datadir)),
AccessViaHosts: map[arvados.URL]arvados.VolumeAccess{
diff --git a/lib/boot/log.go b/lib/boot/log.go
new file mode 100644
index 000000000..062a854a0
--- /dev/null
+++ b/lib/boot/log.go
@@ -0,0 +1,32 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "bytes"
+ "io"
+)
+
+type logPrefixer struct {
+ io.Writer
+ Prefix []byte
+ did bool
+}
+
+func (lp *logPrefixer) Write(p []byte) (int, error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+ if !lp.did {
+ lp.Writer.Write(lp.Prefix)
+ lp.did = p[len(p)-1] != '\n'
+ }
+ out := append(bytes.Replace(p[:len(p)-1], []byte("\n"), append([]byte("\n"), lp.Prefix...), -1), p[len(p)-1])
+ _, err := lp.Writer.Write(out)
+ if err != nil {
+ return 0, err
+ }
+ return len(p), nil
+}
diff --git a/lib/boot/nginx.go b/lib/boot/nginx.go
index c5bfd605a..5c1954c83 100644
--- a/lib/boot/nginx.go
+++ b/lib/boot/nginx.go
@@ -24,11 +24,12 @@ func (runNginx) String() string {
func (runNginx) Run(ctx context.Context, fail func(error), boot *Booter) 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,
+ "LISTENHOST": boot.ListenHost,
+ "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 {
diff --git a/lib/boot/passenger.go b/lib/boot/passenger.go
index b67370e1e..4aada107b 100644
--- a/lib/boot/passenger.go
+++ b/lib/boot/passenger.go
@@ -21,7 +21,7 @@ type installPassenger struct {
}
func (runner installPassenger) String() string {
- return "install " + runner.src
+ return "installPassenger:" + runner.src
}
func (runner installPassenger) Run(ctx context.Context, fail func(error), boot *Booter) error {
@@ -69,8 +69,7 @@ type runPassenger struct {
}
func (runner runPassenger) String() string {
- _, basename := filepath.Split(runner.src)
- return basename
+ return "runPassenger:" + runner.src
}
func (runner runPassenger) Run(ctx context.Context, fail func(error), boot *Booter) error {
@@ -82,9 +81,9 @@ func (runner runPassenger) Run(ctx context.Context, fail func(error), boot *Boot
if err != nil {
return fmt.Errorf("bug: no InternalURLs for component %q: %v", runner, runner.svc.InternalURLs)
}
- err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
- if err != nil {
- return err
- }
+ go func() {
+ err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
+ fail(err)
+ }()
return nil
}
diff --git a/lib/boot/seed.go b/lib/boot/seed.go
index f915764c5..9f086d544 100644
--- a/lib/boot/seed.go
+++ b/lib/boot/seed.go
@@ -15,7 +15,7 @@ func (seedDatabase) String() string {
}
func (seedDatabase) Run(ctx context.Context, fail func(error), boot *Booter) error {
- err := boot.wait(ctx, runPostgreSQL{})
+ err := boot.wait(ctx, runPostgreSQL{}, installPassenger{src: "services/api"})
if err != nil {
return err
}
diff --git a/lib/boot/service.go b/lib/boot/service.go
index 9672dccc4..6edf78b3c 100644
--- a/lib/boot/service.go
+++ b/lib/boot/service.go
@@ -5,18 +5,15 @@
package boot
import (
- "bytes"
"context"
- "fmt"
"path/filepath"
- "git.arvados.org/arvados.git/lib/cmd"
"git.arvados.org/arvados.git/sdk/go/arvados"
)
type runServiceCommand struct {
name string
- command cmd.Handler
+ svc arvados.Service
depends []bootTask
}
@@ -27,11 +24,10 @@ func (runner runServiceCommand) String() string {
func (runner runServiceCommand) Run(ctx context.Context, fail func(error), boot *Booter) error {
boot.wait(ctx, runner.depends...)
go func() {
- // runner.command.RunCommand() doesn't have access to
- // ctx, so it can't shut down by itself when the
- // caller cancels. We just abandon it.
- exitcode := runner.command.RunCommand(runner.name, []string{"-config", boot.configfile}, bytes.NewBuffer(nil), boot.Stderr, boot.Stderr)
- fail(fmt.Errorf("exit code %d", exitcode))
+ var u arvados.URL
+ for u = range runner.svc.InternalURLs {
+ }
+ fail(boot.RunProgram(ctx, boot.tempdir, nil, []string{"ARVADOS_SERVICE_INTERNAL_URL=" + u.String()}, "arvados-server", runner.name, "-config", boot.configfile))
}()
return nil
}
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index b42090fed..b10b3f008 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -17,7 +17,7 @@ http {
uwsgi_temp_path "{{TMPDIR}}";
scgi_temp_path "{{TMPDIR}}";
upstream arv-git-http {
- server localhost:{{GITPORT}};
+ server {{LISTENHOST}}:{{GITPORT}};
}
server {
listen *:{{GITSSLPORT}} ssl default_server;
@@ -33,7 +33,7 @@ http {
}
}
upstream keepproxy {
- server localhost:{{KEEPPROXYPORT}};
+ server {{LISTENHOST}}:{{KEEPPROXYPORT}};
}
server {
listen *:{{KEEPPROXYSSLPORT}} ssl default_server;
@@ -52,7 +52,7 @@ http {
}
}
upstream keep-web {
- server localhost:{{KEEPWEBPORT}};
+ server {{LISTENHOST}}:{{KEEPWEBPORT}};
}
server {
listen *:{{KEEPWEBSSLPORT}} ssl default_server;
@@ -89,7 +89,7 @@ http {
}
}
upstream ws {
- server localhost:{{WSPORT}};
+ server {{LISTENHOST}}:{{WSPORT}};
}
server {
listen *:{{WSSSLPORT}} ssl default_server;
@@ -107,7 +107,7 @@ http {
}
}
upstream workbench1 {
- server localhost:{{WORKBENCH1PORT}};
+ server {{LISTENHOST}}:{{WORKBENCH1PORT}};
}
server {
listen *:{{WORKBENCH1SSLPORT}} ssl default_server;
@@ -123,7 +123,7 @@ http {
}
}
upstream controller {
- server localhost:{{CONTROLLERPORT}};
+ server {{LISTENHOST}}:{{CONTROLLERPORT}};
}
server {
listen *:{{CONTROLLERSSLPORT}} ssl default_server;
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index 005c75edf..639eb87fd 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -606,6 +606,7 @@ def run_nginx():
return
stop_nginx()
nginxconf = {}
+ nginxconf['LISTENHOST'] = 'localhost'
nginxconf['CONTROLLERPORT'] = internal_port_from_config("Controller")
nginxconf['CONTROLLERSSLPORT'] = external_port_from_config("Controller")
nginxconf['KEEPWEBPORT'] = internal_port_from_config("WebDAV")
commit 2fb6eafd2fda2834b0181b908d44a93ff8b6da43
Author: Tom Clegg <tom at tomclegg.ca>
Date: Fri Feb 14 13:07:03 2020 -0500
15954: Add workbench1.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/nginx.go b/lib/boot/nginx.go
index b5b712af6..c5bfd605a 100644
--- a/lib/boot/nginx.go
+++ b/lib/boot/nginx.go
@@ -40,6 +40,7 @@ func (runNginx) Run(ctx context.Context, fail func(error), boot *Booter) error {
{"KEEPWEBDL", boot.cluster.Services.WebDAVDownload},
{"KEEPPROXY", boot.cluster.Services.Keepproxy},
{"GIT", boot.cluster.Services.GitHTTP},
+ {"WORKBENCH1", boot.cluster.Services.Workbench1},
{"WS", boot.cluster.Services.Websocket},
} {
vars[cmpt.varname+"PORT"], err = internalPort(cmpt.svc)
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index e9be12235..b42090fed 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -106,6 +106,22 @@ http {
proxy_redirect off;
}
}
+ upstream workbench1 {
+ server localhost:{{WORKBENCH1PORT}};
+ }
+ server {
+ listen *:{{WORKBENCH1SSLPORT}} ssl default_server;
+ server_name workbench1;
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
+ location / {
+ proxy_pass http://workbench1;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto https;
+ proxy_redirect off;
+ }
+ }
upstream controller {
server localhost:{{CONTROLLERPORT}};
}
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index 5c05c124c..005c75edf 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -617,6 +617,8 @@ def run_nginx():
nginxconf['GITSSLPORT'] = external_port_from_config("GitHTTP")
nginxconf['WSPORT'] = internal_port_from_config("Websocket")
nginxconf['WSSSLPORT'] = external_port_from_config("Websocket")
+ nginxconf['WORKBENCH1PORT'] = internal_port_from_config("Workbench1")
+ nginxconf['WORKBENCH1SSLPORT'] = external_port_from_config("Workbench1")
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')
@@ -648,6 +650,8 @@ def setup_config():
controller_external_port = find_available_port()
websocket_port = find_available_port()
websocket_external_port = find_available_port()
+ workbench1_port = find_available_port()
+ workbench1_external_port = find_available_port()
git_httpd_port = find_available_port()
git_httpd_external_port = find_available_port()
keepproxy_port = find_available_port()
@@ -683,6 +687,12 @@ def setup_config():
"http://%s:%s"%(localhost, websocket_port): {},
},
},
+ "Workbench1": {
+ "ExternalURL": "https://%s:%s/" % (localhost, workbench1_external_port),
+ "InternalURLs": {
+ "http://%s:%s"%(localhost, workbench1_port): {},
+ },
+ },
"GitHTTP": {
"ExternalURL": "https://%s:%s" % (localhost, git_httpd_external_port),
"InternalURLs": {
commit 8a471f18f22f85e996d2ce7110e7848aadfab44b
Author: Tom Clegg <tom at tomclegg.ca>
Date: Thu Feb 13 19:56:59 2020 -0500
15954: Add -controller-address flag.
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 7ce876805..c288bab75 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -18,7 +18,6 @@ import (
"os/exec"
"os/signal"
"path/filepath"
- "strconv"
"strings"
"sync"
"syscall"
@@ -82,6 +81,7 @@ func (bootCommand) RunCommand(prog string, args []string, stdin io.Reader, stdou
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")
+ flags.StringVar(&boot.ControllerAddr, "controller-address", ":0", "desired controller address, `host:port` or `:port`")
flags.BoolVar(&boot.OwnTemporaryDatabase, "own-temporary-database", false, "bring up a postgres server and create a temporary database")
err = flags.Parse(args)
if err == flag.ErrHelp {
@@ -111,6 +111,7 @@ type Booter struct {
SourcePath string // e.g., /home/username/src/arvados
LibPath string // e.g., /var/lib/arvados
ClusterType string // e.g., production
+ ControllerAddr string // e.g., 127.0.0.1:8000
OwnTemporaryDatabase bool
Stderr io.Writer
@@ -431,7 +432,37 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
if err != nil {
return err
}
- port := 9000
+ usedPort := map[string]bool{}
+ nextPort := func() string {
+ for {
+ port, err := availablePort(":0")
+ if err != nil {
+ panic(err)
+ }
+ if usedPort[port] {
+ continue
+ }
+ usedPort[port] = true
+ return port
+ }
+ }
+ if cluster.Services.Controller.ExternalURL.Host == "" {
+ h, p, err := net.SplitHostPort(boot.ControllerAddr)
+ if err != nil {
+ return err
+ }
+ if h == "" {
+ h = "localhost"
+ }
+ if p == "0" {
+ p, err = availablePort(":0")
+ if err != nil {
+ return err
+ }
+ usedPort[p] = true
+ }
+ cluster.Services.Controller.ExternalURL = arvados.URL{Scheme: "https", Host: net.JoinHostPort(h, p)}
+ }
for _, svc := range []*arvados.Service{
&cluster.Services.Controller,
&cluster.Services.DispatchCloud,
@@ -448,12 +479,6 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
if svc == &cluster.Services.DispatchCloud && boot.ClusterType == "test" {
continue
}
- 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 svc.ExternalURL.Host == "" && (svc == &cluster.Services.Controller ||
svc == &cluster.Services.GitHTTP ||
svc == &cluster.Services.Keepproxy ||
@@ -461,8 +486,12 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
svc == &cluster.Services.WebDAVDownload ||
svc == &cluster.Services.Websocket ||
svc == &cluster.Services.Workbench1) {
- port++
- svc.ExternalURL = arvados.URL{Scheme: "https", Host: fmt.Sprintf("localhost:%d", port)}
+ svc.ExternalURL = arvados.URL{Scheme: "https", Host: fmt.Sprintf("localhost:%s", nextPort())}
+ }
+ if len(svc.InternalURLs) == 0 {
+ svc.InternalURLs = map[arvados.URL]arvados.ServiceInstance{
+ arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%s", nextPort())}: arvados.ServiceInstance{},
+ }
}
}
if cluster.SystemRootToken == "" {
@@ -489,8 +518,7 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
}
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{}
+ cluster.Services.Keepstore.InternalURLs[arvados.URL{Scheme: "http", Host: fmt.Sprintf("localhost:%s", nextPort())}] = arvados.ServiceInstance{}
// Create a directory-backed volume for each keepstore
// process.
@@ -514,14 +542,10 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
}
}
if boot.OwnTemporaryDatabase {
- p, err := availablePort()
- if err != nil {
- return err
- }
cluster.PostgreSQL.Connection = arvados.PostgreSQLConnection{
"client_encoding": "utf8",
"host": "localhost",
- "port": strconv.Itoa(p),
+ "port": nextPort(),
"dbname": "arvados_test",
"user": "arvados",
"password": "insecure_arvados_test",
@@ -568,17 +592,17 @@ func externalPort(svc arvados.Service) (string, error) {
}
}
-func availablePort() (int, error) {
- ln, err := net.Listen("tcp", ":0")
+func availablePort(addr string) (string, error) {
+ ln, err := net.Listen("tcp", addr)
if err != nil {
- return 0, err
+ return "", err
}
defer ln.Close()
- _, p, err := net.SplitHostPort(ln.Addr().String())
+ _, port, err := net.SplitHostPort(ln.Addr().String())
if err != nil {
- return 0, err
+ return "", err
}
- return strconv.Atoi(p)
+ return port, nil
}
// Try to connect to addr until it works, then close ch. Give up if
commit 7a24a37aa9e5ed425550403b68c270316a24d772
Author: Tom Clegg <tom at tomclegg.ca>
Date: Thu Feb 13 19:56:48 2020 -0500
15954: Change components to tasks. Add rake db:setup.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/boot/cert.go b/lib/boot/cert.go
index 011f418e9..560579b77 100644
--- a/lib/boot/cert.go
+++ b/lib/boot/cert.go
@@ -10,7 +10,13 @@ import (
"path/filepath"
)
-func createCertificates(ctx context.Context, boot *Booter, ready chan<- bool) error {
+type createCertificates struct{}
+
+func (createCertificates) String() string {
+ return "certificates"
+}
+
+func (createCertificates) Run(ctx context.Context, fail func(error), boot *Booter) error {
// Generate root key
err := boot.RunProgram(ctx, boot.tempdir, nil, nil, "openssl", "genrsa", "-out", "rootCA.key", "4096")
if err != nil {
@@ -48,8 +54,5 @@ subjectAltName=DNS:localhost,DNS:localhost.localdomain
if err != nil {
return err
}
-
- close(ready)
- <-ctx.Done()
return nil
}
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 93f6ee0a8..7ce876805 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -36,6 +36,16 @@ import (
var Command cmd.Handler = bootCommand{}
+type bootTask interface {
+ // Execute the task. Run should return nil when the task is
+ // done enough to satisfy a dependency relationship (e.g., the
+ // service is running and ready). If the task starts a
+ // goroutine that fails after Run returns (e.g., the service
+ // shuts down), it should call cancel.
+ Run(ctx context.Context, fail func(error), boot *Booter) error
+ String() string
+}
+
type bootCommand struct{}
func (bootCommand) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
@@ -111,6 +121,7 @@ type Booter struct {
cancel context.CancelFunc
done chan struct{}
healthChecker *health.Aggregator
+ tasksReady map[string]chan bool
tempdir string
configfile string
@@ -214,48 +225,68 @@ func (boot *Booter) run(loader *config.Loader) error {
return err
}
- var wg sync.WaitGroup
- components := map[string]*component{
- "certificates": &component{runFunc: createCertificates},
- "database": &component{runFunc: runPostgres, depends: []string{"certificates"}},
- "nginx": &component{runFunc: runNginx},
- "controller": &component{cmdHandler: controller.Command, depends: []string{"database"}},
- "dispatchcloud": &component{cmdHandler: dispatchcloud.Command, notIfTest: true},
- "git-httpd": &component{goProg: "services/arv-git-httpd"},
- "health": &component{goProg: "services/health"},
- "keep-balance": &component{goProg: "services/keep-balance", notIfTest: true},
- "keepproxy": &component{goProg: "services/keepproxy"},
- "keepstore": &component{goProg: "services/keepstore", svc: boot.cluster.Services.Keepstore},
- "keep-web": &component{goProg: "services/keep-web"},
- "railsAPI": &component{svc: boot.cluster.Services.RailsAPI, railsApp: "services/api", depends: []string{"database"}},
- "workbench1": &component{svc: boot.cluster.Services.Workbench1, railsApp: "apps/workbench"},
- "ws": &component{goProg: "services/ws", depends: []string{"database"}},
- }
- for _, cmpt := range components {
- cmpt.ready = make(chan bool)
- }
- for name, cmpt := range components {
- name, cmpt := name, cmpt
- wg.Add(1)
- go func() {
- defer wg.Done()
- defer boot.cancel()
- for _, dep := range cmpt.depends {
- boot.logger.WithField("component", name).WithField("dependency", dep).Info("waiting")
- select {
- case <-components[dep].ready:
- case <-boot.ctx.Done():
- return
- }
+ tasks := []bootTask{
+ createCertificates{},
+ runPostgreSQL{},
+ runNginx{},
+ runServiceCommand{name: "controller", command: controller.Command, depends: []bootTask{runPostgreSQL{}}},
+ runGoProgram{src: "services/arv-git-httpd"},
+ runGoProgram{src: "services/health"},
+ runGoProgram{src: "services/keepproxy"},
+ runGoProgram{src: "services/keepstore", svc: boot.cluster.Services.Keepstore},
+ runGoProgram{src: "services/keep-web"},
+ runGoProgram{src: "services/ws", depends: []bootTask{runPostgreSQL{}}},
+ installPassenger{src: "services/api"},
+ runPassenger{src: "services/api", svc: boot.cluster.Services.RailsAPI, depends: []bootTask{createCertificates{}, runPostgreSQL{}, installPassenger{src: "services/api"}}},
+ installPassenger{src: "apps/workbench"},
+ runPassenger{src: "apps/workbench", svc: boot.cluster.Services.Workbench1, depends: []bootTask{installPassenger{src: "apps/workbench"}}},
+ seedDatabase{},
+ }
+ if boot.ClusterType != "test" {
+ tasks = append(tasks,
+ runServiceCommand{name: "dispatchcloud", command: dispatchcloud.Command},
+ runGoProgram{src: "services/keep-balance"},
+ )
+ }
+ boot.tasksReady = map[string]chan bool{}
+ for _, task := range tasks {
+ boot.tasksReady[task.String()] = make(chan bool)
+ }
+ for _, task := range tasks {
+ task := task
+ fail := func(err error) {
+ if boot.ctx.Err() != nil {
+ return
}
- boot.logger.WithField("component", name).Info("starting")
- err := cmpt.Run(boot.ctx, name, boot)
- if err != nil && err != context.Canceled {
- boot.logger.WithError(err).WithField("component", name).Error("exited")
+ boot.cancel()
+ boot.logger.WithField("task", task).WithError(err).Error("task failed")
+ }
+ go func() {
+ boot.logger.WithField("task", task).Info("starting")
+ err := task.Run(boot.ctx, fail, boot)
+ if err != nil {
+ fail(err)
+ return
}
+ close(boot.tasksReady[task.String()])
}()
}
- wg.Wait()
+ return boot.wait(boot.ctx, tasks...)
+}
+
+func (boot *Booter) wait(ctx context.Context, tasks ...bootTask) error {
+ for _, task := range tasks {
+ ch, ok := boot.tasksReady[task.String()]
+ if !ok {
+ return fmt.Errorf("no such task: %s", task)
+ }
+ boot.logger.WithField("task", task).Info("waiting")
+ select {
+ case <-ch:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
return nil
}
@@ -395,116 +426,6 @@ func (boot *Booter) RunProgram(ctx context.Context, dir string, output io.Writer
return nil
}
-type component struct {
- name string
- svc arvados.Service
- cmdHandler cmd.Handler
- runFunc func(ctx context.Context, boot *Booter, ready chan<- bool) 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
- depends []string // don't start until all of these components are ready
-
- ready chan bool
-}
-
-func (cmpt *component) Run(ctx context.Context, name string, boot *Booter) error {
- if cmpt.notIfTest && boot.ClusterType == "test" {
- fmt.Fprintf(boot.Stderr, "skipping component %q in %s mode\n", name, boot.ClusterType)
- <-ctx.Done()
- return nil
- }
- fmt.Fprintf(boot.Stderr, "starting component %q\n", name)
- if cmpt.cmdHandler != nil {
- errs := make(chan error, 1)
- go func() {
- defer close(errs)
- exitcode := cmpt.cmdHandler.RunCommand(name, []string{"-config", boot.configfile}, bytes.NewBuffer(nil), boot.Stderr, boot.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
- }
- }
- 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
- for u := range cmpt.svc.InternalURLs {
- u := u
- wg.Add(1)
- go func() {
- defer wg.Done()
- boot.RunProgram(ctx, boot.tempdir, nil, []string{"ARVADOS_SERVICE_INTERNAL_URL=" + u.String()}, basename)
- }()
- }
- wg.Wait()
- } else {
- // Just run one
- boot.RunProgram(ctx, boot.tempdir, nil, nil, basename)
- }
- return nil
- }
- if cmpt.runFunc != nil {
- return cmpt.runFunc(ctx, boot, cmpt.ready)
- }
- if cmpt.railsApp != "" {
- port, err := internalPort(cmpt.svc)
- if err != nil {
- return fmt.Errorf("bug: no InternalURLs for component %q: %v", name, cmpt.svc.InternalURLs)
- }
- var buf bytes.Buffer
- 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.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.railsApp, nil, nil, "bundle", "install", "--jobs", "4", "--path", filepath.Join(os.Getenv("HOME"), ".gem"))
- if err != nil {
- return err
- }
- 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.railsApp, nil, nil, "bundle", "exec", "passenger-config", "install-standalone-runtime")
- if err != nil {
- return err
- }
- err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "exec", "passenger-config", "validate-install")
- if err != nil {
- return err
- }
- err = boot.RunProgram(ctx, cmpt.railsApp, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
- if err != nil {
- return err
- }
- return nil
- }
- return fmt.Errorf("bug: component %q has nothing to run", name)
-}
-
func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger) error {
cluster, err := cfg.GetCluster("")
if err != nil {
@@ -662,7 +583,7 @@ func availablePort() (int, error) {
// Try to connect to addr until it works, then close ch. Give up if
// ctx cancels.
-func connectAndClose(ctx context.Context, addr string, ch chan<- bool) {
+func waitForConnect(ctx context.Context, addr string) error {
dialer := net.Dialer{Timeout: time.Second}
for ctx.Err() == nil {
conn, err := dialer.DialContext(ctx, "tcp", addr)
@@ -671,7 +592,7 @@ func connectAndClose(ctx context.Context, addr string, ch chan<- bool) {
continue
}
conn.Close()
- close(ch)
- return
+ return nil
}
+ return ctx.Err()
}
diff --git a/lib/boot/nginx.go b/lib/boot/nginx.go
index 2df5e90b3..b5b712af6 100644
--- a/lib/boot/nginx.go
+++ b/lib/boot/nginx.go
@@ -16,7 +16,13 @@ import (
"git.arvados.org/arvados.git/sdk/go/arvados"
)
-func runNginx(ctx context.Context, boot *Booter, ready chan<- bool) error {
+type runNginx struct{}
+
+func (runNginx) String() string {
+ return "nginx"
+}
+
+func (runNginx) Run(ctx context.Context, fail func(error), boot *Booter) 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
@@ -69,9 +75,11 @@ func runNginx(ctx context.Context, boot *Booter, ready chan<- bool) error {
}
}
}
- go connectAndClose(ctx, boot.cluster.Services.Controller.ExternalURL.Host, ready)
- return boot.RunProgram(ctx, ".", nil, nil, nginx,
- "-g", "error_log stderr info;",
- "-g", "pid "+filepath.Join(boot.tempdir, "nginx.pid")+";",
- "-c", conffile)
+ go func() {
+ fail(boot.RunProgram(ctx, ".", nil, nil, nginx,
+ "-g", "error_log stderr info;",
+ "-g", "pid "+filepath.Join(boot.tempdir, "nginx.pid")+";",
+ "-c", conffile))
+ }()
+ return waitForConnect(ctx, boot.cluster.Services.Controller.ExternalURL.Host)
}
diff --git a/lib/boot/passenger.go b/lib/boot/passenger.go
new file mode 100644
index 000000000..b67370e1e
--- /dev/null
+++ b/lib/boot/passenger.go
@@ -0,0 +1,90 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+)
+
+type installPassenger struct {
+ src string
+ depends []bootTask
+}
+
+func (runner installPassenger) String() string {
+ return "install " + runner.src
+}
+
+func (runner installPassenger) Run(ctx context.Context, fail func(error), boot *Booter) error {
+ err := boot.wait(ctx, runner.depends...)
+ if err != nil {
+ return err
+ }
+ var buf bytes.Buffer
+ err = boot.RunProgram(ctx, runner.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, runner.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, runner.src, nil, nil, "bundle", "install", "--jobs", "4", "--path", filepath.Join(os.Getenv("HOME"), ".gem"))
+ if err != nil {
+ return err
+ }
+ err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger-config", "build-native-support")
+ if err != nil {
+ return err
+ }
+ err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger-config", "install-standalone-runtime")
+ if err != nil {
+ return err
+ }
+ err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger-config", "validate-install")
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+type runPassenger struct {
+ src string
+ svc arvados.Service
+ depends []bootTask
+}
+
+func (runner runPassenger) String() string {
+ _, basename := filepath.Split(runner.src)
+ return basename
+}
+
+func (runner runPassenger) Run(ctx context.Context, fail func(error), boot *Booter) error {
+ err := boot.wait(ctx, runner.depends...)
+ if err != nil {
+ return err
+ }
+ port, err := internalPort(runner.svc)
+ if err != nil {
+ return fmt.Errorf("bug: no InternalURLs for component %q: %v", runner, runner.svc.InternalURLs)
+ }
+ err = boot.RunProgram(ctx, runner.src, nil, nil, "bundle", "exec", "passenger", "start", "-p", port)
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/lib/boot/postgresql.go b/lib/boot/postgresql.go
index 86328e110..48e24ffae 100644
--- a/lib/boot/postgresql.go
+++ b/lib/boot/postgresql.go
@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"database/sql"
+ "fmt"
"os"
"os/exec"
"path/filepath"
@@ -18,9 +19,20 @@ import (
"github.com/lib/pq"
)
-func runPostgres(ctx context.Context, boot *Booter, ready chan<- bool) error {
+type runPostgreSQL struct{}
+
+func (runPostgreSQL) String() string {
+ return "postgresql"
+}
+
+func (runPostgreSQL) Run(ctx context.Context, fail func(error), boot *Booter) error {
+ err := boot.wait(ctx, createCertificates{})
+ if err != nil {
+ return err
+ }
+
buf := bytes.NewBuffer(nil)
- err := boot.RunProgram(ctx, boot.tempdir, buf, nil, "pg_config", "--bindir")
+ err = boot.RunProgram(ctx, boot.tempdir, buf, nil, "pg_config", "--bindir")
if err != nil {
return err
}
@@ -44,57 +56,45 @@ func runPostgres(ctx context.Context, boot *Booter, ready chan<- bool) error {
port := boot.cluster.PostgreSQL.Connection["port"]
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
-
go func() {
- for {
- if ctx.Err() != nil {
- return
- }
- if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
- break
- }
- time.Sleep(time.Second / 2)
- }
- db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
- "host": datadir,
- "port": port,
- "dbname": "postgres",
- }.String())
- if err != nil {
- boot.logger.WithError(err).Error("db open failed")
- cancel()
- return
- }
- defer db.Close()
- conn, err := db.Conn(ctx)
- if err != nil {
- boot.logger.WithError(err).Error("db conn failed")
- cancel()
- return
- }
- defer conn.Close()
- _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
- if err != nil {
- boot.logger.WithError(err).Error("createuser failed")
- cancel()
- return
- }
- _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
- if err != nil {
- boot.logger.WithError(err).Error("createdb failed")
- cancel()
- return
- }
- close(ready)
- return
+ fail(boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
+ "-l", // enable ssl
+ "-D", datadir, // data dir
+ "-k", datadir, // socket dir
+ "-p", boot.cluster.PostgreSQL.Connection["port"],
+ ))
}()
- return boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
- "-l", // enable ssl
- "-D", datadir, // data dir
- "-k", datadir, // socket dir
- "-p", boot.cluster.PostgreSQL.Connection["port"],
- )
+ for {
+ if ctx.Err() != nil {
+ return ctx.Err()
+ }
+ if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
+ break
+ }
+ time.Sleep(time.Second / 2)
+ }
+ db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
+ "host": datadir,
+ "port": port,
+ "dbname": "postgres",
+ }.String())
+ if err != nil {
+ return fmt.Errorf("db open failed: %s", err)
+ }
+ defer db.Close()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ return fmt.Errorf("db conn failed: %s", err)
+ }
+ defer conn.Close()
+ _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
+ if err != nil {
+ return fmt.Errorf("createuser failed: %s", err)
+ }
+ _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
+ if err != nil {
+ return fmt.Errorf("createdb failed: %s", err)
+ }
+ return nil
}
diff --git a/lib/boot/seed.go b/lib/boot/seed.go
new file mode 100644
index 000000000..f915764c5
--- /dev/null
+++ b/lib/boot/seed.go
@@ -0,0 +1,27 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "context"
+)
+
+type seedDatabase struct{}
+
+func (seedDatabase) String() string {
+ return "seedDatabase"
+}
+
+func (seedDatabase) Run(ctx context.Context, fail func(error), boot *Booter) error {
+ err := boot.wait(ctx, runPostgreSQL{})
+ if err != nil {
+ return err
+ }
+ err = boot.RunProgram(ctx, "services/api", nil, nil, "bundle", "exec", "rake", "db:setup")
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/lib/boot/service.go b/lib/boot/service.go
new file mode 100644
index 000000000..9672dccc4
--- /dev/null
+++ b/lib/boot/service.go
@@ -0,0 +1,72 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "path/filepath"
+
+ "git.arvados.org/arvados.git/lib/cmd"
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+)
+
+type runServiceCommand struct {
+ name string
+ command cmd.Handler
+ depends []bootTask
+}
+
+func (runner runServiceCommand) String() string {
+ return runner.name
+}
+
+func (runner runServiceCommand) Run(ctx context.Context, fail func(error), boot *Booter) error {
+ boot.wait(ctx, runner.depends...)
+ go func() {
+ // runner.command.RunCommand() doesn't have access to
+ // ctx, so it can't shut down by itself when the
+ // caller cancels. We just abandon it.
+ exitcode := runner.command.RunCommand(runner.name, []string{"-config", boot.configfile}, bytes.NewBuffer(nil), boot.Stderr, boot.Stderr)
+ fail(fmt.Errorf("exit code %d", exitcode))
+ }()
+ return nil
+}
+
+type runGoProgram struct {
+ src string
+ svc arvados.Service
+ depends []bootTask
+}
+
+func (runner runGoProgram) String() string {
+ _, basename := filepath.Split(runner.src)
+ return basename
+}
+
+func (runner runGoProgram) Run(ctx context.Context, fail func(error), boot *Booter) error {
+ boot.wait(ctx, runner.depends...)
+ boot.RunProgram(ctx, runner.src, nil, nil, "go", "install")
+ if ctx.Err() != nil {
+ return ctx.Err()
+ }
+ _, basename := filepath.Split(runner.src)
+ if len(runner.svc.InternalURLs) > 0 {
+ // Run one for each URL
+ for u := range runner.svc.InternalURLs {
+ u := u
+ go func() {
+ fail(boot.RunProgram(ctx, boot.tempdir, nil, []string{"ARVADOS_SERVICE_INTERNAL_URL=" + u.String()}, basename))
+ }()
+ }
+ } else {
+ // Just run one
+ go func() {
+ fail(boot.RunProgram(ctx, boot.tempdir, nil, nil, basename))
+ }()
+ }
+ return nil
+}
commit 7abc7ca38954acd4eaa53c9280504e06a76b8d71
Author: Tom Clegg <tom at tomclegg.ca>
Date: Wed Feb 12 09:12:02 2020 -0500
15954: Start own postgresql server.
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 9e3d718ed..c63550edf 100644
--- a/doc/examples/config/zzzzz.yml
+++ b/doc/examples/config/zzzzz.yml
@@ -1,12 +1,5 @@
Clusters:
zzzzz:
- PostgreSQL:
- Connection:
- client_encoding: utf8
- host: localhost
- dbname: arvados_test
- user: arvados
- password: insecure_arvados_test
ManagementToken: e687950a23c3a9bceec28c6223a06c79
SystemRootToken: systemusertesttoken1234567890aoeuidhtnsqjkxbmwvzpy
API:
diff --git a/go.mod b/go.mod
index 2e16e5a0f..85e5552f6 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,7 @@ require (
github.com/jmcvetta/randutil v0.0.0-20150817122601-2bb1b664bcff
github.com/julienschmidt/httprouter v1.2.0
github.com/kevinburke/ssh_config v0.0.0-20171013211458-802051befeb5 // indirect
- github.com/lib/pq v0.0.0-20171126050459-83612a56d3dd
+ github.com/lib/pq v1.3.0
github.com/marstr/guid v1.1.1-0.20170427235115-8bdf7d1a087c // indirect
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
diff --git a/go.sum b/go.sum
index d7a022dda..6c2323a31 100644
--- a/go.sum
+++ b/go.sum
@@ -111,6 +111,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/lib/pq v0.0.0-20171126050459-83612a56d3dd h1:2RDaVc4/izhWyAvYxNm8c9saSyCDIxefNwOcqaH7pcU=
github.com/lib/pq v0.0.0-20171126050459-83612a56d3dd/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/marstr/guid v1.1.1-0.20170427235115-8bdf7d1a087c h1:ouxemItv3B/Zh008HJkEXDYCN3BIRyNHxtUN7ThJ5Js=
github.com/marstr/guid v1.1.1-0.20170427235115-8bdf7d1a087c/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
diff --git a/lib/boot/cert.go b/lib/boot/cert.go
new file mode 100644
index 000000000..011f418e9
--- /dev/null
+++ b/lib/boot/cert.go
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "context"
+ "io/ioutil"
+ "path/filepath"
+)
+
+func createCertificates(ctx context.Context, boot *Booter, ready chan<- bool) error {
+ // Generate root key
+ err := boot.RunProgram(ctx, boot.tempdir, nil, nil, "openssl", "genrsa", "-out", "rootCA.key", "4096")
+ if err != nil {
+ return err
+ }
+ // Generate a self-signed root certificate
+ err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "openssl", "req", "-x509", "-new", "-nodes", "-key", "rootCA.key", "-sha256", "-days", "3650", "-out", "rootCA.crt", "-subj", "/C=US/ST=MA/O=Example Org/CN=localhost")
+ if err != nil {
+ return err
+ }
+ // Generate server key
+ err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "openssl", "genrsa", "-out", "server.key", "2048")
+ if err != nil {
+ return err
+ }
+ // Build config file for signing request
+ defaultconf, err := ioutil.ReadFile("/etc/ssl/openssl.cnf")
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(filepath.Join(boot.tempdir, "server.cfg"), append(defaultconf, []byte(`
+[SAN]
+subjectAltName=DNS:localhost,DNS:localhost.localdomain
+`)...), 0777)
+ if err != nil {
+ return err
+ }
+ // Generate signing request
+ err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "openssl", "req", "-new", "-sha256", "-key", "server.key", "-subj", "/C=US/ST=MA/O=Example Org/CN=localhost", "-reqexts", "SAN", "-config", "server.cfg", "-out", "server.csr")
+ if err != nil {
+ return err
+ }
+ // Sign certificate
+ err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "openssl", "x509", "-req", "-in", "server.csr", "-CA", "rootCA.crt", "-CAkey", "rootCA.key", "-CAcreateserial", "-out", "server.crt", "-days", "3650", "-sha256")
+ if err != nil {
+ return err
+ }
+
+ close(ready)
+ <-ctx.Done()
+ return nil
+}
diff --git a/lib/boot/cmd.go b/lib/boot/cmd.go
index 4d2c01f2c..93f6ee0a8 100644
--- a/lib/boot/cmd.go
+++ b/lib/boot/cmd.go
@@ -18,6 +18,7 @@ import (
"os/exec"
"os/signal"
"path/filepath"
+ "strconv"
"strings"
"sync"
"syscall"
@@ -71,6 +72,7 @@ func (bootCommand) RunCommand(prog string, args []string, stdin io.Reader, stdou
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")
+ flags.BoolVar(&boot.OwnTemporaryDatabase, "own-temporary-database", false, "bring up a postgres server and create a temporary database")
err = flags.Parse(args)
if err == flag.ErrHelp {
err = nil
@@ -96,10 +98,11 @@ func (bootCommand) RunCommand(prog string, args []string, stdin io.Reader, stdou
}
type Booter struct {
- SourcePath string // e.g., /home/username/src/arvados
- LibPath string // e.g., /var/lib/arvados
- ClusterType string // e.g., production
- Stderr io.Writer
+ SourcePath string // e.g., /home/username/src/arvados
+ LibPath string // e.g., /var/lib/arvados
+ ClusterType string // e.g., production
+ OwnTemporaryDatabase bool
+ Stderr io.Writer
logger logrus.FieldLogger
cluster *arvados.Cluster
@@ -212,29 +215,43 @@ func (boot *Booter) run(loader *config.Loader) error {
}
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: "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: "workbench1", svc: boot.cluster.Services.Workbench1, railsApp: "apps/workbench"},
- {name: "ws", goProg: "services/ws"},
- } {
- cmpt := cmpt
+ components := map[string]*component{
+ "certificates": &component{runFunc: createCertificates},
+ "database": &component{runFunc: runPostgres, depends: []string{"certificates"}},
+ "nginx": &component{runFunc: runNginx},
+ "controller": &component{cmdHandler: controller.Command, depends: []string{"database"}},
+ "dispatchcloud": &component{cmdHandler: dispatchcloud.Command, notIfTest: true},
+ "git-httpd": &component{goProg: "services/arv-git-httpd"},
+ "health": &component{goProg: "services/health"},
+ "keep-balance": &component{goProg: "services/keep-balance", notIfTest: true},
+ "keepproxy": &component{goProg: "services/keepproxy"},
+ "keepstore": &component{goProg: "services/keepstore", svc: boot.cluster.Services.Keepstore},
+ "keep-web": &component{goProg: "services/keep-web"},
+ "railsAPI": &component{svc: boot.cluster.Services.RailsAPI, railsApp: "services/api", depends: []string{"database"}},
+ "workbench1": &component{svc: boot.cluster.Services.Workbench1, railsApp: "apps/workbench"},
+ "ws": &component{goProg: "services/ws", depends: []string{"database"}},
+ }
+ for _, cmpt := range components {
+ cmpt.ready = make(chan bool)
+ }
+ for name, cmpt := range components {
+ name, cmpt := name, cmpt
wg.Add(1)
go func() {
defer wg.Done()
defer boot.cancel()
- boot.logger.WithField("component", cmpt.name).Info("starting")
- err := cmpt.Run(boot.ctx, boot)
+ for _, dep := range cmpt.depends {
+ boot.logger.WithField("component", name).WithField("dependency", dep).Info("waiting")
+ select {
+ case <-components[dep].ready:
+ case <-boot.ctx.Done():
+ return
+ }
+ }
+ boot.logger.WithField("component", name).Info("starting")
+ err := cmpt.Run(boot.ctx, name, boot)
if err != nil && err != context.Canceled {
- boot.logger.WithError(err).WithField("component", cmpt.name).Error("exited")
+ boot.logger.WithError(err).WithField("component", name).Error("exited")
}
}()
}
@@ -382,24 +399,27 @@ type component struct {
name string
svc arvados.Service
cmdHandler cmd.Handler
- runFunc func(ctx context.Context, boot *Booter) 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
+ runFunc func(ctx context.Context, boot *Booter, ready chan<- bool) 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
+ depends []string // don't start until all of these components are ready
+
+ ready chan bool
}
-func (cmpt *component) Run(ctx context.Context, boot *Booter) error {
+func (cmpt *component) Run(ctx context.Context, name string, boot *Booter) error {
if cmpt.notIfTest && boot.ClusterType == "test" {
- fmt.Fprintf(boot.Stderr, "skipping component %q in %s mode\n", cmpt.name, boot.ClusterType)
+ fmt.Fprintf(boot.Stderr, "skipping component %q in %s mode\n", name, boot.ClusterType)
<-ctx.Done()
return nil
}
- fmt.Fprintf(boot.Stderr, "starting component %q\n", cmpt.name)
+ fmt.Fprintf(boot.Stderr, "starting component %q\n", name)
if cmpt.cmdHandler != nil {
errs := make(chan error, 1)
go func() {
defer close(errs)
- exitcode := cmpt.cmdHandler.RunCommand(cmpt.name, []string{"-config", boot.configfile}, bytes.NewBuffer(nil), boot.Stderr, boot.Stderr)
+ exitcode := cmpt.cmdHandler.RunCommand(name, []string{"-config", boot.configfile}, bytes.NewBuffer(nil), boot.Stderr, boot.Stderr)
if exitcode != 0 {
errs <- fmt.Errorf("exit code %d", exitcode)
}
@@ -439,12 +459,12 @@ func (cmpt *component) Run(ctx context.Context, boot *Booter) error {
return nil
}
if cmpt.runFunc != nil {
- return cmpt.runFunc(ctx, boot)
+ return cmpt.runFunc(ctx, boot, cmpt.ready)
}
if cmpt.railsApp != "" {
port, err := internalPort(cmpt.svc)
if err != nil {
- return fmt.Errorf("bug: no InternalURLs for component %q: %v", cmpt.name, cmpt.svc.InternalURLs)
+ return fmt.Errorf("bug: no InternalURLs for component %q: %v", name, cmpt.svc.InternalURLs)
}
var buf bytes.Buffer
err = boot.RunProgram(ctx, cmpt.railsApp, &buf, nil, "gem", "list", "--details", "bundler")
@@ -482,7 +502,7 @@ func (cmpt *component) Run(ctx context.Context, boot *Booter) error {
}
return nil
}
- return fmt.Errorf("bug: component %q has nothing to run", cmpt.name)
+ return fmt.Errorf("bug: component %q has nothing to run", name)
}
func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger) error {
@@ -572,6 +592,21 @@ func (boot *Booter) autofillConfig(cfg *arvados.Config, log logrus.FieldLogger)
}
}
}
+ if boot.OwnTemporaryDatabase {
+ p, err := availablePort()
+ if err != nil {
+ return err
+ }
+ cluster.PostgreSQL.Connection = arvados.PostgreSQLConnection{
+ "client_encoding": "utf8",
+ "host": "localhost",
+ "port": strconv.Itoa(p),
+ "dbname": "arvados_test",
+ "user": "arvados",
+ "password": "insecure_arvados_test",
+ }
+ }
+
cfg.Clusters[cluster.ClusterID] = *cluster
return nil
}
@@ -611,3 +646,32 @@ func externalPort(svc arvados.Service) (string, error) {
return "80", nil
}
}
+
+func availablePort() (int, error) {
+ ln, err := net.Listen("tcp", ":0")
+ if err != nil {
+ return 0, err
+ }
+ defer ln.Close()
+ _, p, err := net.SplitHostPort(ln.Addr().String())
+ if err != nil {
+ return 0, err
+ }
+ return strconv.Atoi(p)
+}
+
+// Try to connect to addr until it works, then close ch. Give up if
+// ctx cancels.
+func connectAndClose(ctx context.Context, addr string, ch chan<- bool) {
+ dialer := net.Dialer{Timeout: time.Second}
+ for ctx.Err() == nil {
+ conn, err := dialer.DialContext(ctx, "tcp", addr)
+ if err != nil {
+ time.Sleep(time.Second / 10)
+ continue
+ }
+ conn.Close()
+ close(ch)
+ return
+ }
+}
diff --git a/lib/boot/nginx.go b/lib/boot/nginx.go
index 1b361dd9c..2df5e90b3 100644
--- a/lib/boot/nginx.go
+++ b/lib/boot/nginx.go
@@ -16,7 +16,7 @@ import (
"git.arvados.org/arvados.git/sdk/go/arvados"
)
-func runNginx(ctx context.Context, boot *Booter) error {
+func runNginx(ctx context.Context, boot *Booter, ready chan<- bool) 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
@@ -69,6 +69,7 @@ func runNginx(ctx context.Context, boot *Booter) error {
}
}
}
+ go connectAndClose(ctx, boot.cluster.Services.Controller.ExternalURL.Host, ready)
return boot.RunProgram(ctx, ".", nil, nil, nginx,
"-g", "error_log stderr info;",
"-g", "pid "+filepath.Join(boot.tempdir, "nginx.pid")+";",
diff --git a/lib/boot/postgresql.go b/lib/boot/postgresql.go
new file mode 100644
index 000000000..86328e110
--- /dev/null
+++ b/lib/boot/postgresql.go
@@ -0,0 +1,100 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "bytes"
+ "context"
+ "database/sql"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+ "github.com/lib/pq"
+)
+
+func runPostgres(ctx context.Context, boot *Booter, ready chan<- bool) error {
+ buf := bytes.NewBuffer(nil)
+ err := boot.RunProgram(ctx, boot.tempdir, buf, nil, "pg_config", "--bindir")
+ if err != nil {
+ return err
+ }
+ datadir := filepath.Join(boot.tempdir, "pgdata")
+
+ err = os.Mkdir(datadir, 0755)
+ if err != nil {
+ return err
+ }
+ bindir := strings.TrimSpace(buf.String())
+
+ err = boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "initdb"), "-D", datadir)
+ if err != nil {
+ return err
+ }
+
+ err = boot.RunProgram(ctx, boot.tempdir, nil, nil, "cp", "server.crt", "server.key", datadir)
+ if err != nil {
+ return err
+ }
+
+ port := boot.cluster.PostgreSQL.Connection["port"]
+
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ go func() {
+ for {
+ if ctx.Err() != nil {
+ return
+ }
+ if exec.CommandContext(ctx, "pg_isready", "--timeout=10", "--host="+boot.cluster.PostgreSQL.Connection["host"], "--port="+port).Run() == nil {
+ break
+ }
+ time.Sleep(time.Second / 2)
+ }
+ db, err := sql.Open("postgres", arvados.PostgreSQLConnection{
+ "host": datadir,
+ "port": port,
+ "dbname": "postgres",
+ }.String())
+ if err != nil {
+ boot.logger.WithError(err).Error("db open failed")
+ cancel()
+ return
+ }
+ defer db.Close()
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ boot.logger.WithError(err).Error("db conn failed")
+ cancel()
+ return
+ }
+ defer conn.Close()
+ _, err = conn.ExecContext(ctx, `CREATE USER `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["user"])+` WITH SUPERUSER ENCRYPTED PASSWORD `+pq.QuoteLiteral(boot.cluster.PostgreSQL.Connection["password"]))
+ if err != nil {
+ boot.logger.WithError(err).Error("createuser failed")
+ cancel()
+ return
+ }
+ _, err = conn.ExecContext(ctx, `CREATE DATABASE `+pq.QuoteIdentifier(boot.cluster.PostgreSQL.Connection["dbname"]))
+ if err != nil {
+ boot.logger.WithError(err).Error("createdb failed")
+ cancel()
+ return
+ }
+ close(ready)
+ return
+ }()
+
+ return boot.RunProgram(ctx, boot.tempdir, nil, nil, filepath.Join(bindir, "postgres"),
+ "-l", // enable ssl
+ "-D", datadir, // data dir
+ "-k", datadir, // socket dir
+ "-p", boot.cluster.PostgreSQL.Connection["port"],
+ )
+}
commit b9fd7e3f374248a61159e4750a84e38d1c48d5dd
Merge: 0446c0a3a b01c43723
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Feb 11 11:39:44 2020 -0500
15954: Merge branch 'master'
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list