Skip to content

Commit

Permalink
Merge pull request #142 from kubefirst/update_argocd_calls
Browse files Browse the repository at this point in the history
ArgoCD Sync retry feature
  • Loading branch information
João Paulo Vanzuita committed Jul 21, 2022
2 parents 16f01c0 + b2ab5f1 commit 2b01c13
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ bin
logs/
/tmp
lint_log.txt
credentials
24 changes: 22 additions & 2 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package cmd

import (
"bytes"
"crypto/tls"
"fmt"
"log"
"net/http"
"os/exec"
"syscall"
"time"
Expand Down Expand Up @@ -151,10 +153,28 @@ to quickly create a Cobra application.`,
}
time.Sleep(45 * time.Second)
}
//TODO: ensure argocd is in a good heathy state before syncing the registry application

informUser("Syncing the registry application")
argocd.SyncArgocdApplication(dryRun, "registry", token)

if dryRun {
log.Printf("[#99] Dry-run mode, Sync ArgoCD skipped")
} else {
// todo: create ArgoCD struct, and host dependencies (like http client)
customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
httpClient := http.Client{Transport: customTransport}

// retry to sync ArgoCD application until reaches the maximum attempts
argoCDIsReady, err := argocd.SyncRetry(&httpClient, 10, 6, "registry", token)
if err != nil {
log.Printf("something went wrong during ArgoCD sync step, error is: %v", err)
}

if !argoCDIsReady {
log.Println("unable to sync ArgoCD application, continuing...")
}
}

progressPrinter.IncrementTracker("step-argo", 1)
// todo, need to stall until the registry has synced, then get to ui asap

Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,4 @@ services:
- "8200:8200" # Vault
volumes:
- ./:/home/developer/kubefirst
env_file:
- .env
command: sh -c "./scripts/kubefirst-dev.sh"
104 changes: 78 additions & 26 deletions internal/argocd/argocd.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,90 @@ import (
"time"
)

// kSyncArgocdApplication request ArgoCD to manual sync an application. Expected parameters are the ArgoCD application
// name and ArgoCD token with enough permission to perform the request against Argo API. When the http request returns
// status 200 it means a successful request/true, any other http status response return false.
func kSyncArgocdApplication(applicationName, argocdAuthToken string) (bool, error) {
type ArgoCDConfig struct {
Username string `json:"username"`
Password string `json:"password"`
}

// todo: instantiate a new client on every http request is bad idea, we might need to set a new architecture to avoid
customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
httpClient := http.Client{Transport: customTransport}
type SyncResponse struct {
Status struct {
Sync struct {
Status string `json:"status"`
} `json:"sync"`
}
}

// todo: url values can be stored on .env files, and consumed when necessary to avoid hardcode urls
url := fmt.Sprintf("https://localhost:8081/api/v1/applications/%s/sync", applicationName)
// SyncRetry tries to Sync ArgoCD as many times as requested by the attempts' parameter. On successful request, returns
// true and no error, on error, returns false and the reason it fails.
// Possible values for the ArgoCD status are Unknown and Synced, Unknown means the application has some error, and Synced
// means the application was synced successfully.
func SyncRetry(httpClient pkg.HTTPDoer, attempts int, interval int, applicationName string, token string) (bool, error) {

for i := 0; i < attempts; i++ {

httpCode, syncStatus, err := Sync(httpClient, applicationName, token)
if err != nil {
log.Println(err)
return false, fmt.Errorf("unable to request ArgoCD Sync, error is: %v", err)
}

// success! ArgoCD is synced!
if syncStatus == "Synced" {
log.Println("ArgoCD application is synced")
return true, nil
}

// keep trying
if httpCode == http.StatusBadRequest {
log.Println("another operation is already in progress")
}

log.Printf(
"sleeping %d seconds before trying to ArgoCD sync again, last Sync status is: %q",
interval,
syncStatus,
)
time.Sleep(time.Duration(interval) * time.Second)
}
return false, nil
}

// Sync request ArgoCD to manual sync an application.
func Sync(httpClient pkg.HTTPDoer, applicationName string, argoCDToken string) (httpCodeResponse int, syncStatus string, Error error) {

url := fmt.Sprintf("%s/api/v1/applications/%s/sync", viper.GetString("argocd.local.service"), applicationName)
fmt.Println(url)
req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
log.Println(err)
return false, err
return 0, "", err
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", argocdAuthToken))
resp, err := httpClient.Do(req)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", argoCDToken))
res, err := httpClient.Do(req)
if err != nil {
log.Printf("error sending POST request to ArgoCD for syncing application (%s)\n", applicationName)
return false, err
fmt.Println(err)
return res.StatusCode, "", err
}
defer res.Body.Close()

if resp.StatusCode == http.StatusOK {
return true, nil
if res.StatusCode != http.StatusOK {
log.Printf("ArgoCD Sync response http code is: %d", res.StatusCode)
return res.StatusCode, "", nil
}
return false, nil
}

type ArgoCDConfig struct {
Username string `json:"username"`
Password string `json:"password"`
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return res.StatusCode, "", err
}

var syncResponse SyncResponse
err = json.Unmarshal(body, &syncResponse)
if err != nil {
return res.StatusCode, "", err
}

return res.StatusCode, syncResponse.Status.Sync.Status, nil
}

// getArgoCDToken expects ArgoCD username and password, and returns a ArgoCD Bearer Token.
Expand Down Expand Up @@ -117,6 +168,7 @@ func getArgoCDToken(username string, password string) (string, error) {
return token, nil
}

// todo: replace this functions with getArgoCDToken
func GetArgocdAuthToken(dryRun bool) string {

if dryRun {
Expand Down Expand Up @@ -145,15 +197,15 @@ func GetArgocdAuthToken(dryRun bool) string {

x := 3
for i := 0; i < x; i++ {
log.Print("requesting auth token from argocd: attempt %s of %s", i, x)
log.Printf("requesting auth token from argocd: attempt %d of %d", i+1, x)
time.Sleep(1 * time.Second)
res, err := client.Do(req)

if err != nil {
log.Print("error requesting auth token from argocd", err)
continue
} else {
defer res.Body.Close()
} else {
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Print("error sending POST request to get argocd auth token:", err)
Expand All @@ -176,7 +228,7 @@ func GetArgocdAuthToken(dryRun bool) string {
}
log.Panic("Fail to get a token")
// This code is unreacheble, as in absence of token we want to fail the install.
// I kept is to avoid compiler to complain.
// I kept is to avoid compiler to complain.
return ""
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package pkg

import "net/http"

type HTTPDoer interface {
Do(req *http.Request) (*http.Response, error)
}

0 comments on commit 2b01c13

Please sign in to comment.