github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/pkg/argocd.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package pkg 5 6 import ( 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 12 "github.com/hashicorp/go-retryablehttp" 13 "github.com/onsi/gomega" 14 "github.com/verrazzano/verrazzano/pkg/constants" 15 "github.com/verrazzano/verrazzano/pkg/httputil" 16 "github.com/verrazzano/verrazzano/pkg/k8s/resource" 17 "github.com/verrazzano/verrazzano/pkg/k8sutil" 18 "go.uber.org/zap" 19 corev1 "k8s.io/api/core/v1" 20 ) 21 22 const ( 23 argoCdHelidonApplicationFile = "tests/e2e/config/scripts/hello-helidon-argocd-application.yaml" 24 ) 25 26 // VerifyArgoCDAccess verifies that Argocd is accessible. 27 func VerifyArgoCDAccess(log *zap.SugaredLogger) error { 28 var err error 29 30 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 31 api := EventuallyGetAPIEndpoint(kubeconfigPath) 32 argocdURL := EventuallyGetURLForIngress(log, api, constants.ArgoCDNamespace, "argocd-server", "https") 33 httpClient := EventuallyVerrazzanoRetryableHTTPClient() 34 var httpResponse *HTTPResponse 35 36 gomega.Eventually(func() (*HTTPResponse, error) { 37 httpResponse, err = GetWebPageWithClient(httpClient, argocdURL, "") 38 return httpResponse, err 39 }, waitTimeout, pollingInterval).Should(HasStatus(http.StatusOK)) 40 41 gomega.Expect(CheckNoServerHeader(httpResponse)).To(gomega.BeTrue(), "Found unexpected server header in response") 42 return nil 43 } 44 45 func VerifyArgoCDApplicationAccess(log *zap.SugaredLogger) error { 46 var err error 47 48 kubeConfigPath, err := k8sutil.GetKubeConfigLocation() 49 if err != nil { 50 return err 51 } 52 argocdAdminPassword, err := eventuallyGetArgocdAdminPassword(log) 53 if err != nil { 54 return err 55 } 56 httpClient, err := GetVerrazzanoHTTPClient(kubeConfigPath) 57 if err != nil { 58 log.Error(fmt.Sprintf("Error getting argocd admin password: %v", err)) 59 return err 60 } 61 62 api := EventuallyGetAPIEndpoint(kubeConfigPath) 63 argocdURL := EventuallyGetURLForIngress(log, api, constants.ArgoCDNamespace, "argocd-server", "https") 64 65 token, err := getArgoCDUserToken(log, argocdURL, "admin", string(argocdAdminPassword), httpClient) 66 if err != nil { 67 log.Error(fmt.Sprintf("Error getting user token from Argocd: %v", err)) 68 return err 69 } 70 var emptyList bool 71 gomega.Eventually(func() (bool, error) { 72 contains, err := GetApplicationsWithClient(log, argocdURL, token) 73 emptyList = contains 74 return emptyList, err 75 }, waitTimeout, pollingInterval).Should(gomega.BeTrue()) 76 77 gomega.Expect(emptyList).To(gomega.BeTrue(), "Argocd UI is accessible and no applications are deployed") 78 return nil 79 } 80 81 func eventuallyGetArgocdAdminPassword(log *zap.SugaredLogger) (string, error) { 82 var err error 83 var secret *corev1.Secret 84 gomega.Eventually(func() error { 85 secret, err = GetSecret(constants.ArgoCDNamespace, "argocd-initial-admin-secret") 86 if err != nil { 87 log.Error(fmt.Sprintf("Error getting argocd-initial-admin-secret, retrying: %v", err)) 88 } 89 return err 90 }, waitTimeout, pollingInterval).Should(gomega.BeNil()) 91 92 if secret == nil { 93 return "", fmt.Errorf("Unable to get argocd admin secret") 94 } 95 96 var argocdAdminPassword []byte 97 var ok bool 98 if argocdAdminPassword, ok = secret.Data["password"]; !ok { 99 return "", fmt.Errorf("Error getting argocd admin credentials") 100 } 101 102 return string(argocdAdminPassword), nil 103 } 104 105 func getArgoCDUserToken(log *zap.SugaredLogger, argoCDURL string, username string, password string, httpClient *retryablehttp.Client) (string, error) { 106 argoCDLoginURL := fmt.Sprintf("%s/%s", argoCDURL, "api/v1/session") 107 payload := `{"Username": "` + username + `", "Password": "` + password + `"}` 108 response, err := httpClient.Post(argoCDLoginURL, "application/json", strings.NewReader(payload)) 109 if err != nil { 110 log.Error(fmt.Sprintf("Error getting argocd admin token: %v", err)) 111 return "", err 112 } 113 114 err = httputil.ValidateResponseCode(response, http.StatusOK) 115 if err != nil { 116 log.Errorf("Invalid response code when fetching argocd token: %v", err) 117 return "", err 118 } 119 120 defer response.Body.Close() 121 122 // extract the response body 123 body, err := io.ReadAll(response.Body) 124 if err != nil { 125 log.Errorf("Failed to read argocd token response: %v", err) 126 return "", err 127 } 128 129 token, err := httputil.ExtractFieldFromResponseBodyOrReturnError(string(body), "token", "unable to find token in Argocd response") 130 if err != nil { 131 log.Errorf("Failed to extra token from argocd response: %v", err) 132 return "", err 133 } 134 135 return token, nil 136 } 137 138 // GetApplicationsWithClient returns true if the user is able to access the applications page post Argo CD install 139 func GetApplicationsWithClient(log *zap.SugaredLogger, argoCDURL string, token string) (bool, error) { 140 kubeConfigPath, err := k8sutil.GetKubeConfigLocation() 141 if err != nil { 142 return false, err 143 } 144 145 httpClient, err := GetVerrazzanoHTTPClient(kubeConfigPath) 146 if err != nil { 147 log.Error(fmt.Sprintf("Error getting argocd admin password: %v", err)) 148 return false, err 149 } 150 151 argoCDLoginURL := fmt.Sprintf("%s/%s", argoCDURL, "api/v1/applications") 152 req, err := retryablehttp.NewRequest("GET", argoCDLoginURL, nil) 153 if err != nil { 154 log.Error("Unexpected error while creating new request=%v", err) 155 return false, err 156 } 157 var bearer = "Bearer " + token 158 159 req.Header.Add("Authorization", bearer) 160 response, err := httpClient.Do(req) 161 if err != nil { 162 return false, err 163 } 164 165 err = httputil.ValidateResponseCode(response, http.StatusOK) 166 if err != nil { 167 log.Errorf("Invalid response code when fetching argocd token: %v", err) 168 return false, err 169 } 170 171 defer response.Body.Close() 172 173 // extract the response body 174 body, err := io.ReadAll(response.Body) 175 if err != nil { 176 log.Errorf("Failed to read argocd response: %v", err) 177 return false, err 178 } 179 180 token, err = httputil.ExtractFieldFromResponseBodyOrReturnError(string(body), "metadata", "unable to find metadata in Argocd response") 181 if err != nil { 182 log.Errorf("Failed to extract token from argocd response: %v", err) 183 return false, err 184 } 185 186 exists := strings.Contains(token, "resourceVersion") 187 return exists, nil 188 189 } 190 191 // CreateArgoCDGitApplication creates an application in Argo CD by connecting to the Git repo 192 // Applies the Argo CD Application to the kubernetes cluster 193 func CreateArgoCDGitApplication() error { 194 Log(Info, "Create Argo CD Application Project") 195 gomega.Eventually(func() error { 196 file, err := FindTestDataFile(argoCdHelidonApplicationFile) 197 if err != nil { 198 return err 199 } 200 return resource.CreateOrUpdateResourceFromFileInGeneratedNamespace(file, "argocd") 201 }, helidonWaitTimeout, helidonPollingInterval).ShouldNot(gomega.HaveOccurred(), "Failed to create Argo CD Application Project file") 202 203 return nil 204 } 205 206 // This function retrieves the ArgoCD password to log into rancher, based on the provided name and namespace of a secret that holds this information 207 func RetrieveArgoCDPassword(namespace, name string) (string, error) { 208 s, err := GetSecret(namespace, name) 209 if err != nil { 210 Log(Error, fmt.Sprintf("Failed to get secret %s in namespace %s with error: %v", name, namespace, err)) 211 return "", err 212 } 213 argoCDPasswordForSecret, ok := s.Data["password"] 214 if !ok { 215 Log(Error, fmt.Sprintf("Failed to find password value in ArgoCD secret %s in namespace %s", name, namespace)) 216 return "", fmt.Errorf("Failed to find password value in ArgoCD secret %s in namespace %s", name, namespace) 217 } 218 return string(argoCDPasswordForSecret), nil 219 }