diff --git a/plugins/workflows/package.json b/plugins/workflows/package.json index 49bb8e0..5e53d27 100644 --- a/plugins/workflows/package.json +++ b/plugins/workflows/package.json @@ -25,6 +25,7 @@ "dependencies": { "@backstage/core-components": "^0.12.4", "@backstage/core-plugin-api": "^1.4.0", + "@backstage/plugin-catalog-react": "^1.3.0", "@backstage/theme": "^0.2.17", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,8 +44,8 @@ "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/node": "*", - "msw": "^0.49.0", - "cross-fetch": "^3.1.5" + "cross-fetch": "^3.1.5", + "msw": "^0.49.0" }, "files": [ "dist" diff --git a/plugins/workflows/src/components/BlueprintComponent/ExampleComponent.tsx b/plugins/workflows/src/components/BlueprintComponent/ExampleComponent.tsx index ceb9ebf..c422b5d 100644 --- a/plugins/workflows/src/components/BlueprintComponent/ExampleComponent.tsx +++ b/plugins/workflows/src/components/BlueprintComponent/ExampleComponent.tsx @@ -8,38 +8,33 @@ import { Header, Page, Content, - ContentHeader, HeaderLabel, - SupportButton, } from '@backstage/core-components'; -import {FetchTFState} from "./FetchTFState"; +import {FetchTFState, ManageBlueprint} from "./FetchTFState"; export const BlueprintsComponent = () => ( -
- - -
{/**/} {/* A description of your plugin goes here.*/} {/**/} - - - Manage this blueprint deployment - - - - - - - - - - - + + {/**/} + {/* */} + {/* Manage this blueprint deployment*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* */} + {/**/} diff --git a/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx b/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx index 7e988ba..053f3db 100644 --- a/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx +++ b/plugins/workflows/src/components/BlueprintComponent/FetchTFState.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Table, TableColumn, Progress } from '@backstage/core-components'; +import {Table, TableColumn, Progress, InfoCard} from '@backstage/core-components'; import Alert from '@material-ui/lab/Alert'; import useAsync from 'react-use/lib/useAsync'; @@ -10,6 +10,13 @@ import { } from '@backstage/core-plugin-api'; // eslint-disable-next-line no-restricted-imports import {gunzipSync} from "zlib"; +import {useEntity} from "@backstage/plugin-catalog-react" +import {IconButton, Typography} from "@material-ui/core"; +import DeleteIcon from "@material-ui/icons/Delete"; +import ClearIcon from "@material-ui/icons/Clear"; +import LinkOffRounded from "@material-ui/icons/LinkOffRounded"; + +const token = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxNWQ5MzdiNTViOWE4NjBhN2MzNjY5ZDBjODAzMjZjZjdlNDhmOGUifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjIl0sImV4cCI6MTY3ODI5OTQ2MywiaWF0IjoxNjc4MjEzMDYzLCJpc3MiOiJodHRwczovL29pZGMuZWtzLnVzLXdlc3QtMi5hbWF6b25hd3MuY29tL2lkLzNDRUJBM0NBNzg3MEEzRTVCRkUyQ0YzRkExNzNFRTU2Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJhZG1pbiIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJhZG1pbiIsInVpZCI6IjQyMzQwOTdiLWFmZjktNDc1Zi05NzY0LTg5NjM4ZWQ4MmVhMiJ9fSwibmJmIjoxNjc4MjEzMDYzLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6YWRtaW46YWRtaW4ifQ.CMew9l09MFBSISOPczcITpz9ZP5XASWlD7ez-tnvfs2ZzhVI7V8OpazQSQSXFcsCs399B3-DHc4Zt6NY9rZgvAMQ9hXtfO2a840o4sRMabOuljS7br503XUPr1JUwkTZ6hS_LRVNDqE8MEw8eyLmKQ7K_jV0fyWsIrR9mVXn6RVhD96P_k-N5da49MP-PbFHJsEeOwVwcUCWRfPYIMp1XlpCGxjqymKnxVQFuVC76aQYpSKzOsI-rYMkCk0Yt7L-NjOpx0tsBvs58SJy5XP_x64cw0_j4Xba9prH8g4OmGeCUV5csaT8N3FaY0e_kGqftY-B2bwtOxl0vx_6gadjyQ" type TFState = { terraform_version: string @@ -59,10 +66,10 @@ export const TFTable = (props: TFTableProps) => { } export const FetchTFState = () => { - const discoveryApi = useApi(discoveryApiRef); + const apiRef = useApi(discoveryApiRef) const { value, loading, error } = useAsync((): Promise => { - return getTFState("tfstate-default-helloworld", "flux-system", discoveryApi) - } ) + return getTFState("tfstate-default-helloworld", "flux-system", apiRef) + }) if (loading) { return } else if (error) { @@ -85,26 +92,19 @@ export const FetchTFState = () => { }) return - - // const { value, loading, error } = useAsync(async (): Promise => { - // const response = await fetch('https://randomuser.me/api/?results=20'); - // const data = await response.json(); - // return data.results; - // }, []); - // - // if (loading) { - // return ; - // } else if (error) { - // return {error.message}; - // } - // - // return ; }; - +// horrible type payload = { kind: string apiVersion: string + items?: { + metadata: { + labels: { + [key: string]: string + } + } + }[] metadata: { annotations: { [key: string]: string @@ -116,15 +116,15 @@ type payload = { } } -async function getTFState(name: string, namespace: string, discoveryApi: DiscoveryApi): Promise { - const kubernetesBaseUrl = await discoveryApi.getBaseUrl('kubernetes'); - const kubernetesProxyEndpoint = `${kubernetesBaseUrl}/proxy`; +async function getTFState(name: string, namespace: string, apiRef: DiscoveryApi): Promise { + const baseUrl = await apiRef.getBaseUrl("kubernetes") + const proxyUrl = `${baseUrl}/proxy` return new Promise(async (resolve, reject) => { - const resp = await fetch(`${kubernetesProxyEndpoint}/api/v1/namespaces/${namespace}/secrets/${name}`, { + const resp = await fetch(`${proxyUrl}/api/v1/namespaces/${namespace}/secrets/${name}`, { method: 'GET', headers: { 'X-Kubernetes-Cluster': "canoe-packaging", - Authorization: `Bearer TOKEN`, + Authorization: `Bearer ${token}`, }, }); if (resp.ok) { @@ -145,4 +145,102 @@ async function getTFState(name: string, namespace: string, discoveryApi: Discove }) } +enum workflowStatus { + UNKNOWN, + DELETING = 0, + CREATING, + DELETED, + NOTFOUND, + FAILED +} +export const ManageBlueprint = () => { + const entity = useEntity() + const apiRef = useApi(discoveryApiRef) + const { value, loading, error } = useAsync((): Promise => { + return getWorkflow(entity.entity.metadata.uid!, "admin", apiRef) + }) + if (loading) { + return + } else if (error) { + return {error}; + } + let text: string + switch (value) { + case workflowStatus.DELETING: + text = "This blueprint deployment is being deleted" + break + case workflowStatus.DELETED: + text = "This blueprint deployment was successfully deleted" + break + case workflowStatus.NOTFOUND: + text = "Manage this blueprint with the buttons below" + break + case workflowStatus.FAILED: + return "failed to delete blueprint deployment"; + default: + return "could not determine blueprint status"; + } + return ( + + + {text} + + + + + + + + + + + + ) +} +async function getWorkflow(entityId: string, namespace: string, apiRef: DiscoveryApi): Promise { + const baseUrl = await apiRef.getBaseUrl("kubernetes") + const proxyUrl = `${baseUrl}/proxy` + return new Promise(async (resolve, reject) => { + const queryParams = new URLSearchParams({ + labelSelector: `entity-id=${entityId}`, + limit: "1" + }).toString() + + const resp = await fetch(`${proxyUrl}/apis/argoproj.io/v1alpha1/namespaces/${namespace}/workflows?${queryParams}`, { + method: 'GET', + headers: { + 'X-Kubernetes-Cluster': "canoe-packaging", + Authorization: `Bearer ${token}`, + }, + }); + if (resp.ok) { + const payload = await resp.json() as payload + if (payload.items!.length > 0) { + const labels = payload.items![0].metadata.labels + if ("workflows.argoproj.io/phase" in labels) { + switch (labels["workflows.argoproj.io/phase"]) { + case "Running": + resolve(workflowStatus.DELETING) + break; + case "Succeeded": + resolve(workflowStatus.DELETED) + break + case "Failed": + resolve(workflowStatus.FAILED) + break + default: + reject(workflowStatus.UNKNOWN) + break + } + } + } else { + resolve(workflowStatus.NOTFOUND) + } + } else { + reject(`Failed to retrieve terraform information: ${resp.status}: ${resp.statusText} `) + } + }) +} + +