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

upcoming: [M3-7927] - Linode Create Refactor - Part 6 - Add-ons #10319

Merged
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

Added jsdoc style comments to `CreateLinodeRequest` based on API documentation ([#10319](https://github.com/linode/manager/pull/10319))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Changed
---

Allows `firewall_id` to be `null` in `CreateLinodeRequest` ([#10319](https://github.com/linode/manager/pull/10319))
82 changes: 81 additions & 1 deletion packages/api-v4/src/linodes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,24 +350,104 @@ export interface CreateLinodePlacementGroupPayload {
}

export interface CreateLinodeRequest {
/**
* The Linode Type of the Linode you are creating.
*/
type: string;
/**
* The Region where the Linode will be located.
*/
region: string;
/**
* A StackScript ID that will cause the referenced StackScript to be run during deployment of this Linode.
*
* This field cannot be used when deploying from a Backup or a Private Image.
*/
stackscript_id?: number;
/**
* A Backup ID from another Linode’s available backups.
*
* Your User must have read_write access to that Linode,
* the Backup must have a status of successful,
* and the Linode must be deployed to the same region as the Backup.
*
* This field and the image field are mutually exclusive.
*/
backup_id?: number;
/**
* When deploying from an Image, this field is optional, otherwise it is ignored.
* This is used to set the swap disk size for the newly-created Linode.
* @default 512
*/
swap_size?: number;
/**
* An Image ID to deploy the Linode Disk from.
*/
image?: string | null;
/**
* This sets the root user’s password on a newly-created Linode Disk when deploying from an Image.
*/
root_pass?: string;
/**
* A list of public SSH keys that will be automatically appended to the root user’s
* `~/.ssh/authorized_keys`file when deploying from an Image.
*/
authorized_keys?: string[];
/**
* If this field is set to true, the created Linode will automatically be enrolled in the Linode Backup service.
* This will incur an additional charge. The cost for the Backup service is dependent on the Type of Linode deployed.
*
* This option is always treated as true if the account-wide backups_enabled setting is true.
*
* @default false
*/
backups_enabled?: boolean;
/**
* This field is required only if the StackScript being deployed requires input data from the User for successful completion
*/
stackscript_data?: any;
/**
* If it is deployed from an Image or a Backup and you wish it to remain offline after deployment, set this to false.
* @default true if the Linode is created with an Image or from a Backup.
*/
booted?: boolean;
/**
* The Linode’s label is for display purposes only.
* If no label is provided for a Linode, a default will be assigned.
*/
label?: string;
/**
* An array of tags applied to this object.
*
* Tags are for organizational purposes only.
*/
tags?: string[];
/**
* If true, the created Linode will have private networking enabled and assigned a private IPv4 address.
* @default false
*/
private_ip?: boolean;
/**
* A list of usernames. If the usernames have associated SSH keys,
* the keys will be appended to the root users `~/.ssh/authorized_keys`
* file automatically when deploying from an Image.
*/
authorized_users?: string[];
/**
* An array of Network Interfaces to add to this Linode’s Configuration Profile.
*/
interfaces?: InterfacePayload[];
/**
* An object containing user-defined data relevant to the creation of Linodes.
*/
metadata?: UserData;
firewall_id?: number;
/**
* The `id` of the Firewall to attach this Linode to upon creation.
*/
firewall_id?: number | null;
/**
* An object that assigns this the Linode to a placment group upon creation.
*/
placement_group?: CreateLinodePlacementGroupPayload;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Linode Create Refactor - Part 6 - Add-ons ([#10319](https://github.com/linode/manager/pull/10319))
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';

import { regionFactory } from 'src/factories';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { HttpResponse, http, server } from 'src/mocks/testServer';
import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';

import { Addons } from './Addons';

describe('Linode Create v2 Addons', () => {
it('should render an "Add-ons" heading', () => {
const { getByText } = renderWithThemeAndHookFormContext({
component: <Addons />,
});

const heading = getByText('Add-ons');

expect(heading).toBeVisible();
expect(heading.tagName).toBe('H2');
});

it('renders a warning if an edge region is selected', async () => {
const region = regionFactory.build({ site_type: 'edge' });

server.use(
http.get('*/v4/regions', () => {
return HttpResponse.json(makeResourcePage([region]));
})
);

const { findByText } = renderWithThemeAndHookFormContext({
component: <Addons />,
useFormOptions: { defaultValues: { region: region.id } },
});

await findByText(
'Backups and Private IP are currently not available for Edge regions.'
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useMemo } from 'react';
import { useWatch } from 'react-hook-form';

import { Divider } from 'src/components/Divider';
import { Notice } from 'src/components/Notice/Notice';
import { Paper } from 'src/components/Paper';
import { Stack } from 'src/components/Stack';
import { Typography } from 'src/components/Typography';
import { useRegionsQuery } from 'src/queries/regions/regions';

import { Backups } from './Backups';
import { PrivateIP } from './PrivateIP';

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

export const Addons = () => {
const regionId = useWatch<CreateLinodeRequest, 'region'>({ name: 'region' });

const { data: regions } = useRegionsQuery();

const selectedRegion = useMemo(
() => regions?.find((r) => r.id === regionId),
[regions, regionId]
);

const isEdgeRegionSelected = selectedRegion?.site_type === 'edge';

return (
<Paper>
<Stack spacing={2}>
<Typography variant="h2">Add-ons</Typography>
{isEdgeRegionSelected && (
<Notice
text="Backups and Private IP are currently not available for Edge regions."
variant="warning"
/>
)}
Comment on lines +32 to +37
Copy link
Member Author

Choose a reason for hiding this comment

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

There are other notices that will need to be added here related to cloning. I will add those when I add cloning support down the road.

<Stack divider={<Divider />} spacing={2}>
<Backups />
<PrivateIP />
</Stack>
</Stack>
</Paper>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { waitFor } from '@testing-library/react';
import React from 'react';

import {
accountSettingsFactory,
profileFactory,
regionFactory,
} from 'src/factories';
import { grantsFactory } from 'src/factories/grants';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { HttpResponse, http, server } from 'src/mocks/testServer';
import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';

import { Backups } from './Backups';

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

describe('Linode Create V2 Backups Addon', () => {
it('should render a label and checkbox', () => {
const { getByLabelText } = renderWithThemeAndHookFormContext({
component: <Backups />,
});

const checkbox = getByLabelText('Backups', { exact: false });

expect(checkbox).toBeEnabled();
expect(checkbox).not.toBeChecked();
});

it('should get its value from the form context', () => {
const {
getByRole,
} = renderWithThemeAndHookFormContext<CreateLinodeRequest>({
component: <Backups />,
useFormOptions: { defaultValues: { backups_enabled: true } },
});

const checkbox = getByRole('checkbox');

expect(checkbox).toBeEnabled();
expect(checkbox).toBeChecked();
});

it('should render special copy, be checked, and be disabled if account backups are enabled', async () => {
server.use(
http.get('*/v4/account/settings', () => {
return HttpResponse.json(
accountSettingsFactory.build({ backups_enabled: true })
);
})
);

const { findByText, getByRole } = renderWithThemeAndHookFormContext({
component: <Backups />,
});

const checkbox = getByRole('checkbox');

await findByText('You have enabled automatic backups for your account.', {
exact: false,
});

expect(checkbox).toBeDisabled();
expect(checkbox).toBeChecked();
});

it('should be disabled if an edge region is selected', async () => {
const region = regionFactory.build({ site_type: 'edge' });

server.use(
http.get('*/v4/regions', () => {
return HttpResponse.json(makeResourcePage([region]));
})
);

const {
getByRole,
} = renderWithThemeAndHookFormContext<CreateLinodeRequest>({
component: <Backups />,
useFormOptions: { defaultValues: { region: region.id } },
});

const checkbox = getByRole('checkbox');

await waitFor(() => {
expect(checkbox).toBeDisabled();
});
});

it('should be disabled if the user does not have permission to create a linode', async () => {
server.use(
http.get('*/v4/profile', () => {
return HttpResponse.json(profileFactory.build({ restricted: true }));
}),
http.get('*/v4/profile/grants', () => {
return HttpResponse.json(
grantsFactory.build({ global: { add_linodes: false } })
);
})
);

const {
getByRole,
} = renderWithThemeAndHookFormContext<CreateLinodeRequest>({
component: <Backups />,
});

const checkbox = getByRole('checkbox');

await waitFor(() => {
expect(checkbox).toBeDisabled();
});
});
});
Loading
Loading