@jfvilas/plugin-kwirth-metrics 0.12.8 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +23 -8
  2. package/dist/api/KwirthMetricsClient.esm.js +4 -57
  3. package/dist/api/KwirthMetricsClient.esm.js.map +1 -1
  4. package/dist/api/types.esm.js.map +1 -1
  5. package/dist/components/{EntityKwirthMetricsContent/EntityKwirthMetricsContent.esm.js → EntityKwirthMetricsContent.esm.js} +84 -64
  6. package/dist/components/EntityKwirthMetricsContent.esm.js.map +1 -0
  7. package/dist/components/{Options/Options.esm.js → Options.esm.js} +1 -1
  8. package/dist/components/Options.esm.js.map +1 -0
  9. package/dist/index.d.ts +5 -4
  10. package/dist/index.esm.js +4 -0
  11. package/dist/index.esm.js.map +1 -1
  12. package/dist/plugin.esm.js +1 -1
  13. package/dist/plugin.esm.js.map +1 -1
  14. package/dist/routes.esm.js.map +1 -1
  15. package/package.json +2 -1
  16. package/dist/assets/kwirthmetrics-component-not-found.svg +0 -29
  17. package/dist/components/ClusterList/ClusterList.esm.js +0 -30
  18. package/dist/components/ClusterList/ClusterList.esm.js.map +0 -1
  19. package/dist/components/ComponentNotFound/ComponentNotFound.esm.js +0 -63
  20. package/dist/components/ComponentNotFound/ComponentNotFound.esm.js.map +0 -1
  21. package/dist/components/EntityKwirthMetricsContent/EntityKwirthMetricsContent.esm.js.map +0 -1
  22. package/dist/components/EntityKwirthMetricsContent/index.esm.js +0 -2
  23. package/dist/components/EntityKwirthMetricsContent/index.esm.js.map +0 -1
  24. package/dist/components/ObjectSelector/ObjectSelector.esm.js +0 -82
  25. package/dist/components/ObjectSelector/ObjectSelector.esm.js.map +0 -1
  26. package/dist/components/Options/Options.esm.js.map +0 -1
  27. package/dist/components/ShowError/ShowError.esm.js +0 -20
  28. package/dist/components/ShowError/ShowError.esm.js.map +0 -1
  29. package/dist/components/StatusLog/StatusLog.esm.js +0 -10
  30. package/dist/components/StatusLog/StatusLog.esm.js.map +0 -1
package/README.md CHANGED
@@ -3,13 +3,13 @@ This package is a Backstage plugin for **showing real-time streamed Kubernetes o
3
3
 
4
4
 
5
5
  ## Version compatibility
6
- This very first version of KwirthMetrics is compatible with Kwirth core server versions according to following table.
6
+ KwirthMetrics is compatible with Kwirth core server versions according to following table.
7
7
 
8
8
  | Plugin Kwirth version | Kwirth version |
9
9
  |-|-|
10
- |0.12.5|0.4.20|
10
+ |0.13.0|0.4.131|
11
11
  |0.12.8|0.4.45|
12
-
12
+ |0.12.5|0.4.20|
13
13
 
14
14
 
15
15
  ## What for?
@@ -39,7 +39,6 @@ This frontend plugin **includes just the visualization of metrics** charts. All
39
39
  The ability to restart pods is also configured in the app-config (YAML, env or whatever), and **restartig permissions are set independently than chart streaming permissions**.
40
40
  The backend plugin is the only responsible for configuration and permissionism, all the capabilities related with showing charts are implemented in the frontend plugin, which is in charge of establishing the connections to the corresponding Kwirth instances (running inside your Kubernetes clusters).
41
41
 
42
-
43
42
  ## How does it work?
44
43
  Let's explain this by following a user working sequence:
45
44
 
@@ -66,7 +65,7 @@ If everyting is correctly configured and tagged, the user should see a list of c
66
65
 
67
66
  ```bash
68
67
  # From your Backstage root directory
69
- yarn --cwd packages/app add @jfvilas/plugin-kwirth-metrics @jfvilas/plugin-kwirth-common @jfvilas/kwirth-common
68
+ yarn --cwd packages/app add @jfvilas/plugin-kwirth-metrics @jfvilas/plugin-kwirth-common @jfvilas/plugin-kwirth-frontend @jfvilas/kwirth-common
70
69
  ```
71
70
 
72
71
  3. Make sure the [Kwirth backend plugin](https://www.npmjs.com/package/@jfvilas/plugin-kwirth-backend#configure) is installed and configured.
@@ -78,13 +77,14 @@ If everyting is correctly configured and tagged, the user should see a list of c
78
77
  1. Add the KwirthMetrics plugin as a tab in your Entity pages:
79
78
 
80
79
  Firstly, import the plugin module.
80
+
81
81
  ```typescript
82
82
  // In packages/app/src/components/catalog/EntityPage.tsx
83
83
  import { EntityKwirthMetricsContent, isKwirthAvailable } from '@jfvilas/plugin-kwirth-metrics';
84
84
  ```
85
85
 
86
86
  Then, add a tab to your EntityPage (the 'if' is optional, you can keep the 'KwirthMetrics' tab always visible if you prefer to do it that way).
87
- ````jsx
87
+ ```jsx
88
88
  // Note: Add to any other Pages as well (e.g. defaultEntityPage or webSiteEntityPage, for example)
89
89
  const serviceEntityPage = (
90
90
  <EntityLayout>
@@ -95,6 +95,21 @@ If everyting is correctly configured and tagged, the user should see a list of c
95
95
  </EntityLayout>
96
96
  )
97
97
  ```
98
+ You can setup some default *viewing* options on the `EntityKwirthMetricsContent` component, so, when the entity loads the default options will be set. These options are:
99
+ - `depth`
100
+ - `width`
101
+ - `interval`
102
+ - `chart`
103
+ (The meaning of these properties are explained at the end of this document)
104
+
105
+ If you want to setup a long-running histogram as a default chart for your entities you should setup your entity page like this:
106
+ ```jsx
107
+ ...
108
+ <EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
109
+ <EntityKwirthMetricsContent allMetrics={true} enableRestart={false} depth={100} chart={'bar'}/>
110
+ </EntityLayout.Route>
111
+ ...
112
+ ```
98
113
 
99
114
  2. Label your catalog-info according to one of these two startegies:
100
115
 
@@ -102,7 +117,7 @@ If everyting is correctly configured and tagged, the user should see a list of c
102
117
 
103
118
  ```yaml
104
119
  metadata:
105
- annotaations:
120
+ annotations:
106
121
  backstage.io/kubernetes-id: entity001
107
122
  ```
108
123
 
@@ -110,7 +125,7 @@ If everyting is correctly configured and tagged, the user should see a list of c
110
125
 
111
126
  ```yaml
112
127
  metadata:
113
- annotaations:
128
+ annotations:
114
129
  backstage.io/kubernetes-id: 'app=core,artifact=backend'
115
130
  ```
116
131
 
@@ -1,4 +1,4 @@
1
- import { getVersion, getResources, requestAccess } from '@jfvilas/plugin-kwirth-common';
1
+ import { getInfo, getVersion, getResources, requestAccess } from '@jfvilas/plugin-kwirth-common';
2
2
 
3
3
  class KwirthMetricsClient {
4
4
  discoveryApi;
@@ -7,71 +7,18 @@ class KwirthMetricsClient {
7
7
  this.discoveryApi = options.discoveryApi;
8
8
  this.fetchApi = options.fetchApi;
9
9
  }
10
- // +++ test
10
+ async getInfo() {
11
+ return getInfo(this.discoveryApi, this.fetchApi);
12
+ }
11
13
  async getVersion() {
12
14
  return getVersion(this.discoveryApi, this.fetchApi);
13
15
  }
14
- // move to common
15
- // async getVersion(): Promise<string> {
16
- // try {
17
- // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')
18
- // const targetUrl = `${baseUrl}/version`
19
- // const result = await this.fetchApi.fetch(targetUrl)
20
- // const data = await result.json()
21
- // if (!result.ok) {
22
- // throw new Error(`getVersion error: not ok`)
23
- // }
24
- // return data.version
25
- // }
26
- // catch (err) {
27
- // throw new Error(`getVersion error: ${err}`)
28
- // }
29
- // }
30
- // async getResources(entity:Entity): Promise<ClusterValidPods> {
31
- // try {
32
- // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')
33
- // const targetUrl = `${baseUrl}/start`
34
- // var payload=JSON.stringify(entity)
35
- // const result = await this.fetchApi.fetch(targetUrl, {method:'POST', body:payload, headers:{'Content-Type':'application/json'}})
36
- // const data = await result.json() as ClusterValidPods
37
- // if (!result.ok) {
38
- // throw new Error(`getResources error: not ok`)
39
- // }
40
- // return data
41
- // }
42
- // catch (err) {
43
- // throw new Error(`getResources error: ${err}`)
44
- // }
45
- // }
46
16
  async getResources(entity) {
47
17
  return getResources(this.discoveryApi, this.fetchApi, entity);
48
18
  }
49
19
  async requestAccess(entity, channel, scopes) {
50
20
  return requestAccess(this.discoveryApi, this.fetchApi, entity, channel, scopes);
51
21
  }
52
- //+++ move to backstage-common
53
- // async requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]> {
54
- // try {
55
- // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')
56
- // var targetUrl:URL= new URL (`${baseUrl}/access`)
57
- // targetUrl.searchParams.append('scopes',scopes.join(','))
58
- // targetUrl.searchParams.append('channel',channel)
59
- // var payload=JSON.stringify(entity)
60
- // const result = await this.fetchApi.fetch(targetUrl, {method:'POST', body:payload, headers:{'Content-Type':'application/json'}})
61
- // const data = await result.json() as ClusterValidPods[]
62
- // // we reconstruct the 'Map' from string of arrays
63
- // for (var c of data) {
64
- // c.accessKeys = new Map(JSON.parse(((c as any).accessKeys)))
65
- // }
66
- // if (!result.ok) {
67
- // throw new Error(`requestAccess error: not ok`)
68
- // }
69
- // return data
70
- // }
71
- // catch (err) {
72
- // throw new Error(`requestAccess error: ${err}`)
73
- // }
74
- // }
75
22
  }
76
23
 
77
24
  export { KwirthMetricsClient };
@@ -1 +1 @@
1
- {"version":3,"file":"KwirthMetricsClient.esm.js","sources":["../../src/api/KwirthMetricsClient.ts"],"sourcesContent":["/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'\r\nimport { KwirthMetricsApi } from './types'\r\nimport { Entity } from '@backstage/catalog-model'\r\nimport { ClusterValidPods, getResources, getVersion, requestAccess } from '@jfvilas/plugin-kwirth-common'\r\nimport { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\nexport interface KwirthMetricsClientOptions {\r\n discoveryApi: DiscoveryApi\r\n fetchApi: FetchApi\r\n}\r\n\r\nexport class KwirthMetricsClient implements KwirthMetricsApi {\r\n private readonly discoveryApi: DiscoveryApi\r\n private readonly fetchApi: FetchApi\r\n\r\n\r\n constructor(options: KwirthMetricsClientOptions) {\r\n this.discoveryApi = options.discoveryApi\r\n this.fetchApi = options.fetchApi\r\n }\r\n\r\n // +++ test\r\n async getVersion() : Promise<string> {\r\n return getVersion(this.discoveryApi, this.fetchApi)\r\n }\r\n\r\n // move to common\r\n // async getVersion(): Promise<string> {\r\n // try {\r\n // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')\r\n // const targetUrl = `${baseUrl}/version`\r\n\r\n // const result = await this.fetchApi.fetch(targetUrl)\r\n // const data = await result.json()\r\n\r\n // if (!result.ok) {\r\n // throw new Error(`getVersion error: not ok`)\r\n // }\r\n // return data.version\r\n // }\r\n // catch (err) {\r\n // throw new Error(`getVersion error: ${err}`)\r\n // }\r\n // }\r\n\r\n // async getResources(entity:Entity): Promise<ClusterValidPods> {\r\n // try {\r\n // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')\r\n // const targetUrl = `${baseUrl}/start`\r\n\r\n // var payload=JSON.stringify(entity)\r\n // const result = await this.fetchApi.fetch(targetUrl, {method:'POST', body:payload, headers:{'Content-Type':'application/json'}})\r\n // const data = await result.json() as ClusterValidPods\r\n\r\n // if (!result.ok) {\r\n // throw new Error(`getResources error: not ok`)\r\n // }\r\n // return data\r\n // }\r\n // catch (err) {\r\n // throw new Error(`getResources error: ${err}`)\r\n // }\r\n // }\r\n async getResources(entity:Entity): Promise<ClusterValidPods> {\r\n return getResources(this.discoveryApi, this.fetchApi, entity)\r\n }\r\n\r\n async requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]> {\r\n return requestAccess(this.discoveryApi, this.fetchApi, entity, channel, scopes)\r\n }\r\n\r\n //+++ move to backstage-common\r\n // async requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]> {\r\n // try {\r\n // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')\r\n // var targetUrl:URL= new URL (`${baseUrl}/access`)\r\n // targetUrl.searchParams.append('scopes',scopes.join(','))\r\n // targetUrl.searchParams.append('channel',channel)\r\n\r\n // var payload=JSON.stringify(entity)\r\n // const result = await this.fetchApi.fetch(targetUrl, {method:'POST', body:payload, headers:{'Content-Type':'application/json'}})\r\n // const data = await result.json() as ClusterValidPods[]\r\n\r\n // // we reconstruct the 'Map' from string of arrays\r\n // for (var c of data) {\r\n // c.accessKeys = new Map(JSON.parse(((c as any).accessKeys)))\r\n // }\r\n // if (!result.ok) {\r\n // throw new Error(`requestAccess error: not ok`)\r\n // }\r\n // return data\r\n // }\r\n // catch (err) {\r\n // throw new Error(`requestAccess error: ${err}`)\r\n // }\r\n // }\r\n\r\n}\r\n"],"names":[],"mappings":";;AA0BO,MAAM,mBAAgD,CAAA;AAAA,EACxC,YAAA;AAAA,EACA,QAAA;AAAA,EAGjB,YAAY,OAAqC,EAAA;AAC7C,IAAA,IAAA,CAAK,eAAe,OAAQ,CAAA,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA;AAAA;AAC5B;AAAA,EAGA,MAAM,UAA+B,GAAA;AACjC,IAAA,OAAO,UAAW,CAAA,IAAA,CAAK,YAAc,EAAA,IAAA,CAAK,QAAQ,CAAA;AAAA;AACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuCA,MAAM,aAAa,MAA0C,EAAA;AACzD,IAAA,OAAO,YAAa,CAAA,IAAA,CAAK,YAAc,EAAA,IAAA,CAAK,UAAU,MAAM,CAAA;AAAA;AAChE,EAEA,MAAM,aAAA,CAAc,MAAe,EAAA,OAAA,EAAgB,MAA+D,EAAA;AAC9G,IAAA,OAAO,cAAc,IAAK,CAAA,YAAA,EAAc,KAAK,QAAU,EAAA,MAAA,EAAQ,SAAS,MAAM,CAAA;AAAA;AAClF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BJ;;;;"}
1
+ {"version":3,"file":"KwirthMetricsClient.esm.js","sources":["../../src/api/KwirthMetricsClient.ts"],"sourcesContent":["/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'\r\nimport { KwirthMetricsApi } from './types'\r\nimport { Entity } from '@backstage/catalog-model'\r\nimport { ClusterValidPods, getInfo, getResources, getVersion, IBackendInfo, requestAccess } from '@jfvilas/plugin-kwirth-common'\r\nimport { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\n\r\nexport interface KwirthMetricsClientOptions {\r\n discoveryApi: DiscoveryApi\r\n fetchApi: FetchApi\r\n}\r\n\r\nexport class KwirthMetricsClient implements KwirthMetricsApi {\r\n private readonly discoveryApi: DiscoveryApi\r\n private readonly fetchApi: FetchApi\r\n\r\n\r\n constructor(options: KwirthMetricsClientOptions) {\r\n this.discoveryApi = options.discoveryApi\r\n this.fetchApi = options.fetchApi\r\n }\r\n\r\n async getInfo() : Promise<IBackendInfo> {\r\n return getInfo(this.discoveryApi, this.fetchApi)\r\n }\r\n\r\n async getVersion() : Promise<string> {\r\n return getVersion(this.discoveryApi, this.fetchApi)\r\n }\r\n\r\n async getResources(entity:Entity): Promise<ClusterValidPods> {\r\n return getResources(this.discoveryApi, this.fetchApi, entity)\r\n }\r\n\r\n async requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]> {\r\n return requestAccess(this.discoveryApi, this.fetchApi, entity, channel, scopes)\r\n }\r\n\r\n}\r\n"],"names":[],"mappings":";;AA0BO,MAAM,mBAAA,CAAgD;AAAA,EACxC,YAAA;AAAA,EACA,QAAA;AAAA,EAGjB,YAAY,OAAA,EAAqC;AAC7C,IAAA,IAAA,CAAK,eAAe,OAAA,CAAQ,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAAA,EAC5B;AAAA,EAEA,MAAM,OAAA,GAAkC;AACpC,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,QAAQ,CAAA;AAAA,EACnD;AAAA,EAEA,MAAM,UAAA,GAA+B;AACjC,IAAA,OAAO,UAAA,CAAW,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,QAAQ,CAAA;AAAA,EACtD;AAAA,EAEA,MAAM,aAAa,MAAA,EAA0C;AACzD,IAAA,OAAO,YAAA,CAAa,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,UAAU,MAAM,CAAA;AAAA,EAChE;AAAA,EAEA,MAAM,aAAA,CAAc,MAAA,EAAe,OAAA,EAAgB,MAAA,EAA+D;AAC9G,IAAA,OAAO,cAAc,IAAA,CAAK,YAAA,EAAc,KAAK,QAAA,EAAU,MAAA,EAAQ,SAAS,MAAM,CAAA;AAAA,EAClF;AAEJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.esm.js","sources":["../../src/api/types.ts"],"sourcesContent":["/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { Entity } from '@backstage/catalog-model'\r\nimport { createApiRef } from '@backstage/core-plugin-api'\r\nimport { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\nimport { ClusterValidPods } from '@jfvilas/plugin-kwirth-common'\r\n\r\nexport interface KwirthMetricsApi {\r\n requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]>\r\n getResources(entity:Entity): Promise<ClusterValidPods>\r\n getVersion(): Promise<string>\r\n}\r\n\r\nexport const kwirthMetricsApiRef = createApiRef<KwirthMetricsApi>({\r\n id: 'plugin.kwirthmetrics.api',\r\n})\r\n"],"names":[],"mappings":";;AA0BO,MAAM,sBAAsB,YAA+B,CAAA;AAAA,EAChE,EAAI,EAAA;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"types.esm.js","sources":["../../src/api/types.ts"],"sourcesContent":["/*\r\nCopyright 2024 Julio Fernandez\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\nimport { Entity } from '@backstage/catalog-model'\r\nimport { createApiRef } from '@backstage/core-plugin-api'\r\nimport { InstanceConfigScopeEnum } from '@jfvilas/kwirth-common'\r\nimport { ClusterValidPods, IBackendInfo } from '@jfvilas/plugin-kwirth-common'\r\n\r\nexport interface KwirthMetricsApi {\r\n requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]>\r\n getResources(entity:Entity): Promise<ClusterValidPods>\r\n getVersion(): Promise<string>\r\n getInfo(): Promise<IBackendInfo>\r\n}\r\n\r\nexport const kwirthMetricsApiRef = createApiRef<KwirthMetricsApi>({\r\n id: 'plugin.kwirthmetrics.api',\r\n})\r\n"],"names":[],"mappings":";;AA2BO,MAAM,sBAAsB,YAAA,CAA+B;AAAA,EAChE,EAAA,EAAI;AACN,CAAC;;;;"}
@@ -2,20 +2,13 @@ import React, { useState, useRef } from 'react';
2
2
  import useAsync from 'react-use/esm/useAsync';
3
3
  import { Progress, WarningPanel } from '@backstage/core-components';
4
4
  import { useApi, alertApiRef } from '@backstage/core-plugin-api';
5
- import { isKwirthAvailable, ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR } from '@jfvilas/plugin-kwirth-common';
5
+ import { isKwirthAvailable, ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR, getPodList, getContainerList } from '@jfvilas/plugin-kwirth-common';
6
6
  import { useEntity, MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
7
- import { kwirthMetricsApiRef } from '../../api/types.esm.js';
7
+ import { kwirthMetricsApiRef } from '../api/types.esm.js';
8
8
  import { SignalMessageLevelEnum, InstanceConfigScopeEnum, InstanceMessageTypeEnum, OpsCommandEnum, accessKeySerialize, InstanceMessageChannelEnum, InstanceMessageFlowEnum, InstanceMessageActionEnum, MetricsConfigModeEnum, InstanceConfigViewEnum, InstanceConfigObjectEnum } from '@jfvilas/kwirth-common';
9
- import { ComponentNotFound, ErrorType } from '../ComponentNotFound/ComponentNotFound.esm.js';
10
- import { Options } from '../Options/Options.esm.js';
11
- import { ClusterList } from '../ClusterList/ClusterList.esm.js';
12
- import { ObjectSelector } from '../ObjectSelector/ObjectSelector.esm.js';
13
- import { ShowError } from '../ShowError/ShowError.esm.js';
14
- import { StatusLog } from '../StatusLog/StatusLog.esm.js';
15
- import { Box, Grid, Card, CardHeader, CardContent, FormControl, Select, MenuItem, Checkbox } from '@material-ui/core';
16
- import Divider from '@material-ui/core/Divider';
17
- import IconButton from '@material-ui/core/IconButton';
18
- import Typography from '@material-ui/core/Typography';
9
+ import { ShowError, ComponentNotFound, ErrorType, ClusterList, KwirthNews, ObjectSelector, StatusLog } from '@jfvilas/plugin-kwirth-frontend';
10
+ import { VERSION } from '../index.esm.js';
11
+ import { Box, Grid, Card, CardHeader, Typography, FormControl, Select, MenuItem, Checkbox, Divider, CardContent, IconButton, Tooltip as Tooltip$1 } from '@material-ui/core';
19
12
  import PlayIcon from '@material-ui/icons/PlayArrow';
20
13
  import PauseIcon from '@material-ui/icons/Pause';
21
14
  import StopIcon from '@material-ui/icons/Stop';
@@ -23,14 +16,15 @@ import InfoIcon from '@material-ui/icons/Info';
23
16
  import WarningIcon from '@material-ui/icons/Warning';
24
17
  import ErrorIcon from '@material-ui/icons/Error';
25
18
  import RefreshIcon from '@material-ui/icons/Refresh';
26
- import KwirthMetricsLogo from '../../assets/kwirthmetrics-logo.svg';
19
+ import KwirthMetricsLogo from '../assets/kwirthmetrics-logo.svg';
27
20
  import { BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, AreaChart, Area, LineChart, Line, ResponsiveContainer } from 'recharts';
21
+ import { Options } from './Options.esm.js';
28
22
 
29
23
  const EntityKwirthMetricsContent = (props) => {
30
24
  const kwirthMetricsApi = useApi(kwirthMetricsApiRef);
31
25
  const alertApi = useApi(alertApiRef);
32
26
  const { entity } = useEntity();
33
- const [clusterValidPods, setClusterValidPods] = useState([]);
27
+ const [validClusters, setValidClusters] = useState([]);
34
28
  const [selectedClusterName, setSelectedClusterName] = useState("");
35
29
  const [selectedNamespaces, setSelectedNamespaces] = useState([]);
36
30
  const [selectedPodNames, setSelectedPodNames] = useState([]);
@@ -44,10 +38,19 @@ const EntityKwirthMetricsContent = (props) => {
44
38
  const [statusMessages, setStatusMessages] = useState([]);
45
39
  const [websocket, setWebsocket] = useState();
46
40
  const [instance, setInstance] = useState();
47
- const kwirthMetricsOptionsRef = useRef({ depth: 10, width: 3, interval: 10, chart: "area", aggregate: false, merge: false, stack: false });
41
+ const kwirthMetricsOptionsRef = useRef({
42
+ depth: props.depth !== void 0 ? props.depth : 10,
43
+ width: props.width !== void 0 ? props.width : 3,
44
+ interval: props.interval !== void 0 ? props.interval : 15,
45
+ chart: props.chart !== void 0 ? props.chart : "area",
46
+ aggregate: false,
47
+ merge: false,
48
+ stack: false
49
+ });
48
50
  const [showStatusDialog, setShowStatusDialog] = useState(false);
49
51
  const [statusLevel, setStatusLevel] = useState(SignalMessageLevelEnum.INFO);
50
52
  const [backendVersion, setBackendVersion] = useState("");
53
+ const [backendInfo, setBackendInfo] = useState();
51
54
  const [_refresh, setRefresh] = useState(0);
52
55
  const [allMetrics, setAllMetrics] = useState(
53
56
  [
@@ -61,10 +64,11 @@ const EntityKwirthMetricsContent = (props) => {
61
64
  );
62
65
  const { loading, error } = useAsync(async () => {
63
66
  if (backendVersion === "") setBackendVersion(await kwirthMetricsApi.getVersion());
67
+ if (!backendInfo) setBackendInfo(await kwirthMetricsApi.getInfo());
64
68
  let reqScopes = [InstanceConfigScopeEnum.STREAM];
65
69
  if (props.enableRestart) reqScopes.push(InstanceConfigScopeEnum.RESTART);
66
70
  let data = await kwirthMetricsApi.requestAccess(entity, "metrics", reqScopes);
67
- setClusterValidPods(data);
71
+ setValidClusters(data);
68
72
  });
69
73
  const colours = [
70
74
  "#6e5bb8",
@@ -148,25 +152,32 @@ const EntityKwirthMetricsContent = (props) => {
148
152
  setStarted(false);
149
153
  paused.current = true;
150
154
  };
151
- const clickStop = () => {
155
+ const onClickStop = () => {
152
156
  setStarted(false);
153
157
  setStopped(true);
154
158
  paused.current = false;
155
159
  stopMetricsViewer();
156
160
  };
157
161
  const onSelectCluster = async (clusterName) => {
162
+ if (started) onClickStop();
158
163
  if (clusterName) {
159
164
  setSelectedClusterName(clusterName);
160
- setSelectedNamespaces([]);
161
165
  setSelectedPodNames([]);
162
166
  setSelectedContainerNames([]);
163
- setMetricsMessages([]);
164
167
  setStatusMessages([]);
165
- clickStop();
166
- let cluster = clusterValidPods.find((cluster2) => cluster2.name === clusterName);
167
- if (cluster && cluster.metrics) {
168
- cluster.metrics.sort((a, b) => a.metric.startsWith("kwirth") ? -1 : 1);
168
+ let cluster = validClusters.find((cluster2) => cluster2.name === clusterName);
169
+ if (cluster && cluster.pods && cluster.metrics) {
170
+ cluster.metrics.sort((a, _b) => a.metric.startsWith("kwirth") ? -1 : 1);
169
171
  setAllMetrics(cluster.metrics);
172
+ let validNamespaces = Array.from(new Set(cluster.pods.map((pod) => pod.namespace)));
173
+ if (validNamespaces.length === 1) {
174
+ setSelectedNamespaces(validNamespaces);
175
+ let podList = getPodList(cluster.pods, validNamespaces);
176
+ setSelectedPodNames(podList.map((pod) => pod.name));
177
+ setSelectedContainerNames(getContainerList(cluster.pods, validNamespaces, podList.map((pod) => pod.name)));
178
+ } else {
179
+ setSelectedNamespaces([]);
180
+ }
170
181
  }
171
182
  }
172
183
  };
@@ -196,24 +207,28 @@ const EntityKwirthMetricsContent = (props) => {
196
207
  setInstance(instanceMessage.instance);
197
208
  else {
198
209
  let signalMessage = instanceMessage;
199
- alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
210
+ if (signalMessage.text) {
211
+ alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
212
+ }
200
213
  }
201
214
  } else {
202
215
  let signalMessage = instanceMessage;
203
- addMessage(signalMessage.level, signalMessage.text);
204
- switch (signalMessage.level) {
205
- case SignalMessageLevelEnum.INFO:
206
- alertApi.post({ message: signalMessage.text, severity: "info", display: "transient" });
207
- break;
208
- case SignalMessageLevelEnum.WARNING:
209
- alertApi.post({ message: signalMessage.text, severity: "warning", display: "transient" });
210
- break;
211
- case SignalMessageLevelEnum.ERROR:
212
- alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
213
- break;
214
- default:
215
- alertApi.post({ message: signalMessage.text, severity: "success", display: "transient" });
216
- break;
216
+ if (signalMessage.text) {
217
+ addMessage(signalMessage.level, signalMessage.text);
218
+ switch (signalMessage.level) {
219
+ case SignalMessageLevelEnum.INFO:
220
+ alertApi.post({ message: signalMessage.text, severity: "info", display: "transient" });
221
+ break;
222
+ case SignalMessageLevelEnum.WARNING:
223
+ alertApi.post({ message: signalMessage.text, severity: "warning", display: "transient" });
224
+ break;
225
+ case SignalMessageLevelEnum.ERROR:
226
+ alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
227
+ break;
228
+ default:
229
+ alertApi.post({ message: signalMessage.text, severity: "success", display: "transient" });
230
+ break;
231
+ }
217
232
  }
218
233
  }
219
234
  break;
@@ -249,12 +264,12 @@ const EntityKwirthMetricsContent = (props) => {
249
264
  }
250
265
  };
251
266
  const websocketOnOpen = (ws) => {
252
- let cluster = clusterValidPods.find((cluster2) => cluster2.name === selectedClusterName);
267
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
253
268
  if (!cluster) {
254
269
  addMessage(SignalMessageLevelEnum.ERROR, "Cluster not found");
255
270
  return;
256
271
  }
257
- let pods = cluster.data.filter((p2) => selectedNamespaces.includes(p2.namespace));
272
+ let pods = cluster.pods.filter((p2) => selectedNamespaces.includes(p2.namespace));
258
273
  if (!pods || pods.length === 0) {
259
274
  addMessage(SignalMessageLevelEnum.ERROR, "Pod not found");
260
275
  return;
@@ -298,7 +313,7 @@ const EntityKwirthMetricsContent = (props) => {
298
313
  }
299
314
  };
300
315
  const startMetricsViewer = () => {
301
- let cluster = clusterValidPods.find((cluster2) => cluster2.name === selectedClusterName);
316
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
302
317
  if (!cluster) {
303
318
  addMessage(SignalMessageLevelEnum.ERROR, "Cluster not found");
304
319
  return;
@@ -328,28 +343,25 @@ const EntityKwirthMetricsContent = (props) => {
328
343
  };
329
344
  const actionButtons = () => {
330
345
  let hasStreamKey = false, hasRestartKey = false;
331
- let cluster = clusterValidPods.find((cluster2) => cluster2.name === selectedClusterName);
346
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
332
347
  if (cluster) {
333
348
  hasStreamKey = Boolean(cluster.accessKeys.has(InstanceConfigScopeEnum.STREAM));
334
349
  hasRestartKey = Boolean(cluster.accessKeys.get(InstanceConfigScopeEnum.RESTART));
335
350
  }
336
- return /* @__PURE__ */ React.createElement(React.Fragment, null, props.enableRestart && /* @__PURE__ */ React.createElement(IconButton, { title: "Restart", onClick: onClickRestart, disabled: selectedPodNames.length === 0 || !hasRestartKey || !websocket || !started }, /* @__PURE__ */ React.createElement(RefreshIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: clickStart, title: "Play", disabled: started || !paused || selectedPodNames.length === 0 || selectedMetrics.length == 0 || !hasStreamKey }, /* @__PURE__ */ React.createElement(PlayIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: clickPause, title: "Pause", disabled: !(started && !paused.current && selectedPodNames.length === 0) }, /* @__PURE__ */ React.createElement(PauseIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: clickStop, title: "Stop", disabled: stopped || selectedPodNames.length === 0 }, /* @__PURE__ */ React.createElement(StopIcon, null)));
351
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, props.enableRestart && /* @__PURE__ */ React.createElement(IconButton, { title: "Restart", onClick: onClickRestart, disabled: selectedPodNames.length === 0 || !hasRestartKey || !websocket || !started }, /* @__PURE__ */ React.createElement(RefreshIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: clickStart, title: "Play", disabled: started || !paused || selectedPodNames.length === 0 || selectedMetrics.length == 0 || !hasStreamKey }, /* @__PURE__ */ React.createElement(PlayIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: clickPause, title: "Pause", disabled: !(started && !paused.current && selectedPodNames.length === 0) }, /* @__PURE__ */ React.createElement(PauseIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: onClickStop, title: "Stop", disabled: stopped || selectedPodNames.length === 0 }, /* @__PURE__ */ React.createElement(StopIcon, null)));
337
352
  };
338
353
  const onMetricsChange = (event) => {
339
354
  setSelectedMetrics(event.target.value);
340
355
  };
341
- const metricsSelector = () => {
342
- let disabled = selectedClusterName === "" || selectedNamespaces.length === 0;
343
- return /* @__PURE__ */ React.createElement(FormControl, { style: { marginLeft: 16, width: "300px" }, size: "small" }, /* @__PURE__ */ React.createElement(Select, { value: selectedMetrics, MenuProps: { variant: "menu" }, multiple: true, onChange: onMetricsChange, renderValue: (selected) => selected.join(", "), disabled: disabled || started }, allMetrics.map(
344
- (m) => /* @__PURE__ */ React.createElement(MenuItem, { key: m.metric, value: m.metric, style: { marginTop: "-6px", marginBottom: "-6px" } }, /* @__PURE__ */ React.createElement(Checkbox, { checked: selectedMetrics.includes(m.metric), style: { marginTop: "-6px", marginBottom: "-6px" } }), /* @__PURE__ */ React.createElement(Typography, { style: { marginTop: "-6px", marginBottom: "-6px" } }, m.metric))
345
- )));
346
- };
347
356
  const statusButtons = (title) => {
348
357
  const show = (level) => {
349
358
  setShowStatusDialog(true);
350
359
  setStatusLevel(level);
351
360
  };
352
- return /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "row" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, title)), /* @__PURE__ */ React.createElement(Grid, { item: true, style: { marginTop: "-8px" } }, /* @__PURE__ */ React.createElement(IconButton, { title: "info", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.INFO), onClick: () => show(SignalMessageLevelEnum.INFO) }, /* @__PURE__ */ React.createElement(InfoIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.INFO) ? "blue" : "#BDBDBD" } })), /* @__PURE__ */ React.createElement(IconButton, { title: "warning", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.WARNING), onClick: () => show(SignalMessageLevelEnum.WARNING), style: { marginLeft: "-16px" } }, /* @__PURE__ */ React.createElement(WarningIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.WARNING) ? "gold" : "#BDBDBD" } })), /* @__PURE__ */ React.createElement(IconButton, { title: "error", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.ERROR), onClick: () => show(SignalMessageLevelEnum.ERROR), style: { marginLeft: "-16px" } }, /* @__PURE__ */ React.createElement(ErrorIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.ERROR) ? "red" : "#BDBDBD" } }))), /* @__PURE__ */ React.createElement(Grid, { item: true }, metricsSelector()));
361
+ const prepareText = (txt) => {
362
+ return txt ? txt.length > 25 ? txt.substring(0, 25) + "..." : txt : "N/A";
363
+ };
364
+ return /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "row" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, prepareText(title))), /* @__PURE__ */ React.createElement(Grid, { item: true, style: { marginTop: "-8px" } }, /* @__PURE__ */ React.createElement(IconButton, { title: "info", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.INFO), onClick: () => show(SignalMessageLevelEnum.INFO) }, /* @__PURE__ */ React.createElement(InfoIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.INFO) ? "blue" : "#BDBDBD" } })), /* @__PURE__ */ React.createElement(IconButton, { title: "warning", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.WARNING), onClick: () => show(SignalMessageLevelEnum.WARNING), style: { marginLeft: "-16px" } }, /* @__PURE__ */ React.createElement(WarningIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.WARNING) ? "gold" : "#BDBDBD" } })), /* @__PURE__ */ React.createElement(IconButton, { title: "error", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.ERROR), onClick: () => show(SignalMessageLevelEnum.ERROR), style: { marginLeft: "-16px" } }, /* @__PURE__ */ React.createElement(ErrorIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.ERROR) ? "red" : "#BDBDBD" } }))));
353
365
  };
354
366
  const statusClear = (level) => {
355
367
  setStatusMessages(statusMessages.filter((m) => m.level !== level));
@@ -382,7 +394,7 @@ const EntityKwirthMetricsContent = (props) => {
382
394
  switch (options.chart) {
383
395
  case "value":
384
396
  height = 40 + series.length * 80;
385
- result = /* @__PURE__ */ React.createElement(Grid, { direction: "row" }, /* @__PURE__ */ React.createElement(Typography, null, series.map((serie, index) => {
397
+ result = /* @__PURE__ */ React.createElement(Grid, null, /* @__PURE__ */ React.createElement(Typography, null, series.map((serie, index) => {
386
398
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, null, serie[serie.length - 1].value), /* @__PURE__ */ React.createElement(Typography, null, names[index]));
387
399
  })));
388
400
  break;
@@ -400,7 +412,16 @@ const EntityKwirthMetricsContent = (props) => {
400
412
  ));
401
413
  break;
402
414
  }
403
- return /* @__PURE__ */ React.createElement(Grid, { direction: "column", style: { width: "100%", marginBottom: 8 } }, /* @__PURE__ */ React.createElement(Typography, { align: "center" }, metric), /* @__PURE__ */ React.createElement(ResponsiveContainer, { height, key: metric + JSON.stringify(names) }, result));
415
+ let title = metric.metric.replaceAll("_", " ");
416
+ title = title[0].toLocaleUpperCase() + title.substring(1);
417
+ title = title.replaceAll("cpu", "CPU");
418
+ title = title.replaceAll(" fs ", " FS ");
419
+ title = title.replaceAll(" io ", " IO ");
420
+ title = title.replaceAll("oom", "OOM");
421
+ title = title.replaceAll("nvm", "NVM");
422
+ title = title.replaceAll("rss", "RSS");
423
+ title = title.replaceAll("failcnt", "fail count");
424
+ return /* @__PURE__ */ React.createElement(Grid, { style: { width: "100%", marginBottom: 32 } }, /* @__PURE__ */ React.createElement(Tooltip$1, { title: /* @__PURE__ */ React.createElement(Typography, { style: { fontSize: 12 } }, /* @__PURE__ */ React.createElement("b", null, metric.metric), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), metric.help) }, /* @__PURE__ */ React.createElement(Typography, { align: "center" }, title)), /* @__PURE__ */ React.createElement(ResponsiveContainer, { height, key: metric + JSON.stringify(names) }, result));
404
425
  };
405
426
  const showMetrics = (options) => {
406
427
  if (!metricsMessages || metricsMessages.length === 0) {
@@ -409,6 +430,8 @@ const EntityKwirthMetricsContent = (props) => {
409
430
  else
410
431
  return /* @__PURE__ */ React.createElement(React.Fragment, null, started ? /* @__PURE__ */ React.createElement(Typography, null, "Waiting for first data, be patient...") : /* @__PURE__ */ React.createElement(Typography, null, "Configure ", /* @__PURE__ */ React.createElement("b", null, "chart options"), ", select some ", /* @__PURE__ */ React.createElement("b", null, "metrics on top"), ", and ", /* @__PURE__ */ React.createElement("b", null, "press PLAY"), " on top-right button to start viewing."));
411
432
  }
433
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
434
+ if (!cluster) return;
412
435
  let data = /* @__PURE__ */ new Map();
413
436
  for (let metricsMessage of metricsMessages) {
414
437
  let ts = new Date(metricsMessage.timestamp);
@@ -428,10 +451,11 @@ const EntityKwirthMetricsContent = (props) => {
428
451
  var firstAsset = assetNames[0];
429
452
  var allMetrics2 = Array.from(new Set(data.get(firstAsset).keys()));
430
453
  for (let metric of allMetrics2) {
454
+ let metricDefinition = cluster.metrics?.find((m) => m.metric === metric);
431
455
  var series = assetNames.map((an) => {
432
456
  return data.get(an).get(metric);
433
457
  });
434
- allCharts.push(/* @__PURE__ */ React.createElement(React.Fragment, null, addChart(options, metric, assetNames, series, "")));
458
+ allCharts.push(/* @__PURE__ */ React.createElement(React.Fragment, null, addChart(options, metricDefinition, assetNames, series, "")));
435
459
  }
436
460
  let rows = [];
437
461
  for (let i2 = 0; i2 < allCharts.length; i2 += options.width) {
@@ -442,7 +466,8 @@ const EntityKwirthMetricsContent = (props) => {
442
466
  let allCharts2 = Array.from(data.keys()).map((asset, index) => {
443
467
  return Array.from(data.get(asset)?.keys()).map((metric) => {
444
468
  var serie = data.get(asset)?.get(metric);
445
- return /* @__PURE__ */ React.createElement(React.Fragment, null, addChart(options, metric, [asset], [serie], colours[index]));
469
+ let metricDefinition = cluster.metrics?.find((m) => m.metric === metric);
470
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, addChart(options, metricDefinition, [asset], [serie], colours[index]));
446
471
  });
447
472
  });
448
473
  let rows = [];
@@ -467,7 +492,7 @@ const EntityKwirthMetricsContent = (props) => {
467
492
  }]);
468
493
  };
469
494
  const onClickRestart = () => {
470
- let cluster = clusterValidPods.find((cluster2) => cluster2.name === selectedClusterName);
495
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
471
496
  if (!cluster) {
472
497
  addMessage(SignalMessageLevelEnum.ERROR, "No cluster selected");
473
498
  return;
@@ -481,7 +506,7 @@ const EntityKwirthMetricsContent = (props) => {
481
506
  addMessage(SignalMessageLevelEnum.ERROR, "No instance has been established");
482
507
  return;
483
508
  }
484
- let pods = cluster.data.filter((pod) => selectedNamespaces.includes(pod.namespace));
509
+ let pods = cluster.pods.filter((pod) => selectedNamespaces.includes(pod.namespace));
485
510
  for (let pod of pods) {
486
511
  let om = {
487
512
  msgtype: "opsmessage",
@@ -512,14 +537,9 @@ const EntityKwirthMetricsContent = (props) => {
512
537
  websocket?.send(JSON.stringify(rm));
513
538
  }
514
539
  };
515
- return /* @__PURE__ */ React.createElement(React.Fragment, null, showError !== "" && /* @__PURE__ */ React.createElement(ShowError, { message: showError, onClose: () => setShowError("") }), loading && /* @__PURE__ */ React.createElement(Progress, null), !isKwirthAvailable(entity) && !loading && error && /* @__PURE__ */ React.createElement(WarningPanel, { title: "An error has ocurred while obtaining data from kuebernetes clusters.", message: error?.message }), !isKwirthAvailable(entity) && !loading && /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, { readMoreUrl: "https://github.com/jfvilas/kwirth", annotation: [ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR] }), isKwirthAvailable(entity) && !loading && clusterValidPods && clusterValidPods.length === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_CLUSTERS, entity }), isKwirthAvailable(entity) && !loading && clusterValidPods && clusterValidPods.length > 0 && clusterValidPods.reduce((sum, cluster) => sum + cluster.data.length, 0) === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_PODS, entity }), isKwirthAvailable(entity) && !loading && clusterValidPods && clusterValidPods.length > 0 && clusterValidPods.reduce((sum, cluster) => sum + cluster.data.length, 0) > 0 && /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex" } }, /* @__PURE__ */ React.createElement(Box, { sx: { width: "200px" } }, /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "column" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(ClusterList, { resources: clusterValidPods, selectedClusterName, onSelect: onSelectCluster }))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(Options, { options: kwirthMetricsOptionsRef.current, selectedNamespaces, selectedPodNames, selectedContainerNames, onChange: onChangeOptions, disabled: selectedNamespaces.length === 0 || paused.current }))))), /* @__PURE__ */ React.createElement(Box, { sx: { flexGrow: 1, p: 1 } }, !selectedClusterName && /* @__PURE__ */ React.createElement("img", { src: KwirthMetricsLogo, alt: "No cluster selected", style: { left: "40%", marginTop: "10%", width: "20%", position: "relative" } }), selectedClusterName && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { style: { marginTop: -8 } }, /* @__PURE__ */ React.createElement(
516
- CardHeader,
517
- {
518
- title: statusButtons(selectedClusterName),
519
- style: { marginTop: -4, marginBottom: 4, flexShrink: 0 },
520
- action: actionButtons()
521
- }
522
- ), /* @__PURE__ */ React.createElement(Typography, { style: { marginLeft: 16, marginBottom: 4 } }, /* @__PURE__ */ React.createElement(ObjectSelector, { cluster: clusterValidPods.find((cluster) => cluster.name === selectedClusterName), onSelect: onSelectObject, disabled: selectedClusterName === "" || started || paused.current, selectedNamespaces, selectedPodNames, selectedContainerNames, scope: InstanceConfigScopeEnum.STREAM })), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, { style: { overflow: "auto" } }, showMetrics(kwirthMetricsOptionsRef.current)))))), showStatusDialog && /* @__PURE__ */ React.createElement(StatusLog, { level: statusLevel, onClose: () => setShowStatusDialog(false), statusMessages, onClear: statusClear }));
540
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, showError !== "" && /* @__PURE__ */ React.createElement(ShowError, { message: showError, onClose: () => setShowError("") }), loading && /* @__PURE__ */ React.createElement(Progress, null), !isKwirthAvailable(entity) && !loading && error && /* @__PURE__ */ React.createElement(WarningPanel, { title: "An error has ocurred while obtaining data from kuebernetes clusters.", message: error?.message }), !isKwirthAvailable(entity) && !loading && /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, { readMoreUrl: "https://github.com/jfvilas/kwirth", annotation: [ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR] }), isKwirthAvailable(entity) && !loading && validClusters && validClusters.length === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_CLUSTERS, entity }), isKwirthAvailable(entity) && !loading && validClusters && validClusters.length > 0 && validClusters.reduce((sum, cluster) => sum + cluster.pods.length, 0) === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_PODS, entity }), isKwirthAvailable(entity) && !loading && validClusters && validClusters.length > 0 && validClusters.reduce((sum, cluster) => sum + cluster.pods.length, 0) > 0 && /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex" } }, /* @__PURE__ */ React.createElement(Box, { sx: { width: "200px", maxWidth: "200px" } }, /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "column" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(ClusterList, { resources: validClusters, selectedClusterName, onSelect: onSelectCluster }))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(Options, { metricsOptions: kwirthMetricsOptionsRef.current, selectedNamespaces, selectedPodNames, selectedContainerNames, onChange: onChangeOptions, disabled: selectedNamespaces.length === 0 || paused.current }))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(KwirthNews, { latestVersions: backendInfo, backendVersion, ownVersion: VERSION }))))), /* @__PURE__ */ React.createElement(Box, { sx: { flexGrow: 1, p: 1, marginLeft: "8px" } }, !selectedClusterName && /* @__PURE__ */ React.createElement("img", { src: KwirthMetricsLogo, alt: "No cluster selected", style: { left: "40%", marginTop: "10%", width: "20%", position: "relative" } }), selectedClusterName && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { style: { marginTop: -8 } }, /* @__PURE__ */ React.createElement(CardHeader, { title: statusButtons(selectedClusterName), style: { marginTop: -4, marginBottom: 4, flexShrink: 0 }, action: actionButtons() }), /* @__PURE__ */ React.createElement(Grid, { container: true, style: { alignItems: "end" } }, /* @__PURE__ */ React.createElement(Grid, { item: true, style: { width: "66%" } }, /* @__PURE__ */ React.createElement(Typography, { style: { marginLeft: 14 } }, /* @__PURE__ */ React.createElement(ObjectSelector, { cluster: validClusters.find((cluster) => cluster.name === selectedClusterName), onSelect: onSelectObject, disabled: selectedClusterName === "" || started || paused.current, selectedNamespaces, selectedPodNames, selectedContainerNames, scope: InstanceConfigScopeEnum.STREAM }))), /* @__PURE__ */ React.createElement(Grid, { item: true, style: { width: "33%", marginLeft: 0, marginBottom: 6, maxWidth: "33%" } }, /* @__PURE__ */ React.createElement(FormControl, { style: { width: "100%" } }, /* @__PURE__ */ React.createElement(Select, { value: selectedMetrics, MenuProps: { variant: "menu" }, multiple: true, onChange: onMetricsChange, renderValue: (selected) => selected.join(", ").substring(0, 40) + "...", disabled: selectedClusterName === "" || selectedNamespaces.length === 0 || started }, allMetrics.map(
541
+ (m) => /* @__PURE__ */ React.createElement(MenuItem, { key: m.metric, value: m.metric, style: { marginTop: "-8px", marginBottom: "-8px" } }, /* @__PURE__ */ React.createElement(Checkbox, { checked: selectedMetrics.includes(m.metric), style: { marginTop: "-8px", marginBottom: "-8px" } }), /* @__PURE__ */ React.createElement(Typography, { style: { marginTop: "-8px", marginBottom: "-8px" } }, m.metric))
542
+ ))))), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, { style: { overflow: "auto" } }, showMetrics(kwirthMetricsOptionsRef.current)))))), showStatusDialog && /* @__PURE__ */ React.createElement(StatusLog, { level: statusLevel, onClose: () => setShowStatusDialog(false), statusMessages, onClear: statusClear }));
523
543
  };
524
544
 
525
545
  export { EntityKwirthMetricsContent };