Skip to content

Commit

Permalink
Flux Runtime Card and Page (#55)
Browse files Browse the repository at this point in the history
Adds a Flux runtime page, listing the clusters and the version of Flux installed along with the installed controllers in each cluster.

Co-authored-by: Simon Howe <[email protected]>
  • Loading branch information
AlinaGoaga and foot authored Nov 6, 2023
1 parent fee13b2 commit 41414bb
Show file tree
Hide file tree
Showing 22 changed files with 1,001 additions and 18 deletions.
3 changes: 2 additions & 1 deletion packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
import { searchPage } from './components/search/SearchPage';
import { Root } from './components/Root';

import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components';
import { githubAuthApiRef } from '@backstage/core-plugin-api';
import { SignInPage } from '@backstage/core-components';
Expand All @@ -36,6 +35,7 @@ import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
import { EntityFluxHelmReleasesCard } from '@weaveworksoss/backstage-plugin-flux';
import { FluxRuntimePage } from '@weaveworksoss/backstage-plugin-flux';

const app = createApp({
components: {
Expand Down Expand Up @@ -111,6 +111,7 @@ const routes = (
<Route path="/settings" element={<UserSettingsPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
<Route path="/weaveworks-flux" element={<EntityFluxHelmReleasesCard />} />
<Route path="/flux-runtime" element={<FluxRuntimePage />} />
</FlatRoutes>
);

Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/components/Root/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '@backstage/core-components';
import MenuIcon from '@material-ui/icons/Menu';
import SearchIcon from '@material-ui/icons/Search';
import { FluxIcon } from '@weaveworksoss/backstage-plugin-flux';

const useSidebarLogoStyles = makeStyles({
root: {
Expand Down Expand Up @@ -73,6 +74,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
<SidebarDivider />
<SidebarScrollWrapper>
<SidebarItem icon={MapIcon} to="tech-radar" text="Tech Radar" />
<SidebarItem icon={FluxIcon} to="flux-runtime" text="Flux Runtime" />
</SidebarScrollWrapper>
</SidebarGroup>
<SidebarSpace />
Expand Down
48 changes: 47 additions & 1 deletion plugins/backstage-plugin-flux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ You can also add cards for resources with the following components, each of thes
- EntityFluxHelmRepositoriesCard
- EntityFluxImagePoliciesCard

The plugin also provides a page for viewing the Flux runtime state across your clusters, and a Card if you would prefer to include it some other page you have instead.

- FluxRuntimePage
- FluxRuntimeCard

As with other Backstage plugins, you can compose the UI you need.

## Prerequisite
Expand Down Expand Up @@ -277,6 +282,48 @@ kubernetes:
caData: LS0tLS1CRUdJTiBDRVJUSUZJQ0...
```

6. [Optional] Add a Flux Runtime page to your app

- An example Page is included as the `<FluxRuntimePage />` component.
- Add the page to your app by first adding a route in `App.tsx`

```tsx
// In packages/app/src/App.tsx
import { FluxRuntimePage } from '@weaveworksoss/backstage-plugin-flux';
// ...
const routes = (
<FlatRoutes>
...
<Route path="/flux-runtime" element={<FluxRuntimePage >} />
</FlatRoutes>
);
```

- Add the page to the navigation bar:

```tsx
// In packages/app/src/components/Root/Root.tsx
import { FluxIcon } from '@weaveworksoss/backstage-plugin-flux';
// ...
export const Root = ({ children }: PropsWithChildren<{}>) => (
<SidebarPage>
<Sidebar>
<SidebarGroup label="Menu" icon={<MenuIcon />}>
...
<SidebarScrollWrapper>
<SidebarItem icon={FluxIcon} to="flux-runtime" text="Flux Runtime" />
</SidebarScrollWrapper>
</SidebarGroup>
</Sidebar>
{children}
</SidebarPage>
```

## Verification

For the resources where we display a Verification status, if the Flux resource
Expand All @@ -302,4 +349,3 @@ Request failed with 401 Unauthorized, {"error":{"name":"AuthenticationError","me
This is likely caused by this issue in [Backstage](https://github.com/backstage/backstage/issues/12394).
The simplest thing to do is put some sort of authentication in front of your Backstage setup, for example using the [GitHub Authentication Provider](https://backstage.io/docs/auth/github/provider/) this will ensure there's an authentication token available.
23 changes: 23 additions & 0 deletions plugins/backstage-plugin-flux/dev/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,26 @@ export const newTestImagePolicy = (
},
};
};

export const newTestFluxController = (
name: string,
namespace: string,
labels: { [name: string]: string },
) => {
return {
apiVersion: 'meta.k8s.io/v1',
kind: 'PartialObjectMetadata',
metadata: {
name,
namespace,
uid: 'b062d329-538d-4bb3-b4df-b2ac4b06dba8',
resourceVersion: '1001263',
generation: 1,
creationTimestamp: '2023-10-19T16:34:14Z',
labels,
annotations: {
'deployment.kubernetes.io/revision': '1',
},
},
};
};
160 changes: 157 additions & 3 deletions plugins/backstage-plugin-flux/dev/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ import {
newTestKustomization,
newTestHelmRepository,
newTestImagePolicy,
newTestFluxController,
} from './helpers';
import { ReconcileRequestAnnotation } from '../src/hooks';
import { EntityFluxSourcesCard } from '../src/components/EntityFluxSourcesCard';
import { FluxRuntimeCard } from '../src/components/FluxRuntimeCard';
import {
NAMESPACES_PATH,
getDeploymentsPath,
} from '../src/hooks/useGetDeployments';
import { Namespace } from '../src/objects';

const fakeEntity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
Expand Down Expand Up @@ -72,7 +79,10 @@ class StubKubernetesClient implements KubernetesApi {

async getClusters(): Promise<{ name: string; authProvider: string }[]> {
await new Promise(resolve => setTimeout(resolve, 100));
return [{ name: 'mock-cluster', authProvider: 'serviceAccount' }];
return [
{ name: 'mock-cluster-1', authProvider: 'serviceAccount1' },
{ name: 'mock-cluster-2', authProvider: 'serviceAccount2' },
];
}

getWorkloadsByEntity(
Expand Down Expand Up @@ -117,12 +127,12 @@ class StubKubernetesClient implements KubernetesApi {
};
}

// this is only used by sync and suspend/resume
async proxy({
clusterName,
init,
path,
}: {
clusterName: string;
clusterName?: string;
path: string;
init?: RequestInit | undefined;
}): Promise<any> {
Expand Down Expand Up @@ -174,6 +184,93 @@ class StubKubernetesClient implements KubernetesApi {
} as Response;
}

if (!init?.method && path === NAMESPACES_PATH) {
if (clusterName === 'mock-cluster-1') {
return {
ok: true,
json: () =>
Promise.resolve({
kind: 'NamespacesList',
apiVersion: 'meta.k8s.io/v1',
items: [
{
metadata: {
name: 'flux-system',
labels: {
'app.kubernetes.io/instance': 'flux-system',
'app.kubernetes.io/part-of': 'flux',
'app.kubernetes.io/version': 'v2.0.0',
'kubernetes.io/metadata.name': 'flux-system',
'kustomize.toolkit.fluxcd.io/name': 'flux-system',
'kustomize.toolkit.fluxcd.io/namespace': 'flux-system',
'pod-security.kubernetes.io/warn': 'restricted',
'pod-security.kubernetes.io/warn-version': 'latest',
},
uid: '1dcca7cb-c651-4a86-93b4-ecf440df2353',
resourceVersion: '1583',
creationTimestamp: '2023-10-19T16:34:12Z',
},
} as Namespace,
],
}),
} as Response;
}
if (clusterName === 'mock-cluster-2') {
return {
ok: true,
json: () =>
Promise.resolve({
kind: 'NamespacesList',
apiVersion: 'meta.k8s.io/v1',
items: [
{
metadata: {
name: 'default',
uid: '1dcca7cb-c651-4a86-93b4-ecf440df2353',
resourceVersion: '1583',
creationTimestamp: '2023-10-19T16:34:12Z',
labels: {
'app.kubernetes.io/instance': 'default',
'app.kubernetes.io/part-of': 'flux',
'app.kubernetes.io/version': 'v2.0.0',
'kubernetes.io/metadata.name': 'default',
'kustomize.toolkit.fluxcd.io/name': 'default',
'kustomize.toolkit.fluxcd.io/namespace': 'default',
'pod-security.kubernetes.io/warn': 'restricted',
'pod-security.kubernetes.io/warn-version': 'latest',
},
},
} as Namespace,
],
}),
} as Response;
}
}

if (!init?.method && path === getDeploymentsPath('flux-system')) {
return {
ok: true,
json: () =>
Promise.resolve({
kind: 'DeploymentList',
apiVersion: 'apps/v1',
items: [this.resources[0], this.resources[1]],
}),
} as Response;
}

if (!init?.method && path === getDeploymentsPath('default')) {
return {
ok: true,
json: () =>
Promise.resolve({
kind: 'DeploymentList',
apiVersion: 'apps/v1',
items: [this.resources[2]],
}),
} as Response;
}

// very simple right now
if (this.mockResponses[path]?.length) {
// shift pops the [0]th element off the array
Expand Down Expand Up @@ -605,5 +702,62 @@ createDevApp()
</TestApiProvider>
),
})
.addPage({
title: 'Flux Runtime',
path: '/flux_runtime',
element: (
<TestApiProvider
apis={[
[
configApiRef,
new ConfigReader({
gitops: { baseUrl: 'https://example.com/wego' },
}),
],
[
kubernetesApiRef,
new StubKubernetesClient([
newTestFluxController('helm-controller', 'flux-system', {
'app.kubernetes.io/component': 'helm-controller',
'app.kubernetes.io/instance': 'flux-system',
'app.kubernetes.io/part-of': 'flux',
'app.kubernetes.io/version': 'v2.1.2',
'control-plane': 'controller',
'kustomize.toolkit.fluxcd.io/name': 'flux-system',
'kustomize.toolkit.fluxcd.io/namespace': 'flux-system',
}),
newTestFluxController(
'image-automation-controller',
'flux-system',
{
'app.kubernetes.io/component': 'image-automation-controller',
'app.kubernetes.io/instance': 'flux-system',
'app.kubernetes.io/part-of': 'flux',
'app.kubernetes.io/version': 'v2.1.2',
'control-plane': 'controller',
'kustomize.toolkit.fluxcd.io/name': 'flux-system',
'kustomize.toolkit.fluxcd.io/namespace': 'flux-system',
},
),
newTestFluxController('image-automation-controller', 'default', {
'app.kubernetes.io/component': 'image-automation-controller',
'app.kubernetes.io/instance': 'default',
'app.kubernetes.io/part-of': 'flux',
'app.kubernetes.io/version': 'v2.1.2',
'control-plane': 'controller',
'kustomize.toolkit.fluxcd.io/name': 'default',
'kustomize.toolkit.fluxcd.io/namespace': 'default',
}),
]),
],
[kubernetesAuthProvidersApiRef, new StubKubernetesAuthProvidersApi()],
]}
>
<Content>
<FluxRuntimeCard />
</Content>
</TestApiProvider>
),
})
.registerPlugin(weaveworksFluxPlugin)
.render();
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export function FluxEntityTable<T extends object = {}>({
emptyContent={
<div className={classes.empty}>
<Typography variant="body1">
No {title} found for this entity.
No {title} found
{title === 'flux controllers' ? '' : 'for this entity'}.
</Typography>
</div>
}
Expand Down
Loading

0 comments on commit 41414bb

Please sign in to comment.