Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [LKEAPIFW-428] LKE clusters should have IP ACL integration on CM (part 1) #10968

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
693eaf6
[LKEAPIFW-428] LKE clusters now have IP ACLs
Sep 19, 2024
7817a17
[LKEAPIFW-428] Migration of non-ipacl'd clusters now working
Sep 22, 2024
13b644f
[LKEAPIFW-428] Ongoing UI tweaks
Sep 24, 2024
9ead96e
[LKEAPIFW-428] Additional UI tweaks
Sep 26, 2024
1eec5c0
[LKEAPIFW-428] Substituition of UI components
Sep 26, 2024
8c5534f
[LKEAPIFW-428] Another round of UI tweaks: multiline IP default value…
Sep 30, 2024
d41ff96
fix multiple ip css issues
hana-linode Oct 1, 2024
265a3ca
[LKEAPIFW-428] Copy text adjustment
Oct 2, 2024
bc7af8a
add changeset
coliu-akamai Oct 2, 2024
903ce45
Added changeset: ACL related endpoints and types for LKE clusters
coliu-akamai Oct 3, 2024
2bb302b
fix small eslint warnings + update spacing/design review with Daniel
coliu-akamai Oct 4, 2024
27bbbbb
get rid of extra divider, will need to make a few more design updates
coliu-akamai Oct 4, 2024
4af433e
updates as per Daniel's feedback
coliu-akamai Oct 7, 2024
1235902
margin fixes
coliu-akamai Oct 7, 2024
39610eb
height issues
coliu-akamai Oct 7, 2024
672ad55
Merge branch 'develop' into feature/lkeapifw-428
coliu-akamai Oct 8, 2024
900e18f
quick initial cleanup
coliu-akamai Oct 8, 2024
b324a6b
refactor some styling, initial transition to react-hook-form?
coliu-akamai Oct 9, 2024
fcecc60
adding some code description around the update cluster calls
Oct 10, 2024
34da662
move button file to Kube summary panel
coliu-akamai Oct 10, 2024
1a70f0b
invalidate query after installing ipacl
coliu-akamai Oct 10, 2024
ecbc30c
additional cleanup
coliu-akamai Oct 10, 2024
a5a6b4a
new UX changes, remove refresh
coliu-akamai Oct 11, 2024
6183ec0
make function async
coliu-akamai Oct 11, 2024
dbe9387
some bit more cleanup
coliu-akamai Oct 11, 2024
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
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-10968-added-1727966811522.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Added
---

ACL related endpoints and types for LKE clusters ([#10968](https://github.com/linode/manager/pull/10968))
1 change: 1 addition & 0 deletions packages/api-v4/src/account/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export type AccountCapability =
| 'Kubernetes'
| 'Linodes'
| 'LKE HA Control Planes'
| 'LKE Network Access Control List (IP ACL)'
| 'Machine Images'
| 'Managed Databases'
| 'Managed Databases Beta'
Expand Down
36 changes: 36 additions & 0 deletions packages/api-v4/src/kubernetes/kubernetes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
KubernetesEndpointResponse,
KubernetesDashboardResponse,
KubernetesVersion,
KubernetesControlPlaneACLPayload,
} from './types';

/**
Expand Down Expand Up @@ -221,3 +222,38 @@ export const getKubernetesTypes = (params?: Params) =>
setMethod('GET'),
setParams(params)
);

/**
* getKubernetesClusterControlPlaneACL
*
* Return control plane access list about a single Kubernetes cluster
*
*/
export const getKubernetesClusterControlPlaneACL = (clusterID: number) =>
Request<KubernetesControlPlaneACLPayload>(
setMethod('GET'),
setURL(
`${API_ROOT}/lke/clusters/${encodeURIComponent(
clusterID
)}/control_plane_acl`
)
);

/**
* updateKubernetesClusterControlPlaneACL
*
* Update an existing ACL from a single Kubernetes cluster.
*/
export const updateKubernetesClusterControlPlaneACL = (
clusterID: number,
data: Partial<KubernetesControlPlaneACLPayload>
) =>
Request<KubernetesControlPlaneACLPayload>(
setMethod('PUT'),
setURL(
`${API_ROOT}/lke/clusters/${encodeURIComponent(
clusterID
)}/control_plane_acl`
),
setData(data)
);
14 changes: 14 additions & 0 deletions packages/api-v4/src/kubernetes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,22 @@ export interface KubernetesDashboardResponse {
url: string;
}

export interface KubernetesControlPlaneACLPayload {
acl: ControlPlaneACLOptions;
}

export interface ControlPlaneACLOptions {
enabled?: boolean;
'revision-id'?: string;
addresses?: null | {
ipv4?: null | string[];
ipv6?: null | string[];
};
}

export interface ControlPlaneOptions {
high_availability?: boolean;
acl?: ControlPlaneACLOptions;
}

export interface CreateKubeClusterPayload {
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10968-added-1727901904107.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

IP ACL integration to LKE clusters ([#10968](https://github.com/linode/manager/pull/10968))
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ import { Interception } from 'cypress/types/net-stubbing';
const expectedGranularityArray = ['Auto', '1 day', '1 hr', '5 min'];
const timeDurationToSelect = 'Last 24 Hours';

const { metrics, id, serviceType, dashboardName, region, resource } =
widgetDetails.linode;
const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this file and factories/dashboards.ts snuck in as eslint fixes while I was resolving conflicts - gonna leave them in this PR

metrics,
id,
serviceType,
dashboardName,
region,
resource,
} = widgetDetails.linode;

const dashboard = dashboardFactory.build({
label: dashboardName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import Close from '@mui/icons-material/Close';
import { InputBaseProps } from '@mui/material/InputBase';
import Grid from '@mui/material/Unstable_Grid2';
import { Theme } from '@mui/material/styles';
import * as React from 'react';
import { makeStyles } from 'tss-react/mui';

Expand All @@ -13,7 +11,10 @@ import { StyledLinkButtonBox } from 'src/components/SelectFirewallPanel/SelectFi
import { TextField } from 'src/components/TextField';
import { TooltipIcon } from 'src/components/TooltipIcon';
import { Typography } from 'src/components/Typography';
import { ExtendedIP } from 'src/utilities/ipUtils';

import type { InputBaseProps } from '@mui/material/InputBase';
import type { Theme } from '@mui/material/styles';
import type { ExtendedIP } from 'src/utilities/ipUtils';

const useStyles = makeStyles()((theme: Theme) => ({
addIP: {
Expand Down Expand Up @@ -66,6 +67,7 @@ interface Props {
helperText?: string;
inputProps?: InputBaseProps;
ips: ExtendedIP[];
isLinkStyled?: boolean;
onBlur?: (ips: ExtendedIP[]) => void;
onChange: (ips: ExtendedIP[]) => void;
placeholder?: string;
Expand All @@ -83,6 +85,7 @@ export const MultipleIPInput = React.memo((props: Props) => {
forVPCIPv4Ranges,
helperText,
ips,
isLinkStyled,
onBlur,
onChange,
placeholder,
Expand Down Expand Up @@ -128,20 +131,21 @@ export const MultipleIPInput = React.memo((props: Props) => {
return null;
}

const addIPButton = forVPCIPv4Ranges ? (
<StyledLinkButtonBox>
<LinkButton onClick={addNewInput}>{buttonText}</LinkButton>
</StyledLinkButtonBox>
) : (
<Button
buttonType="secondary"
className={classes.addIP}
compactX
onClick={addNewInput}
>
{buttonText ?? 'Add an IP'}
</Button>
);
const addIPButton =
forVPCIPv4Ranges || isLinkStyled ? (
<StyledLinkButtonBox sx={{ marginTop: isLinkStyled ? '8px' : '12px' }}>
<LinkButton onClick={addNewInput}>{buttonText}</LinkButton>
</StyledLinkButtonBox>
Comment on lines +135 to +138
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

introduced a new prop, isLinkStyled, because of the margin differences (and forVPCIPv4Ranges does additional things) - this does feel a bit jank/not clean, but not too sure what else to do πŸ₯²...will keep thinking but putting this up for review

) : (
<Button
buttonType="secondary"
className={classes.addIP}
compactX
onClick={addNewInput}
>
{buttonText ?? 'Add an IP'}
</Button>
);

return (
<div className={cx(classes.root, className)}>
Expand Down
21 changes: 12 additions & 9 deletions packages/manager/src/factories/dashboards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export const widgetFactory = Factory.Sync.makeFactory<Widgets>({
y_label: Factory.each((i) => `y_label_${i}`),
});

export const dashboardMetricFactory =
Factory.Sync.makeFactory<AvailableMetrics>({
export const dashboardMetricFactory = Factory.Sync.makeFactory<AvailableMetrics>(
{
available_aggregate_functions: ['min', 'max', 'avg', 'sum'],
dimensions: [],
label: Factory.each((i) => `widget_label_${i}`),
Expand All @@ -62,25 +62,28 @@ export const dashboardMetricFactory =
(i) => scrape_interval[i % scrape_interval.length]
),
unit: 'defaultUnit',
});
}
);

export const cloudPulseMetricsResponseDataFactory =
Factory.Sync.makeFactory<CloudPulseMetricsResponseData>({
export const cloudPulseMetricsResponseDataFactory = Factory.Sync.makeFactory<CloudPulseMetricsResponseData>(
{
result: [
{
metric: {},
values: [],
},
],
result_type: 'matrix',
});
}
);

export const cloudPulseMetricsResponseFactory =
Factory.Sync.makeFactory<CloudPulseMetricsResponse>({
export const cloudPulseMetricsResponseFactory = Factory.Sync.makeFactory<CloudPulseMetricsResponse>(
{
data: cloudPulseMetricsResponseDataFactory.build(),
isPartial: false,
stats: {
series_fetched: 2,
},
status: 'success',
});
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Box, FormLabel } from '@mui/material';
import * as React from 'react';

import { ErrorMessage } from 'src/components/ErrorMessage';
import { FormControl } from 'src/components/FormControl';
import { FormControlLabel } from 'src/components/FormControlLabel';
import { Link } from 'src/components/Link';
import { MultipleIPInput } from 'src/components/MultipleIPInput/MultipleIPInput';
import { Notice } from 'src/components/Notice/Notice';
import { Toggle } from 'src/components/Toggle/Toggle';
import { Typography } from 'src/components/Typography';
import { validateIPs } from 'src/utilities/ipUtils';

import type { ExtendedIP } from 'src/utilities/ipUtils';

export interface ControlPlaneACLProps {
enableControlPlaneACL: boolean;
errorText: string | undefined;
handleIPv4Change: (ips: ExtendedIP[]) => void;
handleIPv6Change: (ips: ExtendedIP[]) => void;
ipV4Addr: ExtendedIP[];
ipV6Addr: ExtendedIP[];
setControlPlaneACL: (enabled: boolean) => void;
}

export const IPACLCopy = () => (
coliu-akamai marked this conversation as resolved.
Show resolved Hide resolved
<Typography>
This is the text for Control Plane Access Control.{' '}
<Link to="https://www.linode.com/docs/guides/enable-lke-high-availability/">
Learn more.
</Link>
</Typography>
);

export const ControlPlaneACLPane = (props: ControlPlaneACLProps) => {
const {
enableControlPlaneACL,
errorText,
handleIPv4Change,
handleIPv6Change,
ipV4Addr,
ipV6Addr,
setControlPlaneACL,
} = props;

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setControlPlaneACL(!enableControlPlaneACL);
};

const handleIPv4ChangeCB = React.useCallback(
(_ips: ExtendedIP[]) => {
handleIPv4Change(_ips);
},
[handleIPv4Change]
);

const handleIPv6ChangeCB = React.useCallback(
(_ips: ExtendedIP[]) => {
handleIPv6Change(_ips);
},
[handleIPv6Change]
);

return (
<>
<FormControl data-testid="control-plane-ipacl-form">
<FormLabel id="ipacl-radio-buttons-group-label">
<Typography variant="inherit">
Control Plane Access Control (IPACL)
</Typography>
</FormLabel>
{errorText && (
<Notice spacingTop={8} variant="error">
<ErrorMessage message={errorText} />{' '}
</Notice>
)}
<Typography mb={1}>
This is the text for Control Plane Access Control.{' '}
<Link to="https://www.linode.com/docs/guides/enable-lke-high-availability/">
Learn more.
</Link>
</Typography>
<FormControlLabel
control={
<Toggle
checked={enableControlPlaneACL}
name="ipacl-checkbox"
onChange={(e) => handleChange(e)}
/>
}
label="Enable IPACL"
/>
</FormControl>
{enableControlPlaneACL && (
<Box sx={{ marginBottom: 3, maxWidth: 450 }}>
<MultipleIPInput
onBlur={(_ips: ExtendedIP[]) => {
const validatedIPs = validateIPs(_ips, {
allowEmptyAddress: false,
errorMessage: 'Must be a valid IPv4 address.',
});
handleIPv4ChangeCB(validatedIPs);
}}
buttonText="Add IPv4 Address"
ips={ipV4Addr}
isLinkStyled
onChange={handleIPv4ChangeCB}
placeholder="0.0.0.0/0"
title="IPv4 Addresses or CIDRs"
/>
<Box marginTop={2}>
<MultipleIPInput
onBlur={(_ips: ExtendedIP[]) => {
const validatedIPs = validateIPs(_ips, {
allowEmptyAddress: false,
errorMessage: 'Must be a valid IPv6 address.',
});
handleIPv6ChangeCB(validatedIPs);
}}
buttonText="Add IPv6 Address"
ips={ipV6Addr}
isLinkStyled
onChange={handleIPv6ChangeCB}
placeholder="::/0"
title="IPv6 Addresses or CIDRs"
/>
</Box>
</Box>
)}
</>
);
};
Loading