Add 'sdk/java-v2/' from commit '55f103e336ca9fb8bf1720d2ef4ee8dd4e221118'
[arvados.git] / lib / service / cmd.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 // package service provides a cmd.Handler that brings up a system service.
6 package service
7
8 import (
9         "context"
10         "flag"
11         "fmt"
12         "io"
13         "net/http"
14         "os"
15
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"
22 )
23
24 type Handler interface {
25         http.Handler
26         CheckHealth() error
27 }
28
29 type NewHandlerFunc func(context.Context, *arvados.Cluster, *arvados.NodeProfile) Handler
30
31 type command struct {
32         newHandler NewHandlerFunc
33         svcName    arvados.ServiceName
34 }
35
36 // Command returns a cmd.Handler that loads site config, calls
37 // newHandler with the current cluster and node configs, and brings up
38 // an http server with the returned handler.
39 //
40 // The handler is wrapped with server middleware (adding X-Request-ID
41 // headers, logging requests/responses, etc).
42 func Command(svcName arvados.ServiceName, newHandler NewHandlerFunc) cmd.Handler {
43         return &command{
44                 newHandler: newHandler,
45                 svcName:    svcName,
46         }
47 }
48
49 func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
50         log := ctxlog.New(stderr, "json", "info")
51
52         var err error
53         defer func() {
54                 if err != nil {
55                         log.WithError(err).Info("exiting")
56                 }
57         }()
58         flags := flag.NewFlagSet("", flag.ContinueOnError)
59         flags.SetOutput(stderr)
60         configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
61         nodeProfile := flags.String("node-profile", "", "`Name` of NodeProfiles config entry to use (if blank, use $ARVADOS_NODE_PROFILE or hostname reported by OS)")
62         err = flags.Parse(args)
63         if err == flag.ErrHelp {
64                 err = nil
65                 return 0
66         } else if err != nil {
67                 return 2
68         }
69         cfg, err := arvados.GetConfig(*configFile)
70         if err != nil {
71                 return 1
72         }
73         cluster, err := cfg.GetCluster("")
74         if err != nil {
75                 return 1
76         }
77         log = ctxlog.New(stderr, cluster.Logging.Format, cluster.Logging.Level).WithFields(logrus.Fields{
78                 "PID": os.Getpid(),
79         })
80         ctx := ctxlog.Context(context.Background(), log)
81         profileName := *nodeProfile
82         if profileName == "" {
83                 profileName = os.Getenv("ARVADOS_NODE_PROFILE")
84         }
85         profile, err := cluster.GetNodeProfile(profileName)
86         if err != nil {
87                 return 1
88         }
89         listen := profile.ServicePorts()[c.svcName]
90         if listen == "" {
91                 err = fmt.Errorf("configuration does not enable the %s service on this host", c.svcName)
92                 return 1
93         }
94         handler := c.newHandler(ctx, cluster, profile)
95         if err = handler.CheckHealth(); err != nil {
96                 return 1
97         }
98         srv := &httpserver.Server{
99                 Server: http.Server{
100                         Handler: httpserver.AddRequestIDs(httpserver.LogRequests(log, handler)),
101                 },
102                 Addr: listen,
103         }
104         err = srv.Start()
105         if err != nil {
106                 return 1
107         }
108         log.WithFields(logrus.Fields{
109                 "Listen":  srv.Addr,
110                 "Service": c.svcName,
111         }).Info("listening")
112         if _, err := daemon.SdNotify(false, "READY=1"); err != nil {
113                 log.WithError(err).Errorf("error notifying init daemon")
114         }
115         err = srv.Wait()
116         if err != nil {
117                 return 1
118         }
119         return 0
120 }
121
122 const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"