blob: c2b569c602854644369ea53075c5b8a498334459 [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
19// Supporting infrastructure to allow running some non-Go payloads under supervision.
20
21import (
22 "context"
23 "net"
24 "os/exec"
Serge Bazanski05604292021-03-12 17:47:21 +010025 "source.monogon.dev/metropolis/pkg/logtree"
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010026
27 "google.golang.org/grpc"
28)
29
30// GRPCServer creates a Runnable that serves gRPC requests as longs as it's not canceled.
31// If graceful is set to true, the server will be gracefully stopped instead of plain stopped. This means all pending
32// RPCs will finish, but also requires streaming gRPC handlers to check their context liveliness and exit accordingly.
33// If the server code does not support this, `graceful` should be false and the server will be killed violently instead.
34func GRPCServer(srv *grpc.Server, lis net.Listener, graceful bool) Runnable {
35 return func(ctx context.Context) error {
36 Signal(ctx, SignalHealthy)
37 errC := make(chan error)
38 go func() {
39 errC <- srv.Serve(lis)
40 }()
41 select {
42 case <-ctx.Done():
43 if graceful {
44 srv.GracefulStop()
45 } else {
46 srv.Stop()
47 }
48 return ctx.Err()
49 case err := <-errC:
50 return err
51 }
52 }
53}
54
Serge Bazanski967be212020-11-02 11:26:59 +010055// RunCommand will create a Runnable that starts a long-running command, whose exit is determined to be a failure.
Serge Bazanski05604292021-03-12 17:47:21 +010056func RunCommand(ctx context.Context, cmd *exec.Cmd, opts ...RunCommandOption) error {
Serge Bazanski967be212020-11-02 11:26:59 +010057 Signal(ctx, SignalHealthy)
Serge Bazanski05604292021-03-12 17:47:21 +010058
59 var parseKLog bool
60 for _, opt := range opts {
61 if opt.parseKlog {
62 parseKLog = true
63 }
64 }
65
66 if parseKLog {
67 // We make two klogs, one for each of stdout/stderr. This is to prevent
68 // accidental interleaving of both.
69 klogStdout := logtree.KLogParser(Logger(ctx))
70 defer klogStdout.Close()
71 klogStderr := logtree.KLogParser(Logger(ctx))
72 defer klogStderr.Close()
73
74 cmd.Stdout = klogStdout
75 cmd.Stderr = klogStderr
76 } else {
77 cmd.Stdout = RawLogger(ctx)
78 cmd.Stderr = RawLogger(ctx)
79 }
Serge Bazanski967be212020-11-02 11:26:59 +010080 err := cmd.Run()
81 Logger(ctx).Infof("Command returned: %v", err)
82 return err
Serge Bazanski9c09c4e2020-03-24 13:58:01 +010083}
Serge Bazanski05604292021-03-12 17:47:21 +010084
85type RunCommandOption struct {
86 parseKlog bool
87}
88
89// ParseKLog signals that the command being run will return klog-compatible logs
90// to stdout and/or stderr, and these will be re-interpreted as structured
91// logging and emitted to the supervisor's logger.
92func ParseKLog() RunCommandOption {
93 return RunCommandOption{
94 parseKlog: true,
95 }
96}