Skip to content

Commit 38a5851

Browse files
committed
Merge branch 'postgres-ha-cluster' into 'master'
feat(ui): postgres ha cluster (https://gitlab.com/postgres-ai/platform/-/issues/261) See merge request postgres-ai/database-lab!826
2 parents dfe61c8 + 2ea3b36 commit 38a5851

33 files changed

+3727
-637
lines changed

ui/cspell.json

+12-1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@
175175
"nvme",
176176
"HCLOUD",
177177
"gserviceaccount",
178-
"pgrst"
178+
"pgrst",
179+
"postgis",
180+
"pgbouncer",
181+
"pooler",
182+
"netdata",
183+
"Netdata",
184+
"pgcluster",
185+
"patroni",
186+
"pgnode",
187+
"pgbackrest",
188+
"vitabaks",
189+
"distro"
179190
]
180191
}

ui/packages/platform/src/api/configs/launchDeploy.ts

+77-32
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const launchDeploy = async ({
2121
orgKey,
2222
extraEnvs,
2323
cloudImage,
24+
launchType,
2425
}: {
2526
state: typeof initialState
2627
orgKey: string
@@ -29,43 +30,87 @@ export const launchDeploy = async ({
2930
[key: string]: string
3031
}
3132
cloudImage: string
33+
launchType: 'cluster' | 'instance'
3234
}) => {
35+
const instanceBody = {
36+
playbook: 'deploy_dle.yml',
37+
provision: state.provider,
38+
server: {
39+
name: state.name,
40+
serverType: state.instanceType.native_name,
41+
image: cloudImage,
42+
location: state.location.native_code,
43+
},
44+
image: 'postgresai/dle-se-ansible:v1.0-rc.8',
45+
extraVars: [
46+
`provision=${state.provider}`,
47+
`server_name=${state.name}`,
48+
`dle_platform_project_name=${state.name}`,
49+
`server_type=${state.instanceType.native_name}`,
50+
`server_image=${cloudImage}`,
51+
`server_location=${state.location.native_code}`,
52+
`volume_size=${state.storage}`,
53+
`dle_version=${state.tag}`,
54+
`zpool_datasets_number=${state.snapshots}`,
55+
`dle_verification_token=${state.verificationToken}`,
56+
`dle_platform_org_key=${orgKey}`,
57+
...(state.publicKeys
58+
? // eslint-disable-next-line no-useless-escape
59+
[`ssh_public_keys=\"${state.publicKeys}\"`]
60+
: []),
61+
...(API_SERVER === DEBUG_API_SERVER
62+
? [`dle_platform_url=https://v2.postgres.ai/api/general`]
63+
: []),
64+
],
65+
extraEnvs: formatExtraEnvs(extraEnvs),
66+
}
67+
68+
const clusterBody = {
69+
playbook: 'deploy_pgcluster.yml',
70+
provision: state.provider,
71+
server: {
72+
name: state.name,
73+
serverType: state.instanceType.native_name,
74+
image: cloudImage,
75+
location: state.location.native_code,
76+
},
77+
image: 'vitabaks/postgresql_cluster:cloud',
78+
extraVars: [
79+
`ansible_user=${state.provider === "aws" ? 'ubuntu' : 'root'}`,
80+
`provision=${state.provider}`,
81+
`servers_count=${state.numberOfInstances}`,
82+
`server_type=${state.instanceType.native_name}`,
83+
`server_image=${cloudImage}`,
84+
`server_location=${state.location.native_code}`,
85+
`volume_size=${state.storage}`,
86+
`postgresql_version=${state.version}`,
87+
`database_public_access=${state.database_public_access}`,
88+
`database_public_access=${state.database_public_access}`,
89+
`with_haproxy_load_balancing=${state.with_haproxy_load_balancing}`,
90+
`pgbouncer_install=${state.pgbouncer_install}`,
91+
`synchronous_mode=${state.synchronous_mode}`,
92+
...(state.synchronous_mode ? [`synchronous_node_count=${state.synchronous_node_count}`] : []),
93+
`netdata_install=${state.netdata_install}`,
94+
`patroni_cluster_name=${state.name}`,
95+
`platform_org_key=${orgKey}`,
96+
...(state.publicKeys
97+
? // eslint-disable-next-line no-useless-escape
98+
[`ssh_public_keys=\"${state.publicKeys}\"`]
99+
: []),
100+
...(API_SERVER === DEBUG_API_SERVER
101+
? [`platform_url=https://v2.postgres.ai/api/general`]
102+
: []),
103+
],
104+
extraEnvs: formatExtraEnvs(extraEnvs),
105+
}
106+
33107
const response = await simpleInstallRequest(
34108
'/launch',
35109
{
36110
method: 'POST',
37-
body: JSON.stringify({
38-
playbook: 'deploy_dle.yml',
39-
provision: state.provider,
40-
server: {
41-
name: state.name,
42-
serverType: state.instanceType.native_name,
43-
image: cloudImage,
44-
location: state.location.native_code,
45-
},
46-
image: 'postgresai/dle-se-ansible:v1.0-rc.8',
47-
extraVars: [
48-
`provision=${state.provider}`,
49-
`server_name=${state.name}`,
50-
`dle_platform_project_name=${state.name}`,
51-
`server_type=${state.instanceType.native_name}`,
52-
`server_image=${cloudImage}`,
53-
`server_location=${state.location.native_code}`,
54-
`volume_size=${state.storage}`,
55-
`dle_version=${state.tag}`,
56-
`zpool_datasets_number=${state.snapshots}`,
57-
`dle_verification_token=${state.verificationToken}`,
58-
`dle_platform_org_key=${orgKey}`,
59-
...(state.publicKeys
60-
? // eslint-disable-next-line no-useless-escape
61-
[`ssh_public_keys=\"${state.publicKeys}\"`]
62-
: []),
63-
...(API_SERVER === DEBUG_API_SERVER
64-
? [`dle_platform_url=https://v2.postgres.ai/api/general`]
65-
: []),
66-
],
67-
extraEnvs: formatExtraEnvs(extraEnvs),
68-
}),
111+
body: JSON.stringify(
112+
launchType === 'cluster' ? clusterBody : instanceBody,
113+
),
69114
},
70115
userID,
71116
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { StubContainer } from '@postgres.ai/shared/components/StubContainer'
2+
import { icons } from '@postgres.ai/shared/styles/icons'
3+
import { ConsoleButtonWrapper } from 'components/ConsoleButton/ConsoleButtonWrapper'
4+
import { ProductCardWrapper } from 'components/ProductCard/ProductCardWrapper'
5+
import { DashboardProps } from 'components/Dashboard/DashboardWrapper'
6+
7+
import Urls from '../../utils/urls'
8+
import { messages } from '../../assets/messages'
9+
import { useStyles } from 'components/CreateDbLabCards/CreateDbLabCards'
10+
11+
export const CreateClusterCards = ({
12+
props,
13+
dblabPermitted,
14+
}: {
15+
props: DashboardProps
16+
dblabPermitted: boolean | undefined
17+
}) => {
18+
const classes = useStyles()
19+
20+
const createClusterInstanceButton = (provider: string) => {
21+
props.history.push(Urls.linkClusterInstanceAdd(props, provider))
22+
}
23+
24+
const CreateButton = ({ type, title }: { type: string; title: string }) => (
25+
<ConsoleButtonWrapper
26+
disabled={!dblabPermitted}
27+
variant="contained"
28+
color="primary"
29+
onClick={() => createClusterInstanceButton(type)}
30+
title={dblabPermitted ? title : messages.noPermission}
31+
>
32+
{type === 'create' ? 'Create' : 'Install'}
33+
</ConsoleButtonWrapper>
34+
)
35+
36+
const productData = [
37+
{
38+
title: 'Create Postgres Cluster in your cloud',
39+
renderDescription: () => (
40+
<>
41+
<p>
42+
Supported cloud platforms include AWS, GCP, Digital Ocean, and
43+
Hetzner Cloud.
44+
</p>
45+
<p>All components are installed within your cloud account.</p>
46+
<p>Your data remains secure and never leaves your infrastructure.</p>
47+
</>
48+
),
49+
icon: icons.createDLEIcon,
50+
actions: [
51+
{
52+
id: 'createDblabInstanceButton',
53+
content: (
54+
<CreateButton type="create" title="Create Cluster in your cloud" />
55+
),
56+
},
57+
],
58+
},
59+
{
60+
title: 'BYOM (Bring Your Own Machines)',
61+
renderDescription: () => (
62+
<>
63+
<p>
64+
Install on your existing resources, regardless of the machine or
65+
location. Compatible with both cloud and bare metal infrastructures.
66+
Your data remains secure and never leaves your infrastructure.
67+
</p>
68+
<p>Requirements:</p>
69+
<ul>
70+
<li>
71+
Three or more servers running a supported Linux distro: Ubuntu
72+
(20.04/22.04), Debian (11/12), CentOS Stream (8/9), Rocky Linux
73+
(8/9), AlmaLinux (8/9), or Red Hat Enterprise Linux (8/9).
74+
</li>
75+
<li>Internet connectivity</li>
76+
</ul>
77+
</>
78+
),
79+
icon: icons.installDLEIcon,
80+
actions: [
81+
{
82+
id: 'createDblabInstanceButton',
83+
content: (
84+
<CreateButton
85+
type="install"
86+
title="Install Cluster on an existing machine"
87+
/>
88+
),
89+
},
90+
],
91+
},
92+
]
93+
94+
return (
95+
<StubContainer className={classes.stubContainerProjects}>
96+
{productData.map((product) => (
97+
<ProductCardWrapper
98+
inline
99+
className={classes.productCardProjects}
100+
title={product.title}
101+
actions={product.actions}
102+
icon={product.icon}
103+
>
104+
<div>{product.renderDescription()}</div>
105+
</ProductCardWrapper>
106+
))}
107+
</StubContainer>
108+
)
109+
}

ui/packages/platform/src/components/CreateDbLabCards/CreateDbLabCards.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import { DashboardProps } from 'components/Dashboard/DashboardWrapper'
88
import Urls from '../../utils/urls'
99
import { messages } from '../../assets/messages'
1010

11-
const useStyles = makeStyles((theme) => ({
11+
export const useStyles = makeStyles((theme) => ({
1212
stubContainerProjects: {
1313
marginRight: '-20px',
1414
padding: '0 40px',
15+
alignItems: 'initial !important',
1516

1617
'& > div:nth-child(1), & > div:nth-child(2)': {
1718
minHeight: '350px',

ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance.tsx

+44-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import {
2121
import { InstanceFormCreation } from 'components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation'
2222

2323
import { initialState } from '../reducer'
24+
import {
25+
cloneClusterRepositoryCommand,
26+
getClusterPlaybookCommandWithoutDocker,
27+
} from 'components/PostgresClusterForm/utils'
2428

2529
export const formStyles = makeStyles({
2630
marginTop: {
@@ -68,6 +72,10 @@ export const formStyles = makeStyles({
6872
},
6973
ul: {
7074
paddingInlineStart: '30px',
75+
76+
'& li': {
77+
marginBottom: '5px',
78+
},
7179
},
7280
important: {
7381
fontWeight: 600,
@@ -111,13 +119,15 @@ export const InstanceDocumentation = ({
111119
)
112120

113121
export const AnsibleInstance = ({
122+
cluster,
114123
state,
115124
orgId,
116125
goBack,
117126
goBackToForm,
118127
formStep,
119128
setFormStep,
120129
}: {
130+
cluster?: boolean
121131
state: typeof initialState
122132
orgId: number
123133
goBack: () => void
@@ -179,10 +189,24 @@ export const AnsibleInstance = ({
179189
</a>
180190
.
181191
</span>
182-
<p className={classes.title}>4. Clone the dle-se-ansible repository</p>
183-
<SyntaxHighlight content={cloneRepositoryCommand()} />
184-
<p className={classes.title}>5. Install requirements</p>
185-
<SyntaxHighlight content={'ansible-galaxy install -r requirements.yml'} />
192+
<p className={classes.title}>
193+
4. Clone the {cluster ? 'postgresql_cluster' : 'dle-se-ansible'}{' '}
194+
repository
195+
</p>
196+
<SyntaxHighlight
197+
content={
198+
cluster ? cloneClusterRepositoryCommand() : cloneRepositoryCommand()
199+
}
200+
/>
201+
202+
{!cluster && (
203+
<>
204+
<p className={classes.title}>5. Install requirements</p>
205+
<SyntaxHighlight
206+
content={'ansible-galaxy install -r requirements.yml'}
207+
/>
208+
</>
209+
)}
186210
</>
187211
)
188212

@@ -249,19 +273,26 @@ export const AnsibleInstance = ({
249273
) : null}
250274
<AnsibleInstallation />
251275
<p className={classes.title}>
252-
6. Run ansible playbook to create server and install DBLab SE
276+
{cluster
277+
? '5. Run ansible playbook to deploy Postgres Cluster'
278+
: '6. Run ansible playbook to create server and install DBLab SE'}
253279
</p>
254280
<SyntaxHighlight
255-
content={getPlaybookCommandWithoutDocker(
256-
state,
257-
cloudImages[0],
258-
orgKey,
259-
)}
281+
content={
282+
cluster
283+
? getClusterPlaybookCommandWithoutDocker(
284+
state,
285+
cloudImages[0],
286+
orgKey,
287+
)
288+
: getPlaybookCommandWithoutDocker(state, cloudImages[0], orgKey)
289+
}
260290
/>
261291
{getNetworkSubnet(state.provider, classes)}
262292
<p className={classes.title}>
263-
7. After the code snippet runs successfully, follow the directions
264-
displayed in the resulting output to start using DBLab AUI/API/CLI.
293+
{cluster
294+
? '6. After the code snippet runs successfully, follow the directions displayed in the resulting output to start using the database.'
295+
: '7. After the code snippet runs successfully, follow the directions displayed in the resulting output to start using DBLab AUI/API/CLI.'}
265296
</p>
266297
<Box
267298
sx={{
@@ -274,7 +305,7 @@ export const AnsibleInstance = ({
274305
Back to form
275306
</Button>
276307
<Button variant="contained" color="primary" onClick={goBack}>
277-
See list of instances
308+
See list of {cluster ? ' clusters' : ' instances'}
278309
</Button>
279310
</Box>
280311
</>

0 commit comments

Comments
 (0)