Explorar o código

error handling on FE for addon deploy & separate backend call for addons

sunguroku %!s(int64=4) %!d(string=hai) anos
pai
achega
54faba6bbd

+ 10 - 9
dashboard/src/main/home/launch/launch-flow/LaunchFlow.tsx

@@ -118,9 +118,8 @@ class LaunchFlow extends Component<PropsType, StateType> {
       .catch((err) => {
         let parsedErr =
           err?.response?.data?.errors && err.response.data.errors[0];
-        if (parsedErr) {
-          err = parsedErr;
-        }
+        err = parsedErr || err.message || JSON.stringify(err);
+
         this.setState({
           saveValuesStatus: `Could not create GitHub Action: ${err}`,
         });
@@ -140,9 +139,9 @@ class LaunchFlow extends Component<PropsType, StateType> {
     for (let key in wildcard) {
       _.set(values, key, wildcard[key]);
     }
-
+    console.log("OJKOKOKOK")
     api
-      .deployTemplate(
+      .deployAddon(
         "<token>",
         {
           templateName: this.props.currentTemplate.name,
@@ -180,14 +179,16 @@ class LaunchFlow extends Component<PropsType, StateType> {
         });
       })
       .catch((err) => {
+        console.log("ERROR HERE", err)
         let parsedErr =
           err?.response?.data?.errors && err.response.data.errors[0];
-        if (parsedErr) {
-          err = parsedErr;
-        }
+
+        err = parsedErr || err.message || JSON.stringify(err);
+
         this.setState({
-          saveValuesStatus: parsedErr,
+          saveValuesStatus: err,
         });
+
         setCurrentError(err);
         window.analytics.track("Failed to Deploy Add-on", {
           name: this.props.currentTemplate.name,

+ 22 - 0
dashboard/src/shared/api.tsx

@@ -292,6 +292,27 @@ const deployTemplate = baseApi<
   return `/api/projects/${id}/deploy/${name}/${version}?cluster_id=${cluster_id}`;
 });
 
+const deployAddon = baseApi<
+  {
+    templateName: string;
+    formValues?: any;
+    storage: StorageType;
+    namespace: string;
+    name: string;
+  },
+  {
+    id: number;
+    cluster_id: number;
+    name: string;
+    version: string;
+    repo_url?: string;
+  }
+>("POST", (pathParams) => {
+  let { cluster_id, id, name, version, repo_url } = pathParams;
+
+  return `/api/projects/${id}/deploy/addon/${name}/${version}?cluster_id=${cluster_id}&repo_url=${repo_url}`;
+});
+
 const destroyCluster = baseApi<
   {
     eks_name: string;
@@ -914,6 +935,7 @@ export default {
   deleteRegistryIntegration,
   createSubdomain,
   deployTemplate,
+  deployAddon,
   destroyEKS,
   destroyGKE,
   destroyDOKS,

+ 109 - 1
server/api/deploy_handler.go

@@ -125,7 +125,13 @@ func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
 	}
 
 	// create release with webhook token in db
-	repository := rel.Config["image"].(map[string]interface{})["repository"]
+	image, ok := rel.Config["image"].(map[string]interface{})
+	if !ok {
+		app.handleErrorInternal(fmt.Errorf("Could not find field image in config"), w)
+		return
+	}
+
+	repository := image["repository"]
 	repoStr, ok := repository.(string)
 
 	if !ok {
@@ -176,6 +182,108 @@ func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 }
 
+// HandleDeployAddon triggers a addon deployment from a template
+func (app *App) HandleDeployAddon(w http.ResponseWriter, r *http.Request) {
+	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+
+	if err != nil || projID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	name := chi.URLParam(r, "name")
+	version := chi.URLParam(r, "version")
+
+	// if version passed as latest, pass empty string to loader to get latest
+	if version == "latest" {
+		version = ""
+	}
+
+	getChartForm := &forms.ChartForm{
+		Name:    name,
+		Version: version,
+		RepoURL: app.ServerConf.DefaultApplicationHelmRepoURL,
+	}
+
+	// if a repo_url is passed as query param, it will be populated
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	getChartForm.PopulateRepoURLFromQueryParams(vals)
+
+	chart, err := loader.LoadChartPublic(getChartForm.RepoURL, getChartForm.Name, getChartForm.Version)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	form := &forms.InstallChartTemplateForm{
+		ReleaseForm: &forms.ReleaseForm{
+			Form: &helm.Form{
+				Repo:              app.Repo,
+				DigitalOceanOAuth: app.DOConf,
+			},
+		},
+		ChartTemplateForm: &forms.ChartTemplateForm{},
+	}
+
+	form.ReleaseForm.PopulateHelmOptionsFromQueryParams(
+		vals,
+		app.Repo.Cluster,
+	)
+
+	if err := json.NewDecoder(r.Body).Decode(form); err != nil {
+		app.handleErrorFormDecoding(err, ErrUserDecode, w)
+		return
+	}
+
+	agent, err := app.getAgentFromReleaseForm(
+		w,
+		r,
+		form.ReleaseForm,
+	)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrUserDecode, w)
+		return
+	}
+
+	registries, err := app.Repo.Registry.ListRegistriesByProjectID(uint(projID))
+
+	if err != nil {
+		app.handleErrorDataRead(err, w)
+		return
+	}
+
+	conf := &helm.InstallChartConfig{
+		Chart:      chart,
+		Name:       form.ChartTemplateForm.Name,
+		Namespace:  form.ReleaseForm.Form.Namespace,
+		Values:     form.ChartTemplateForm.FormValues,
+		Cluster:    form.ReleaseForm.Cluster,
+		Repo:       *app.Repo,
+		Registries: registries,
+	}
+
+	_, err = agent.InstallChart(conf, app.DOConf)
+
+	if err != nil {
+		app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+			Code:   ErrReleaseDeploy,
+			Errors: []string{"error installing a new chart: " + err.Error()},
+		}, w)
+
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+}
+
 // HandleUninstallTemplate triggers a chart deployment from a template
 func (app *App) HandleUninstallTemplate(w http.ResponseWriter, r *http.Request) {
 	name := chi.URLParam(r, "name")

+ 14 - 0
server/router/router.go

@@ -1476,6 +1476,20 @@ func New(a *api.App) *chi.Mux {
 					mw.ReadAccess,
 				),
 			)
+
+			r.Method(
+				"POST",
+				"/projects/{project_id}/deploy/addon/{name}/{version}",
+				auth.DoesUserHaveProjectAccess(
+					auth.DoesUserHaveClusterAccess(
+						requestlog.NewHandler(a.HandleDeployAddon, l),
+						mw.URLParam,
+						mw.QueryParam,
+					),
+					mw.URLParam,
+					mw.ReadAccess,
+				),
+			)
 		})
 
 		// Create group for long-running Helm operations