query for workflows

This commit is contained in:
Manabu Mccloskey 2023-03-07 17:10:04 -08:00
parent f3c473ccf3
commit 617f0053a5
3 changed files with 141 additions and 47 deletions

View file

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

View file

@ -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 = () => (
<Page themeId="tool">
<Header title="Blueprint information">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
{/*<ContentHeader title="Blueprint information">*/}
{/* <SupportButton>A description of your plugin goes here.</SupportButton>*/}
{/*</ContentHeader>*/}
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Blueprint management">
<Typography color="textSecondary">
Manage this blueprint deployment
</Typography>
<IconButton aria-label="delete" size="medium">
<DeleteIcon />
</IconButton>
<IconButton aria-label="clear" size="medium">
<ClearIcon />
</IconButton>
<IconButton aria-label="link" size="medium">
<LinkOffRounded />
</IconButton>
</InfoCard>
<ManageBlueprint />
{/*<InfoCard title="Blueprint management">*/}
{/* <Typography color="textSecondary">*/}
{/* Manage this blueprint deployment*/}
{/* </Typography>*/}
{/* <IconButton aria-label="delete" size="medium">*/}
{/* <DeleteIcon />*/}
{/* </IconButton>*/}
{/* <IconButton aria-label="clear" size="medium">*/}
{/* <ClearIcon />*/}
{/* </IconButton>*/}
{/* <IconButton aria-label="link" size="medium">*/}
{/* <LinkOffRounded />*/}
{/* </IconButton>*/}
{/*</InfoCard>*/}
</Grid>
<Grid item>
<FetchTFState />

View file

@ -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<TFState> => {
return getTFState("tfstate-default-helloworld", "flux-system", discoveryApi)
} )
return getTFState("tfstate-default-helloworld", "flux-system", apiRef)
})
if (loading) {
return <Progress />
} else if (error) {
@ -85,26 +92,19 @@ export const FetchTFState = () => {
})
return <TFTable resources={resources}/>
// const { value, loading, error } = useAsync(async (): Promise<User[]> => {
// const response = await fetch('https://randomuser.me/api/?results=20');
// const data = await response.json();
// return data.results;
// }, []);
//
// if (loading) {
// return <Progress />;
// } else if (error) {
// return <Alert severity="error">{error.message}</Alert>;
// }
//
// return <DenseTable users={value || []} />;
};
// 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<TFState> {
const kubernetesBaseUrl = await discoveryApi.getBaseUrl('kubernetes');
const kubernetesProxyEndpoint = `${kubernetesBaseUrl}/proxy`;
async function getTFState(name: string, namespace: string, apiRef: DiscoveryApi): Promise<TFState> {
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<workflowStatus> => {
return getWorkflow(entity.entity.metadata.uid!, "admin", apiRef)
})
if (loading) {
return <Progress />
} else if (error) {
return <Alert severity="error">{error}</Alert>;
}
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 <Alert severity="error">"failed to delete blueprint deployment"</Alert>;
default:
return <Alert severity="error">"could not determine blueprint status"</Alert>;
}
return (
<InfoCard title="Blueprint management">
<Typography color="textSecondary">
{text}
</Typography>
<IconButton aria-label="delete" size="medium">
<DeleteIcon />
</IconButton>
<IconButton aria-label="clear" size="medium">
<ClearIcon />
</IconButton>
<IconButton aria-label="link" size="medium">
<LinkOffRounded />
</IconButton>
</InfoCard>
)
}
async function getWorkflow(entityId: string, namespace: string, apiRef: DiscoveryApi): Promise<workflowStatus> {
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} `)
}
})
}