1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
5 // package service provides a cmd.Handler that brings up a system service.
16 "git.curoverse.com/arvados.git/lib/cmd"
17 "git.curoverse.com/arvados.git/sdk/go/arvados"
18 "git.curoverse.com/arvados.git/sdk/go/ctxlog"
19 "git.curoverse.com/arvados.git/sdk/go/httpserver"
20 "github.com/coreos/go-systemd/daemon"
21 "github.com/sirupsen/logrus"
24 type Handler interface {
29 type NewHandlerFunc func(_ context.Context, _ *arvados.Cluster, _ *arvados.NodeProfile, token string) Handler
32 newHandler NewHandlerFunc
33 svcName arvados.ServiceName
34 ctx context.Context // enables tests to shutdown service; no public API yet
37 // Command returns a cmd.Handler that loads site config, calls
38 // newHandler with the current cluster and node configs, and brings up
39 // an http server with the returned handler.
41 // The handler is wrapped with server middleware (adding X-Request-ID
42 // headers, logging requests/responses, etc).
43 func Command(svcName arvados.ServiceName, newHandler NewHandlerFunc) cmd.Handler {
45 newHandler: newHandler,
47 ctx: context.Background(),
51 func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
52 log := ctxlog.New(stderr, "json", "info")
57 log.WithError(err).Info("exiting")
60 flags := flag.NewFlagSet("", flag.ContinueOnError)
61 flags.SetOutput(stderr)
62 configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
63 nodeProfile := flags.String("node-profile", "", "`Name` of NodeProfiles config entry to use (if blank, use $ARVADOS_NODE_PROFILE or hostname reported by OS)")
64 err = flags.Parse(args)
65 if err == flag.ErrHelp {
68 } else if err != nil {
71 cfg, err := arvados.GetConfig(*configFile)
75 cluster, err := cfg.GetCluster("")
79 log = ctxlog.New(stderr, cluster.Logging.Format, cluster.Logging.Level).WithFields(logrus.Fields{
82 ctx := ctxlog.Context(c.ctx, log)
84 profileName := *nodeProfile
85 if profileName == "" {
86 profileName = os.Getenv("ARVADOS_NODE_PROFILE")
88 profile, err := cluster.GetNodeProfile(profileName)
92 listen := profile.ServicePorts()[c.svcName]
94 err = fmt.Errorf("configuration does not enable the %s service on this host", c.svcName)
98 // Currently all components use SystemRootToken if configured,
99 // otherwise ARVADOS_API_TOKEN. In future, per-process tokens
100 // will be generated/obtained here.
101 token := cluster.SystemRootToken
103 log.Warn("SystemRootToken missing from cluster config, falling back to ARVADOS_API_TOKEN environment variable")
104 token = os.Getenv("ARVADOS_API_TOKEN")
107 handler := c.newHandler(ctx, cluster, profile, token)
108 if err = handler.CheckHealth(); err != nil {
111 srv := &httpserver.Server{
113 Handler: httpserver.AddRequestIDs(httpserver.LogRequests(log, handler)),
121 log.WithFields(logrus.Fields{
123 "Service": c.svcName,
125 if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
126 log.WithError(err).Errorf("error notifying init daemon")
139 const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"