Skip to content

Commit

Permalink
Merge pull request #73 from deggja/feat_40
Browse files Browse the repository at this point in the history
feat: added API for policy suggestions #major
  • Loading branch information
deggja authored Dec 15, 2023
2 parents 066b023 + df2c50e commit ca2d999
Show file tree
Hide file tree
Showing 8 changed files with 1,414 additions and 804 deletions.
111 changes: 111 additions & 0 deletions backend/cmd/dash.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ func startDashboardServer() {
http.HandleFunc("/scan", k8s.HandleScanRequest)
http.HandleFunc("/namespaces", k8s.HandleNamespaceListRequest)
http.HandleFunc("/add-policy", k8s.HandleAddPolicyRequest)
http.HandleFunc("/create-policy", HandleCreatePolicyRequest)
http.HandleFunc("/namespaces-with-policies", handleNamespacesWithPoliciesRequest)
http.HandleFunc("/namespace-policies", handleNamespacePoliciesRequest)
http.HandleFunc("/visualization", k8s.HandleVisualizationRequest)
http.HandleFunc("/visualization/cluster", handleClusterVisualizationRequest)
http.HandleFunc("/policy-yaml", k8s.HandlePolicyYAMLRequest)
http.HandleFunc("/pod-info", handlePodInfoRequest)

// Wrap the default serve mux with the CORS middleware
handler := c.Handler(http.DefaultServeMux)
Expand Down Expand Up @@ -129,6 +132,42 @@ func handleNamespacesWithPoliciesRequest(w http.ResponseWriter, r *http.Request)
}
}

// handleNamespacePoliciesRequest handles the HTTP request for serving a list of network policies in a namespace.
func handleNamespacePoliciesRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}

// Extract the namespace parameter from the query string
namespace := r.URL.Query().Get("namespace")
if namespace == "" {
http.Error(w, "Namespace parameter is required", http.StatusBadRequest)
return
}

// Obtain the Kubernetes clientset
clientset, err := k8s.GetClientset()
if err != nil {
http.Error(w, fmt.Sprintf("Failed to create Kubernetes client: %v", err), http.StatusInternalServerError)
return
}

// Fetch network policies from the specified namespace
policies, err := clientset.NetworkingV1().NetworkPolicies(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
http.Error(w, fmt.Sprintf("Failed to get network policies: %v", err), http.StatusInternalServerError)
return
}

// Convert the list of network policies to a more simple structure if needed or encode directly
// For example, you might want to return only the names and some identifiers of the policies

setNoCacheHeaders(w)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(policies)
}

// handleClusterVisualizationRequest handles the HTTP request for serving cluster-wide visualization data.
func handleClusterVisualizationRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
Expand All @@ -149,3 +188,75 @@ func handleClusterVisualizationRequest(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Failed to encode cluster visualization data", http.StatusInternalServerError)
}
}

// handlePodInfoRequest handles the HTTP request for serving pod information.
func handlePodInfoRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}

// Extract the namespace parameter from the query string
namespace := r.URL.Query().Get("namespace")
if namespace == "" {
http.Error(w, "Namespace parameter is required", http.StatusBadRequest)
return
}

// Obtain the Kubernetes clientset
clientset, err := k8s.GetClientset()
if err != nil {
http.Error(w, fmt.Sprintf("Failed to create Kubernetes client: %v", err), http.StatusInternalServerError)
return
}

// Fetch pod information from the specified namespace
podInfo, err := k8s.GetPodInfo(clientset, namespace)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to get pod information: %v", err), http.StatusInternalServerError)
return
}

setNoCacheHeaders(w)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(podInfo)
}

// HandleCreatePolicyRequest handles the HTTP request to create a network policy from YAML.
func HandleCreatePolicyRequest(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}

var policyRequest struct {
YAML string `json:"yaml"`
Namespace string `json:"namespace"`
}
if err := json.NewDecoder(r.Body).Decode(&policyRequest); err != nil {
http.Error(w, fmt.Sprintf("Failed to decode request body: %v", err), http.StatusBadRequest)
return
}
defer r.Body.Close()

clientset, err := k8s.GetClientset()
if err != nil {
http.Error(w, fmt.Sprintf("Failed to create Kubernetes client: %v", err), http.StatusInternalServerError)
return
}

networkPolicy, err := k8s.YAMLToNetworkPolicy(policyRequest.YAML)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to parse network policy YAML: %v", err), http.StatusBadRequest)
return
}

createdPolicy, err := clientset.NetworkingV1().NetworkPolicies(policyRequest.Namespace).Create(context.Background(), networkPolicy, metav1.CreateOptions{})
if err != nil {
http.Error(w, fmt.Sprintf("Failed to create network policy: %v", err), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(createdPolicy)
}
Binary file modified backend/netfetch
Binary file not shown.
59 changes: 59 additions & 0 deletions backend/pkg/k8s/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import (
"path/filepath"

"github.com/AlecAivazis/survey/v2"
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
Expand Down Expand Up @@ -486,3 +489,59 @@ func containsPodDetail(slice []string, detail string) bool {
}
return false
}

// PodInfo holds the desired information from a Pods YAML.
type PodInfo struct {
Name string
Namespace string
Labels map[string]string
Ports []v1.ContainerPort
}

// Hold the desired info from a Pods ports
type ContainerPortInfo struct {
Name string
ContainerPort int32
Protocol v1.Protocol
}

func GetPodInfo(clientset *kubernetes.Clientset, namespace string) ([]PodInfo, error) {
pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}

var podInfos []PodInfo
for _, pod := range pods.Items {
var containerPorts []v1.ContainerPort
for _, container := range pod.Spec.Containers {
containerPorts = append(containerPorts, container.Ports...)
}

podInfo := PodInfo{
Name: pod.Name,
Namespace: pod.Namespace,
Labels: pod.Labels,
Ports: containerPorts,
}
podInfos = append(podInfos, podInfo)
}

return podInfos, nil
}

// YAMLToNetworkPolicy converts a YAML string to a NetworkPolicy object.
func YAMLToNetworkPolicy(yamlContent string) (*networkingv1.NetworkPolicy, error) {
decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDeserializer()
obj, _, err := decoder.Decode([]byte(yamlContent), nil, nil)
if err != nil {
return nil, err
}

networkPolicy, ok := obj.(*networkingv1.NetworkPolicy)
if !ok {
return nil, fmt.Errorf("decoded object is not a NetworkPolicy")
}

return networkPolicy, nil
}
2 changes: 1 addition & 1 deletion backend/statik/statik.go

Large diffs are not rendered by default.

65 changes: 52 additions & 13 deletions frontend/dash/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/dash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"axios": "^1.6.2",
"core-js": "^3.8.3",
"d3": "^7.8.5",
"js-yaml": "^4.1.0",
"vue": "^3.2.13"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit ca2d999

Please sign in to comment.