Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions internal/mirroring/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"gitlab-sync/internal/utils"

gitlab "gitlab.com/gitlab-org/api/client-go"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -70,7 +71,7 @@ func MirrorGitlabs(gitlabMirrorArgs *utils.ParserArgs) []error {

// In case of dry run, simply print the groups and projects that would be created or updated
if gitlabMirrorArgs.DryRun {
DryRun(sourceGitlabInstance, gitlabMirrorArgs)
destinationGitlabInstance.DryRun(sourceGitlabInstance, gitlabMirrorArgs.MirrorMapping)
return nil
}

Expand Down Expand Up @@ -128,28 +129,55 @@ func processFilters(filters *utils.MirrorMapping) (map[string]struct{}, map[stri
}

// DryRun prints the groups and projects that would be created or updated in dry run mode.
func DryRun(sourceGitlabInstance *GitlabInstance, gitlabMirrorArgs *utils.ParserArgs) {
func (destinationGitlabInstance *GitlabInstance) DryRun(sourceGitlabInstance *GitlabInstance, mirrorMapping *utils.MirrorMapping) []error {
zap.L().Info("Dry run mode enabled, will not create groups or projects")
zap.L().Info("Groups that will be created (or updated if they already exist):")
for sourceGroupPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Groups {
for sourceGroupPath, copyOptions := range mirrorMapping.Groups {
if sourceGroup, ok := sourceGitlabInstance.Groups[sourceGroupPath]; ok {
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceGroup.WebURL, copyOptions.DestinationPath)
}
}
zap.L().Info("Projects that will be created (or updated if they already exist):")
for sourceProjectPath, copyOptions := range gitlabMirrorArgs.MirrorMapping.Projects {
for sourceProjectPath, copyOptions := range mirrorMapping.Projects {
if sourceProject, ok := sourceGitlabInstance.Projects[sourceProjectPath]; ok {
fmt.Printf(" - %s (source gitlab) -> %s (destination gitlab)\n", sourceProject.WebURL, copyOptions.DestinationPath)

if copyOptions.MirrorReleases {
if err := destinationGitlabInstance.DryRunReleases(sourceGitlabInstance, sourceProject, copyOptions); err != nil {
zap.L().Error("Failed to dry run releases", zap.Error(err))
return []error{err}
}
}
}

}
zap.L().Info("Dry run completed")
return nil
}

// DryRunReleases prints the releases that would be created in dry run mode.
// It fetches the releases from the source project and prints them.
// It does not create any releases in the destination project.
func (destinationGitlabInstance *GitlabInstance) DryRunReleases(sourceGitlabInstance *GitlabInstance, sourceProject *gitlab.Project, copyOptions *utils.MirroringOptions) error {
// Fetch releases from the source project
sourceReleases, _, err := sourceGitlabInstance.Gitlab.Releases.ListReleases(sourceProject.ID, &gitlab.ListReleasesOptions{})
if err != nil {
return fmt.Errorf("failed to fetch releases for source project %s: %s", sourceProject.HTTPURLToRepo, err)
}
// Print the releases that will be created in the destination project
for _, release := range sourceReleases {
fmt.Printf(" - Release %s will be created in %s (if it does not already exist)\n", release.Name, destinationGitlabInstance.Gitlab.BaseURL().String()+copyOptions.DestinationPath)
}
return nil
}

func (destinationGitlab *GitlabInstance) CheckDestinationInstance() error {
// CheckDestinationInstance checks the destination GitLab instance for version and license compatibility.
func (g *GitlabInstance) CheckDestinationInstance() error {
zap.L().Info("Checking destination GitLab instance")
if err := destinationGitlab.CheckVersion(); err != nil {
if err := g.CheckVersion(); err != nil {
return fmt.Errorf("destination GitLab instance version check failed: %w", err)
}
if err := destinationGitlab.CheckVersion(); err != nil {
if err := g.CheckLicense(); err != nil {
return fmt.Errorf("destination GitLab instance version check failed: %w", err)
}
return nil
Expand Down
114 changes: 114 additions & 0 deletions internal/mirroring/main_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mirroring

import (
"net/http"
"reflect"
"testing"

Expand Down Expand Up @@ -130,3 +131,116 @@ func TestProcessFilters(t *testing.T) {
})
}
}

func TestDryRun(t *testing.T) {
tests := []struct {
name string
sourceSize string
}{
{
name: "Dry Run Source Small",
sourceSize: INSTANCE_SIZE_SMALL,
},
{
name: "Dry Run Source Big",
sourceSize: INSTANCE_SIZE_BIG,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
_, sourceGitlabInstance := setupTestServer(t, ROLE_SOURCE, tt.sourceSize)
_, destinationGitlabInstance := setupTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
gitlabMirrorArgs := &utils.MirrorMapping{
Projects: map[string]*utils.MirroringOptions{
TEST_PROJECT.PathWithNamespace: {
DestinationPath: TEST_PROJECT.PathWithNamespace,
MirrorReleases: true,
},
},
Groups: map[string]*utils.MirroringOptions{
TEST_GROUP_2.FullPath: {
DestinationPath: TEST_GROUP_2.FullPath,
MirrorReleases: true,
},
},
}
sourceGitlabInstance.addProject(TEST_PROJECT)
sourceGitlabInstance.addGroup(TEST_GROUP_2)
sourceGitlabInstance.addGroup(TEST_GROUP_2)

destinationGitlabInstance.DryRun(sourceGitlabInstance, gitlabMirrorArgs)
})
}
}

func TestCheckDestinationInstance(t *testing.T) {
tests := []struct {
name string
licensePlan string
version string
expectedError bool
}{
{
name: "Premium license, good version",
licensePlan: PREMIUM_PLAN,
version: "18.0.0",
expectedError: false,
},
{
name: "Ultimate license, good version",
licensePlan: ULTIMATE_PLAN,
version: "18.0.0",
expectedError: false,
},
{
name: "Free license, good version",
licensePlan: "free",
version: "18.0.0",
expectedError: true,
},
{
name: "Premium license, bad version",
licensePlan: PREMIUM_PLAN,
version: "17.0.0",
expectedError: true,
},
{
name: "Ultimate license, bad version",
licensePlan: ULTIMATE_PLAN,
version: "17.0.0",
expectedError: true,
},
{
name: "Bad license, good version",
licensePlan: "bad_license",
version: "18.0.0",
expectedError: true,
},
{
name: "Bad license, bad version",
licensePlan: "bad_license",
version: "17.0.0",
expectedError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
mux, gitlabInstance := setupEmptyTestServer(t, ROLE_DESTINATION, INSTANCE_SIZE_SMALL)
mux.HandleFunc("/api/v4/license", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte(`{"plan": "` + tt.licensePlan + `", "expired": false}`))
})
mux.HandleFunc("/api/v4/metadata", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte(`{"version": "` + tt.version + `"}`))
})

err := gitlabInstance.CheckDestinationInstance()
if (err != nil) != tt.expectedError {
t.Errorf("CheckDestinationInstance() error = %v, expectedError %v", err, tt.expectedError)
}
})
}
}
Loading