Skip to content

Commit

Permalink
Merge pull request #12934 from rancher/gke-po
Browse files Browse the repository at this point in the history
e2e test that creates GKE cluster with default settings
  • Loading branch information
IsaSih authored Feb 6, 2025
2 parents 8f2edd7 + 8f83f1e commit 8795891
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 3 deletions.
3 changes: 2 additions & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ export default defineConfig({
azureClientId: process.env.AZURE_CLIENT_ID,
azureClientSecret: process.env.AZURE_CLIENT_SECRET,
customNodeIp: process.env.CUSTOM_NODE_IP,
customNodeKey: process.env.CUSTOM_NODE_KEY
customNodeKey: process.env.CUSTOM_NODE_KEY,
gkeServiceAccount: process.env.GKE_SERVICE_ACCOUNT,
},
e2e: {
fixturesFolder: 'cypress/e2e/blueprints',
Expand Down
12 changes: 12 additions & 0 deletions cypress/e2e/po/components/button-file-selector.po.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ComponentPo from '@/cypress/e2e/po/components/component.po';

export default class ButtonFileSelectorPo extends ComponentPo {
/**
* Returns a file-selector button
* @param id
* @returns
*/
static readFromFileButton(): Cypress.Chainable {
return cy.getId('file-selector__uploader-button');
}
}
18 changes: 18 additions & 0 deletions cypress/e2e/po/edit/cloud-credentials-gke.po.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import LabeledInputPo from '@/cypress/e2e/po/components/labeled-input.po';
import ButtonFileSelectorPo from '@/cypress/e2e/po/components/button-file-selector.po';
import BaseCloudCredentialsPo from '@/cypress/e2e/po/edit/base-cloud-credentials.po';
import AsyncButtonPo from '@/cypress/e2e/po/components/async-button.po';

export default class GKECloudCredentialsCreateEditPo extends BaseCloudCredentialsPo {
serviceAccount(): LabeledInputPo {
return LabeledInputPo.byLabel(this.self(), 'Service Account');
}

readFromFile(): Cypress.Chainable {
return ButtonFileSelectorPo.readFromFileButton().click();
}

authenticateButton() {
return new AsyncButtonPo('[data-testid="action-button-async-button"]', this.self());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import PagePo from '@/cypress/e2e/po/pages/page.po';
import ClusterManagerCreatePagePo from '@/cypress/e2e/po/edit/provisioning.cattle.io.cluster/create/cluster-create.po';
import LabeledInputPo from '@/cypress/e2e/po/components/labeled-input.po';
import LabeledSelectPo from '@/cypress/e2e/po/components/labeled-select.po';
import GKECloudCredentialsCreateEditPo from '@/cypress/e2e/po/edit/cloud-credentials-gke.po';
import AsyncButtonPo from '@/cypress/e2e/po/components/async-button.po';

/**
* Create page for a GKE cluster
*/
export default class ClusterManagerCreateGKEPagePo extends ClusterManagerCreatePagePo {
static url(clusterId: string) {
return `${ ClusterManagerCreatePagePo.url(clusterId) }/create?type=googlegke`;
}

static goTo(clusterId: string): Cypress.Chainable<Cypress.AUTWindow> {
return PagePo.goTo(ClusterManagerCreateGKEPagePo.url(clusterId));
}

goToGKEClusterCreation(clusterId: string): Cypress.Chainable<Cypress.AUTWindow> {
return PagePo.goTo(`${ ClusterManagerCreatePagePo.url(clusterId) }?type=googlegke`);
}

cloudCredentialsForm(): GKECloudCredentialsCreateEditPo {
return new GKECloudCredentialsCreateEditPo();
}

authProjectId(): LabeledInputPo {
return LabeledInputPo.byLabel(this.self(), 'Google Project ID');
}

saveCreateGkeCluster(): AsyncButtonPo {
return new AsyncButtonPo('[data-testid="form-save"]', this.self());
}

static getGkeVersionSelect() {
return new LabeledSelectPo('[data-testid="gke-version-select"]');
}

static getGkeZoneSelect() {
return new LabeledSelectPo('[data-testid="gke-zone-select"]');
}

getClusterName() {
return new LabeledInputPo('[data-testid="gke-cluster-name"]');
}

getClusterDescription() {
return new LabeledInputPo('[data-testid="gke-cluster-description"]');
}
}
161 changes: 161 additions & 0 deletions cypress/e2e/tests/pages/manager/gke-cluster-provisioning.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import HomePagePo from '@/cypress/e2e/po/pages/home.po';
import ClusterManagerListPagePo from '@/cypress/e2e/po/pages/cluster-manager/cluster-manager-list.po';
import LoadingPo from '@/cypress/e2e/po/components/loading.po';
import ClusterManagerCreateGKEPagePo from '@/cypress/e2e/po/edit/provisioning.cattle.io.cluster/create/cluster-create-gke.po';
import { DEFAULT_GCP_ZONE } from '@/pkg/gke/util/gcp';

/******
* Running this test will delete all GKE cloud credentials from the target cluster
******/

// will only run this in jenkins pipeline where cloud credentials are stored
describe('Deploy GKE cluster with default settings', { tags: ['@manager', '@adminUser', '@jenkins'] }, () => {
const clusterList = new ClusterManagerListPagePo();
const loadingPo = new LoadingPo('.loading-indicator');

let cloudcredentialId = '';
const gkeDefaultZone = 'us-central1-c';
let gkeVersion = '';
let clusterId = '';
let clusterDescription = '';
const base64EncodedServiceAccount = Cypress.env('gkeServiceAccount');
let gkeProjectId = '';

// Check if the base64 string is defined and valid
if (base64EncodedServiceAccount) {
try {
// Decode the base64 string into a JSON string
const decodedServiceAccountJson = atob(base64EncodedServiceAccount);

// Parse the decoded JSON string
const serviceAccount = JSON.parse(decodedServiceAccountJson);

// Now you can access the project_id
gkeProjectId = serviceAccount.project_id;
/* eslint-disable no-console */
console.log(gkeProjectId); // Check if the value is correct
} catch (error) {
// Handle any error that occurs during decoding or parsing
console.error('Error decoding or parsing service account JSON:', error);
}
} else {
console.warn('gkeServiceAccount environment variable is undefined or empty.');
}

before(() => {
cy.login();
HomePagePo.goTo();

// clean up GKE cloud credentials
cy.getRancherResource('v3', 'cloudcredentials', null, null).then((resp: Cypress.Response<any>) => {
const body = resp.body;

if (body.pagination['total'] > 0) {
body.data.forEach((item: any) => {
if (item.googlecredentialConfig) {
const id = item.id;

cy.deleteRancherResource('v3', 'cloudcredentials', id);
} else {
cy.log('There are no existing GKE cloud credentials to delete');
}
});
}
});
});

beforeEach(() => {
cy.createE2EResourceName('gkecluster').as('gkeClusterName');
cy.createE2EResourceName('gkecloudcredential').as('gkeCloudCredentialName');
});

it('Successfully create GKE cluster with default settings', function() {
const createGKEClusterPage = new ClusterManagerCreateGKEPagePo();
const cloudCredForm = createGKEClusterPage.cloudCredentialsForm();

// Select GKE and create GKE cluster page
ClusterManagerListPagePo.navTo();
clusterList.waitForPage();
clusterList.createCluster();
createGKEClusterPage.selectKubeProvider(2);
loadingPo.checkNotExists();
createGKEClusterPage.rke2PageTitle().should('include', 'Create Google GKE');
createGKEClusterPage.waitForPage('type=googlegke&rkeType=rke2');

// create GKE cloud credential
cloudCredForm.saveButton().expectToBeDisabled();
cloudCredForm.nameNsDescription().name().set(this.gkeCloudCredentialName);
cloudCredForm.serviceAccount().set(Cypress.env('gkeServiceAccount'));
cloudCredForm.saveButton().expectToBeEnabled();
cy.intercept('GET', '/v1/management.cattle.io.users?exclude=metadata.managedFields').as('pageLoad');
cloudCredForm.saveCreateForm().cruResource().saveAndWaitForRequests('POST', '/v3/cloudcredentials').then((req) => {
expect(req.response?.statusCode).to.equal(201);
cloudcredentialId = req.response?.body.id.replace(':', '%3A');

// Authenticate GKE credential by providing the Project ID
createGKEClusterPage.waitForPage('type=googlegke&rkeType=rke2');
createGKEClusterPage.authProjectId().set( gkeProjectId );
cy.intercept('POST', `/meta/gkeVersions?cloudCredentialId=${ cloudcredentialId }&projectId=${ gkeProjectId }&zone=${ gkeDefaultZone }`).as('getGKEVersions');
cloudCredForm.authenticateButton().click();
cy.wait('@pageLoad').its('response.statusCode').should('eq', 200);
loadingPo.checkNotExists();

// Verify that gke-zone-select dropdown is set to the default zone
createGKEClusterPage.waitForPage('type=googlegke&rkeType=rke2');
ClusterManagerCreateGKEPagePo.getGkeZoneSelect().checkOptionSelected(DEFAULT_GCP_ZONE);

// Get latest GKE kubernetes version and verify that gke-version-select dropdown is set to the default version as defined by versionOptions(); in Config.vue
cy.wait('@getGKEVersions').then(({ response }) => {
expect(response.statusCode).to.eq(200);
gkeVersion = response.body.validMasterVersions[0];
cy.wrap(gkeVersion).as('gkeVersion');
ClusterManagerCreateGKEPagePo.getGkeVersionSelect().checkOptionSelected(gkeVersion);
});

// Set the cluster name and description in the Create GKE Page
createGKEClusterPage.getClusterName().set(this.gkeClusterName);
clusterDescription = `${ this.gkeClusterName }-description`;
createGKEClusterPage.getClusterDescription().set(clusterDescription);
});

// Create GKE Cluster and verify that the properties posted to the server match the expected settings
cy.intercept('POST', 'v3/clusters').as('createGKECluster');

createGKEClusterPage.saveCreateGkeCluster().click();
cy.wait('@createGKECluster').then(({ response }) => {
expect(response?.statusCode).to.eq(201);
expect(response?.body).to.have.property('baseType', 'cluster');
expect(response?.body.gkeConfig).to.have.property('clusterName', this.gkeClusterName);
expect(response?.body).to.have.property('description', clusterDescription);
expect(response?.body.gkeConfig).to.have.property('kubernetesVersion').contains(gkeVersion);
clusterId = response?.body.id;
});

// Verify that the GKE created cluster is listed in the clusters list and has the Provisioning status
clusterList.waitForPage();
clusterList.list().state(this.gkeClusterName).should('contain.text', 'Provisioning');
});

after('clean up', () => {
// delete cluster
cy.deleteRancherResource('v1', 'provisioning.cattle.io.clusters', `fleet-default/${ clusterId }`, false);

// clean up GKE cloud credentials
cy.getRancherResource('v3', 'cloudcredentials', null, null).then((resp: Cypress.Response<any>) => {
const body = resp.body;

if (body.pagination['total'] > 0) {
body.data.forEach((item: any) => {
if (item.googlecredentialConfig) {
const id = item.id;

cy.deleteRancherResource('v3', 'cloudcredentials', id);
} else {
cy.log('There are no existing GKE cloud credentials to delete');
}
});
}
});
}
);
});
8 changes: 6 additions & 2 deletions pkg/gke/components/CruGKE.vue
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,9 @@ export default defineComponent({
<div
class="row mb-10"
>
<div class="col span-6">
<div
class="col span-6"
>
<LabeledInput
:value="normanCluster.name"
:mode="mode"
Expand All @@ -708,7 +710,9 @@ export default defineComponent({
@update:value="setClusterName"
/>
</div>
<div class="col span-6">
<div
class="col span-6"
>
<LabeledInput
:value="normanCluster.description"
:mode="mode"
Expand Down

0 comments on commit 8795891

Please sign in to comment.