FormDebugger.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. import React, { Component } from "react";
  2. import styled from "styled-components";
  3. import AceEditor from "react-ace";
  4. import PorterFormWrapper from "./PorterFormWrapper";
  5. import CheckboxRow from "components/form-components/CheckboxRow";
  6. import InputRow from "components/form-components/InputRow";
  7. import yaml from "js-yaml";
  8. import "shared/ace-porter-theme";
  9. import "ace-builds/src-noconflict/mode-text";
  10. import Heading from "../form-components/Heading";
  11. import Helper from "../form-components/Helper";
  12. import { ChartType } from "shared/types";
  13. type PropsType = {
  14. goBack: () => void;
  15. };
  16. type StateType = {
  17. rawYaml: string;
  18. showBonusTabs: boolean;
  19. showStateDebugger: boolean;
  20. valuesToOverride: any;
  21. checkbox_a: boolean;
  22. input_a: string;
  23. isReadOnly: boolean;
  24. };
  25. const tabOptions = [
  26. { value: "a", label: "Bonus Tab A" },
  27. { value: "b", label: "Bonus Tab B" },
  28. ];
  29. export default class FormDebugger extends Component<PropsType, StateType> {
  30. state = {
  31. rawYaml: initYaml,
  32. showBonusTabs: false,
  33. showStateDebugger: true,
  34. valuesToOverride: {
  35. checkbox_a: {
  36. value: true,
  37. },
  38. } as any,
  39. checkbox_a: true,
  40. input_a: "",
  41. isReadOnly: false,
  42. };
  43. renderTabContents = (currentTab: string) => {
  44. return (
  45. <TabWrapper>
  46. {this.state.rawYaml.toString().slice(0, 300) || "No raw YAML inputted."}
  47. </TabWrapper>
  48. );
  49. };
  50. aceEditorRef = React.createRef<AceEditor>();
  51. render() {
  52. let formData = {};
  53. try {
  54. formData = yaml.load(this.state.rawYaml);
  55. } catch (err: any) {
  56. console.log("YAML parsing error.", err);
  57. }
  58. return (
  59. <StyledFormDebugger>
  60. <Button onClick={this.props.goBack}>
  61. <i className="material-icons">keyboard_backspace</i>
  62. Back
  63. </Button>
  64. <Heading isAtTop={true}>✨ Form.yaml Editor</Heading>
  65. <Helper>Write and test form.yaml free of consequence.</Helper>
  66. <EditorWrapper>
  67. <AceEditor
  68. ref={this.aceEditorRef}
  69. mode="yaml"
  70. value={this.state.rawYaml}
  71. theme="porter"
  72. onChange={(e: string) => this.setState({ rawYaml: e })}
  73. name="codeEditor"
  74. editorProps={{ $blockScrolling: true }}
  75. height="450px"
  76. width="100%"
  77. style={{
  78. borderRadius: "5px",
  79. border: "1px solid #ffffff22",
  80. marginTop: "27px",
  81. marginBottom: "27px",
  82. }}
  83. showPrintMargin={false}
  84. showGutter={true}
  85. highlightActiveLine={true}
  86. />
  87. </EditorWrapper>
  88. <CheckboxRow
  89. label="Show form state debugger"
  90. checked={this.state.showStateDebugger}
  91. toggle={() =>
  92. this.setState({ showStateDebugger: !this.state.showStateDebugger })
  93. }
  94. />
  95. <CheckboxRow
  96. label="Read-only"
  97. checked={this.state.isReadOnly}
  98. toggle={() =>
  99. this.setState({
  100. isReadOnly: !this.state.isReadOnly,
  101. })
  102. }
  103. />
  104. <CheckboxRow
  105. label="Include non-form dummy tabs"
  106. checked={this.state.showBonusTabs}
  107. toggle={() =>
  108. this.setState({ showBonusTabs: !this.state.showBonusTabs })
  109. }
  110. />
  111. <CheckboxRow
  112. label="checkbox_a"
  113. checked={this.state.checkbox_a}
  114. toggle={() =>
  115. this.setState({
  116. checkbox_a: !this.state.checkbox_a,
  117. // Override the form value for checkbox_a
  118. valuesToOverride: {
  119. ...this.state.valuesToOverride,
  120. checkbox_a: {
  121. value: !this.state.checkbox_a,
  122. },
  123. },
  124. })
  125. }
  126. />
  127. <InputRow
  128. type="string"
  129. value={this.state.input_a}
  130. setValue={(x: string) =>
  131. this.setState({
  132. input_a: x,
  133. // Override the form value for input_a
  134. valuesToOverride: {
  135. ...this.state.valuesToOverride,
  136. input_a: {
  137. value: x,
  138. },
  139. },
  140. })
  141. }
  142. label={"input_a"}
  143. placeholder="ex: override text"
  144. />
  145. <Heading>🎨 Rendered Form</Heading>
  146. <Br />
  147. <PorterFormWrapper
  148. showStateDebugger={this.state.showStateDebugger}
  149. formData={formData}
  150. valuesToOverride={{
  151. input_a: this.state.valuesToOverride?.input_a?.value,
  152. }}
  153. isReadOnly={this.state.isReadOnly}
  154. onSubmit={(vars) => {
  155. alert("check console output");
  156. console.log(vars);
  157. }}
  158. rightTabOptions={this.state.showBonusTabs ? tabOptions : []}
  159. renderTabContents={this.renderTabContents}
  160. saveButtonText={"Test Submit"}
  161. injectedProps={{
  162. "url-link": {
  163. chart: {
  164. name: "something",
  165. } as ChartType,
  166. },
  167. }}
  168. />
  169. </StyledFormDebugger>
  170. );
  171. }
  172. }
  173. const Br = styled.div`
  174. width: 100%;
  175. height: 12px;
  176. `;
  177. const TabWrapper = styled.div`
  178. background: #ffffff11;
  179. height: 200px;
  180. width: 100%;
  181. border-radius: 8px;
  182. display: flex;
  183. align-items: center;
  184. justify-content: center;
  185. font-size: 13px;
  186. overflow: auto;
  187. padding: 50px;
  188. `;
  189. const EditorWrapper = styled.div`
  190. .ace_editor,
  191. .ace_editor * {
  192. font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas",
  193. monospace !important;
  194. font-size: 12px !important;
  195. font-weight: 400 !important;
  196. letter-spacing: 0 !important;
  197. }
  198. `;
  199. const StyledFormDebugger = styled.div`
  200. position: relative;
  201. `;
  202. const Button = styled.div`
  203. display: flex;
  204. flex-direction: row;
  205. align-items: center;
  206. justify-content: space-between;
  207. font-size: 13px;
  208. cursor: pointer;
  209. font-family: "Work Sans", sans-serif;
  210. border-radius: 20px;
  211. color: white;
  212. height: 35px;
  213. margin-left: -2px;
  214. padding: 0px 8px;
  215. width: 85px;
  216. float: right;
  217. padding-bottom: 1px;
  218. font-weight: 500;
  219. padding-right: 15px;
  220. overflow: hidden;
  221. white-space: nowrap;
  222. text-overflow: ellipsis;
  223. cursor: pointer;
  224. border: 2px solid #969fbbaa;
  225. :hover {
  226. background: #ffffff11;
  227. }
  228. > i {
  229. color: white;
  230. width: 18px;
  231. height: 18px;
  232. color: #969fbbaa;
  233. font-weight: 600;
  234. font-size: 14px;
  235. border-radius: 20px;
  236. display: flex;
  237. align-items: center;
  238. margin-right: 5px;
  239. justify-content: center;
  240. }
  241. `;
  242. const initYaml = `name: Web
  243. hasSource: true
  244. includeHiddenFields: true
  245. tabs:
  246. - name: main
  247. label: Main
  248. sections:
  249. - name: section_one
  250. contents:
  251. - type: heading
  252. label: Container Settings
  253. - type: variable
  254. variable: showStartCommand
  255. settings:
  256. default: true
  257. - name: command
  258. show_if: showStartCommand
  259. contents:
  260. - type: subtitle
  261. name: command_description
  262. label: (Optional) Set a start command for this service.
  263. - type: string-input
  264. label: Start Command
  265. placeholder: "ex: sh ./script.sh"
  266. variable: container.command
  267. - name: section_one_cont
  268. contents:
  269. - type: subtitle
  270. label: Specify the port your application is running on.
  271. - type: number-input
  272. variable: container.port
  273. label: Container Port
  274. placeholder: "ex: 80"
  275. settings:
  276. default: 80
  277. - type: heading
  278. label: Deploy Webhook
  279. - type: checkbox
  280. variable: auto_deploy
  281. label: Auto-deploy when webhook is called.
  282. settings:
  283. default: true
  284. - name: network
  285. contents:
  286. - type: heading
  287. label: Network Settings
  288. - type: subtitle
  289. label: For containers that you do not want to expose to external traffic (e.g. databases and add-ons), you may make them accessible only to other internal services running within the same cluster.
  290. - type: checkbox
  291. variable: ingress.enabled
  292. label: Expose to external traffic
  293. settings:
  294. default: true
  295. - name: domain_toggle
  296. show_if: ingress.enabled
  297. contents:
  298. - type: subtitle
  299. label: Assign custom domain to your deployment. You must first create an A record in your domain provider that points to your cluster load balancer's IP address for this.
  300. - type: checkbox
  301. variable: ingress.custom_domain
  302. label: Configure Custom Domain
  303. settings:
  304. default: false
  305. - name: domain_name
  306. show_if: ingress.custom_domain
  307. contents:
  308. - type: array-input
  309. variable: ingress.hosts
  310. label: Domain Name
  311. - name: do_wildcard
  312. show_if:
  313. and:
  314. - ingress.custom_domain
  315. - currentCluster.service.is_do
  316. contents:
  317. - type: subtitle
  318. label: If you're hosting on Digital Ocean and have enabled the wildcard domains from the 'HTTPS Issuer', you can use a wildcard certificate.
  319. - type: checkbox
  320. variable: ingress.wildcard
  321. label: Use Wildcard Certificate
  322. - name: resources
  323. label: Resources
  324. sections:
  325. - name: main_section
  326. contents:
  327. - type: heading
  328. label: Resources
  329. - type: subtitle
  330. label: Configure resources assigned to this container.
  331. - type: number-input
  332. label: RAM
  333. variable: resources.requests.memory
  334. placeholder: "ex: 256"
  335. settings:
  336. unit: Mi
  337. default: 256
  338. - type: number-input
  339. label: CPU
  340. variable: resources.requests.cpu
  341. placeholder: "ex: 100"
  342. settings:
  343. unit: m
  344. default: 100
  345. - type: number-input
  346. label: Replicas
  347. variable: replicaCount
  348. placeholder: "ex: 1"
  349. settings:
  350. default: 1
  351. - type: checkbox
  352. variable: autoscaling.enabled
  353. label: Enable autoscaling
  354. settings:
  355. default: false
  356. - name: autoscaler
  357. show_if: autoscaling.enabled
  358. contents:
  359. - type: number-input
  360. label: Minimum Replicas
  361. variable: autoscaling.minReplicas
  362. placeholder: "ex: 1"
  363. settings:
  364. default: 1
  365. - type: number-input
  366. label: Maximum Replicas
  367. variable: autoscaling.maxReplicas
  368. placeholder: "ex: 10"
  369. settings:
  370. default: 10
  371. - type: number-input
  372. label: Target CPU Utilization
  373. variable: autoscaling.targetCPUUtilizationPercentage
  374. placeholder: "ex: 50"
  375. settings:
  376. omitUnitFromValue: true
  377. unit: "%"
  378. default: 50
  379. - type: number-input
  380. label: Target RAM Utilization
  381. variable: autoscaling.targetMemoryUtilizationPercentage
  382. placeholder: "ex: 50"
  383. settings:
  384. omitUnitFromValue: true
  385. unit: "%"
  386. default: 50
  387. - name: env
  388. label: Environment
  389. sections:
  390. - name: env_vars
  391. contents:
  392. - type: heading
  393. label: Environment variables
  394. - type: subtitle
  395. label: Set environment variables for your secrets and environment-specific configuration.
  396. - type: env-key-value-array
  397. label:
  398. variable: container.env.normal
  399. - name: advanced
  400. label: Advanced
  401. sections:
  402. - name: ingress_annotations
  403. contents:
  404. - type: heading
  405. label: Ingress Custom Annotations
  406. - type: subtitle
  407. label: Assign custom annotations to Ingress. These annotations will overwrite the annotations Porter assigns by default.
  408. - type: key-value-array
  409. variable: ingress.annotations
  410. settings:
  411. default: {}
  412. - name: health_check
  413. contents:
  414. - type: heading
  415. label: Custom Health Checks
  416. - type: subtitle
  417. label: Define custom health check endpoints to ensure zero down-time deployments.
  418. - type: checkbox
  419. variable: health.enabled
  420. label: Enable Custom Health Checks
  421. settings:
  422. default: false
  423. - name: health_check_endpoint
  424. show_if: health.enabled
  425. contents:
  426. - type: string-input
  427. label: Health Check Endpoint
  428. variable: health.path
  429. placeholder: "ex: /healthz"
  430. settings:
  431. default: /healthz
  432. - type: heading
  433. label: Custom Health Check Rules
  434. - type: subtitle
  435. label: Configure how many times a health check will be performed before deeming the container as failed.
  436. - type: number-input
  437. label: Failure Threshold
  438. variable: health.failureThreshold
  439. placeholder: "ex: 3"
  440. - type: subtitle
  441. label: Configure the interval at which health check is repeated in the case of failure.
  442. - type: number-input
  443. label: Repeat Interval
  444. variable: health.periodSeconds
  445. placeholder: "ex: 30"
  446. - name: persistence_toggle
  447. contents:
  448. - type: heading
  449. label: Persistent Disks
  450. - type: subtitle
  451. label: Attach persistent disks to your deployment to retain data across releases.
  452. - type: checkbox
  453. label: Enable Persistence
  454. variable: pvc.enabled
  455. - name: persistent_storage
  456. show_if: pvc.enabled
  457. contents:
  458. - type: number-input
  459. label: Persistent Storage
  460. variable: pvc.storage
  461. placeholder: "ex: 20"
  462. settings:
  463. unit: Gi
  464. default: 20
  465. - type: string-input
  466. label: Mount Path
  467. variable: pvc.mountPath
  468. placeholder: "ex: /mypath"
  469. settings:
  470. default: /mypath
  471. - name: termination_grace_period
  472. contents:
  473. - type: heading
  474. label: Termination Grace Period
  475. - type: subtitle
  476. label: Specify how much time app processes have to gracefully shut down on SIGTERM.
  477. - type: number-input
  478. label: Termination Grace Period (seconds)
  479. variable: terminationGracePeriodSeconds
  480. placeholder: "ex: 30"
  481. settings:
  482. default: 30
  483. - name: container_hooks
  484. contents:
  485. - type: heading
  486. label: Container hooks
  487. - type: subtitle
  488. label: (Optional) Set post start or pre stop commands for this service.
  489. - type: string-input
  490. label: Post start command
  491. placeholder: "ex: /bin/sh ./myscript.sh"
  492. variable: container.lifecycle.postStart
  493. - type: string-input
  494. label: Pre stop command
  495. placeholder: "ex: /bin/sh ./myscript.sh"
  496. variable: container.lifecycle.preStop
  497. - name: cloud_sql_toggle
  498. show_if: currentCluster.service.is_gcp
  499. contents:
  500. - type: heading
  501. label: Google Cloud SQL
  502. - type: subtitle
  503. label: Securely connect to Google Cloud SQL (GKE only).
  504. - type: checkbox
  505. variable: cloudsql.enabled
  506. label: Enable Google Cloud SQL Proxy
  507. settings:
  508. default: false
  509. - name: cloud_sql_contents
  510. show_if: cloudsql.enabled
  511. contents:
  512. - type: string-input
  513. label: Instance Connection Name
  514. variable: cloudsql.connectionName
  515. placeholder: "ex: project-123:us-east1:pachyderm"
  516. - type: number-input
  517. label: DB Port
  518. variable: cloudsql.dbPort
  519. placeholder: "ex: 5432"
  520. settings:
  521. default: 5432
  522. - type: string-input
  523. label: Service Account JSON
  524. variable: cloudsql.serviceAccountJSON
  525. placeholder: "ex: { <SERVICE_ACCOUNT_JSON> }"`;