merge back to main (#8)
bump backstage version add cluster pickers add spark and argo workflows plugins
This commit is contained in:
parent
383885df21
commit
0061e42d91
20 changed files with 3235 additions and 1814 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -49,3 +49,6 @@ site
|
|||
|
||||
# vscode database functionality support files
|
||||
*.session.sql
|
||||
|
||||
# JetBrains
|
||||
.idea
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"version": "1.12.1"
|
||||
"version": "1.14.2"
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.22.5",
|
||||
"@backstage/cli": "^0.22.7",
|
||||
"@spotify/prettier-config": "^12.0.0",
|
||||
"concurrently": "^6.0.0",
|
||||
"lerna": "^4.0.0",
|
||||
|
|
|
@ -18,35 +18,40 @@
|
|||
"cy:run": "cypress run --browser chrome"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/app-defaults": "^1.2.1",
|
||||
"@backstage/catalog-model": "^1.2.1",
|
||||
"@backstage/cli": "^0.22.5",
|
||||
"@backstage/core-app-api": "^1.6.0",
|
||||
"@backstage/core-components": "^0.12.5",
|
||||
"@backstage/core-plugin-api": "^1.5.0",
|
||||
"@backstage/integration-react": "^1.1.11",
|
||||
"@backstage/plugin-api-docs": "^0.9.1",
|
||||
"@backstage/plugin-catalog": "^1.9.0",
|
||||
"@backstage/plugin-catalog-common": "^1.0.12",
|
||||
"@backstage/plugin-catalog-graph": "^0.2.28",
|
||||
"@backstage/plugin-catalog-import": "^0.9.6",
|
||||
"@backstage/plugin-catalog-react": "^1.4.0",
|
||||
"@backstage/plugin-github-actions": "^0.5.16",
|
||||
"@backstage/plugin-kubernetes": "^0.7.9",
|
||||
"@backstage/plugin-org": "^0.6.6",
|
||||
"@backstage/plugin-permission-react": "^0.4.11",
|
||||
"@backstage/plugin-scaffolder": "^1.12.0",
|
||||
"@backstage/plugin-search": "^1.1.1",
|
||||
"@backstage/plugin-search-react": "^1.5.1",
|
||||
"@backstage/plugin-tech-radar": "^0.6.2",
|
||||
"@backstage/plugin-techdocs": "^1.6.0",
|
||||
"@backstage/plugin-techdocs-module-addons-contrib": "^1.0.11",
|
||||
"@backstage/plugin-techdocs-react": "^1.1.4",
|
||||
"@backstage/plugin-user-settings": "^0.7.1",
|
||||
"@backstage/theme": "^0.2.18",
|
||||
"@backstage/app-defaults": "^1.3.1",
|
||||
"@backstage/catalog-model": "^1.3.0",
|
||||
"@backstage/cli": "^0.22.7",
|
||||
"@backstage/core-app-api": "^1.8.0",
|
||||
"@backstage/core-components": "^0.13.1",
|
||||
"@backstage/core-plugin-api": "^1.5.1",
|
||||
"@backstage/integration-react": "^1.1.13",
|
||||
"@backstage/plugin-api-docs": "^0.9.4",
|
||||
"@backstage/plugin-catalog": "^1.11.1",
|
||||
"@backstage/plugin-catalog-common": "^1.0.13",
|
||||
"@backstage/plugin-catalog-graph": "^0.2.30",
|
||||
"@backstage/plugin-catalog-import": "^0.9.8",
|
||||
"@backstage/plugin-catalog-react": "^1.6.0",
|
||||
"@backstage/plugin-github-actions": "^0.5.18",
|
||||
"@backstage/plugin-kubernetes": "^0.9.1",
|
||||
"@backstage/plugin-org": "^0.6.8",
|
||||
"@backstage/plugin-permission-react": "^0.4.12",
|
||||
"@backstage/plugin-scaffolder": "^1.13.1",
|
||||
"@backstage/plugin-scaffolder-react": "^1.4.0",
|
||||
"@backstage/plugin-search": "^1.3.1",
|
||||
"@backstage/plugin-search-react": "^1.6.1",
|
||||
"@backstage/plugin-tech-radar": "^0.6.4",
|
||||
"@backstage/plugin-techdocs": "^1.6.3",
|
||||
"@backstage/plugin-techdocs-module-addons-contrib": "^1.0.13",
|
||||
"@backstage/plugin-techdocs-react": "^1.1.6",
|
||||
"@backstage/plugin-user-settings": "^0.7.3",
|
||||
"@backstage/theme": "^0.3.0",
|
||||
"@cnoe-io/plugin-apache-spark": "file:/var/folders/b7/h6wzrfwn6l30pn3fk5j2794dcy0vlz/T/tmp-26390-66rLxROMRq6K",
|
||||
"@cnoe-io/plugin-argo-workflows": "file:/var/folders/b7/h6wzrfwn6l30pn3fk5j2794dcy0vlz/T/tmp-32426-R2tjIfGLJy55",
|
||||
"@internal/plugin-workflows": "^0.1.0",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@rjsf/core": "^5.8.1",
|
||||
"@rjsf/utils": "^5.8.1",
|
||||
"history": "^5.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
@ -54,7 +59,7 @@
|
|||
"react-use": "^17.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/test-utils": "^1.2.6",
|
||||
"@backstage/test-utils": "^1.3.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
catalogImportPlugin,
|
||||
} from '@backstage/plugin-catalog-import';
|
||||
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
|
||||
import { ScaffolderFieldExtensions } from '@backstage/plugin-scaffolder-react';
|
||||
import { orgPlugin } from '@backstage/plugin-org';
|
||||
import { SearchPage } from '@backstage/plugin-search';
|
||||
import { TechRadarPage } from '@backstage/plugin-tech-radar';
|
||||
|
@ -22,35 +23,39 @@ import {
|
|||
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
|
||||
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
|
||||
import { UserSettingsPage } from '@backstage/plugin-user-settings';
|
||||
import {apis} from './apis';
|
||||
import {keycloakOIDCAuthApiRef} from "@internal/plugin-workflows"
|
||||
import { apis, keycloakOIDCAuthApiRef } from './apis';
|
||||
import { entityPage } from './components/catalog/EntityPage';
|
||||
import { searchPage } from './components/search/SearchPage';
|
||||
import { Root } from './components/Root';
|
||||
|
||||
import {AlertDisplay, OAuthRequestDialog, SignInPage} from '@backstage/core-components';
|
||||
import {
|
||||
AlertDisplay,
|
||||
OAuthRequestDialog,
|
||||
SignInPage,
|
||||
} from '@backstage/core-components';
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
|
||||
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
|
||||
import { RequirePermission } from '@backstage/plugin-permission-react';
|
||||
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
import { GetK8sOIDCTokenExtension } from './scaffolder/credentials';
|
||||
|
||||
const app = createApp({
|
||||
apis,
|
||||
components: {
|
||||
// SignInPage: (props) => <ProxiedSignInPage {...props} provider="oauth2Proxy" />,
|
||||
SignInPage: props => (
|
||||
<SignInPage
|
||||
{...props}
|
||||
auto
|
||||
provider={{
|
||||
id: 'keycloak-oidc',
|
||||
title: 'Keycloak',
|
||||
message: 'Sign in using Keycloak',
|
||||
apiRef: keycloakOIDCAuthApiRef,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
SignInPage: props => (
|
||||
<SignInPage
|
||||
{...props}
|
||||
auto
|
||||
provider={{
|
||||
id: 'keycloak-oidc',
|
||||
title: 'Keycloak',
|
||||
message: 'Sign in using Keycloak',
|
||||
apiRef: keycloakOIDCAuthApiRef,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
bindRoutes({ bind }) {
|
||||
bind(catalogPlugin.externalRoutes, {
|
||||
|
@ -88,7 +93,11 @@ const routes = (
|
|||
<ReportIssue />
|
||||
</TechDocsAddons>
|
||||
</Route>
|
||||
<Route path="/create" element={<ScaffolderPage />} />
|
||||
<Route path="/create" element={<ScaffolderPage />}>
|
||||
<ScaffolderFieldExtensions>
|
||||
<GetK8sOIDCTokenExtension />
|
||||
</ScaffolderFieldExtensions>
|
||||
</Route>
|
||||
<Route path="/api-docs" element={<ApiExplorerPage />} />
|
||||
<Route
|
||||
path="/tech-radar"
|
||||
|
|
|
@ -4,26 +4,25 @@ import {
|
|||
ScmAuth,
|
||||
} from '@backstage/integration-react';
|
||||
import {
|
||||
AnyApiFactory,
|
||||
ApiRef,
|
||||
BackstageIdentityApi,
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
createApiRef,
|
||||
discoveryApiRef,
|
||||
oauthRequestApiRef,
|
||||
OpenIdConnectApi,
|
||||
ProfileInfoApi,
|
||||
SessionApi,
|
||||
AnyApiFactory,
|
||||
ApiRef,
|
||||
BackstageIdentityApi,
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
createApiRef,
|
||||
discoveryApiRef,
|
||||
oauthRequestApiRef,
|
||||
OpenIdConnectApi,
|
||||
ProfileInfoApi,
|
||||
SessionApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {OAuth2} from "@backstage/core-app-api";
|
||||
import {keycloakOIDCAuthApiRef} from "@internal/plugin-workflows"
|
||||
import { OAuth2 } from '@backstage/core-app-api';
|
||||
|
||||
// export const keycloakOIDCAuthApiRef: ApiRef<
|
||||
// OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
// > = createApiRef({
|
||||
// id: 'auth.keycloak-oidc-provider',
|
||||
// });
|
||||
export const keycloakOIDCAuthApiRef: ApiRef<
|
||||
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'auth.keycloak-oidc-provider',
|
||||
});
|
||||
export const apis: AnyApiFactory[] = [
|
||||
createApiFactory({
|
||||
api: scmIntegrationsApiRef,
|
||||
|
@ -39,16 +38,16 @@ export const apis: AnyApiFactory[] = [
|
|||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
OAuth2.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
provider: {
|
||||
id: 'keycloak-oidc',
|
||||
title: 'Keycloak OIDC',
|
||||
icon: () => null,
|
||||
},
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
defaultScopes: ['openid', 'profile', 'email', 'groups'],
|
||||
}),
|
||||
OAuth2.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
provider: {
|
||||
id: 'keycloak-oidc',
|
||||
title: 'Keycloak OIDC',
|
||||
icon: () => null,
|
||||
},
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
defaultScopes: ['openid', 'profile', 'email', 'groups'],
|
||||
}),
|
||||
}),
|
||||
];
|
||||
|
|
|
@ -58,7 +58,13 @@ import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
|
|||
|
||||
import { EntityKubernetesContent } from '@backstage/plugin-kubernetes';
|
||||
|
||||
import {EntityWorkflowsContent} from '@internal/plugin-workflows'
|
||||
import {
|
||||
EntityArgoWorkflowsOverviewCard,
|
||||
EntityArgoWorkflowsTemplateOverviewCard,
|
||||
isArgoWorkflowsAvailable,
|
||||
} from '@cnoe-io/plugin-argo-workflows';
|
||||
|
||||
import { ApacheSparkPage } from '@cnoe-io/plugin-apache-spark';
|
||||
|
||||
const techdocsContent = (
|
||||
<EntityTechdocsContent>
|
||||
|
@ -121,6 +127,16 @@ const overviewContent = (
|
|||
<Grid item md={6}>
|
||||
<EntityAboutCard variant="gridItem" />
|
||||
</Grid>
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={e => isArgoWorkflowsAvailable(e)}>
|
||||
<Grid item md={6}>
|
||||
<EntityArgoWorkflowsOverviewCard />
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<EntityArgoWorkflowsTemplateOverviewCard />
|
||||
</Grid>
|
||||
</EntitySwitch.Case>
|
||||
</EntitySwitch>
|
||||
<Grid item md={6} xs={12}>
|
||||
<EntityCatalogGraphCard variant="gridItem" height={400} />
|
||||
</Grid>
|
||||
|
@ -199,6 +215,17 @@ const websiteEntityPage = (
|
|||
</EntityLayout>
|
||||
);
|
||||
|
||||
const jobEntityPage = (
|
||||
<EntityLayout>
|
||||
<EntityLayout.Route path="/" title="Overview">
|
||||
{overviewContent}
|
||||
</EntityLayout.Route>
|
||||
<EntityLayout.Route path="/apache-spark" title="Apache Spark">
|
||||
<ApacheSparkPage />
|
||||
</EntityLayout.Route>
|
||||
</EntityLayout>
|
||||
);
|
||||
|
||||
/**
|
||||
* NOTE: This page is designed to work on small screens such as mobile devices.
|
||||
* This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`,
|
||||
|
@ -227,6 +254,9 @@ const componentPage = (
|
|||
<EntitySwitch.Case if={isComponentType('website')}>
|
||||
{websiteEntityPage}
|
||||
</EntitySwitch.Case>
|
||||
<EntitySwitch.Case if={isComponentType('job')}>
|
||||
{jobEntityPage}
|
||||
</EntitySwitch.Case>
|
||||
|
||||
<EntitySwitch.Case>{defaultEntityPage}</EntitySwitch.Case>
|
||||
</EntitySwitch>
|
||||
|
@ -349,9 +379,6 @@ const systemPage = (
|
|||
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
|
||||
<EntityKubernetesContent refreshIntervalMs={30000} />
|
||||
</EntityLayout.Route>
|
||||
<EntityLayout.Route path="/workflows" title="Workflows">
|
||||
<EntityWorkflowsContent />
|
||||
</EntityLayout.Route>
|
||||
</EntityLayout>
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
import React, { useState } from 'react';
|
||||
import {
|
||||
createScaffolderLayout,
|
||||
LayoutTemplate,
|
||||
} from '@backstage/plugin-scaffolder-react';
|
||||
import { scaffolderPlugin } from '@backstage/plugin-scaffolder';
|
||||
import { Button, Grid } from '@material-ui/core';
|
||||
import {
|
||||
ObjectFieldTemplatePropertyType,
|
||||
ObjectFieldTemplateProps,
|
||||
StrictRJSFSchema,
|
||||
FormContextType,
|
||||
RJSFSchema,
|
||||
titleId,
|
||||
getTemplate,
|
||||
getUiOptions,
|
||||
} from '@rjsf/utils';
|
||||
|
||||
const TwoColumn: LayoutTemplate = ({ properties, description, title }) => {
|
||||
const mid = Math.ceil(properties.length / 2);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>{title}</h1>
|
||||
<h2>In two column layout!!</h2>
|
||||
<Grid container justifyContent="flex-end">
|
||||
{properties.slice(0, mid).map(prop => (
|
||||
<Grid item xs={6} key={prop.content.key}>
|
||||
{prop.content}
|
||||
</Grid>
|
||||
))}
|
||||
{properties.slice(mid).map(prop => (
|
||||
<Grid item xs={6} key={prop.content.key}>
|
||||
{prop.content}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
{description}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function CollapsableFieldTemplate<
|
||||
T = any,
|
||||
S extends StrictRJSFSchema = RJSFSchema,
|
||||
F extends FormContextType = any,
|
||||
>(props: ObjectFieldTemplateProps<T, S, F>) {
|
||||
const {
|
||||
registry,
|
||||
properties,
|
||||
title,
|
||||
description,
|
||||
uiSchema,
|
||||
required,
|
||||
schema,
|
||||
idSchema,
|
||||
} = props;
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
const out = (
|
||||
<div>
|
||||
{title} hiii{description}
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
float: 'right',
|
||||
fontSize: 'large',
|
||||
}}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
Collapse
|
||||
</Button>
|
||||
<div>
|
||||
{collapsed
|
||||
? null
|
||||
: properties.map(prop => (
|
||||
<div key={prop.content.key}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
float: 'right',
|
||||
fontSize: 'large',
|
||||
}}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
Collapse
|
||||
</Button>
|
||||
{prop.content}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return out;
|
||||
// return (
|
||||
// <>
|
||||
// {hidden ? null : (
|
||||
// <div className={classNames}>
|
||||
// <>
|
||||
// {!isThisTheTopmostElement() && (
|
||||
// <Button
|
||||
// variant="outlined"
|
||||
// size="small"
|
||||
// style={{
|
||||
// display: 'inline-block',
|
||||
// float: 'right',
|
||||
// fontSize: 'large',
|
||||
// }}
|
||||
// onClick={() => setCollapsed(!collapsed)}
|
||||
// >
|
||||
// {collapsed ? (
|
||||
// <>
|
||||
// +
|
||||
// {(errors?.props?.errors ?? []).length ? (
|
||||
// <span style={{ fontSize: 'small' }}>
|
||||
// {' '}
|
||||
// (Contains errors)
|
||||
// </span>
|
||||
// ) : null}
|
||||
// </>
|
||||
// ) : (
|
||||
// '-'
|
||||
// )}
|
||||
// </Button>
|
||||
// )}
|
||||
// {get(schema, 'type', undefined) !== 'object' &&
|
||||
// get(schema, 'type', undefined) !== 'array' ? (
|
||||
// <>{label ? `${label}${required ? ' *required' : ''}` : null}</>
|
||||
// ) : (
|
||||
// <fieldset className="field field-array field-array-of-object">
|
||||
// {label ? (
|
||||
// <legend>{`${label}${required ? '*required' : ''}`}</legend>
|
||||
// ) : null}
|
||||
// </fieldset>
|
||||
// )}
|
||||
// {!collapsed && (
|
||||
// <>
|
||||
// {get(schema, 'type', undefined) !== 'object' &&
|
||||
// get(schema, 'type', undefined) !== 'array'
|
||||
// ? description
|
||||
// : null}
|
||||
// {children}
|
||||
// {errors}
|
||||
// {help}
|
||||
// </>
|
||||
// )}
|
||||
// </>
|
||||
// </div>
|
||||
// )}
|
||||
// </>
|
||||
// );
|
||||
}
|
||||
|
||||
export const CollapsableField = scaffolderPlugin.provide(
|
||||
createScaffolderLayout({
|
||||
name: 'CollapsableField',
|
||||
component: CollapsableFieldTemplate,
|
||||
}),
|
||||
);
|
14
packages/app/src/scaffolder/credentials/extensions.ts
Normal file
14
packages/app/src/scaffolder/credentials/extensions.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {scaffolderPlugin,} from '@backstage/plugin-scaffolder';
|
||||
import {createScaffolderFieldExtension} from "@backstage/plugin-scaffolder-react";
|
||||
import {GetK8sOIDCToken} from "./getOIDCToken";
|
||||
import {ClusterPickerSchema} from "./schema";
|
||||
|
||||
export const GetK8sOIDCTokenExtension = scaffolderPlugin.provide(
|
||||
createScaffolderFieldExtension(
|
||||
{
|
||||
name: 'GetK8sOIDCToken',
|
||||
component: GetK8sOIDCToken,
|
||||
schema: ClusterPickerSchema,
|
||||
}
|
||||
)
|
||||
)
|
102
packages/app/src/scaffolder/credentials/getOIDCToken.tsx
Normal file
102
packages/app/src/scaffolder/credentials/getOIDCToken.tsx
Normal file
|
@ -0,0 +1,102 @@
|
|||
import React, {useCallback, useEffect} from 'react';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import {
|
||||
useApi, configApiRef, discoveryApiRef, oauthRequestApiRef
|
||||
} from "@backstage/core-plugin-api";
|
||||
import { kubernetesApiRef } from "@backstage/plugin-kubernetes";
|
||||
import { FormHelperText } from "@material-ui/core";
|
||||
import {Progress, Select} from "@backstage/core-components";
|
||||
import useAsync from "react-use/lib/useAsync";
|
||||
import {useTemplateSecrets} from "@backstage/plugin-scaffolder-react";
|
||||
import {ClusterPickerProps} from "./schema";
|
||||
import {OAuth2} from "@backstage/core-app-api";
|
||||
|
||||
|
||||
export const GetK8sOIDCToken = (props: ClusterPickerProps) => {
|
||||
|
||||
const k8sApi = useApi(kubernetesApiRef)
|
||||
const { setSecrets, secrets } = useTemplateSecrets();
|
||||
|
||||
const discoveryApi = useApi(discoveryApiRef)
|
||||
const oauthRequestApi = useApi(oauthRequestApiRef)
|
||||
const configApi = useApi(configApiRef)
|
||||
|
||||
const {uiSchema, required} = props
|
||||
let {rawErrors} = props
|
||||
const {value: {clusters} = {clusters: []}, loading } = useAsync(
|
||||
async () => {
|
||||
const c = await k8sApi.getClusters()
|
||||
return {clusters: c.map(i => ({ label: i.name, value: i.name}))}
|
||||
}
|
||||
)
|
||||
if (!rawErrors) {
|
||||
rawErrors = []
|
||||
}
|
||||
|
||||
const getToken = useCallback( async (clusterName: string) => {
|
||||
const {requestUserCredentials} = uiSchema?.['ui:options'] ?? {}
|
||||
if (!requestUserCredentials) {
|
||||
return;
|
||||
}
|
||||
const cs = await k8sApi.getClusters()
|
||||
const cluster = cs.find(c => {
|
||||
return c.name === clusterName
|
||||
})
|
||||
if (cluster?.oidcTokenProvider === undefined) {
|
||||
throw new Error("no oidc provider defined for this cluster")
|
||||
}
|
||||
|
||||
const oidc = OAuth2.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
provider: {
|
||||
id: cluster.oidcTokenProvider,
|
||||
title: 'OIDC',
|
||||
icon: () => null,
|
||||
},
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
defaultScopes: ['openid', 'profile', 'email', 'groups'],
|
||||
})
|
||||
const token = await oidc.getIdToken()
|
||||
|
||||
setSecrets({ [requestUserCredentials.secretKey]: token })
|
||||
}, [configApi, discoveryApi, k8sApi, oauthRequestApi, setSecrets, uiSchema]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const {requestUserCredentials} = uiSchema?.['ui:options'] ?? {}
|
||||
if (!requestUserCredentials?.secretKey || secrets[requestUserCredentials?.secretKey!]) {
|
||||
return
|
||||
}
|
||||
|
||||
if (clusters.length) {
|
||||
getToken(clusters[0].value).catch(console.error)
|
||||
}
|
||||
}, [clusters, getToken, secrets, uiSchema])
|
||||
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
margin="normal"
|
||||
required={required}
|
||||
error={rawErrors?.length > 0}
|
||||
>
|
||||
<Select
|
||||
native
|
||||
label="Cluster"
|
||||
items={clusters}
|
||||
onChange={e => getToken(e.toString())}
|
||||
placeholder="select one"
|
||||
/>
|
||||
<FormHelperText id="entityName">
|
||||
Kubernetes Cluster Name
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
1
packages/app/src/scaffolder/credentials/index.ts
Normal file
1
packages/app/src/scaffolder/credentials/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export {GetK8sOIDCTokenExtension} from './extensions'
|
20
packages/app/src/scaffolder/credentials/schema.ts
Normal file
20
packages/app/src/scaffolder/credentials/schema.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { z } from 'zod';
|
||||
import {makeFieldSchemaFromZod} from "@backstage/plugin-scaffolder";
|
||||
|
||||
export const ClusterPickerFieldSchema = makeFieldSchemaFromZod(
|
||||
z.string(),
|
||||
z.object( {
|
||||
requestUserCredentials: z.object({
|
||||
secretKey: z.string().describe('Key used within the template secrets context to store the credential')
|
||||
}
|
||||
)
|
||||
.optional()
|
||||
.describe('If defined will request user credentials to auth against the cluster')
|
||||
})
|
||||
)
|
||||
|
||||
export const ClusterPickerSchema = ClusterPickerFieldSchema.schema
|
||||
|
||||
export type ClusterPickerProps = typeof ClusterPickerFieldSchema.type
|
||||
|
||||
export type ClusterPickerUiOptions = typeof ClusterPickerFieldSchema.uiOptionsType
|
|
@ -16,38 +16,41 @@
|
|||
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.18.3",
|
||||
"@backstage/backend-tasks": "^0.5.0",
|
||||
"@backstage/catalog-client": "^1.4.0",
|
||||
"@backstage/catalog-model": "^1.2.1",
|
||||
"@backstage/backend-common": "^0.18.5",
|
||||
"@backstage/backend-tasks": "^0.5.2",
|
||||
"@backstage/catalog-client": "^1.4.1",
|
||||
"@backstage/catalog-model": "^1.3.0",
|
||||
"@backstage/config": "^1.0.7",
|
||||
"@backstage/integration": "^1.4.3",
|
||||
"@backstage/plugin-app-backend": "^0.3.43",
|
||||
"@backstage/plugin-auth-backend": "^0.18.1",
|
||||
"@backstage/plugin-auth-node": "^0.2.12",
|
||||
"@backstage/plugin-catalog-backend": "^1.8.0",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.9.4",
|
||||
"@backstage/plugin-permission-common": "^0.7.4",
|
||||
"@backstage/plugin-permission-node": "^0.7.6",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.37",
|
||||
"@backstage/plugin-scaffolder-backend": "^1.12.0",
|
||||
"@backstage/plugin-scaffolder-node": "^0.1.1",
|
||||
"@backstage/plugin-search-backend": "^1.2.4",
|
||||
"@backstage/plugin-search-backend-module-pg": "^0.5.4",
|
||||
"@backstage/plugin-search-backend-node": "^1.1.4",
|
||||
"@backstage/plugin-techdocs-backend": "^1.6.0",
|
||||
"@backstage/integration": "^1.4.5",
|
||||
"@backstage/plugin-app-backend": "^0.3.45",
|
||||
"@backstage/plugin-auth-backend": "^0.18.3",
|
||||
"@backstage/plugin-auth-node": "^0.2.14",
|
||||
"@backstage/plugin-catalog-backend": "^1.9.1",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.11.0",
|
||||
"@backstage/plugin-permission-common": "^0.7.5",
|
||||
"@backstage/plugin-permission-node": "^0.7.8",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.39",
|
||||
"@backstage/plugin-scaffolder-backend": "^1.14.0",
|
||||
"@backstage/plugin-scaffolder-node": "^0.1.3",
|
||||
"@backstage/plugin-search-backend": "^1.3.1",
|
||||
"@backstage/plugin-search-backend-module-pg": "^0.5.6",
|
||||
"@backstage/plugin-search-backend-node": "^1.2.1",
|
||||
"@backstage/plugin-techdocs-backend": "^1.6.2",
|
||||
"@backstage/types": "^1.1.0",
|
||||
"@internal/plugin-argo-workflows-backend-backend": "^0.1.0",
|
||||
"@kubernetes/client-node": "^0.18.1",
|
||||
"@roadiehq/scaffolder-backend-module-utils": "^1.8.4",
|
||||
"@roadiehq/scaffolder-backend-module-utils": "^1.8.7",
|
||||
"app": "link:../app",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"dockerode": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"pg": "^8.3.0",
|
||||
"winston": "^3.2.1"
|
||||
"winston": "^3.2.1",
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.22.5",
|
||||
"@backstage/cli": "^0.22.7",
|
||||
"@types/dockerode": "^3.3.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
|
|
|
@ -5,7 +5,11 @@ import {
|
|||
} from '@backstage/plugin-auth-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import {DEFAULT_NAMESPACE, stringifyEntityRef} from "@backstage/catalog-model";
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { JsonArray } from '@backstage/types';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
|
@ -26,83 +30,16 @@ export default async function createPlugin(
|
|||
name: info.result.userinfo.sub,
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
});
|
||||
console.log(info.result.userinfo.groups)
|
||||
return ctx.issueToken({
|
||||
claims: {
|
||||
sub: userRef, // The user's own identity
|
||||
ent: [userRef], // A list of identities that the user claims ownership through
|
||||
sub: userRef,
|
||||
ent: [userRef],
|
||||
groups: (info.result.userinfo.groups as JsonArray) || [],
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
// providerFactories: {
|
||||
// ...defaultAuthProviderFactories,
|
||||
// oauth2Proxy: providers.oauth2Proxy.create({
|
||||
// signIn: {
|
||||
// async resolver({ result }, ctx) {
|
||||
// console.log(result)
|
||||
// const name = result.getHeader('x-forwarded-preferred-username');
|
||||
// if (!name) {
|
||||
// throw new Error('Request did not contain a user');
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// // Attempts to sign in existing user
|
||||
// const signedInUser = await ctx.signInWithCatalogUser({
|
||||
// entityRef: { name },
|
||||
// });
|
||||
//
|
||||
// return Promise.resolve(signedInUser);
|
||||
// } catch (e) {
|
||||
// // Create stub user
|
||||
// const userEntityRef = stringifyEntityRef({
|
||||
// kind: 'User',
|
||||
// name: name,
|
||||
// namespace: DEFAULT_NAMESPACE,
|
||||
// });
|
||||
// return ctx.issueToken({
|
||||
// claims: {
|
||||
// sub: userEntityRef,
|
||||
// ent: [userEntityRef],
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// // This replaces the default GitHub auth provider with a customized one.
|
||||
// // The `signIn` option enables sign-in for this provider, using the
|
||||
// // identity resolution logic that's provided in the `resolver` callback.
|
||||
// //
|
||||
// // This particular resolver makes all users share a single "guest" identity.
|
||||
// // It should only be used for testing and trying out Backstage.
|
||||
// //
|
||||
// // If you want to use a production ready resolver you can switch to
|
||||
// // the one that is commented out below, it looks up a user entity in the
|
||||
// // catalog using the GitHub username of the authenticated user.
|
||||
// // That resolver requires you to have user entities populated in the catalog,
|
||||
// // for example using https://backstage.io/docs/integrations/github/org
|
||||
// //
|
||||
// // There are other resolvers to choose from, and you can also create
|
||||
// // your own, see the auth documentation for more details:
|
||||
// //
|
||||
// // https://backstage.io/docs/auth/identity-resolver
|
||||
// // github: providers.github.create({
|
||||
// // signIn: {
|
||||
// // resolver(_, ctx) {
|
||||
// // const userRef = 'user:default/guest'; // Must be a full entity reference
|
||||
// // return ctx.issueToken({
|
||||
// // claims: {
|
||||
// // sub: userRef, // The user's own identity
|
||||
// // ent: [userRef], // A list of identities that the user claims ownership through
|
||||
// // },
|
||||
// // });
|
||||
// // },
|
||||
// // // resolver: providers.github.resolvers.usernameMatchingUserEntityName(),
|
||||
// // },
|
||||
// // }),
|
||||
// },
|
||||
});
|
||||
}
|
||||
|
|
182
packages/backend/src/plugins/kubernetes-apply.ts
Normal file
182
packages/backend/src/plugins/kubernetes-apply.ts
Normal file
|
@ -0,0 +1,182 @@
|
|||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { KubeConfig, CustomObjectsApi } from '@kubernetes/client-node';
|
||||
import YAML from 'yaml';
|
||||
import { Config } from '@backstage/config';
|
||||
import { resolveSafeChildPath } from '@backstage/backend-common';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
export const kubernetesApply = (config: Config) => {
|
||||
return createTemplateAction<{
|
||||
manifestString?: string;
|
||||
manifestObject?: any;
|
||||
manifestPath?: string;
|
||||
namespaced: boolean;
|
||||
clusterName: string;
|
||||
}>({
|
||||
id: 'cnoe:kubernetes:apply',
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
required: ['namespaced'],
|
||||
properties: {
|
||||
manifestString: {
|
||||
type: 'string',
|
||||
title: 'Manifest',
|
||||
description: 'The manifest to apply in the cluster',
|
||||
},
|
||||
manifestObject: {
|
||||
type: 'object',
|
||||
title: 'Manifest',
|
||||
description: 'The manifest to apply in the cluster',
|
||||
},
|
||||
manifestPath: {
|
||||
type: 'string',
|
||||
title: 'Path to the manifest file',
|
||||
description: 'The path to the manifest file',
|
||||
},
|
||||
namespaced: {
|
||||
type: 'boolean',
|
||||
title: 'Namespaced',
|
||||
description: 'Whether the API is namespaced or not',
|
||||
},
|
||||
clusterName: {
|
||||
type: 'string',
|
||||
title: 'Cluster Name',
|
||||
description: 'The name of the cluster to apply this',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
let obj: any;
|
||||
if (ctx.input.manifestString) {
|
||||
obj = YAML.parse(ctx.input.manifestString);
|
||||
} else if (ctx.input.manifestObject) {
|
||||
obj = ctx.input.manifestObject;
|
||||
} else {
|
||||
const filePath = resolveSafeChildPath(
|
||||
ctx.workspacePath,
|
||||
ctx.input.manifestPath!,
|
||||
);
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
obj = YAML.parse(fileContent);
|
||||
}
|
||||
const words = obj.apiVersion.split('/');
|
||||
const group = words[0];
|
||||
const version = words[1];
|
||||
// hack. needs fixing to correctly extract the plurals
|
||||
const plural = `${obj.kind.toLowerCase()}s`;
|
||||
const targetCluster = getClusterConfig(ctx.input.clusterName!, config);
|
||||
// hack. needs fixing to get the KubeConfig info from app-config.yaml
|
||||
const kc = new KubeConfig();
|
||||
kc.addCluster({
|
||||
name: targetCluster.getString('name'),
|
||||
caData: targetCluster.getString('caData'),
|
||||
server: targetCluster.getString('url'),
|
||||
skipTLSVerify: targetCluster.getBoolean('skipTLSVerify'),
|
||||
});
|
||||
kc.addUser({
|
||||
name: 'scaffolder-user',
|
||||
token: targetCluster.getString('serviceAccountToken'),
|
||||
});
|
||||
kc.addContext({
|
||||
cluster: ctx.input.clusterName,
|
||||
user: 'scaffolder-user',
|
||||
name: ctx.input.clusterName,
|
||||
});
|
||||
kc.setCurrentContext(ctx.input.clusterName);
|
||||
|
||||
const client = kc.makeApiClient(CustomObjectsApi);
|
||||
// Server-side apply.
|
||||
if (ctx.input.namespaced) {
|
||||
await client
|
||||
.patchNamespacedCustomObject(
|
||||
group,
|
||||
version,
|
||||
obj.metadata.namespace,
|
||||
plural,
|
||||
obj.metadata.name,
|
||||
obj,
|
||||
undefined,
|
||||
'backstage',
|
||||
true,
|
||||
{ headers: { 'Content-Type': 'application/apply-patch+yaml' } },
|
||||
)
|
||||
.then(
|
||||
resp => {
|
||||
ctx.logger.info(
|
||||
`Successfully created ${obj.metadata.namespace}/${obj.metadata.name} Application: HTTP ${resp.response.statusCode}`,
|
||||
);
|
||||
},
|
||||
err => {
|
||||
ctx.logger.error(
|
||||
`Failed to make PATCH call for ${obj.metadata.namespace}/${
|
||||
obj.metadata.name
|
||||
} Application: Body ${JSON.stringify(
|
||||
err.body,
|
||||
null,
|
||||
2,
|
||||
)} Response ${JSON.stringify(err.response, null, 2)}.`,
|
||||
);
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
await client
|
||||
.patchClusterCustomObject(
|
||||
group,
|
||||
version,
|
||||
plural,
|
||||
obj.metadata.name,
|
||||
obj,
|
||||
undefined,
|
||||
'backstage',
|
||||
true,
|
||||
{ headers: { 'Content-Type': 'application/apply-patch+yaml' } },
|
||||
)
|
||||
.then(
|
||||
resp => {
|
||||
ctx.logger.info(
|
||||
`Successfully created ${obj.metadata.name} Application: HTTP ${resp.response.statusCode}`,
|
||||
);
|
||||
},
|
||||
err => {
|
||||
ctx.logger.error(
|
||||
`Failed to make PATCH call for ${
|
||||
obj.metadata.name
|
||||
} Application: Body ${JSON.stringify(
|
||||
err.body,
|
||||
null,
|
||||
2,
|
||||
)} Response ${JSON.stringify(err.response, null, 2)}.`,
|
||||
);
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
function getClusterConfig(name: string, config: Config): Config {
|
||||
const clusterConfigs = config
|
||||
.getConfigArray('kubernetes.clusterLocatorMethods')
|
||||
.filter((val: Config) => {
|
||||
return val.getString('type') === 'config';
|
||||
});
|
||||
|
||||
const clusters = new Array<Config>();
|
||||
clusterConfigs.filter((conf: Config) => {
|
||||
const cluster = conf.getConfigArray('clusters').find((val: Config) => {
|
||||
return val.getString('name') === name;
|
||||
});
|
||||
if (cluster) {
|
||||
clusters.push(cluster);
|
||||
}
|
||||
});
|
||||
|
||||
if (clusters.length === 0) {
|
||||
throw new Error(`Cluster with name ${name} not found`);
|
||||
}
|
||||
return clusters[0];
|
||||
}
|
|
@ -4,13 +4,15 @@ import { PluginEnvironment } from '../types';
|
|||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const catalogApi = new CatalogClient({discoveryApi: env.discovery});
|
||||
const {router} = await KubernetesBuilder.createBuilder({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
catalogApi,
|
||||
}).build();
|
||||
return router;
|
||||
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
catalogApi,
|
||||
permissions: env.permissions,
|
||||
}).build();
|
||||
|
||||
return router;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import {createBuiltinActions, createRouter} from '@backstage/plugin-scaffolder-backend';
|
||||
import {
|
||||
createBuiltinActions,
|
||||
createRouter,
|
||||
} from '@backstage/plugin-scaffolder-backend';
|
||||
import { Router } from 'express';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import {createInvokeArgoAction} from './workflow-argo'
|
||||
import { createInvokeArgoAction } from './workflow-argo';
|
||||
import {
|
||||
createZipAction,
|
||||
createSleepAction,
|
||||
|
@ -18,6 +21,7 @@ import {
|
|||
createYamlJSONataTransformAction,
|
||||
createJsonJSONataTransformAction,
|
||||
} from '@roadiehq/scaffolder-backend-module-utils';
|
||||
import { kubernetesApply } from './kubernetes-apply';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
|
@ -34,7 +38,7 @@ export default async function createPlugin(
|
|||
reader: env.reader,
|
||||
});
|
||||
|
||||
const scaffolderBackendModuleUtils = [
|
||||
const scaffolderBackendModuleUtils = [
|
||||
createZipAction(),
|
||||
createSleepAction(),
|
||||
createWriteFileAction(),
|
||||
|
@ -46,10 +50,15 @@ export default async function createPlugin(
|
|||
createSerializeJsonAction(),
|
||||
createJSONataAction(),
|
||||
createYamlJSONataTransformAction(),
|
||||
createJsonJSONataTransformAction()
|
||||
]
|
||||
|
||||
const actions = [...builtInActions, ...scaffolderBackendModuleUtils, createInvokeArgoAction(env.config, env.logger)];
|
||||
createJsonJSONataTransformAction(),
|
||||
];
|
||||
|
||||
const actions = [
|
||||
...builtInActions,
|
||||
...scaffolderBackendModuleUtils,
|
||||
createInvokeArgoAction(env.config, env.logger),
|
||||
kubernetesApply(env.config),
|
||||
];
|
||||
|
||||
return await createRouter({
|
||||
actions: actions,
|
||||
|
|
|
@ -7,6 +7,7 @@ import {HttpError} from "@kubernetes/client-node";
|
|||
type argoInput = {
|
||||
namespace: string
|
||||
clusterName: string
|
||||
userOIDCToken: string
|
||||
templateName: string
|
||||
parameters: parameter[]
|
||||
wait?: boolean
|
||||
|
@ -98,9 +99,14 @@ export function createInvokeArgoAction(config: Config, logger: Logger) {
|
|||
description: 'Name of Cluster',
|
||||
type: 'string',
|
||||
},
|
||||
userOIDCToken: {
|
||||
title: 'User\'s OIDC token',
|
||||
description: "If specified, it will use the provided token to communicate with the Kubernetes cluster",
|
||||
type: 'string'
|
||||
},
|
||||
templateName: {
|
||||
title: 'Template name',
|
||||
description: 'Argo Workflows template name',
|
||||
description: 'Argo Workflows template name to run',
|
||||
type: 'string',
|
||||
},
|
||||
parameters: {
|
||||
|
@ -121,7 +127,7 @@ export function createInvokeArgoAction(config: Config, logger: Logger) {
|
|||
},
|
||||
wait: {
|
||||
title: 'Wait for completion',
|
||||
description: 'specify weather to wait for completion of this workflow',
|
||||
description: 'specify weather to wait for completion of this workflow.',
|
||||
type: 'boolean',
|
||||
}
|
||||
},
|
||||
|
@ -142,22 +148,23 @@ export function createInvokeArgoAction(config: Config, logger: Logger) {
|
|||
},
|
||||
async handler(ctx: ActionContext<argoInput>) {
|
||||
logger.debug(`Invoked with ${JSON.stringify(ctx.input)})`)
|
||||
|
||||
logger.info(JSON.stringify(ctx.secrets))
|
||||
const targetCluster = getClusterConfig(ctx.input.clusterName, config)
|
||||
const kc = new k8s.KubeConfig()
|
||||
kc.addCluster({
|
||||
name: ctx.input.clusterName,
|
||||
name: targetCluster.getString("name"),
|
||||
caData: targetCluster.getString("caData"),
|
||||
server: targetCluster.getString("url"),
|
||||
skipTLSVerify: targetCluster.getBoolean("skipTLSVerify"),
|
||||
})
|
||||
|
||||
kc.addUser({
|
||||
name: "admin",
|
||||
token: targetCluster.getString("serviceAccountToken")
|
||||
name: "scaffolder-user",
|
||||
token: ctx.input.userOIDCToken? ctx.input.userOIDCToken : targetCluster.getString("serviceAccountToken")
|
||||
})
|
||||
kc.addContext({
|
||||
cluster: ctx.input.clusterName,
|
||||
user: "admin",
|
||||
user: "scaffolder-user",
|
||||
name: ctx.input.clusterName
|
||||
})
|
||||
kc.setCurrentContext(ctx.input.clusterName)
|
||||
|
@ -197,21 +204,23 @@ export function createInvokeArgoAction(config: Config, logger: Logger) {
|
|||
}
|
||||
|
||||
function getClusterConfig(name: string, config: Config): Config {
|
||||
const c = config.getConfigArray("kubernetes.clusterLocatorMethods")
|
||||
const cc = c.filter(function(val) {
|
||||
return val.getString("type") === "config"
|
||||
})
|
||||
|
||||
const clusterConfigs = config.getConfigArray("kubernetes.clusterLocatorMethods").filter(
|
||||
(val: Config) => {
|
||||
return val.getString('type') === 'config'
|
||||
}
|
||||
)
|
||||
|
||||
const clusters = new Array<Config>();
|
||||
// this is shit
|
||||
cc.forEach(function(conf ) {
|
||||
const cl = conf.getConfigArray("clusters")
|
||||
cl.forEach(function(val) {
|
||||
if (val.getString("name") === name) {
|
||||
clusters.push(val)
|
||||
}
|
||||
clusterConfigs.filter( (conf: Config) => {
|
||||
const cluster = conf.getConfigArray("clusters").find( (val: Config) => {
|
||||
return val.getString("name") === name
|
||||
})
|
||||
if (cluster) {
|
||||
clusters.push(cluster)
|
||||
}
|
||||
})
|
||||
|
||||
if (clusters.length === 0 ) {
|
||||
throw new Error(`Cluster with name ${name} not found`)
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-components": "^0.12.5",
|
||||
"@backstage/core-plugin-api": "^1.5.0",
|
||||
"@backstage/plugin-catalog-react": "^1.4.0",
|
||||
"@backstage/theme": "^0.2.18",
|
||||
"@backstage/core-components": "^0.13.1",
|
||||
"@backstage/core-plugin-api": "^1.5.1",
|
||||
"@backstage/plugin-catalog-react": "^1.6.0",
|
||||
"@backstage/theme": "^0.3.0",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
|
@ -36,10 +36,10 @@
|
|||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.22.5",
|
||||
"@backstage/core-app-api": "^1.6.0",
|
||||
"@backstage/dev-utils": "^1.0.13",
|
||||
"@backstage/test-utils": "^1.2.6",
|
||||
"@backstage/cli": "^0.22.7",
|
||||
"@backstage/core-app-api": "^1.8.0",
|
||||
"@backstage/dev-utils": "^1.0.15",
|
||||
"@backstage/test-utils": "^1.3.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
|
|
Loading…
Reference in a new issue