@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.
- package/README.md +56 -15
- package/dist/api/KwirthLogClient.esm.js +4 -56
- package/dist/api/KwirthLogClient.esm.js.map +1 -1
- package/dist/api/types.esm.js.map +1 -1
- package/dist/components/{EntityKwirthLogContent/EntityKwirthLogContent.esm.js → EntityKwirthLogContent.esm.js} +120 -50
- package/dist/components/EntityKwirthLogContent.esm.js.map +1 -0
- package/dist/components/Options.esm.js +20 -0
- package/dist/components/Options.esm.js.map +1 -0
- package/dist/index.d.ts +16 -6
- package/dist/index.esm.js +4 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +1 -1
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js.map +1 -1
- package/package.json +2 -1
- package/dist/assets/kwirth-log-component-not-found.svg +0 -30
- package/dist/components/ClusterList/ClusterList.esm.js +0 -30
- package/dist/components/ClusterList/ClusterList.esm.js.map +0 -1
- package/dist/components/ComponentNotFound/ComponentNotFound.esm.js +0 -64
- package/dist/components/ComponentNotFound/ComponentNotFound.esm.js.map +0 -1
- package/dist/components/EntityKwirthLogContent/EntityKwirthLogContent.esm.js.map +0 -1
- package/dist/components/EntityKwirthLogContent/index.esm.js +0 -2
- package/dist/components/EntityKwirthLogContent/index.esm.js.map +0 -1
- package/dist/components/ObjectSelector/ObjectSelector.esm.js +0 -84
- package/dist/components/ObjectSelector/ObjectSelector.esm.js.map +0 -1
- package/dist/components/Options/Options.esm.js +0 -20
- package/dist/components/Options/Options.esm.js.map +0 -1
- package/dist/components/StatusLog/StatusLog.esm.js +0 -10
- 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
|
-
|
|
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
|
-
|
|
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
|

|
|
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
|

|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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":";;
|
|
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 '
|
|
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 {
|
|
10
|
-
import { ObjectSelector } from '
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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 '
|
|
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 [
|
|
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({
|
|
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 = (
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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 =
|
|
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.
|
|
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.
|
|
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 =
|
|
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 =
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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 &&
|
|
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(
|
|
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 };
|