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

gitaly_builder.go « testcfg « testhelper « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 855bbe5493855c98358ed461e5cc441c1cb81910 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package testcfg

import (
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/stretchr/testify/require"
	"gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest"
	"gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config"
	"gitlab.com/gitlab-org/gitaly/v14/internal/testhelper"
	"gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
)

// UnconfiguredSocketPath is used to bypass config validation errors
// when building the configuration. The socket path is now known yet
// at the time of building the configuration and is substituted later
// when the service is actually spun up.
const UnconfiguredSocketPath = "it is a stub to bypass Validate method"

// Option is a configuration option for the builder.
type Option func(*GitalyCfgBuilder)

// WithBase allows use cfg as a template for start building on top of.
// override parameter signals if settings of the cfg can be overridden or not
// (if setting has a default value it is considered "not configured" and can be
// set despite flag value).
func WithBase(cfg config.Cfg) Option {
	return func(builder *GitalyCfgBuilder) {
		builder.cfg = cfg
	}
}

// WithStorages allows to configure list of storages under this gitaly instance.
// All storages will have a test repository by default.
func WithStorages(name string, names ...string) Option {
	return func(builder *GitalyCfgBuilder) {
		builder.storages = append([]string{name}, names...)
	}
}

// WithRealLinguist suppress stubbing of the linguist language detection.
func WithRealLinguist() Option {
	return func(builder *GitalyCfgBuilder) {
		builder.realLinguist = true
	}
}

// WithPackObjectsCacheEnabled enables the pack object cache.
func WithPackObjectsCacheEnabled() Option {
	return func(builder *GitalyCfgBuilder) {
		builder.packObjectsCacheEnabled = true
	}
}

// NewGitalyCfgBuilder returns gitaly configuration builder with configured set of options.
func NewGitalyCfgBuilder(opts ...Option) GitalyCfgBuilder {
	cfgBuilder := GitalyCfgBuilder{}

	for _, opt := range opts {
		opt(&cfgBuilder)
	}

	return cfgBuilder
}

// GitalyCfgBuilder automates creation of the gitaly configuration and filesystem structure required.
type GitalyCfgBuilder struct {
	cfg config.Cfg

	storages                []string
	realLinguist            bool
	packObjectsCacheEnabled bool
}

// Build setups required filesystem structure, creates and returns configuration of the gitaly service.
func (gc *GitalyCfgBuilder) Build(t testing.TB) config.Cfg {
	t.Helper()

	cfg := gc.cfg
	if cfg.SocketPath == "" {
		cfg.SocketPath = UnconfiguredSocketPath
	}

	root := testhelper.TempDir(t)

	if cfg.BinDir == "" {
		cfg.BinDir = filepath.Join(root, "bin.d")
		require.NoError(t, os.Mkdir(cfg.BinDir, 0o755))
	}

	if cfg.Ruby.Dir == "" {
		_, currentFile, _, ok := runtime.Caller(0)
		require.True(t, ok, "could not get caller info")
		cfg.Ruby.Dir = filepath.Join(filepath.Dir(currentFile), "../../../ruby")
	}

	if cfg.Logging.Dir == "" {
		cfg.Logging.Dir = filepath.Join(root, "log.d")
		require.NoError(t, os.Mkdir(cfg.Logging.Dir, 0o755))
	}

	if cfg.GitlabShell.Dir == "" {
		cfg.GitlabShell.Dir = filepath.Join(root, "shell.d")
		require.NoError(t, os.Mkdir(cfg.GitlabShell.Dir, 0o755))
	}

	if cfg.RuntimeDir == "" {
		cfg.RuntimeDir = filepath.Join(root, "runtime.d")
		require.NoError(t, os.Mkdir(cfg.RuntimeDir, 0o700))
	}

	if cfg.InternalSocketDir == "" {
		cfg.InternalSocketDir = filepath.Join(cfg.RuntimeDir, "sock.d")
		require.NoError(t, os.Mkdir(cfg.InternalSocketDir, 0o755))
	}

	if len(cfg.Storages) != 0 && len(gc.storages) != 0 {
		require.FailNow(t, "invalid configuration build setup: fix storages configured")
	}

	if len(cfg.Storages) == 0 {
		storagesDir := filepath.Join(root, "storages.d")
		require.NoError(t, os.Mkdir(storagesDir, 0o755))

		if len(gc.storages) == 0 {
			gc.storages = []string{"default"}
		}

		// creation of the required storages (empty storage directories)
		cfg.Storages = make([]config.Storage, len(gc.storages))
		for i, storageName := range gc.storages {
			storagePath := filepath.Join(storagesDir, storageName)
			require.NoError(t, os.MkdirAll(storagePath, 0o755))
			cfg.Storages[i].Name = storageName
			cfg.Storages[i].Path = storagePath
		}
	}

	if !gc.realLinguist {
		if cfg.Ruby.LinguistLanguagesPath == "" {
			// set a stub to prevent a long ruby process to run where it is not needed
			cfg.Ruby.LinguistLanguagesPath = filepath.Join(root, "linguist_languages.json")
			require.NoError(t, os.WriteFile(cfg.Ruby.LinguistLanguagesPath, []byte(`{}`), 0o655))
		}
	}

	cfg.PackObjectsCache.Enabled = gc.packObjectsCacheEnabled

	require.NoError(t, cfg.Validate())

	return cfg
}

// BuildWithRepoAt setups required filesystem structure, creates and returns configuration of the gitaly service,
// clones test repository into each configured storage the provided relative path.
func (gc *GitalyCfgBuilder) BuildWithRepoAt(t testing.TB, relativePath string) (config.Cfg, []*gitalypb.Repository) {
	t.Helper()

	cfg := gc.Build(t)

	// clone the test repo to the each storage
	repos := make([]*gitalypb.Repository, len(cfg.Storages))
	for i, gitalyStorage := range cfg.Storages {
		repo, _ := gittest.CloneRepo(t, cfg, gitalyStorage, gittest.CloneRepoOpts{
			RelativePath: relativePath,
		})

		repos[i] = repo
		repos[i].StorageName = gitalyStorage.Name
	}

	return cfg, repos
}

// Build creates a minimal configuration setup with no options and returns it with cleanup function.
func Build(t testing.TB, opts ...Option) config.Cfg {
	cfgBuilder := NewGitalyCfgBuilder(opts...)

	return cfgBuilder.Build(t)
}

// BuildWithRepo creates a minimal configuration setup with no options.
// It also clones test repository at the storage and returns it with the full path to the repository.
func BuildWithRepo(t testing.TB, opts ...Option) (config.Cfg, *gitalypb.Repository, string) {
	cfgBuilder := NewGitalyCfgBuilder(opts...)

	cfg, repos := cfgBuilder.BuildWithRepoAt(t, t.Name())
	repoPath := filepath.Join(cfg.Storages[0].Path, repos[0].RelativePath)
	return cfg, repos[0], repoPath
}