Skip to content

Commit 0799677

Browse files
author
Triona Doyle
committed
merge remote 9682 into 9685 and retain updated CI scripts and README
Signed-off-by: Triona Doyle <tekton@example.com>
2 parents 146cd63 + d29895d commit 0799677

7 files changed

Lines changed: 114 additions & 24 deletions

File tree

cmd/main.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ func main() {
223223
registerComponentOrExit(mgr, argov1beta1api.AddToScheme)
224224

225225
// Setup Scheme for OpenShift Config if available
226+
// Disables default Argo CD instance if the cluster doesn't contain OpenShift config API
226227
if util.IsConfigAPIFound() {
227228
registerComponentOrExit(mgr, configv1.AddToScheme)
228229
}
@@ -254,13 +255,17 @@ func main() {
254255
}
255256
}
256257

257-
if err = (&controllers.ReconcileGitopsService{
258-
Client: client,
259-
Scheme: mgr.GetScheme(),
260-
DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true",
261-
}).SetupWithManager(mgr); err != nil {
262-
setupLog.Error(err, "unable to create controller", "controller", "GitopsService")
263-
os.Exit(1)
258+
if util.IsOpenShiftCluster() {
259+
if err = (&controllers.ReconcileGitopsService{
260+
Client: client,
261+
Scheme: mgr.GetScheme(),
262+
DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true",
263+
}).SetupWithManager(mgr); err != nil {
264+
setupLog.Error(err, "unable to create controller", "controller", "GitopsService")
265+
os.Exit(1)
266+
}
267+
} else {
268+
setupLog.Info("skipping GitopsService controller setup", "reason", "OpenShift Config API not available")
264269
}
265270

266271
if util.IsRouteAPIFound() {

controllers/argocd/argocd.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ import (
2121

2222
argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1"
2323
argoappController "github.com/argoproj-labs/argocd-operator/controllers/argocd"
24+
"github.com/redhat-developer/gitops-operator/controllers/util"
2425
v1 "k8s.io/api/core/v1"
2526
resourcev1 "k8s.io/apimachinery/pkg/api/resource"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"sigs.k8s.io/controller-runtime/pkg/client"
29+
logf "sigs.k8s.io/controller-runtime/pkg/log"
2830
"sigs.k8s.io/yaml"
2931
)
3032

33+
var log = logf.Log.WithName("controller_argocd")
34+
3135
var (
3236
defaultAdminPolicy = "g, system:cluster-admins, role:admin\ng, cluster-admins, role:admin\n"
3337
defaultScope = "[groups]"
@@ -90,7 +94,12 @@ func getArgoDexSpec() *argoapp.ArgoCDDexSpec {
9094
}
9195

9296
func getArgoSSOSpec(client client.Client) *argoapp.ArgoCDSSOSpec {
93-
if argoappController.IsOpenShiftCluster() && argoappController.IsExternalAuthenticationEnabledOnCluster(context.TODO(), client) {
97+
if !util.IsOpenShiftCluster() {
98+
log.Info("non-OpenShift cluster detected, skipping SSO/Dex configuration")
99+
return nil
100+
}
101+
if argoappController.IsExternalAuthenticationEnabledOnCluster(context.TODO(), client) {
102+
log.Info("external authentication enabled on cluster, skipping SSO/Dex configuration")
94103
return nil
95104
}
96105
return &argoapp.ArgoCDSSOSpec{

controllers/argocd/argocd_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
argoapp "github.com/argoproj-labs/argocd-operator/api/v1beta1"
2424
configv1 "github.com/openshift/api/config/v1"
25+
"github.com/redhat-developer/gitops-operator/controllers/util"
2526
"gotest.tools/assert"
2627
v1 "k8s.io/api/core/v1"
2728
resourcev1 "k8s.io/apimachinery/pkg/api/resource"
@@ -30,6 +31,9 @@ import (
3031
)
3132

3233
func TestArgoCD(t *testing.T) {
34+
util.SetConfigAPIFound(true)
35+
defer util.SetConfigAPIFound(false)
36+
3337
scheme := runtime.NewScheme()
3438
_ = argoapp.AddToScheme(scheme)
3539
_ = configv1.AddToScheme(scheme)
@@ -199,6 +203,9 @@ func TestArgoCD(t *testing.T) {
199203
}
200204

201205
func TestDexConfiguration(t *testing.T) {
206+
util.SetConfigAPIFound(true)
207+
defer util.SetConfigAPIFound(false)
208+
202209
scheme := runtime.NewScheme()
203210
_ = argoapp.AddToScheme(scheme)
204211
_ = configv1.AddToScheme(scheme)
@@ -223,3 +230,20 @@ func TestDexConfiguration(t *testing.T) {
223230
}
224231
assert.DeepEqual(t, testArgoCD.Spec.RBAC, testRBAC)
225232
}
233+
234+
// kubernetes environment test, no defer required as the Config API is false by default
235+
func TestSSOSkippedOnNonOpenShift(t *testing.T) {
236+
util.SetConfigAPIFound(false)
237+
238+
scheme := runtime.NewScheme()
239+
_ = argoapp.AddToScheme(scheme)
240+
_ = configv1.AddToScheme(scheme)
241+
242+
fakeClient := fake.NewClientBuilder().
243+
WithScheme(scheme).
244+
Build()
245+
246+
testArgoCD, _ := NewCR("openshift-gitops", "openshift-gitops", fakeClient)
247+
248+
assert.Assert(t, testArgoCD.Spec.SSO == nil, "SSO should be nil on non-OpenShift clusters")
249+
}

controllers/util/util.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,16 @@ func InspectCluster() error {
116116
return stderrors.Join(errs...)
117117
}
118118

119-
// used as a shortcut to check if the cluster is an OpenShift cluster
119+
// IsConfigAPIFound return true if the CRD config.openshift.io is available in the cluster and false otherwise.
120120
func IsConfigAPIFound() bool {
121121
return configAPIFound
122122
}
123123

124+
// IsOpenShiftCluster uses IsConfigAPIFound to check if the cluster is an OpenShift cluster.
125+
func IsOpenShiftCluster() bool {
126+
return IsConfigAPIFound()
127+
}
128+
124129
// verify if the Config.Openshift.io API is found
125130
func verifyConfigAPI() error {
126131
found, err := argoutil.VerifyAPI(configv1.GroupName, configv1.GroupVersion.Version)
@@ -131,6 +136,7 @@ func verifyConfigAPI() error {
131136
return nil
132137
}
133138

139+
// IsConsoleAPIFound return true if the CRD console.openshift.io is available in the cluster.
134140
func IsConsoleAPIFound() bool {
135141
return consoleAPIFound
136142
}
@@ -144,6 +150,7 @@ func verifyConsoleAPI() error {
144150
return nil
145151
}
146152

153+
// IsRouteAPIFound return true if the CRD route.openshift.io is available in the cluster.
147154
func IsRouteAPIFound() bool {
148155
return routeAPIFound
149156
}
@@ -169,10 +176,12 @@ func verifyMonitoringAPI() error {
169176
return nil
170177
}
171178

179+
// IsMonitoringAPIFound return true if the CRD monitoring.coreos.com is available in the cluster.
172180
func IsMonitoringAPIFound() bool {
173181
return monitoringAPIFound
174182
}
175183

184+
// IsTemplateAPIFound return true if the CRD template.openshift.io is available in the cluster.
176185
func IsTemplateAPIFound() bool {
177186
return templateAPIFound
178187
}
@@ -186,6 +195,7 @@ func verifyTemplateAPI() error {
186195
return nil
187196
}
188197

198+
// IsAppsAPIFound return true if the CRD apps.openshift.io is available in the cluster.
189199
func IsAppsAPIFound() bool {
190200
return appsAPIFound
191201
}
@@ -199,6 +209,7 @@ func verifyAppsAPI() error {
199209
return nil
200210
}
201211

212+
// IsOAuthAPIFound return true if the CRD oauth.openshift.io is available in the cluster.
202213
func IsOAuthAPIFound() bool {
203214
return oauthAPIFound
204215
}
@@ -212,6 +223,7 @@ func verifyOAuthAPI() error {
212223
return nil
213224
}
214225

226+
// IsOLMAPIFound return true if the CRD operators.coreos.com is available in the cluster.
215227
func IsOLMAPIFound() bool {
216228
return olmAPIFound
217229
}

test/ui-e2e/.auth/setup.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,20 @@ setup('authenticate to OpenShift Cluster', async ({ page, baseURL }) => {
5555
await passwordInput.fill(process.env.CLUSTER_PASSWORD);
5656
await page.getByRole('button', { name: /Log in/i }).click();
5757

58+
// Handle the OpenShift 4.x Welcome Tour modal if it appears
59+
try {
60+
const skipTourButton = page.getByRole('button', { name: /skip tour/i });
61+
// Wait up to 5 seconds for the modal to pop up
62+
await skipTourButton.waitFor({ state: 'visible', timeout: 5000 });
63+
await skipTourButton.click();
64+
console.log('Dismissed the OpenShift Welcome Tour modal.');
65+
} catch (error) {
66+
// If it doesn't appear within 5 seconds, it's an older cluster or already dismissed.
67+
// Safely ignore the error and move on
68+
}
69+
5870
// Save the auth state
59-
await expect(page.getByRole('navigation').first()).toBeVisible({ timeout: 15000 });
71+
await expect(page.getByRole('navigation').first()).toBeVisible({ timeout: 20000 });
6072
await expect(page).toHaveURL(/(console|k8s|overview|dashboards)/i, { timeout: 15000 });
6173
await page.context().storageState({ path: authFile });
62-
6374
});

test/ui-e2e/src/pages/ApplicationDetailsPage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export class ApplicationDetailsPage {
66
readonly slideOutPanel: Locator;
77
readonly logsTab: Locator;
88

9-
constructor(page: Page) {
9+
constructor(page: Page) {
1010
this.page = page;
1111

1212
//main container
@@ -45,7 +45,7 @@ constructor(page: Page) {
4545
}).toPass({ timeout: 10000 });
4646
}
4747

48-
async verifyPodLogs(expectedLogText?: string) {
48+
async verifyPodLogs(expectedLogText?: string) {
4949
//click Logs
5050
await this.logsTab.waitFor({ state: 'visible', timeout: 5000 });
5151
await this.logsTab.click();

test/ui-e2e/src/pages/ApplicationsPage.ts

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,19 @@ export class ApplicationsPage {
6363
await locator.press('Enter');
6464
}
6565

66-
async createApp(appName: string, repoUrl: string, repoPath: string) {
66+
async createApp(appName: string, repoUrl: string, repoPath: string) {
6767
await this.newAppButton.click();
68+
69+
//handle the "failed to load data" banner if it appears inside the slide-out panel
70+
const errorBanner = this.page.getByText('try again');
71+
try {
72+
//wait 3 secs
73+
await errorBanner.waitFor({ state: 'visible', timeout: 3000 });
74+
await errorBanner.click();
75+
} catch (error) {
76+
//banner didn't appear so just continue
77+
}
78+
6879
await this.page.getByText('Loading...').first().waitFor({ state: 'hidden', timeout: 15000 });
6980

7081
await this.appNameInput.fill(appName);
@@ -82,31 +93,33 @@ export class ApplicationsPage {
8293
await this.createButton.click();
8394
}
8495

85-
async syncApplication(appName: string, expectedResource: string = 'spring-petclinic') {
96+
async syncApplication(appName: string, expectedResource: string = 'spring-petclinic') {
8697
//search for app
8798
await this.page.getByPlaceholder(/Search applications/i).fill(appName);
8899

89100
const appContainer = this.page.locator('.white-box, .argo-table-list__row').filter({ hasText: appName });
90101
await appContainer.waitFor({ state: 'visible', timeout: 20000 });
102+
await expect(appContainer.getByText(/OutOfSync|Out of Sync/i).first()).toBeVisible({ timeout: 45000 });
103+
//safe to open the panel
91104
await appContainer.getByText('Sync', { exact: true }).click();
92105

93-
//slideout panel
94-
// Wait for the manifests to fetch from Git and render on the panel
95-
await expect(this.page.getByText(expectedResource).first()).toBeVisible({ timeout: 15000 });
96-
97-
//click 'all' to ensure all resource checkboxes are ticked across all Argo CD versions
106+
//click 'all'
98107
const allLink = this.page.getByRole('link', { name: 'all', exact: true });
99108
try {
100-
await allLink.waitFor({ state: 'visible', timeout: 3000 });
109+
await allLink.waitFor({ state: 'visible', timeout: 5000 });
101110
await allLink.click();
102111
} catch (error) {
103-
//all link didn't appear within 3 sec
112+
// all link didn't appear within 5 sec
104113
}
114+
115+
//wait for the manifests to render on the panel
116+
await expect(this.page.getByText(expectedResource).first()).toBeVisible({ timeout: 30000 });
117+
105118
//click the main sync button
106119
await this.page.getByRole('button', { name: /^synchronize$/i }).first().click();
107120

108-
//wait for the panel to close
109-
await expect(this.page.getByText('SYNCHRONIZE RESOURCES')).toBeHidden({ timeout: 10000 });
121+
//wait for the panel to close
122+
await expect(this.page.getByText('SYNCHRONIZE RESOURCES')).toBeHidden({ timeout: 15000 });
110123
}
111124

112125
async verifyStatus(appName: string) {
@@ -118,4 +131,20 @@ async syncApplication(appName: string, expectedResource: string = 'spring-petcli
118131
await expect(appContainer.getByText(/synced/i)).toBeVisible({ timeout: 90000 });
119132
await expect(appContainer.getByText(/healthy/i)).toBeVisible({ timeout: 90000 });
120133
}
134+
135+
async openApplication(appName: string) {
136+
//re-apply search filter just in case the UI refreshed
137+
await this.page.getByPlaceholder(/Search applications/i).fill(appName);
138+
139+
//find the container, then specifically click the link of the app name
140+
const appLink = this.page.locator('.white-box, .argo-table-list__row')
141+
.filter({ hasText: appName })
142+
.getByRole('link', { name: appName });
143+
144+
await appLink.waitFor({ state: 'visible', timeout: 15000 });
145+
await appLink.click();
146+
147+
//wait for the URL to change to the details page to ensure the click worked
148+
await expect(this.page).toHaveURL(/.*\/applications\/.*\/.*/, { timeout: 15000 });
149+
}
121150
}

0 commit comments

Comments
 (0)