[ARVADOS] created: 1.1.4-369-gd636833ef
Git user
git at public.curoverse.com
Tue Jun 12 16:48:10 EDT 2018
at d636833ef26fd6568acc49e32ee9d040bea67f92 (commit)
commit d636833ef26fd6568acc49e32ee9d040bea67f92
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 00c7dd4b4677f734ea14c2a7855d5251f84da140
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 6f8541529d68addd2915dfc849b988a6e41f66a3
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..68087139c 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