소스 검색

get pod logs by label selectors - backend

sunguroku 5 년 전
부모
커밋
f6c7489f26
5개의 변경된 파일132개의 추가작업 그리고 33개의 파일을 삭제
  1. 55 32
      internal/helm/grapher/relation.go
  2. 11 0
      internal/kubernetes/agent.go
  3. 64 0
      server/api/k8s_handler.go
  4. 1 1
      server/api/release_handler.go
  5. 1 0
      server/router/router.go

+ 55 - 32
internal/helm/grapher/relation.go

@@ -31,7 +31,8 @@ type SpecRel struct {
 
 // ParsedObjs has methods GetControlRel and GetLabelRel that updates its objects array.
 type ParsedObjs struct {
-	Objects []Object
+	Objects      []Object
+	PodSelectors []string
 }
 
 // Relations is embedded into the Object struct and contains arrays of the three types of relationships.
@@ -62,6 +63,7 @@ type MatchExpression struct {
 func (parsed *ParsedObjs) GetControlRel() {
 	// First collect all children (Pods) that are not included in the yaml as top-level object.
 	children := []Object{}
+	selectors := []string{}
 	for i, obj := range parsed.Objects {
 		yaml := obj.RawYAML
 		kind := getField(yaml, "kind")
@@ -113,50 +115,35 @@ func (parsed *ParsedObjs) GetControlRel() {
 				obj.Relations.ControlRels = append(obj.Relations.ControlRels, crel)
 				parsed.Objects[i] = obj
 			}
+
+			// Get pod label selectors
+			matchLabels, _ := aggregateLabelSelectors(yaml)
+
+			// stringify selectors
+			selector := ""
+			for i, ml := range matchLabels {
+				selector = selector + ml.key + "=" + ml.value
+				if i != len(matchLabels)-1 {
+					selector = selector + ","
+				}
+			}
+			selectors = append(selectors, selector)
 		}
 	}
 
 	// add children to the objects array at the end.
 	parsed.Objects = append(parsed.Objects, children...)
+	parsed.PodSelectors = selectors
 }
 
 // GetLabelRel is generates relationships between objects connected by selector-label.
 // It supports both Equality-based and Set-based operators with MatchLabels and MatchExpressions, respectively.
 func (parsed *ParsedObjs) GetLabelRel() {
 	for i, o := range parsed.Objects {
+
 		// Skip Pods
 		yaml := o.RawYAML
-		matchLabels := []MatchLabel{}
-		matchExpressions := []MatchExpression{}
-
-		// First check for the outdated syntax (matchLabels were added in recent k8s version)
-		if l := getField(yaml, "spec", "selector"); l != nil {
-			simple := true
-			if ml := getField(yaml, "spec", "selector", "matchLabels"); ml != nil {
-				matchLabels = addMatchLabels(matchLabels, ml.(map[string]interface{}))
-				simple = false
-			}
-
-			if me := getField(yaml, "spec", "selector", "matchExpressions"); me != nil {
-				for _, o := range me.([]interface{}) {
-					ot := o.(map[string]interface{})
-					values := []string{}
-					for _, arg := range ot["values"].([]interface{}) {
-						values = append(values, arg.(string))
-					}
-					matchExpressions = append(matchExpressions, MatchExpression{
-						key:      ot["key"].(string),
-						operator: ot["operator"].(string),
-						values:   values,
-					})
-				}
-				simple = false
-			}
-
-			if simple {
-				matchLabels = addMatchLabels(matchLabels, l.(map[string]interface{}))
-			}
-		}
+		matchLabels, matchExpressions := aggregateLabelSelectors(yaml)
 
 		// Find ID's of targets that match the label selector
 		targetID := parsed.findLabelsBySelector(o.ID, matchLabels, matchExpressions)
@@ -255,6 +242,42 @@ func (parsed *ParsedObjs) GetSpecRel() {
 	}
 }
 
+// LabelRel helpers
+func aggregateLabelSelectors(yaml map[string]interface{}) ([]MatchLabel, []MatchExpression) {
+	matchLabels := []MatchLabel{}
+	matchExpressions := []MatchExpression{}
+
+	// First check for the outdated syntax (matchLabels were added in recent k8s version)
+	if l := getField(yaml, "spec", "selector"); l != nil {
+		simple := true
+		if ml := getField(yaml, "spec", "selector", "matchLabels"); ml != nil {
+			matchLabels = addMatchLabels(matchLabels, ml.(map[string]interface{}))
+			simple = false
+		}
+
+		if me := getField(yaml, "spec", "selector", "matchExpressions"); me != nil {
+			for _, o := range me.([]interface{}) {
+				ot := o.(map[string]interface{})
+				values := []string{}
+				for _, arg := range ot["values"].([]interface{}) {
+					values = append(values, arg.(string))
+				}
+				matchExpressions = append(matchExpressions, MatchExpression{
+					key:      ot["key"].(string),
+					operator: ot["operator"].(string),
+					values:   values,
+				})
+			}
+			simple = false
+		}
+
+		if simple {
+			matchLabels = addMatchLabels(matchLabels, l.(map[string]interface{}))
+		}
+	}
+	return matchLabels, matchExpressions
+}
+
 // SpecRel helpers
 func (parsed *ParsedObjs) findObjectByNameAndKind(parentID int, name interface{}, kind string) []int {
 	targets := []int{}

+ 11 - 0
internal/kubernetes/agent.go

@@ -28,6 +28,17 @@ func (a *Agent) ListNamespaces() (*v1.NamespaceList, error) {
 	)
 }
 
+// GetPodsByLabel retrieves pods with matching labels
+func (a *Agent) GetPodsByLabel(selector string) (*v1.PodList, error) {
+	// Search in all namespaces for matching pods
+	return a.Clientset.CoreV1().Pods("").List(
+		context.TODO(),
+		metav1.ListOptions{
+			LabelSelector: selector,
+		},
+	)
+}
+
 // GetPodLogs streams real-time logs from a given pod.
 func (a *Agent) GetPodLogs(namespace string, name string, conn *websocket.Conn) error {
 	// follow logs

+ 64 - 0
server/api/k8s_handler.go

@@ -142,3 +142,67 @@ func (app *App) HandleGetPodLogs(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 }
+
+// HandleListPods returns all pods that match the given selectors
+// TODO: Refactor repeated calls.
+func (app *App) HandleListPods(w http.ResponseWriter, r *http.Request) {
+
+	// get session to retrieve correct kubeconfig
+	session, err := app.store.Get(r, app.cookieName)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+
+	if err != nil {
+		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
+		return
+	}
+
+	// get the filter options
+	form := &forms.K8sForm{
+		OutOfClusterConfig: &kubernetes.OutOfClusterConfig{},
+	}
+	form.PopulateK8sOptionsFromQueryParams(vals)
+
+	if sessID, ok := session.Values["user_id"].(uint); ok {
+		form.PopulateK8sOptionsFromUserID(sessID, app.repo.User)
+	}
+
+	// validate the form
+	if err := app.validator.Struct(form); err != nil {
+		app.handleErrorFormValidation(err, ErrK8sValidate, w)
+		return
+	}
+
+	// create a new agent
+	var agent *kubernetes.Agent
+
+	if app.testing {
+		agent = app.TestAgents.K8sAgent
+	} else {
+		agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
+	}
+
+	pods := []string{}
+	for _, selector := range vals["selectors"] {
+		podsList, err := agent.GetPodsByLabel(selector)
+
+		if err != nil {
+			app.handleErrorFormValidation(err, ErrK8sValidate, w)
+			return
+		}
+
+		for _, pod := range podsList.Items {
+			pods = append(pods, pod.ObjectMeta.Name)
+		}
+	}
+
+	if err := json.NewEncoder(w).Encode(pods); err != nil {
+		app.handleErrorFormDecoding(err, ErrK8sDecode, w)
+		return
+	}
+}

+ 1 - 1
server/api/release_handler.go

@@ -145,7 +145,7 @@ func (app *App) HandleGetReleaseComponents(w http.ResponseWriter, r *http.Reques
 	parsed.GetLabelRel()
 	parsed.GetSpecRel()
 
-	if err := json.NewEncoder(w).Encode(parsed.Objects); err != nil {
+	if err := json.NewEncoder(w).Encode(parsed); err != nil {
 		app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
 		return
 	}

+ 1 - 0
server/router/router.go

@@ -46,6 +46,7 @@ func New(
 		// /api/k8s routes
 		r.Method("GET", "/k8s/namespaces", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleListNamespaces, l)))
 		r.Method("GET", "/k8s/{namespace}/pod/{name}/logs", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleGetPodLogs, l)))
+		r.Method("GET", "/k8s/pods", auth.BasicAuthenticate(requestlog.NewHandler(a.HandleListPods, l)))
 	})
 
 	fs := http.FileServer(http.Dir(staticFilePath))