@jfvilas/plugin-kwirth-log 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 (29) hide show
  1. package/README.md +56 -15
  2. package/dist/api/KwirthLogClient.esm.js +4 -56
  3. package/dist/api/KwirthLogClient.esm.js.map +1 -1
  4. package/dist/api/types.esm.js.map +1 -1
  5. package/dist/components/{EntityKwirthLogContent/EntityKwirthLogContent.esm.js → EntityKwirthLogContent.esm.js} +120 -50
  6. package/dist/components/EntityKwirthLogContent.esm.js.map +1 -0
  7. package/dist/components/Options.esm.js +20 -0
  8. package/dist/components/Options.esm.js.map +1 -0
  9. package/dist/index.d.ts +16 -6
  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/kwirth-log-component-not-found.svg +0 -30
  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 -64
  20. package/dist/components/ComponentNotFound/ComponentNotFound.esm.js.map +0 -1
  21. package/dist/components/EntityKwirthLogContent/EntityKwirthLogContent.esm.js.map +0 -1
  22. package/dist/components/EntityKwirthLogContent/index.esm.js +0 -2
  23. package/dist/components/EntityKwirthLogContent/index.esm.js.map +0 -1
  24. package/dist/components/ObjectSelector/ObjectSelector.esm.js +0 -84
  25. package/dist/components/ObjectSelector/ObjectSelector.esm.js.map +0 -1
  26. package/dist/components/Options/Options.esm.js +0 -20
  27. package/dist/components/Options/Options.esm.js.map +0 -1
  28. package/dist/components/StatusLog/StatusLog.esm.js +0 -10
  29. package/dist/components/StatusLog/StatusLog.esm.js.map +0 -1
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Backstage frontend KwirthLog plugin
2
2
  This package is a Backstage frontend plugin for **viewing Kubernetes logs** in real-time (live-streaming) via Kwirth.
3
3
 
4
- **NOTE: KwithLog requires Kwirth vesrsion 0.3.160 or greater**
5
-
6
4
  This Backstage plugin allows you to live-stream Kubernetes logs associated to your Backstage entities directly inside your Backstage instance. It's very important to understand that for this plugin to work...:
7
5
 
8
6
  - You need to install the Kwirth [Backstage backend plugin](https://www.npmjs.com/package/@jfvilas/plugin-kwirth-backend).
@@ -13,6 +11,17 @@ Kwirth is a really-easy-to-use data-exporting system for Kubernetes that runs in
13
11
  You can access [Kwirth project here](https://github.com/jfvilas/kwirth).
14
12
 
15
13
 
14
+ ## Version compatibility
15
+ Following table shows version compatibility between this Kwirth Backstage plugin and Kwirth Core server.
16
+
17
+ | Plugin Kwirth version | Kwirth version |
18
+ |-|-|
19
+ |0.13.0|0.4.131|
20
+ |0.12.8|0.4.45|
21
+ |0.12.5|0.4.20|
22
+ |0.11.3|0.3.160|
23
+
24
+
16
25
  ## What is this plugin for?
17
26
  This Backstage plugin adds Backstage a feature for viewing real-time Kubernetes logs of your Backstage entities directly inside Backstage frontend application. The plugin will be enabled for any entity that is correctly tagged (according to Backstage Kubernetes core feature) and its correpsonding Kubernetes resources are found on any of the clusters that have been added to Backstage.
18
27
 
@@ -38,16 +47,6 @@ Let's explain this by following a user working sequence:
38
47
  If everyting is correctly configured and tagged, the user should see a list of clusters. When selecting a cluster, the user should see a list of namespaces where the entity is running.
39
48
 
40
49
 
41
- ## Version compatibility
42
- Following table shows version compatibility between this Kwirth Backstage plugin and Kwirth Core server.
43
-
44
- | Plugin Kwirth version | Kwirth version |
45
- |-|-|
46
- |0.11.3|0.3.160|
47
- |0.12.5|0.4.20|
48
- |0.12.8|0.4.45|
49
-
50
-
51
50
  ## Installation
52
51
  It's very simple and straightforward, it is in fact very similar to any other forntend Backstage plugin.
53
52
 
@@ -57,7 +56,7 @@ It's very simple and straightforward, it is in fact very similar to any other fo
57
56
 
58
57
  ```sh
59
58
  # From your Backstage root directory
60
- yarn --cwd packages/app add @jfvilas/plugin-kwirth-log @jfvilas/plugin-kwirth-common @jfvilas/kwirth-common
59
+ yarn --cwd packages/app add @jfvilas/plugin-kwirth-log @jfvilas/plugin-kwirth-frontend @jfvilas/plugin-kwirth-common @jfvilas/kwirth-common
61
60
  ```
62
61
 
63
62
  3. Make sure the [Kwirth backend plugin](https://www.npmjs.com/package/@jfvilas/plugin-kwirth-backend#configure) is installed and configured.
@@ -78,7 +77,7 @@ For Kwirth plugin to be usable on the frontend, you must tailor your Entity Page
78
77
  ```
79
78
 
80
79
  Then, add a tab to your EntityPage (the 'if' is optional, you can keep the 'KwirthLog' tab always visible if you prefer to do it that way).
81
- ````jsx
80
+ ```jsx
82
81
  // Note: Add to any other Pages as well (e.g. defaultEntityPage or webSiteEntityPage, for example)
83
82
  const serviceEntityPage = (
84
83
  <EntityLayout>
@@ -90,13 +89,30 @@ For Kwirth plugin to be usable on the frontend, you must tailor your Entity Page
90
89
  )
91
90
  ```
92
91
 
92
+ You can setup some default *viewing* options on the `EntityKwirthLogContent` component, so, when the entity loads the default options will be set. These options are:
93
+ - `formStart`
94
+ - `showNames`
95
+ - `showTimestamp`
96
+ - `followLog`
97
+ - `wrapLines`
98
+ (The meaning of these properties are explained at the end of this document)
99
+
100
+ For example, you could setup your default log stream like this:
101
+ ```jsx
102
+ ...
103
+ <EntityLayout.Route if={isKwirthAvailable} path="/kwirthlog" title="KwirthLog">
104
+ <EntityKwirthLogContent enableRestart={false} fromStrat={true} showTimestamp={true} wrapLines={true} />
105
+ </EntityLayout.Route>
106
+ ...
107
+ ```
108
+
93
109
  2. Label your catalog-info according to one of these two startegies:
94
110
 
95
111
  - **Strategy 1: one-to-one**. Add `backstage.io/kubernetes-id` annotation to your `catalog-info.yaml` for the entities deployed to Kubernetes you want to work with on Backstage. This is the same annotation that the Kubernetes core plugin uses, so, maybe you already have added it to your components. Exmaple:
96
112
 
97
113
  ```yaml
98
114
  metadata:
99
- annotaations:
115
+ annotations:
100
116
  backstage.io/kubernetes-id: entity001
101
117
  ```
102
118
 
@@ -166,6 +182,13 @@ Now you will see your log refreshing in real-time. If you selected more than one
166
182
 
167
183
  ![running](https://raw.githubusercontent.com/jfvilas/plugin-kwirth-log/master/images/kwirthlog-running.png)
168
184
 
185
+ ### Filtering
186
+ When the log stream starts you will see a filter text field you can use for filtering messages. The use is as follows:
187
+ - If you type something, the messages (or the pod names or the container names) are expected to match that text.
188
+ - If you select `Aa`, the text match will be performed taking **casing** into account.
189
+ - If you select `.*` then the text entered will be treated as a regular expression.
190
+ - You can select *casing* and *regex* if you want regexes to match taking casing into account.
191
+
169
192
  Feel free to open issues and ask for more features.
170
193
 
171
194
  ## Status information
@@ -182,3 +205,21 @@ This is how it feels:
182
205
 
183
206
  If you click on one of the status icons when theyr are enableds (coloured), you will see the detail of the status.
184
207
  ![status detail](https://raw.githubusercontent.com/jfvilas/plugin-kwirth-log/master/images/kwirthlog-status-detail.png)
208
+
209
+ ## Log stream options
210
+ The log streaming system implemented in KwirthLog has some configuration options whose meaning is explained below.
211
+
212
+ ### From start (`fromStart`)
213
+ If checked, the log streams will be retieved since pod start time. Please be careful with this option, it may impact your browser performance.
214
+
215
+ ### Show message time (`showTimestamp`)
216
+ Decide whether to show timestamps next to messages or not. Usually your application logs messages with timestamp, but it this doesn't occur you can add message time (as provided by Kubernetes) to your messages.
217
+
218
+ ### Show pod names (`showNames`)
219
+ Show pod names and container names next to messages. You may need to use this option if you are showing messages from different sources.
220
+
221
+ ### Follow log stream (`followLog`)
222
+ If you activate this option, the log stream wil move to the end wehn a new messages arrives (it's like a `tail -f`).
223
+
224
+ ### Wrap log lines (`wrapLines`)
225
+ Normally, KwirthLog will add hotizontal and vertical scrollers as needed in order to preserve original log lines, but you can activate this option to wrap lines and not to show horizontal scroller.
@@ -1,4 +1,4 @@
1
- import { getVersion, getResources, requestAccess } from '@jfvilas/plugin-kwirth-common';
1
+ import { getVersion, getInfo, getResources, requestAccess } from '@jfvilas/plugin-kwirth-common';
2
2
 
3
3
  class KwirthLogClient {
4
4
  discoveryApi;
@@ -12,70 +12,18 @@ class KwirthLogClient {
12
12
  * @param entity
13
13
  * @returns an array of clusters (with their correpsonding info) and a pod list for each, where the entity has been dicovered
14
14
  */
15
- // +++ test
16
15
  async getVersion() {
17
16
  return getVersion(this.discoveryApi, this.fetchApi);
18
17
  }
19
- // async getVersion(): Promise<string> {
20
- // try {
21
- // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')
22
- // const targetUrl = `${baseUrl}/version`
23
- // const result = await this.fetchApi.fetch(targetUrl)
24
- // const data = await result.json()
25
- // if (!result.ok) {
26
- // throw new Error(`getVersion error: not ok`)
27
- // }
28
- // return data.version
29
- // }
30
- // catch (err) {
31
- // throw new Error(`getVersion error: ${err}`)
32
- // }
33
- // }
34
- // move to common
35
- // async getResources(entity:Entity): Promise<ClusterValidPods> {
36
- // try {
37
- // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')
38
- // const targetUrl = `${baseUrl}/start`
39
- // var payload=JSON.stringify(entity)
40
- // const result = await this.fetchApi.fetch(targetUrl, {method:'POST', body:payload, headers:{'Content-Type':'application/json'}})
41
- // const data = await result.json() as ClusterValidPods
42
- // if (!result.ok) {
43
- // throw new Error(`getResources error: not ok`)
44
- // }
45
- // return data
46
- // }
47
- // catch (err) {
48
- // throw new Error(`getResources error: ${err}`)
49
- // }
50
- // }
18
+ async getInfo() {
19
+ return getInfo(this.discoveryApi, this.fetchApi);
20
+ }
51
21
  async getResources(entity) {
52
22
  return getResources(this.discoveryApi, this.fetchApi, entity);
53
23
  }
54
24
  async requestAccess(entity, channel, scopes) {
55
25
  return requestAccess(this.discoveryApi, this.fetchApi, entity, channel, scopes);
56
26
  }
57
- // async requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]> {
58
- // try {
59
- // const baseUrl = await this.discoveryApi.getBaseUrl('kwirth')
60
- // var targetUrl:URL= new URL (`${baseUrl}/access`)
61
- // targetUrl.searchParams.append('scopes',scopes.join(','))
62
- // targetUrl.searchParams.append('channel',channel)
63
- // var payload=JSON.stringify(entity)
64
- // const result = await this.fetchApi.fetch(targetUrl, {method:'POST', body:payload, headers:{'Content-Type':'application/json'}})
65
- // const data = await result.json() as ClusterValidPods[]
66
- // // we reconstruct the 'Map' from string of arrays
67
- // for (var c of data) {
68
- // c.accessKeys = new Map(JSON.parse(((c as any).accessKeys)))
69
- // }
70
- // if (!result.ok) {
71
- // throw new Error(`requestAccess error: not ok`)
72
- // }
73
- // return data
74
- // }
75
- // catch (err) {
76
- // throw new Error(`requestAccess error: ${err}`)
77
- // }
78
- // }
79
27
  }
80
28
 
81
29
  export { KwirthLogClient };
@@ -1 +1 @@
1
- {"version":3,"file":"KwirthLogClient.esm.js","sources":["../../src/api/KwirthLogClient.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 { KwirthLogApi } 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 KwirthLogClientOptions {\r\n discoveryApi: DiscoveryApi\r\n fetchApi: FetchApi\r\n}\r\n\r\nexport class KwirthLogClient implements KwirthLogApi {\r\n private readonly discoveryApi: DiscoveryApi\r\n private readonly fetchApi: FetchApi\r\n\r\n constructor(options: KwirthLogClientOptions) {\r\n this.discoveryApi = options.discoveryApi\r\n this.fetchApi = options.fetchApi\r\n }\r\n\r\n /**\r\n * \r\n * @param entity \r\n * @returns an array of clusters (with their correpsonding info) and a pod list for each, where the entity has been dicovered\r\n */\r\n // +++ test\r\n async getVersion() : Promise<string> {\r\n return getVersion(this.discoveryApi, this.fetchApi)\r\n }\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 // move to common\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 // 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,eAAwC,CAAA;AAAA,EAChC,YAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAiC,EAAA;AACzC,IAAA,IAAA,CAAK,eAAe,OAAQ,CAAA,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA;AAAA;AAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,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,EAsCA,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;AA2BJ;;;;"}
1
+ {"version":3,"file":"KwirthLogClient.esm.js","sources":["../../src/api/KwirthLogClient.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 { KwirthLogApi } 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 KwirthLogClientOptions {\r\n discoveryApi: DiscoveryApi\r\n fetchApi: FetchApi\r\n}\r\n\r\nexport class KwirthLogClient implements KwirthLogApi {\r\n private readonly discoveryApi: DiscoveryApi\r\n private readonly fetchApi: FetchApi\r\n\r\n constructor(options: KwirthLogClientOptions) {\r\n this.discoveryApi = options.discoveryApi\r\n this.fetchApi = options.fetchApi\r\n }\r\n\r\n /**\r\n * \r\n * @param entity \r\n * @returns an array of clusters (with their correpsonding info) and a pod list for each, where the entity has been dicovered\r\n */\r\n async getVersion() : Promise<string> {\r\n return getVersion(this.discoveryApi, this.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 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,eAAA,CAAwC;AAAA,EAChC,YAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAA,EAAiC;AACzC,IAAA,IAAA,CAAK,eAAe,OAAA,CAAQ,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAA,GAA+B;AACjC,IAAA,OAAO,UAAA,CAAW,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,QAAQ,CAAA;AAAA,EACtD;AAAA,EAEA,MAAM,OAAA,GAAkC;AACpC,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,YAAA,EAAc,IAAA,CAAK,QAAQ,CAAA;AAAA,EACnD;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 KwirthLogApi {\r\n getResources(entity:Entity): Promise<any>\r\n requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]>\r\n getVersion(): Promise<any>\r\n}\r\n\r\nexport const kwirthLogApiRef = createApiRef<KwirthLogApi>({\r\n id: 'plugin.kwirthlog.api',\r\n})\r\n"],"names":[],"mappings":";;AA0BO,MAAM,kBAAkB,YAA2B,CAAA;AAAA,EACxD,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 } from '@jfvilas/plugin-kwirth-common'\r\n\r\nexport interface KwirthLogApi {\r\n getResources(entity:Entity): Promise<any>\r\n requestAccess(entity:Entity, channel:string, scopes:InstanceConfigScopeEnum[]): Promise<ClusterValidPods[]>\r\n getVersion(): Promise<any>\r\n getInfo(): any\r\n}\r\n\r\nexport const kwirthLogApiRef = createApiRef<KwirthLogApi>({\r\n id: 'plugin.kwirthlog.api',\r\n})\r\n"],"names":[],"mappings":";;AA2BO,MAAM,kBAAkB,YAAA,CAA2B;AAAA,EACxD,EAAA,EAAI;AACN,CAAC;;;;"}
@@ -2,16 +2,14 @@ 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 { kwirthLogApiRef } from '../../api/types.esm.js';
7
+ import { kwirthLogApiRef } from '../api/types.esm.js';
8
8
  import { SignalMessageLevelEnum, InstanceConfigScopeEnum, InstanceMessageChannelEnum, InstanceMessageTypeEnum, OpsCommandEnum, accessKeySerialize, InstanceMessageFlowEnum, InstanceMessageActionEnum, InstanceConfigViewEnum, InstanceConfigObjectEnum } from '@jfvilas/kwirth-common';
9
- import { ComponentNotFound, ErrorType } from '../ComponentNotFound/ComponentNotFound.esm.js';
10
- import { ObjectSelector } from '../ObjectSelector/ObjectSelector.esm.js';
11
- import { Options } from '../Options/Options.esm.js';
12
- import { ClusterList } from '../ClusterList/ClusterList.esm.js';
13
- import { StatusLog } from '../StatusLog/StatusLog.esm.js';
14
- import { Box, Grid, Card, CardHeader, CardContent } from '@material-ui/core';
9
+ import { Options } from './Options.esm.js';
10
+ import { ComponentNotFound, ErrorType, ClusterList, KwirthNews, ObjectSelector, StatusLog } from '@jfvilas/plugin-kwirth-frontend';
11
+ import { VERSION } from '../index.esm.js';
12
+ import { Box, Grid, Card, CardHeader, TextField, InputAdornment, CardContent } from '@material-ui/core';
15
13
  import Divider from '@material-ui/core/Divider';
16
14
  import IconButton from '@material-ui/core/IconButton';
17
15
  import Typography from '@material-ui/core/Typography';
@@ -22,7 +20,7 @@ import InfoIcon from '@material-ui/icons/Info';
22
20
  import WarningIcon from '@material-ui/icons/Warning';
23
21
  import ErrorIcon from '@material-ui/icons/Error';
24
22
  import DownloadIcon from '@material-ui/icons/CloudDownload';
25
- import KwirthLogLogo from '../../assets/kwirthlog-logo.svg';
23
+ import KwirthLogLogo from '../assets/kwirthlog-logo.svg';
26
24
  import RefreshIcon from '@material-ui/icons/Refresh';
27
25
 
28
26
  const LOG_MAX_MESSAGES = 1e3;
@@ -30,7 +28,7 @@ const EntityKwirthLogContent = (props) => {
30
28
  const { entity } = useEntity();
31
29
  const kwirthLogApi = useApi(kwirthLogApiRef);
32
30
  const alertApi = useApi(alertApiRef);
33
- const [resources, setResources] = useState([]);
31
+ const [validClusters, setResources] = useState([]);
34
32
  const [selectedClusterName, setSelectedClusterName] = useState("");
35
33
  const [selectedNamespaces, setSelectedNamespaces] = useState([]);
36
34
  const [selectedPodNames, setSelectedPodNames] = useState([]);
@@ -43,20 +41,33 @@ const EntityKwirthLogContent = (props) => {
43
41
  const [statusMessages, setStatusMessages] = useState([]);
44
42
  const [websocket, setWebsocket] = useState();
45
43
  const [instance, setInstance] = useState();
46
- const kwirthLogOptionsRef = useRef({ timestamp: false, follow: true, fromStart: false });
44
+ const kwirthLogOptionsRef = useRef({
45
+ fromStart: props.fromStart !== void 0 ? props.fromStart : false,
46
+ showTimestamp: props.showTimestamp !== void 0 ? props.showTimestamp : false,
47
+ showNames: props.showNames !== void 0 ? props.showNames : true,
48
+ followLog: props.followLog !== void 0 ? props.followLog : true,
49
+ wrapLines: props.wrapLines !== void 0 ? props.wrapLines : false
50
+ });
47
51
  const [showStatusDialog, setShowStatusDialog] = useState(false);
48
52
  const [statusLevel, setStatusLevel] = useState(SignalMessageLevelEnum.INFO);
49
53
  const preRef = useRef(null);
50
54
  const lastRef = useRef(null);
51
55
  const [backendVersion, setBackendVersion] = useState("");
56
+ const [backendInfo, setBackendInfo] = useState();
52
57
  const { loading, error } = useAsync(async () => {
53
58
  if (backendVersion === "") setBackendVersion(await kwirthLogApi.getVersion());
59
+ if (!backendInfo) setBackendInfo(await kwirthLogApi.getInfo());
54
60
  let reqScopes = [InstanceConfigScopeEnum.VIEW];
55
61
  if (props.enableRestart) reqScopes.push(InstanceConfigScopeEnum.RESTART);
56
62
  let data = await kwirthLogApi.requestAccess(entity, InstanceMessageChannelEnum.LOG, reqScopes);
57
63
  setResources(data);
58
64
  });
59
65
  const buffer = useRef(/* @__PURE__ */ new Map());
66
+ const [filter, setFilter] = useState("");
67
+ const [filterCasing, setFilterCasing] = useState(false);
68
+ const [filterRegex, setFilterRegex] = useState(false);
69
+ const adornmentSelected = { margin: 0, borderWidth: 1, borderStyle: "solid", borderColor: "gray", paddingLeft: 3, paddingRight: 3, backgroundColor: "gray", cursor: "pointer", color: "white" };
70
+ const adornmentNotSelected = { margin: 0, borderWidth: 1, borderStyle: "solid", borderColor: "#f0f0f0", backgroundColor: "#f0f0f0", paddingLeft: 3, paddingRight: 3, cursor: "pointer" };
60
71
  const clickStart = (options) => {
61
72
  if (!paused.current) {
62
73
  setStarted(true);
@@ -80,21 +91,32 @@ const EntityKwirthLogContent = (props) => {
80
91
  paused.current = false;
81
92
  stopLogViewer();
82
93
  };
83
- const onSelectCluster = (name) => {
84
- if (name) {
85
- setSelectedClusterName(name);
86
- setMessages([{
87
- type: InstanceMessageTypeEnum.SIGNAL,
88
- text: "Select namespace in order to decide which pod logs to view.",
89
- namespace: "",
90
- pod: "",
91
- container: ""
92
- }]);
93
- setSelectedNamespaces([]);
94
+ const onSelectCluster = (clusterName) => {
95
+ if (started) onClickStop();
96
+ if (clusterName) {
97
+ setSelectedClusterName(clusterName);
94
98
  setSelectedPodNames([]);
95
99
  setSelectedContainerNames([]);
96
100
  setStatusMessages([]);
97
- onClickStop();
101
+ let cluster = validClusters.find((cluster2) => cluster2.name === clusterName);
102
+ if (cluster && cluster.pods) {
103
+ let validNamespaces = Array.from(new Set(cluster.pods.map((pod) => pod.namespace)));
104
+ if (validNamespaces.length === 1) {
105
+ setSelectedNamespaces(validNamespaces);
106
+ let podList = getPodList(cluster.pods, validNamespaces);
107
+ setSelectedPodNames(podList.map((pod) => pod.name));
108
+ setSelectedContainerNames(getContainerList(cluster.pods, validNamespaces, podList.map((pod) => pod.name)));
109
+ } else {
110
+ setMessages([{
111
+ type: InstanceMessageTypeEnum.SIGNAL,
112
+ text: "Select namespace in order to decide which pod logs to view.",
113
+ namespace: "",
114
+ pod: "",
115
+ container: ""
116
+ }]);
117
+ setSelectedNamespaces([]);
118
+ }
119
+ }
98
120
  }
99
121
  };
100
122
  const processLogMessage = (wsEvent) => {
@@ -130,7 +152,7 @@ const EntityKwirthLogContent = (props) => {
130
152
  while (prev.length > LOG_MAX_MESSAGES - 1) {
131
153
  prev.splice(0, 1);
132
154
  }
133
- if (kwirthLogOptionsRef.current.follow && lastRef.current) lastRef.current.scrollIntoView({ behavior: "instant", block: "start" });
155
+ if (kwirthLogOptionsRef.current.followLog && lastRef.current) lastRef.current.scrollIntoView({ behavior: "instant", block: "start" });
134
156
  return [...prev, logLine];
135
157
  });
136
158
  }
@@ -142,24 +164,28 @@ const EntityKwirthLogContent = (props) => {
142
164
  setInstance(instanceMessage.instance);
143
165
  else {
144
166
  let signalMessage = instanceMessage;
145
- alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
167
+ if (signalMessage.text) {
168
+ alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
169
+ }
146
170
  }
147
171
  } else {
148
172
  let signalMessage = instanceMessage;
149
- addMessage(signalMessage.level, signalMessage.text);
150
- switch (signalMessage.level) {
151
- case SignalMessageLevelEnum.INFO:
152
- alertApi.post({ message: signalMessage.text, severity: "info", display: "transient" });
153
- break;
154
- case SignalMessageLevelEnum.WARNING:
155
- alertApi.post({ message: signalMessage.text, severity: "warning", display: "transient" });
156
- break;
157
- case SignalMessageLevelEnum.ERROR:
158
- alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
159
- break;
160
- default:
161
- alertApi.post({ message: signalMessage.text, severity: "success", display: "transient" });
162
- break;
173
+ if (signalMessage.text) {
174
+ addMessage(signalMessage.level, signalMessage.text);
175
+ switch (signalMessage.level) {
176
+ case SignalMessageLevelEnum.INFO:
177
+ alertApi.post({ message: signalMessage.text, severity: "info", display: "transient" });
178
+ break;
179
+ case SignalMessageLevelEnum.WARNING:
180
+ alertApi.post({ message: signalMessage.text, severity: "warning", display: "transient" });
181
+ break;
182
+ case SignalMessageLevelEnum.ERROR:
183
+ alertApi.post({ message: signalMessage.text, severity: "error", display: "transient" });
184
+ break;
185
+ default:
186
+ alertApi.post({ message: signalMessage.text, severity: "success", display: "transient" });
187
+ break;
188
+ }
163
189
  }
164
190
  }
165
191
  break;
@@ -203,12 +229,12 @@ const EntityKwirthLogContent = (props) => {
203
229
  }
204
230
  };
205
231
  const websocketOnOpen = (ws, options) => {
206
- let cluster = resources.find((cluster2) => cluster2.name === selectedClusterName);
232
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
207
233
  if (!cluster) {
208
234
  addMessage(SignalMessageLevelEnum.ERROR, "No cluster selected");
209
235
  return;
210
236
  }
211
- let pods = cluster.data.filter((p2) => selectedNamespaces.includes(p2.namespace));
237
+ let pods = cluster.pods.filter((p2) => selectedNamespaces.includes(p2.namespace));
212
238
  if (!pods) {
213
239
  addMessage(SignalMessageLevelEnum.ERROR, "No pods found");
214
240
  return;
@@ -238,7 +264,7 @@ const EntityKwirthLogContent = (props) => {
238
264
  pod: selectedPodNames.map((p2) => p2).join(","),
239
265
  container: containers.join(","),
240
266
  data: {
241
- timestamp: options.timestamp,
267
+ timestamp: options.showTimestamp,
242
268
  previous: false,
243
269
  maxMessages: LOG_MAX_MESSAGES,
244
270
  fromStart: options.fromStart
@@ -252,7 +278,7 @@ const EntityKwirthLogContent = (props) => {
252
278
  }
253
279
  };
254
280
  const startLogViewer = (options) => {
255
- let cluster = resources.find((cluster2) => cluster2.name === selectedClusterName);
281
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
256
282
  if (!cluster) {
257
283
  addMessage(SignalMessageLevelEnum.ERROR, "No cluster selected");
258
284
  return;
@@ -314,7 +340,7 @@ const EntityKwirthLogContent = (props) => {
314
340
  URL.revokeObjectURL(url);
315
341
  };
316
342
  const onClickRestart = () => {
317
- var cluster = resources.find((cluster2) => cluster2.name === selectedClusterName);
343
+ var cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
318
344
  if (!cluster) {
319
345
  addMessage(SignalMessageLevelEnum.ERROR, "No cluster selected");
320
346
  return;
@@ -328,7 +354,7 @@ const EntityKwirthLogContent = (props) => {
328
354
  addMessage(SignalMessageLevelEnum.ERROR, "No instance has been established");
329
355
  return;
330
356
  }
331
- let pods = cluster.data.filter((pod) => selectedNamespaces.includes(pod.namespace));
357
+ let pods = cluster.pods.filter((pod) => selectedNamespaces.includes(pod.namespace));
332
358
  for (let pod of pods) {
333
359
  let opsMessage = {
334
360
  msgtype: "opsmessage",
@@ -361,7 +387,7 @@ const EntityKwirthLogContent = (props) => {
361
387
  };
362
388
  const actionButtons = () => {
363
389
  let hasViewKey = false, hasRestartKey = false;
364
- let cluster = resources.find((cluster2) => cluster2.name === selectedClusterName);
390
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
365
391
  if (cluster) {
366
392
  hasViewKey = Boolean(cluster.accessKeys.get(InstanceConfigScopeEnum.VIEW));
367
393
  hasRestartKey = Boolean(cluster.accessKeys.get(InstanceConfigScopeEnum.RESTART));
@@ -373,7 +399,10 @@ const EntityKwirthLogContent = (props) => {
373
399
  setShowStatusDialog(true);
374
400
  setStatusLevel(level);
375
401
  };
376
- 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) ? "orange" : "#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" } }))));
402
+ const prepareText = (txt) => {
403
+ return txt ? txt.length > 25 ? txt.substring(0, 25) + "..." : txt : "N/A";
404
+ };
405
+ 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) ? "orange" : "#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" } }))));
377
406
  };
378
407
  const statusClear = (level) => {
379
408
  setStatusMessages(statusMessages.filter((m) => m.level !== level));
@@ -384,12 +413,40 @@ const EntityKwirthLogContent = (props) => {
384
413
  setSelectedPodNames(podNames);
385
414
  setSelectedContainerNames(containerNames);
386
415
  };
416
+ const onChangeFilter = (event) => {
417
+ setFilter(event.target?.value);
418
+ };
387
419
  const formatMessage = (logLine) => {
388
420
  if (!logLine.pod) {
389
421
  return /* @__PURE__ */ React.createElement(React.Fragment, null, logLine.text + "\n");
390
422
  }
423
+ if (filter !== "") {
424
+ if (filterCasing) {
425
+ if (filterRegex) {
426
+ try {
427
+ const regex = new RegExp(filter);
428
+ if (!regex.test(logLine.text) && !regex.test(logLine.pod) && !regex.test(logLine.container)) return /* @__PURE__ */ React.createElement(React.Fragment, null);
429
+ } catch {
430
+ return /* @__PURE__ */ React.createElement(React.Fragment, null);
431
+ }
432
+ } else {
433
+ if (!logLine.text.includes(filter) && !logLine.pod.includes(filter) && !logLine.container.includes(filter)) return /* @__PURE__ */ React.createElement(React.Fragment, null);
434
+ }
435
+ } else {
436
+ if (filterRegex) {
437
+ try {
438
+ const regex = new RegExp(filter.toLocaleLowerCase());
439
+ if (!regex.test(logLine.text.toLocaleLowerCase()) && !regex.test(logLine.pod.toLocaleLowerCase()) && !regex.test(logLine.container.toLocaleLowerCase())) return /* @__PURE__ */ React.createElement(React.Fragment, null);
440
+ } catch {
441
+ return /* @__PURE__ */ React.createElement(React.Fragment, null);
442
+ }
443
+ } else {
444
+ if (!logLine.text.toLocaleLowerCase().includes(filter.toLowerCase()) && !logLine.pod.toLocaleLowerCase().includes(filter.toLocaleLowerCase()) && !logLine.container.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) return /* @__PURE__ */ React.createElement(React.Fragment, null);
445
+ }
446
+ }
447
+ }
391
448
  let podPrefix = /* @__PURE__ */ React.createElement(React.Fragment, null);
392
- if (selectedPodNames.length !== 1) {
449
+ if (selectedPodNames.length !== 1 || kwirthLogOptionsRef.current.showNames) {
393
450
  podPrefix = /* @__PURE__ */ React.createElement("span", { style: { color: "green" } }, logLine.pod + " ");
394
451
  }
395
452
  let containerPrefix = /* @__PURE__ */ React.createElement(React.Fragment, null);
@@ -398,14 +455,27 @@ const EntityKwirthLogContent = (props) => {
398
455
  }
399
456
  return /* @__PURE__ */ React.createElement(React.Fragment, null, podPrefix, containerPrefix, logLine.text + "\n");
400
457
  };
401
- return /* @__PURE__ */ React.createElement(React.Fragment, null, 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/plugin-kwirth-log", annotation: [ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR] }), isKwirthAvailable(entity) && !loading && resources && resources.length === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_CLUSTERS, entity }), isKwirthAvailable(entity) && !loading && resources && resources.length > 0 && resources.reduce((sum, cluster) => sum + cluster.data.length, 0) === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_PODS, entity }), isKwirthAvailable(entity) && !loading && resources && resources.length > 0 && resources.reduce((sum, cluster) => sum + cluster.data.length, 0) > 0 && /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", height: "70vh" } }, /* @__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, selectedClusterName, onSelect: onSelectCluster }))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(Options, { options: kwirthLogOptionsRef.current, onChange: onChangeLogConfig, disabled: selectedContainerNames.length === 0 || started || paused.current }))))), /* @__PURE__ */ React.createElement(Box, { sx: { flexGrow: 1, flex: 1, overflow: "hidden", p: 1, maxWidth: "85%" } }, !selectedClusterName && /* @__PURE__ */ React.createElement("img", { src: KwirthLogLogo, 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, height: "100%", display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(
458
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, 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/plugin-kwirth-log", 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", height: "70vh" } }, /* @__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, { options: kwirthLogOptionsRef.current, onChange: onChangeLogConfig, disabled: selectedContainerNames.length === 0 || started || 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, flex: 1, overflow: "hidden", p: 1, marginLeft: "8px" } }, !selectedClusterName && /* @__PURE__ */ React.createElement("img", { src: KwirthLogLogo, 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, height: "100%", display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(
402
459
  CardHeader,
403
460
  {
404
461
  title: statusButtons(selectedClusterName),
405
462
  style: { marginTop: -4, marginBottom: 4, flexShrink: 0 },
406
463
  action: actionButtons()
407
464
  }
408
- ), /* @__PURE__ */ React.createElement(Typography, { style: { marginLeft: 16, marginBottom: 4 } }, /* @__PURE__ */ React.createElement(ObjectSelector, { cluster: resources.find((cluster) => cluster.name === selectedClusterName), onSelect: onSelectObject, disabled: selectedClusterName === "" || started || paused.current, selectedNamespaces, selectedPodNames, selectedContainerNames })), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, { style: { flexGrow: 1, overflow: "hidden", display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(Box, { style: { overflowY: "auto", overflowX: "auto", width: "100%", flexGrow: 1 } }, /* @__PURE__ */ React.createElement("pre", { ref: preRef }, messages.map((m) => formatMessage(m))), /* @__PURE__ */ React.createElement("span", { ref: lastRef }))))))), showStatusDialog && /* @__PURE__ */ React.createElement(StatusLog, { level: statusLevel, onClose: () => setShowStatusDialog(false), statusMessages, onClear: statusClear }));
465
+ ), /* @__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.VIEW }))), /* @__PURE__ */ React.createElement(Grid, { item: true, style: { width: "33%", marginLeft: 0 } }, /* @__PURE__ */ React.createElement(
466
+ TextField,
467
+ {
468
+ value: filter,
469
+ onChange: onChangeFilter,
470
+ label: "Filter",
471
+ fullWidth: true,
472
+ style: { marginBottom: 6, marginLeft: 0 },
473
+ disabled: !started,
474
+ InputProps: {
475
+ endAdornment: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InputAdornment, { position: "start", onClick: () => started && setFilterRegex(!filterRegex), style: { margin: 0 } }, /* @__PURE__ */ React.createElement(Typography, { style: filterRegex ? adornmentSelected : adornmentNotSelected }, ".*")), /* @__PURE__ */ React.createElement(InputAdornment, { position: "start", onClick: () => started && setFilterCasing(!filterCasing), style: { margin: 0, marginLeft: 1 } }, /* @__PURE__ */ React.createElement(Typography, { style: filterCasing ? adornmentSelected : adornmentNotSelected }, "Aa")))
476
+ }
477
+ }
478
+ ))), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, { style: { flexGrow: 1, overflow: "hidden", display: "flex", flexDirection: "column" } }, /* @__PURE__ */ React.createElement(Box, { style: { overflowY: "auto", width: "100%", flexGrow: 1 } }, /* @__PURE__ */ React.createElement("pre", { ref: preRef, style: { overflowX: kwirthLogOptionsRef.current.wrapLines ? "hidden" : "auto", whiteSpace: kwirthLogOptionsRef.current.wrapLines ? "pre-wrap" : "pre", wordBreak: kwirthLogOptionsRef.current.wrapLines ? "break-word" : "normal" } }, messages.map((m) => formatMessage(m))), /* @__PURE__ */ React.createElement("span", { ref: lastRef }))))))), showStatusDialog && /* @__PURE__ */ React.createElement(StatusLog, { level: statusLevel, onClose: () => setShowStatusDialog(false), statusMessages, onClear: statusClear }));
409
479
  };
410
480
 
411
481
  export { EntityKwirthLogContent };