File: main.go

package info (click to toggle)
gitlab-shell 14.35.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,652 kB
  • sloc: ruby: 1,129; makefile: 583; sql: 391; sh: 384
file content (126 lines) | stat: -rw-r--r-- 3,917 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Command praefect provides a reverse-proxy server with high-availability
// specific features for Gitaly.
//
// Additionally, praefect has subcommands for common tasks:
//
// The subcommand "sql-migrate-status" will show which SQL migrations have
// been applied and which ones have not:
//
//	praefect -config PATH_TO_CONFIG sql-migrate-status
//

package praefect

import (
	"fmt"
	"log"
	"os"
	"os/signal"

	"github.com/urfave/cli/v2"
	"gitlab.com/gitlab-org/gitaly/v16/internal/praefect/service"
	"gitlab.com/gitlab-org/gitaly/v16/internal/version"
	"golang.org/x/exp/slices"
)

func init() {
	// Override the version printer so the output format matches what Praefect
	// used before the introduction of the CLI toolkit.
	cli.VersionPrinter = func(ctx *cli.Context) {
		fmt.Fprintln(ctx.App.Writer, version.GetVersionString("Praefect"))
	}
}

const (
	progname = "praefect"

	configFlagName = "config"
)

// NewApp returns a new praefect app.
func NewApp() *cli.App {
	interrupt := make(chan os.Signal, 1)

	return &cli.App{
		Name:    progname,
		Usage:   "a gitaly proxy",
		Version: version.GetVersionString("Praefect"),
		// serveAction is also here in the root to keep the CLI backwards compatible with
		// the previous way to launch Praefect with just `praefect -config FILE`.
		// We may want to deprecate this eventually.
		//
		// The 'DefaultCommand: "serve"' setting can't be used here because it won't be
		// possible to invoke sub-command not yet registered.
		Action:          serveAction,
		HideHelpCommand: true,
		Commands: []*cli.Command{
			newServeCommand(),
			newConfigurationCommand(),
			newAcceptDatalossCommand(),
			newCheckCommand(service.AllChecks()),
			newDatalossCommand(),
			newDialNodesCommand(),
			newListStoragesCommand(),
			newListUntrackedRepositoriesCommand(),
			newTrackRepositoryCommand(),
			newTrackRepositoriesCommand(),
			newVerifyCommand(),
			newMetadataCommand(),
			newSQLPingCommand(),
			newSQLMigrateCommand(),
			newSQLMigrateDownCommand(),
			newSQLMigrateStatusCommand(),
			newRemoveRepositoryCommand(),
			newSetReplicationFactorCommand(),
		},
		Flags: []cli.Flag{
			&cli.StringFlag{
				// We can't mark it required, because it is not for all sub-commands.
				// We need it as it is used by majority of the sub-commands and
				// because of the existing format of commands invocation.
				Name:  configFlagName,
				Usage: "load configuration from `FILE`",
			},
		},
		Before: func(appCtx *cli.Context) error {
			// Praefect service manages os.Interrupt on its own, by making a "table-flip".
			// That is why the signal listening is omitted if there are no arguments passed
			// (old-fashioned method of starting Praefect service) or 'serve' sub-command
			// is invoked. Other sub-commands require signal to be properly handled.
			args := appCtx.Args().Slice()
			if len(args) == 0 || slices.Contains(args, "serve") {
				return nil
			}

			signal.Notify(interrupt, os.Interrupt)
			go func() {
				if _, ok := <-interrupt; ok {
					os.Exit(130) // indicates program was interrupted
				}
			}()

			return nil
		},
		After: func(*cli.Context) error {
			close(interrupt)
			return nil
		},
	}
}

// mustProvideConfigFlag extracts value of the 'config' flag and returns it.
// If flag is not set the help for the command will be printed and terminated with exit code 2.
func mustProvideConfigFlag(ctx *cli.Context, command string) string {
	pathToConfigFile := ctx.String(configFlagName)
	if pathToConfigFile == "" {
		// We can't make 'config' flag required for all commands, but we still want the
		// same output to be printed if it is not provided.
		// It should be removed after migration to the `praefect CMD -config FILE`
		// where we can mark it as required for each sub-command.
		_ = cli.ShowCommandHelp(ctx, command)
		log.Printf("Required flag %q not set\n", configFlagName)
		os.Exit(2)
	}

	return pathToConfigFile
}