feat(helm): make the atomic flag optional [r8s-314] (#733)

release/2.30.0
Ali 2025-05-14 16:31:42 +12:00 committed by GitHub
parent 4ee349bd6b
commit d49fcd8f3e
11 changed files with 71 additions and 10 deletions

View File

@ -27,6 +27,7 @@ type installChartPayload struct {
Repo string `json:"repo"`
Values string `json:"values"`
Version string `json:"version"`
Atomic bool `json:"atomic"`
}
var errChartNameInvalid = errors.New("invalid chart name. " +
@ -105,6 +106,7 @@ func (handler *Handler) installChart(r *http.Request, p installChartPayload) (*r
Version: p.Version,
Namespace: p.Namespace,
Repo: p.Repo,
Atomic: p.Atomic,
KubernetesClusterAccess: clusterAccess,
}

View File

@ -11,6 +11,7 @@ import { Input } from '@@/form-components/Input';
import { CodeEditor } from '@@/CodeEditor';
import { FormControl } from '@@/form-components/FormControl';
import { WidgetTitle } from '@@/Widget';
import { Checkbox } from '@@/form-components/Checkbox';
import { UpdateHelmReleasePayload } from '../queries/useUpdateHelmReleaseMutation';
import { ChartVersion } from '../queries/useHelmRepositories';
@ -37,7 +38,7 @@ export function UpgradeHelmModal({ values, versions, onSubmit }: Props) {
versionOptions[0]?.value;
const [version, setVersion] = useState<ChartVersion>(defaultVersion);
const [userValues, setUserValues] = useState<string>(values.values || '');
const [atomic, setAtomic] = useState<boolean>(false);
return (
<Modal
onDismiss={() => onSubmit()}
@ -88,6 +89,20 @@ export function UpgradeHelmModal({ values, versions, onSubmit }: Props) {
data-cy="helm-namespace-input"
/>
</FormControl>
<FormControl
label="Rollback on failure"
tooltip="Enables automatic rollback on failure (equivalent to the helm --atomic flag). It may increase the time to upgrade."
inputId="atomic-input"
className="[&>label]:!pl-0"
size="medium"
>
<Checkbox
id="atomic-input"
checked={atomic}
data-cy="atomic-checkbox"
onChange={(e) => setAtomic(e.target.checked)}
/>
</FormControl>
<FormControl
label="User-defined values"
inputId="user-values-editor"
@ -125,6 +140,7 @@ export function UpgradeHelmModal({ values, versions, onSubmit }: Props) {
chart: values.chart,
repo: version.Repo,
version: version.Version,
atomic,
})
}
color="primary"

View File

@ -218,7 +218,9 @@ describe('HelmEventsDatatable', () => {
await waitFor(() => {
expect(
screen.getByText('Events reflect the latest revision only.')
screen.getByText(
'Only events for resources currently in the cluster will be displayed.'
)
).toBeInTheDocument();
});

View File

@ -42,7 +42,8 @@ export function HelmEventsDatatable({
dataset={eventsQuery.data || []}
title={
<TextTip inline color="blue" className="!text-xs">
Events reflect the latest revision only.
Only events for resources currently in the cluster will be
displayed.
</TextTip>
}
titleIcon={null}

View File

@ -167,9 +167,13 @@ describe('ResourcesTable', () => {
);
// Check that success badge is rendered
const successBadge = screen.getByText('MinimumReplicasAvailable');
const successBadge = screen.getByText(
(content, element) =>
content.includes('MinimumReplicasAvailable') &&
element !== null &&
element.className.includes('bg-success')
);
expect(successBadge).toBeInTheDocument();
expect(successBadge.className).toContain('bg-success');
});
it('should show error badges for failed resources', () => {
@ -177,8 +181,12 @@ describe('ResourcesTable', () => {
expect(screen.getByText('probe-failure-nginx-bad')).toBeInTheDocument();
// Check for the unhealthy status badge and make sure it has the error styling
const errorBadge = screen.getByText('InsufficientPods');
const errorBadge = screen.getByText(
(content, element) =>
content.includes('InsufficientPods') &&
element !== null &&
element.className.includes('bg-error')
);
expect(errorBadge).toBeInTheDocument();
expect(errorBadge.className).toContain('bg-error');
});
});

View File

@ -59,7 +59,7 @@ export function ResourcesTable() {
emptyContentLabel="No resources found"
title={
<TextTip inline color="blue" className="!text-xs">
Resources reflect the latest revision only.
Only resources currently in the cluster will be displayed.
</TextTip>
}
disableSelect

View File

@ -1,6 +1,20 @@
import { Row } from '@tanstack/react-table';
import { filterHOC } from '@@/datatables/Filter';
import { ResourceRow } from '../types';
import { columnHelper } from './helper';
export const resourceType = columnHelper.accessor((row) => row.resourceType, {
header: 'Resource type',
id: 'resourceType',
meta: {
filter: filterHOC('Filter by resource type'),
},
enableColumnFilter: true,
filterFn: (row: Row<ResourceRow>, _: string, filterValue: string[]) =>
filterValue.length === 0 ||
(!!row.original.resourceType &&
filterValue.includes(row.original.resourceType)),
});

View File

@ -1,6 +1,7 @@
import { CellContext } from '@tanstack/react-table';
import { CellContext, Row } from '@tanstack/react-table';
import { StatusBadge } from '@@/StatusBadge';
import { filterHOC } from '@@/datatables/Filter';
import { ResourceRow } from '../types';
@ -10,6 +11,21 @@ export const status = columnHelper.accessor((row) => row.status.label, {
header: 'Status',
id: 'status',
cell: Cell,
meta: {
filter: filterHOC(
'Filter by status',
// don't include empty values in the filter options
(rows: Row<ResourceRow>[]) =>
Array.from(
new Set(rows.map((row) => row.original.status.label).filter(Boolean))
)
),
},
enableColumnFilter: true,
filterFn: (row: Row<ResourceRow>, _: string, filterValue: string[]) =>
filterValue.length === 0 ||
(!!row.original.status.label &&
filterValue.includes(row.original.status.label)),
});
function Cell({ row }: CellContext<ResourceRow, string>) {

View File

@ -14,6 +14,7 @@ export interface UpdateHelmReleasePayload {
name: string;
chart: string;
version?: string;
atomic?: boolean;
}
export function useUpdateHelmReleaseMutation(environmentId: EnvironmentId) {
const queryClient = useQueryClient();

View File

@ -11,6 +11,7 @@ type InstallOptions struct {
Wait bool
ValuesFile string
PostRenderer string
Atomic bool
Timeout time.Duration
KubernetesClusterAccess *KubernetesClusterAccess

View File

@ -133,7 +133,7 @@ func (hspm *HelmSDKPackageManager) Upgrade(upgradeOpts options.InstallOptions) (
func initUpgradeClient(actionConfig *action.Configuration, upgradeOpts options.InstallOptions) (*action.Upgrade, error) {
upgradeClient := action.NewUpgrade(actionConfig)
upgradeClient.DependencyUpdate = true
upgradeClient.Atomic = true
upgradeClient.Atomic = upgradeOpts.Atomic
upgradeClient.ChartPathOptions.RepoURL = upgradeOpts.Repo
upgradeClient.Wait = upgradeOpts.Wait