Skip to content

Commit adbdfdb

Browse files
committed
fix: logging upgrade
logging output is now done in a proper list instead of an indented string fixed MergeErrors function to support new errors aggregation required
1 parent fde4d32 commit adbdfdb

File tree

11 files changed

+292
-195
lines changed

11 files changed

+292
-195
lines changed

cmd/main.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,16 @@ func main() {
6464
zap.L().Debug("Mirror Mapping file resolved path: " + filepath.Clean(mirrorMappingPath))
6565

6666
zap.L().Debug("Parsing mirror mapping file")
67-
mapping, err := utils.OpenMirrorMapping(mirrorMappingPath)
68-
if err != nil {
69-
zap.L().Fatal("Error opening mirror mapping file", zap.Error(err))
67+
mapping, mappingErrors := utils.OpenMirrorMapping(mirrorMappingPath)
68+
if mappingErrors != nil {
69+
zap.L().Fatal("Error opening mirror mapping file", zap.Array("errors", utils.ErrorArray(mappingErrors)))
7070
}
7171
zap.L().Debug("Mirror mapping file parsed successfully")
7272
args.MirrorMapping = mapping
7373

74-
err = mirroring.MirrorGitlabs(&args)
75-
if err != nil {
76-
zap.L().Error("Error during mirroring process: " + err.Error())
74+
mirroringErrors := mirroring.MirrorGitlabs(&args)
75+
if mirroringErrors != nil {
76+
zap.L().Error("Error during mirroring process", zap.Array("errors", utils.ErrorArray(mirroringErrors)))
7777
}
7878
zap.L().Info("Mirroring completed")
7979
},

internal/mirroring/get.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import (
1212

1313
// fetchAll retrieves all projects and groups from the GitLab instance
1414
// that match the filters and stores them in the instance cache.
15-
func (g *GitlabInstance) fetchAll(projectFilters map[string]struct{}, groupFilters map[string]struct{}, mirrorMapping *utils.MirrorMapping) error {
16-
zap.L().Info("Fetching all projects and groups from GitLab instance", zap.String("role", g.Role), zap.String(INSTANCE_SIZE, g.InstanceSize), zap.Int("projects", len(projectFilters)), zap.Int("groups", len(groupFilters)))
15+
func (g *GitlabInstance) fetchAll(projectFilters map[string]struct{}, groupFilters map[string]struct{}, mirrorMapping *utils.MirrorMapping) []error {
16+
zap.L().Info("Fetching all projects and groups from GitLab instance", zap.String(ROLE, g.Role), zap.String(INSTANCE_SIZE, g.InstanceSize), zap.Int("projects", len(projectFilters)), zap.Int("groups", len(groupFilters)))
1717
wg := sync.WaitGroup{}
18-
errCh := make(chan error, 2)
18+
errCh := make(chan []error, 2)
1919
wg.Add(2)
2020
go func() {
2121
defer wg.Done()
@@ -32,7 +32,7 @@ func (g *GitlabInstance) fetchAll(projectFilters map[string]struct{}, groupFilte
3232
wg.Wait()
3333
close(errCh)
3434

35-
return utils.MergeErrors(errCh, 2)
35+
return utils.MergeErrors(errCh)
3636
}
3737

3838
// getParentNamespaceID retrieves the parent namespace ID for a given project or group path.

internal/mirroring/get_group.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import (
1414
// It also updates the mirror mapping with the corresponding group creation options.
1515
//
1616
// The function is run in a goroutine for each group, and a wait group is used to wait for all goroutines to finish.
17-
func (g *GitlabInstance) fetchAndProcessGroups(groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) error {
17+
func (g *GitlabInstance) fetchAndProcessGroups(groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) []error {
1818
zap.L().Debug("Fetching and processing groups from GitLab instance", zap.String(ROLE, g.Role), zap.Int("groups", len(*groupFilters)))
1919
if !g.isBig() {
20-
return g.fetchAndProcessGroupsSmallInstance(groupFilters, mirrorMapping)
20+
return []error{g.fetchAndProcessGroupsSmallInstance(groupFilters, mirrorMapping)}
2121
}
2222
return g.fetchAndProcessGroupsLargeInstance(groupFilters, mirrorMapping)
2323
}
@@ -137,7 +137,7 @@ func (g *GitlabInstance) processGroupsSmallInstance(allGroups []*gitlab.Group, g
137137
// fetchAndProcessGroupsLargeInstance retrieves all groups that match the filters from the GitLab instance and stores them in the instance cache.
138138
// It also updates the mirror mapping with the corresponding group creation options.
139139
// It uses goroutines to fetch groups and their projects concurrently.
140-
func (g *GitlabInstance) fetchAndProcessGroupsLargeInstance(groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) error {
140+
func (g *GitlabInstance) fetchAndProcessGroupsLargeInstance(groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) []error {
141141
errChan := make(chan error)
142142
var wg sync.WaitGroup
143143
wg.Add(len(*groupFilters))
@@ -166,7 +166,7 @@ func (g *GitlabInstance) fetchAndProcessGroupsLargeInstance(groupFilters *map[st
166166
// Wait for all goroutines to finish
167167
wg.Wait()
168168
close(errChan)
169-
return utils.MergeErrors(errs, 2)
169+
return utils.MergeErrors(errs)
170170
}
171171

172172
// fetchAndProcessGroupRecursive fetches a group and its projects recursively

internal/mirroring/get_project.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
// It also updates the mirror mapping with the corresponding group creation options.
1616
//
1717
// The function is run in a goroutine for each project, and a wait group is used to wait for all goroutines to finish.
18-
func (g *GitlabInstance) fetchAndProcessProjects(projectFilters *map[string]struct{}, groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) error {
18+
func (g *GitlabInstance) fetchAndProcessProjects(projectFilters *map[string]struct{}, groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) []error {
1919
zap.L().Debug("Fetching and processing projects from GitLab instance", zap.String(ROLE, g.Role), zap.String(INSTANCE_SIZE, g.InstanceSize), zap.Int("projects", len(*projectFilters)), zap.Int("groups", len(*groupFilters)))
2020
if !g.isBig() {
2121
return g.fetchAndProcessProjectsSmallInstance(projectFilters, groupFilters, mirrorMapping)
@@ -63,11 +63,11 @@ func (g *GitlabInstance) storeProject(project *gitlab.Project, parentGroupPath s
6363

6464
// fetchAndProcessProjectsSmallInstance retrieves all projects from the small GitLab instance
6565
// and processes them to store in the instance cache.
66-
func (g *GitlabInstance) fetchAndProcessProjectsSmallInstance(projectFilters *map[string]struct{}, groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) error {
66+
func (g *GitlabInstance) fetchAndProcessProjectsSmallInstance(projectFilters *map[string]struct{}, groupFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) []error {
6767
allProjects, err := g.fetchAllProjectsSmallInstance()
6868
if err != nil {
6969
if len(allProjects) == 0 {
70-
return err
70+
return []error{err}
7171
} else {
7272
zap.L().Warn("Failed to fetch all projects from GitLab instance", zap.String(ROLE, g.Role), zap.Error(err))
7373
}
@@ -146,7 +146,7 @@ func (g *GitlabInstance) processProjectsSmallInstance(allProjects []*gitlab.Proj
146146
//
147147
// It uses goroutines to fetch each project in parallel and a wait group to wait for all goroutines to finish.
148148
// It returns an error if any of the goroutines fail.
149-
func (g *GitlabInstance) fetchAndProcessProjectsBigInstance(projectFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) error {
149+
func (g *GitlabInstance) fetchAndProcessProjectsBigInstance(projectFilters *map[string]struct{}, mirrorMapping *utils.MirrorMapping) []error {
150150
// Fetch each project in parallel
151151
var wg sync.WaitGroup
152152
projectsChan := make(chan *gitlab.Project, len(*projectFilters))
@@ -170,7 +170,7 @@ func (g *GitlabInstance) fetchAndProcessProjectsBigInstance(projectFilters *map[
170170
for project := range projectsChan {
171171
g.storeProject(project, project.PathWithNamespace, mirrorMapping)
172172
}
173-
return utils.MergeErrors(errCh, 2)
173+
return utils.MergeErrors(errCh)
174174
}
175175

176176
// fetchAndProcessGroupProjects retrieves all projects from the group and processes them to store in the instance cache.

internal/mirroring/main.go

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
// It creates two GitLab instances (source and destination) and fetches the groups and projects from both instances.
1616
// It then processes the filters for groups and projects, and finally creates the groups and projects in the destination GitLab instance.
1717
// If the dry run flag is set, it will only print the groups and projects that would be created or updated.
18-
func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
18+
func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) []error {
1919
zap.L().Info("Starting GitLab mirroring process", zap.String(ROLE_SOURCE, gitlabMirrorArgs.SourceGitlabURL), zap.String(ROLE_DESTINATION, gitlabMirrorArgs.DestinationGitlabURL))
2020
sourceGitlabSize := INSTANCE_SIZE_SMALL
2121
if gitlabMirrorArgs.SourceGitlabIsBig {
@@ -29,7 +29,7 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
2929
InstanceSize: sourceGitlabSize,
3030
})
3131
if err != nil {
32-
return err
32+
return []error{err}
3333
}
3434

3535
destinationGitlabSize := INSTANCE_SIZE_SMALL
@@ -44,26 +44,22 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
4444
InstanceSize: destinationGitlabSize,
4545
})
4646
if err != nil {
47-
return err
47+
return []error{err}
4848
}
4949

5050
sourceProjectFilters, sourceGroupFilters, destinationProjectFilters, destinationGroupFilters := processFilters(gitlabMirrorArgs.MirrorMapping)
5151

5252
wg := sync.WaitGroup{}
53-
errCh := make(chan error, 4)
53+
errCh := make(chan []error, 4)
5454
wg.Add(2)
5555

5656
go func() {
5757
defer wg.Done()
58-
if err := sourceGitlabInstance.fetchAll(sourceProjectFilters, sourceGroupFilters, gitlabMirrorArgs.MirrorMapping); err != nil {
59-
errCh <- err
60-
}
58+
errCh <- sourceGitlabInstance.fetchAll(sourceProjectFilters, sourceGroupFilters, gitlabMirrorArgs.MirrorMapping)
6159
}()
6260
go func() {
6361
defer wg.Done()
64-
if err := destinationGitlabInstance.fetchAll(destinationProjectFilters, destinationGroupFilters, gitlabMirrorArgs.MirrorMapping); err != nil {
65-
errCh <- err
66-
}
62+
errCh <- destinationGitlabInstance.fetchAll(destinationProjectFilters, destinationGroupFilters, gitlabMirrorArgs.MirrorMapping)
6763
}()
6864

6965
wg.Wait()
@@ -74,17 +70,12 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) error {
7470
return nil
7571
}
7672

77-
// Create groups and projects in the destination GitLab instance
78-
err = destinationGitlabInstance.createGroups(sourceGitlabInstance, gitlabMirrorArgs.MirrorMapping)
79-
if err != nil {
80-
errCh <- err
81-
}
82-
err = destinationGitlabInstance.createProjects(sourceGitlabInstance, gitlabMirrorArgs.MirrorMapping)
83-
if err != nil {
84-
errCh <- err
85-
}
73+
// Create groups and projects in the destination GitLab instance (Groups must be created before projects)
74+
errCh <- destinationGitlabInstance.createGroups(sourceGitlabInstance, gitlabMirrorArgs.MirrorMapping)
75+
errCh <- destinationGitlabInstance.createProjects(sourceGitlabInstance, gitlabMirrorArgs.MirrorMapping)
76+
8677
close(errCh)
87-
return utils.MergeErrors(errCh, 2)
78+
return utils.MergeErrors(errCh)
8879
}
8980

9081
// processFilters processes the filters for groups and projects.
@@ -137,14 +128,14 @@ func DryRun(sourceGitlabInstance *GitlabInstance, gitlabMirrorArgs *utils.Parser
137128
zap.L().Info("Dry run mode enabled, will not create groups or projects")
138129
zap.L().Info("Groups that will be created (or updated if they already exist):")
139130
for sourceGroupPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Groups {
140-
if sourceGitlabInstance.Groups[sourceGroupPath] != nil {
141-
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceGroupPath, copyOptions.DestinationPath)
131+
if sourceGroup, ok := sourceGitlabInstance.Groups[sourceGroupPath]; ok {
132+
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceGroup.WebURL, copyOptions.DestinationPath)
142133
}
143134
}
144135
zap.L().Info("Projects that will be created (or updated if they already exist):")
145136
for sourceProjectPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Projects {
146-
if sourceGitlabInstance.Projects[sourceProjectPath] != nil {
147-
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceProjectPath, copyOptions.DestinationPath)
137+
if sourceProject, ok := sourceGitlabInstance.Projects[sourceProjectPath]; ok {
138+
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceProject.WebURL, copyOptions.DestinationPath)
148139
}
149140
}
150141
}

internal/mirroring/post.go

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// createGroups creates GitLab groups in the destination GitLab instance based on the mirror mapping.
1919
// It retrieves the source group path for each destination group and creates the group in the destination instance.
2020
// The function also handles the copying of group avatars from the source to the destination instance.
21-
func (destinationGitlab *GitlabInstance) createGroups(sourceGitlab *GitlabInstance, mirrorMapping *utils.MirrorMapping) error {
21+
func (destinationGitlab *GitlabInstance) createGroups(sourceGitlab *GitlabInstance, mirrorMapping *utils.MirrorMapping) []error {
2222
zap.L().Info("Creating groups in GitLab Instance", zap.String(ROLE, ROLE_DESTINATION))
2323

2424
// Reverse the mirror mapping to get the source group path for each destination group
@@ -33,7 +33,7 @@ func (destinationGitlab *GitlabInstance) createGroups(sourceGitlab *GitlabInstan
3333
}
3434
}
3535
close(errorChan)
36-
return utils.MergeErrors(errorChan, 2)
36+
return utils.MergeErrors(errorChan)
3737
}
3838

3939
// createGroup creates a GitLab group in the destination GitLab instance based on the source group and mirror mapping.
@@ -115,7 +115,7 @@ func (g *GitlabInstance) createGroupFromSource(sourceGroup *gitlab.Group, copyOp
115115

116116
// createProjects creates GitLab projects in the destination GitLab instance based on the mirror mapping.
117117
// It retrieves the source project path for each destination project and creates the project in the destination instance.
118-
func (destinationGitlab *GitlabInstance) createProjects(sourceGitlab *GitlabInstance, mirrorMapping *utils.MirrorMapping) error {
118+
func (destinationGitlab *GitlabInstance) createProjects(sourceGitlab *GitlabInstance, mirrorMapping *utils.MirrorMapping) []error {
119119
zap.L().Info("Creating projects in GitLab Instance", zap.String(ROLE, ROLE_DESTINATION))
120120

121121
// Create a wait group to wait for all goroutines to finish
@@ -147,39 +147,36 @@ func (destinationGitlab *GitlabInstance) createProjects(sourceGitlab *GitlabInst
147147
wg.Wait()
148148
close(errorChan)
149149

150-
return utils.MergeErrors(errorChan, 2)
150+
return utils.MergeErrors(errorChan)
151151
}
152152

153153
// createProject creates a GitLab project in the destination GitLab instance based on the source project and mirror mapping.
154154
// It checks if the project already exists in the destination instance and creates it if not.
155155
// The function also handles the copying of project avatars from the source to the destination instance.
156-
func (destinationGitlab *GitlabInstance) createProject(sourceProjectPath string, projectCreationOptions *utils.MirroringOptions, sourceGitlab *GitlabInstance) (*gitlab.Project, error) {
156+
func (destinationGitlab *GitlabInstance) createProject(sourceProjectPath string, projectCreationOptions *utils.MirroringOptions, sourceGitlab *GitlabInstance) (*gitlab.Project, []error) {
157157
destinationProjectPath := projectCreationOptions.DestinationPath
158158
// Check if the project already exists
159159
destinationProject := destinationGitlab.getProject(destinationProjectPath)
160160
var err error
161161
sourceProject := sourceGitlab.Projects[sourceProjectPath]
162162
if sourceProject == nil {
163-
return nil, fmt.Errorf("project %s not found in source GitLab instance (internal error, please review script)", sourceProjectPath)
163+
return nil, []error{fmt.Errorf("project %s not found in source GitLab instance (internal error, please review script)", sourceProjectPath)}
164164
}
165165

166166
// Check if the project already exists in the destination GitLab instance
167167
// If it does not exist, create it
168168
if destinationProject == nil {
169169
destinationProject, err = destinationGitlab.createProjectFromSource(sourceProject, projectCreationOptions)
170170
if err != nil {
171-
return nil, fmt.Errorf("failed to create project %s in destination GitLab instance: %s", destinationProjectPath, err)
171+
return nil, []error{fmt.Errorf("failed to create project %s in destination GitLab instance: %s", destinationProjectPath, err)}
172172
}
173173
}
174174

175175
// If the project already exists, update it with the source project details
176-
err = destinationGitlab.updateProjectFromSource(sourceGitlab, sourceProject, destinationProject, projectCreationOptions)
177-
if err != nil {
178-
return destinationProject, fmt.Errorf("failed to update project %s in destination GitLab instance: %s", destinationProjectPath, err)
179-
}
176+
mergedError := destinationGitlab.updateProjectFromSource(sourceGitlab, sourceProject, destinationProject, projectCreationOptions)
180177

181178
zap.L().Info("Completed project mirroring", zap.String(ROLE_SOURCE, sourceProjectPath), zap.String(ROLE_DESTINATION, destinationProjectPath))
182-
return destinationProject, nil
179+
return destinationProject, mergedError
183180
}
184181

185182
// createProjectFromSource creates a GitLab project in the destination GitLab instance based on the source project.
@@ -228,12 +225,12 @@ func (g *GitlabInstance) createProjectFromSource(sourceProject *gitlab.Project,
228225
// It fetches existing releases from the destination project and creates new releases for those that do not exist.
229226
// The function handles the API calls concurrently using goroutines and a wait group.
230227
// It returns an error if any of the API calls fail.
231-
func (destinationGitlab *GitlabInstance) mirrorReleases(sourceGitlab *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project) error {
228+
func (destinationGitlab *GitlabInstance) mirrorReleases(sourceGitlab *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project) []error {
232229
zap.L().Debug("Starting releases mirroring", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
233230
// Fetch existing releases from the destination project
234231
existingReleases, _, err := destinationGitlab.Gitlab.Releases.ListReleases(destinationProject.ID, &gitlab.ListReleasesOptions{})
235232
if err != nil {
236-
return fmt.Errorf("failed to fetch existing releases for destination project %s: %s", destinationProject.HTTPURLToRepo, err)
233+
return []error{fmt.Errorf("failed to fetch existing releases for destination project %s: %s", destinationProject.HTTPURLToRepo, err)}
237234
}
238235

239236
// Create a map of existing release tags for quick lookup
@@ -247,7 +244,7 @@ func (destinationGitlab *GitlabInstance) mirrorReleases(sourceGitlab *GitlabInst
247244
// Fetch releases from the source project
248245
sourceReleases, _, err := sourceGitlab.Gitlab.Releases.ListReleases(sourceProject.ID, &gitlab.ListReleasesOptions{})
249246
if err != nil {
250-
return fmt.Errorf("failed to fetch releases for source project %s: %s", sourceProject.HTTPURLToRepo, err)
247+
return []error{fmt.Errorf("failed to fetch releases for source project %s: %s", sourceProject.HTTPURLToRepo, err)}
251248
}
252249

253250
// Create a wait group and an error channel for handling API calls concurrently
@@ -288,7 +285,7 @@ func (destinationGitlab *GitlabInstance) mirrorReleases(sourceGitlab *GitlabInst
288285
close(errorChan)
289286

290287
zap.L().Info("Releases mirroring completed", zap.String(ROLE_SOURCE, sourceProject.HTTPURLToRepo), zap.String(ROLE_DESTINATION, destinationProject.HTTPURLToRepo))
291-
return utils.MergeErrors(errorChan, 2)
288+
return utils.MergeErrors(errorChan)
292289
}
293290

294291
// ============================================================ //

internal/mirroring/put.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func (sourceGitlabInstance *GitlabInstance) copyGroupAvatar(destinationGitlabIns
9292
// It enables the project mirror pull, copies the project avatar, and optionally adds the project to the CI/CD catalog.
9393
// It also mirrors releases if the option is set.
9494
// The function uses goroutines to perform these tasks concurrently and waits for all of them to finish.
95-
func (destinationGitlabInstance *GitlabInstance) updateProjectFromSource(sourceGitlabInstance *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project, copyOptions *utils.MirroringOptions) error {
95+
func (destinationGitlabInstance *GitlabInstance) updateProjectFromSource(sourceGitlabInstance *GitlabInstance, sourceProject *gitlab.Project, destinationProject *gitlab.Project, copyOptions *utils.MirroringOptions) []error {
9696
wg := sync.WaitGroup{}
9797
maxErrors := 2
9898
if copyOptions.CI_CD_Catalog {
@@ -145,5 +145,5 @@ func (destinationGitlabInstance *GitlabInstance) updateProjectFromSource(sourceG
145145

146146
wg.Wait()
147147
close(errorChan)
148-
return utils.MergeErrors(errorChan, 4)
148+
return utils.MergeErrors(errorChan)
149149
}

0 commit comments

Comments
 (0)