13497: Accept host key on command line.
[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         "flag"
10         "fmt"
11         "io"
12         "net/http"
13
14         "git.curoverse.com/arvados.git/lib/cmd"
15         "git.curoverse.com/arvados.git/sdk/go/arvados"
16         "git.curoverse.com/arvados.git/sdk/go/httpserver"
17         "github.com/Sirupsen/logrus"
18 )
19
20 type NewHandlerFunc func(*arvados.Cluster, *arvados.SystemNode) http.Handler
21
22 type command struct {
23         newHandler NewHandlerFunc
24         svcName    arvados.ServiceName
25 }
26
27 // Command returns a cmd.Handler that loads site config, calls
28 // newHandler with the current cluster and node configs, and brings up
29 // an http server with the returned handler.
30 //
31 // The handler is wrapped with server middleware (adding X-Request-ID
32 // headers, logging requests/responses, etc).
33 func Command(svcName arvados.ServiceName, newHandler NewHandlerFunc) cmd.Handler {
34         return &command{
35                 newHandler: newHandler,
36                 svcName:    svcName,
37         }
38 }
39
40 func (c *command) RunCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.Writer) int {
41         log := logrus.New()
42         log.Formatter = &logrus.JSONFormatter{
43                 TimestampFormat: rfc3339NanoFixed,
44         }
45         log.Out = stderr
46
47         var err error
48         defer func() {
49                 if err != nil {
50                         log.WithError(err).Info("exiting")
51                 }
52         }()
53         flags := flag.NewFlagSet("", flag.ContinueOnError)
54         flags.SetOutput(stderr)
55         configFile := flags.String("config", arvados.DefaultConfigFile, "Site configuration `file`")
56         hostName := flags.String("host", "", "Host profile `name` to use in SystemNodes config (if blank, use hostname reported by OS)")
57         err = flags.Parse(args)
58         if err == flag.ErrHelp {
59                 err = nil
60                 return 0
61         } else if err != nil {
62                 return 2
63         }
64         cfg, err := arvados.GetConfig(*configFile)
65         if err != nil {
66                 return 1
67         }
68         cluster, err := cfg.GetCluster("")
69         if err != nil {
70                 return 1
71         }
72         node, err := cluster.GetSystemNode(*hostName)
73         if err != nil {
74                 return 1
75         }
76         listen := node.ServicePorts()[c.svcName]
77         if listen == "" {
78                 err = fmt.Errorf("configuration does not enable the %s service on this host", c.svcName)
79                 return 1
80         }
81         srv := &httpserver.Server{
82                 Server: http.Server{
83                         Handler: httpserver.AddRequestIDs(httpserver.LogRequests(log, c.newHandler(cluster, node))),
84                 },
85                 Addr: listen,
86         }
87         err = srv.Start()
88         if err != nil {
89                 return 1
90         }
91         log.WithFields(logrus.Fields{
92                 "Listen":  srv.Addr,
93                 "Service": c.svcName,
94         }).Info("listening")
95         err = srv.Wait()
96         if err != nil {
97                 return 1
98         }
99         return 0
100 }
101
102 const rfc3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"