Skip to content

Commit 947630e

Browse files
committed
Merge branch 'dle-form-improvements' into 'master'
fix(ui): remove duplicate title, polish DLE form components See merge request postgres-ai/database-lab!759
2 parents 93f3822 + 11e6f33 commit 947630e

File tree

11 files changed

+461
-614
lines changed

11 files changed

+461
-614
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
import { Box } from '@mui/material'
2+
import { useEffect, useState } from 'react'
3+
import { makeStyles, Button } from '@material-ui/core'
4+
5+
import { Spinner } from '@postgres.ai/shared/components/Spinner'
6+
import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub'
7+
import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight'
8+
9+
import { getOrgKeys } from 'api/cloud/getOrgKeys'
10+
import { getCloudImages } from 'api/cloud/getCloudImages'
11+
12+
import {
13+
getGcpAccountContents,
14+
getPlaybookCommandWithoutDocker,
15+
} from 'components/DbLabInstanceForm/utils'
16+
import {
17+
cloneRepositoryCommand,
18+
getAnsibleInstallationCommand,
19+
} from 'components/DbLabInstanceInstallForm/utils'
20+
import { InstanceFormCreation } from 'components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation'
21+
22+
import { initialState } from '../reducer'
23+
24+
export const formStyles = makeStyles({
25+
marginTop: {
26+
marginTop: '20px',
27+
},
28+
marginBottom: {
29+
marginBottom: '20px',
30+
display: 'block',
31+
},
32+
spinner: {
33+
display: 'flex',
34+
justifyContent: 'center',
35+
alignItems: 'center',
36+
height: '100%',
37+
},
38+
title: {
39+
fontWeight: 600,
40+
fontSize: '15px',
41+
margin: '10px 0',
42+
},
43+
code: {
44+
backgroundColor: '#eee',
45+
borderRadius: '3px',
46+
padding: '0 3px',
47+
marginLeft: '0.25em',
48+
},
49+
ul: {
50+
paddingInlineStart: '30px',
51+
},
52+
important: {
53+
fontWeight: 600,
54+
margin: 0,
55+
},
56+
})
57+
58+
export const InstanceDocumentation = ({
59+
fistStep,
60+
firsStepDescription,
61+
documentation,
62+
secondStep,
63+
snippetContent,
64+
classes,
65+
}: {
66+
fistStep: string
67+
firsStepDescription?: React.ReactNode
68+
documentation: string
69+
secondStep: React.ReactNode
70+
snippetContent: string
71+
classes: ReturnType<typeof formStyles>
72+
}) => (
73+
<>
74+
<p className={classes.title}>1. {fistStep}</p>
75+
{firsStepDescription && <p>{firsStepDescription}</p>}
76+
<p className={classes.marginBottom}>
77+
Documentation:{' '}
78+
<a href={documentation} target="_blank" rel="noreferrer">
79+
{documentation}
80+
</a>
81+
</p>
82+
<p className={classes.title}>2. Export {secondStep}</p>
83+
<SyntaxHighlight content={snippetContent} />
84+
</>
85+
)
86+
87+
export const AnsibleInstance = ({
88+
state,
89+
orgId,
90+
goBack,
91+
goBackToForm,
92+
formStep,
93+
setFormStep,
94+
}: {
95+
state: typeof initialState
96+
orgId: number
97+
goBack: () => void
98+
goBackToForm: () => void
99+
formStep: string
100+
setFormStep: (step: string) => void
101+
}) => {
102+
const classes = formStyles()
103+
const [orgKey, setOrgKey] = useState('')
104+
const [isLoading, setIsLoading] = useState(false)
105+
const [cloudImages, setCloudImages] = useState([])
106+
const [orgKeyError, setOrgKeyError] = useState(false)
107+
108+
useEffect(() => {
109+
setIsLoading(true)
110+
getOrgKeys(orgId).then((data) => {
111+
if (data.error !== null || !Array.isArray(data.response)) {
112+
setIsLoading(false)
113+
setOrgKeyError(true)
114+
} else {
115+
setOrgKeyError(false)
116+
setOrgKey(data.response[0].value)
117+
}
118+
})
119+
getCloudImages({
120+
os_name: 'Ubuntu',
121+
os_version: '22.04%20LTS',
122+
arch: state.instanceType.arch,
123+
cloud_provider: state.provider,
124+
region: state.provider === 'aws' ? state.location.native_code : 'all',
125+
}).then((data) => {
126+
setIsLoading(false)
127+
setOrgKeyError(false)
128+
setCloudImages(data.response)
129+
})
130+
}, [
131+
orgId,
132+
state.instanceType.arch,
133+
state.location.native_code,
134+
state.provider,
135+
])
136+
137+
const AnsibleInstallation = () => (
138+
<>
139+
<p className={classes.title}>
140+
3. Install Ansible on your working machine (which could easily be a
141+
laptop)
142+
</p>
143+
<p>example of installing the latest version:</p>
144+
<SyntaxHighlight content={getAnsibleInstallationCommand()} />
145+
<span className={classes.marginBottom}>
146+
for more instructions on installing ansible, see{' '}
147+
<a
148+
target="_blank"
149+
href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html"
150+
rel="noreferrer"
151+
>
152+
here
153+
</a>
154+
.
155+
</span>
156+
<p className={classes.title}>4. Clone the dle-se-ansible repository</p>
157+
<SyntaxHighlight content={cloneRepositoryCommand()} />
158+
<p className={classes.title}>5. Install requirements</p>
159+
<SyntaxHighlight content={'ansible-galaxy install -r requirements.yml'} />
160+
</>
161+
)
162+
163+
return (
164+
<InstanceFormCreation formStep={formStep} setFormStep={setFormStep}>
165+
{isLoading ? (
166+
<span className={classes.spinner}>
167+
<Spinner />
168+
</span>
169+
) : (
170+
<>
171+
{orgKeyError ? (
172+
<ErrorStub
173+
title="Error 404"
174+
message="Unexpected error occurred. Please try again later"
175+
/>
176+
) : state.provider === 'digitalocean' ? (
177+
<InstanceDocumentation
178+
fistStep="Create Personal Access Token"
179+
documentation="https://docs.digitalocean.com/reference/api/create-personal-access-token"
180+
secondStep={
181+
<>
182+
<code className={classes.code}>DO_API_TOKEN</code>
183+
</>
184+
}
185+
snippetContent="export DO_API_TOKEN=XXXXXX"
186+
classes={classes}
187+
/>
188+
) : state.provider === 'hetzner' ? (
189+
<InstanceDocumentation
190+
fistStep="Create API Token"
191+
documentation="https://docs.hetzner.com/cloud/api/getting-started/generating-api-token"
192+
secondStep={
193+
<code className={classes.code}>HCLOUD_API_TOKEN</code>
194+
}
195+
snippetContent="export HCLOUD_API_TOKEN=XXXXXX"
196+
classes={classes}
197+
/>
198+
) : state.provider === 'aws' ? (
199+
<InstanceDocumentation
200+
fistStep="Create access key"
201+
documentation="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html"
202+
secondStep={
203+
<>
204+
<code className={classes.code}>AWS_ACCESS_KEY_ID </code> and
205+
<code className={classes.code}>AWS_SECRET_ACCESS_KEY</code>
206+
</>
207+
}
208+
snippetContent={`export AWS_ACCESS_KEY_ID=XXXXXX\nexport AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXX`}
209+
classes={classes}
210+
/>
211+
) : state.provider === 'gcp' ? (
212+
<InstanceDocumentation
213+
fistStep="Create a service account"
214+
documentation="https://developers.google.com/identity/protocols/oauth2/service-account#creatinganaccount"
215+
secondStep={
216+
<code className={classes.code}>
217+
GCP_SERVICE_ACCOUNT_CONTENTS
218+
</code>
219+
}
220+
snippetContent={getGcpAccountContents()}
221+
classes={classes}
222+
/>
223+
) : null}
224+
<AnsibleInstallation />
225+
<p className={classes.title}>
226+
6. Run ansible playbook to create server and install DLE SE
227+
</p>
228+
<SyntaxHighlight
229+
content={getPlaybookCommandWithoutDocker(
230+
state,
231+
cloudImages[0],
232+
orgKey,
233+
)}
234+
/>
235+
<p className={classes.title}>
236+
7. After the code snippet runs successfully, follow the directions
237+
displayed in the resulting output to start using DLE AUI/API/CLI.
238+
</p>
239+
<Box
240+
sx={{
241+
display: 'flex',
242+
gap: '10px',
243+
margin: '20px 0',
244+
}}
245+
>
246+
<Button variant="contained" color="primary" onClick={goBack}>
247+
See list of instances
248+
</Button>
249+
<Button variant="outlined" color="secondary" onClick={goBackToForm}>
250+
Back to form
251+
</Button>
252+
</Box>
253+
</>
254+
)}
255+
</InstanceFormCreation>
256+
)
257+
}

0 commit comments

Comments
 (0)