AzureCredentialForm.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import React, { useEffect, useState, useContext, useMemo } from "react";
  2. import styled from "styled-components";
  3. import { v4 as uuidv4 } from "uuid";
  4. import api from "shared/api";
  5. import azure from "assets/azure.png";
  6. import { Context } from "shared/Context";
  7. import Text from "./porter/Text";
  8. import Spacer from "./porter/Spacer";
  9. import InputRow from "./form-components/InputRow";
  10. import SaveButton from "./SaveButton";
  11. import Fieldset from "./porter/Fieldset";
  12. import Input from "./porter/Input";
  13. import Button from "./porter/Button";
  14. import DocsHelper from "./DocsHelper";
  15. import Error from "./porter/Error";
  16. import Step from "./porter/Step";
  17. import Link from "./porter/Link";
  18. import Container from "./porter/Container";
  19. type Props = {
  20. goBack: () => void;
  21. proceed: (id: string) => void;
  22. };
  23. const AzureCredentialForm: React.FC<Props> = ({ goBack, proceed }) => {
  24. const { currentProject } = useContext(Context);
  25. const [clientId, setClientId] = useState("");
  26. const [servicePrincipalKey, setServicePrincipalKey] = useState("");
  27. const [tenantId, setTenantId] = useState("");
  28. const [subscriptionId, setSubscriptionId] = useState("");
  29. const [errorMessage, setErrorMessage] = useState("");
  30. const [isLoading, setIsLoading] = useState(false);
  31. const saveCredentials = async () => {
  32. setIsLoading(true);
  33. if (currentProject == null) {
  34. setErrorMessage("Current project is not defined.");
  35. } else if (subscriptionId.trim() === "") {
  36. setErrorMessage("Subscription ID is required");
  37. } else if (clientId.trim() === "") {
  38. setErrorMessage("App ID is required");
  39. } else if (tenantId.trim() === "") {
  40. setErrorMessage("Tenant ID is required");
  41. } else if (servicePrincipalKey.trim() === "") {
  42. setErrorMessage("Password is required");
  43. } else {
  44. try {
  45. const azureIntegrationResponse = await api.createAzureIntegration(
  46. "<token>",
  47. {
  48. azure_client_id: clientId,
  49. azure_subscription_id: subscriptionId,
  50. azure_tenant_id: tenantId,
  51. service_principal_key: servicePrincipalKey,
  52. },
  53. {
  54. id: currentProject.id,
  55. });
  56. const azureIntegrationId = azureIntegrationResponse.data.cloud_provider_credentials_id;
  57. proceed(azureIntegrationId)
  58. } catch (err) {
  59. if (err.response?.data?.error) {
  60. setErrorMessage(err.response?.data?.error.replace("unknown: ", ""));
  61. } else {
  62. setErrorMessage("Something went wrong, please try again later.");
  63. }
  64. }
  65. }
  66. setIsLoading(false);
  67. };
  68. const getButtonStatus = () => {
  69. if (isLoading) {
  70. return "loading";
  71. } else if (errorMessage !== "") {
  72. return <Error
  73. message={errorMessage}
  74. />;
  75. } else {
  76. return null;
  77. }
  78. };
  79. const renderContent = () => {
  80. return (
  81. <>
  82. <Spacer y={1} />
  83. <Fieldset>
  84. <Text size={16}>
  85. Create an Azure Service Principal and input credentials
  86. </Text>
  87. <Spacer height="15px" />
  88. <Text color="helper">
  89. Provide the credentials for an Azure Service Principal authorized on
  90. your Azure subscription.
  91. </Text>
  92. <Spacer y={1} />
  93. <Input
  94. label={<Flex>Subscription ID</Flex>}
  95. value={subscriptionId}
  96. setValue={(e) => {
  97. setSubscriptionId(e.trim());
  98. }}
  99. placeholder="ex: 12345678-abcd-1234-abcd-12345678abcd"
  100. width="100%"
  101. />
  102. <Spacer y={1} />
  103. <Input
  104. label={<Flex>App ID</Flex>}
  105. value={clientId}
  106. setValue={(e) => {
  107. setClientId(e.trim());
  108. }}
  109. placeholder="ex: 12345678-abcd-1234-abcd-12345678abcd"
  110. width="100%"
  111. />
  112. <Spacer y={1} />
  113. <Input
  114. type="password"
  115. label={<Flex>Password</Flex>}
  116. value={servicePrincipalKey}
  117. setValue={(e) => {
  118. setServicePrincipalKey(e.trim());
  119. }}
  120. placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
  121. width="100%"
  122. />
  123. <Spacer y={1} />
  124. <Input
  125. label={<Flex>Tenant ID</Flex>}
  126. value={tenantId}
  127. setValue={(e) => {
  128. setTenantId(e.trim());
  129. }}
  130. placeholder="ex: 12345678-abcd-1234-abcd-12345678abcd"
  131. width="100%"
  132. />
  133. </Fieldset>
  134. <Spacer y={1} />
  135. <Button
  136. onClick={saveCredentials}
  137. status={getButtonStatus()}
  138. >
  139. Continue
  140. </Button>
  141. </>
  142. );
  143. };
  144. return (
  145. <>
  146. <Container row>
  147. <BackButton width="140px" onClick={goBack}>
  148. <i className="material-icons">first_page</i>
  149. Select cloud
  150. </BackButton>
  151. <Spacer x={1} inline />
  152. <Img src={azure} />
  153. <Text size={16}>Grant Azure permissions</Text>
  154. </Container>
  155. <Spacer y={1} />
  156. <Text color="helper">
  157. Grant Porter permissions to create infrastructure in your Azure
  158. subscription.
  159. </Text>
  160. {renderContent()}
  161. </>
  162. );
  163. };
  164. export default AzureCredentialForm;
  165. const Flex = styled.div`
  166. display: flex;
  167. align-items: center;
  168. > i {
  169. margin-left: 10px;
  170. font-size: 16px;
  171. cursor: pointer;
  172. }
  173. `;
  174. const Img = styled.img`
  175. height: 18px;
  176. margin-right: 15px;
  177. `;
  178. const BackButton = styled.div`
  179. display: flex;
  180. align-items: center;
  181. justify-content: space-between;
  182. cursor: pointer;
  183. font-size: 13px;
  184. height: 35px;
  185. padding: 5px 13px;
  186. padding-right: 15px;
  187. border: 1px solid #ffffff55;
  188. border-radius: 100px;
  189. width: ${(props: { width: string }) => props.width};
  190. color: white;
  191. background: #ffffff11;
  192. :hover {
  193. background: #ffffff22;
  194. }
  195. > i {
  196. color: white;
  197. font-size: 16px;
  198. margin-right: 6px;
  199. margin-left: -2px;
  200. }
  201. `;