Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/gitaly/main.go18
-rw-r--r--cmd/gitaly/validate.go73
-rw-r--r--cmd/gitaly/validate_test.go106
-rw-r--r--internal/gitaly/config/config.go2
4 files changed, 194 insertions, 5 deletions
diff --git a/cmd/gitaly/main.go b/cmd/gitaly/main.go
index df49c8b44..4b8a0b3a7 100644
--- a/cmd/gitaly/main.go
+++ b/cmd/gitaly/main.go
@@ -76,13 +76,23 @@ func flagUsage() {
fmt.Println(version.GetVersionString("Gitaly"))
fmt.Printf("Usage: %v [command] [options] <configfile>\n", os.Args[0])
flag.PrintDefaults()
- fmt.Printf("\nThe commands are:\n\n\tcheck\tchecks accessability of internal Rails API\n")
+ fmt.Printf(`
+The commands are:
+
+ check checks accessability of internal Rails API
+ validate-configuration validates provided configuration
+`)
}
func main() {
- // If invoked with subcommand check
- if len(os.Args) > 1 && os.Args[1] == "check" {
- execCheck()
+ // If invoked with subcommand
+ if len(os.Args) > 1 {
+ switch os.Args[1] {
+ case "check":
+ execCheck()
+ case "validate-configuration":
+ execValidateConfiguration()
+ }
}
flag.Usage = flagUsage
diff --git a/cmd/gitaly/validate.go b/cmd/gitaly/validate.go
new file mode 100644
index 000000000..4e5a3f5be
--- /dev/null
+++ b/cmd/gitaly/validate.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+
+ "github.com/pelletier/go-toml/v2"
+ "github.com/sirupsen/logrus"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config"
+)
+
+type validationOutputError struct {
+ Key []string `json:"key,omitempty"`
+ Message string `json:"message,omitempty"`
+}
+
+type validationOutput struct {
+ Errors []validationOutputError `json:"errors,omitempty"`
+}
+
+func execValidateConfiguration() {
+ logrus.SetLevel(logrus.ErrorLevel)
+
+ command := flag.NewFlagSet("validate-configuration", flag.ExitOnError)
+ command.Usage = func() {
+ fmt.Fprintf(os.Stderr, "Usage: %v validate-configuration < <configfile>\n", os.Args[0])
+ command.PrintDefaults()
+ }
+
+ cfg, err := config.Load(os.Stdin)
+ if err != nil {
+ terr := &toml.DecodeError{}
+ if errors.As(err, &terr) {
+ row, column := terr.Position()
+ jsonEncoded(os.Stdout, os.Stderr, " ", validationOutput{Errors: []validationOutputError{{
+ Key: terr.Key(),
+ Message: fmt.Sprintf("line %d column %d: %v", row, column, terr.Error()),
+ }}})
+ os.Exit(1)
+ }
+ fmt.Fprintf(os.Stderr, "processing input data: %v\n", err)
+ os.Exit(1)
+ }
+
+ if err := cfg.Validate(); err != nil {
+ var terr config.ValidationErrors
+ if errors.As(err, &terr) {
+ out := validationOutput{}
+ for _, err := range terr {
+ out.Errors = append(out.Errors, validationOutputError{
+ Key: err.Key,
+ Message: err.Message,
+ })
+ }
+ jsonEncoded(os.Stdout, os.Stderr, " ", out)
+ os.Exit(1)
+ }
+ }
+
+ os.Exit(0)
+}
+
+func jsonEncoded(outStream io.Writer, errStream io.Writer, indent string, val any) {
+ encoder := json.NewEncoder(outStream)
+ encoder.SetIndent("", indent)
+ if err := encoder.Encode(val); err != nil {
+ fmt.Fprintf(errStream, "writing results: %v\n", err)
+ }
+}
diff --git a/cmd/gitaly/validate_test.go b/cmd/gitaly/validate_test.go
new file mode 100644
index 000000000..5725f4022
--- /dev/null
+++ b/cmd/gitaly/validate_test.go
@@ -0,0 +1,106 @@
+package main
+
+import (
+ "bytes"
+ "io"
+ "os/exec"
+ "strings"
+ "testing"
+
+ "github.com/pelletier/go-toml/v2"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/command"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/gitaly/config"
+ "gitlab.com/gitlab-org/gitaly/v15/internal/testhelper/testcfg"
+)
+
+func TestValidateConfiguration(t *testing.T) {
+ t.Parallel()
+ cfg := testcfg.Build(t)
+ testcfg.BuildGitaly(t, cfg)
+
+ for _, tc := range []struct {
+ name string
+ exitCode int
+ stdin func(t *testing.T) io.Reader
+ stderr string
+ stdout string
+ }{
+ {
+ name: "ok",
+ exitCode: 0,
+ stdin: func(*testing.T) io.Reader {
+ t.Helper()
+ var stdin bytes.Buffer
+ require.NoError(t, toml.NewEncoder(&stdin).Encode(cfg))
+ return &stdin
+ },
+ },
+ {
+ name: "bad toml format",
+ exitCode: 1,
+ stdin: func(*testing.T) io.Reader {
+ return strings.NewReader(`graceful_restart_timeout = "bad value"`)
+ },
+ stdout: `{
+ "errors": [
+ {
+ "message": "line 1 column 28: toml: time: invalid duration \"bad value\""
+ }
+ ]
+}
+`,
+ },
+ {
+ name: "validation failures",
+ exitCode: 1,
+ stdin: func(t *testing.T) io.Reader {
+ cfg := cfg
+ cfg.Git.Config = []config.GitConfig{{Key: "bad"}}
+ cfg.Storages = []config.Storage{{Name: " ", Path: cfg.Storages[0].Path}}
+ var stdin bytes.Buffer
+ require.NoError(t, toml.NewEncoder(&stdin).Encode(cfg))
+ return &stdin
+ },
+ stdout: `{
+ "errors": [
+ {
+ "key": [
+ "storage",
+ "name"
+ ],
+ "message": "empty value at declaration 1"
+ },
+ {
+ "key": [
+ "git",
+ "config"
+ ],
+ "message": "invalid configuration key 'bad': key must contain at least one section"
+ }
+ ]
+}
+`,
+ },
+ } {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+ cmd := exec.Command(cfg.BinaryPath("gitaly"), "validate-configuration")
+ var stderr, stdout bytes.Buffer
+ cmd.Stderr = &stderr
+ cmd.Stdout = &stdout
+ cmd.Stdin = tc.stdin(t)
+
+ err := cmd.Run()
+ if tc.exitCode != 0 {
+ status, ok := command.ExitStatus(err)
+ require.Truef(t, ok, "%T: %v", err, err)
+ assert.Equal(t, tc.exitCode, status)
+ }
+ assert.Equal(t, tc.stderr, stderr.String())
+ assert.Equal(t, tc.stdout, stdout.String())
+ })
+ }
+}
diff --git a/internal/gitaly/config/config.go b/internal/gitaly/config/config.go
index f36e484f7..632b65c5e 100644
--- a/internal/gitaly/config/config.go
+++ b/internal/gitaly/config/config.go
@@ -300,7 +300,7 @@ func Load(file io.Reader) (Cfg, error) {
}
if err := toml.NewDecoder(file).Decode(&cfg); err != nil {
- return Cfg{}, fmt.Errorf("load toml: %v", err)
+ return Cfg{}, fmt.Errorf("load toml: %w", err)
}
if err := cfg.setDefaults(); err != nil {