Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions flux/src/actions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ActionButton, ConfirmDialog } from '@kinvolk/headlamp-plugin/lib/compon
import { KubeObject } from '@kinvolk/headlamp-plugin/lib/lib/k8s/cluster';
import { useSnackbar } from 'notistack';
import React from 'react';
import { useSource } from '../sources/Source';

function ForceReconciliationAction(props) {
const { enqueueSnackbar } = useSnackbar();
Expand Down Expand Up @@ -215,16 +216,20 @@ function SyncAction(props) {
}

function SyncWithSourceAction(props) {
const { resource, source } = props;
const { resource } = props;
const { enqueueSnackbar } = useSnackbar();
const source = useSource(resource);

if (!source) return null;

return (
<ActionButton
description="Sync with source"
onClick={() => {
enqueueSnackbar(`Starting sync for source ${source.metadata.name}`, { variant: 'info' });
const date = new Date().toISOString();
syncRequest(source, enqueueSnackbar, date).then(() => {
const get = source.constructor.apiEndpoint.get;
const get = (source.constructor as any).apiEndpoint.get;
let isSourceSynced = false;
get(source.metadata.namespace, source.metadata.name, newSource => {
if (newSource.status.lastHandledReconcileAt === date && !isSourceSynced) {
Expand Down
111 changes: 111 additions & 0 deletions flux/src/common/Resources.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { KubeObject } from '@kinvolk/headlamp-plugin/lib/k8s/cluster';

export class Kustomization extends KubeObject {
static kind = 'Kustomization';
static apiName = 'kustomizations';
static apiVersion = 'kustomize.toolkit.fluxcd.io/v1';
static isNamespaced = true;
}

export class HelmRelease extends KubeObject {
static kind = 'HelmRelease';
static apiName = 'helmreleases';
static apiVersion = ['helm.toolkit.fluxcd.io/v2', 'helm.toolkit.fluxcd.io/v2beta1'];
static isNamespaced = true;
}

export class GitRepository extends KubeObject {
static kind = 'GitRepository';
static apiName = 'gitrepositories';
static apiVersion = 'source.toolkit.fluxcd.io/v1';
static isNamespaced = true;
}

export class OCIRepository extends KubeObject {
static kind = 'OCIRepository';
static apiName = 'ocirepositories';
static apiVersion = 'source.toolkit.fluxcd.io/v1beta2';
static isNamespaced = true;
}

export class BucketRepository extends KubeObject {
static kind = 'Bucket';
static apiName = 'buckets';
static apiVersion = ['source.toolkit.fluxcd.io/v1', 'source.toolkit.fluxcd.io/v1beta2'];
static isNamespaced = true;
}

export class HelmRepository extends KubeObject {
static kind = 'HelmRepository';
static apiName = 'helmrepositories';
static apiVersion = ['source.toolkit.fluxcd.io/v1', 'source.toolkit.fluxcd.io/v1beta2'];
static isNamespaced = true;
}

export class HelmChart extends KubeObject {
static kind = 'HelmChart';
static apiName = 'helmcharts';
static apiVersion = ['source.toolkit.fluxcd.io/v1', 'source.toolkit.fluxcd.io/v1beta2'];
static isNamespaced = true;
}

export class AlertNotification extends KubeObject {
static kind = 'Alert';
static apiName = 'alerts';
static apiVersion = [
'notification.toolkit.fluxcd.io/v1beta3',
'notification.toolkit.fluxcd.io/v1beta2',
];
static isNamespaced = true;
}

export class ProviderNotification extends KubeObject {
static kind = 'Provider';
static apiName = 'providers';
static apiVersion = [
'notification.toolkit.fluxcd.io/v1beta3',
'notification.toolkit.fluxcd.io/v1beta2',
];
static isNamespaced = true;
}

export class ReceiverNotification extends KubeObject {
static kind = 'Receiver';
static apiName = 'receivers';
static apiVersion = [
'notification.toolkit.fluxcd.io/v1beta3',
'notification.toolkit.fluxcd.io/v1beta2',
'notification.toolkit.fluxcd.io/v1',
];
static isNamespaced = true;
}

export class ImageUpdateAutomation extends KubeObject {
static kind = 'ImageUpdateAutomation';
static apiName = 'imageupdateautomations';
static apiVersion = ['image.toolkit.fluxcd.io/v1beta2', 'image.toolkit.fluxcd.io/v1beta1'];
static isNamespaced = true;
}

export class ImagePolicy extends KubeObject {
static kind = 'ImagePolicy';
static apiName = 'imagepolicies';
static apiVersion = 'image.toolkit.fluxcd.io/v1beta2';
static isNamespaced = true;
}

export class ImageRepository extends KubeObject {
static kind = 'ImageRepository';
static apiName = 'imagerepositories';
static apiVersion = 'image.toolkit.fluxcd.io/v1beta2';
static isNamespaced = true;
}

export const getSourceClassByPluralName = (pluralName: string) =>
({
gitrepositories: GitRepository,
ocirepositories: OCIRepository,
buckets: BucketRepository,
helmrepositories: HelmRepository,
helmcharts: HelmChart,
}[pluralName]);
28 changes: 3 additions & 25 deletions flux/src/helm-releases/HelmReleaseList.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,14 @@
import { SectionBox, SectionFilterHeader } from '@kinvolk/headlamp-plugin/lib/components/common';
import { makeCustomResourceClass } from '@kinvolk/headlamp-plugin/lib/lib/k8s/crd';
import { useFilterFunc } from '@kinvolk/headlamp-plugin/lib/Utils';
import React from 'react';
import { NotSupported } from '../checkflux';
import { HelmRelease } from '../common/Resources';
import Table from '../common/Table';
import { NameLink } from '../helpers';

export function HelmReleases() {
return <HelmReleasesList />;
}

export function helmReleaseClass() {
const helmreleaseGroup = 'helm.toolkit.fluxcd.io';
const helmreleaseVersion = 'v2';

return makeCustomResourceClass({
apiInfo: [
{ group: helmreleaseGroup, version: helmreleaseVersion },
{ group: helmreleaseGroup, version: 'v2beta1' },
],
isNamespaced: true,
singularName: 'HelmRelease',
pluralName: 'helmreleases',
});
}

function HelmReleasesList() {
const filterFunction = useFilterFunc();
const [resources, setResources] = React.useState(null);
const [error, setError] = React.useState(null);

helmReleaseClass().useApiList(setResources, setError);
const [resources, error] = HelmRelease.useList();

if (error?.status === 404) {
return <NotSupported typeName="Helm Releases" />;
Expand All @@ -43,7 +21,7 @@ function HelmReleasesList() {
// @ts-ignore -- TODO Update the sorting param
defaultSortingColumn={2}
columns={[
NameLink(helmReleaseClass()),
NameLink(HelmRelease),
'namespace',
'status',
'source',
Expand Down
80 changes: 71 additions & 9 deletions flux/src/helm-releases/HelmReleaseSingle.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { registerDetailsViewSection } from '@kinvolk/headlamp-plugin/lib';
import {
ConditionsTable,
Link,
Expand All @@ -17,10 +18,9 @@ import {
SyncWithSourceAction,
} from '../actions/index';
import RemainingTimeDisplay from '../common/RemainingTimeDisplay';
import { HelmRelease } from '../common/Resources';
import StatusLabel from '../common/StatusLabel';
import { getSourceNameAndPluralKind, ObjectEvents } from '../helpers/index';
import { GetSource } from '../sources/Source';
import { helmReleaseClass } from './HelmReleaseList';
import { HelmInventory } from './Inventory';

export function FluxHelmReleaseDetailView(props: { name?: string; namespace?: string }) {
Expand All @@ -30,17 +30,80 @@ export function FluxHelmReleaseDetailView(props: { name?: string; namespace?: st
return (
<>
<CustomResourceDetails name={name} namespace={namespace} />
<ObjectEvents name={name} namespace={namespace} resourceClass={helmReleaseClass()} />
<ObjectEvents name={name} namespace={namespace} resourceClass={HelmRelease} />
</>
);
}

export const registerHelmRelease = () => {
registerDetailsViewSection(({ resource }: { resource: HelmRelease }) => {
console.log('flux', { resource });
if (resource.kind !== 'HelmRelease') return null;

const themeName = localStorage.getItem('headlampThemePreference');

const cr = resource;

return (
<>
{cr && cr?.jsonData?.spec?.values && (
<SectionBox title="Values">
<Editor
language="yaml"
value={YAML.stringify(cr?.jsonData?.spec?.values)}
height={200}
theme={themeName === 'dark' ? 'vs-dark' : 'light'}
/>
</SectionBox>
)}

<SectionBox title="Inventory">
<HelmInventory name={resource.metadata.name} namespace={resource.metadata.namespace} />
</SectionBox>

<SectionBox title="Dependencies">
<Table
data={cr?.jsonData?.spec?.dependsOn}
columns={[
{
header: 'Name',
accessorFn: item => (
<Link
routeName="helm"
params={{
name: item.name,
namespace: item.namespace || resource.metadata.namespace,
}}
>
{item.name}
</Link>
),
},
{
header: 'Namespace',
accessorFn: item => (
<Link
routeName="namespace"
params={{ name: item.namespace || resource.metadata.namespace }}
>
{item.namespace || resource.metadata.namespace}
</Link>
),
},
]}
/>
</SectionBox>
<SectionBox title="Conditions">
<ConditionsTable resource={cr?.jsonData} />
</SectionBox>
</>
);
});
};

function CustomResourceDetails(props) {
const { name, namespace } = props;
const [cr, setCr] = React.useState(null);
const [source, setSource] = React.useState(null);

helmReleaseClass().useApiGet(setCr, name, namespace);
const [cr] = HelmRelease.useGet(name, namespace);

function prepareExtraInfo(cr) {
if (!cr) {
Expand Down Expand Up @@ -131,7 +194,7 @@ function CustomResourceDetails(props) {
}

const actions = [];
actions.push(<SyncWithSourceAction resource={cr} source={source} />);
actions.push(<SyncWithSourceAction resource={cr} />);
actions.push(<SyncWithoutSourceAction resource={cr} />);
actions.push(<SuspendAction resource={cr} />);
actions.push(<ResumeAction resource={cr} />);
Expand All @@ -143,7 +206,6 @@ function CustomResourceDetails(props) {

return (
<>
{cr && <GetSource item={cr} setSource={setSource} />}
{cr && (
<MainInfoSection
resource={cr}
Expand Down
3 changes: 3 additions & 0 deletions flux/src/helpers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,21 @@ export function ObjectEventsRenderer(props: { events?: Event[] }) {
columns={[
{
header: 'Type',
gridTemplate: 'min-content',
accessorFn: item => {
return item.type;
},
},
{
header: 'Reason',
gridTemplate: 'min-content',
accessorFn: item => {
return item.reason;
},
},
{
header: 'From',
gridTemplate: 'min-content',
accessorFn: item => {
return item.source.component;
},
Expand Down
Loading
Loading