Skip to content

Added FecthAllByDomainId, fixed handler sync check #37

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
Sep 15, 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
14 changes: 14 additions & 0 deletions src/controller/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewAccountController(repo repository.AccountRepository, coreHandler *core.C
func (controller *AccountController) RegisterRoutes(r *mux.Router) http.Handler {
r.NewRoute().Path(controller.RouteAccountPath).Name("CreateAccount").HandlerFunc(controller.CreateAccountHandler).Methods(http.MethodPost)
r.NewRoute().Path(controller.RouteAccountPath).Name("UpdateAccount").HandlerFunc(controller.UpdateAccountHandler).Methods(http.MethodPut)
r.NewRoute().Path(controller.RouteAccountPath + "/{domainId}").Name("GelAllAccountsByDomainId").HandlerFunc(controller.FetchAllAccountsByDomainIdHandler).Methods(http.MethodGet)
r.NewRoute().Path(controller.RouteAccountPath + "/{domainId}/{enviroment}").Name("GetAccount").HandlerFunc(controller.FetchAccountHandler).Methods(http.MethodGet)
r.NewRoute().Path(controller.RouteAccountPath + "/{domainId}/{enviroment}").Name("DeleteAccount").HandlerFunc(controller.DeleteAccountHandler).Methods(http.MethodDelete)

Expand Down Expand Up @@ -80,6 +81,19 @@ func (controller *AccountController) FetchAccountHandler(w http.ResponseWriter,
utils.ResponseJSON(w, account, http.StatusOK)
}

func (controller *AccountController) FetchAllAccountsByDomainIdHandler(w http.ResponseWriter, r *http.Request) {
domainId := mux.Vars(r)["domainId"]

accounts := controller.AccountRepository.FetchAllByDomainId(domainId)
if accounts == nil {
utils.Log(utils.LogLevelError, "Not found accounts for domain: %s", domainId)
utils.ResponseJSON(w, ErrorResponse{Error: "Not found accounts for domain: " + domainId}, http.StatusNotFound)
return
}

utils.ResponseJSON(w, accounts, http.StatusOK)
}

func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter, r *http.Request) {
var accountRequest model.Account
err := json.NewDecoder(r.Body).Decode(&accountRequest)
Expand Down
40 changes: 38 additions & 2 deletions src/controller/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,53 @@ func TestFetchAccountHandler(t *testing.T) {
assert.Equal(t, http.StatusNotFound, response.Code)
assert.Equal(t, "{\"error\":\"Account not found\"}", response.Body.String())
})

t.Run("Should fetch all accounts by domain ID", func(t *testing.T) {
// Create an account
accountV1.Domain.ID = "123-controller-fetch-all-accounts"
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
accountV1.Environment = "staging"
accountController.CreateAccountHandler(givenAccountRequest(accountV1))

// Test
payload := []byte("")
req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/"+accountV1.Domain.ID, bytes.NewBuffer(payload))
response := executeRequest(req)

// Assert
var accountsResponse []model.Account
err := json.NewDecoder(response.Body).Decode(&accountsResponse)

assert.Equal(t, http.StatusOK, response.Code)
assert.Nil(t, err)
assert.Equal(t, 2, len(accountsResponse))
})

t.Run("Should not fetch all accounts by domain ID - not found", func(t *testing.T) {
// Test
payload := []byte("")
req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/not-found", bytes.NewBuffer(payload))
response := executeRequest(req)

// Assert
assert.Equal(t, http.StatusNotFound, response.Code)
assert.Equal(t, "{\"error\":\"Not found accounts for domain: not-found\"}", response.Body.String())
})
}

func TestUpdateAccountHandler(t *testing.T) {
t.Run("Should update an account", func(t *testing.T) {
// Create an account
accountV1.Domain.ID = "123-controller-update-account"
accountV1.Environment = "default"
accountController.CreateAccountHandler(givenAccountRequest(accountV1))

// Test
accountV2.Domain.ID = accountV1.Domain.ID
// Update the account
accountV2.Domain.ID = "123-controller-update-account"
accountV2.Environment = "default"
accountV2.Domain.Message = "Updated successfully"

// Test
payload, _ := json.Marshal(accountV2)
req, _ := http.NewRequest(http.MethodPut, accountController.RouteAccountPath, bytes.NewBuffer(payload))
response := executeRequest(req)
Expand Down
47 changes: 29 additions & 18 deletions src/core/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func NewCoreHandler(accountRepository repository.AccountRepository, apiService I
}
}

func (c *CoreHandler) InitCoreHandlerCoroutine() (int, error) {
func (c *CoreHandler) InitCoreHandlerGoroutine() (int, error) {
// Check if core handler is already running
if c.Status == CoreHandlerStatusRunning {
return c.Status, nil
Expand All @@ -39,7 +39,7 @@ func (c *CoreHandler) InitCoreHandlerCoroutine() (int, error) {
c.Status = CoreHandlerStatusInit

// Load all accounts
accounts, _ := c.AccountRepository.FetchAllActiveAccounts()
accounts := c.AccountRepository.FetchAllActiveAccounts()

// Iterate over accounts and start account handlers
for _, account := range accounts {
Expand Down Expand Up @@ -70,7 +70,9 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi

// Wait for account to be active
if !account.Settings.Active {
utils.Log(utils.LogLevelInfo, "[%s - %s] Account is not active, waiting for activation", accountId, account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Account is not active, waiting for activation",
accountId, account.Domain.Name, account.Environment)

c.updateDomainStatus(*account, model.StatusPending, "Account was deactivated")
time.Sleep(1 * time.Minute)
continue
Expand All @@ -83,7 +85,9 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi
repositoryData, err := gitService.GetRepositoryData(account.Environment)

if err != nil {
utils.Log(utils.LogLevelError, "[%s - %s] Failed to fetch repository data - %s", accountId, account.Domain.Name, err.Error())
utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to fetch repository data - %s",
accountId, account.Domain.Name, account.Environment, err.Error())

c.updateDomainStatus(*account, model.StatusError, "Failed to fetch repository data - "+err.Error())
time.Sleep(1 * time.Minute)
continue
Expand All @@ -93,7 +97,9 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi
snapshotVersionPayload, err := c.ApiService.FetchSnapshotVersion(account.Domain.ID, account.Environment)

if err != nil {
utils.Log(utils.LogLevelError, "[%s - %s] Failed to fetch snapshot version - %s", accountId, account.Domain.Name, err.Error())
utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to fetch snapshot version - %s",
accountId, account.Domain.Name, account.Environment, err.Error())

c.updateDomainStatus(*account, model.StatusError, "Failed to fetch snapshot version - "+err.Error())
time.Sleep(1 * time.Minute)
continue
Expand All @@ -111,9 +117,11 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi
}

func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.RepositoryData, gitService IGitService) {
utils.Log(utils.LogLevelInfo, "[%s - %s] Syncing up", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Syncing up", account.ID.Hex(), account.Domain.Name, account.Environment)

// Update account status: Out of sync
account.Domain.LastCommit = repositoryData.CommitHash
account.Domain.LastDate = repositoryData.CommitDate
c.updateDomainStatus(account, model.StatusOutSync, model.MessageSyncingUp)

// Check for changes
Expand All @@ -124,17 +132,19 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi
return
}

utils.Log(utils.LogLevelDebug, "[%s - %s] SnapshotAPI version: %s - SnapshotRepo version: %s",
account.ID.Hex(), account.Domain.Name, fmt.Sprint(snapshotApi.Domain.Version), fmt.Sprint(account.Domain.Version))
utils.Log(utils.LogLevelDebug, "[%s - %s (%s)] SnapshotAPI version: %s - SnapshotRepo version: %s",
account.ID.Hex(), account.Domain.Name, account.Environment, fmt.Sprint(snapshotApi.Domain.Version), fmt.Sprint(account.Domain.Version))

// Apply changes
changeSource := ""
if snapshotApi.Domain.Version > account.Domain.Version {
changeSource = "Repository"
if c.isRepositoryOutSync(account, repositoryData, diff) {
if c.isRepositoryOutSync(repositoryData, diff) {
account, err = c.applyChangesToRepository(account, snapshotApi, gitService)
} else {
utils.Log(utils.LogLevelInfo, "[%s - %s] Repository is up to date", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Repository is up to date",
account.ID.Hex(), account.Domain.Name, account.Environment)

account.Domain.Version = snapshotApi.Domain.Version
account.Domain.LastCommit = repositoryData.CommitHash
}
Expand All @@ -144,7 +154,9 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi
}

if err != nil {
utils.Log(utils.LogLevelError, "[%s - %s] Failed to apply changes [%s] - %s", account.ID.Hex(), account.Domain.Name, changeSource, err.Error())
utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to apply changes [%s] - %s",
account.ID.Hex(), account.Domain.Name, account.Environment, changeSource, err.Error())

c.updateDomainStatus(account, model.StatusError, "Failed to apply changes ["+changeSource+"] - "+err.Error())
return
}
Expand Down Expand Up @@ -178,7 +190,7 @@ func (c *CoreHandler) checkForChanges(account model.Account, content string) (mo
}

func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *model.RepositoryData, diff model.DiffResult) model.Account {
utils.Log(utils.LogLevelInfo, "[%s - %s] Pushing changes to API", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Pushing changes to API", account.ID.Hex(), account.Domain.Name, account.Environment)

// Removed deleted if force prune is disabled
if !account.Settings.ForcePrune {
Expand All @@ -195,7 +207,7 @@ func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *m
}

func (c *CoreHandler) applyChangesToRepository(account model.Account, snapshot model.Snapshot, gitService IGitService) (model.Account, error) {
utils.Log(utils.LogLevelInfo, "[%s - %s] Pushing changes to repository", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Pushing changes to repository", account.ID.Hex(), account.Domain.Name, account.Environment)

// Remove version from domain
snapshotContent := snapshot
Expand All @@ -217,17 +229,16 @@ func (c *CoreHandler) applyChangesToRepository(account model.Account, snapshot m
func (c *CoreHandler) isOutSync(account model.Account, lastCommit string, snapshotVersionPayload string) bool {
snapshotVersion := c.ApiService.NewDataFromJson([]byte(snapshotVersionPayload)).Snapshot.Domain.Version

utils.Log(utils.LogLevelDebug, "[%s - %s] Checking account - Last commit: %s - Domain Version: %d - Snapshot Version: %d",
account.ID.Hex(), account.Domain.Name, account.Domain.LastCommit, account.Domain.Version, snapshotVersion)
utils.Log(utils.LogLevelDebug, "[%s - %s (%s)] Checking account - Last commit: %s - Domain Version: %d - Snapshot Version: %d",
account.ID.Hex(), account.Domain.Name, account.Environment, account.Domain.LastCommit, account.Domain.Version, snapshotVersion)

return account.Domain.LastCommit == "" || // First sync
account.Domain.LastCommit != lastCommit || // Repository out of sync
account.Domain.Version != snapshotVersion // API out of sync
}

func (c *CoreHandler) isRepositoryOutSync(account model.Account, repositoryData *model.RepositoryData, diff model.DiffResult) bool {
return account.Domain.Version == 0 || // First/Force-push sync
len(repositoryData.Content) <= 1 || // File is empty
func (c *CoreHandler) isRepositoryOutSync(repositoryData *model.RepositoryData, diff model.DiffResult) bool {
return len(repositoryData.Content) <= 1 || // File is empty
len(diff.Changes) > 0 // Changes detected
}

Expand Down
13 changes: 6 additions & 7 deletions src/core/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/switcherapi/switcher-gitops/src/model"
)

func TestInitCoreHandlerCoroutine(t *testing.T) {
func TestInitCoreHandlerGoroutine(t *testing.T) {
t.Run("Should start account handlers for all active accounts", func(t *testing.T) {
// Given
fakeApiService := NewFakeApiService()
Expand All @@ -23,7 +23,7 @@ func TestInitCoreHandlerCoroutine(t *testing.T) {
accountCreated, _ := coreHandler.AccountRepository.Create(&account)

// Test
status, err := coreHandler.InitCoreHandlerCoroutine()
status, err := coreHandler.InitCoreHandlerGoroutine()

// Terminate the goroutine
coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment)
Expand All @@ -46,8 +46,8 @@ func TestInitCoreHandlerCoroutine(t *testing.T) {
accountCreated, _ := coreHandler.AccountRepository.Create(&account)

// Test
coreHandler.InitCoreHandlerCoroutine()
status, _ := coreHandler.InitCoreHandlerCoroutine()
coreHandler.InitCoreHandlerGoroutine()
status, _ := coreHandler.InitCoreHandlerGoroutine()

// Terminate the goroutine
coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment)
Expand Down Expand Up @@ -211,7 +211,6 @@ func TestStartAccountHandler(t *testing.T) {

account := givenAccount()
account.Domain.ID = "123-up-to-date-not-synced"
account.Domain.Version = -1 // Different from the API version
accountCreated, _ := coreHandler.AccountRepository.Create(&account)

// Test
Expand Down Expand Up @@ -350,7 +349,7 @@ func TestStartAccountHandler(t *testing.T) {
accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment)
assert.Equal(t, model.StatusError, accountFromDb.Domain.Status)
assert.Contains(t, accountFromDb.Domain.Message, "Failed to check for changes")
assert.Equal(t, "", accountFromDb.Domain.LastCommit)
assert.Equal(t, "123", accountFromDb.Domain.LastCommit)
assert.NotEqual(t, "", accountFromDb.Domain.LastDate)

tearDown()
Expand Down Expand Up @@ -379,7 +378,7 @@ func TestStartAccountHandler(t *testing.T) {
assert.Equal(t, model.StatusError, accountFromDb.Domain.Status)
assert.Contains(t, accountFromDb.Domain.Message, "authorization failed")
assert.Contains(t, accountFromDb.Domain.Message, "Failed to apply changes [Repository]")
assert.Equal(t, "", accountFromDb.Domain.LastCommit)
assert.Equal(t, "123", accountFromDb.Domain.LastCommit)
assert.NotEqual(t, "", accountFromDb.Domain.LastDate)

tearDown()
Expand Down
28 changes: 24 additions & 4 deletions src/repository/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ type AccountRepository interface {
Create(account *model.Account) (*model.Account, error)
FetchByAccountId(accountId string) (*model.Account, error)
FetchByDomainIdEnvironment(domainId string, environment string) (*model.Account, error)
FetchAllActiveAccounts() ([]model.Account, error)
FetchAllByDomainId(domainId string) []model.Account
FetchAllActiveAccounts() []model.Account
Update(account *model.Account) (*model.Account, error)
DeleteByAccountId(accountId string) error
DeleteByDomainIdEnvironment(domainId string, environment string) error
Expand Down Expand Up @@ -69,7 +70,7 @@ func (repo *AccountRepositoryMongo) FetchByDomainIdEnvironment(domainId string,
defer cancel()

var account model.Account
filter := primitive.M{domainIdFilter: domainId}
filter := primitive.M{domainIdFilter: domainId, environmentFilter: environment}
err := collection.FindOne(ctx, filter).Decode(&account)
if err != nil {
return nil, err
Expand All @@ -78,7 +79,26 @@ func (repo *AccountRepositoryMongo) FetchByDomainIdEnvironment(domainId string,
return &account, nil
}

func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() ([]model.Account, error) {
func (repo *AccountRepositoryMongo) FetchAllByDomainId(domainId string) []model.Account {
collection, ctx, cancel := getDbContext(repo)
defer cancel()

filter := primitive.M{domainIdFilter: domainId}
cursor, _ := collection.Find(ctx, filter)

var accounts []model.Account
for cursor.Next(ctx) {
var account model.Account
err := cursor.Decode(&account)
if err == nil {
accounts = append(accounts, account)
}
}

return accounts
}

func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() []model.Account {
collection, ctx, cancel := getDbContext(repo)
defer cancel()

Expand All @@ -94,7 +114,7 @@ func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() ([]model.Account, e
}
}

return accounts, nil
return accounts
}

func (repo *AccountRepositoryMongo) Update(account *model.Account) (*model.Account, error) {
Expand Down
25 changes: 23 additions & 2 deletions src/repository/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,33 @@ func TestFetchAccount(t *testing.T) {
accountRepository.Create(&account2)

// Test
accounts, err := accountRepository.FetchAllActiveAccounts()
accounts := accountRepository.FetchAllActiveAccounts()

// Assert
assert.Nil(t, err)
assert.NotNil(t, accounts)
assert.Equal(t, 1, len(accounts))
})

t.Run("Should fetch all accounts by domain ID", func(t *testing.T) {
// Drop collection
mongoDb.Collection("accounts").Drop(context.Background())

// Given
account1 := givenAccount(true)
account1.Domain.ID = "123-fetch-all-accounts-by-domain-id"
account2 := givenAccount(true)
account2.Domain.ID = "123-fetch-all-accounts-by-domain-id"

accountRepository.Create(&account1)
accountRepository.Create(&account2)

// Test
accounts := accountRepository.FetchAllByDomainId(account1.Domain.ID)

// Assert
assert.NotNil(t, accounts)
assert.Equal(t, 2, len(accounts))
})
}

func TestUpdateAccount(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion src/server/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func initCoreHandler(db *mongo.Database) *core.CoreHandler {
)

coreHandler := core.NewCoreHandler(accountRepository, apiService, comparatorService)
coreHandler.InitCoreHandlerCoroutine()
coreHandler.InitCoreHandlerGoroutine()

return coreHandler
}
3 changes: 1 addition & 2 deletions src/utils/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package utils

import (
"encoding/json"
"fmt"
"net/http"
)

Expand All @@ -12,7 +11,7 @@ func ResponseJSON(w http.ResponseWriter, data interface{}, status int) {

encodedData, err := json.Marshal(data)
if err != nil {
fmt.Println("Error encoding JSON:", err)
Log(LogLevelError, "Error encoding JSON: %s", err.Error())
return
}

Expand Down