[ARVADOS] created: 1.1.4-384-gf777c7488
Git user
git at public.curoverse.com
Fri Jun 15 15:26:40 EDT 2018
at f777c74882e6b0f52b15f62d1d6251cd180979e4 (commit)
commit f777c74882e6b0f52b15f62d1d6251cd180979e4
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Fri Jun 15 15:26:24 2018 -0400
13497: Rename SystemNodes to NodeProfiles in config.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/controller/cmd.go b/lib/controller/cmd.go
index 02fb3afba..94eb2580b 100644
--- a/lib/controller/cmd.go
+++ b/lib/controller/cmd.go
@@ -12,6 +12,6 @@ import (
var Command cmd.Handler = service.Command(arvados.ServiceNameController, newHandler)
-func newHandler(cluster *arvados.Cluster, node *arvados.SystemNode) service.Handler {
- return &Handler{Cluster: cluster, Node: node}
+func newHandler(cluster *arvados.Cluster, np *arvados.NodeProfile) service.Handler {
+ return &Handler{Cluster: cluster, NodeProfile: np}
}
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index 643a932a5..59c2f2a61 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -20,8 +20,8 @@ import (
)
type Handler struct {
- Cluster *arvados.Cluster
- Node *arvados.SystemNode
+ Cluster *arvados.Cluster
+ NodeProfile *arvados.NodeProfile
setupOnce sync.Once
handlerStack http.Handler
@@ -63,7 +63,7 @@ var dropHeaders = map[string]bool{
}
func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
- urlOut, err := findRailsAPI(h.Cluster, h.Node)
+ urlOut, err := findRailsAPI(h.Cluster, h.NodeProfile)
if err != nil {
httpserver.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -123,8 +123,8 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
// For now, findRailsAPI always uses the rails API running on this
// node.
-func findRailsAPI(cluster *arvados.Cluster, node *arvados.SystemNode) (*url.URL, error) {
- hostport := node.RailsAPI.Listen
+func findRailsAPI(cluster *arvados.Cluster, np *arvados.NodeProfile) (*url.URL, error) {
+ hostport := np.RailsAPI.Listen
if len(hostport) > 1 && hostport[0] == ':' && strings.TrimRight(hostport[1:], "0123456789") == "" {
// ":12345" => connect to indicated port on localhost
hostport = "localhost" + hostport
@@ -134,7 +134,7 @@ func findRailsAPI(cluster *arvados.Cluster, node *arvados.SystemNode) (*url.URL,
return nil, err
}
proto := "http"
- if node.RailsAPI.TLS {
+ if np.RailsAPI.TLS {
proto = "https"
}
return url.Parse(proto + "://" + hostport)
diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go
index 70a337a6c..981ad7ab9 100644
--- a/lib/controller/handler_test.go
+++ b/lib/controller/handler_test.go
@@ -35,14 +35,14 @@ type HandlerSuite struct {
func (s *HandlerSuite) SetUpTest(c *check.C) {
s.cluster = &arvados.Cluster{
ClusterID: "zzzzz",
- SystemNodes: map[string]arvados.SystemNode{
+ NodeProfiles: map[string]arvados.NodeProfile{
"*": {
Controller: arvados.SystemServiceInstance{Listen: ":"},
RailsAPI: arvados.SystemServiceInstance{Listen: os.Getenv("ARVADOS_TEST_API_HOST"), TLS: true},
},
},
}
- node := s.cluster.SystemNodes["*"]
+ node := s.cluster.NodeProfiles["*"]
s.handler = newHandler(s.cluster, &node)
}
diff --git a/lib/service/cmd.go b/lib/service/cmd.go
index a144c01a6..4584939f7 100644
--- a/lib/service/cmd.go
+++ b/lib/service/cmd.go
@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"net/http"
+ "os"
"git.curoverse.com/arvados.git/lib/cmd"
"git.curoverse.com/arvados.git/sdk/go/arvados"
@@ -23,7 +24,7 @@ type Handler interface {
CheckHealth() error
}
-type NewHandlerFunc func(*arvados.Cluster, *arvados.SystemNode) Handler
+type NewHandlerFunc func(*arvados.Cluster, *arvados.NodeProfile) Handler
type command struct {
newHandler NewHandlerFunc
@@ -59,7 +60,7 @@ func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout
flags := flag.NewFlagSet("", flag.ContinueOnError)
flags.SetOutput(stderr)
configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
- hostName := flags.String("host", "", "Host profile `name` to use in SystemNodes config (if blank, use hostname reported by OS)")
+ nodeProfile := flags.String("node-profile", "", "`Name` of NodeProfiles config entry to use (if blank, use $ARVADOS_NODE_PROFILE or hostname reported by OS)")
err = flags.Parse(args)
if err == flag.ErrHelp {
err = nil
@@ -75,16 +76,20 @@ func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout
if err != nil {
return 1
}
- node, err := cluster.GetSystemNode(*hostName)
+ profileName := *nodeProfile
+ if profileName == "" {
+ profileName = os.Getenv("ARVADOS_NODE_PROFILE")
+ }
+ profile, err := cluster.GetNodeProfile(profileName)
if err != nil {
return 1
}
- listen := node.ServicePorts()[c.svcName]
+ listen := profile.ServicePorts()[c.svcName]
if listen == "" {
err = fmt.Errorf("configuration does not enable the %s service on this host", c.svcName)
return 1
}
- handler := c.newHandler(cluster, node)
+ handler := c.newHandler(cluster, profile)
if err = handler.CheckHealth(); err != nil {
return 1
}
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index a74c6d8d6..8856c9295 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -51,7 +51,7 @@ func (sc *Config) GetCluster(clusterID string) (*Cluster, error) {
type Cluster struct {
ClusterID string `json:"-"`
ManagementToken string
- SystemNodes map[string]SystemNode
+ NodeProfiles map[string]NodeProfile
InstanceTypes []InstanceType
HTTPRequestTimeout Duration
}
@@ -65,17 +65,11 @@ type InstanceType struct {
Price float64
}
-// GetThisSystemNode returns a SystemNode for the node we're running
-// on right now.
-func (cc *Cluster) GetThisSystemNode() (*SystemNode, error) {
- return cc.GetSystemNode("")
-}
-
-// GetSystemNode returns a SystemNode for the given hostname. An error
-// is returned if the appropriate configuration can't be determined
-// (e.g., this does not appear to be a system node). If node is empty,
-// use the OS-reported hostname.
-func (cc *Cluster) GetSystemNode(node string) (*SystemNode, error) {
+// GetNodeProfile returns a NodeProfile for the given hostname. An
+// error is returned if the appropriate configuration can't be
+// determined (e.g., this does not appear to be a system node). If
+// node is empty, use the OS-reported hostname.
+func (cc *Cluster) GetNodeProfile(node string) (*NodeProfile, error) {
if node == "" {
hostname, err := os.Hostname()
if err != nil {
@@ -83,18 +77,18 @@ func (cc *Cluster) GetSystemNode(node string) (*SystemNode, error) {
}
node = hostname
}
- if cfg, ok := cc.SystemNodes[node]; ok {
+ if cfg, ok := cc.NodeProfiles[node]; ok {
return &cfg, nil
}
// If node is not listed, but "*" gives a default system node
// config, use the default config.
- if cfg, ok := cc.SystemNodes["*"]; ok {
+ if cfg, ok := cc.NodeProfiles["*"]; ok {
return &cfg, nil
}
return nil, fmt.Errorf("config does not provision host %q as a system node", node)
}
-type SystemNode struct {
+type NodeProfile struct {
Controller SystemServiceInstance `json:"arvados-controller"`
Health SystemServiceInstance `json:"arvados-health"`
Keepproxy SystemServiceInstance `json:"keepproxy"`
@@ -121,16 +115,16 @@ const (
// ServicePorts returns the configured listening address (or "" if
// disabled) for each service on the node.
-func (sn *SystemNode) ServicePorts() map[ServiceName]string {
+func (np *NodeProfile) ServicePorts() map[ServiceName]string {
return map[ServiceName]string{
- ServiceNameRailsAPI: sn.RailsAPI.Listen,
- ServiceNameController: sn.Controller.Listen,
- ServiceNameNodemanager: sn.Nodemanager.Listen,
- ServiceNameWorkbench: sn.Workbench.Listen,
- ServiceNameWebsocket: sn.Websocket.Listen,
- ServiceNameKeepweb: sn.Keepweb.Listen,
- ServiceNameKeepproxy: sn.Keepproxy.Listen,
- ServiceNameKeepstore: sn.Keepstore.Listen,
+ ServiceNameRailsAPI: np.RailsAPI.Listen,
+ ServiceNameController: np.Controller.Listen,
+ ServiceNameNodemanager: np.Nodemanager.Listen,
+ ServiceNameWorkbench: np.Workbench.Listen,
+ ServiceNameWebsocket: np.Websocket.Listen,
+ ServiceNameKeepweb: np.Keepweb.Listen,
+ ServiceNameKeepproxy: np.Keepproxy.Listen,
+ ServiceNameKeepstore: np.Keepstore.Listen,
}
}
diff --git a/sdk/go/health/aggregator.go b/sdk/go/health/aggregator.go
index 3f3a91800..a6cb8798a 100644
--- a/sdk/go/health/aggregator.go
+++ b/sdk/go/health/aggregator.go
@@ -113,8 +113,8 @@ func (agg *Aggregator) ClusterHealth(cluster *arvados.Cluster) ClusterHealthResp
mtx := sync.Mutex{}
wg := sync.WaitGroup{}
- for node, nodeConfig := range cluster.SystemNodes {
- for svc, addr := range nodeConfig.ServicePorts() {
+ for profileName, profile := range cluster.NodeProfiles {
+ for svc, addr := range profile.ServicePorts() {
// Ensure svc is listed in resp.Services.
mtx.Lock()
if _, ok := resp.Services[svc]; !ok {
@@ -128,10 +128,10 @@ func (agg *Aggregator) ClusterHealth(cluster *arvados.Cluster) ClusterHealthResp
}
wg.Add(1)
- go func(node string, svc arvados.ServiceName, addr string) {
+ go func(profileName string, svc arvados.ServiceName, addr string) {
defer wg.Done()
var result CheckResult
- url, err := agg.pingURL(node, addr)
+ url, err := agg.pingURL(profileName, addr)
if err != nil {
result = CheckResult{
Health: "ERROR",
@@ -152,7 +152,7 @@ func (agg *Aggregator) ClusterHealth(cluster *arvados.Cluster) ClusterHealthResp
} else {
resp.Health = "ERROR"
}
- }(node, svc, addr)
+ }(profileName, svc, addr)
}
}
wg.Wait()
diff --git a/sdk/go/health/aggregator_test.go b/sdk/go/health/aggregator_test.go
index 4c652254b..a96ed136c 100644
--- a/sdk/go/health/aggregator_test.go
+++ b/sdk/go/health/aggregator_test.go
@@ -34,7 +34,7 @@ func (s *AggregatorSuite) SetUpTest(c *check.C) {
Clusters: map[string]arvados.Cluster{
"zzzzz": {
ManagementToken: arvadostest.ManagementToken,
- SystemNodes: map[string]arvados.SystemNode{},
+ NodeProfiles: map[string]arvados.NodeProfile{},
},
},
}}
@@ -86,7 +86,7 @@ func (*unhealthyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request)
func (s *AggregatorSuite) TestUnhealthy(c *check.C) {
srv, listen := s.stubServer(&unhealthyHandler{})
defer srv.Close()
- s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
+ s.handler.Config.Clusters["zzzzz"].NodeProfiles["localhost"] = arvados.NodeProfile{
Keepstore: arvados.SystemServiceInstance{Listen: listen},
}
s.handler.ServeHTTP(s.resp, s.req)
@@ -106,7 +106,7 @@ func (*healthyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
func (s *AggregatorSuite) TestHealthy(c *check.C) {
srv, listen := s.stubServer(&healthyHandler{})
defer srv.Close()
- s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
+ s.handler.Config.Clusters["zzzzz"].NodeProfiles["localhost"] = arvados.NodeProfile{
Controller: arvados.SystemServiceInstance{Listen: listen},
Keepproxy: arvados.SystemServiceInstance{Listen: listen},
Keepstore: arvados.SystemServiceInstance{Listen: listen},
@@ -130,7 +130,7 @@ func (s *AggregatorSuite) TestHealthyAndUnhealthy(c *check.C) {
defer srvH.Close()
srvU, listenU := s.stubServer(&unhealthyHandler{})
defer srvU.Close()
- s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
+ s.handler.Config.Clusters["zzzzz"].NodeProfiles["localhost"] = arvados.NodeProfile{
Controller: arvados.SystemServiceInstance{Listen: listenH},
Keepproxy: arvados.SystemServiceInstance{Listen: listenH},
Keepstore: arvados.SystemServiceInstance{Listen: listenH},
@@ -140,7 +140,7 @@ func (s *AggregatorSuite) TestHealthyAndUnhealthy(c *check.C) {
Websocket: arvados.SystemServiceInstance{Listen: listenH},
Workbench: arvados.SystemServiceInstance{Listen: listenH},
}
- s.handler.Config.Clusters["zzzzz"].SystemNodes["127.0.0.1"] = arvados.SystemNode{
+ s.handler.Config.Clusters["zzzzz"].NodeProfiles["127.0.0.1"] = arvados.NodeProfile{
Keepstore: arvados.SystemServiceInstance{Listen: listenU},
}
s.handler.ServeHTTP(s.resp, s.req)
@@ -194,7 +194,7 @@ func (s *AggregatorSuite) TestPingTimeout(c *check.C) {
s.handler.timeout = arvados.Duration(100 * time.Millisecond)
srv, listen := s.stubServer(&slowHandler{})
defer srv.Close()
- s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
+ s.handler.Config.Clusters["zzzzz"].NodeProfiles["localhost"] = arvados.NodeProfile{
Keepstore: arvados.SystemServiceInstance{Listen: listen},
}
s.handler.ServeHTTP(s.resp, s.req)
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index dc4d721f1..f7ca6daf6 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -408,7 +408,7 @@ def run_controller():
f.write("""
Clusters:
zzzzz:
- SystemNodes:
+ NodeProfiles:
"*":
"arvados-controller":
Listen: ":{}"
diff --git a/services/health/main.go b/services/health/main.go
index 376d4830b..1d2ec47a6 100644
--- a/services/health/main.go
+++ b/services/health/main.go
@@ -41,7 +41,7 @@ func main() {
if err != nil {
log.Fatal(err)
}
- nodeCfg, err := clusterCfg.GetThisSystemNode()
+ nodeCfg, err := clusterCfg.GetNodeProfile("")
if err != nil {
log.Fatal(err)
}
commit 1b5156270c5cb8d7a4a1b095d981f1a84a98554f
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Fri Jun 15 15:08:59 2018 -0400
13497: Abort startup if Rails API cannot be found.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/controller/cmd.go b/lib/controller/cmd.go
index e006b6594..02fb3afba 100644
--- a/lib/controller/cmd.go
+++ b/lib/controller/cmd.go
@@ -5,8 +5,6 @@
package controller
import (
- "net/http"
-
"git.curoverse.com/arvados.git/lib/cmd"
"git.curoverse.com/arvados.git/lib/service"
"git.curoverse.com/arvados.git/sdk/go/arvados"
@@ -14,6 +12,6 @@ import (
var Command cmd.Handler = service.Command(arvados.ServiceNameController, newHandler)
-func newHandler(cluster *arvados.Cluster, node *arvados.SystemNode) http.Handler {
+func newHandler(cluster *arvados.Cluster, node *arvados.SystemNode) service.Handler {
return &Handler{Cluster: cluster, Node: node}
}
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index ad765bafa..643a932a5 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -33,6 +33,12 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.handlerStack.ServeHTTP(w, req)
}
+func (h *Handler) CheckHealth() error {
+ h.setupOnce.Do(h.setup)
+ _, err := findRailsAPI(h.Cluster, h.NodeProfile)
+ return err
+}
+
func (h *Handler) setup() {
mux := http.NewServeMux()
mux.Handle("/_health/", &health.Handler{
diff --git a/lib/service/cmd.go b/lib/service/cmd.go
index ab7886066..a144c01a6 100644
--- a/lib/service/cmd.go
+++ b/lib/service/cmd.go
@@ -18,7 +18,12 @@ import (
"github.com/coreos/go-systemd/daemon"
)
-type NewHandlerFunc func(*arvados.Cluster, *arvados.SystemNode) http.Handler
+type Handler interface {
+ http.Handler
+ CheckHealth() error
+}
+
+type NewHandlerFunc func(*arvados.Cluster, *arvados.SystemNode) Handler
type command struct {
newHandler NewHandlerFunc
@@ -79,9 +84,13 @@ func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout
err = fmt.Errorf("configuration does not enable the %s service on this host", c.svcName)
return 1
}
+ handler := c.newHandler(cluster, node)
+ if err = handler.CheckHealth(); err != nil {
+ return 1
+ }
srv := &httpserver.Server{
Server: http.Server{
- Handler: httpserver.AddRequestIDs(httpserver.LogRequests(log, c.newHandler(cluster, node))),
+ Handler: httpserver.AddRequestIDs(httpserver.LogRequests(log, handler)),
},
Addr: listen,
}
commit b58c06e93fc1392aea0347ea099376b41ec4b7c3
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Fri Jun 15 15:06:02 2018 -0400
13497: Support "run-tests.sh --only go".
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index ad7960275..210f11f20 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -652,8 +652,9 @@ do_test() {
;;
esac
if [[ -z "${skip[$suite]}" && -z "${skip[$1]}" && \
- (-z "${only}" || "${only}" == "${suite}" || \
- "${only}" == "${1}") ]]; then
+ (-z "${only}" || "${only}" == "${suite}" || \
+ "${only}" == "${1}") ||
+ "${only}" == "${2}" ]]; then
retry do_test_once ${@}
else
title "Skipping ${1} tests"
commit a1f0e517f6b37ea987c0146a4ca93f50715f00f2
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Fri Jun 15 15:05:17 2018 -0400
13497: Bump version numbers for Go packages when lib/ changes.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-library.sh b/build/run-library.sh
index fb4df6a79..4b18d037b 100755
--- a/build/run-library.sh
+++ b/build/run-library.sh
@@ -129,10 +129,7 @@ package_go_binary() {
# Arvados SDK and the SDK has changed.
declare -a checkdirs=(vendor)
if grep -qr git.curoverse.com/arvados .; then
- checkdirs+=(sdk/go)
- if [[ "$prog" -eq "crunch-dispatch-slurm" ]]; then
- checkdirs+=(lib/dispatchcloud)
- fi
+ checkdirs+=(sdk/go lib)
fi
for dir in ${checkdirs[@]}; do
cd "$GOPATH/src/git.curoverse.com/arvados.git/$dir"
commit 21c5372c6b670820e842e01336eb6b191d6e10b7
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 14 17:03:24 2018 -0400
13497: Quote file paths in test suite nginx.conf.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index ce1b17062..ce1929fdf 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -7,7 +7,7 @@ error_log "{{ERRORLOG}}" info; # Yes, must be specified here _and_ cmdl
events {
}
http {
- access_log {{ACCESSLOG}} combined;
+ access_log "{{ACCESSLOG}}" combined;
client_body_temp_path "{{TMPDIR}}";
upstream arv-git-http {
server localhost:{{GITPORT}};
@@ -15,8 +15,8 @@ http {
server {
listen *:{{GITSSLPORT}} ssl default_server;
server_name _;
- ssl_certificate {{SSLCERT}};
- ssl_certificate_key {{SSLKEY}};
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
location / {
proxy_pass http://arv-git-http;
}
@@ -27,8 +27,8 @@ http {
server {
listen *:{{KEEPPROXYSSLPORT}} ssl default_server;
server_name _;
- ssl_certificate {{SSLCERT}};
- ssl_certificate_key {{SSLKEY}};
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
location / {
proxy_pass http://keepproxy;
}
@@ -39,8 +39,8 @@ http {
server {
listen *:{{KEEPWEBSSLPORT}} ssl default_server;
server_name ~^(?<request_host>.*)$;
- ssl_certificate {{SSLCERT}};
- ssl_certificate_key {{SSLKEY}};
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
location / {
proxy_pass http://keep-web;
proxy_set_header Host $request_host:{{KEEPWEBPORT}};
@@ -50,8 +50,8 @@ http {
server {
listen *:{{KEEPWEBDLSSLPORT}} ssl default_server;
server_name ~.*;
- ssl_certificate {{SSLCERT}};
- ssl_certificate_key {{SSLKEY}};
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
location / {
proxy_pass http://keep-web;
proxy_set_header Host download:{{KEEPWEBPORT}};
@@ -65,8 +65,8 @@ http {
server {
listen *:{{WSSPORT}} ssl default_server;
server_name ~^(?<request_host>.*)$;
- ssl_certificate {{SSLCERT}};
- ssl_certificate_key {{SSLKEY}};
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
location / {
proxy_pass http://ws;
proxy_set_header Upgrade $http_upgrade;
@@ -81,8 +81,8 @@ http {
server {
listen *:{{CONTROLLERSSLPORT}} ssl default_server;
server_name _;
- ssl_certificate {{SSLCERT}};
- ssl_certificate_key {{SSLKEY}};
+ ssl_certificate "{{SSLCERT}}";
+ ssl_certificate_key "{{SSLKEY}}";
location / {
proxy_pass http://controller;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
commit 4a98eba9ae08ffccb822842f74b1b805302a1ad1
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 14 17:02:10 2018 -0400
13497: Suppress nginx "info" logs from console.
(unless ARVADOS_DEBUG is set)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index bda67e630..ce1b17062 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
daemon off;
-error_log stderr info; # Yes, must be specified here _and_ cmdline
+error_log "{{ERRORLOG}}" info; # Yes, must be specified here _and_ cmdline
events {
}
http {
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index 258cc38d6..dc4d721f1 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -666,6 +666,7 @@ def run_nginx():
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')
+ nginxconf['ERRORLOG'] = _logfilename('nginx_error')
nginxconf['TMPDIR'] = TEST_TMPDIR
conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
commit e26dc8ebc182bec997624213c771f06e9b0179e8
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 14 16:29:11 2018 -0400
13497: Send test server logs to {workspace}/tmp/*.log.
Don't send them to console unless ARVADOS_DEBUG is set.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index 52269fd6e..ad7960275 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -413,6 +413,8 @@ do
fi
done
+rm -vf "${WORKSPACE}/tmp/*.log"
+
setup_ruby_environment() {
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
source "$HOME/.rvm/scripts/rvm"
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index f0fbfe742..258cc38d6 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -202,14 +202,21 @@ def _wait_until_port_listens(port, timeout=10):
format(port, timeout),
file=sys.stderr)
-def _fifo2stderr(label):
- """Create a fifo, and copy it to stderr, prepending label to each line.
+def _logfilename(label):
+ """Set up a labelled log file, and return a path to write logs to.
- Return value is the path to the new FIFO.
+ Normally, the returned path is {tmpdir}/{label}.log.
+
+ In debug mode, logs are also written to stderr, with [label]
+ prepended to each line. The returned path is a FIFO.
+label+ should contain only alphanumerics: it is also used as part
of the FIFO filename.
+
"""
+ logfilename = os.path.join(TEST_TMPDIR, label+'.log')
+ if not os.environ.get('ARVADOS_DEBUG', ''):
+ return logfilename
fifo = os.path.join(TEST_TMPDIR, label+'.fifo')
try:
os.remove(fifo)
@@ -217,8 +224,21 @@ def _fifo2stderr(label):
if error.errno != errno.ENOENT:
raise
os.mkfifo(fifo, 0o700)
+ stdbuf = ['stdbuf', '-i0', '-oL', '-eL']
+ # open(fifo, 'r') would block waiting for someone to open the fifo
+ # for writing, so we need a separate cat process to open it for
+ # us.
+ cat = subprocess.Popen(
+ stdbuf+['cat', fifo],
+ stdin=open('/dev/null'),
+ stdout=subprocess.PIPE)
+ tee = subprocess.Popen(
+ stdbuf+['tee', '-a', logfilename],
+ stdin=cat.stdout,
+ stdout=subprocess.PIPE)
subprocess.Popen(
- ['stdbuf', '-i0', '-oL', '-eL', 'sed', '-e', 's/^/['+label+'] /', fifo],
+ stdbuf+['sed', '-e', 's/^/['+label+'] /'],
+ stdin=tee.stdout,
stdout=sys.stderr)
return fifo
@@ -396,7 +416,7 @@ Clusters:
Listen: ":{}"
TLS: true
""".format(port, rails_api_port))
- logf = open(_fifo2stderr('controller'), 'w')
+ logf = open(_logfilename('controller'), 'a')
controller = subprocess.Popen(
["arvados-server", "controller", "-config", conf],
stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
@@ -437,7 +457,7 @@ Postgres:
_dbconfig('database'),
_dbconfig('username'),
_dbconfig('password')))
- logf = open(_fifo2stderr('ws'), 'w')
+ logf = open(_logfilename('ws'), 'a')
ws = subprocess.Popen(
["ws", "-config", conf],
stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
@@ -463,7 +483,7 @@ def _start_keep(n, keep_args):
for arg, val in keep_args.items():
keep_cmd.append("{}={}".format(arg, val))
- logf = open(_fifo2stderr('keep{}'.format(n)), 'w')
+ logf = open(_logfilename('keep{}'.format(n)), 'a')
kp0 = subprocess.Popen(
keep_cmd, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
@@ -547,7 +567,7 @@ def run_keep_proxy():
port = find_available_port()
env = os.environ.copy()
env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
- logf = open(_fifo2stderr('keepproxy'), 'w')
+ logf = open(_logfilename('keepproxy'), 'a')
kp = subprocess.Popen(
['keepproxy',
'-pid='+_pidfile('keepproxy'),
@@ -586,7 +606,7 @@ def run_arv_git_httpd():
gitport = find_available_port()
env = os.environ.copy()
env.pop('ARVADOS_API_TOKEN', None)
- logf = open(_fifo2stderr('arv-git-httpd'), 'w')
+ logf = open(_logfilename('arv-git-httpd'), 'a')
agh = subprocess.Popen(
['arv-git-httpd',
'-repo-root='+gitdir+'/test',
@@ -610,7 +630,7 @@ def run_keep_web():
keepwebport = find_available_port()
env = os.environ.copy()
env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
- logf = open(_fifo2stderr('keep-web'), 'w')
+ logf = open(_logfilename('keep-web'), 'a')
keepweb = subprocess.Popen(
['keep-web',
'-allow-anonymous',
@@ -645,7 +665,7 @@ def run_nginx():
nginxconf['WSSPORT'] = _getport('wss')
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'] = _fifo2stderr('nginx_access_log')
+ nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
nginxconf['TMPDIR'] = TEST_TMPDIR
conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
commit 46434cfe5a053097440bcccc35d0ce7d00bbcfee
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 14 15:37:13 2018 -0400
13497: Add systemd unit to arvados-controller package.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/cmd/arvados-server/arvados-controller.service b/cmd/arvados-server/arvados-controller.service
new file mode 100644
index 000000000..fbd73e334
--- /dev/null
+++ b/cmd/arvados-server/arvados-controller.service
@@ -0,0 +1,27 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+[Unit]
+Description=Arvados controller
+Documentation=https://doc.arvados.org/
+After=network.target
+AssertPathExists=/etc/arvados/config.yml
+
+# systemd==229 (ubuntu:xenial) obeys StartLimitInterval in the [Unit] section
+StartLimitInterval=0
+
+# systemd>=230 (debian:9) obeys StartLimitIntervalSec in the [Unit] section
+StartLimitIntervalSec=0
+
+[Service]
+Type=notify
+ExecStart=/usr/bin/arvados-controller
+Restart=always
+RestartSec=1
+
+# systemd<=219 (centos:7, debian:8, ubuntu:trusty) obeys StartLimitInterval in the [Service] section
+StartLimitInterval=0
+
+[Install]
+WantedBy=multi-user.target
diff --git a/lib/service/cmd.go b/lib/service/cmd.go
index 3447bfb16..ab7886066 100644
--- a/lib/service/cmd.go
+++ b/lib/service/cmd.go
@@ -15,6 +15,7 @@ import (
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/httpserver"
"github.com/Sirupsen/logrus"
+ "github.com/coreos/go-systemd/daemon"
)
type NewHandlerFunc func(*arvados.Cluster, *arvados.SystemNode) http.Handler
@@ -92,6 +93,9 @@ func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout
"Listen": srv.Addr,
"Service": c.svcName,
}).Info("listening")
+ if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
+ log.WithError(err).Errorf("error notifying init daemon")
+ }
err = srv.Wait()
if err != nil {
return 1
commit 18d6239d25924545ba91825011d467861cd5513c
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 14 15:23:08 2018 -0400
13497: Accept host key on command line.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/service/cmd.go b/lib/service/cmd.go
index e59ac486a..3447bfb16 100644
--- a/lib/service/cmd.go
+++ b/lib/service/cmd.go
@@ -53,6 +53,7 @@ func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout
flags := flag.NewFlagSet("", flag.ContinueOnError)
flags.SetOutput(stderr)
configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
+ hostName := flags.String("host", "", "Host profile `name` to use in SystemNodes config (if blank, use hostname reported by OS)")
err = flags.Parse(args)
if err == flag.ErrHelp {
err = nil
@@ -68,7 +69,7 @@ func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout
if err != nil {
return 1
}
- node, err := cluster.GetThisSystemNode()
+ node, err := cluster.GetSystemNode(*hostName)
if err != nil {
return 1
}
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index 16d93362a..a74c6d8d6 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -68,17 +68,21 @@ type InstanceType struct {
// GetThisSystemNode returns a SystemNode for the node we're running
// on right now.
func (cc *Cluster) GetThisSystemNode() (*SystemNode, error) {
- hostname, err := os.Hostname()
- if err != nil {
- return nil, err
- }
- return cc.GetSystemNode(hostname)
+ return cc.GetSystemNode("")
}
// GetSystemNode returns a SystemNode for the given hostname. An error
// is returned if the appropriate configuration can't be determined
-// (e.g., this does not appear to be a system node).
+// (e.g., this does not appear to be a system node). If node is empty,
+// use the OS-reported hostname.
func (cc *Cluster) GetSystemNode(node string) (*SystemNode, error) {
+ if node == "" {
+ hostname, err := os.Hostname()
+ if err != nil {
+ return nil, err
+ }
+ node = hostname
+ }
if cfg, ok := cc.SystemNodes[node]; ok {
return &cfg, nil
}
commit 48fd863c654325eefceb8dfd182c88a8149ca309
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 14 13:34:58 2018 -0400
13497: Add controller to health check.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/sdk/go/health/aggregator_test.go b/sdk/go/health/aggregator_test.go
index 8a540371c..4c652254b 100644
--- a/sdk/go/health/aggregator_test.go
+++ b/sdk/go/health/aggregator_test.go
@@ -107,6 +107,7 @@ func (s *AggregatorSuite) TestHealthy(c *check.C) {
srv, listen := s.stubServer(&healthyHandler{})
defer srv.Close()
s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
+ Controller: arvados.SystemServiceInstance{Listen: listen},
Keepproxy: arvados.SystemServiceInstance{Listen: listen},
Keepstore: arvados.SystemServiceInstance{Listen: listen},
Keepweb: arvados.SystemServiceInstance{Listen: listen},
@@ -130,6 +131,7 @@ func (s *AggregatorSuite) TestHealthyAndUnhealthy(c *check.C) {
srvU, listenU := s.stubServer(&unhealthyHandler{})
defer srvU.Close()
s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
+ Controller: arvados.SystemServiceInstance{Listen: listenH},
Keepproxy: arvados.SystemServiceInstance{Listen: listenH},
Keepstore: arvados.SystemServiceInstance{Listen: listenH},
Keepweb: arvados.SystemServiceInstance{Listen: listenH},
commit 8666f138c10e2a201ee288770f29c5a20b9fc706
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Jun 13 16:59:52 2018 -0400
13497: Set usable path for nginx request body buffering.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index d818c5f9c..bda67e630 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -8,6 +8,7 @@ events {
}
http {
access_log {{ACCESSLOG}} combined;
+ client_body_temp_path "{{TMPDIR}}";
upstream arv-git-http {
server localhost:{{GITPORT}};
}
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index d8a21204a..f0fbfe742 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -646,6 +646,7 @@ def run_nginx():
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'] = _fifo2stderr('nginx_access_log')
+ nginxconf['TMPDIR'] = TEST_TMPDIR
conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
conffile = os.path.join(TEST_TMPDIR, 'nginx.conf')
commit 488bc59b2d90e0a9a23801b034c8a54525d83da4
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Jun 13 16:43:41 2018 -0400
13497: Remove alternate integration test glue in Workbench.
Expect run-tests.sh to have set everything up correctly instead.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/apps/workbench/test/test_helper.rb b/apps/workbench/test/test_helper.rb
index 60dadec61..2fd926ff1 100644
--- a/apps/workbench/test/test_helper.rb
+++ b/apps/workbench/test/test_helper.rb
@@ -177,38 +177,14 @@ class ApiServerForTests
end
def run_test_server
- env_script = nil
Dir.chdir PYTHON_TESTS_DIR do
- # These are no-ops if we're running within run-tests.sh (except
- # that we do get a useful env_script back from "start", even
- # though it doesn't need to start up a new server).
- env_script = check_output %w(python ./run_test_server.py start --auth admin)
- check_output %w(python ./run_test_server.py start_arv-git-httpd)
- check_output %w(python ./run_test_server.py start_keep-web)
- check_output %w(python ./run_test_server.py start_nginx)
- # This one isn't a no-op, even under run-tests.sh.
check_output %w(python ./run_test_server.py start_keep)
end
- test_env = {}
- env_script.each_line do |line|
- line = line.chomp
- if 0 == line.index('export ')
- toks = line.sub('export ', '').split '=', 2
- $stderr.puts "run_test_server.py: #{toks[0]}=#{toks[1]}"
- test_env[toks[0]] = toks[1]
- end
- end
- test_env
end
def stop_test_server
Dir.chdir PYTHON_TESTS_DIR do
check_output %w(python ./run_test_server.py stop_keep)
- # These are no-ops if we're running within run-tests.sh
- check_output %w(python ./run_test_server.py stop_nginx)
- check_output %w(python ./run_test_server.py stop_arv-git-httpd)
- check_output %w(python ./run_test_server.py stop_keep-web)
- check_output %w(python ./run_test_server.py stop)
end
@@server_is_running = false
end
@@ -223,9 +199,9 @@ class ApiServerForTests
stop_test_server
end
- test_env = run_test_server
- $application_config['arvados_login_base'] = "https://#{test_env['ARVADOS_API_HOST']}/login"
- $application_config['arvados_v1_base'] = "https://#{test_env['ARVADOS_API_HOST']}/arvados/v1"
+ run_test_server
+ $application_config['arvados_login_base'] = "https://#{ENV['ARVADOS_API_HOST']}/login"
+ $application_config['arvados_v1_base'] = "https://#{ENV['ARVADOS_API_HOST']}/arvados/v1"
$application_config['arvados_insecure_host'] = true
ActiveSupport::TestCase.reset_application_config
commit e2f03263c7c2496ff3ee84e43eb133fe171905f9
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Jun 13 15:51:30 2018 -0400
13497: Route API traffic through controller in test suites.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index 9bcc5ba05..52269fd6e 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -349,15 +349,19 @@ start_services() {
rm -f "$WORKSPACE/tmp/api.pid"
fi
cd "$WORKSPACE" \
- && eval $(python sdk/python/tests/run_test_server.py start --auth admin) \
+ && eval $(python sdk/python/tests/run_test_server.py start --auth admin || echo fail=1) \
&& export ARVADOS_TEST_API_HOST="$ARVADOS_API_HOST" \
&& export ARVADOS_TEST_API_INSTALLED="$$" \
+ && python sdk/python/tests/run_test_server.py start_controller \
&& python sdk/python/tests/run_test_server.py start_keep_proxy \
&& python sdk/python/tests/run_test_server.py start_keep-web \
&& python sdk/python/tests/run_test_server.py start_arv-git-httpd \
&& python sdk/python/tests/run_test_server.py start_ws \
- && python sdk/python/tests/run_test_server.py start_nginx \
+ && eval $(python sdk/python/tests/run_test_server.py start_nginx || echo fail=1) \
&& (env | egrep ^ARVADOS)
+ if [[ -n "$fail" ]]; then
+ return 1
+ fi
}
stop_services() {
@@ -371,6 +375,7 @@ stop_services() {
&& python sdk/python/tests/run_test_server.py stop_ws \
&& python sdk/python/tests/run_test_server.py stop_keep-web \
&& python sdk/python/tests/run_test_server.py stop_keep_proxy \
+ && python sdk/python/tests/run_test_server.py stop_controller \
&& python sdk/python/tests/run_test_server.py stop
}
diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go
index dcd4d26a3..70a337a6c 100644
--- a/lib/controller/handler_test.go
+++ b/lib/controller/handler_test.go
@@ -38,7 +38,7 @@ func (s *HandlerSuite) SetUpTest(c *check.C) {
SystemNodes: map[string]arvados.SystemNode{
"*": {
Controller: arvados.SystemServiceInstance{Listen: ":"},
- RailsAPI: arvados.SystemServiceInstance{Listen: os.Getenv("ARVADOS_API_HOST"), TLS: true},
+ RailsAPI: arvados.SystemServiceInstance{Listen: os.Getenv("ARVADOS_TEST_API_HOST"), TLS: true},
},
},
}
diff --git a/sdk/python/tests/nginx.conf b/sdk/python/tests/nginx.conf
index 780968cb8..d818c5f9c 100644
--- a/sdk/python/tests/nginx.conf
+++ b/sdk/python/tests/nginx.conf
@@ -74,4 +74,17 @@ http {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
+ upstream controller {
+ server localhost:{{CONTROLLERPORT}};
+ }
+ server {
+ listen *:{{CONTROLLERSSLPORT}} ssl default_server;
+ server_name _;
+ ssl_certificate {{SSLCERT}};
+ ssl_certificate_key {{SSLKEY}};
+ location / {
+ proxy_pass http://controller;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
}
diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py
index 567b3b3bf..d8a21204a 100644
--- a/sdk/python/tests/run_test_server.py
+++ b/sdk/python/tests/run_test_server.py
@@ -377,6 +377,40 @@ def stop(force=False):
kill_server_pid(_pidfile('api'))
my_api_host = None
+def run_controller():
+ if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
+ return
+ stop_controller()
+ rails_api_port = int(string.split(os.environ.get('ARVADOS_TEST_API_HOST', my_api_host), ':')[-1])
+ port = find_available_port()
+ conf = os.path.join(TEST_TMPDIR, 'arvados.yml')
+ with open(conf, 'w') as f:
+ f.write("""
+Clusters:
+ zzzzz:
+ SystemNodes:
+ "*":
+ "arvados-controller":
+ Listen: ":{}"
+ "arvados-api-server":
+ Listen: ":{}"
+ TLS: true
+ """.format(port, rails_api_port))
+ logf = open(_fifo2stderr('controller'), 'w')
+ controller = subprocess.Popen(
+ ["arvados-server", "controller", "-config", conf],
+ stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
+ with open(_pidfile('controller'), 'w') as f:
+ f.write(str(controller.pid))
+ _wait_until_port_listens(port)
+ _setport('controller', port)
+ return port
+
+def stop_controller():
+ if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
+ return
+ kill_server_pid(_pidfile('controller'))
+
def run_ws():
if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
return
@@ -598,6 +632,8 @@ def run_nginx():
return
stop_nginx()
nginxconf = {}
+ nginxconf['CONTROLLERPORT'] = _getport('controller')
+ nginxconf['CONTROLLERSSLPORT'] = find_available_port()
nginxconf['KEEPWEBPORT'] = _getport('keep-web')
nginxconf['KEEPWEBDLSSLPORT'] = find_available_port()
nginxconf['KEEPWEBSSLPORT'] = find_available_port()
@@ -628,6 +664,7 @@ def run_nginx():
'-g', 'pid '+_pidfile('nginx')+';',
'-c', conffile],
env=env, stdin=open('/dev/null'), stdout=sys.stderr)
+ _setport('controller-ssl', nginxconf['CONTROLLERSSLPORT'])
_setport('keep-web-dl-ssl', nginxconf['KEEPWEBDLSSLPORT'])
_setport('keep-web-ssl', nginxconf['KEEPWEBSSLPORT'])
_setport('keepproxy-ssl', nginxconf['KEEPPROXYSSLPORT'])
@@ -766,6 +803,7 @@ if __name__ == "__main__":
actions = [
'start', 'stop',
'start_ws', 'stop_ws',
+ 'start_controller', 'stop_controller',
'start_keep', 'stop_keep',
'start_keep_proxy', 'stop_keep_proxy',
'start_keep-web', 'stop_keep-web',
@@ -802,6 +840,10 @@ if __name__ == "__main__":
run_ws()
elif args.action == 'stop_ws':
stop_ws()
+ elif args.action == 'start_controller':
+ run_controller()
+ elif args.action == 'stop_controller':
+ stop_controller()
elif args.action == 'start_keep':
run_keep(enforce_permissions=args.keep_enforce_permissions, num_servers=args.num_keep_servers)
elif args.action == 'stop_keep':
@@ -820,6 +862,7 @@ if __name__ == "__main__":
stop_keep_web()
elif args.action == 'start_nginx':
run_nginx()
+ print("export ARVADOS_API_HOST=0.0.0.0:{}".format(_getport('controller-ssl')))
elif args.action == 'stop_nginx':
stop_nginx()
else:
commit db7330822cb7dbdd1b61a34737d1b24158d8068d
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Jun 13 15:51:07 2018 -0400
13497: Don't propagate connection-oriented headers when proxying.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index 013d293f2..ad765bafa 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -43,6 +43,19 @@ func (h *Handler) setup() {
h.handlerStack = mux
}
+// headers that shouldn't be forwarded when proxying. See
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
+var dropHeaders = map[string]bool{
+ "Connection": true,
+ "Keep-Alive": true,
+ "Proxy-Authenticate": true,
+ "Proxy-Authorization": true,
+ "TE": true,
+ "Trailer": true,
+ "Transfer-Encoding": true,
+ "Upgrade": true,
+}
+
func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
urlOut, err := findRailsAPI(h.Cluster, h.Node)
if err != nil {
@@ -61,7 +74,9 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
// headers like Via and X-Forwarded-For.
hdrOut := http.Header{}
for k, v := range reqIn.Header {
- hdrOut[k] = v
+ if !dropHeaders[k] {
+ hdrOut[k] = v
+ }
}
xff := reqIn.RemoteAddr
if xffIn := reqIn.Header.Get("X-Forwarded-For"); xffIn != "" {
commit 2e0b7fcafcccc50602f8fd4df11b6312467e95fa
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Jun 13 15:49:26 2018 -0400
13497: Send request body when proxying.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index 6e4f0e3b4..013d293f2 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -81,6 +81,7 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
Method: reqIn.Method,
URL: urlOut,
Header: hdrOut,
+ Body: reqIn.Body,
}).WithContext(ctx)
resp, err := arvados.InsecureHTTPClient.Do(reqOut)
if err != nil {
diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go
index a187ba443..dcd4d26a3 100644
--- a/lib/controller/handler_test.go
+++ b/lib/controller/handler_test.go
@@ -8,7 +8,9 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
+ "net/url"
"os"
+ "strings"
"testing"
"time"
@@ -94,6 +96,20 @@ func (s *HandlerSuite) TestProxyWithToken(c *check.C) {
c.Check(u.UUID, check.Equals, arvadostest.ActiveUserUUID)
}
+func (s *HandlerSuite) TestProxyWithTokenInRequestBody(c *check.C) {
+ req := httptest.NewRequest("POST", "/arvados/v1/users/current", strings.NewReader(url.Values{
+ "_method": {"GET"},
+ "api_token": {arvadostest.ActiveToken},
+ }.Encode()))
+ resp := httptest.NewRecorder()
+ s.handler.ServeHTTP(resp, req)
+ c.Check(resp.Code, check.Equals, http.StatusOK)
+ var u arvados.User
+ err := json.Unmarshal(resp.Body.Bytes(), &u)
+ c.Check(err, check.IsNil)
+ c.Check(u.UUID, check.Equals, arvadostest.ActiveUserUUID)
+}
+
func (s *HandlerSuite) TestProxyNotFound(c *check.C) {
req := httptest.NewRequest("GET", "/arvados/v1/xyzzy", nil)
resp := httptest.NewRecorder()
commit fa8fd28e3ca22518a147cf34bf7146ef2a173257
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Tue Jun 12 16:39:41 2018 -0400
13497: Add timeout for proxy requests.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index a1b3848e5..6e4f0e3b4 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -5,12 +5,14 @@
package controller
import (
+ "context"
"io"
"net"
"net/http"
"net/url"
"strings"
"sync"
+ "time"
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/health"
@@ -44,7 +46,7 @@ func (h *Handler) setup() {
func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
urlOut, err := findRailsAPI(h.Cluster, h.Node)
if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
+ httpserver.Error(w, err.Error(), http.StatusInternalServerError)
return
}
urlOut = &url.URL{
@@ -68,14 +70,21 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
hdrOut.Set("X-Forwarded-For", xff)
hdrOut.Add("Via", reqIn.Proto+" arvados-controller")
+ ctx := reqIn.Context()
+ if timeout := h.Cluster.HTTPRequestTimeout; timeout > 0 {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Duration(timeout)))
+ defer cancel()
+ }
+
reqOut := (&http.Request{
Method: reqIn.Method,
URL: urlOut,
Header: hdrOut,
- }).WithContext(reqIn.Context())
+ }).WithContext(ctx)
resp, err := arvados.InsecureHTTPClient.Do(reqOut)
if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
+ httpserver.Error(w, err.Error(), http.StatusInternalServerError)
return
}
for k, v := range resp.Header {
diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go
index 57bb13d95..a187ba443 100644
--- a/lib/controller/handler_test.go
+++ b/lib/controller/handler_test.go
@@ -10,9 +10,11 @@ import (
"net/http/httptest"
"os"
"testing"
+ "time"
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/arvadostest"
+ "git.curoverse.com/arvados.git/sdk/go/httpserver"
check "gopkg.in/check.v1"
)
@@ -56,6 +58,19 @@ func (s *HandlerSuite) TestProxyDiscoveryDoc(c *check.C) {
c.Check(len(dd.Schemas), check.Not(check.Equals), 0)
}
+func (s *HandlerSuite) TestRequestTimeout(c *check.C) {
+ s.cluster.HTTPRequestTimeout = arvados.Duration(time.Nanosecond)
+ req := httptest.NewRequest("GET", "/discovery/v1/apis/arvados/v1/rest", nil)
+ resp := httptest.NewRecorder()
+ s.handler.ServeHTTP(resp, req)
+ c.Check(resp.Code, check.Equals, http.StatusInternalServerError)
+ var jresp httpserver.ErrorResponse
+ err := json.Unmarshal(resp.Body.Bytes(), &jresp)
+ c.Check(err, check.IsNil)
+ c.Assert(len(jresp.Errors), check.Equals, 1)
+ c.Check(jresp.Errors[0], check.Matches, `.*context deadline exceeded`)
+}
+
func (s *HandlerSuite) TestProxyWithoutToken(c *check.C) {
req := httptest.NewRequest("GET", "/arvados/v1/users/current", nil)
resp := httptest.NewRecorder()
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index e0a2b1d28..16d93362a 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -49,10 +49,11 @@ func (sc *Config) GetCluster(clusterID string) (*Cluster, error) {
}
type Cluster struct {
- ClusterID string `json:"-"`
- ManagementToken string
- SystemNodes map[string]SystemNode
- InstanceTypes []InstanceType
+ ClusterID string `json:"-"`
+ ManagementToken string
+ SystemNodes map[string]SystemNode
+ InstanceTypes []InstanceType
+ HTTPRequestTimeout Duration
}
type InstanceType struct {
diff --git a/sdk/go/httpserver/error.go b/sdk/go/httpserver/error.go
new file mode 100644
index 000000000..398e61fcd
--- /dev/null
+++ b/sdk/go/httpserver/error.go
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package httpserver
+
+import (
+ "encoding/json"
+ "net/http"
+)
+
+type ErrorResponse struct {
+ Errors []string `json:"errors"`
+}
+
+func Error(w http.ResponseWriter, error string, code int) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.WriteHeader(code)
+ json.NewEncoder(w).Encode(ErrorResponse{Errors: []string{error}})
+}
commit 36f8e449321e4fa02d88fee1fded14aa8ff81723
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Tue Jun 12 16:00:07 2018 -0400
13497: Proxy requests to Rails API.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/controller/cmd.go b/lib/controller/cmd.go
index 2bb68aed9..e006b6594 100644
--- a/lib/controller/cmd.go
+++ b/lib/controller/cmd.go
@@ -12,6 +12,8 @@ import (
"git.curoverse.com/arvados.git/sdk/go/arvados"
)
-var Command cmd.Handler = service.Command(arvados.ServiceNameController, func(cluster *arvados.Cluster, _ *arvados.SystemNode) http.Handler {
- return &Handler{Cluster: cluster}
-})
+var Command cmd.Handler = service.Command(arvados.ServiceNameController, newHandler)
+
+func newHandler(cluster *arvados.Cluster, node *arvados.SystemNode) http.Handler {
+ return &Handler{Cluster: cluster, Node: node}
+}
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index f0354d94d..a1b3848e5 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -6,16 +6,20 @@ package controller
import (
"io"
+ "net"
"net/http"
"net/url"
+ "strings"
"sync"
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/health"
+ "git.curoverse.com/arvados.git/sdk/go/httpserver"
)
type Handler struct {
Cluster *arvados.Cluster
+ Node *arvados.SystemNode
setupOnce sync.Once
handlerStack http.Handler
@@ -37,15 +41,39 @@ func (h *Handler) setup() {
h.handlerStack = mux
}
-func (h *Handler) proxyRailsAPI(w http.ResponseWriter, incomingReq *http.Request) {
- url, err := findRailsAPI(h.Cluster)
+func (h *Handler) proxyRailsAPI(w http.ResponseWriter, reqIn *http.Request) {
+ urlOut, err := findRailsAPI(h.Cluster, h.Node)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- req := *incomingReq
- req.URL.Host = url.Host
- resp, err := arvados.InsecureHTTPClient.Do(&req)
+ urlOut = &url.URL{
+ Scheme: urlOut.Scheme,
+ Host: urlOut.Host,
+ Path: reqIn.URL.Path,
+ RawPath: reqIn.URL.RawPath,
+ RawQuery: reqIn.URL.RawQuery,
+ }
+
+ // Copy headers from incoming request, then add/replace proxy
+ // headers like Via and X-Forwarded-For.
+ hdrOut := http.Header{}
+ for k, v := range reqIn.Header {
+ hdrOut[k] = v
+ }
+ xff := reqIn.RemoteAddr
+ if xffIn := reqIn.Header.Get("X-Forwarded-For"); xffIn != "" {
+ xff = xffIn + "," + xff
+ }
+ hdrOut.Set("X-Forwarded-For", xff)
+ hdrOut.Add("Via", reqIn.Proto+" arvados-controller")
+
+ reqOut := (&http.Request{
+ Method: reqIn.Method,
+ URL: urlOut,
+ Header: hdrOut,
+ }).WithContext(reqIn.Context())
+ resp, err := arvados.InsecureHTTPClient.Do(reqOut)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -56,15 +84,27 @@ func (h *Handler) proxyRailsAPI(w http.ResponseWriter, incomingReq *http.Request
}
}
w.WriteHeader(resp.StatusCode)
- io.Copy(w, resp.Body)
+ n, err := io.Copy(w, resp.Body)
+ if err != nil {
+ httpserver.Logger(reqIn).WithError(err).WithField("bytesCopied", n).Error("error copying response body")
+ }
}
// For now, findRailsAPI always uses the rails API running on this
// node.
-func findRailsAPI(cluster *arvados.Cluster) (*url.URL, error) {
- node, err := cluster.GetThisSystemNode()
- if err != nil {
+func findRailsAPI(cluster *arvados.Cluster, node *arvados.SystemNode) (*url.URL, error) {
+ hostport := node.RailsAPI.Listen
+ if len(hostport) > 1 && hostport[0] == ':' && strings.TrimRight(hostport[1:], "0123456789") == "" {
+ // ":12345" => connect to indicated port on localhost
+ hostport = "localhost" + hostport
+ } else if _, _, err := net.SplitHostPort(hostport); err == nil {
+ // "[::1]:12345" => connect to indicated address & port
+ } else {
return nil, err
}
- return url.Parse("http://" + node.RailsAPI.Listen)
+ proto := "http"
+ if node.RailsAPI.TLS {
+ proto = "https"
+ }
+ return url.Parse(proto + "://" + hostport)
}
diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go
new file mode 100644
index 000000000..57bb13d95
--- /dev/null
+++ b/lib/controller/handler_test.go
@@ -0,0 +1,91 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package controller
+
+import (
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
+ "git.curoverse.com/arvados.git/sdk/go/arvadostest"
+ check "gopkg.in/check.v1"
+)
+
+// Gocheck boilerplate
+func Test(t *testing.T) {
+ check.TestingT(t)
+}
+
+var _ = check.Suite(&HandlerSuite{})
+
+type HandlerSuite struct {
+ cluster *arvados.Cluster
+ handler http.Handler
+}
+
+func (s *HandlerSuite) SetUpTest(c *check.C) {
+ s.cluster = &arvados.Cluster{
+ ClusterID: "zzzzz",
+ SystemNodes: map[string]arvados.SystemNode{
+ "*": {
+ Controller: arvados.SystemServiceInstance{Listen: ":"},
+ RailsAPI: arvados.SystemServiceInstance{Listen: os.Getenv("ARVADOS_API_HOST"), TLS: true},
+ },
+ },
+ }
+ node := s.cluster.SystemNodes["*"]
+ s.handler = newHandler(s.cluster, &node)
+}
+
+func (s *HandlerSuite) TestProxyDiscoveryDoc(c *check.C) {
+ req := httptest.NewRequest("GET", "/discovery/v1/apis/arvados/v1/rest", nil)
+ resp := httptest.NewRecorder()
+ s.handler.ServeHTTP(resp, req)
+ c.Check(resp.Code, check.Equals, http.StatusOK)
+ var dd arvados.DiscoveryDocument
+ err := json.Unmarshal(resp.Body.Bytes(), &dd)
+ c.Check(err, check.IsNil)
+ c.Check(dd.BlobSignatureTTL, check.Not(check.Equals), int64(0))
+ c.Check(dd.BlobSignatureTTL > 0, check.Equals, true)
+ c.Check(len(dd.Resources), check.Not(check.Equals), 0)
+ c.Check(len(dd.Schemas), check.Not(check.Equals), 0)
+}
+
+func (s *HandlerSuite) TestProxyWithoutToken(c *check.C) {
+ req := httptest.NewRequest("GET", "/arvados/v1/users/current", nil)
+ resp := httptest.NewRecorder()
+ s.handler.ServeHTTP(resp, req)
+ c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
+ jresp := map[string]interface{}{}
+ err := json.Unmarshal(resp.Body.Bytes(), &jresp)
+ c.Check(err, check.IsNil)
+ c.Check(jresp["errors"], check.FitsTypeOf, []interface{}{})
+}
+
+func (s *HandlerSuite) TestProxyWithToken(c *check.C) {
+ req := httptest.NewRequest("GET", "/arvados/v1/users/current", nil)
+ req.Header.Set("Authorization", "Bearer "+arvadostest.ActiveToken)
+ resp := httptest.NewRecorder()
+ s.handler.ServeHTTP(resp, req)
+ c.Check(resp.Code, check.Equals, http.StatusOK)
+ var u arvados.User
+ err := json.Unmarshal(resp.Body.Bytes(), &u)
+ c.Check(err, check.IsNil)
+ c.Check(u.UUID, check.Equals, arvadostest.ActiveUserUUID)
+}
+
+func (s *HandlerSuite) TestProxyNotFound(c *check.C) {
+ req := httptest.NewRequest("GET", "/arvados/v1/xyzzy", nil)
+ resp := httptest.NewRecorder()
+ s.handler.ServeHTTP(resp, req)
+ c.Check(resp.Code, check.Equals, http.StatusNotFound)
+ jresp := map[string]interface{}{}
+ err := json.Unmarshal(resp.Body.Bytes(), &jresp)
+ c.Check(err, check.IsNil)
+ c.Check(jresp["errors"], check.FitsTypeOf, []interface{}{})
+}
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index 875a274dc..e0a2b1d28 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -131,4 +131,5 @@ func (sn *SystemNode) ServicePorts() map[ServiceName]string {
type SystemServiceInstance struct {
Listen string
+ TLS bool
}
diff --git a/sdk/go/httpserver/logger.go b/sdk/go/httpserver/logger.go
index ec3fa7fae..9577718c7 100644
--- a/sdk/go/httpserver/logger.go
+++ b/sdk/go/httpserver/logger.go
@@ -17,7 +17,10 @@ type contextKey struct {
name string
}
-var requestTimeContextKey = contextKey{"requestTime"}
+var (
+ requestTimeContextKey = contextKey{"requestTime"}
+ loggerContextKey = contextKey{"logger"}
+)
// LogRequests wraps an http.Handler, logging each request and
// response via logger.
@@ -27,7 +30,6 @@ func LogRequests(logger logrus.FieldLogger, h http.Handler) http.Handler {
}
return http.HandlerFunc(func(wrapped http.ResponseWriter, req *http.Request) {
w := &responseTimer{ResponseWriter: WrapResponseWriter(wrapped)}
- req = req.WithContext(context.WithValue(req.Context(), &requestTimeContextKey, time.Now()))
lgr := logger.WithFields(logrus.Fields{
"RequestID": req.Header.Get("X-Request-Id"),
"remoteAddr": req.RemoteAddr,
@@ -38,12 +40,25 @@ func LogRequests(logger logrus.FieldLogger, h http.Handler) http.Handler {
"reqQuery": req.URL.RawQuery,
"reqBytes": req.ContentLength,
})
+ ctx := req.Context()
+ ctx = context.WithValue(ctx, &requestTimeContextKey, time.Now())
+ ctx = context.WithValue(ctx, &loggerContextKey, lgr)
+ req = req.WithContext(ctx)
+
logRequest(w, req, lgr)
defer logResponse(w, req, lgr)
h.ServeHTTP(w, req)
})
}
+func Logger(req *http.Request) logrus.FieldLogger {
+ if lgr, ok := req.Context().Value(&loggerContextKey).(logrus.FieldLogger); ok {
+ return lgr
+ } else {
+ return logrus.StandardLogger()
+ }
+}
+
func logRequest(w *responseTimer, req *http.Request, lgr *logrus.Entry) {
lgr.Info("request")
}
commit 8f76037ba8a37c488612285ffe70d26d0d038124
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Tue Jun 12 11:10:12 2018 -0400
13497: Move common system service code to lib/service.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/cmd/cmd.go b/lib/cmd/cmd.go
index 0d3a07a61..353167e80 100644
--- a/lib/cmd/cmd.go
+++ b/lib/cmd/cmd.go
@@ -58,6 +58,11 @@ func (m Multi) RunCommand(prog string, args []string, stdin io.Reader, stdout, s
return 2
}
_, basename := filepath.Split(prog)
+ if strings.HasPrefix(basename, "arvados-") {
+ basename = basename[8:]
+ } else if strings.HasPrefix(basename, "crunch-") {
+ basename = basename[7:]
+ }
if cmd, ok := m[basename]; ok {
return cmd.RunCommand(prog, args, stdin, stdout, stderr)
} else if cmd, ok = m[args[0]]; ok {
diff --git a/lib/controller/cmd.go b/lib/controller/cmd.go
index c13d0fa07..2bb68aed9 100644
--- a/lib/controller/cmd.go
+++ b/lib/controller/cmd.go
@@ -5,75 +5,13 @@
package controller
import (
- "flag"
- "fmt"
- "io"
"net/http"
"git.curoverse.com/arvados.git/lib/cmd"
+ "git.curoverse.com/arvados.git/lib/service"
"git.curoverse.com/arvados.git/sdk/go/arvados"
- "git.curoverse.com/arvados.git/sdk/go/httpserver"
- "github.com/Sirupsen/logrus"
)
-const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
-
-var Command cmd.Handler = &command{}
-
-type command struct{}
-
-func (*command) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
- log := logrus.StandardLogger()
- log.Formatter = &logrus.JSONFormatter{
- TimestampFormat: rfc3339NanoFixed,
- }
- log.Out = stderr
-
- var err error
- defer func() {
- if err != nil {
- log.WithError(err).Info("exiting")
- }
- }()
- flags := flag.NewFlagSet("", flag.ContinueOnError)
- flags.SetOutput(stderr)
- configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
- err = flags.Parse(args)
- if err != nil {
- return 2
- }
- cfg, err := arvados.GetConfig(*configFile)
- if err != nil {
- return 1
- }
- cluster, err := cfg.GetCluster("")
- if err != nil {
- return 1
- }
- node, err := cluster.GetThisSystemNode()
- if err != nil {
- return 1
- }
- if node.Controller.Listen == "" {
- err = fmt.Errorf("configuration does not run a controller on this host: Clusters[%q].SystemNodes[`hostname` or *].Controller.Listen == \"\"", cluster.ClusterID)
- return 1
- }
- srv := &httpserver.Server{
- Server: http.Server{
- Handler: httpserver.LogRequests(&Handler{
- Cluster: cluster,
- }),
- },
- Addr: node.Controller.Listen,
- }
- err = srv.Start()
- if err != nil {
- return 1
- }
- log.WithField("Listen", srv.Addr).Info("listening")
- err = srv.Wait()
- if err != nil {
- return 1
- }
- return 0
-}
+var Command cmd.Handler = service.Command(arvados.ServiceNameController, func(cluster *arvados.Cluster, _ *arvados.SystemNode) http.Handler {
+ return &Handler{Cluster: cluster}
+})
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index c51f8668b..f0354d94d 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -12,14 +12,12 @@ import (
"git.curoverse.com/arvados.git/sdk/go/arvados"
"git.curoverse.com/arvados.git/sdk/go/health"
- "git.curoverse.com/arvados.git/sdk/go/httpserver"
)
type Handler struct {
Cluster *arvados.Cluster
setupOnce sync.Once
- mux http.ServeMux
handlerStack http.Handler
proxyClient *arvados.Client
}
@@ -30,12 +28,13 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
func (h *Handler) setup() {
- h.mux.Handle("/_health/", &health.Handler{
+ mux := http.NewServeMux()
+ mux.Handle("/_health/", &health.Handler{
Token: h.Cluster.ManagementToken,
Prefix: "/_health/",
})
- h.mux.Handle("/", http.HandlerFunc(h.proxyRailsAPI))
- h.handlerStack = httpserver.LogRequests(&h.mux)
+ mux.Handle("/", http.HandlerFunc(h.proxyRailsAPI))
+ h.handlerStack = mux
}
func (h *Handler) proxyRailsAPI(w http.ResponseWriter, incomingReq *http.Request) {
diff --git a/lib/service/cmd.go b/lib/service/cmd.go
new file mode 100644
index 000000000..e59ac486a
--- /dev/null
+++ b/lib/service/cmd.go
@@ -0,0 +1,101 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// package service provides a cmd.Handler that brings up a system service.
+package service
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "net/http"
+
+ "git.curoverse.com/arvados.git/lib/cmd"
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
+ "git.curoverse.com/arvados.git/sdk/go/httpserver"
+ "github.com/Sirupsen/logrus"
+)
+
+type NewHandlerFunc func(*arvados.Cluster, *arvados.SystemNode) http.Handler
+
+type command struct {
+ newHandler NewHandlerFunc
+ svcName arvados.ServiceName
+}
+
+// Command returns a cmd.Handler that loads site config, calls
+// newHandler with the current cluster and node configs, and brings up
+// an http server with the returned handler.
+//
+// The handler is wrapped with server middleware (adding X-Request-ID
+// headers, logging requests/responses, etc).
+func Command(svcName arvados.ServiceName, newHandler NewHandlerFunc) cmd.Handler {
+ return &command{
+ newHandler: newHandler,
+ svcName: svcName,
+ }
+}
+
+func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ log := logrus.New()
+ log.Formatter = &logrus.JSONFormatter{
+ TimestampFormat: rfc3339NanoFixed,
+ }
+ log.Out = stderr
+
+ var err error
+ defer func() {
+ if err != nil {
+ log.WithError(err).Info("exiting")
+ }
+ }()
+ flags := flag.NewFlagSet("", flag.ContinueOnError)
+ flags.SetOutput(stderr)
+ configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
+ err = flags.Parse(args)
+ if err == flag.ErrHelp {
+ err = nil
+ return 0
+ } else if err != nil {
+ return 2
+ }
+ cfg, err := arvados.GetConfig(*configFile)
+ if err != nil {
+ return 1
+ }
+ cluster, err := cfg.GetCluster("")
+ if err != nil {
+ return 1
+ }
+ node, err := cluster.GetThisSystemNode()
+ if err != nil {
+ return 1
+ }
+ listen := node.ServicePorts()[c.svcName]
+ if listen == "" {
+ err = fmt.Errorf("configuration does not enable the %s service on this host", c.svcName)
+ return 1
+ }
+ srv := &httpserver.Server{
+ Server: http.Server{
+ Handler: httpserver.AddRequestIDs(httpserver.LogRequests(log, c.newHandler(cluster, node))),
+ },
+ Addr: listen,
+ }
+ err = srv.Start()
+ if err != nil {
+ return 1
+ }
+ log.WithFields(logrus.Fields{
+ "Listen": srv.Addr,
+ "Service": c.svcName,
+ }).Info("listening")
+ err = srv.Wait()
+ if err != nil {
+ return 1
+ }
+ return 0
+}
+
+const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index 2de13d784..875a274dc 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -101,18 +101,31 @@ type SystemNode struct {
Workbench SystemServiceInstance `json:"arvados-workbench"`
}
+type ServiceName string
+
+const (
+ ServiceNameRailsAPI ServiceName = "arvados-api-server"
+ ServiceNameController ServiceName = "arvados-controller"
+ ServiceNameNodemanager ServiceName = "arvados-node-manager"
+ ServiceNameWorkbench ServiceName = "arvados-workbench"
+ ServiceNameWebsocket ServiceName = "arvados-ws"
+ ServiceNameKeepweb ServiceName = "keep-web"
+ ServiceNameKeepproxy ServiceName = "keepproxy"
+ ServiceNameKeepstore ServiceName = "keepstore"
+)
+
// ServicePorts returns the configured listening address (or "" if
// disabled) for each service on the node.
-func (sn *SystemNode) ServicePorts() map[string]string {
- return map[string]string{
- "arvados-api-server": sn.RailsAPI.Listen,
- "arvados-controller": sn.Controller.Listen,
- "arvados-node-manager": sn.Nodemanager.Listen,
- "arvados-workbench": sn.Workbench.Listen,
- "arvados-ws": sn.Websocket.Listen,
- "keep-web": sn.Keepweb.Listen,
- "keepproxy": sn.Keepproxy.Listen,
- "keepstore": sn.Keepstore.Listen,
+func (sn *SystemNode) ServicePorts() map[ServiceName]string {
+ return map[ServiceName]string{
+ ServiceNameRailsAPI: sn.RailsAPI.Listen,
+ ServiceNameController: sn.Controller.Listen,
+ ServiceNameNodemanager: sn.Nodemanager.Listen,
+ ServiceNameWorkbench: sn.Workbench.Listen,
+ ServiceNameWebsocket: sn.Websocket.Listen,
+ ServiceNameKeepweb: sn.Keepweb.Listen,
+ ServiceNameKeepproxy: sn.Keepproxy.Listen,
+ ServiceNameKeepstore: sn.Keepstore.Listen,
}
}
diff --git a/sdk/go/health/aggregator.go b/sdk/go/health/aggregator.go
index 5edb1f95c..3f3a91800 100644
--- a/sdk/go/health/aggregator.go
+++ b/sdk/go/health/aggregator.go
@@ -87,7 +87,7 @@ type ClusterHealthResponse struct {
// exposes problems that can't be expressed in Checks, like
// "service S is needed, but isn't configured to run
// anywhere."
- Services map[string]ServiceHealth `json:"services"`
+ Services map[arvados.ServiceName]ServiceHealth `json:"services"`
}
type CheckResult struct {
@@ -108,7 +108,7 @@ func (agg *Aggregator) ClusterHealth(cluster *arvados.Cluster) ClusterHealthResp
resp := ClusterHealthResponse{
Health: "OK",
Checks: make(map[string]CheckResult),
- Services: make(map[string]ServiceHealth),
+ Services: make(map[arvados.ServiceName]ServiceHealth),
}
mtx := sync.Mutex{}
@@ -128,7 +128,7 @@ func (agg *Aggregator) ClusterHealth(cluster *arvados.Cluster) ClusterHealthResp
}
wg.Add(1)
- go func(node, svc, addr string) {
+ go func(node string, svc arvados.ServiceName, addr string) {
defer wg.Done()
var result CheckResult
url, err := agg.pingURL(node, addr)
@@ -143,7 +143,7 @@ func (agg *Aggregator) ClusterHealth(cluster *arvados.Cluster) ClusterHealthResp
mtx.Lock()
defer mtx.Unlock()
- resp.Checks[svc+"+"+url] = result
+ resp.Checks[fmt.Sprintf("%s+%s", svc, url)] = result
if result.Health == "OK" {
h := resp.Services[svc]
h.N++
diff --git a/sdk/go/httpserver/logger.go b/sdk/go/httpserver/logger.go
index 1a4b7c559..ec3fa7fae 100644
--- a/sdk/go/httpserver/logger.go
+++ b/sdk/go/httpserver/logger.go
@@ -19,15 +19,16 @@ type contextKey struct {
var requestTimeContextKey = contextKey{"requestTime"}
-var Logger logrus.FieldLogger = logrus.StandardLogger()
-
// LogRequests wraps an http.Handler, logging each request and
-// response via logrus.
-func LogRequests(h http.Handler) http.Handler {
+// response via logger.
+func LogRequests(logger logrus.FieldLogger, h http.Handler) http.Handler {
+ if logger == nil {
+ logger = logrus.StandardLogger()
+ }
return http.HandlerFunc(func(wrapped http.ResponseWriter, req *http.Request) {
w := &responseTimer{ResponseWriter: WrapResponseWriter(wrapped)}
req = req.WithContext(context.WithValue(req.Context(), &requestTimeContextKey, time.Now()))
- lgr := Logger.WithFields(logrus.Fields{
+ lgr := logger.WithFields(logrus.Fields{
"RequestID": req.Header.Get("X-Request-Id"),
"remoteAddr": req.RemoteAddr,
"reqForwardedFor": req.Header.Get("X-Forwarded-For"),
diff --git a/sdk/go/httpserver/logger_test.go b/sdk/go/httpserver/logger_test.go
index bbcafa143..bdde3303e 100644
--- a/sdk/go/httpserver/logger_test.go
+++ b/sdk/go/httpserver/logger_test.go
@@ -9,11 +9,10 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
- "os"
"testing"
"time"
- log "github.com/Sirupsen/logrus"
+ "github.com/Sirupsen/logrus"
check "gopkg.in/check.v1"
)
@@ -26,12 +25,13 @@ var _ = check.Suite(&Suite{})
type Suite struct{}
func (s *Suite) TestLogRequests(c *check.C) {
- defer log.SetOutput(os.Stdout)
captured := &bytes.Buffer{}
- log.SetOutput(captured)
- log.SetFormatter(&log.JSONFormatter{
+ log := logrus.New()
+ log.Out = captured
+ log.Formatter = &logrus.JSONFormatter{
TimestampFormat: time.RFC3339Nano,
- })
+ }
+
h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello world"))
})
@@ -39,7 +39,7 @@ func (s *Suite) TestLogRequests(c *check.C) {
req.Header.Set("X-Forwarded-For", "1.2.3.4:12345")
c.Assert(err, check.IsNil)
resp := httptest.NewRecorder()
- AddRequestIDs(LogRequests(h)).ServeHTTP(resp, req)
+ AddRequestIDs(LogRequests(log, h)).ServeHTTP(resp, req)
dec := json.NewDecoder(captured)
diff --git a/services/keep-web/server.go b/services/keep-web/server.go
index 2995bd30a..e51376c3b 100644
--- a/services/keep-web/server.go
+++ b/services/keep-web/server.go
@@ -14,7 +14,7 @@ type server struct {
}
func (srv *server) Start() error {
- srv.Handler = httpserver.AddRequestIDs(httpserver.LogRequests(&handler{Config: srv.Config}))
+ srv.Handler = httpserver.AddRequestIDs(httpserver.LogRequests(nil, &handler{Config: srv.Config}))
srv.Addr = srv.Config.Listen
return srv.Server.Start()
}
diff --git a/services/keepproxy/keepproxy.go b/services/keepproxy/keepproxy.go
index 07fc63b63..b6c8bd66a 100644
--- a/services/keepproxy/keepproxy.go
+++ b/services/keepproxy/keepproxy.go
@@ -182,7 +182,7 @@ func main() {
// Start serving requests.
router = MakeRESTRouter(!cfg.DisableGet, !cfg.DisablePut, kc, time.Duration(cfg.Timeout), cfg.ManagementToken)
- http.Serve(listener, httpserver.AddRequestIDs(httpserver.LogRequests(router)))
+ http.Serve(listener, httpserver.AddRequestIDs(httpserver.LogRequests(nil, router)))
log.Println("shutting down")
}
diff --git a/services/keepstore/handlers.go b/services/keepstore/handlers.go
index a84a84db3..fb327a386 100644
--- a/services/keepstore/handlers.go
+++ b/services/keepstore/handlers.go
@@ -92,7 +92,7 @@ func MakeRESTRouter() http.Handler {
mux := http.NewServeMux()
mux.Handle("/", theConfig.metrics.Instrument(
- httpserver.AddRequestIDs(httpserver.LogRequests(rtr.limiter))))
+ httpserver.AddRequestIDs(httpserver.LogRequests(nil, rtr.limiter))))
mux.HandleFunc("/metrics.json", theConfig.metrics.exportJSON)
mux.Handle("/metrics", theConfig.metrics.exportProm)
commit 72bd39971753efa7e951b945d07e8d9704a07221
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 7 15:23:05 2018 -0400
13497: Build arvados-server and arvados-controller packages.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-build-packages.sh b/build/run-build-packages.sh
index 63f81832f..61dc07e4a 100755
--- a/build/run-build-packages.sh
+++ b/build/run-build-packages.sh
@@ -291,6 +291,10 @@ export GOPATH=$(mktemp -d)
go get github.com/kardianos/govendor
package_go_binary cmd/arvados-client arvados-client \
"Arvados command line tool (beta)"
+package_go_binary cmd/arvados-server arvados-server \
+ "Arvados server daemons"
+package_go_binary cmd/arvados-server arvados-controller \
+ "Arvados cluster controller daemon"
package_go_binary sdk/go/crunchrunner crunchrunner \
"Crunchrunner executes a command inside a container and uploads the output"
package_go_binary services/arv-git-httpd arvados-git-httpd \
commit ceb5ad39a5b94ba26d4a4a059f7801b758ddcec8
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Wed Jun 6 15:25:45 2018 -0400
13427: More symlink hack
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index 40d37621e..9bcc5ba05 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -516,12 +516,17 @@ export GOPATH
set -e
mkdir -p "$GOPATH/src/git.curoverse.com"
rmdir -v --parents --ignore-fail-on-non-empty "${temp}/GOPATH"
+ if [[ ! -h "$GOPATH/src/git.curoverse.com/arvados.git" ]]; then
+ for d in \
+ "$GOPATH/src/git.curoverse.com/arvados.git/tmp/GOPATH" \
+ "$GOPATH/src/git.curoverse.com/arvados.git/tmp" \
+ "$GOPATH/src/git.curoverse.com/arvados.git"; do
+ [[ -d "$d" ]] && rmdir "$d"
+ done
+ fi
for d in \
- "$GOPATH/src/git.curoverse.com/arvados.git/tmp/GOPATH" \
- "$GOPATH/src/git.curoverse.com/arvados.git/tmp" \
"$GOPATH/src/git.curoverse.com/arvados.git/arvados" \
"$GOPATH/src/git.curoverse.com/arvados.git"; do
- [[ -d "$d" ]] && rmdir "$d"
[[ -h "$d" ]] && rm "$d"
done
ln -vsfT "$WORKSPACE" "$GOPATH/src/git.curoverse.com/arvados.git"
commit 220778381f3a6aa6988c682f914fb9baeada85be
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 7 10:20:29 2018 -0400
13497: Add arvados-server command.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index 17a4fbd35..40d37621e 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -70,6 +70,7 @@ apps/workbench_integration (*)
apps/workbench_benchmark
apps/workbench_profile
cmd/arvados-client
+cmd/arvados-server
doc
lib/cli
lib/cmd
@@ -891,6 +892,7 @@ do_install services/api apiserver
declare -a gostuff
gostuff=(
cmd/arvados-client
+ cmd/arvados-server
lib/cli
lib/cmd
lib/controller
diff --git a/cmd/arvados-client/cmd.go b/cmd/arvados-client/cmd.go
index b616b54bd..4550ae53a 100644
--- a/cmd/arvados-client/cmd.go
+++ b/cmd/arvados-client/cmd.go
@@ -5,24 +5,19 @@
package main
import (
- "fmt"
- "io"
"os"
- "regexp"
- "runtime"
"git.curoverse.com/arvados.git/lib/cli"
"git.curoverse.com/arvados.git/lib/cmd"
)
var (
- version = "dev"
- cmdVersion cmd.Handler = versionCmd{}
- handler = cmd.Multi(map[string]cmd.Handler{
- "-e": cmdVersion,
- "version": cmdVersion,
- "-version": cmdVersion,
- "--version": cmdVersion,
+ version = "dev"
+ handler = cmd.Multi(map[string]cmd.Handler{
+ "-e": cmd.Version(version),
+ "version": cmd.Version(version),
+ "-version": cmd.Version(version),
+ "--version": cmd.Version(version),
"copy": cli.Copy,
"create": cli.Create,
@@ -61,14 +56,6 @@ var (
})
)
-type versionCmd struct{}
-
-func (versionCmd) RunCommand(prog string, args []string, _ io.Reader, stdout, _ io.Writer) int {
- prog = regexp.MustCompile(` -*version$`).ReplaceAllLiteralString(prog, "")
- fmt.Fprintf(stdout, "%s %s (%s)\n", prog, version, runtime.Version())
- return 0
-}
-
func fixLegacyArgs(args []string) []string {
flags, _ := cli.LegacyFlagSet()
return cmd.SubcommandToFront(args, flags)
diff --git a/cmd/arvados-server/cmd.go b/cmd/arvados-server/cmd.go
new file mode 100644
index 000000000..1af3745df
--- /dev/null
+++ b/cmd/arvados-server/cmd.go
@@ -0,0 +1,27 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package main
+
+import (
+ "os"
+
+ "git.curoverse.com/arvados.git/lib/cmd"
+ "git.curoverse.com/arvados.git/lib/controller"
+)
+
+var (
+ version = "dev"
+ handler = cmd.Multi(map[string]cmd.Handler{
+ "version": cmd.Version(version),
+ "-version": cmd.Version(version),
+ "--version": cmd.Version(version),
+
+ "controller": controller.Command,
+ })
+)
+
+func main() {
+ os.Exit(handler.RunCommand(os.Args[0], os.Args[1:], os.Stdin, os.Stdout, os.Stderr))
+}
diff --git a/lib/cmd/cmd.go b/lib/cmd/cmd.go
index 8b8427a70..0d3a07a61 100644
--- a/lib/cmd/cmd.go
+++ b/lib/cmd/cmd.go
@@ -12,6 +12,8 @@ import (
"io"
"io/ioutil"
"path/filepath"
+ "regexp"
+ "runtime"
"sort"
"strings"
)
@@ -26,6 +28,14 @@ func (f HandlerFunc) RunCommand(prog string, args []string, stdin io.Reader, std
return f(prog, args, stdin, stdout, stderr)
}
+type Version string
+
+func (v Version) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ prog = regexp.MustCompile(` -*version$`).ReplaceAllLiteralString(prog, "")
+ fmt.Fprintf(stdout, "%s %s (%s)\n", prog, v, runtime.Version())
+ return 0
+}
+
// Multi is a Handler that looks up its first argument in a map, and
// invokes the resulting Handler with the remaining args.
//
commit 25a80e9318880fbff91289ac8f70e1cae4c132a2
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 7 09:33:20 2018 -0400
13497: Use basename($0) as subcommand, if it is one.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/cmd/cmd.go b/lib/cmd/cmd.go
index 2cc71e68a..8b8427a70 100644
--- a/lib/cmd/cmd.go
+++ b/lib/cmd/cmd.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "path/filepath"
"sort"
"strings"
)
@@ -46,12 +47,15 @@ func (m Multi) RunCommand(prog string, args []string, stdin io.Reader, stdout, s
m.Usage(stderr)
return 2
}
- if cmd, ok := m[args[0]]; !ok {
- fmt.Fprintf(stderr, "unrecognized command %q\n", args[0])
+ _, basename := filepath.Split(prog)
+ if cmd, ok := m[basename]; ok {
+ return cmd.RunCommand(prog, args, stdin, stdout, stderr)
+ } else if cmd, ok = m[args[0]]; ok {
+ return cmd.RunCommand(prog+" "+args[0], args[1:], stdin, stdout, stderr)
+ } else {
+ fmt.Fprintf(stderr, "%s: unrecognized command %q\n", prog, args[0])
m.Usage(stderr)
return 2
- } else {
- return cmd.RunCommand(prog+" "+args[0], args[1:], stdin, stdout, stderr)
}
}
diff --git a/lib/cmd/cmd_test.go b/lib/cmd/cmd_test.go
index d8a486157..2fc50985f 100644
--- a/lib/cmd/cmd_test.go
+++ b/lib/cmd/cmd_test.go
@@ -42,6 +42,16 @@ func (s *CmdSuite) TestHello(c *check.C) {
c.Check(stderr.String(), check.Equals, "")
}
+func (s *CmdSuite) TestHelloViaProg(c *check.C) {
+ defer cmdtest.LeakCheck(c)()
+ stdout := bytes.NewBuffer(nil)
+ stderr := bytes.NewBuffer(nil)
+ exited := testCmd.RunCommand("/usr/local/bin/echo", []string{"hello", "world"}, bytes.NewReader(nil), stdout, stderr)
+ c.Check(exited, check.Equals, 0)
+ c.Check(stdout.String(), check.Equals, "hello world\n")
+ c.Check(stderr.String(), check.Equals, "")
+}
+
func (s *CmdSuite) TestUsage(c *check.C) {
defer cmdtest.LeakCheck(c)()
stdout := bytes.NewBuffer(nil)
@@ -49,7 +59,7 @@ func (s *CmdSuite) TestUsage(c *check.C) {
exited := testCmd.RunCommand("prog", []string{"nosuchcommand", "hi"}, bytes.NewReader(nil), stdout, stderr)
c.Check(exited, check.Equals, 2)
c.Check(stdout.String(), check.Equals, "")
- c.Check(stderr.String(), check.Matches, `(?ms)^unrecognized command "nosuchcommand"\n.*echo.*\n`)
+ c.Check(stderr.String(), check.Matches, `(?ms)^prog: unrecognized command "nosuchcommand"\n.*echo.*\n`)
}
func (s *CmdSuite) TestSubcommandToFront(c *check.C) {
commit a788135c352c36d1a905c7630423ba57b2ae072a
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Thu Jun 7 00:46:47 2018 -0400
13497: Add controller, proxy to Rails API.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/build/run-tests.sh b/build/run-tests.sh
index 1b6e4f142..17a4fbd35 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -73,6 +73,7 @@ cmd/arvados-client
doc
lib/cli
lib/cmd
+lib/controller
lib/crunchstat
lib/dispatchcloud
services/api
@@ -892,6 +893,7 @@ gostuff=(
cmd/arvados-client
lib/cli
lib/cmd
+ lib/controller
lib/crunchstat
lib/dispatchcloud
sdk/go/arvados
diff --git a/lib/controller/cmd.go b/lib/controller/cmd.go
new file mode 100644
index 000000000..c13d0fa07
--- /dev/null
+++ b/lib/controller/cmd.go
@@ -0,0 +1,79 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package controller
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "net/http"
+
+ "git.curoverse.com/arvados.git/lib/cmd"
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
+ "git.curoverse.com/arvados.git/sdk/go/httpserver"
+ "github.com/Sirupsen/logrus"
+)
+
+const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
+
+var Command cmd.Handler = &command{}
+
+type command struct{}
+
+func (*command) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
+ log := logrus.StandardLogger()
+ log.Formatter = &logrus.JSONFormatter{
+ TimestampFormat: rfc3339NanoFixed,
+ }
+ log.Out = stderr
+
+ var err error
+ defer func() {
+ if err != nil {
+ log.WithError(err).Info("exiting")
+ }
+ }()
+ flags := flag.NewFlagSet("", flag.ContinueOnError)
+ flags.SetOutput(stderr)
+ configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
+ err = flags.Parse(args)
+ if err != nil {
+ return 2
+ }
+ cfg, err := arvados.GetConfig(*configFile)
+ if err != nil {
+ return 1
+ }
+ cluster, err := cfg.GetCluster("")
+ if err != nil {
+ return 1
+ }
+ node, err := cluster.GetThisSystemNode()
+ if err != nil {
+ return 1
+ }
+ if node.Controller.Listen == "" {
+ err = fmt.Errorf("configuration does not run a controller on this host: Clusters[%q].SystemNodes[`hostname` or *].Controller.Listen == \"\"", cluster.ClusterID)
+ return 1
+ }
+ srv := &httpserver.Server{
+ Server: http.Server{
+ Handler: httpserver.LogRequests(&Handler{
+ Cluster: cluster,
+ }),
+ },
+ Addr: node.Controller.Listen,
+ }
+ err = srv.Start()
+ if err != nil {
+ return 1
+ }
+ log.WithField("Listen", srv.Addr).Info("listening")
+ err = srv.Wait()
+ if err != nil {
+ return 1
+ }
+ return 0
+}
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
new file mode 100644
index 000000000..c51f8668b
--- /dev/null
+++ b/lib/controller/handler.go
@@ -0,0 +1,71 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package controller
+
+import (
+ "io"
+ "net/http"
+ "net/url"
+ "sync"
+
+ "git.curoverse.com/arvados.git/sdk/go/arvados"
+ "git.curoverse.com/arvados.git/sdk/go/health"
+ "git.curoverse.com/arvados.git/sdk/go/httpserver"
+)
+
+type Handler struct {
+ Cluster *arvados.Cluster
+
+ setupOnce sync.Once
+ mux http.ServeMux
+ handlerStack http.Handler
+ proxyClient *arvados.Client
+}
+
+func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ h.setupOnce.Do(h.setup)
+ h.handlerStack.ServeHTTP(w, req)
+}
+
+func (h *Handler) setup() {
+ h.mux.Handle("/_health/", &health.Handler{
+ Token: h.Cluster.ManagementToken,
+ Prefix: "/_health/",
+ })
+ h.mux.Handle("/", http.HandlerFunc(h.proxyRailsAPI))
+ h.handlerStack = httpserver.LogRequests(&h.mux)
+}
+
+func (h *Handler) proxyRailsAPI(w http.ResponseWriter, incomingReq *http.Request) {
+ url, err := findRailsAPI(h.Cluster)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ req := *incomingReq
+ req.URL.Host = url.Host
+ resp, err := arvados.InsecureHTTPClient.Do(&req)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ for k, v := range resp.Header {
+ for _, v := range v {
+ w.Header().Add(k, v)
+ }
+ }
+ w.WriteHeader(resp.StatusCode)
+ io.Copy(w, resp.Body)
+}
+
+// For now, findRailsAPI always uses the rails API running on this
+// node.
+func findRailsAPI(cluster *arvados.Cluster) (*url.URL, error) {
+ node, err := cluster.GetThisSystemNode()
+ if err != nil {
+ return nil, err
+ }
+ return url.Parse("http://" + node.RailsAPI.Listen)
+}
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index 9ed0eacf2..2de13d784 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -90,6 +90,7 @@ func (cc *Cluster) GetSystemNode(node string) (*SystemNode, error) {
}
type SystemNode struct {
+ Controller SystemServiceInstance `json:"arvados-controller"`
Health SystemServiceInstance `json:"arvados-health"`
Keepproxy SystemServiceInstance `json:"keepproxy"`
Keepstore SystemServiceInstance `json:"keepstore"`
@@ -105,6 +106,7 @@ type SystemNode struct {
func (sn *SystemNode) ServicePorts() map[string]string {
return map[string]string{
"arvados-api-server": sn.RailsAPI.Listen,
+ "arvados-controller": sn.Controller.Listen,
"arvados-node-manager": sn.Nodemanager.Listen,
"arvados-workbench": sn.Workbench.Listen,
"arvados-ws": sn.Websocket.Listen,
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list