Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace old Mesh page with New Mesh page #7321

Merged
merged 47 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
27d2a29
- replace old mesh page with new mesh page
jshaughn Apr 23, 2024
471ae22
WIP - restoring overview control plane card to mostly standard namesp…
jshaughn Apr 24, 2024
f02630a
Add mesh page link to control plane badge
jshaughn Apr 26, 2024
808f7b3
Clean up exposure of special "_external_" deployment name
jshaughn Apr 29, 2024
8f98e18
- Limit the Mesh Tour to what is currently offered
jshaughn Apr 29, 2024
bad7a9a
Apply some refresh/resize mesh page learnings to GraphPF.
jshaughn Apr 30, 2024
464b51d
In anticipation of Fernando's resize fix, remove the unnecessary resize
jshaughn Apr 30, 2024
96bad78
fix unit test
jshaughn Apr 30, 2024
d3ca7b8
- finish removing use of '/api/clusters' API
jshaughn Apr 30, 2024
0b18f1d
WIP - Cypress
jshaughn May 1, 2024
e4215e6
some i18n updates
jshaughn May 5, 2024
da5d036
remove @selected from mesh feature
jshaughn May 5, 2024
b990914
Update some cypress tests to account for the removal of /api/clusters
jshaughn May 5, 2024
5787fac
embed the data plane config in each expandable row
jshaughn May 6, 2024
26b147a
If no health is set, assume healthy
jshaughn May 6, 2024
9316a92
WIP - add some test infra to allow the test code to select nodes...
jshaughn May 6, 2024
b539788
Update expected mesh graph.
jshaughn May 7, 2024
6192923
Add managed clusters to infra mesh graph
ferhoyos May 7, 2024
a174c9b
Enhance to test to cover multi-cluster primary-remote
jshaughn May 7, 2024
f7d5ef8
update mesh generator test to be multi-cluster
jshaughn May 7, 2024
8817a3d
- Call out the MeshRefs we pass back up for use in toolbar, tests, etc
jshaughn May 8, 2024
10ed534
- fix PF Badge on DataPlane node.
jshaughn May 8, 2024
d3f9311
Polish target panel UI styling
ferhoyos May 8, 2024
fdf2c90
change approach for grabbing meshpagecomponent to be more predictable…
jshaughn May 8, 2024
96e38c5
Fix a couple of Find/Hide issues given ongoing changes
jshaughn May 8, 2024
7293319
Adjust to get all meshpage sypress tests passing in CI, and clean up …
jshaughn May 9, 2024
f1de2a5
Canary upgrade UI adjustments
ferhoyos May 9, 2024
668c2de
Simplify i18n translation in target panel
ferhoyos May 9, 2024
d47b470
Fix yarn unit test fails
jshaughn May 9, 2024
632bc68
Make Infra IDs more unique by incorporating version. This solves an
jshaughn May 9, 2024
6bf67c0
Add canary badge to data plane
ferhoyos May 10, 2024
37a22a5
Change some badge tooltip sentences
ferhoyos May 13, 2024
c708472
Show istiod problems in the mesh if istiod-pod has any issue
ferhoyos May 13, 2024
aa1b221
Remove istiod hardcode
ferhoyos May 13, 2024
d41029d
More info at Mesh side target panel
ferhoyos May 14, 2024
a7b7d90
Adjust margin style
ferhoyos May 14, 2024
cc5e107
Add i18n support to mesh target panel
ferhoyos May 14, 2024
e25b8ef
Attach istio revision by default
ferhoyos May 14, 2024
c4246ae
Unknown version if empty
ferhoyos May 14, 2024
90b8c15
minor tweaks:
jshaughn May 14, 2024
3f8c1ab
i18n
jshaughn May 14, 2024
766cfdf
Fix mesh cypress
jshaughn May 14, 2024
bc432bf
- migrate some overview control-plane card tests to mesh page
jshaughn May 14, 2024
b88b804
PR Feedback, hayk+leandro:
jshaughn May 15, 2024
c9c0fe5
PR Feedback, leandro:
jshaughn May 15, 2024
64c8e7e
Determine Jaeger version
ferhoyos May 16, 2024
add1a35
Merge pull request #31 from ferhoyos/7298-jaeger-version
jshaughn May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,6 @@ type ListUIDefaults struct {
type MeshUIDefaults struct {
FindOptions []GraphFindOption `yaml:"find_options,omitempty" json:"findOptions,omitempty"`
HideOptions []GraphFindOption `yaml:"hide_options,omitempty" json:"hideOptions,omitempty"`
Impl string `yaml:"impl,omitempty" json:"impl,omitempty"` // classic | topo | topo-as-overview
}

// Aggregation represents label's allowed aggregations, transformed from aggregation in MonitoringDashboard config resource
Expand Down Expand Up @@ -845,7 +844,6 @@ func NewConfig() (c *Config) {
Expression: "healthy",
},
},
Impl: "classic",
},
MetricsInbound: MetricsDefaults{},
MetricsOutbound: MetricsDefaults{},
Expand Down
158 changes: 158 additions & 0 deletions frontend/cypress/integration/common/mesh_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { Before, Then, When } from '@badeball/cypress-cucumber-preprocessor';
import { Controller, Edge, Node, Visualization, isEdge, isNode } from '@patternfly/react-topology';
import { MeshInfraType, MeshNodeData } from '../../../src/types/Mesh';

Before(() => {
// Copied from overview.ts. This prevents cypress from stopping on errors unrelated to the tests.
// There can be random failures due timeouts/loadtime/framework that throw browser errors. This
// prevents a CI failure due something like a "slow". There may be a better way to handle this.
cy.on('uncaught:exception', (err, runnable, promise) => {
// when the exception originated from an unhandled promise
// rejection, the promise is provided as a third argument
// you can turn off failing the test in this case
if (promise) {
return false;
}
// we still want to ensure there are no other unexpected
// errors, so we let them fail the test
});
});

When('user closes mesh tour', () => {
cy.get('div[role="dialog"]').find('button[aria-label="Close"]').click();
});

When('user opens mesh tour', () => {
cy.get('button#mesh-tour').click();
});

When('user selects cluster mesh node', () => {
cy.waitForReact();
cy.getReact('MeshPageComponent', { state: { meshData: { isLoading: false } } })
.should('have.length', 1)
.getCurrentState()
.then(state => {
const controller = state.meshRefs.controller as Visualization;
assert.isTrue(controller.hasGraph());
const { nodes } = elems(controller);
const node = nodes.find(n => (n.getData() as MeshNodeData).infraType === MeshInfraType.CLUSTER);
assert.exists(node);
const setSelectedIds = state.meshRefs.setSelectedIds as (values: string[]) => void;
setSelectedIds([node.getId()]);
});
});

When('user selects mesh node with label {string}', (label: string) => {
cy.waitForReact();
cy.getReact('MeshPageComponent', { state: { meshData: { isLoading: false } } })
.should('have.length', 1)
.getCurrentState()
.then(state => {
const controller = state.meshRefs.controller as Visualization;
assert.isTrue(controller.hasGraph());
const { nodes } = elems(controller);
const node = nodes.find(n => n.getLabel() === label);
assert.exists(node);
const setSelectedIds = state.meshRefs.setSelectedIds as (values: string[]) => void;
setSelectedIds([node.getId()]);
});
});

Then('user sees cluster side panel', () => {
cy.get('#loading_kiali_spinner').should('not.exist');
cy.get('#target-panel-cluster').should('be.visible');
});

Then('user sees control plane side panel', () => {
cy.get('#loading_kiali_spinner').should('not.exist');
cy.get('#target-panel-control-plane')
.should('be.visible')
.within(div => {
cy.contains('istiod');
cy.contains('Control plane').should('be.visible');
cy.contains('Outbound policy').should('be.visible');
cy.get('div[data-test="memory-chart"]').should('exist');
cy.get('div[data-test="cpu-chart"]').should('exist');
cy.get('[data-test="label-TLS"]').contains('N/A');
cy.get('[data-test="lockerCA"]').should('exist');
});
cy.get('[data-test="lockerCA"]').trigger('mouseenter').get('[role="tooltip"]').contains('Valid From');
});

Then('user sees data plane side panel', () => {
cy.get('#loading_kiali_spinner').should('not.exist');
cy.get('#target-panel-data-plane')
.should('be.visible')
.within(div => {
cy.contains('Data Plane');
});
});

Then('user sees expected mesh infra', () => {
cy.waitForReact();
cy.getReact('MeshPageComponent', { state: { meshData: { isLoading: false } } })
.should('have.length', 1)
.getCurrentState()
.then(state => {
const controller = state.meshRefs.controller;
assert.isTrue(controller.hasGraph());
const { nodes, edges } = elems(controller);
assert.equal(nodes.length, 8, 'Unexpected number of infra nodes');
assert.equal(edges.length, 5, 'Unexpected number of infra edges');
const nodeNames = nodes.map(n => n.getLabel());
assert.isTrue(nodeNames.some(n => n === 'Data Plane'));
assert.isTrue(nodeNames.some(n => n === 'Grafana'));
assert.isTrue(nodeNames.some(n => n.startsWith('istiod')));
assert.isTrue(nodeNames.some(n => n === 'jaeger' || n === 'Tempo'));
assert.isTrue(nodeNames.some(n => n === 'kiali'));
assert.isTrue(nodeNames.some(n => n === 'Prometheus'));
});
});

Then('user sees mesh side panel', () => {
cy.get('#loading_kiali_spinner').should('not.exist');
cy.get('#target-panel-mesh')
.should('be.visible')
.within(div => {
cy.contains('Mesh Name: Istio Mesh');
});
});

Then('user {string} mesh tour', (action: string) => {
if (action === 'sees') {
cy.get('div[role="dialog"]').find('span').contains('Shortcuts').should('exist');
} else {
cy.get('div[role="dialog"]').should('not.exist');
}
});

Then('user sees {string} namespace side panel', (name: string) => {
cy.get('#loading_kiali_spinner').should('not.exist');
cy.get('#target-panel-namespace')
.should('be.visible')
.within(div => {
cy.contains(name);
});
});

Then('user sees {string} node side panel', (name: string) => {
cy.get('#loading_kiali_spinner').should('not.exist');
cy.get('#target-panel-node')
.should('be.visible')
.within(div => {
cy.contains(name);
});
});

//
// Since I can't import from MeshElems.tsx, copying some helpers here...
//

const elems = (c: Controller): { edges: Edge[]; nodes: Node[] } => {
const elems = c.getElements();

return {
nodes: elems.filter(e => isNode(e)) as Node[],
edges: elems.filter(e => isEdge(e)) as Edge[]
};
};
26 changes: 0 additions & 26 deletions frontend/cypress/integration/common/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,6 @@ Then(
}
);

Then('user sees the memory chart', () => {
cy.get('div[data-test="memory-chart"]').should('exist');
});

Then('user sees the cpu chart', () => {
cy.get('div[data-test="cpu-chart"]').should('exist');
});

Then('there should be a {string} application indicator in the namespace', function (healthStatus: string) {
cy.get(`[data-test=${this.targetNamespace}-EXPAND] [data-test=overview-app-health]`)
.find('span')
Expand Down Expand Up @@ -246,24 +238,6 @@ Then('the toggle on the right side of the {string} namespace card exists', (ns:
cy.get(`div[data-test^="${ns}"]`).should('exist');
});

Then('the user sees the certificates information', () => {
cy.get('[data-test="lockerCA"]').trigger('mouseenter').get('[role="tooltip"]').contains('Valid From');
});

// We will suppose that the min TLS Version was not set
// So we verify the default
Then('the minimum TLS version', () => {
cy.get('[data-test="label-TLS"]').contains('N/A');
});

Then('the user sees no information related to canary upgrades', () => {
cy.get('[data-test="canary-upgrade"]').should('not.exist');
});

Then('the user sees information related to canary upgrades', () => {
cy.get('[data-test="canary-upgrade"]').should('exist');
});

Then('user sees the {string} cluster badge in the Kiali header', (name: string) => {
cy.get('[data-test="cluster-icon"]').contains(name).should('be.visible');
});
Expand Down
41 changes: 26 additions & 15 deletions frontend/cypress/integration/common/sidecar_injection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor';
import { ensureKialiFinishedLoading } from './transition';
import { MeshCluster } from '../../../src/types/Mesh';

// Most of these "Given" implementations are directly using the Kiali API
// in order to reach a well known state in the environment before performing
Expand Down Expand Up @@ -247,11 +248,13 @@ When('I override the default automatic sidecar injection policy in the namespace
cy.request('GET', '/api/status').then(response => {
expect(response.status).to.equal(200);

cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.getBySel('overview-type-LIST').should('be.visible').click();

Expand All @@ -273,11 +276,13 @@ When(
cy.request('GET', '/api/status').then(response => {
expect(response.status).to.equal(200);

cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.getBySel('overview-type-LIST').should('be.visible').click();

Expand All @@ -300,11 +305,13 @@ When('I remove override configuration for sidecar injection in the namespace', f
cy.request('GET', '/api/status').then(response => {
expect(response.status).to.equal(200);

cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.getBySel('overview-type-LIST').should('be.visible').click();

Expand Down Expand Up @@ -351,11 +358,13 @@ Then('I should see the override annotation for sidecar injection in the namespac

const expectation = 'exist';

cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.getBySel(`VirtualItem_Cluster${cluster}_${this.targetNamespace}`)
.contains(`istio-injection=${enabled}`)
Expand All @@ -368,11 +377,13 @@ Then('I should see no override annotation for sidecar injection in the namespace
cy.request('GET', '/api/status').then(response => {
expect(response.status).to.equal(200);

cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.getBySel(`VirtualItem_Cluster${cluster}_${this.targetNamespace}`)
.contains(`istio-injection`)
Expand Down
19 changes: 12 additions & 7 deletions frontend/cypress/integration/common/table.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Then, When } from '@badeball/cypress-cucumber-preprocessor';
import { TableDefinition } from 'cypress-cucumber-preprocessor';
import { MeshCluster } from '../../../src/types/Mesh';

enum SortOrder {
Ascending = 'ascending',
Expand Down Expand Up @@ -210,15 +211,17 @@ export const checkHealthIndicatorInTable = (
: `${targetNamespace}_${targetRowItemName}`;

// cy.getBySel(`VirtualItem_Ns${selector}]`).find('span').filter(`.icon-${healthStatus}`).should('exist');
// Fetch the cluster info from /api/clusters
// Fetch the cluster info from /api/config
// TODO: Move this somewhere else since other tests will most likely need this info as well.
// VirtualItem_Clustercluster-default_Nsbookinfo_details
// VirtualItem_Clustercluster-default_Nsbookinfo_productpage
cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.getBySel(`VirtualItem_Cluster${cluster}_Ns${selector}`)
.find('span')
Expand All @@ -237,11 +240,13 @@ export const checkHealthStatusInTable = (
? `${targetNamespace}_${targetType}_${targetRowItemName}`
: `${targetNamespace}_${targetRowItemName}`;

cy.request('/api/clusters').then(response => {
cy.request('/api/config').then(response => {
cy.wrap(response.isOkStatusCode).should('be.true');
cy.wrap(response.body).should('have.length', 1);

const cluster = response.body[0].name;
const clusters: { [key: string]: MeshCluster } = response.body.clusters;
const clusterNames = Object.keys(clusters);
cy.wrap(clusterNames).should('have.length', 1);
const cluster = clusterNames[0];

cy.get(
`[data-test=VirtualItem_Cluster${cluster}_Ns${selector}] td:first-child span[class=pf-v5-c-icon__content]`
Expand Down