Explorar el Código

Merge branch 'master' into 0.6.0-github-org-access

Ivan Galakhov hace 4 años
padre
commit
3d3297678f

+ 4 - 0
dashboard/src/components/repo-selector/ContentsList.tsx

@@ -273,6 +273,10 @@ export default class ContentsList extends Component<PropsType, StateType> {
         );
       }
 
+      if (processes.length == 0) {
+        this.props.setProcfilePath("");
+      }
+
       return (
         <Overlay>
           <BgOverlay

+ 12 - 7
dashboard/src/components/values-form/FormWrapper.tsx

@@ -203,13 +203,15 @@ export default class FormWrapper extends Component<PropsType, StateType> {
       if (this.props.tabOptions?.length > 0) {
         let prependTabs = [] as { value: string; label: string }[];
         let appendTabs = [] as { value: string; label: string }[];
-        this.props.tabOptions.forEach((tab: { value: string; label: string }) => {
-          if (tab.value === "status" || tab.value === "metrics") {
-            prependTabs.push(tab);
-          } else {
-            appendTabs.push(tab);
+        this.props.tabOptions.forEach(
+          (tab: { value: string; label: string }) => {
+            if (tab.value === "status" || tab.value === "metrics") {
+              prependTabs.push(tab);
+            } else {
+              appendTabs.push(tab);
+            }
           }
-        });
+        );
         tabOptions = prependTabs.concat(tabOptions.concat(appendTabs));
       }
       this.setState({ tabOptions }, callback);
@@ -267,7 +269,10 @@ export default class FormWrapper extends Component<PropsType, StateType> {
       !_.isEqual(prevProps.tabOptions, this.props.tabOptions) ||
       !_.isEqual(prevProps.formData, this.props.formData)
     ) {
-      if (prevProps.tabOptions?.length === 0 && !_.isEqual(prevProps.tabOptions, this.props.tabOptions)) {
+      if (
+        prevProps.tabOptions?.length === 0 &&
+        !_.isEqual(prevProps.tabOptions, this.props.tabOptions)
+      ) {
         this.setState({ currentTab: "status" });
       }
       let formHasChanged = !_.isEqual(prevProps.formData, this.props.formData);

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -103,7 +103,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
       )
       .then((res) => {
         let image = res.data?.config?.image?.repository;
-        let tag = res.data?.config?.image?.tag.toString();
+        let tag = res.data?.config?.image?.tag?.toString();
         let newestImage = tag ? image + ":" + tag : image;
         let imageIsPlaceholder = false;
         if (

+ 26 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/status/Logs.tsx

@@ -225,6 +225,32 @@ export default class Logs extends Component<PropsType, StateType> {
               System
             </Tab>
           </LogTabs>
+          <Options>
+            <Scroll
+              onClick={() => {
+                this.setState({ scroll: !this.state.scroll }, () => {
+                  if (this.state.scroll) {
+                    this.scrollToBottom(true);
+                  }
+                });
+              }}
+            >
+              <input
+                type="checkbox"
+                checked={this.state.scroll}
+                onChange={() => {}}
+              />
+              Scroll to Bottom
+            </Scroll>
+            <Refresh
+              onClick={() => {
+                this.refreshLogs();
+              }}
+            >
+              <i className="material-icons">autorenew</i>
+              Refresh
+            </Refresh>
+          </Options>
         </LogStreamAlt>
       );
     }

+ 1 - 1
internal/auth/sessionstore/sessionstore.go

@@ -124,7 +124,7 @@ func NewStore(repo *repository.Repository, conf config.ServerConf) (*PGStore, er
 			MaxAge:   86400 * 30,
 			Secure:   true,
 			HttpOnly: true,
-			SameSite: http.SameSiteStrictMode,
+			SameSite: http.SameSiteLaxMode,
 		},
 		Repo: repo,
 	}

+ 28 - 20
internal/helm/postrenderer.go

@@ -216,21 +216,12 @@ func (d *DockerSecretsPostRenderer) getRegistriesToLink(renderedManifests *bytes
 
 		// read the image url
 		for _, image := range images {
-			named, err := reference.ParseNormalizedNamed(image)
+			regName, err := getRegNameFromImageRef(image)
 
 			if err != nil {
 				continue
 			}
 
-			domain := reference.Domain(named)
-			path := reference.Path(named)
-
-			regName := domain
-
-			if pathArr := strings.Split(path, "/"); len(pathArr) > 1 {
-				regName += "/" + strings.Join(pathArr[:len(pathArr)-1], "/")
-			}
-
 			// check if the integration is native to the cluster/registry combination
 			isNative := d.isRegistryNative(regName)
 
@@ -353,21 +344,12 @@ func (d *DockerSecretsPostRenderer) updatePodSpecs(secrets map[string]string) {
 				continue
 			}
 
-			named, err := reference.ParseNormalizedNamed(image)
+			regName, err := getRegNameFromImageRef(image)
 
 			if err != nil {
 				continue
 			}
 
-			domain := reference.Domain(named)
-			path := reference.Path(named)
-
-			regName := domain
-
-			if pathArr := strings.Split(path, "/"); len(pathArr) > 1 {
-				regName += "/" + strings.Join(pathArr[:len(pathArr)-1], "/")
-			}
-
 			if secretName, ok := secrets[regName]; ok && secretName != "" {
 				imagePullSecrets = append(imagePullSecrets, map[string]interface{}{
 					"name": secretName,
@@ -504,3 +486,29 @@ func getNestedResource(res resource, keys ...string) resource {
 
 	return curr
 }
+
+func getRegNameFromImageRef(image string) (string, error) {
+	named, err := reference.ParseNormalizedNamed(image)
+
+	if err != nil {
+		return "", err
+	}
+
+	domain := reference.Domain(named)
+	path := reference.Path(named)
+
+	var regName string
+
+	// if registry is dockerhub, leave the image name as-is
+	if strings.Contains(domain, "docker.io") {
+		regName = "index.docker.io/" + path
+	} else {
+		regName = domain
+
+		if pathArr := strings.Split(path, "/"); len(pathArr) > 1 {
+			regName += "/" + strings.Join(pathArr[:len(pathArr)-1], "/")
+		}
+	}
+
+	return regName, nil
+}

+ 1 - 1
server/api/git_repo_handler.go

@@ -354,7 +354,7 @@ func (app *App) HandleGetProcfileContents(w http.ResponseWriter, r *http.Request
 	)
 
 	if err != nil {
-		app.handleErrorInternal(err, w)
+		http.NotFound(w, r)
 		return
 	}
 

+ 123 - 0
server/api/release_handler.go

@@ -582,6 +582,129 @@ func (app *App) HandleGetReleaseAllPods(w http.ResponseWriter, r *http.Request)
 	}
 }
 
+type GetJobStatusResult struct {
+	Status string `json:"status"`
+}
+
+// HandleGetJobStatus gets the status for a specific job
+func (app *App) HandleGetJobStatus(w http.ResponseWriter, r *http.Request) {
+	name := chi.URLParam(r, "name")
+	namespace := chi.URLParam(r, "namespace")
+
+	form := &forms.GetReleaseForm{
+		ReleaseForm: &forms.ReleaseForm{
+			Form: &helm.Form{
+				Repo:              app.Repo,
+				DigitalOceanOAuth: app.DOConf,
+				Storage:           "secret",
+				Namespace:         namespace,
+			},
+		},
+		Name:     name,
+		Revision: 0,
+	}
+
+	agent, err := app.getAgentFromQueryParams(
+		w,
+		r,
+		form.ReleaseForm,
+		form.ReleaseForm.PopulateHelmOptionsFromQueryParams,
+	)
+
+	// errors are handled in app.getAgentFromQueryParams
+	if err != nil {
+		return
+	}
+
+	release, err := agent.GetRelease(form.Name, form.Revision)
+
+	if err != nil {
+		app.sendExternalError(err, http.StatusNotFound, HTTPError{
+			Code:   ErrReleaseReadData,
+			Errors: []string{"release not found"},
+		}, w)
+
+		return
+	}
+
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	// get the filter options
+	k8sForm := &forms.K8sForm{
+		OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
+			Repo:              app.Repo,
+			DigitalOceanOAuth: app.DOConf,
+		},
+	}
+
+	k8sForm.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
+	k8sForm.DefaultNamespace = form.ReleaseForm.Namespace
+
+	// validate the form
+	if err := app.validator.Struct(k8sForm); err != nil {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	// create a new kubernetes agent
+	var k8sAgent *kubernetes.Agent
+
+	if app.ServerConf.IsTesting {
+		k8sAgent = app.TestAgents.K8sAgent
+	} else {
+		k8sAgent, err = kubernetes.GetAgentOutOfClusterConfig(k8sForm.OutOfClusterConfig)
+	}
+
+	jobs, err := k8sAgent.ListJobsByLabel(namespace, kubernetes.Label{
+		Key: "helm.sh/chart",
+		Val: fmt.Sprintf("%s-%s", release.Chart.Name(), release.Chart.Metadata.Version),
+	}, kubernetes.Label{
+		Key: "meta.helm.sh/release-name",
+		Val: name,
+	})
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	res := &GetJobStatusResult{
+		Status: "succeeded",
+	}
+
+	// get the most recent job
+	if len(jobs) > 0 {
+		mostRecentJob := jobs[0]
+
+		for _, job := range jobs {
+			createdAt := job.ObjectMeta.CreationTimestamp
+
+			if mostRecentJob.CreationTimestamp.Before(&createdAt) {
+				mostRecentJob = job
+			}
+		}
+
+		// get the status of the most recent job
+		if mostRecentJob.Status.Succeeded >= 1 {
+			res.Status = "succeeded"
+		} else if mostRecentJob.Status.Active >= 1 {
+			res.Status = "running"
+		} else if mostRecentJob.Status.Failed >= 1 {
+			res.Status = "failed"
+		}
+	}
+
+	if err := json.NewEncoder(w).Encode(res); err != nil {
+		app.handleErrorFormDecoding(err, ErrK8sDecode, w)
+		return
+	}
+}
+
 // HandleListReleaseHistory retrieves a history of releases based on a release name
 func (app *App) HandleListReleaseHistory(w http.ResponseWriter, r *http.Request) {
 	name := chi.URLParam(r, "name")

+ 14 - 0
server/router/router.go

@@ -1278,6 +1278,20 @@ func New(a *api.App) *chi.Mux {
 				),
 			)
 
+			r.Method(
+				"GET",
+				"/projects/{project_id}/k8s/{namespace}/{name}/jobs/status",
+				auth.DoesUserHaveProjectAccess(
+					auth.DoesUserHaveClusterAccess(
+						requestlog.NewHandler(a.HandleGetJobStatus, l),
+						mw.URLParam,
+						mw.QueryParam,
+					),
+					mw.URLParam,
+					mw.ReadAccess,
+				),
+			)
+
 			r.Method(
 				"GET",
 				"/projects/{project_id}/k8s/jobs/{namespace}/{name}/pods",