Skip to content

Commit 01b7a61

Browse files
committed
feat: add a forced status of premium instance
Required for use cases where user token does not have admin permissions
1 parent 902627f commit 01b7a61

File tree

8 files changed

+68
-30
lines changed

8 files changed

+68
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,4 @@ unit-tests.xml
6464
coverage.out
6565
.scannerwork/
6666
*-report*
67+
.sonarlint/

cmd/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ func main() {
7777
rootCmd.Flags().StringVar(&args.DestinationGitlabURL, "destination-url", os.Getenv("DESTINATION_GITLAB_URL"), "Destination GitLab URL")
7878
rootCmd.Flags().StringVar(&args.DestinationGitlabToken, "destination-token", os.Getenv("DESTINATION_GITLAB_TOKEN"), "Destination GitLab Token")
7979
rootCmd.Flags().BoolVar(&args.DestinationGitlabIsBig, "destination-big", strings.TrimSpace(os.Getenv("DESTINATION_GITLAB_BIG")) != "", "Destination GitLab is a big instance")
80+
rootCmd.Flags().BoolVarP(&args.DestinationGitlabForcePremium, "destination-force-premium", "p", false, "Force the destination GitLab to be treated as a premium instance")
8081
rootCmd.Flags().BoolVarP(&args.Verbose, "verbose", "v", false, "Enable verbose output")
8182
rootCmd.Flags().BoolVarP(&args.NoPrompt, "no-prompt", "n", strings.TrimSpace(os.Getenv("NO_PROMPT")) != "", "Disable prompting for missing values")
8283
rootCmd.Flags().StringVar(&mirrorMappingPath, "mirror-mapping", os.Getenv("MIRROR_MAPPING"), "Path to the mirror mapping file")

internal/mirroring/get_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import (
66
"testing"
77
)
88

9+
const (
10+
EXPECTED_ERROR_MESSAGE = "expected error: %v, got: %v"
11+
)
12+
913
func TestCheckPathMatchesFilters(t *testing.T) {
1014
tests := []struct {
1115
name string
@@ -112,7 +116,7 @@ func TestGetParentNamespaceID(t *testing.T) {
112116

113117
// Check if an error was expected
114118
if (err != nil) != test.expectedError {
115-
t.Errorf("expected error: %v, got: %v", test.expectedError, err)
119+
t.Errorf(EXPECTED_ERROR_MESSAGE, test.expectedError, err)
116120
}
117121
})
118122
}
@@ -180,7 +184,7 @@ func TestFetchAll(t *testing.T) {
180184

181185
// Check if an error was expected
182186
if (err != nil) != test.expectedError {
183-
t.Errorf("expected error: %v, got: %v", test.expectedError, err)
187+
t.Errorf(EXPECTED_ERROR_MESSAGE, test.expectedError, err)
184188
}
185189

186190
//Check if the instance cache contains the expected projects and groups
@@ -261,7 +265,7 @@ func TestIsVersionGreaterThanThreshold(t *testing.T) {
261265

262266
thresholdOk, err := gitlabInstance.IsVersionGreaterThanThreshold()
263267
if (err != nil) != test.expectedError {
264-
t.Fatalf("expected error: %v, got: %v", test.expectedError, err)
268+
t.Fatalf(EXPECTED_ERROR_MESSAGE, test.expectedError, err)
265269
}
266270
if thresholdOk != test.expectedResponse {
267271
t.Errorf("expected thresholdOk: %v, got: %v", test.expectedResponse, thresholdOk)
@@ -327,7 +331,7 @@ func TestIsLicensePremium(t *testing.T) {
327331

328332
isPremium, err := gitlabInstance.IsLicensePremium()
329333
if (err != nil) != test.expectedError {
330-
t.Fatalf("expected error: %v, got: %v", test.expectedError, err)
334+
t.Fatalf(EXPECTED_ERROR_MESSAGE, test.expectedError, err)
331335
}
332336
if isPremium != test.expectedResponse {
333337
t.Errorf("expected isPremium: %v, got: %v", test.expectedResponse, isPremium)

internal/mirroring/main.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) []error {
4848
if err != nil {
4949
return []error{err}
5050
}
51-
pullMirrorAvailable, err := destinationGitlabInstance.IsPullMirrorAvailable()
51+
pullMirrorAvailable, err := destinationGitlabInstance.IsPullMirrorAvailable(gitlabMirrorArgs.DestinationGitlabForcePremium)
5252
if err != nil {
5353
return []error{err}
5454
} else if pullMirrorAvailable {
@@ -178,7 +178,7 @@ func (destinationGitlabInstance *GitlabInstance) DryRunReleases(sourceGitlabInst
178178
}
179179

180180
// IsPullMirrorAvailable checks the destination GitLab instance for version and license compatibility.
181-
func (g *GitlabInstance) IsPullMirrorAvailable() (bool, error) {
181+
func (g *GitlabInstance) IsPullMirrorAvailable(forcePremium bool) (bool, error) {
182182
zap.L().Info("Checking destination GitLab instance")
183183
thresholdOk, err := g.IsVersionGreaterThanThreshold()
184184
if err != nil {
@@ -187,8 +187,10 @@ func (g *GitlabInstance) IsPullMirrorAvailable() (bool, error) {
187187

188188
isPremium, err := g.IsLicensePremium()
189189
if err != nil {
190-
return false, fmt.Errorf("failed to check if destination GitLab instance is premium: %w", err)
190+
if !forcePremium {
191+
return false, fmt.Errorf("failed to check if destination GitLab instance is premium: %w", err)
192+
}
191193
}
192194

193-
return thresholdOk && isPremium, nil
195+
return thresholdOk && (isPremium || forcePremium), nil
194196
}

internal/mirroring/main_test.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,53 +175,63 @@ func TestDryRun(t *testing.T) {
175175
}
176176

177177
func TestIsPullMirrorAvailable(t *testing.T) {
178+
const supportedVersion = "18.0.0"
179+
const unsupportedVersion = "17.0.0"
178180
tests := []struct {
179181
name string
180182
licensePlan string
181183
version string
182184
expectedError bool
183185
expectedResult bool
186+
forcePremium bool
184187
}{
185188
{
186189
name: "Premium license, good version",
187190
licensePlan: PREMIUM_PLAN,
188-
version: "18.0.0",
191+
version: supportedVersion,
189192
expectedResult: true,
190193
},
191194
{
192195
name: "Ultimate license, good version",
193196
licensePlan: ULTIMATE_PLAN,
194-
version: "18.0.0",
197+
version: supportedVersion,
195198
expectedResult: true,
196199
},
197200
{
198201
name: "Free license, good version",
199202
licensePlan: "free",
200-
version: "18.0.0",
203+
version: supportedVersion,
201204
expectedResult: false,
202205
},
206+
{
207+
name: "Free license, good version, force premium",
208+
licensePlan: "free",
209+
version: supportedVersion,
210+
expectedResult: true,
211+
forcePremium: true,
212+
},
203213
{
204214
name: "Premium license, bad version",
205215
licensePlan: PREMIUM_PLAN,
206-
version: "17.0.0",
216+
version: unsupportedVersion,
207217
expectedResult: false,
208218
},
209219
{
210220
name: "Ultimate license, bad version",
211221
licensePlan: ULTIMATE_PLAN,
212-
version: "17.0.0",
222+
version: unsupportedVersion,
213223
expectedResult: false,
214224
},
215225
{
216226
name: "Bad license, good version",
217227
licensePlan: "bad_license",
218-
version: "18.0.0",
228+
version: supportedVersion,
219229
expectedResult: false,
220230
},
221231
{
222232
name: "Bad license, bad version",
223233
licensePlan: "bad_license",
224-
version: "17.0.0",
234+
version: unsupportedVersion,
225235
expectedResult: false,
226236
},
227237
{
@@ -247,7 +257,7 @@ func TestIsPullMirrorAvailable(t *testing.T) {
247257
})
248258
}
249259

250-
pullMirrorAvailable, err := gitlabInstance.IsPullMirrorAvailable()
260+
pullMirrorAvailable, err := gitlabInstance.IsPullMirrorAvailable(tt.forcePremium)
251261
if (err != nil) != tt.expectedError {
252262
t.Fatalf("CheckDestinationInstance() error = %v, expectedError %v", err, tt.expectedError)
253263
}

internal/mirroring/post_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func TestCreateProjects(t *testing.T) {
212212
},
213213
}
214214
err := destinationGitlabInstance.createProjects(sourceGitlabInstance, mirrorMapping)
215-
if err != nil && len(err) > 0 {
215+
if len(err) > 0 {
216216
t.Errorf("Unexpected error when creating projects: %v", err)
217217
}
218218
if len(destinationGitlabInstance.Projects) == 0 {

internal/utils/types.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,18 @@ const (
3333
// - version: whether to show the version
3434
// - retry: the number of retries for the GitLab API requests
3535
type ParserArgs struct {
36-
SourceGitlabURL string
37-
SourceGitlabToken string
38-
SourceGitlabIsBig bool
39-
DestinationGitlabURL string
40-
DestinationGitlabToken string
41-
DestinationGitlabIsBig bool
42-
MirrorMapping *MirrorMapping
43-
Verbose bool
44-
NoPrompt bool
45-
DryRun bool
46-
Retry int
36+
SourceGitlabURL string
37+
SourceGitlabToken string
38+
SourceGitlabIsBig bool
39+
DestinationGitlabURL string
40+
DestinationGitlabToken string
41+
DestinationGitlabIsBig bool
42+
DestinationGitlabForcePremium bool
43+
MirrorMapping *MirrorMapping
44+
Verbose bool
45+
NoPrompt bool
46+
DryRun bool
47+
Retry int
4748
}
4849

4950
// ProjectMirrorOptions defines how the project should be mirrored
@@ -72,25 +73,33 @@ type MirrorMapping struct {
7273
muGroups sync.RWMutex
7374
}
7475

76+
// AddProject adds a project to the mapping
77+
// It takes the project name and the mirroring options as parameters
78+
// It locks the projects mutex to ensure thread safety
7579
func (m *MirrorMapping) AddProject(project string, options *MirroringOptions) {
7680
m.muProjects.Lock()
7781
defer m.muProjects.Unlock()
7882
m.Projects[project] = options
7983
}
8084

85+
// AddGroup adds a group to the mapping
86+
// It takes the group name and the mirroring options as parameters
87+
// It locks the groups mutex to ensure thread safety
8188
func (m *MirrorMapping) AddGroup(group string, options *MirroringOptions) {
8289
m.muGroups.Lock()
8390
defer m.muGroups.Unlock()
8491
m.Groups[group] = options
8592
}
8693

94+
// GetProject retrieves the mirroring options for a project
8795
func (m *MirrorMapping) GetProject(project string) (*MirroringOptions, bool) {
8896
m.muProjects.RLock()
8997
defer m.muProjects.RUnlock()
9098
options, ok := m.Projects[project]
9199
return options, ok
92100
}
93101

102+
// GetGroup retrieves the mirroring options for a group
94103
func (m *MirrorMapping) GetGroup(group string) (*MirroringOptions, bool) {
95104
m.muGroups.RLock()
96105
defer m.muGroups.RUnlock()
@@ -167,6 +176,9 @@ func (m *MirrorMapping) checkProjects(errChan chan error) {
167176
}
168177
}
169178

179+
// checkCopyPaths checks if the source and destination paths are valid
180+
// It checks if the paths are not empty, do not start or end with a slash,
181+
// and if the destination path is in a namespace for projects
170182
func checkCopyPaths(sourcePath string, destinationPath string, pathType string, errChan chan error) {
171183
// Ensure the source project path and destination path are not empty
172184
if sourcePath == "" || destinationPath == "" {
@@ -191,6 +203,8 @@ func checkCopyPaths(sourcePath string, destinationPath string, pathType string,
191203
}
192204
}
193205

206+
// checkGroups checks if the groups are valid
207+
// It checks if the group names and destination paths are valid
194208
func (m *MirrorMapping) checkGroups(errChan chan error) {
195209
duplicateDestinationFinder := make(map[string]struct{}, len(m.Groups))
196210
for group, options := range m.Groups {
@@ -212,6 +226,8 @@ func (m *MirrorMapping) checkGroups(errChan chan error) {
212226
}
213227
}
214228

229+
// checkVisibility checks if the visibility string is valid
230+
// It checks if the visibility string is one of the valid GitLab visibility values
215231
func checkVisibility(visibility string) bool {
216232
var valid bool
217233
switch visibility {
@@ -227,6 +243,8 @@ func checkVisibility(visibility string) bool {
227243
return valid
228244
}
229245

246+
// ConvertVisibility converts a visibility string to a gitlab.VisibilityValue
247+
// It returns the corresponding gitlab.VisibilityValue or gitlab.PublicVisibility if the string is invalid
230248
func ConvertVisibility(visibility string) gitlab.VisibilityValue {
231249
switch visibility {
232250
case string(gitlab.PublicVisibility):
@@ -240,6 +258,8 @@ func ConvertVisibility(visibility string) gitlab.VisibilityValue {
240258
}
241259
}
242260

261+
// StringArraysMatchValues checks if two string arrays match in values
262+
// It returns true if both arrays have the same values, regardless of order
243263
func StringArraysMatchValues(array1 []string, array2 []string) bool {
244264
if len(array1) != len(array2) {
245265
return false

internal/utils/types_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ func TestCheckVisibility(t *testing.T) {
396396
}
397397
}
398398

399-
func TestMirrorMapping_GetProject(t *testing.T) {
399+
func TestMirrorMappingGetProject(t *testing.T) {
400400
// Prepare a mirror mapping with some project entries
401401
opts1 := &MirroringOptions{
402402
DestinationPath: "dest1",
@@ -463,7 +463,7 @@ func TestMirrorMapping_GetProject(t *testing.T) {
463463
}
464464
}
465465

466-
func TestMirrorMapping_GetGroup(t *testing.T) {
466+
func TestMirrorMappingGetGroup(t *testing.T) {
467467
// Prepare a mirror mapping with some group entries
468468
optsA := &MirroringOptions{
469469
DestinationPath: "groupDestA",

0 commit comments

Comments
 (0)