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