diff --git a/package.json b/package.json index 507a79f8..d32ea526 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "gen:proxying": "./scripts/generate-proxying.sh", "gen:serving": "./scripts/generate-serving.sh", "test": "yarn workspaces foreach -v -Aipt --exclude @hawtio/online-root --exclude \"@hawtio/*-app\" run test", + "test:watch": "yarn workspaces foreach -v -Aipt --exclude @hawtio/online-root --exclude \"@hawtio/*-app\" run test:watch", "deploy:k8s:namespace": "./scripts/kube-apply.sh deploy/k8s/namespace/", "deploy:k8s:cluster": "./scripts/kube-apply.sh deploy/k8s/cluster/", "deploy:openshift:namespace": "./scripts/kube-apply.sh deploy/openshift/namespace/", diff --git a/packages/kubernetes-api/jest.config.ts b/packages/kubernetes-api/jest.config.ts index ec6711ee..51c12519 100644 --- a/packages/kubernetes-api/jest.config.ts +++ b/packages/kubernetes-api/jest.config.ts @@ -3,7 +3,7 @@ import path from 'path' export default { preset: 'ts-jest', testEnvironment: 'jsdom', - silent: true, + silent: false, // Automatically clear mock calls and instances between every test clearMocks: true, @@ -18,6 +18,7 @@ export default { 'react-markdown': '/../../node_modules/react-markdown/react-markdown.min.js', '@patternfly/react-code-editor': path.resolve(__dirname, './src/__mocks__/codeEditorMock.js'), oauth4webapi: path.resolve(__dirname, './src/__mocks__/oauth4webapi.js'), + '@hawtio/react': path.resolve(__dirname, './src/__mocks__/@hawtioReact.js'), }, // The path to a module that runs some code to configure or set up the testing diff --git a/packages/kubernetes-api/src/__mocks__/@hawtioReact.js b/packages/kubernetes-api/src/__mocks__/@hawtioReact.js new file mode 100644 index 00000000..e74f13ba --- /dev/null +++ b/packages/kubernetes-api/src/__mocks__/@hawtioReact.js @@ -0,0 +1,18 @@ +class Logger { + #name + + static get(name) { + return new Logger(name) + } + + constructor(name) { + this.#name = name + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + debug(_text) { + /* no-op */ + } +} + +module.exports = { Logger } diff --git a/packages/kubernetes-api/src/client/namespace-client.test.ts b/packages/kubernetes-api/src/client/namespace-client.test.ts new file mode 100644 index 00000000..d522c9ea --- /dev/null +++ b/packages/kubernetes-api/src/client/namespace-client.test.ts @@ -0,0 +1,323 @@ +/* eslint-disable import/first */ +/* + * Need to disable rule due to using mock class that needs to be undefined + * before import command + */ + +import path from 'path' +import fs from 'fs' +import { KubeObject, KubePod } from '../globals' +import { TypeFilter } from '../filter' +import { SortOrder } from '../sort' +import { KOptions, ProcessDataCallback } from './globals' +import { getKey } from './support' + +// To be populated each test +let namespacedPods: KubePod[] = [] + +// To mock CollectionImpl and avoid any network connecting +class MockCollectionImpl { + constructor(private _options: KOptions) {} + + get connected(): boolean { + return true + } + + getKey() { + return getKey(this._options.kind, this._options.namespace, this._options.name) + } + + connect() { + // no-op + } + + watch(watchCb: ProcessDataCallback): ProcessDataCallback { + if (!this._options.name) { + // No name so this is a namespace client + setTimeout(() => { + watchCb(namespacedPods) + }, 100) + } else { + // Name defined so this is a named pod client + setTimeout(() => { + const namedPods = namespacedPods.filter(pod => pod.metadata?.name === this._options.name) + watchCb(namedPods) + }, 100) + } + + return watchCb + } + + unwatch(cb: ProcessDataCallback) { + // no-op + } + + destroy() { + // no-op + } +} + +jest.mock('./collection', () => { + return { + CollectionImpl: MockCollectionImpl, + } +}) + +// Import after the MockCollectionImpl class has been defined +import { NamespaceClient } from './namespace-client' + +function readPods(limit: number): KubePod[] { + const pods: KubePod[] = [] + for (let i = 0; i < limit; ++i) { + const podTestFile = `quarkus-example-pod${i + 1}.json` + const podJsonPath = path.resolve(__dirname, '..', 'testdata', podTestFile) + const podResourceJson = fs.readFileSync(podJsonPath, { encoding: 'utf8', flag: 'r' }) + const podResource = JSON.parse(podResourceJson) + pods.push(podResource as KubePod) + } + return pods +} + +const NAMESPACE = 'hawtio' + +let nc: NamespaceClient + +describe('namespace-client', () => { + afterEach(() => { + if (nc) nc.destroy() + }) + + test('getNamespace', () => { + const cb = (_: KubePod[], __: number) => { + // Nothing to do + } + + nc = new NamespaceClient(NAMESPACE, cb) + expect(nc.namespace).toBe(NAMESPACE) + }) + + test('not-connected-emptyJolokiaPods', () => { + const cb = (_: KubePod[], __: number) => { + // Nothing to do + } + + nc = new NamespaceClient(NAMESPACE, cb) + expect(nc.isConnected()).toEqual(false) + expect(nc.getJolokiaPods()).toEqual([]) + }) + + test('connect-default', done => { + const nsLimit = 3 + + // Read in the test pods + namespacedPods = readPods(2) + + // Callback used for the namespace client - use done() to restrict the test + const cb = (jolokiaPods: KubePod[], fullPodCount: number) => { + expect(fullPodCount).toBe(namespacedPods.length) + + // Expect jolokiaPods to be limited by the page limit + expect(jolokiaPods.length).toBe(namespacedPods.length > nsLimit ? nsLimit : namespacedPods.length) + + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1) + done() + } + + nc = new NamespaceClient(NAMESPACE, cb) + nc.connect(nsLimit) + expect(nc.isConnected()).toEqual(true) + }) + + test('connect-page-limited', done => { + const nsLimit = 3 + + // Read in the test pods + namespacedPods = readPods(4) + + // Callback used for the namespace client - use done() to restrict the test + const cb = (jolokiaPods: KubePod[], fullPodCount: number) => { + expect(fullPodCount).toBe(namespacedPods.length) + + // Expect jolokiaPods to be limited by the page limit + expect(jolokiaPods.length).toBe(namespacedPods.length > nsLimit ? nsLimit : namespacedPods.length) + + // Expect the first 3 pods to be present + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).not.toBe(-1) + + // Expect the 4th pod to not be present since it is on the 2nd page + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).toBe(-1) + + done() + } + + nc = new NamespaceClient(NAMESPACE, cb) + nc.connect(nsLimit) + expect(nc.isConnected()).toEqual(true) + }) + + test('connect-raise-the-page-limit', done => { + const nsLimit = 4 + + // Read in the test pods + namespacedPods = readPods(4) + + // Callback used for the namespace client - use done() to restrict the test + const cb = (jolokiaPods: KubePod[], fullPodCount: number) => { + expect(fullPodCount).toBe(namespacedPods.length) + + // Expect jolokiaPods to be limited by the page limit + expect(jolokiaPods.length).toBe(namespacedPods.length > nsLimit ? nsLimit : namespacedPods.length) + + // Expect the first 4 pods to all be present + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).not.toBe(-1) + + done() + } + + nc = new NamespaceClient(NAMESPACE, cb) + nc.connect(nsLimit) + expect(nc.isConnected()).toEqual(true) + }) + + test('connect-then-filter-4-3', done => { + const nsLimit = 3 + + // Read in the test pods + namespacedPods = readPods(4) + + // Callback used for the namespace client - use done() to restrict the test + const cb = (jolokiaPods: KubePod[], fullPodCount: number) => { + if (fullPodCount === 4) { + /* + * On return of connect() through the callback initiate the filter call + */ + nc.filter(new TypeFilter([], ['zzzzz'])) + return + } + + /* + * Since this was a filter we get a callback from notifyChange + * which is necessary if the case where the filter returns the + * same number of pods as the previous collection. However, we + * still need to wait for the 4th previously unwatched pod to + * be added to the collection and so call this callback again. + */ + if (jolokiaPods.length < 3) { + return + } + + // filter should only return 3 pods + expect(fullPodCount).toBe(3) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).not.toBe(-1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).not.toBe(-1) + + // Expect the first pod to not be present since it does not conform to the filter + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).toBe(-1) + + done() + } + + nc = new NamespaceClient(NAMESPACE, cb) + nc.connect(nsLimit) + expect(nc.isConnected()).toEqual(true) + }) + + test('connect-then-filter-4-1', done => { + const nsLimit = 3 + + // Read in the test pods + namespacedPods = readPods(4) + + // Callback used for the namespace client - use done() to restrict the test + const cb = (jolokiaPods: KubePod[], fullPodCount: number) => { + if (fullPodCount === 4) { + /* + * On return of connect() through the callback initiate the filter call + */ + nc.filter(new TypeFilter([], ['4wdlk'])) + return + } + + /* + * Since this was a filter we get a callback from notifyChange + * which is necessary if the case where the filter returns the + * same number of pods as the previous collection. However, we + * still need to wait for the 4th previously unwatched pod to + * be added to the collection and so call this callback again. + */ + if (jolokiaPods.length !== 1) { + return + } + + // filter should only return 3 pods + expect(fullPodCount).toBe(1) + + // Expect the first pod to be present since it only conforms to the filter + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).not.toBe(-1) + + done() + } + + nc = new NamespaceClient(NAMESPACE, cb) + nc.connect(nsLimit) + expect(nc.isConnected()).toEqual(true) + }) + + test('connect-then-sort-4', done => { + const nsLimit = 3 + + // Read in the test pods + namespacedPods = readPods(4) + + let firstCallbackComplete = false + + // Callback used for the namespace client - use done() to restrict the test + const cb = (jolokiaPods: KubePod[], fullPodCount: number) => { + if (!firstCallbackComplete) { + /* + * On return of connect() through the callback initiate the sort call + */ + firstCallbackComplete = true + nc.sort(SortOrder.DESC) + return + } + + /* + * Since this was a sort we get a callback from notifyChange + * which is necessary if the case where the sort returns the + * same number of pods as the previous collection. However, we + * still need to wait for the last previously unwatched pod to + * be added to the collection and so call this callback again. + */ + if (jolokiaPods.length < 3) { + return + } + + // paging should only return 3 pods in the right order + expect(jolokiaPods.length).toBe(3) + + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[1].metadata?.uid)).toBe(2) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[2].metadata?.uid)).toBe(1) + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[3].metadata?.uid)).toBe(0) + + /* + * Expect the first pod to not be present since it is now last + * due to the sort and not included in the first page + */ + expect(jolokiaPods.findIndex(pod => pod.metadata?.uid === namespacedPods[0].metadata?.uid)).toBe(-1) + + done() + } + + nc = new NamespaceClient(NAMESPACE, cb) + nc.connect(nsLimit) + expect(nc.isConnected()).toEqual(true) + }) +}) diff --git a/packages/kubernetes-api/src/client/support.test.ts b/packages/kubernetes-api/src/client/support.test.ts new file mode 100644 index 00000000..19ba05c4 --- /dev/null +++ b/packages/kubernetes-api/src/client/support.test.ts @@ -0,0 +1,55 @@ +import path from 'path' +import fs from 'fs' +import { compare, getKey, isKubeObject } from './support' +import { KubeObject, ObjectMeta } from '../globals' + +describe('support', () => { + test('getKey', () => { + const kind = 'pod' + const namespace = 'hawtio' + const name = 'camel-helloworld' + + expect(getKey(kind)).toEqual(`${kind}`) + expect(getKey(kind, namespace)).toEqual(`${namespace}-${kind}`) + expect(getKey(kind, namespace, name)).toEqual(`${name}-${namespace}-${kind}`) + }) + + test('isKubeObject', () => { + const podJsonPath = path.resolve(__dirname, '..', 'testdata', 'quarkus-example-pod1.json') + const podResourceJson = fs.readFileSync(podJsonPath, { encoding: 'utf8', flag: 'r' }) + const podResource = JSON.parse(podResourceJson) + expect(isKubeObject(podResource)).toBeTruthy() + + const nsJsonPath = path.resolve(__dirname, '..', 'testdata', 'quarkus-namespace.json') + const nsResourceJson = fs.readFileSync(nsJsonPath, { encoding: 'utf8', flag: 'r' }) + const nsResource = JSON.parse(nsResourceJson) + expect(isKubeObject(nsResource)).toBeTruthy() + + const genericObject = { name: 'test', age: 12 } + expect(isKubeObject(genericObject)).toBeFalsy() + + const emptyObject = {} + expect(isKubeObject(emptyObject)).toBeFalsy() + }) + + test('compare', () => { + const podJsonPath = path.resolve(__dirname, '..', 'testdata', 'quarkus-example-pod1.json') + const podResourceJson = fs.readFileSync(podJsonPath, { encoding: 'utf8', flag: 'r' }) + const pod = JSON.parse(podResourceJson) as KubeObject + expect(compare([pod], [pod])).toEqual({ added: [], modified: [], deleted: [] }) + + const podCopy = JSON.parse(podResourceJson) as KubeObject + const metadata = podCopy.metadata as ObjectMeta + metadata.name = 'change-it' + // pod and podCopy share the same uid + expect(compare([pod], [podCopy])).toEqual({ added: [], modified: [podCopy], deleted: [] }) + + const pod2JsonPath = path.resolve(__dirname, '..', 'testdata', 'quarkus-example-pod2.json') + const pod2ResourceJson = fs.readFileSync(pod2JsonPath, { encoding: 'utf8', flag: 'r' }) + const pod2 = JSON.parse(pod2ResourceJson) as KubeObject + + expect(compare([pod, pod], [pod, pod2])).toEqual({ added: [pod2], modified: [], deleted: [] }) + expect(compare([pod], [pod, pod2])).toEqual({ added: [pod2], modified: [], deleted: [] }) + expect(compare([pod], [pod2])).toEqual({ added: [pod2], modified: [], deleted: [pod] }) + }) +}) diff --git a/packages/kubernetes-api/src/index.ts b/packages/kubernetes-api/src/index.ts index f2326706..2146d4b5 100644 --- a/packages/kubernetes-api/src/index.ts +++ b/packages/kubernetes-api/src/index.ts @@ -14,19 +14,27 @@ export async function isK8ApiRegistered(): Promise { return await registerK8Api() } -export { - K8Actions, - JOLOKIA_PORT_QUERY -} from './globals' +export { K8Actions, JOLOKIA_PORT_QUERY } from './globals' export type { Paging, - KubeObject, KubeObjectList, - KubePod, KubeProject, KubePodsOrError, KubePodsByProject, - NamespaceSpec, NamespaceStatus, - Pod, PodCondition, PodSpec, PodStatus, - Container, ContainerPort, ContainerStatus, - ObjectMeta, OwnerReference + KubeObject, + KubeObjectList, + KubePod, + KubeProject, + KubePodsOrError, + KubePodsByProject, + NamespaceSpec, + NamespaceStatus, + Pod, + PodCondition, + PodSpec, + PodStatus, + Container, + ContainerPort, + ContainerStatus, + ObjectMeta, + OwnerReference, } from './globals' export * from './filter' diff --git a/packages/kubernetes-api/src/testdata/quarkus-example-pod1.json b/packages/kubernetes-api/src/testdata/quarkus-example-pod1.json new file mode 100644 index 00000000..4e32b37c --- /dev/null +++ b/packages/kubernetes-api/src/testdata/quarkus-example-pod1.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-12 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-12T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fbb" + }, + "name": "quarkus-helloworld-55944c8fbb-4wdlk", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-55944c8fbb", + "uid": "0e3d0595-018b-4f34-ae71-c437bbec3448" + } + ], + "resourceVersion": "5304757", + "uid": "a4100744-15c0-4ba0-b7e4-5e2d4ce6554d" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/kubernetes-api/src/testdata/quarkus-example-pod2.json b/packages/kubernetes-api/src/testdata/quarkus-example-pod2.json new file mode 100644 index 00000000..b461d16c --- /dev/null +++ b/packages/kubernetes-api/src/testdata/quarkus-example-pod2.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-cccccc", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-zzzzzzzzz", + "uid": "0e000005-018b-4f34-ae71-c437bbec0000" + } + ], + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce00000" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/kubernetes-api/src/testdata/quarkus-example-pod3.json b/packages/kubernetes-api/src/testdata/quarkus-example-pod3.json new file mode 100644 index 00000000..c6a2f029 --- /dev/null +++ b/packages/kubernetes-api/src/testdata/quarkus-example-pod3.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-dddddd", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-xxxxxxxxx", + "uid": "0e000005-018b-4f34-ae71-c437bbec1111" + } + ], + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce111111" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/kubernetes-api/src/testdata/quarkus-example-pod4.json b/packages/kubernetes-api/src/testdata/quarkus-example-pod4.json new file mode 100644 index 00000000..08d8d19b --- /dev/null +++ b/packages/kubernetes-api/src/testdata/quarkus-example-pod4.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-eeeeee", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-55944c8fbb", + "uid": "0e3d0595-018b-4f34-ae71-c437bbec3448" + } + ], + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce222222" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/kubernetes-api/src/testdata/quarkus-example-pod5.json b/packages/kubernetes-api/src/testdata/quarkus-example-pod5.json new file mode 100644 index 00000000..63ebdd97 --- /dev/null +++ b/packages/kubernetes-api/src/testdata/quarkus-example-pod5.json @@ -0,0 +1,225 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-eeeeee", + "namespace": "quarkus", + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce222222" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/kubernetes-api/src/testdata/quarkus-namespace.json b/packages/kubernetes-api/src/testdata/quarkus-namespace.json new file mode 100644 index 00000000..49a07048 --- /dev/null +++ b/packages/kubernetes-api/src/testdata/quarkus-namespace.json @@ -0,0 +1,31 @@ +{ + "apiVersion": "v1", + "kind": "Namespace", + "metadata": { + "annotations": { + "openshift.io/description": "", + "openshift.io/display-name": "", + "openshift.io/requester": "kubeadmin", + "openshift.io/sa.scc.mcs": "s0:c26,c10", + "openshift.io/sa.scc.supplemental-groups": "1000670000/10000", + "openshift.io/sa.scc.uid-range": "1000670000/10000" + }, + "creationTimestamp": "2024-09-11T12:46:04Z", + "labels": { + "kubernetes.io/metadata.name": "quarkus", + "pod-security.kubernetes.io/audit": "restricted", + "pod-security.kubernetes.io/audit-version": "v1.24", + "pod-security.kubernetes.io/warn": "restricted", + "pod-security.kubernetes.io/warn-version": "v1.24" + }, + "name": "quarkus", + "resourceVersion": "5080231", + "uid": "b6890d1c-f4f2-4ed9-b4a7-27d47a5b636c" + }, + "spec": { + "finalizers": ["kubernetes"] + }, + "status": { + "phase": "Active" + } +} diff --git a/packages/online-shell/jest.config.ts b/packages/online-shell/jest.config.ts new file mode 100644 index 00000000..51c12519 --- /dev/null +++ b/packages/online-shell/jest.config.ts @@ -0,0 +1,35 @@ +import path from 'path' + +export default { + preset: 'ts-jest', + testEnvironment: 'jsdom', + silent: false, + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Mocks necessary for dealing with jest issues, eg. + // "SyntaxError: Cannot use import statement outside a module" + // can happen if the react-code-editor entry here is removed. + moduleNameMapper: { + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|md)$': + '/src/__mocks__/fileMock.js', + '\\.(css|less)$': '/src/__mocks__/styleMock.js', + 'react-markdown': '/../../node_modules/react-markdown/react-markdown.min.js', + '@patternfly/react-code-editor': path.resolve(__dirname, './src/__mocks__/codeEditorMock.js'), + oauth4webapi: path.resolve(__dirname, './src/__mocks__/oauth4webapi.js'), + '@hawtio/react': path.resolve(__dirname, './src/__mocks__/@hawtioReact.js'), + }, + + // The path to a module that runs some code to configure or set up the testing + // framework before each test + // + // Necessary to avoid the error message "ReferenceError: jQuery is not defined" + setupFilesAfterEnv: ['/src/setupTests.ts'], + + testPathIgnorePatterns: ['/node_modules/', '../../node_modules'], + + transformIgnorePatterns: ['node_modules/(?!@patternfly/react-icons/dist/esm/icons)/'], + + coveragePathIgnorePatterns: ['node_modules/'], +} diff --git a/packages/online-shell/package.json b/packages/online-shell/package.json index 605a8c1a..f6b1c5db 100644 --- a/packages/online-shell/package.json +++ b/packages/online-shell/package.json @@ -23,6 +23,7 @@ "build": "yarn build:webpack", "build:webpack": "webpack --mode production --progress --config webpack.config.prod.js --output-public-path='/online/' --env PACKAGE_VERSION=$npm_package_version", "test": "jest --watchAll=false --passWithNoTests", + "test:watch": "jest --watch", "test:coverage": "yarn test --coverage", "analyze:webpack:dev": "webpack --mode development --analyze --progress --config webpack.config.dev.js --output-public-path='/online/'", "analyze:webpack:prod": "webpack --mode production --analyze --progress --config webpack.config.prod.js --output-public-path='/online/'" diff --git a/packages/online-shell/src/__mocks__/@hawtioReact.js b/packages/online-shell/src/__mocks__/@hawtioReact.js new file mode 100644 index 00000000..e74f13ba --- /dev/null +++ b/packages/online-shell/src/__mocks__/@hawtioReact.js @@ -0,0 +1,18 @@ +class Logger { + #name + + static get(name) { + return new Logger(name) + } + + constructor(name) { + this.#name = name + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + debug(_text) { + /* no-op */ + } +} + +module.exports = { Logger } diff --git a/packages/online-shell/src/__mocks__/codeEditorMock.js b/packages/online-shell/src/__mocks__/codeEditorMock.js new file mode 100644 index 00000000..c05ac841 --- /dev/null +++ b/packages/online-shell/src/__mocks__/codeEditorMock.js @@ -0,0 +1,13 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const React = require('react') + +exports.CodeEditor = CodeEditor +exports.Language = { + json: 'json', +} + +function CodeEditor(props) { + return React.createElement('textarea', { + defaultValue: props.code, + }) +} diff --git a/packages/online-shell/src/__mocks__/fileMock.js b/packages/online-shell/src/__mocks__/fileMock.js new file mode 100644 index 00000000..0e56c5b5 --- /dev/null +++ b/packages/online-shell/src/__mocks__/fileMock.js @@ -0,0 +1 @@ +module.exports = 'test-file-stub' diff --git a/packages/online-shell/src/__mocks__/oauth4webapi.js b/packages/online-shell/src/__mocks__/oauth4webapi.js new file mode 100644 index 00000000..4ba52ba2 --- /dev/null +++ b/packages/online-shell/src/__mocks__/oauth4webapi.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/packages/online-shell/src/__mocks__/styleMock.js b/packages/online-shell/src/__mocks__/styleMock.js new file mode 100644 index 00000000..4ba52ba2 --- /dev/null +++ b/packages/online-shell/src/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/packages/online-shell/src/discover/discover-project.test.ts b/packages/online-shell/src/discover/discover-project.test.ts new file mode 100644 index 00000000..ea20db35 --- /dev/null +++ b/packages/online-shell/src/discover/discover-project.test.ts @@ -0,0 +1,157 @@ +/* eslint-disable import/first */ +/* + * Need to disable rule due to using mock class that needs to be undefined + * before import command + */ +import fs from 'fs' +import path from 'path' + +import { KubePod, ObjectMeta, SortOrder } from '@hawtio/online-kubernetes-api' + +/** + * Mock out any references to the oauth module + */ +jest.mock('@hawtio/online-oauth', () => { + /* no-op */ +}) + +class MockManagementService {} + +class MockManagedPod { + constructor(private kubePod: KubePod) {} + + get metadata(): ObjectMeta | undefined { + return this.kubePod.metadata + } + + errorNotify() { + /* no-op */ + } +} + +jest.mock('@hawtio/online-management-api', () => { + return { + mgmtService: new MockManagementService(), + ManagedPod: MockManagedPod, + } +}) + +import { ManagedPod } from '@hawtio/online-management-api' +import { DiscoverProject } from './discover-project' + +function readPod(fileName: string): ManagedPod { + const podJsonPath = path.resolve(__dirname, '..', 'testdata', fileName) + const podResourceJson = fs.readFileSync(podJsonPath, { encoding: 'utf8', flag: 'r' }) + const podResource = JSON.parse(podResourceJson) + return new ManagedPod(podResource as KubePod) +} + +function readPods(limit: number): ManagedPod[] { + const pods: ManagedPod[] = [] + for (let i = 0; i < limit; ++i) { + const podTestFile = `quarkus-example-pod${i + 1}.json` + pods.push(readPod(podTestFile)) + } + return pods +} + +function owner(pod: ManagedPod): string { + const owner1Refs = pod.metadata?.ownerReferences || [] + expect(owner1Refs.length).toBe(1) + return owner1Refs[0].name +} + +const NAMESPACE = 'hawtio' + +describe('discover-project', () => { + test('name', () => { + const dp: DiscoverProject = new DiscoverProject(NAMESPACE, 0, []) + expect(dp.name).toBe(NAMESPACE) + }) + + test('fullPodCount', () => { + const dp: DiscoverProject = new DiscoverProject(NAMESPACE, 4, []) + expect(dp.fullPodCount).toBe(4) + }) + + test('pods-no-owner', () => { + const podTestFile = `quarkus-example-pod5.json` + const mPod = readPod(podTestFile) + const dp: DiscoverProject = new DiscoverProject(NAMESPACE, 1, [mPod]) + expect(dp.groups.length).toBe(0) + expect(dp.pods.length).toBe(1) + }) + + test('groups', () => { + const mPods = readPods(4) + const dp: DiscoverProject = new DiscoverProject(NAMESPACE, mPods.length, mPods) + expect(dp.groups.length).toBe(3) + dp.groups.forEach((group, idx) => { + if (idx === 0) { + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.length).toBe(2) + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.filter(pod => pod.name === mPods[0].metadata?.name).length).toBe(1) + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.filter(pod => pod.name === mPods[3].metadata?.name).length).toBe(1) + } else if (idx === 1) { + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.length).toBe(1) + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.filter(pod => pod.name === mPods[1].metadata?.name).length).toBe(1) + } else if (idx === 2) { + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.length).toBe(1) + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.filter(pod => pod.name === mPods[2].metadata?.name).length).toBe(1) + } + }) + }) + + test('sort-order', () => { + const mPods = readPods(4) + + const owner1Name = owner(mPods[0]) + const owner2Name = owner(mPods[1]) + const owner3Name = owner(mPods[2]) + + const sortOrder = SortOrder.DESC + const dp: DiscoverProject = new DiscoverProject(NAMESPACE, mPods.length, mPods, sortOrder) + expect(dp.groups.length).toBe(3) + + const posOfGrp1 = dp.groups.findIndex(group => group.name === owner1Name) + const posOfGrp2 = dp.groups.findIndex(group => group.name === owner2Name) + const posOfGrp3 = dp.groups.findIndex(group => group.name === owner3Name) + + // Sort order is descending by name so order should be + // [ group2, group3, group1 ] + expect(posOfGrp2 < posOfGrp3).toBeTruthy() + expect(posOfGrp2 < posOfGrp1).toBeTruthy() + expect(posOfGrp3 < posOfGrp1).toBeTruthy() + + // Check the order of pods is descending too + dp.groups.forEach(group => { + expect(group.name === owner1Name || group.name === owner2Name || group.name === owner3Name).toBeTruthy() + + switch (group.name) { + case owner1Name: { + // eslint-disable-next-line jest/no-conditional-expect + expect(group.replicas.length).toBe(2) + const posOfPod1 = group.replicas.findIndex(pod => pod.name === mPods[0].metadata?.name) + const posOfPod4 = group.replicas.findIndex(pod => pod.name === mPods[3].metadata?.name) + + // Sort order is descending by name so pod1 should be latter in array than pod4 + // eslint-disable-next-line jest/no-conditional-expect + expect(posOfPod1 > posOfPod4).toBeTruthy() + break + } + case owner2Name: + case owner3Name: + // Nothing to do given only 1 pod in these deployments + break + default: + throw new Error('fail(Deployment does not have expected name)') + } + }) + }) +}) diff --git a/packages/online-shell/src/setupTests.ts b/packages/online-shell/src/setupTests.ts new file mode 100644 index 00000000..acc5c4c4 --- /dev/null +++ b/packages/online-shell/src/setupTests.ts @@ -0,0 +1,28 @@ +/* eslint-disable no-console */ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom' +import fetchMock from 'jest-fetch-mock' +import $ from 'jquery' + +fetchMock.enableMocks() + +// Default mock response for every usage of fetch +fetchMock.mockResponse(req => { + console.log('Mock fetch:', req.url) + let res = '{}' + switch (req.url) { + case 'user': + res = '"public"' + break + default: + } + return Promise.resolve(res) +}) + +// To fix "jQuery is not defined" error +// eslint-disable-next-line @typescript-eslint/no-explicit-any +declare const global: any +global.$ = global.jQuery = $ diff --git a/packages/online-shell/src/testdata/quarkus-example-pod1.json b/packages/online-shell/src/testdata/quarkus-example-pod1.json new file mode 100644 index 00000000..4e32b37c --- /dev/null +++ b/packages/online-shell/src/testdata/quarkus-example-pod1.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-12 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-12T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fbb" + }, + "name": "quarkus-helloworld-55944c8fbb-4wdlk", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-55944c8fbb", + "uid": "0e3d0595-018b-4f34-ae71-c437bbec3448" + } + ], + "resourceVersion": "5304757", + "uid": "a4100744-15c0-4ba0-b7e4-5e2d4ce6554d" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/online-shell/src/testdata/quarkus-example-pod2.json b/packages/online-shell/src/testdata/quarkus-example-pod2.json new file mode 100644 index 00000000..b461d16c --- /dev/null +++ b/packages/online-shell/src/testdata/quarkus-example-pod2.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-cccccc", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-zzzzzzzzz", + "uid": "0e000005-018b-4f34-ae71-c437bbec0000" + } + ], + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce00000" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/online-shell/src/testdata/quarkus-example-pod3.json b/packages/online-shell/src/testdata/quarkus-example-pod3.json new file mode 100644 index 00000000..c6a2f029 --- /dev/null +++ b/packages/online-shell/src/testdata/quarkus-example-pod3.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-dddddd", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-xxxxxxxxx", + "uid": "0e000005-018b-4f34-ae71-c437bbec1111" + } + ], + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce111111" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/online-shell/src/testdata/quarkus-example-pod4.json b/packages/online-shell/src/testdata/quarkus-example-pod4.json new file mode 100644 index 00000000..08d8d19b --- /dev/null +++ b/packages/online-shell/src/testdata/quarkus-example-pod4.json @@ -0,0 +1,235 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-eeeeee", + "namespace": "quarkus", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "quarkus-helloworld-55944c8fbb", + "uid": "0e3d0595-018b-4f34-ae71-c437bbec3448" + } + ], + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce222222" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +} diff --git a/packages/online-shell/src/testdata/quarkus-example-pod5.json b/packages/online-shell/src/testdata/quarkus-example-pod5.json new file mode 100644 index 00000000..63ebdd97 --- /dev/null +++ b/packages/online-shell/src/testdata/quarkus-example-pod5.json @@ -0,0 +1,225 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "alpha.image.policy.openshift.io/resolve-names": "*", + "app.openshift.io/vcs-uri": "\u003c\u003cunknown\u003e\u003e", + "app.quarkus.io/build-timestamp": "2024-09-13 - 13:24:59 +0000", + "app.quarkus.io/commit-id": "6bf09e9862327624ebce91365ae3a9d8c7f56d9c", + "app.quarkus.io/quarkus-version": "3.14.2", + "k8s.ovn.org/pod-networks": "{\"default\":{\"ip_addresses\":[\"10.217.0.148/23\"],\"mac_address\":\"0a:58:0a:d9:00:94\",\"gateway_ips\":[\"10.217.0.1\"],\"routes\":[{\"dest\":\"10.217.0.0/22\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"10.217.4.0/23\",\"nextHop\":\"10.217.0.1\"},{\"dest\":\"100.64.0.0/16\",\"nextHop\":\"10.217.0.1\"}],\"ip_address\":\"10.217.0.148/23\",\"gateway_ip\":\"10.217.0.1\"}}", + "k8s.v1.cni.cncf.io/network-status": "[{\n \"name\": \"ovn-kubernetes\",\n \"interface\": \"eth0\",\n \"ips\": [\n \"10.217.0.148\"\n ],\n \"mac\": \"0a:58:0a:d9:00:94\",\n \"default\": true,\n \"dns\": {}\n}]", + "openshift.io/scc": "restricted-v2", + "seccomp.security.alpha.kubernetes.io/pod": "runtime/default" + }, + "creationTimestamp": "2024-09-13T13:26:38Z", + "generateName": "quarkus-helloworld-55944c8fbb-", + "labels": { + "app.kubernetes.io/managed-by": "quarkus", + "app.kubernetes.io/name": "quarkus-helloworld", + "app.kubernetes.io/version": "1.0.0-SNAPSHOT", + "app.openshift.io/runtime": "quarkus", + "pod-template-hash": "55944c8fff" + }, + "name": "quarkus-helloworld-zzzzzzzzzz-eeeeee", + "namespace": "quarkus", + "resourceVersion": "5304757", + "uid": "a0000000-15c0-4ba0-b7e4-5e2d4ce222222" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "JAVA_OPTS_APPEND", + "value": "-javaagent:lib/main/org.jolokia.jolokia-agent-jvm-2.1.0-javaagent.jar=protocol=https,host=*,port=8778,useSslClientAuthentication=true,caCert=/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt,clientPrincipal.1=cn=hawtio-online.hawtio.svc,extendedClientCheck=true,discoveryEnabled=false" + } + ], + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imagePullPolicy": "Always", + "name": "quarkus-helloworld", + "ports": [ + { + "containerPort": 8778, + "name": "jolokia", + "protocol": "TCP" + } + ], + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000670000 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "kube-api-access-tvfpg", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "imagePullSecrets": [ + { + "name": "default-dockercfg-lghf9" + } + ], + "nodeName": "crc", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "fsGroup": 1000670000, + "seLinuxOptions": { + "level": "s0:c26,c10" + }, + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "serviceAccount": "default", + "serviceAccountName": "default", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "kube-api-access-tvfpg", + "projected": { + "defaultMode": 420, + "sources": [ + { + "serviceAccountToken": { + "expirationSeconds": 3607, + "path": "token" + } + }, + { + "configMap": { + "items": [ + { + "key": "ca.crt", + "path": "ca.crt" + } + ], + "name": "kube-root-ca.crt" + } + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + }, + "path": "namespace" + } + ] + } + }, + { + "configMap": { + "items": [ + { + "key": "service-ca.crt", + "path": "service-ca.crt" + } + ], + "name": "openshift-service-ca.crt" + } + } + ] + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:42Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-09-12T13:26:38Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "cri-o://e4ed61c9b0bd04f85092d9bd4dbfdf17ecd509568a660905304cbe8c7fe295f1", + "image": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "imageID": "image-registry.openshift-image-registry.svc:5000/quarkus/quarkus-helloworld@sha256:a3acea0dd811f72a0519903226b4abcd9c5359f796c1aba8d82da12f4ccb1585", + "lastState": {}, + "name": "quarkus-helloworld", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2024-09-12T13:26:42Z" + } + } + } + ], + "hostIP": "192.168.126.11", + "hostIPs": [ + { + "ip": "192.168.126.11" + } + ], + "phase": "Running", + "podIP": "10.217.0.148", + "podIPs": [ + { + "ip": "10.217.0.148" + } + ], + "qosClass": "BestEffort", + "startTime": "2024-09-12T13:26:38Z" + } +}