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