Skip to content

Added GitService::PushChanges and integrated with handler #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 24, 2024
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
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ SWITCHER_API_JWT_SECRET=[YOUR_JWT_SECRET]
# Only for testing purposes. Values are loaded from accounts
API_DOMAIN_ID=
GIT_TOKEN=
GIT_TOKEN_READ_ONLY=
GIT_REPO_URL=https://github.com/switcherapi/switcher-gitops-fixture
GIT_BRANCH=main
1 change: 1 addition & 0 deletions .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
SWITCHER_API_JWT_SECRET: ${{ secrets.SWITCHER_API_JWT_SECRET }}
API_DOMAIN_ID: ${{ secrets.API_DOMAIN_ID }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
GIT_TOKEN_READ_ONLY: ${{ secrets.GIT_TOKEN_READ_ONLY }}
GIT_REPO_URL: ${{ secrets.GIT_REPO_URL }}
GIT_BRANCH: ${{ secrets.GIT_BRANCH }}

Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"go.testFlags": ["-v"],
"go.testFlags": ["-v", "clean -testcache"],
}
1 change: 1 addition & 0 deletions src/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func shutdown() {
func canRunIntegratedTests() bool {
return config.GetEnv("GIT_REPO_URL") != "" &&
config.GetEnv("GIT_TOKEN") != "" &&
config.GetEnv("GIT_TOKEN_READ_ONLY") != "" &&
config.GetEnv("GIT_BRANCH") != "" &&
config.GetEnv("API_DOMAIN_ID") != ""
}
Expand Down
82 changes: 57 additions & 25 deletions src/core/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package core
import (
"time"

"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
Expand All @@ -14,6 +15,7 @@ import (

type IGitService interface {
GetRepositoryData(environment string) (*model.RepositoryData, error)
PushChanges(environment string, content string) (string, error)
}

type GitService struct {
Expand Down Expand Up @@ -44,6 +46,51 @@ func (g *GitService) GetRepositoryData(environment string) (*model.RepositoryDat
}, nil
}

func (g *GitService) PushChanges(environment string, content string) (string, error) {
// Create an in-memory file system
fs := memfs.New()

// Get the repository & Clone repository using in-memory storage
r, _ := g.getRepository(fs)

// Write the content to the in-memory file
filePath := model.FilePath + environment + ".json"
file, _ := fs.Create(filePath)
file.Write([]byte(content))
file.Close()

// Get the worktree
w, _ := r.Worktree()

// Add the file to the worktree
w.Add(filePath)

// Commit the changes
commit, _ := w.Commit("[switcher-gitops] updated "+environment+".json", g.createCommitOptions())

// Push the changes
err := r.Push(&git.PushOptions{
Force: true,
Auth: g.getAuth(),
})

if err != nil {
return "", err
}

return commit.String(), nil
}

func (g *GitService) createCommitOptions() *git.CommitOptions {
return &git.CommitOptions{
Author: &object.Signature{
Name: "Switcher GitOps",
Email: "switcher.gitops.no-reply@switcherapi.com",
When: time.Now(),
},
}
}

func (g *GitService) getLastCommitData(filePath string) (string, time.Time, string, error) {
c, err := g.getCommitObject()

Expand All @@ -69,7 +116,7 @@ func (g *GitService) getLastCommitData(filePath string) (string, time.Time, stri
}

func (g *GitService) getCommitObject() (*object.Commit, error) {
r, err := g.getRepository()
r, err := g.getRepository(memfs.New())

if err != nil {
return nil, err
Expand All @@ -82,32 +129,17 @@ func (g *GitService) getCommitObject() (*object.Commit, error) {
return r.CommitObject(ref.Hash())
}

func (g *GitService) getRepository() (*git.Repository, error) {
// Clone repository using in-memory storage
r, _ := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
URL: g.RepoURL,
Auth: &http.BasicAuth{
Username: "git-user",
Password: g.Token,
},
func (g *GitService) getRepository(fs billy.Filesystem) (*git.Repository, error) {
return git.Clone(memory.NewStorage(), fs, &git.CloneOptions{
URL: g.RepoURL,
ReferenceName: plumbing.NewBranchReferenceName(g.BranchName),
Auth: g.getAuth(),
})

// Checkout branch
return g.checkoutBranch(*r)
}

func (g *GitService) checkoutBranch(r git.Repository) (*git.Repository, error) {
// Fetch worktree
w, err := r.Worktree()

if err != nil {
return nil, err
func (g *GitService) getAuth() *http.BasicAuth {
return &http.BasicAuth{
Username: "git-user",
Password: g.Token,
}

// Checkout remote branch
err = w.Checkout(&git.CheckoutOptions{
Branch: plumbing.NewRemoteReferenceName("origin", g.BranchName),
})

return &r, err
}
117 changes: 107 additions & 10 deletions src/core/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package core

import (
"testing"

"time"

"github.com/go-git/go-billy/v5/memfs"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/stretchr/testify/assert"
"github.com/switcherapi/switcher-gitops/src/config"
appConfig "github.com/switcherapi/switcher-gitops/src/config"
)

const (
Expand Down Expand Up @@ -33,9 +40,9 @@ func TestGetRepositoryData(t *testing.T) {

// Given
gitService := NewGitService(
config.GetEnv("GIT_REPO_URL"),
config.GetEnv("GIT_TOKEN"),
config.GetEnv("GIT_BRANCH"))
appConfig.GetEnv("GIT_REPO_URL"),
appConfig.GetEnv("GIT_TOKEN"),
appConfig.GetEnv("GIT_BRANCH"))

// Test
repositoryData, err := gitService.GetRepositoryData("default")
Expand All @@ -54,9 +61,9 @@ func TestGetRepositoryDataErrorInvalidEnvironment(t *testing.T) {

// Given
gitService := NewGitService(
config.GetEnv("GIT_REPO_URL"),
config.GetEnv("GIT_TOKEN"),
config.GetEnv("GIT_BRANCH"))
appConfig.GetEnv("GIT_REPO_URL"),
appConfig.GetEnv("GIT_TOKEN"),
appConfig.GetEnv("GIT_BRANCH"))

// Test
repositoryData, err := gitService.GetRepositoryData("invalid")
Expand All @@ -73,9 +80,9 @@ func TestGetRepositoryDataErrorInvalidToken(t *testing.T) {

// Given
gitService := NewGitService(
config.GetEnv("GIT_REPO_URL"),
appConfig.GetEnv("GIT_REPO_URL"),
"invalid",
config.GetEnv("GIT_BRANCH"))
appConfig.GetEnv("GIT_BRANCH"))

// Test
repositoryData, err := gitService.GetRepositoryData("default")
Expand All @@ -84,3 +91,93 @@ func TestGetRepositoryDataErrorInvalidToken(t *testing.T) {
assert.NotNil(t, err)
assert.Nil(t, repositoryData)
}

func TestPushChanges(t *testing.T) {
if !canRunIntegratedTests() {
t.Skip(SkipMessage)
}

t.Run("Should push changes to repository", func(t *testing.T) {
// Given
branchName := "test-branch-" + time.Now().Format("20060102150405")
createBranch(branchName)
gitService := NewGitService(
appConfig.GetEnv("GIT_REPO_URL"),
appConfig.GetEnv("GIT_TOKEN"),
branchName)

// Test
commitHash, err := gitService.PushChanges("default", "content")

// Assert
assert.Nil(t, err)
assert.NotEmpty(t, commitHash)

data, _ := gitService.GetRepositoryData("default")
assert.Equal(t, commitHash, data.CommitHash)
assert.Equal(t, "content", data.Content)

// Clean up
deleteBranch(branchName)
})

t.Run("Should fail to push changes to repository - no write access", func(t *testing.T) {
// Given
gitService := NewGitService(
appConfig.GetEnv("GIT_REPO_URL"),
appConfig.GetEnv("GIT_TOKEN_READ_ONLY"),
appConfig.GetEnv("GIT_BRANCH"))

// Test
commitHash, err := gitService.PushChanges("default", "content")

// Assert
assert.NotNil(t, err)
assert.Equal(t, "authorization failed", err.Error())
assert.Empty(t, commitHash)
})
}

// Helpers

func createBranch(branchName string) {
repo, _ := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
URL: appConfig.GetEnv("GIT_REPO_URL"),
Auth: getAuth(),
})

wt, _ := repo.Worktree()

head, _ := repo.Head()

wt.Checkout(&git.CheckoutOptions{
Branch: plumbing.NewBranchReferenceName(branchName),
Hash: head.Hash(),
Create: true,
})

repo.Push(&git.PushOptions{
RemoteName: "origin",
Auth: getAuth(),
RefSpecs: []config.RefSpec{config.RefSpec("refs/heads/" + branchName + ":refs/heads/" + branchName)},
})
}

func deleteBranch(branchName string) {
r, _ := git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
URL: appConfig.GetEnv("GIT_REPO_URL"),
Auth: getAuth(),
})

r.Push(&git.PushOptions{
Auth: getAuth(),
RefSpecs: []config.RefSpec{config.RefSpec(":refs/heads/" + branchName)},
})
}

func getAuth() *http.BasicAuth {
return &http.BasicAuth{
Username: "git-user",
Password: appConfig.GetEnv("GIT_TOKEN"),
}
}
16 changes: 10 additions & 6 deletions src/core/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/switcherapi/switcher-gitops/src/model"
"github.com/switcherapi/switcher-gitops/src/repository"
"github.com/switcherapi/switcher-gitops/src/utils"
)

type CoreHandler struct {
Expand Down Expand Up @@ -81,7 +82,7 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi
}

if snapshotApi.Domain.Version > account.Domain.Version {
account = c.applyChangesToRepository(account, snapshotApi.Domain)
account = c.applyChangesToRepository(account, snapshotApi)
} else if len(diff.Changes) > 0 {
account = c.applyChangesToAPI(account, repositoryData)
}
Expand Down Expand Up @@ -125,13 +126,16 @@ func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *m
return account
}

func (c *CoreHandler) applyChangesToRepository(account model.Account, domain model.Domain) model.Account {
// Push changes to repository
println("Pushing changes to repository")
func (c *CoreHandler) applyChangesToRepository(account model.Account, snapshot model.Snapshot) model.Account {
// Remove version from domain
snapshotContent := snapshot
snapshotContent.Domain.Version = ""

lastCommit, _ := c.GitService.PushChanges(account.Environment, utils.ToJsonFromObject(snapshotContent))

// Update domain
account.Domain.Version = domain.Version
account.Domain.LastCommit = "111"
account.Domain.Version = snapshot.Domain.Version
account.Domain.LastCommit = lastCommit

return account
}
Expand Down
8 changes: 4 additions & 4 deletions src/core/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ func TestStartAccountHandler(t *testing.T) {
t.Run("Should sync successfully when API has a newer version", func(t *testing.T) {
// Given
fakeGitService := NewFakeGitService()
fakeGitService.lastCommit = "111"

fakeApiService := NewFakeApiService(false)
coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeGitService, fakeApiService, NewComparatorService())

Expand Down Expand Up @@ -172,7 +174,6 @@ type FakeGitService struct {
date string
content string
status string
message string
}

func NewFakeGitService() *FakeGitService {
Expand Down Expand Up @@ -209,9 +210,8 @@ func (f *FakeGitService) GetRepositoryData(environment string) (*model.Repositor
}, nil
}

func (f *FakeGitService) CheckForChanges(account model.Account, lastCommit string,
date string, content string) (status string, message string) {
return f.status, f.message
func (f *FakeGitService) PushChanges(environment string, content string) (string, error) {
return f.lastCommit, nil
}

type FakeApiService struct {
Expand Down