start adding tests

This commit is contained in:
Manabu Mccloskey 2023-06-27 16:27:25 -07:00
parent cf2bdf9d9b
commit afc86729c3
4 changed files with 440 additions and 23 deletions

View file

@ -0,0 +1,103 @@
import {DiscoveryApi} from "@backstage/core-plugin-api";
import {ArgoWorkflows} from "./ArgoWorkflows";
import {KubernetesApi} from "@backstage/plugin-kubernetes";
import {MockConfigApi, MockFetchApi} from "@backstage/test-utils";
import {FrontendHostDiscovery} from "@backstage/core-app-api";
import {UserIdentity} from "@backstage/core-components";
import {inProgress} from "../test-data/in-progress";
describe('ArgoWorkflowsClient', () => {
const mockDiscoveryApi: jest.Mocked<DiscoveryApi> = {
getBaseUrl: jest.fn().mockImplementation((id) => {
return Promise.resolve(`https://backstage.io/${id}`)
})
}
const mockConfigApi = new MockConfigApi({
app: { baseUrl: 'https://backstage.io'}
})
const noopFetchApi = new MockFetchApi({baseImplementation: 'none'})
const mockKClient: jest.Mocked<KubernetesApi> = {
getObjectsByEntity: jest.fn(),
getClusters: jest.fn(),
getWorkloadsByEntity: jest.fn(),
getCustomObjectsByEntity: jest.fn(),
proxy: jest.fn()
}
beforeAll( () => {
jest.spyOn(FrontendHostDiscovery.prototype, 'getBaseUrl')
.mockImplementation((id) => {
return Promise.resolve(`https://backstage.io/${id}`)
})
jest.spyOn(UserIdentity.prototype, 'getCredentials')
.mockImplementation( () => {
return Promise.resolve({token: 'abc'})
})
})
afterEach(() => {
jest.clearAllMocks()
})
it('can fetch from k8s', async () => {
mockKClient.proxy.mockResolvedValue(
{
status: 200,
ok: true,
text: async () => (JSON.stringify(inProgress))
} as Response
)
const a = new ArgoWorkflows(mockDiscoveryApi, mockKClient, mockConfigApi, noopFetchApi)
const spy = jest.spyOn(mockKClient, "proxy")
const resp = await a.getWorkflowsFromK8s("abc", "default", "my=env")
expect(resp.items.length).toBe(1)
expect(spy).toHaveBeenCalledWith({
clusterName: 'abc',
path: "/apis/argoproj.io/v1alpha1/namespaces/default/workflows?timeoutSeconds=30&labelSelector=my%3Denv",
})
})
it('can fetch from default k8s cluster', async () => {
mockKClient.proxy.mockResolvedValue(
{
status: 200,
ok: true,
text: async () => (JSON.stringify(inProgress))
} as Response
)
mockKClient.getClusters.mockResolvedValue(
[
{
name: 'cluster-1',
authProvider: 'provider-1'
}
]
)
const a = new ArgoWorkflows(mockDiscoveryApi, mockKClient, mockConfigApi, noopFetchApi)
const spy = jest.spyOn(a, "getCluster")
const resp = await a.getWorkflowsFromK8s(undefined, "default", "my=env")
expect(resp.items.length).toBe(1)
expect(spy).toHaveBeenCalled()
})
it('non ok status returned', async () => {
mockKClient.proxy.mockResolvedValue(
{
status: 500,
ok: false,
statusText: "something went wrong",
text: async () => ("oh no")
} as Response
)
const a = new ArgoWorkflows(mockDiscoveryApi, mockKClient, mockConfigApi, noopFetchApi)
await expect(a.getWorkflowsFromK8s("abc", "default", 'not used'))
.rejects.toEqual("failed to fetch resources: 500, something went wrong, oh no")
})
})

View file

@ -1,4 +1,4 @@
import {ConfigApi, DiscoveryApi, IdentityApi} from "@backstage/core-plugin-api";
import {ConfigApi, DiscoveryApi, FetchApi} from "@backstage/core-plugin-api";
import {KubernetesApi} from "@backstage/plugin-kubernetes";
import {IoArgoprojWorkflowV1alpha1WorkflowList} from "./generated";
import {ArgoWorkflowsApi} from "./index";
@ -14,13 +14,13 @@ export class ArgoWorkflows implements ArgoWorkflowsApi {
discoveryApi: DiscoveryApi
kubernetesApi: KubernetesApi
configApi: ConfigApi
identityApi: IdentityApi
fetchApi: FetchApi
constructor(discoveryApi: DiscoveryApi, kubernetesApi: KubernetesApi, configApi: ConfigApi, identityApi: IdentityApi) {
constructor(discoveryApi: DiscoveryApi, kubernetesApi: KubernetesApi, configApi: ConfigApi, fetchApi: FetchApi) {
this.discoveryApi = discoveryApi
this.kubernetesApi = kubernetesApi
this.configApi = configApi
this.identityApi = identityApi
this.fetchApi = fetchApi
}
async getWorkflowsFromK8s(clusterName: string | undefined, namespace: string | undefined, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
@ -39,7 +39,7 @@ export class ArgoWorkflows implements ArgoWorkflowsApi {
})
if (!resp.ok) {
return Promise.reject(`failed to fetch resources: ${resp.status}, ${resp.statusText}, ${await resp.json()}`)
return Promise.reject(`failed to fetch resources: ${resp.status}, ${resp.statusText}, ${await resp.text()}`)
}
// need validation
return JSON.parse(await resp.text()) as IoArgoprojWorkflowV1alpha1WorkflowList
@ -64,19 +64,7 @@ export class ArgoWorkflows implements ArgoWorkflowsApi {
if (labels) {
query.set(API_LABEL_SELECTOR, labels)
}
const { token } = await this.identityApi.getCredentials()
const headers = new Headers(
{
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
}
)
const resp = await fetch(`${url}?${query.toString()}`, {
headers: headers
})
const resp = await this.fetchApi.fetch(`${url}?${query.toString()}`, {})
if (!resp.ok) {
return Promise.reject(`failed to fetch resources: ${resp.status}, ${resp.statusText}, ${await resp.json()}`)

View file

@ -3,7 +3,7 @@ import {
createApiFactory,
createPlugin,
createRoutableExtension,
discoveryApiRef, identityApiRef
discoveryApiRef, fetchApiRef
} from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
@ -22,12 +22,13 @@ export const argoWorkflowsPlugin = createPlugin({
deps: {
discoveryApi: discoveryApiRef,
kubernetesApi: kubernetesApiRef,
identityApi: identityApiRef,
configApi: configApiRef},
configApi: configApiRef,
fetchApi: fetchApiRef,
},
factory: ({
discoveryApi, kubernetesApi, configApi, identityApi,
discoveryApi, kubernetesApi, configApi, fetchApi,
}) =>
new ArgoWorkflows(discoveryApi, kubernetesApi, configApi, identityApi)
new ArgoWorkflows(discoveryApi, kubernetesApi, configApi, fetchApi)
})
]
});

View file

@ -0,0 +1,325 @@
export const inProgress= {
"apiVersion": "v1",
"items": [
{
"apiVersion": "argoproj.io/v1alpha1",
"kind": "Workflow",
"metadata": {
"annotations": {
"workflows.argoproj.io/pod-name-format": "v2"
},
"creationTimestamp": "2023-06-27T21:41:33Z",
"generateName": "test-workflow-",
"generation": 3,
"labels": {
"backstage.io/kubernetes-id": "backstage",
"env": "dev",
"my": "label",
"workflows.argoproj.io/phase": "Running"
},
"name": "test-workflow-f49nr",
"namespace": "default",
"resourceVersion": "44977391",
"uid": "188b33ab-c877-4e04-901c-32babece9573"
},
"spec": {
"arguments": {
"parameters": [
{
"name": "message",
"value": "from workflow"
}
]
},
"workflowTemplateRef": {
"name": "workflow-template-whalesay-template"
}
},
"status": {
"artifactGCStatus": {
"notSpecified": true
},
"artifactRepositoryRef": {
"artifactRepository": {},
"default": true
},
"conditions": [
{
"status": "True",
"type": "PodRunning"
}
],
"finishedAt": null,
"nodes": {
"test-workflow-f49nr": {
"children": [
"test-workflow-f49nr-1432144569"
],
"displayName": "test-workflow-f49nr",
"finishedAt": null,
"id": "test-workflow-f49nr",
"inputs": {
"parameters": [
{
"name": "message",
"value": "from workflow"
}
]
},
"name": "test-workflow-f49nr",
"phase": "Running",
"progress": "1/2",
"startedAt": "2023-06-27T21:41:33Z",
"templateName": "whalesay-template",
"templateScope": "local/",
"type": "Steps"
},
"test-workflow-f49nr-1432144569": {
"boundaryID": "test-workflow-f49nr",
"children": [
"test-workflow-f49nr-1588075630",
"test-workflow-f49nr-2771663768"
],
"displayName": "[0]",
"finishedAt": null,
"id": "test-workflow-f49nr-1432144569",
"name": "test-workflow-f49nr[0]",
"phase": "Running",
"progress": "1/2",
"startedAt": "2023-06-27T21:41:33Z",
"templateScope": "local/",
"type": "StepGroup"
},
"test-workflow-f49nr-1588075630": {
"boundaryID": "test-workflow-f49nr",
"displayName": "whalesay3",
"finishedAt": "2023-06-27T21:41:37Z",
"hostNodeName": "ip-192-168-10-135.us-west-2.compute.internal",
"id": "test-workflow-f49nr-1588075630",
"inputs": {
"parameters": [
{
"name": "message",
"value": "from workflow"
}
]
},
"name": "test-workflow-f49nr[0].whalesay3",
"outputs": {
"exitCode": "0"
},
"phase": "Succeeded",
"progress": "1/1",
"resourcesDuration": {
"cpu": 4,
"memory": 4
},
"startedAt": "2023-06-27T21:41:33Z",
"templateName": "whalesay-template-3",
"templateScope": "local/",
"type": "Pod"
},
"test-workflow-f49nr-2771663768": {
"boundaryID": "test-workflow-f49nr",
"displayName": "sleep",
"finishedAt": null,
"hostNodeName": "ip-192-168-5-156.us-west-2.compute.internal",
"id": "test-workflow-f49nr-2771663768",
"name": "test-workflow-f49nr[0].sleep",
"phase": "Running",
"progress": "0/1",
"startedAt": "2023-06-27T21:41:33Z",
"templateName": "sleep",
"templateScope": "local/",
"type": "Pod"
}
},
"phase": "Running",
"progress": "1/2",
"resourcesDuration": {
"cpu": 4,
"memory": 4
},
"startedAt": "2023-06-27T21:41:33Z",
"storedTemplates": {
"namespaced/workflow-template-whalesay-template/sleep": {
"container": {
"args": [
"600"
],
"command": [
"sleep"
],
"image": "docker/whalesay",
"name": "",
"resources": {}
},
"inputs": {},
"metadata": {},
"name": "sleep",
"outputs": {}
},
"namespaced/workflow-template-whalesay-template/whalesay-template": {
"inputs": {
"parameters": [
{
"name": "message"
}
]
},
"metadata": {},
"name": "whalesay-template",
"outputs": {},
"steps": [
[
{
"arguments": {
"parameters": [
{
"name": "message",
"value": "{{inputs.parameters.message}}"
}
]
},
"name": "whalesay3",
"template": "whalesay-template-3"
},
{
"arguments": {},
"name": "sleep",
"template": "sleep"
}
]
]
},
"namespaced/workflow-template-whalesay-template/whalesay-template-3": {
"container": {
"args": [
"{{inputs.parameters.message}}"
],
"command": [
"cowsay"
],
"image": "docker/whalesay",
"name": "",
"resources": {}
},
"inputs": {
"parameters": [
{
"name": "message"
}
]
},
"metadata": {},
"name": "whalesay-template-3",
"outputs": {}
}
},
"storedWorkflowTemplateSpec": {
"arguments": {
"parameters": [
{
"name": "message",
"value": "from workflow"
}
]
},
"entrypoint": "whalesay-template",
"templates": [
{
"inputs": {
"parameters": [
{
"name": "message"
}
]
},
"metadata": {},
"name": "whalesay-template",
"outputs": {},
"steps": [
[
{
"arguments": {
"parameters": [
{
"name": "message",
"value": "{{inputs.parameters.message}}"
}
]
},
"name": "whalesay3",
"template": "whalesay-template-3"
},
{
"arguments": {},
"name": "sleep",
"template": "sleep"
}
]
]
},
{
"container": {
"args": [
"600"
],
"command": [
"sleep"
],
"image": "docker/whalesay",
"name": "",
"resources": {}
},
"inputs": {},
"metadata": {},
"name": "sleep",
"outputs": {}
},
{
"container": {
"args": [
"{{inputs.parameters.message}}"
],
"command": [
"cowsay"
],
"image": "docker/whalesay",
"name": "",
"resources": {}
},
"inputs": {
"parameters": [
{
"name": "message"
}
]
},
"metadata": {},
"name": "whalesay-template-3",
"outputs": {}
}
],
"ttlStrategy": {
"secondsAfterCompletion": 28800
},
"workflowMetadata": {
"labels": {
"env": "dev",
"my": "label"
}
},
"workflowTemplateRef": {
"name": "workflow-template-whalesay-template"
}
}
}
}
],
"kind": "List",
"metadata": {
"resourceVersion": ""
}
}