[ARVADOS] updated: 1.2.0-149-g1f7a87ea4

Git user git at public.curoverse.com
Fri Oct 12 13:57:58 EDT 2018


Summary of changes:
 doc/admin/health-checks.html.textile.liquid        |  21 +--
 .../install-keep-balance.html.textile.liquid       |   2 +
 doc/install/install-keepstore.html.textile.liquid  |   6 +-
 sdk/go/auth/auth.go                                |   6 +-
 sdk/go/auth/handlers.go                            |  47 ++++++
 sdk/go/health/aggregator.go                        |   2 +-
 sdk/go/httpserver/metrics.go                       |  16 +-
 services/arv-git-httpd/auth_handler.go             |   2 +-
 services/keep-balance/balance_run_test.go          |   7 +
 services/keep-balance/main.go                      | 176 ---------------------
 services/keep-balance/{main.go => server.go}       |  93 +----------
 services/keep-balance/usage.go                     |   2 +
 services/keep-web/handler.go                       |   4 +-
 services/keep-web/server.go                        |   2 +-
 services/keep-web/server_test.go                   |  13 ++
 services/keepstore/config.go                       |   3 +-
 services/keepstore/handlers.go                     |   4 +-
 services/keepstore/mounts_test.go                  |   7 +-
 18 files changed, 123 insertions(+), 290 deletions(-)
 create mode 100644 sdk/go/auth/handlers.go
 copy services/keep-balance/{main.go => server.go} (65%)

       via  1f7a87ea4c342d16e0992872e2893cb6a2da92e9 (commit)
       via  051ad2017b69ca8e438396b461525e485a896321 (commit)
       via  c5335b934aacebe7a3707c6a52abafe457a6f76d (commit)
       via  e7e011e1fce68c484724073acd588655a2a10875 (commit)
      from  e849fc92933403506bee7f9fa132289d9a5c2d1e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 1f7a87ea4c342d16e0992872e2893cb6a2da92e9
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Fri Oct 12 13:56:22 2018 -0400

    14285: Add keep-balance to example cluster config.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/doc/admin/health-checks.html.textile.liquid b/doc/admin/health-checks.html.textile.liquid
index fa2668c71..eb71fda5e 100644
--- a/doc/admin/health-checks.html.textile.liquid
+++ b/doc/admin/health-checks.html.textile.liquid
@@ -62,6 +62,8 @@ Cluster:
 	  Listen: :8005
 	keepproxy:
 	  Listen: :8006
+	keep-balance:
+	  Listen: :9005
       keep0:
         keepstore:
 	  Listen: :25107

commit 051ad2017b69ca8e438396b461525e485a896321
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Fri Oct 12 13:52:08 2018 -0400

    14285: Add token middleware. Require management token for metrics.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/doc/admin/health-checks.html.textile.liquid b/doc/admin/health-checks.html.textile.liquid
index 630c6a178..fa2668c71 100644
--- a/doc/admin/health-checks.html.textile.liquid
+++ b/doc/admin/health-checks.html.textile.liquid
@@ -39,32 +39,33 @@ The healthcheck aggregator uses the @NodeProfile@ section of the cluster-wide @a
 Cluster:
   # The cluster uuid prefix
   zzzzz:
+    ManagementToken: xyzzy
     NodeProfile:
       # For each node, the profile name corresponds to a
       # locally-resolvable hostname, and describes which Arvados
       # services are available on that machine.
       api:
         arvados-controller:
-          Listen: 8000
+          Listen: :8000
         arvados-api-server:
-          Listen: 8001
+          Listen: :8001
       manage:
 	arvados-node-manager:
-	  Listen: 8002
+	  Listen: :8002
       workbench:
 	arvados-workbench:
-	  Listen: 8003
+	  Listen: :8003
 	arvados-ws:
-	  Listen: 8004
+	  Listen: :8004
       keep:
 	keep-web:
-	  Listen: 8005
+	  Listen: :8005
 	keepproxy:
-	  Listen: 8006
+	  Listen: :8006
       keep0:
         keepstore:
-	  Listen: 25701
+	  Listen: :25107
       keep1:
         keepstore:
-	  Listen: 25701
+	  Listen: :25107
 </pre>
diff --git a/doc/install/install-keep-balance.html.textile.liquid b/doc/install/install-keep-balance.html.textile.liquid
index 043f3ebfd..3b8b3c053 100644
--- a/doc/install/install-keep-balance.html.textile.liquid
+++ b/doc/install/install-keep-balance.html.textile.liquid
@@ -81,6 +81,8 @@ Client:
   AuthToken: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
 KeepServiceTypes:
   - disk
+Listen: :9005
+ManagementToken: <span class="userinput">xyzzy</span>
 RunPeriod: 10m
 CollectionBatchSize: 100000
 CollectionBuffers: 1000
diff --git a/doc/install/install-keepstore.html.textile.liquid b/doc/install/install-keepstore.html.textile.liquid
index 943c9bae3..fc4914efd 100644
--- a/doc/install/install-keepstore.html.textile.liquid
+++ b/doc/install/install-keepstore.html.textile.liquid
@@ -88,9 +88,9 @@ Listen: :25107
 # Format of request/response and error logs: "json" or "text".
 LogFormat: json
 
-# The secret key that must be provided by monitoring services
-# wishing to access the health check endpoint (/_health).
-ManagementToken: ""
+# The secret key that must be provided by monitoring services when
+# using the health check and metrics endpoints (/_health, /metrics).
+ManagementToken: xyzzy
 
 # Maximum RAM to use for data buffers, given in multiples of block
 # size (64 MiB). When this limit is reached, HTTP requests requiring
diff --git a/sdk/go/auth/auth.go b/sdk/go/auth/auth.go
index ad1d398c7..3c266e0d3 100644
--- a/sdk/go/auth/auth.go
+++ b/sdk/go/auth/auth.go
@@ -19,7 +19,11 @@ func NewCredentials() *Credentials {
 	return &Credentials{Tokens: []string{}}
 }
 
-func NewCredentialsFromHTTPRequest(r *http.Request) *Credentials {
+func CredentialsFromRequest(r *http.Request) *Credentials {
+	if c, ok := r.Context().Value(contextKeyCredentials).(*Credentials); ok {
+		// preloaded by middleware
+		return c
+	}
 	c := NewCredentials()
 	c.LoadTokensFromHTTPRequest(r)
 	return c
diff --git a/sdk/go/auth/handlers.go b/sdk/go/auth/handlers.go
new file mode 100644
index 000000000..7b1760f4b
--- /dev/null
+++ b/sdk/go/auth/handlers.go
@@ -0,0 +1,47 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package auth
+
+import (
+	"context"
+	"net/http"
+)
+
+type contextKey string
+
+var contextKeyCredentials contextKey = "credentials"
+
+// LoadToken wraps the next handler, adding credentials to the request
+// context so subsequent handlers can access them efficiently via
+// CredentialsFromRequest.
+func LoadToken(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextKeyCredentials, CredentialsFromRequest(r))))
+	})
+}
+
+// RequireLiteralToken wraps the next handler, rejecting any request
+// that doesn't supply the given token. If the given token is empty,
+// RequireLiteralToken returns next (i.e., no auth checks are
+// performed).
+func RequireLiteralToken(token string, next http.Handler) http.Handler {
+	if token == "" {
+		return next
+	}
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		c := CredentialsFromRequest(r)
+		if len(c.Tokens) == 0 {
+			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+			return
+		}
+		for _, t := range c.Tokens {
+			if t == token {
+				next.ServeHTTP(w, r)
+				return
+			}
+		}
+		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+	})
+}
diff --git a/sdk/go/health/aggregator.go b/sdk/go/health/aggregator.go
index a6cb8798a..564331327 100644
--- a/sdk/go/health/aggregator.go
+++ b/sdk/go/health/aggregator.go
@@ -217,7 +217,7 @@ func (agg *Aggregator) ping(url string, cluster *arvados.Cluster) (result CheckR
 }
 
 func (agg *Aggregator) checkAuth(req *http.Request, cluster *arvados.Cluster) bool {
-	creds := auth.NewCredentialsFromHTTPRequest(req)
+	creds := auth.CredentialsFromRequest(req)
 	for _, token := range creds.Tokens {
 		if token != "" && token == cluster.ManagementToken {
 			return true
diff --git a/sdk/go/httpserver/metrics.go b/sdk/go/httpserver/metrics.go
index b52068e95..a0455f11b 100644
--- a/sdk/go/httpserver/metrics.go
+++ b/sdk/go/httpserver/metrics.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"git.curoverse.com/arvados.git/sdk/go/auth"
 	"git.curoverse.com/arvados.git/sdk/go/stats"
 	"github.com/Sirupsen/logrus"
 	"github.com/gogo/protobuf/jsonpb"
@@ -23,7 +24,7 @@ type Handler interface {
 	// Returns an http.Handler that serves the Handler's metrics
 	// data at /metrics and /metrics.json, and passes other
 	// requests through to next.
-	ServeAPI(next http.Handler) http.Handler
+	ServeAPI(token string, next http.Handler) http.Handler
 }
 
 type metrics struct {
@@ -73,19 +74,24 @@ func (m *metrics) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 // metrics API endpoints (currently "GET /metrics(.json)?") and passes
 // other requests through to next.
 //
+// If the given token is not empty, that token must be supplied by a
+// client in order to access the metrics endpoints.
+//
 // Typical example:
 //
 //	m := Instrument(...)
-//	srv := http.Server{Handler: m.ServeAPI(m)}
-func (m *metrics) ServeAPI(next http.Handler) http.Handler {
+//	srv := http.Server{Handler: m.ServeAPI("secrettoken", m)}
+func (m *metrics) ServeAPI(token string, next http.Handler) http.Handler {
+	jsonMetrics := auth.RequireLiteralToken(token, http.HandlerFunc(m.exportJSON))
+	plainMetrics := auth.RequireLiteralToken(token, m.exportProm)
 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 		switch {
 		case req.Method != "GET" && req.Method != "HEAD":
 			next.ServeHTTP(w, req)
 		case req.URL.Path == "/metrics.json":
-			m.exportJSON(w, req)
+			jsonMetrics.ServeHTTP(w, req)
 		case req.URL.Path == "/metrics":
-			m.exportProm.ServeHTTP(w, req)
+			plainMetrics.ServeHTTP(w, req)
 		default:
 			next.ServeHTTP(w, req)
 		}
diff --git a/services/arv-git-httpd/auth_handler.go b/services/arv-git-httpd/auth_handler.go
index b4dc58b24..3b3032afd 100644
--- a/services/arv-git-httpd/auth_handler.go
+++ b/services/arv-git-httpd/auth_handler.go
@@ -91,7 +91,7 @@ func (h *authHandler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 		httpserver.Log(r.RemoteAddr, passwordToLog, w.WroteStatus(), statusText, repoName, r.Method, r.URL.Path)
 	}()
 
-	creds := auth.NewCredentialsFromHTTPRequest(r)
+	creds := auth.CredentialsFromRequest(r)
 	if len(creds.Tokens) == 0 {
 		statusCode, statusText = http.StatusUnauthorized, "no credentials provided"
 		w.Header().Add("WWW-Authenticate", "Basic realm=\"git\"")
diff --git a/services/keep-balance/balance_run_test.go b/services/keep-balance/balance_run_test.go
index 26aee213d..f42383297 100644
--- a/services/keep-balance/balance_run_test.go
+++ b/services/keep-balance/balance_run_test.go
@@ -433,6 +433,7 @@ func (s *runSuite) TestDryRun(c *check.C) {
 
 func (s *runSuite) TestCommit(c *check.C) {
 	s.config.Listen = ":"
+	s.config.ManagementToken = "xyzzy"
 	opts := RunOptions{
 		CommitPulls: true,
 		CommitTrash: true,
@@ -466,6 +467,7 @@ func (s *runSuite) TestCommit(c *check.C) {
 
 func (s *runSuite) TestRunForever(c *check.C) {
 	s.config.Listen = ":"
+	s.config.ManagementToken = "xyzzy"
 	opts := RunOptions{
 		CommitPulls: true,
 		CommitTrash: true,
@@ -508,6 +510,11 @@ func (s *runSuite) TestRunForever(c *check.C) {
 func (s *runSuite) getMetrics(c *check.C, srv *Server) string {
 	resp, err := http.Get("http://" + srv.listening + "/metrics")
 	c.Assert(err, check.IsNil)
+	c.Check(resp.StatusCode, check.Equals, http.StatusUnauthorized)
+
+	resp, err = http.Get("http://" + srv.listening + "/metrics?api_token=xyzzy")
+	c.Assert(err, check.IsNil)
+	c.Check(resp.StatusCode, check.Equals, http.StatusOK)
 	buf, err := ioutil.ReadAll(resp.Body)
 	c.Check(err, check.IsNil)
 	return string(buf)
diff --git a/services/keep-balance/server.go b/services/keep-balance/server.go
index c47305aef..ad13be751 100644
--- a/services/keep-balance/server.go
+++ b/services/keep-balance/server.go
@@ -13,6 +13,7 @@ import (
 	"time"
 
 	"git.curoverse.com/arvados.git/sdk/go/arvados"
+	"git.curoverse.com/arvados.git/sdk/go/auth"
 	"git.curoverse.com/arvados.git/sdk/go/httpserver"
 	"github.com/Sirupsen/logrus"
 )
@@ -40,6 +41,9 @@ type Config struct {
 	// address, address:port, or :port for management interface
 	Listen string
 
+	// token for management APIs
+	ManagementToken string
+
 	// How often to check
 	RunPeriod arvados.Duration
 
@@ -121,7 +125,9 @@ func (srv *Server) start() error {
 	}
 	server := &httpserver.Server{
 		Server: http.Server{
-			Handler: httpserver.LogRequests(srv.Logger, srv.metrics.Handler(srv.Logger)),
+			Handler: httpserver.LogRequests(srv.Logger,
+				auth.RequireLiteralToken(srv.config.ManagementToken,
+					srv.metrics.Handler(srv.Logger))),
 		},
 		Addr: srv.config.Listen,
 	}
diff --git a/services/keep-balance/usage.go b/services/keep-balance/usage.go
index 8df4a2364..b39e83905 100644
--- a/services/keep-balance/usage.go
+++ b/services/keep-balance/usage.go
@@ -18,6 +18,7 @@ Client:
 KeepServiceTypes:
     - disk
 Listen: ":9005"
+ManagementToken: xyzzy
 RunPeriod: 600s
 CollectionBatchSize: 100000
 CollectionBuffers: 1000
diff --git a/services/keep-web/handler.go b/services/keep-web/handler.go
index 912398fa6..95948e325 100644
--- a/services/keep-web/handler.go
+++ b/services/keep-web/handler.go
@@ -320,7 +320,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 
 	if useSiteFS {
 		if tokens == nil {
-			tokens = auth.NewCredentialsFromHTTPRequest(r).Tokens
+			tokens = auth.CredentialsFromRequest(r).Tokens
 		}
 		h.serveSiteFS(w, r, tokens, credentialsOK, attachment)
 		return
@@ -342,7 +342,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 
 	if tokens == nil {
 		if credentialsOK {
-			reqTokens = auth.NewCredentialsFromHTTPRequest(r).Tokens
+			reqTokens = auth.CredentialsFromRequest(r).Tokens
 		}
 		tokens = append(reqTokens, h.Config.AnonymousTokens...)
 	}
diff --git a/services/keep-web/server.go b/services/keep-web/server.go
index 68ff8a7b0..f70dd1a71 100644
--- a/services/keep-web/server.go
+++ b/services/keep-web/server.go
@@ -21,7 +21,7 @@ func (srv *server) Start() error {
 	reg := prometheus.NewRegistry()
 	h.Config.Cache.registry = reg
 	mh := httpserver.Instrument(reg, nil, httpserver.AddRequestIDs(httpserver.LogRequests(nil, h)))
-	h.MetricsAPI = mh.ServeAPI(http.NotFoundHandler())
+	h.MetricsAPI = mh.ServeAPI(h.Config.ManagementToken, http.NotFoundHandler())
 	srv.Handler = mh
 	srv.Addr = srv.Config.Listen
 	return srv.Server.Start()
diff --git a/services/keep-web/server_test.go b/services/keep-web/server_test.go
index 7e738cb9f..48c9726e3 100644
--- a/services/keep-web/server_test.go
+++ b/services/keep-web/server_test.go
@@ -323,6 +323,18 @@ func (s *IntegrationSuite) TestMetrics(c *check.C) {
 	req, _ = http.NewRequest("GET", origin+"/metrics.json", nil)
 	resp, err = http.DefaultClient.Do(req)
 	c.Assert(err, check.IsNil)
+	c.Check(resp.StatusCode, check.Equals, http.StatusUnauthorized)
+
+	req, _ = http.NewRequest("GET", origin+"/metrics.json", nil)
+	req.Header.Set("Authorization", "Bearer badtoken")
+	resp, err = http.DefaultClient.Do(req)
+	c.Assert(err, check.IsNil)
+	c.Check(resp.StatusCode, check.Equals, http.StatusForbidden)
+
+	req, _ = http.NewRequest("GET", origin+"/metrics.json", nil)
+	req.Header.Set("Authorization", "Bearer "+arvadostest.ManagementToken)
+	resp, err = http.DefaultClient.Do(req)
+	c.Assert(err, check.IsNil)
 	c.Check(resp.StatusCode, check.Equals, http.StatusOK)
 	type summary struct {
 		SampleCount string  `json:"sample_count"`
@@ -418,6 +430,7 @@ func (s *IntegrationSuite) SetUpTest(c *check.C) {
 		Insecure: true,
 	}
 	cfg.Listen = "127.0.0.1:0"
+	cfg.ManagementToken = arvadostest.ManagementToken
 	s.testServer = &server{Config: cfg}
 	err := s.testServer.Start()
 	c.Assert(err, check.Equals, nil)
diff --git a/services/keepstore/config.go b/services/keepstore/config.go
index 1f8c7e31a..2e3fe0a5b 100644
--- a/services/keepstore/config.go
+++ b/services/keepstore/config.go
@@ -46,8 +46,7 @@ type Config struct {
 	systemAuthToken string
 	debugLogf       func(string, ...interface{})
 
-	ManagementToken string `doc: The secret key that must be provided by monitoring services
-wishing to access the health check endpoint (/_health).`
+	ManagementToken string
 }
 
 var (
diff --git a/services/keepstore/handlers.go b/services/keepstore/handlers.go
index 2426c9cbd..a325d9820 100644
--- a/services/keepstore/handlers.go
+++ b/services/keepstore/handlers.go
@@ -87,9 +87,9 @@ func MakeRESTRouter(cluster *arvados.Cluster) http.Handler {
 
 	rtr.limiter = httpserver.NewRequestLimiter(theConfig.MaxRequests, rtr)
 
-	stack := httpserver.Instrument(nil, nil,
+	instrumented := httpserver.Instrument(nil, nil,
 		httpserver.AddRequestIDs(httpserver.LogRequests(nil, rtr.limiter)))
-	return stack.ServeAPI(stack)
+	return instrumented.ServeAPI(theConfig.ManagementToken, instrumented)
 }
 
 // BadRequestHandler is a HandleFunc to address bad requests.
diff --git a/services/keepstore/mounts_test.go b/services/keepstore/mounts_test.go
index 9fa0090aa..31b1a684f 100644
--- a/services/keepstore/mounts_test.go
+++ b/services/keepstore/mounts_test.go
@@ -27,6 +27,7 @@ func (s *MountsSuite) SetUpTest(c *check.C) {
 	KeepVM = s.vm
 	theConfig = DefaultConfig()
 	theConfig.systemAuthToken = arvadostest.DataManagerToken
+	theConfig.ManagementToken = arvadostest.ManagementToken
 	theConfig.Start()
 	s.rtr = MakeRESTRouter(testCluster)
 }
@@ -104,6 +105,10 @@ func (s *MountsSuite) TestMetrics(c *check.C) {
 	s.call("PUT", "/"+TestHash, "", TestBlock)
 	s.call("PUT", "/"+TestHash2, "", TestBlock2)
 	resp := s.call("GET", "/metrics.json", "", nil)
+	c.Check(resp.Code, check.Equals, http.StatusUnauthorized)
+	resp = s.call("GET", "/metrics.json", "foobar", nil)
+	c.Check(resp.Code, check.Equals, http.StatusForbidden)
+	resp = s.call("GET", "/metrics.json", arvadostest.ManagementToken, nil)
 	c.Check(resp.Code, check.Equals, http.StatusOK)
 	var j []struct {
 		Name   string
@@ -144,7 +149,7 @@ func (s *MountsSuite) call(method, path, tok string, body []byte) *httptest.Resp
 	resp := httptest.NewRecorder()
 	req, _ := http.NewRequest(method, path, bytes.NewReader(body))
 	if tok != "" {
-		req.Header.Set("Authorization", "OAuth2 "+tok)
+		req.Header.Set("Authorization", "Bearer "+tok)
 	}
 	s.rtr.ServeHTTP(resp, req)
 	return resp

commit c5335b934aacebe7a3707c6a52abafe457a6f76d
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Fri Oct 12 10:04:04 2018 -0400

    14285: Update sample config.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/services/keep-balance/usage.go b/services/keep-balance/usage.go
index 4c7d50671..8df4a2364 100644
--- a/services/keep-balance/usage.go
+++ b/services/keep-balance/usage.go
@@ -17,6 +17,7 @@ Client:
     Insecure: false
 KeepServiceTypes:
     - disk
+Listen: ":9005"
 RunPeriod: 600s
 CollectionBatchSize: 100000
 CollectionBuffers: 1000

commit e7e011e1fce68c484724073acd588655a2a10875
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Fri Oct 12 10:01:50 2018 -0400

    14285: Split Server to server.go.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/services/keep-balance/main.go b/services/keep-balance/main.go
index eb741fa7e..e3e90d358 100644
--- a/services/keep-balance/main.go
+++ b/services/keep-balance/main.go
@@ -11,75 +11,13 @@ import (
 	"log"
 	"net/http"
 	"os"
-	"os/signal"
-	"syscall"
 	"time"
 
 	"git.curoverse.com/arvados.git/sdk/go/arvados"
 	"git.curoverse.com/arvados.git/sdk/go/config"
-	"git.curoverse.com/arvados.git/sdk/go/httpserver"
 	"github.com/Sirupsen/logrus"
 )
 
-var version = "dev"
-
-const (
-	defaultConfigPath = "/etc/arvados/keep-balance/keep-balance.yml"
-	rfc3339NanoFixed  = "2006-01-02T15:04:05.000000000Z07:00"
-)
-
-// Config specifies site configuration, like API credentials and the
-// choice of which servers are to be balanced.
-//
-// Config is loaded from a JSON config file (see usage()).
-type Config struct {
-	// Arvados API endpoint and credentials.
-	Client arvados.Client
-
-	// List of service types (e.g., "disk") to balance.
-	KeepServiceTypes []string
-
-	KeepServiceList arvados.KeepServiceList
-
-	// address, address:port, or :port for management interface
-	Listen string
-
-	// How often to check
-	RunPeriod arvados.Duration
-
-	// Number of collections to request in each API call
-	CollectionBatchSize int
-
-	// Max collections to buffer in memory (bigger values consume
-	// more memory, but can reduce store-and-forward latency when
-	// fetching pages)
-	CollectionBuffers int
-
-	// Timeout for outgoing http request/response cycle.
-	RequestTimeout arvados.Duration
-}
-
-// RunOptions controls runtime behavior. The flags/options that belong
-// here are the ones that are useful for interactive use. For example,
-// "CommitTrash" is a runtime option rather than a config item because
-// it invokes a troubleshooting feature rather than expressing how
-// balancing is meant to be done at a given site.
-//
-// RunOptions fields are controlled by command line flags.
-type RunOptions struct {
-	Once        bool
-	CommitPulls bool
-	CommitTrash bool
-	Logger      *logrus.Logger
-	Dumper      *logrus.Logger
-
-	// SafeRendezvousState from the most recent balance operation,
-	// or "" if unknown. If this changes from one run to the next,
-	// we need to watch out for races. See
-	// (*Balancer)ClearTrashLists.
-	SafeRendezvousState string
-}
-
 var debugf = func(string, ...interface{}) {}
 
 func main() {
@@ -160,117 +98,3 @@ func mustReadConfig(dst interface{}, path string) {
 		log.Fatal(err)
 	}
 }
-
-type Server struct {
-	config     Config
-	runOptions RunOptions
-	metrics    *metrics
-	listening  string // for tests
-
-	Logger *logrus.Logger
-	Dumper *logrus.Logger
-}
-
-// NewServer returns a new Server that runs Balancers using the given
-// config and runOptions.
-func NewServer(config Config, runOptions RunOptions) (*Server, error) {
-	if len(config.KeepServiceList.Items) > 0 && config.KeepServiceTypes != nil {
-		return nil, fmt.Errorf("cannot specify both KeepServiceList and KeepServiceTypes in config")
-	}
-	if !runOptions.Once && config.RunPeriod == arvados.Duration(0) {
-		return nil, fmt.Errorf("you must either use the -once flag, or specify RunPeriod in config")
-	}
-
-	if runOptions.Logger == nil {
-		log := logrus.New()
-		log.Formatter = &logrus.JSONFormatter{
-			TimestampFormat: rfc3339NanoFixed,
-		}
-		log.Out = os.Stderr
-		runOptions.Logger = log
-	}
-
-	srv := &Server{
-		config:     config,
-		runOptions: runOptions,
-		metrics:    newMetrics(),
-		Logger:     runOptions.Logger,
-		Dumper:     runOptions.Dumper,
-	}
-	return srv, srv.start()
-}
-
-func (srv *Server) start() error {
-	if srv.config.Listen == "" {
-		return nil
-	}
-	server := &httpserver.Server{
-		Server: http.Server{
-			Handler: httpserver.LogRequests(srv.Logger, srv.metrics.Handler(srv.Logger)),
-		},
-		Addr: srv.config.Listen,
-	}
-	err := server.Start()
-	if err != nil {
-		return err
-	}
-	srv.Logger.Printf("listening at %s", server.Addr)
-	srv.listening = server.Addr
-	return nil
-}
-
-func (srv *Server) Run() (*Balancer, error) {
-	bal := &Balancer{
-		Logger:  srv.Logger,
-		Dumper:  srv.Dumper,
-		Metrics: srv.metrics,
-	}
-	var err error
-	srv.runOptions, err = bal.Run(srv.config, srv.runOptions)
-	return bal, err
-}
-
-// RunForever runs forever, or (for testing purposes) until the given
-// stop channel is ready to receive.
-func (srv *Server) RunForever(stop <-chan interface{}) error {
-	logger := srv.runOptions.Logger
-
-	ticker := time.NewTicker(time.Duration(srv.config.RunPeriod))
-
-	// The unbuffered channel here means we only hear SIGUSR1 if
-	// it arrives while we're waiting in select{}.
-	sigUSR1 := make(chan os.Signal)
-	signal.Notify(sigUSR1, syscall.SIGUSR1)
-
-	logger.Printf("starting up: will scan every %v and on SIGUSR1", srv.config.RunPeriod)
-
-	for {
-		if !srv.runOptions.CommitPulls && !srv.runOptions.CommitTrash {
-			logger.Print("WARNING: Will scan periodically, but no changes will be committed.")
-			logger.Print("=======  Consider using -commit-pulls and -commit-trash flags.")
-		}
-
-		_, err := srv.Run()
-		if err != nil {
-			logger.Print("run failed: ", err)
-		} else {
-			logger.Print("run succeeded")
-		}
-
-		select {
-		case <-stop:
-			signal.Stop(sigUSR1)
-			return nil
-		case <-ticker.C:
-			logger.Print("timer went off")
-		case <-sigUSR1:
-			logger.Print("received SIGUSR1, resetting timer")
-			// Reset the timer so we don't start the N+1st
-			// run too soon after the Nth run is triggered
-			// by SIGUSR1.
-			ticker.Stop()
-			ticker = time.NewTicker(time.Duration(srv.config.RunPeriod))
-		}
-		logger.Print("starting next run")
-	}
-}
diff --git a/services/keep-balance/main.go b/services/keep-balance/server.go
similarity index 66%
copy from services/keep-balance/main.go
copy to services/keep-balance/server.go
index eb741fa7e..c47305aef 100644
--- a/services/keep-balance/main.go
+++ b/services/keep-balance/server.go
@@ -5,10 +5,7 @@
 package main
 
 import (
-	"encoding/json"
-	"flag"
 	"fmt"
-	"log"
 	"net/http"
 	"os"
 	"os/signal"
@@ -16,7 +13,6 @@ import (
 	"time"
 
 	"git.curoverse.com/arvados.git/sdk/go/arvados"
-	"git.curoverse.com/arvados.git/sdk/go/config"
 	"git.curoverse.com/arvados.git/sdk/go/httpserver"
 	"github.com/Sirupsen/logrus"
 )
@@ -80,87 +76,6 @@ type RunOptions struct {
 	SafeRendezvousState string
 }
 
-var debugf = func(string, ...interface{}) {}
-
-func main() {
-	var cfg Config
-	var runOptions RunOptions
-
-	configPath := flag.String("config", defaultConfigPath,
-		"`path` of JSON or YAML configuration file")
-	serviceListPath := flag.String("config.KeepServiceList", "",
-		"`path` of JSON or YAML file with list of keep services to balance, as given by \"arv keep_service list\" "+
-			"(default: config[\"KeepServiceList\"], or if none given, get all available services and filter by config[\"KeepServiceTypes\"])")
-	flag.BoolVar(&runOptions.Once, "once", false,
-		"balance once and then exit")
-	flag.BoolVar(&runOptions.CommitPulls, "commit-pulls", false,
-		"send pull requests (make more replicas of blocks that are underreplicated or are not in optimal rendezvous probe order)")
-	flag.BoolVar(&runOptions.CommitTrash, "commit-trash", false,
-		"send trash requests (delete unreferenced old blocks, and excess replicas of overreplicated blocks)")
-	dumpConfig := flag.Bool("dump-config", false, "write current configuration to stdout and exit")
-	dumpFlag := flag.Bool("dump", false, "dump details for each block to stdout")
-	debugFlag := flag.Bool("debug", false, "enable debug messages")
-	getVersion := flag.Bool("version", false, "Print version information and exit.")
-	flag.Usage = usage
-	flag.Parse()
-
-	// Print version information if requested
-	if *getVersion {
-		fmt.Printf("keep-balance %s\n", version)
-		return
-	}
-
-	mustReadConfig(&cfg, *configPath)
-	if *serviceListPath != "" {
-		mustReadConfig(&cfg.KeepServiceList, *serviceListPath)
-	}
-
-	if *dumpConfig {
-		log.Fatal(config.DumpAndExit(cfg))
-	}
-
-	to := time.Duration(cfg.RequestTimeout)
-	if to == 0 {
-		to = 30 * time.Minute
-	}
-	arvados.DefaultSecureClient.Timeout = to
-	arvados.InsecureHTTPClient.Timeout = to
-	http.DefaultClient.Timeout = to
-
-	log.Printf("keep-balance %s started", version)
-
-	if *debugFlag {
-		debugf = log.Printf
-		if j, err := json.Marshal(cfg); err != nil {
-			log.Fatal(err)
-		} else {
-			log.Printf("config is %s", j)
-		}
-	}
-	if *dumpFlag {
-		runOptions.Dumper = logrus.New()
-		runOptions.Dumper.Out = os.Stdout
-		runOptions.Dumper.Formatter = &logrus.TextFormatter{}
-	}
-	srv, err := NewServer(cfg, runOptions)
-	if err != nil {
-		// (don't run)
-	} else if runOptions.Once {
-		_, err = srv.Run()
-	} else {
-		err = srv.RunForever(nil)
-	}
-	if err != nil {
-		log.Fatal(err)
-	}
-}
-
-func mustReadConfig(dst interface{}, path string) {
-	if err := config.LoadFile(dst, path); err != nil {
-		log.Fatal(err)
-	}
-}
-
 type Server struct {
 	config     Config
 	runOptions RunOptions

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list