diff --git a/.circleci/config.yml b/.circleci/config.yml index ff2ba2ee..c5606b6b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,7 @@ jobs: - checkout - run: dep ensure - run: go get -u github.com/golang/lint/golint - - run: go list ./... | grep -v vendor | xargs golint + - run: go list ./... | grep -v vendor | xargs golint -set_exit_status - run: go list ./... | grep -v vendor | xargs go vet - run: go test ./pkg/... -v -coverprofile cover.out diff --git a/main.go b/main.go index df1d8409..0b46d586 100644 --- a/main.go +++ b/main.go @@ -65,7 +65,7 @@ func main() { func startDashboardServer(c conf.Configuration) { http.HandleFunc("/results.json", func(w http.ResponseWriter, r *http.Request) { - dashboard.RenderJSON(w, r, c) + dashboard.EndpointHandler(w, r, c) }) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("public/")))) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { @@ -73,7 +73,7 @@ func startDashboardServer(c conf.Configuration) { http.NotFound(w, r) return } - dashboard.Render(w, r, c) + dashboard.MainHandler(w, r, c) }) glog.Println("Starting Fairwinds dashboard server on port 8080.") glog.Fatal(http.ListenAndServe(":8080", nil)) diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index 1ea79030..2f2cdaf7 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -3,7 +3,6 @@ package dashboard import ( "encoding/json" "html/template" - "log" "net/http" conf "github.com/reactiveops/fairwinds/pkg/config" @@ -12,49 +11,46 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type DashboardData struct { +// TemplateData represents data in a format that's template friendly. +type TemplateData struct { ClusterSummary *validator.ResultSummary NamespacedResults map[string]*validator.NamespacedResult } var tmpl = template.Must(template.ParseFiles("pkg/dashboard/templates/dashboard.gohtml")) -func Render(w http.ResponseWriter, r *http.Request, c conf.Configuration) { - dashboardData, err := getDashboardData(c) +// MainHandler gets template data and renders the dashboard with it. +func MainHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration) { + templateData, err := getTemplateData(c) if err != nil { - http.Error(w, "Error Fetching Deploys", 500) + http.Error(w, "Error Fetching Deployments", 500) return } - tmpl.Execute(w, dashboardData) + tmpl.Execute(w, templateData) } -func RenderJSON(w http.ResponseWriter, r *http.Request, c conf.Configuration) { - results := []validator.Results{} - var clientset = kube.CreateClientset() - pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{}) +// EndpointHandler gets template data and renders json with it. +func EndpointHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration) { + templateData, err := getTemplateData(c) if err != nil { - http.Error(w, "Error Fetching Pods", 500) + http.Error(w, "Error Fetching Deployments", 500) return } - log.Println("pods count:", len(pods.Items)) - for _, pod := range pods.Items { - result := validator.ValidatePods(c, &pod.Spec) - results = append(results, result) - } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(results) + json.NewEncoder(w).Encode(templateData) } -func getDashboardData(c conf.Configuration) (DashboardData, error) { +func getTemplateData(c conf.Configuration) (TemplateData, error) { var clientset = kube.CreateClientset() deploys, err := clientset.AppsV1().Deployments("").List(metav1.ListOptions{}) if err != nil { - return DashboardData{}, err + return TemplateData{}, err } - dashboardData := DashboardData{ + templateData := TemplateData{ ClusterSummary: &validator.ResultSummary{ Successes: 0, Warnings: 4, @@ -76,8 +72,8 @@ func getDashboardData(c conf.Configuration) (DashboardData, error) { ContainerResults: []validator.ContainerResult{}, } - if dashboardData.NamespacedResults[deploy.Namespace] == nil { - dashboardData.NamespacedResults[deploy.Namespace] = &validator.NamespacedResult{ + if templateData.NamespacedResults[deploy.Namespace] == nil { + templateData.NamespacedResults[deploy.Namespace] = &validator.NamespacedResult{ Results: []validator.ResourceResult{}, Summary: &validator.ResultSummary{ Successes: 0, @@ -94,23 +90,23 @@ func getDashboardData(c conf.Configuration) (DashboardData, error) { Messages: []validator.ResultMessage{}, } for _, success := range containerValidation.Successes { - dashboardData.ClusterSummary.Successes++ - dashboardData.NamespacedResults[deploy.Namespace].Summary.Successes++ + templateData.ClusterSummary.Successes++ + templateData.NamespacedResults[deploy.Namespace].Summary.Successes++ deployResult.Summary.Successes++ containerResult.Messages = append(containerResult.Messages, success) } for _, failure := range containerValidation.Failures { - dashboardData.ClusterSummary.Failures++ - dashboardData.NamespacedResults[deploy.Namespace].Summary.Failures++ + templateData.ClusterSummary.Failures++ + templateData.NamespacedResults[deploy.Namespace].Summary.Failures++ deployResult.Summary.Failures++ containerResult.Messages = append(containerResult.Messages, failure) } deployResult.ContainerResults = append(deployResult.ContainerResults, containerResult) } - dashboardData.NamespacedResults[deploy.Namespace].Results = append( - dashboardData.NamespacedResults[deploy.Namespace].Results, deployResult) + templateData.NamespacedResults[deploy.Namespace].Results = append( + templateData.NamespacedResults[deploy.Namespace].Results, deployResult) } - return dashboardData, nil + return templateData, nil } diff --git a/pkg/kube/clientset.go b/pkg/kube/clientset.go index 6cd55353..9d8aa8b1 100644 --- a/pkg/kube/clientset.go +++ b/pkg/kube/clientset.go @@ -4,10 +4,12 @@ import ( "fmt" "k8s.io/client-go/kubernetes" + // Required for GKE auth _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client/config" ) +// CreateClientset returns a new Kubernetes clientset func CreateClientset() *kubernetes.Clientset { kubeConf := config.GetConfigOrDie() diff --git a/pkg/validator/container.go b/pkg/validator/container.go index d239d1f9..77a5429d 100644 --- a/pkg/validator/container.go +++ b/pkg/validator/container.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" ) -// ContainerValidation tracks validation failures associated with a Container +// ContainerValidation tracks validation failures associated with a Container. type ContainerValidation struct { Container corev1.Container Failures []ResultMessage diff --git a/pkg/validator/types.go b/pkg/validator/types.go index 790a2279..831c9211 100644 --- a/pkg/validator/types.go +++ b/pkg/validator/types.go @@ -1,10 +1,12 @@ package validator +// NamespacedResult groups resource results by namespace. type NamespacedResult struct { Summary *ResultSummary Results []ResourceResult } +// ResourceResult groups container results by parent resource. type ResourceResult struct { Name string Type string @@ -12,34 +14,41 @@ type ResourceResult struct { ContainerResults []ContainerResult } +// ResultSummary provides a high level overview of success, warnings, and failures. type ResultSummary struct { Successes uint Warnings uint Failures uint } +// ContainerResult provides a list of validation messages for each container. type ContainerResult struct { Name string Messages []ResultMessage } +// ResultMessage contains a message and a type indicator (success, warning, or failure). type ResultMessage struct { Message string Type string } +// Score represents a percentage of validations that were successful. func (rs *ResultSummary) Score() uint { return uint(float64(rs.Successes) / float64(rs.Successes+rs.Warnings+rs.Failures) * 100) } +// WarningWidth is a UI specific helper that helps determine the width of a progress bar. func (rs *ResultSummary) WarningWidth(fullWidth uint) uint { return uint(float64(rs.Successes+rs.Warnings) / float64(rs.Successes+rs.Warnings+rs.Failures) * float64(fullWidth)) } +// SuccessWidth is a UI specific helper that helps determine the width of a progress bar. func (rs *ResultSummary) SuccessWidth(fullWidth uint) uint { return uint(float64(rs.Successes) / float64(rs.Successes+rs.Warnings+rs.Failures) * float64(fullWidth)) } +// HTMLSpecialCharCode is a UI specific helper that provides an HTML char code. func (rm *ResultMessage) HTMLSpecialCharCode() string { switch rm.Type { case "success":