add formater and fix formating
This commit is contained in:
parent
b500dbd903
commit
a0ba50d39f
19 changed files with 772 additions and 714 deletions
|
@ -1,3 +1,4 @@
|
|||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
|
||||
ignorePatterns: ["src/api/generated/models/**.ts"]
|
||||
module.exports = require("@backstage/cli/config/eslint-factory")(__dirname, {
|
||||
ignorePatterns: ["src/api/generated/models/**.ts"],
|
||||
extends: ["prettier"],
|
||||
});
|
||||
|
|
3
plugins/argo-workflows/.prettierignore
Normal file
3
plugins/argo-workflows/.prettierignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
build
|
||||
coverage
|
||||
src/api/generated
|
1
plugins/argo-workflows/.prettierrc.json
Normal file
1
plugins/argo-workflows/.prettierrc.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -6,10 +6,10 @@ This plugin displays your Argo Workflows in Backstage
|
|||
|
||||
## Getting started
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
Entities must be annotated with Kubernetes annotations. For example:
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
|
@ -22,69 +22,69 @@ metadata:
|
|||
```
|
||||
|
||||
Configure your Argo Workflows' instance base URL
|
||||
|
||||
```yaml
|
||||
argoWorkflows:
|
||||
baseUrl: https://my-argo-workflows.url
|
||||
baseUrl: https://my-argo-workflows.url
|
||||
```
|
||||
|
||||
|
||||
### Authentication
|
||||
This plugin supports two methods of authentication.
|
||||
|
||||
This plugin supports two methods of authentication.
|
||||
|
||||
#### Through Argo API with Service Account Token
|
||||
|
||||
This method uses a service account token to retrieve information from Argo API through the configured proxy endpoint.
|
||||
This method uses a service account token to retrieve information from Argo API through the configured proxy endpoint.
|
||||
|
||||
1. Create a service account and associated permissions. For this plugin to work, you need list, get, and watch verbs.
|
||||
for example create a file called `sa.yaml` with the following contents:
|
||||
1. Create a service account and associated permissions. For this plugin to work, you need list, get, and watch verbs.
|
||||
for example create a file called `sa.yaml` with the following contents:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin
|
||||
namespace: argo
|
||||
|
||||
---
|
||||
# This is a long-lived token intended to be used by the backstage proxy.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin-token
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: backstage-argo-workflows-plugin
|
||||
namespace: argo
|
||||
type: kubernetes.io/service-account-token
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin
|
||||
rules:
|
||||
- apiGroups: ["argoproj.io"]
|
||||
resources: ["workflows"]
|
||||
verbs: [ "get", "watch", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: backstage-argo-workflows-plugin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: backstage-argo-workflows-plugin
|
||||
namespace: argo
|
||||
```
|
||||
2. Apply them to your cluster
|
||||
---
|
||||
# This is a long-lived token intended to be used by the backstage proxy.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin-token
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: backstage-argo-workflows-plugin
|
||||
namespace: argo
|
||||
type: kubernetes.io/service-account-token
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin
|
||||
rules:
|
||||
- apiGroups: ["argoproj.io"]
|
||||
resources: ["workflows"]
|
||||
verbs: [ "get", "watch", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: backstage-argo-workflows-plugin
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: backstage-argo-workflows-plugin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: backstage-argo-workflows-plugin
|
||||
namespace: argo
|
||||
```
|
||||
2. Apply them to your cluster
|
||||
```bash
|
||||
kubectl apply -f sa.yaml
|
||||
```
|
||||
3. Configure [Backstage Proxy](https://backstage.io/docs/plugins/proxying). In this example we are instructing Backstage to get the token value from environment variable called `ARGO_WORKFLOWS_AUTH_TOKEN`
|
||||
3. Configure [Backstage Proxy](https://backstage.io/docs/plugins/proxying). In this example we are instructing Backstage to get the token value from environment variable called `ARGO_WORKFLOWS_AUTH_TOKEN`
|
||||
```yaml
|
||||
proxy:
|
||||
'/argo-workflows/api':
|
||||
"/argo-workflows/api":
|
||||
target: https://argo.a1.mccloman.people.aws.dev
|
||||
changeOrigin: true
|
||||
secure: true
|
||||
|
@ -92,32 +92,33 @@ for example create a file called `sa.yaml` with the following contents:
|
|||
Authorization:
|
||||
$env: ARGO_WORKFLOWS_AUTH_TOKEN
|
||||
```
|
||||
4. Grab the token value and make it available as an environment variable for your backstage backend.
|
||||
4. Grab the token value and make it available as an environment variable for your backstage backend.
|
||||
```bash
|
||||
ARGO_TOKEN="Bearer $(kubectl get secret -n argo backstage-argo-workflows-plugin-token -o=jsonpath='{.data.token}' | base64 --decode)"
|
||||
```
|
||||
If this is running in Kubernetes see this [documentation](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#define-container-environment-variables-using-secret-data).
|
||||
|
||||
|
||||
See [this documentation](https://argoproj.github.io/argo-workflows/access-token/) for more information on getting your token.
|
||||
|
||||
#### Using configured Kubernetes API
|
||||
|
||||
The plugin can use configured Kubernetes clusters to fetch resources instead of going through the Argo Workflows API
|
||||
The entity must be annotated correctly for it to work.
|
||||
|
||||
For example, for a Kubernetes cluster given in your `ap-config.yaml`
|
||||
|
||||
```yaml
|
||||
kubernetes:
|
||||
serviceLocatorMethod:
|
||||
type: 'multiTenant'
|
||||
type: "multiTenant"
|
||||
clusterLocatorMethods:
|
||||
- type: 'config'
|
||||
clusters:
|
||||
- url: https://abcd.gr7.us-west-2.eks.amazonaws.com:443
|
||||
name: my-cluster-1
|
||||
authProvider: 'serviceAccount'
|
||||
serviceAccountToken: eyJh
|
||||
caData: LS0t
|
||||
- type: "config"
|
||||
clusters:
|
||||
- url: https://abcd.gr7.us-west-2.eks.amazonaws.com:443
|
||||
name: my-cluster-1
|
||||
authProvider: "serviceAccount"
|
||||
serviceAccountToken: eyJh
|
||||
caData: LS0t
|
||||
```
|
||||
|
||||
For this configuration, the `argo-workflows/cluster-name` annotaton value must be `my-cluster-1`
|
||||
|
|
16
plugins/argo-workflows/config.d.ts
vendored
16
plugins/argo-workflows/config.d.ts
vendored
|
@ -1,10 +1,10 @@
|
|||
export interface Config {
|
||||
/** Optional configurations for the Argo Workflows plugin */
|
||||
argoWorkflows: {
|
||||
/**
|
||||
* The base url of the Argo Workflows installation.
|
||||
* @visibility frontend
|
||||
*/
|
||||
baseUrl: string;
|
||||
};
|
||||
/** Optional configurations for the Argo Workflows plugin */
|
||||
argoWorkflows: {
|
||||
/**
|
||||
* The base url of the Argo Workflows installation.
|
||||
* @visibility frontend
|
||||
*/
|
||||
baseUrl: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { argoWorkflowsPlugin, ArgoWorkflowsPage } from '../src/plugin';
|
||||
import React from "react";
|
||||
import { createDevApp } from "@backstage/dev-utils";
|
||||
import { argoWorkflowsPlugin, ArgoWorkflowsPage } from "../src/plugin";
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(argoWorkflowsPlugin)
|
||||
.addPage({
|
||||
element: <ArgoWorkflowsPage />,
|
||||
title: 'Root Page',
|
||||
path: '/argo-workflows'
|
||||
title: "Root Page",
|
||||
path: "/argo-workflows",
|
||||
})
|
||||
.render();
|
||||
|
|
|
@ -46,7 +46,9 @@
|
|||
"@testing-library/user-event": "^14.0.0",
|
||||
"@types/node": "*",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^1.0.0"
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
"msw": "^1.0.0",
|
||||
"prettier": "2.8.8"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
|
|
@ -1,103 +1,110 @@
|
|||
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";
|
||||
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" });
|
||||
|
||||
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(),
|
||||
};
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
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")
|
||||
})
|
||||
})
|
||||
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"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,85 +1,111 @@
|
|||
import {ConfigApi, DiscoveryApi, FetchApi} from "@backstage/core-plugin-api";
|
||||
import {KubernetesApi} from "@backstage/plugin-kubernetes";
|
||||
import {IoArgoprojWorkflowV1alpha1WorkflowList} from "./generated";
|
||||
import {ArgoWorkflowsApi} from "./index";
|
||||
import { ConfigApi, DiscoveryApi, FetchApi } from "@backstage/core-plugin-api";
|
||||
import { KubernetesApi } from "@backstage/plugin-kubernetes";
|
||||
import { IoArgoprojWorkflowV1alpha1WorkflowList } from "./generated";
|
||||
import { ArgoWorkflowsApi } from "./index";
|
||||
|
||||
const API_VERSION = 'argoproj.io/v1alpha1'
|
||||
const WORKFLOW_PLURAL = 'workflows'
|
||||
const DEFAULT_WORKFLOW_PROXY = '/argo-workflows/api'
|
||||
const API_LABEL_SELECTOR = 'listOptions.labelSelector'
|
||||
const API_TIMEOUT = "listOptions.timeoutSeconds"
|
||||
const K8s_API_TIMEOUT = "timeoutSeconds"
|
||||
const API_VERSION = "argoproj.io/v1alpha1";
|
||||
const WORKFLOW_PLURAL = "workflows";
|
||||
const DEFAULT_WORKFLOW_PROXY = "/argo-workflows/api";
|
||||
const API_LABEL_SELECTOR = "listOptions.labelSelector";
|
||||
const API_TIMEOUT = "listOptions.timeoutSeconds";
|
||||
const K8s_API_TIMEOUT = "timeoutSeconds";
|
||||
|
||||
export class ArgoWorkflows implements ArgoWorkflowsApi {
|
||||
discoveryApi: DiscoveryApi
|
||||
kubernetesApi: KubernetesApi
|
||||
configApi: ConfigApi
|
||||
discoveryApi: DiscoveryApi;
|
||||
kubernetesApi: KubernetesApi;
|
||||
configApi: ConfigApi;
|
||||
fetchApi: FetchApi;
|
||||
|
||||
constructor(
|
||||
discoveryApi: DiscoveryApi,
|
||||
kubernetesApi: KubernetesApi,
|
||||
configApi: ConfigApi,
|
||||
fetchApi: FetchApi
|
||||
) {
|
||||
this.discoveryApi = discoveryApi;
|
||||
this.kubernetesApi = kubernetesApi;
|
||||
this.configApi = configApi;
|
||||
this.fetchApi = fetchApi;
|
||||
}
|
||||
|
||||
constructor(discoveryApi: DiscoveryApi, kubernetesApi: KubernetesApi, configApi: ConfigApi, fetchApi: FetchApi) {
|
||||
this.discoveryApi = discoveryApi
|
||||
this.kubernetesApi = kubernetesApi
|
||||
this.configApi = configApi
|
||||
this.fetchApi = fetchApi
|
||||
async getWorkflowsFromK8s(
|
||||
clusterName: string | undefined,
|
||||
namespace: string | undefined,
|
||||
labels: string | undefined
|
||||
): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
|
||||
const ns = namespace !== undefined ? namespace : "default";
|
||||
const path = `/apis/${API_VERSION}/namespaces/${ns}/${WORKFLOW_PLURAL}`;
|
||||
const query = new URLSearchParams({
|
||||
[K8s_API_TIMEOUT]: "30",
|
||||
});
|
||||
if (labels) {
|
||||
query.set("labelSelector", labels);
|
||||
}
|
||||
// need limits and pagination
|
||||
const resp = await this.kubernetesApi.proxy({
|
||||
clusterName:
|
||||
clusterName !== undefined ? clusterName : await this.getCluster(),
|
||||
path: `${path}?${query.toString()}`,
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
return Promise.reject(
|
||||
`failed to fetch resources: ${resp.status}, ${
|
||||
resp.statusText
|
||||
}, ${await resp.text()}`
|
||||
);
|
||||
}
|
||||
// need validation
|
||||
return JSON.parse(
|
||||
await resp.text()
|
||||
) as IoArgoprojWorkflowV1alpha1WorkflowList;
|
||||
}
|
||||
|
||||
getWorkflows(
|
||||
clusterName: string | undefined,
|
||||
namespace: string,
|
||||
labels: string | undefined
|
||||
): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
|
||||
if (clusterName) {
|
||||
return this.getWorkflowsFromK8s(clusterName, namespace, labels);
|
||||
}
|
||||
return this.getWorkflowsFromProxy(namespace, labels);
|
||||
}
|
||||
|
||||
async getWorkflowsFromProxy(
|
||||
namespace: string | undefined,
|
||||
labels: string | undefined
|
||||
): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
|
||||
const proxyUrl = await this.discoveryApi.getBaseUrl("proxy");
|
||||
|
||||
const ns = namespace !== undefined ? namespace : "default";
|
||||
const url = `${proxyUrl}${DEFAULT_WORKFLOW_PROXY}/api/v1/workflows/${ns}`;
|
||||
|
||||
const query = new URLSearchParams({ [API_TIMEOUT]: "30" });
|
||||
if (labels) {
|
||||
query.set(API_LABEL_SELECTOR, labels);
|
||||
}
|
||||
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()}`
|
||||
);
|
||||
}
|
||||
|
||||
async getWorkflowsFromK8s(clusterName: string | undefined, namespace: string | undefined, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
|
||||
const ns = namespace !== undefined ? namespace : 'default'
|
||||
const path = `/apis/${API_VERSION}/namespaces/${ns}/${WORKFLOW_PLURAL}`
|
||||
const query = new URLSearchParams({
|
||||
[K8s_API_TIMEOUT]: "30"
|
||||
})
|
||||
if (labels) {
|
||||
query.set('labelSelector', labels)
|
||||
}
|
||||
// need limits and pagination
|
||||
const resp = await this.kubernetesApi.proxy({
|
||||
clusterName: clusterName !== undefined ? clusterName: await this.getCluster(),
|
||||
path: `${path}?${query.toString()}`
|
||||
})
|
||||
// need validation
|
||||
return Promise.resolve(
|
||||
JSON.parse(await resp.text()) as IoArgoprojWorkflowV1alpha1WorkflowList
|
||||
);
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
return Promise.reject(`failed to fetch resources: ${resp.status}, ${resp.statusText}, ${await resp.text()}`)
|
||||
}
|
||||
// need validation
|
||||
return JSON.parse(await resp.text()) as IoArgoprojWorkflowV1alpha1WorkflowList
|
||||
}
|
||||
|
||||
getWorkflows(clusterName: string | undefined, namespace: string, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
|
||||
if (clusterName) {
|
||||
return this.getWorkflowsFromK8s(clusterName, namespace, labels)
|
||||
}
|
||||
return this.getWorkflowsFromProxy(namespace, labels);
|
||||
}
|
||||
|
||||
async getWorkflowsFromProxy(namespace: string | undefined, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> {
|
||||
const proxyUrl = await this.discoveryApi.getBaseUrl('proxy')
|
||||
|
||||
const ns = namespace !== undefined ? namespace : 'default'
|
||||
const url = `${proxyUrl}${DEFAULT_WORKFLOW_PROXY}/api/v1/workflows/${ns}`
|
||||
|
||||
const query = new URLSearchParams(
|
||||
{[API_TIMEOUT]: "30"}
|
||||
)
|
||||
if (labels) {
|
||||
query.set(API_LABEL_SELECTOR, labels)
|
||||
}
|
||||
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()}`)
|
||||
}
|
||||
|
||||
// need validation
|
||||
return Promise.resolve(JSON.parse(await resp.text()) as IoArgoprojWorkflowV1alpha1WorkflowList);
|
||||
}
|
||||
|
||||
|
||||
async getCluster(): Promise<string> {
|
||||
const clusters = await this.kubernetesApi.getClusters()
|
||||
if (clusters.length > 0) {
|
||||
return Promise.resolve(clusters[0].name)
|
||||
}
|
||||
return Promise.reject("no clusters found in configuration")
|
||||
async getCluster(): Promise<string> {
|
||||
const clusters = await this.kubernetesApi.getClusters();
|
||||
if (clusters.length > 0) {
|
||||
return Promise.resolve(clusters[0].name);
|
||||
}
|
||||
return Promise.reject("no clusters found in configuration");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
import {
|
||||
createApiRef,
|
||||
DiscoveryApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { createApiRef, DiscoveryApi } from "@backstage/core-plugin-api";
|
||||
|
||||
import { KubernetesApi } from "@backstage/plugin-kubernetes";
|
||||
import { IoArgoprojWorkflowV1alpha1WorkflowList } from "./generated/";
|
||||
|
||||
export {ArgoWorkflows} from "./ArgoWorkflows";
|
||||
export { ArgoWorkflows } from "./ArgoWorkflows";
|
||||
|
||||
export const argoWorkflowsApiRef = createApiRef<ArgoWorkflowsApi>({
|
||||
id: 'plugin.argoworkflows',
|
||||
})
|
||||
id: "plugin.argoworkflows",
|
||||
});
|
||||
export interface ArgoWorkflowsApi {
|
||||
discoveryApi: DiscoveryApi
|
||||
kubernetesApi: KubernetesApi
|
||||
getWorkflowsFromK8s(clusterName: string, namespace: string | undefined, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList>
|
||||
getWorkflows(clusterName: string | undefined, namespace: string | undefined, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList>
|
||||
getWorkflowsFromProxy(namespace: string, labels: string | undefined): Promise<IoArgoprojWorkflowV1alpha1WorkflowList>
|
||||
discoveryApi: DiscoveryApi;
|
||||
kubernetesApi: KubernetesApi;
|
||||
getWorkflowsFromK8s(
|
||||
clusterName: string,
|
||||
namespace: string | undefined,
|
||||
labels: string | undefined
|
||||
): Promise<IoArgoprojWorkflowV1alpha1WorkflowList>;
|
||||
getWorkflows(
|
||||
clusterName: string | undefined,
|
||||
namespace: string | undefined,
|
||||
labels: string | undefined
|
||||
): Promise<IoArgoprojWorkflowV1alpha1WorkflowList>;
|
||||
getWorkflowsFromProxy(
|
||||
namespace: string,
|
||||
labels: string | undefined
|
||||
): Promise<IoArgoprojWorkflowV1alpha1WorkflowList>;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
|
||||
import React from 'react';
|
||||
import {Header, HeaderLabel, Page, Content, ContentHeader, SupportButton} from "@backstage/core-components";
|
||||
import {Grid} from "@material-ui/core";
|
||||
import {WorkflowOverviewComponent} from "../WorkflowOverview/WorkflowOverview";
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Header,
|
||||
HeaderLabel,
|
||||
Page,
|
||||
Content,
|
||||
ContentHeader,
|
||||
SupportButton,
|
||||
} from "@backstage/core-components";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import { WorkflowOverviewComponent } from "../WorkflowOverview/WorkflowOverview";
|
||||
|
||||
export const OverviewComponent = () => (
|
||||
<Page themeId="tool">
|
||||
<Header title="Argo Workflows">
|
||||
<HeaderLabel label="Lifecycle" value="Alpha" />
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentHeader title="Overview">
|
||||
<SupportButton>
|
||||
Overview of your Argo Workflows
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<Grid item>
|
||||
<WorkflowOverviewComponent />
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
)
|
||||
<Page themeId="tool">
|
||||
<Header title="Argo Workflows">
|
||||
<HeaderLabel label="Lifecycle" value="Alpha" />
|
||||
</Header>
|
||||
<Content>
|
||||
<ContentHeader title="Overview">
|
||||
<SupportButton>Overview of your Argo Workflows</SupportButton>
|
||||
</ContentHeader>
|
||||
<Grid item>
|
||||
<WorkflowOverviewComponent />
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
|
|
|
@ -1 +1 @@
|
|||
export {OverviewComponent} from "./Overview";
|
||||
export { OverviewComponent } from "./Overview";
|
||||
|
|
|
@ -1,99 +1,127 @@
|
|||
import {configApiRef, useApi} from "@backstage/core-plugin-api";
|
||||
import {argoWorkflowsApiRef} from "../../api";
|
||||
import { configApiRef, useApi } from "@backstage/core-plugin-api";
|
||||
import { argoWorkflowsApiRef } from "../../api";
|
||||
import useAsync from "react-use/lib/useAsync";
|
||||
import {Link, Progress, Table, TableColumn} from '@backstage/core-components'
|
||||
import { Link, Progress, Table, TableColumn } from "@backstage/core-components";
|
||||
import React from "react";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import {IoArgoprojWorkflowV1alpha1WorkflowList} from "../../api/generated";
|
||||
|
||||
import { useEntity } from "@backstage/plugin-catalog-react";
|
||||
import { IoArgoprojWorkflowV1alpha1WorkflowList } from "../../api/generated";
|
||||
|
||||
type TableData = {
|
||||
name: string
|
||||
namespace: string
|
||||
phase?: string
|
||||
progress?: string
|
||||
startedAt?: string
|
||||
finishedAt?: string
|
||||
}
|
||||
name: string;
|
||||
namespace: string;
|
||||
phase?: string;
|
||||
progress?: string;
|
||||
startedAt?: string;
|
||||
finishedAt?: string;
|
||||
};
|
||||
|
||||
export const WorkflowOverviewComponent = () => {
|
||||
const {entity} = useEntity()
|
||||
const apiClient = useApi(argoWorkflowsApiRef)
|
||||
const configApi = useApi(configApiRef)
|
||||
let argoWorkflowsBaseUrl = configApi.getOptionalString("argoWorkflows.baseUrl")
|
||||
if (argoWorkflowsBaseUrl && argoWorkflowsBaseUrl.endsWith("/")) {
|
||||
argoWorkflowsBaseUrl = argoWorkflowsBaseUrl.substring(0, argoWorkflowsBaseUrl.length - 1)
|
||||
}
|
||||
const { entity } = useEntity();
|
||||
const apiClient = useApi(argoWorkflowsApiRef);
|
||||
const configApi = useApi(configApiRef);
|
||||
let argoWorkflowsBaseUrl = configApi.getOptionalString(
|
||||
"argoWorkflows.baseUrl"
|
||||
);
|
||||
if (argoWorkflowsBaseUrl && argoWorkflowsBaseUrl.endsWith("/")) {
|
||||
argoWorkflowsBaseUrl = argoWorkflowsBaseUrl.substring(
|
||||
0,
|
||||
argoWorkflowsBaseUrl.length - 1
|
||||
);
|
||||
}
|
||||
|
||||
const ln = entity.metadata.annotations?.["backstage.io/kubernetes-namespace"];
|
||||
const ns = ln !== undefined ? ln : "default";
|
||||
const clusterName =
|
||||
entity.metadata.annotations?.["argo-workflows/cluster-name"];
|
||||
const k8sLabelSelector =
|
||||
entity.metadata.annotations?.["backstage.io/kubernetes-label-selector"];
|
||||
|
||||
const ln = entity.metadata.annotations?.['backstage.io/kubernetes-namespace']
|
||||
const ns = ln !== undefined ? ln : 'default'
|
||||
const clusterName = entity.metadata.annotations?.['argo-workflows/cluster-name']
|
||||
const k8sLabelSelector = entity.metadata.annotations?.['backstage.io/kubernetes-label-selector']
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{title: "Name", field: "name", render: (data: any | TableData, _): any => {
|
||||
if (data && argoWorkflowsBaseUrl) {
|
||||
return (<Link to={`${argoWorkflowsBaseUrl}/workflows/${data.namespace}/${data.name}`}>{data.name}</Link>)
|
||||
}
|
||||
return data.name
|
||||
}},
|
||||
{title: "Phase", field: "phase", cellStyle: (data, _) => {
|
||||
if (data === "Succeeded") {
|
||||
return {
|
||||
color: '#6CD75F',
|
||||
}
|
||||
}
|
||||
if (data === "Error" || data === "Failed") {
|
||||
return {
|
||||
color: '#DC3D5A'
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}},
|
||||
{title: "Progress", field: "progress"},
|
||||
{title: "StartTime", field: "startedAt", type: "datetime", defaultSort: 'desc'},
|
||||
{title: "EndTime", field: "finishedAt", type: "datetime"},
|
||||
{title: "Namespace", field: "namespace", type: "string"}
|
||||
]
|
||||
|
||||
const {value, loading, error} = useAsync(
|
||||
async (): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> => {
|
||||
return await apiClient.getWorkflows(clusterName, ns, k8sLabelSelector)
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: "Name",
|
||||
field: "name",
|
||||
render: (data: any | TableData, _): any => {
|
||||
if (data && argoWorkflowsBaseUrl) {
|
||||
return (
|
||||
<Link
|
||||
to={`${argoWorkflowsBaseUrl}/workflows/${data.namespace}/${data.name}`}
|
||||
>
|
||||
{data.name}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
)
|
||||
return data.name;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Phase",
|
||||
field: "phase",
|
||||
cellStyle: (data, _) => {
|
||||
if (data === "Succeeded") {
|
||||
return {
|
||||
color: "#6CD75F",
|
||||
};
|
||||
}
|
||||
if (data === "Error" || data === "Failed") {
|
||||
return {
|
||||
color: "#DC3D5A",
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
},
|
||||
{ title: "Progress", field: "progress" },
|
||||
{
|
||||
title: "StartTime",
|
||||
field: "startedAt",
|
||||
type: "datetime",
|
||||
defaultSort: "desc",
|
||||
},
|
||||
{ title: "EndTime", field: "finishedAt", type: "datetime" },
|
||||
{ title: "Namespace", field: "namespace", type: "string" },
|
||||
];
|
||||
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
} else if (error) {
|
||||
return <Alert severity="error">{error.message}</Alert>;
|
||||
const { value, loading, error } = useAsync(
|
||||
async (): Promise<IoArgoprojWorkflowV1alpha1WorkflowList> => {
|
||||
return await apiClient.getWorkflows(clusterName, ns, k8sLabelSelector);
|
||||
}
|
||||
);
|
||||
|
||||
const data = value?.items?.map( val => {
|
||||
return {
|
||||
name: val.metadata.name,
|
||||
namespace: val.metadata.namespace,
|
||||
phase: val.status?.phase,
|
||||
progress: val.status?.progress,
|
||||
startedAt: val.status?.startedAt,
|
||||
finishedAt: val.status?.finishedAt
|
||||
} as TableData
|
||||
})
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
} else if (error) {
|
||||
return <Alert severity="error">{error.message}</Alert>;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
return (
|
||||
<Table options={{
|
||||
paging: true,
|
||||
search: true,
|
||||
sorting: true,
|
||||
}}
|
||||
columns={columns}
|
||||
data={data.sort()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
const data = value?.items?.map((val) => {
|
||||
return {
|
||||
name: val.metadata.name,
|
||||
namespace: val.metadata.namespace,
|
||||
phase: val.status?.phase,
|
||||
progress: val.status?.progress,
|
||||
startedAt: val.status?.startedAt,
|
||||
finishedAt: val.status?.finishedAt,
|
||||
} as TableData;
|
||||
});
|
||||
|
||||
return <Alert severity="warning">No Workflows found with given filter. Check your entity's annotations.</Alert>
|
||||
if (data) {
|
||||
return (
|
||||
<Table
|
||||
options={{
|
||||
paging: true,
|
||||
search: true,
|
||||
sorting: true,
|
||||
}}
|
||||
columns={columns}
|
||||
data={data.sort()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
return (
|
||||
<Alert severity="warning">
|
||||
No Workflows found with given filter. Check your entity's annotations.
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
export { argoWorkflowsPlugin, ArgoWorkflowsPage } from './plugin';
|
||||
export { argoWorkflowsPlugin, ArgoWorkflowsPage } from "./plugin";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { argoWorkflowsPlugin } from './plugin';
|
||||
import { argoWorkflowsPlugin } from "./plugin";
|
||||
|
||||
describe('argo-workflows', () => {
|
||||
it('should export plugin', () => {
|
||||
describe("argo-workflows", () => {
|
||||
it("should export plugin", () => {
|
||||
expect(argoWorkflowsPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,43 +1,41 @@
|
|||
import {
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
discoveryApiRef, fetchApiRef
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
import { rootRouteRef } from './routes';
|
||||
import {ArgoWorkflows, argoWorkflowsApiRef} from "./api";
|
||||
import {kubernetesApiRef} from "@backstage/plugin-kubernetes";
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
} from "@backstage/core-plugin-api";
|
||||
|
||||
import { rootRouteRef } from "./routes";
|
||||
import { ArgoWorkflows, argoWorkflowsApiRef } from "./api";
|
||||
import { kubernetesApiRef } from "@backstage/plugin-kubernetes";
|
||||
|
||||
export const argoWorkflowsPlugin = createPlugin({
|
||||
id: 'argo-workflows',
|
||||
id: "argo-workflows",
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: argoWorkflowsApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
kubernetesApi: kubernetesApiRef,
|
||||
configApi: configApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
},
|
||||
factory: ({
|
||||
discoveryApi, kubernetesApi, configApi, fetchApi,
|
||||
}) =>
|
||||
new ArgoWorkflows(discoveryApi, kubernetesApi, configApi, fetchApi)
|
||||
})
|
||||
]
|
||||
createApiFactory({
|
||||
api: argoWorkflowsApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
kubernetesApi: kubernetesApiRef,
|
||||
configApi: configApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, kubernetesApi, configApi, fetchApi }) =>
|
||||
new ArgoWorkflows(discoveryApi, kubernetesApi, configApi, fetchApi),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const ArgoWorkflowsPage = argoWorkflowsPlugin.provide(
|
||||
createRoutableExtension({
|
||||
name: 'ArgoWorkflowsPage',
|
||||
name: "ArgoWorkflowsPage",
|
||||
component: () =>
|
||||
import('./components/Overview').then(m => m.OverviewComponent),
|
||||
import("./components/Overview").then((m) => m.OverviewComponent),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
import { createRouteRef } from "@backstage/core-plugin-api";
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'argo-workflows',
|
||||
id: "argo-workflows",
|
||||
});
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
import '@testing-library/jest-dom';
|
||||
import 'cross-fetch/polyfill';
|
||||
import "@testing-library/jest-dom";
|
||||
import "cross-fetch/polyfill";
|
||||
|
|
|
@ -1,325 +1,307 @@
|
|||
|
||||
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"
|
||||
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",
|
||||
},
|
||||
"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",
|
||||
},
|
||||
"workflowTemplateRef": {
|
||||
"name": "workflow-template-whalesay-template"
|
||||
}
|
||||
],
|
||||
},
|
||||
"status": {
|
||||
"artifactGCStatus": {
|
||||
"notSpecified": true
|
||||
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",
|
||||
},
|
||||
"artifactRepositoryRef": {
|
||||
"artifactRepository": {},
|
||||
"default": true
|
||||
],
|
||||
},
|
||||
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",
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
],
|
||||
},
|
||||
metadata: {},
|
||||
name: "whalesay-template",
|
||||
outputs: {},
|
||||
steps: [
|
||||
[
|
||||
{
|
||||
arguments: {
|
||||
parameters: [
|
||||
{
|
||||
name: "message",
|
||||
value: "{{inputs.parameters.message}}",
|
||||
},
|
||||
],
|
||||
"ttlStrategy": {
|
||||
"secondsAfterCompletion": 28800
|
||||
},
|
||||
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}}",
|
||||
},
|
||||
],
|
||||
},
|
||||
"workflowMetadata": {
|
||||
"labels": {
|
||||
"env": "dev",
|
||||
"my": "label"
|
||||
}
|
||||
},
|
||||
"workflowTemplateRef": {
|
||||
"name": "workflow-template-whalesay-template"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"kind": "List",
|
||||
"metadata": {
|
||||
"resourceVersion": ""
|
||||
}
|
||||
}
|
||||
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: "",
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue