blob: 699ea18896559f98949a6ba3effccd5cf70fca9e [file] [log] [blame]
Serge Bazanski9c09c4e2020-03-24 13:58:01 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package supervisor
18
Serge Bazanski216fe7b2021-05-21 18:36:16 +020019// Supporting infrastructure to allow running some non-Go payloads under
20// supervision.
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010021
22import (
23 "context"
24 "net"
25 "os/exec"
Serge Bazanski216fe7b2021-05-21 18:36:16 +020026
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010027 "google.golang.org/grpc"
Serge Bazanski96043bc2021-10-05 12:10:13 +020028
29 "source.monogon.dev/metropolis/pkg/logtree"
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010030)
31
Serge Bazanski216fe7b2021-05-21 18:36:16 +020032// GRPCServer creates a Runnable that serves gRPC requests as longs as it's not
33// canceled.
34// If graceful is set to true, the server will be gracefully stopped instead of
35// plain stopped. This means all pending RPCs will finish, but also requires
36// streaming gRPC handlers to check their context liveliness and exit
37// accordingly. If the server code does not support this, `graceful` should be
38// false and the server will be killed violently instead.
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010039func GRPCServer(srv *grpc.Server, lis net.Listener, graceful bool) Runnable {
40 return func(ctx context.Context) error {
41 Signal(ctx, SignalHealthy)
42 errC := make(chan error)
43 go func() {
44 errC <- srv.Serve(lis)
45 }()
46 select {
47 case <-ctx.Done():
48 if graceful {
49 srv.GracefulStop()
50 } else {
51 srv.Stop()
52 }
53 return ctx.Err()
54 case err := <-errC:
55 return err
56 }
57 }
58}
59
Serge Bazanski216fe7b2021-05-21 18:36:16 +020060// RunCommand will create a Runnable that starts a long-running command, whose
61// exit is determined to be a failure.
Serge Bazanski05604292021-03-12 17:47:21 +010062func RunCommand(ctx context.Context, cmd *exec.Cmd, opts ...RunCommandOption) error {
Serge Bazanski967be212020-11-02 11:26:59 +010063 Signal(ctx, SignalHealthy)
Serge Bazanski05604292021-03-12 17:47:21 +010064
65 var parseKLog bool
66 for _, opt := range opts {
67 if opt.parseKlog {
68 parseKLog = true
69 }
70 }
71
72 if parseKLog {
73 // We make two klogs, one for each of stdout/stderr. This is to prevent
74 // accidental interleaving of both.
75 klogStdout := logtree.KLogParser(Logger(ctx))
76 defer klogStdout.Close()
77 klogStderr := logtree.KLogParser(Logger(ctx))
78 defer klogStderr.Close()
79
80 cmd.Stdout = klogStdout
81 cmd.Stderr = klogStderr
82 } else {
83 cmd.Stdout = RawLogger(ctx)
84 cmd.Stderr = RawLogger(ctx)
85 }
Serge Bazanski967be212020-11-02 11:26:59 +010086 err := cmd.Run()
87 Logger(ctx).Infof("Command returned: %v", err)
88 return err
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010089}
Serge Bazanski05604292021-03-12 17:47:21 +010090
91type RunCommandOption struct {
92 parseKlog bool
93}
94
Serge Bazanski216fe7b2021-05-21 18:36:16 +020095// ParseKLog signals that the command being run will return klog-compatible
96// logs to stdout and/or stderr, and these will be re-interpreted as structured
Serge Bazanski05604292021-03-12 17:47:21 +010097// logging and emitted to the supervisor's logger.
98func ParseKLog() RunCommandOption {
99 return RunCommandOption{
100 parseKlog: true,
101 }
102}