Skip to content

Commit

Permalink
feat: [M3-7487] - Add AGLB Endpoint Health (#10008)
Browse files Browse the repository at this point in the history
* add load balancer endpoint health api call and query

* and mocks and inital ui

* add health to summary tab and add unit test

* add endpoint health to configurations

* add service target endpoint health

* add test and improve some comments

* add unit testing

* add more testing and comments

* Added changeset: AGLB endpoint health endpoints

* add changeset

* fix spelling of `LoadBalancerEndpontHeath` ➡️ `LoadBalancerEndpointHealth`

* add minimum with to skeleton so that loading state is not tiny

* hide `Endpoints:` if there is no status for a configuration

* update tense `Added` ➡️ `Add` in the changeset

* Make Configurations health behavior consistent with others

---------

Co-authored-by: Banks Nussman <banks@nussman.us>
  • Loading branch information
bnussman-akamai and bnussman authored Jan 16, 2024
1 parent 384d00b commit be1d910
Show file tree
Hide file tree
Showing 27 changed files with 661 additions and 6 deletions.
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-10008-added-1704919102710.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Added
---

AGLB endpoint health endpoints ([#10008](https://github.com/linode/manager/pull/10008))
18 changes: 18 additions & 0 deletions packages/api-v4/src/aglb/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BETA_API_ROOT } from '../constants';
import type {
Configuration,
ConfigurationPayload,
ConfigurationsEndpointHealth,
UpdateConfigurationPayload,
} from './types';
import {
Expand Down Expand Up @@ -56,6 +57,23 @@ export const getLoadbalancerConfiguration = (
setMethod('GET')
);

/**
* getLoadbalancerConfigurationsEndpointHealth
*
* Returns endpoint health for an Akamai Global Load Balancer configuration
*/
export const getLoadbalancerConfigurationsEndpointHealth = (
loadbalancerId: number
) =>
Request<ConfigurationsEndpointHealth>(
setURL(
`${BETA_API_ROOT}/aglb/${encodeURIComponent(
loadbalancerId
)}/configurations/endpoints-health`
),
setMethod('GET')
);

/**
* createLoadbalancerConfiguration
*
Expand Down
12 changes: 12 additions & 0 deletions packages/api-v4/src/aglb/loadbalancers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Filter, Params, ResourcePage } from '../types';
import type {
CreateBasicLoadbalancerPayload,
CreateLoadbalancerPayload,
LoadBalancerEndpointHealth,
Loadbalancer,
UpdateLoadbalancerPayload,
} from './types';
Expand Down Expand Up @@ -39,6 +40,17 @@ export const getLoadbalancer = (id: number) =>
setMethod('GET')
);

/**
* getLoadbalancerEndpointHealth
*
* Returns the general endpoint health of an Akamai Global Load Balancer
*/
export const getLoadbalancerEndpointHealth = (id: number) =>
Request<LoadBalancerEndpointHealth>(
setURL(`${BETA_API_ROOT}/aglb/${encodeURIComponent(id)}/endpoints-health`),
setMethod('GET')
);

/**
* createLoadbalancer
*
Expand Down
21 changes: 20 additions & 1 deletion packages/api-v4/src/aglb/service-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import Request, {
} from '../request';
import { Filter, Params, ResourcePage } from '../types';
import { BETA_API_ROOT } from '../constants';
import type { ServiceTarget, ServiceTargetPayload } from './types';
import type {
ServiceTarget,
ServiceTargetPayload,
ServiceTargetsEndpointHealth,
} from './types';
import {
CreateServiceTargetSchema,
UpdateServiceTargetSchema,
Expand Down Expand Up @@ -52,6 +56,21 @@ export const getServiceTarget = (
setMethod('GET')
);

/**
* getServiceTargetsEndpointHealth
*
* Returns endpoint health data for each service targets on an Akamai Global Load Balancer
*/
export const getServiceTargetsEndpointHealth = (loadbalancerId: number) =>
Request<ServiceTargetsEndpointHealth>(
setURL(
`${BETA_API_ROOT}/aglb/${encodeURIComponent(
loadbalancerId
)}/service-targets/endpoints-health`
),
setMethod('GET')
);

/**
* createLoadbalancerServiceTarget
*
Expand Down
39 changes: 39 additions & 0 deletions packages/api-v4/src/aglb/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,42 @@ export interface UpdateCertificatePayload {
label?: string;
type?: CertificateType;
}

export interface LoadBalancerEndpointHealth {
id: number;
healthy_endpoints: number;
total_endpoints: number;
timestamp: string;
}

export interface EndpointHealth {
id: number;
label: string;
url: string;
type: string;
healthy_endpoints: number;
total_endpoints: number;
timestamp: string;
}

export interface ConfigurationsEndpointHealth {
/**
* The id of the AGLB
*/
id: number;
/**
* An array of health data for each configuration on the AGLB
*/
configurations: EndpointHealth[];
}

export interface ServiceTargetsEndpointHealth {
/**
* The id of the AGLB
*/
id: number;
/**
* An array of health data for each service target on the AGLB
*/
service_targets: EndpointHealth[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add AGLB Endpoint Health ([#10008](https://github.com/linode/manager/pull/10008))
37 changes: 37 additions & 0 deletions packages/manager/src/factories/aglb.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {
Certificate,
Configuration,
ConfigurationsEndpointHealth,
CreateCertificatePayload,
CreateLoadbalancerPayload,
CreateRoutePayload,
Endpoint,
EndpointHealth,
LoadBalancerEndpointHealth,
Loadbalancer,
Route,
ServiceTarget,
ServiceTargetPayload,
ServiceTargetsEndpointHealth,
UpdateLoadbalancerPayload,
} from '@linode/api-v4/lib/aglb/types';
import * as Factory from 'factory.ts';
Expand Down Expand Up @@ -337,3 +341,36 @@ export const endpointFactory = Factory.Sync.makeFactory<Endpoint>({
port: 80,
rate_capacity: 10_000,
});

export const loadbalancerEndpointHealthFactory = Factory.Sync.makeFactory<LoadBalancerEndpointHealth>(
{
healthy_endpoints: 4,
id: Factory.each((i) => i),
timestamp: '2020-01-31T12:00:00',
total_endpoints: 6,
}
);

export const endpointHealthFactory = Factory.Sync.makeFactory<EndpointHealth>({
healthy_endpoints: 4,
id: Factory.each((i) => i),
label: Factory.each((i) => `endpoint-${i}`),
timestamp: '2020-01-31T12:00:00',
total_endpoints: 6,
type: '',
url: '',
});

export const configurationsEndpointHealthFactory = Factory.Sync.makeFactory<ConfigurationsEndpointHealth>(
{
configurations: endpointHealthFactory.buildList(5),
id: Factory.each((i) => i),
}
);

export const serviceTargetsEndpointHealthFactory = Factory.Sync.makeFactory<ServiceTargetsEndpointHealth>(
{
id: Factory.each((i) => i),
service_targets: endpointHealthFactory.buildList(5),
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ interface Props {

export const ConfigurationAccordion = (props: Props) => {
const { configuration } = props;
const { configurationId } = useParams<{ configurationId: string }>();
const { configurationId, loadbalancerId } = useParams<{
configurationId: string;
loadbalancerId: string;
}>();

return (
<Accordion
heading={
<ConfigurationAccordionHeader
configuration={configuration}
loadbalancerId={Number(loadbalancerId)}
/>
}
defaultExpanded={configuration.id === Number(configurationId)}
heading={<ConfigurationAccordionHeader configuration={configuration} />}
headingProps={{ sx: { width: '100%' } }}
>
<ConfigurationForm configuration={configuration} mode="edit" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';

import {
configurationFactory,
configurationsEndpointHealthFactory,
endpointHealthFactory,
} from 'src/factories';
import { rest, server } from 'src/mocks/testServer';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { ConfigurationAccordionHeader } from './ConfigurationAccordionHeader';

describe('ConfigurationAccordionHeader', () => {
it('renders configuration information', () => {
const configuration = configurationFactory.build({ routes: [] });

const { getByText } = renderWithTheme(
<ConfigurationAccordionHeader
configuration={configuration}
loadbalancerId={0}
/>
);

// Label
expect(getByText(configuration.label)).toBeVisible();

// ID
expect(getByText(`ID: ${configuration.id}`)).toBeVisible();

// Port
expect(
getByText(`Port ${configuration.port}`, { exact: false })
).toBeVisible();

// Number of Routes
expect(getByText('0 Routes', { exact: false })).toBeVisible();
});

it('renders endpoint health for a configuration', async () => {
const configuration = configurationFactory.build({ routes: [] });

const configurationHealth = endpointHealthFactory.build({
healthy_endpoints: 5,
id: configuration.id,
total_endpoints: 9,
});

const allConfigurationsEndpointHealth = configurationsEndpointHealthFactory.build(
{
configurations: [configurationHealth],
id: 5,
}
);

server.use(
rest.get(
'*/v4beta/aglb/5/configurations/endpoints-health',
(req, res, ctx) => res(ctx.json(allConfigurationsEndpointHealth))
)
);

const { findByText } = renderWithTheme(
<ConfigurationAccordionHeader
configuration={configuration}
loadbalancerId={5}
/>
);

await findByText('5 up');

await findByText('4 down'); // 9 - 5
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ import { Stack } from 'src/components/Stack';
import { Typography } from 'src/components/Typography';
import { pluralize } from 'src/utilities/pluralize';

import { ConfigurationEndpointHealth } from './ConfigurationEndpointHealth';

import type { Configuration } from '@linode/api-v4';

interface Props {
configuration: Configuration;
loadbalancerId: number;
}

export const ConfigurationAccordionHeader = ({ configuration }: Props) => {
export const ConfigurationAccordionHeader = (props: Props) => {
const { configuration, loadbalancerId } = props;

return (
<Stack
alignItems="center"
Expand All @@ -28,7 +33,13 @@ export const ConfigurationAccordionHeader = ({ configuration }: Props) => {
{pluralize('Route', 'Routes', configuration.routes.length)}
</Typography>
</Stack>
<Typography>ID: {configuration.id}</Typography>
<Stack alignItems="center" direction="row" spacing={2}>
<ConfigurationEndpointHealth
configurationId={configuration.id}
loadBalancerId={loadbalancerId}
/>
<Typography>ID: {configuration.id}</Typography>
</Stack>
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';

import {
configurationsEndpointHealthFactory,
endpointHealthFactory,
} from 'src/factories';
import { rest, server } from 'src/mocks/testServer';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { ConfigurationEndpointHealth } from './ConfigurationEndpointHealth';

describe('ConfigurationEndpointHealth', () => {
it('renders endpoint health from API data', async () => {
server.use(
rest.get(
'*/v4beta/aglb/:id/configurations/endpoints-health',
(req, res, ctx) => {
const health = configurationsEndpointHealthFactory.build({
configurations: [
endpointHealthFactory.build({
healthy_endpoints: 10,
id: 0,
total_endpoints: 20,
}),
],
});
return res(ctx.json(health));
}
)
);

const { findByText } = renderWithTheme(
<ConfigurationEndpointHealth configurationId={0} loadBalancerId={0} />
);

await findByText('10 up'); // 20 - 10 = 10

await findByText('10 down'); // 20 - 10 = 10
});
});
Loading

0 comments on commit be1d910

Please sign in to comment.