useChart.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import yaml from "js-yaml";
  2. import { useContext, useEffect, useState } from "react";
  3. import { useRouteMatch } from "react-router";
  4. import api from "shared/api";
  5. import { onlyInLeft } from "shared/array_utils";
  6. import { Context } from "shared/Context";
  7. import { useRouting } from "shared/routing";
  8. import { ChartType, ChartTypeWithExtendedConfig } from "shared/types";
  9. export const useChart = (oldChart: ChartType, closeChart: () => void) => {
  10. const { currentProject, currentCluster, setCurrentError } = useContext(
  11. Context
  12. );
  13. const [chart, setChart] = useState<ChartTypeWithExtendedConfig>(null);
  14. const { url: matchUrl } = useRouteMatch();
  15. const [status, setStatus] = useState<"ready" | "loading" | "deleting">(
  16. "loading"
  17. );
  18. const [saveStatus, setSaveStatus] = useState<
  19. "loading" | "successful" | string
  20. >("");
  21. const { pushFiltered, getQueryParam, pushQueryParams } = useRouting();
  22. useEffect(() => {
  23. const { namespace, name: chartName } = oldChart;
  24. setStatus("loading");
  25. const revision = getQueryParam("chart_revision");
  26. api
  27. .getChart<ChartTypeWithExtendedConfig>(
  28. "token",
  29. {},
  30. {
  31. id: currentProject?.id,
  32. cluster_id: currentCluster?.id,
  33. namespace,
  34. name: chartName,
  35. revision: Number(revision) ? Number(revision) : 0,
  36. }
  37. )
  38. .then((res) => {
  39. if (res?.data) {
  40. setChart(res.data);
  41. }
  42. })
  43. .finally(() => {
  44. setStatus("ready");
  45. });
  46. }, [oldChart, currentCluster, currentProject]);
  47. /**
  48. * Upgrade chart version
  49. */
  50. const upgradeChart = async () => {
  51. // convert current values to yaml
  52. let valuesYaml = yaml.dump({
  53. ...(chart.config as Object),
  54. });
  55. try {
  56. await api.upgradeChartValues(
  57. "<token>",
  58. {
  59. values: valuesYaml,
  60. version: chart.latest_version,
  61. },
  62. {
  63. id: currentProject.id,
  64. name: chart.name,
  65. namespace: chart.namespace,
  66. cluster_id: currentCluster.id,
  67. }
  68. );
  69. window.analytics?.track("Chart Upgraded", {
  70. chart: chart.name,
  71. values: valuesYaml,
  72. });
  73. } catch (err) {
  74. let parsedErr = err?.response?.data?.error;
  75. if (parsedErr) {
  76. err = parsedErr;
  77. }
  78. setCurrentError(parsedErr);
  79. window.analytics?.track("Failed to Upgrade Chart", {
  80. chart: chart.name,
  81. values: valuesYaml,
  82. error: err,
  83. });
  84. }
  85. };
  86. const uninstallChart = async () => {
  87. if (chart.stack_id) {
  88. await api.removeStackAppResource(
  89. "<token>",
  90. {},
  91. {
  92. project_id: currentProject.id,
  93. cluster_id: currentCluster.id,
  94. app_resource_name: chart.name,
  95. namespace: chart.namespace,
  96. stack_id: chart.stack_id,
  97. }
  98. );
  99. } else {
  100. await api.uninstallTemplate(
  101. "<token>",
  102. {},
  103. {
  104. namespace: chart.namespace,
  105. name: chart.name,
  106. id: currentProject.id,
  107. cluster_id: currentCluster.id,
  108. }
  109. );
  110. }
  111. };
  112. /**
  113. * Delete/Uninstall chart
  114. */
  115. const deleteChart = async () => {
  116. setStatus("deleting");
  117. try {
  118. const syncedEnvGroups = chart.config?.container?.env?.synced || [];
  119. const removeApplicationToEnvGroupPromises = syncedEnvGroups.map(
  120. (envGroup: any) => {
  121. return api.removeApplicationFromEnvGroup(
  122. "<token>",
  123. {
  124. name: envGroup?.name,
  125. app_name: chart.name,
  126. },
  127. {
  128. project_id: currentProject.id,
  129. cluster_id: currentCluster.id,
  130. namespace: chart.namespace,
  131. }
  132. );
  133. }
  134. );
  135. try {
  136. await Promise.all(removeApplicationToEnvGroupPromises);
  137. } catch (error) {
  138. setCurrentError(
  139. "We coudln't remove the synced env group from the application, please remove it manually before uninstalling the chart, or try again."
  140. );
  141. return;
  142. }
  143. await uninstallChart();
  144. setStatus("ready");
  145. closeChart();
  146. return;
  147. } catch (error) {
  148. console.log(error);
  149. throw new Error("Couldn't uninstall the chart");
  150. }
  151. };
  152. /**
  153. * Update chart values
  154. */
  155. const updateChart = async (
  156. processValues: (
  157. chart: ChartType,
  158. oldChart?: ChartType
  159. ) => { yaml: string; metadata: any }
  160. ) => {
  161. setSaveStatus("loading");
  162. const { yaml: values, metadata } = processValues(chart, oldChart);
  163. const syncEnvGroups = metadata ? metadata["container.env"] : {};
  164. const addedEnvGroups = syncEnvGroups?.added || [];
  165. const deletedEnvGroups = syncEnvGroups?.deleted || [];
  166. const addApplicationToEnvGroupPromises = addedEnvGroups.map(
  167. (envGroup: any) => {
  168. return api.addApplicationToEnvGroup(
  169. "<token>",
  170. {
  171. name: envGroup?.name,
  172. app_name: chart.name,
  173. },
  174. {
  175. project_id: currentProject.id,
  176. cluster_id: currentCluster.id,
  177. namespace: chart.namespace,
  178. }
  179. );
  180. }
  181. );
  182. try {
  183. await Promise.all(addApplicationToEnvGroupPromises);
  184. } catch (error) {
  185. setCurrentError(
  186. "We coudln't sync the env group to the application, please try again."
  187. );
  188. }
  189. const removeApplicationToEnvGroupPromises = deletedEnvGroups.map(
  190. (envGroup: any) => {
  191. return api.removeApplicationFromEnvGroup(
  192. "<token>",
  193. {
  194. name: envGroup?.name,
  195. app_name: chart.name,
  196. },
  197. {
  198. project_id: currentProject.id,
  199. cluster_id: currentCluster.id,
  200. namespace: chart.namespace,
  201. }
  202. );
  203. }
  204. );
  205. try {
  206. await Promise.all(removeApplicationToEnvGroupPromises);
  207. } catch (error) {
  208. setCurrentError(
  209. "We coudln't remove the synced env group from the application, please try again."
  210. );
  211. }
  212. try {
  213. await api.upgradeChartValues(
  214. "<token>",
  215. {
  216. values,
  217. },
  218. {
  219. id: currentProject.id,
  220. name: chart.name,
  221. namespace: chart.namespace,
  222. cluster_id: currentCluster.id,
  223. }
  224. );
  225. setSaveStatus("successful");
  226. setTimeout(() => setSaveStatus(""), 500);
  227. } catch (err) {
  228. let parsedErr = err?.response?.data?.error;
  229. if (!parsedErr) {
  230. parsedErr = err;
  231. }
  232. setCurrentError(parsedErr);
  233. setSaveStatus("Couldn't process the request.");
  234. // throw new Error(parsedErr);
  235. }
  236. };
  237. /**
  238. * Refresh the chart data
  239. */
  240. const refreshChart = async () => {
  241. try {
  242. const newChart = await api
  243. .getChart(
  244. "<token>",
  245. {},
  246. {
  247. name: chart.name,
  248. revision: 0,
  249. namespace: chart.namespace,
  250. cluster_id: currentCluster.id,
  251. id: currentProject.id,
  252. }
  253. )
  254. .then((res) => res.data);
  255. pushQueryParams({
  256. chart_version: newChart.version,
  257. });
  258. setChart(newChart);
  259. } catch (error) {}
  260. };
  261. const loadChartWithSpecificRevision = async (revision: number) => {
  262. try {
  263. const newChart = await api
  264. .getChart(
  265. "<token>",
  266. {},
  267. {
  268. name: chart.name,
  269. revision: revision,
  270. namespace: chart.namespace,
  271. cluster_id: currentCluster.id,
  272. id: currentProject.id,
  273. }
  274. )
  275. .then((res) => res.data);
  276. pushQueryParams({
  277. chart_revision: newChart.version,
  278. });
  279. setChart(newChart);
  280. } catch (error) {}
  281. };
  282. return {
  283. chart,
  284. status,
  285. saveStatus,
  286. upgradeChart,
  287. deleteChart,
  288. updateChart,
  289. refreshChart,
  290. loadChartWithSpecificRevision,
  291. };
  292. };