Skip to content

Commit

Permalink
rendering initial dashboard UI with go template and sample data
Browse files Browse the repository at this point in the history
  • Loading branch information
robscott committed Jan 28, 2019
1 parent 88c0b39 commit 700b645
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 35 deletions.
15 changes: 15 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
resources:
requests:
cpu:
min: 100m
max: 1
memory:
min: 100M
max: 3G
limits:
cpu:
min: 150m
max: 2
memory:
min: 150M
max: 4G
45 changes: 19 additions & 26 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@
package main

import (
"encoding/json"
"flag"
glog "log"
"net/http"
"os"

conf "github.com/reactiveops/fairwinds/pkg/config"
"github.com/reactiveops/fairwinds/pkg/kube"
"github.com/reactiveops/fairwinds/pkg/dashboard"
"github.com/reactiveops/fairwinds/pkg/validator"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apitypes "k8s.io/apimachinery/pkg/types"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
"sigs.k8s.io/controller-runtime/pkg/client/config"
Expand All @@ -42,7 +40,7 @@ var FairwindsName = "fairwinds"
var log = logf.Log.WithName(FairwindsName)

func main() {
dashboard := flag.Bool("dashboard", false, "Runs the webserver for Fairwinds dashboard.")
// dashboard := flag.Bool("dashboard", false, "Runs the webserver for Fairwinds dashboard.")
webhook := flag.Bool("webhook", false, "Runs the webhook webserver.")

var disableWebhookConfigInstaller bool
Expand All @@ -53,40 +51,35 @@ func main() {

c, err := conf.ParseFile("config.yml")
if err != nil {
return
glog.Println("Error parsing config.yml:", err)
os.Exit(1)
}

if *webhook {
startWebhookServer(c, disableWebhookConfigInstaller)
}

if *dashboard {
startDashboardServer(c)
}
// if *dashboard {
startDashboardServer(c)
// }
}

func startDashboardServer(c conf.Configuration) {
http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { validateHandler(w, r, c) })
glog.Println("Starting Fairwinds dashboard webserver on port 8080.")
http.HandleFunc("/results.json", func(w http.ResponseWriter, r *http.Request) {
dashboard.RenderJSON(w, r, c)
})
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("public/"))))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
dashboard.Render(w, r, c)
})
glog.Println("Starting Fairwinds dashboard server on port 8080.")
glog.Fatal(http.ListenAndServe(":8080", nil))
}

func validateHandler(w http.ResponseWriter, r *http.Request, c conf.Configuration) {
var results []validator.Results
pods, err := kube.CoreV1API.Pods("").List(metav1.ListOptions{})
if err != nil {
return
}
glog.Println("pods count:", len(pods.Items))
for _, pod := range pods.Items {
result := validator.ValidatePods(c, &pod, validator.Results{})
results = append(results, result)
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}

func startWebhookServer(c conf.Configuration, disableWebhookConfigInstaller bool) {
logf.SetLogger(logf.ZapLogger(false))
entryLog := log.WithName("entrypoint")
Expand Down
80 changes: 80 additions & 0 deletions pkg/dashboard/dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package dashboard

import (
"encoding/json"
"html/template"
"log"
"net/http"

conf "github.com/reactiveops/fairwinds/pkg/config"
"github.com/reactiveops/fairwinds/pkg/kube"
"github.com/reactiveops/fairwinds/pkg/validator"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type DashboardData struct {
ClusterSummary *validator.ResultSummary
NamespacedResults []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 := getDashboardData()
tmpl.Execute(w, dashboardData)
}

func RenderJSON(w http.ResponseWriter, r *http.Request, c conf.Configuration) {
results := []validator.Results{}
pods, err := kube.CoreV1API.Pods("").List(metav1.ListOptions{})
if err != nil {
http.Error(w, "Error Fetching Pods", 500)
return
}
log.Println("pods count:", len(pods.Items))
for _, pod := range pods.Items {
result := validator.ValidatePods(c, &pod, validator.Results{})
results = append(results, result)
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}

func getDashboardData() DashboardData {
return DashboardData{
ClusterSummary: &validator.ResultSummary{
Successes: 46,
Warnings: 8,
Failures: 5,
},
NamespacedResults: []validator.NamespacedResult{{
Namespace: "kube-system",
Results: []validator.ResourceResult{{
Name: "tiller",
Type: "Deployment",
Summary: &validator.ResultSummary{
Successes: 7,
Warnings: 3,
Failures: 2,
},
Messages: []validator.ResultMessage{{
Message: "Image Tag Specified",
Type: "success",
}, {
Message: "Liveness Probe Specified",
Type: "success",
}, {
Message: "Readiness Probe Specified",
Type: "success",
}, {
Message: "Container Running As Root",
Type: "warning",
}, {
Message: "Resource requests are not set",
Type: "failure",
}},
}},
}},
}
}
78 changes: 78 additions & 0 deletions pkg/dashboard/templates/dashboard.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Fairwinds</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet">
<link rel="stylesheet" href="/static/css/normalize.css">
<link rel="stylesheet" href="/static/css/main.css">
</head>

<body>
<div class="header">
<h1>Fairwinds</h1>
</div>

<div class="cluster-score">
<p>Overall Score:</p>
<h2>{{ .ClusterSummary.Score }}%</h2>
<div class="status">
<div class="failing">
<div class="warning" style="width: {{ .ClusterSummary.WarningWidth 400 }}px;">
<div class="passing" style="width: {{ .ClusterSummary.SuccessWidth 400 }}px;"></div>
</div>
</div>
</div>
<div class="summary">
<div class="item">
<span class="label">Successes:</span>
<strong class="value">{{ .ClusterSummary.Successes }}</strong>
</div>
<div class="item">
<span class="label">Warnings:</span>
<strong class="value">{{ .ClusterSummary.Warnings }}</strong>
</div>
<div class="item">
<span class="label">Failures:</span>
<strong class="value">{{ .ClusterSummary.Failures }}</strong>
</div>
</div>
</div>

{{ range .NamespacedResults }}
<div class="namespace-header">
<h3>Namespace: <strong>{{ .Namespace }}</strong></h3>
</div>

<table class="namespace-content">
{{ range .Results }}
<tr>
<td>
<div class="name"><span class="caret-expander expanded"></span>{{ .Type }}: <strong>{{ .Name }}</strong></div>
<ul class="extra">
{{ range .Messages}}
<li class="{{ .Type }}"><span>&#{{ .HTMLSpecialCharCode }};</span> {{ .Message }}</li>
{{ end }}
</ul>
</td>
<td class="status-bar">
<div class="status">
<div class="failing">
<div class="warning" style="width: {{ .Summary.WarningWidth 200 }}px;">
<div class="passing" style="width: {{ .Summary.SuccessWidth 200 }}px;"></div>
</div>
</div>
</div>
</td>
</tr>
{{ end }}
</table>
{{ end }}
</body>

</html>
64 changes: 64 additions & 0 deletions pkg/validator/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package validator

// namespaceResults: [{
// name: "kube-system",
// resourceResults: [{
// name: "example-deployment",
// type: "DaemonSet",
// summary: {
// successes: 28,
// warnings: 12,
// failures: 18,
// },
// messages: [{
// message: "Resource requests are not set",
// type: "failure",
// }]
// }]
// }]

type NamespacedResult struct {
Namespace string
Results []ResourceResult
}

type ResourceResult struct {
Name string
Type string
Summary *ResultSummary
Messages []ResultMessage
}

type ResultSummary struct {
Successes uint
Warnings uint
Failures uint
}

type ResultMessage struct {
Message string
Type string
}

func (rs *ResultSummary) Score() uint {
return uint(float64(rs.Successes) / float64(rs.Successes+rs.Warnings+rs.Failures) * 100)
}

func (rs *ResultSummary) WarningWidth(fullWidth uint) uint {
return uint(float64(rs.Successes+rs.Warnings) / float64(rs.Successes+rs.Warnings+rs.Failures) * float64(fullWidth))
}

func (rs *ResultSummary) SuccessWidth(fullWidth uint) uint {
return uint(float64(rs.Successes) / float64(rs.Successes+rs.Warnings+rs.Failures) * float64(fullWidth))
}

func (rm *ResultMessage) HTMLSpecialCharCode() string {
switch rm.Type {
case "success":
return "9745"
case "warning":
return "9888"
default:
return "9746"
}
}
14 changes: 8 additions & 6 deletions public/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ body {
margin: 30px auto;
}

.cluster-score .status .passing, .cluster-score .status .failing, .cluster-score .status .warning {
.cluster-score .status div {
height: 30px;
border-radius: 8px;
}
Expand Down Expand Up @@ -105,6 +105,8 @@ body {
.namespace-content {
width: 100%;
background-color: #eaf3f9;
border-spacing: 0;
border-collapse: collapse;
}

.namespace-content tr {
Expand Down Expand Up @@ -148,19 +150,19 @@ body {
width: 20px;
}

.namespace-content .extra .pass {
.namespace-content .extra .success {
color: #2a9957;
}

.namespace-content .extra .warn {
.namespace-content .extra .warning {
color: #adad43;
}

.namespace-content .extra .warn span {
.namespace-content .extra .warning span {
font-size: 11px;
}

.namespace-content .extra .fail {
.namespace-content .extra .failure {
color: #a35555;
}

Expand All @@ -174,7 +176,7 @@ body {
width: 200px;
}

.namespace-content .status .passing, .namespace-content .status .failing, .namespace-content .status .warning {
.namespace-content .status div {
height: 15px;
border-radius: 4px;
}
Expand Down
6 changes: 3 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">

<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet">
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="/static/css/normalize.css">
<link rel="stylesheet" href="/static/css/main.css">
</head>

<body>
Expand Down Expand Up @@ -89,4 +89,4 @@ <h3>Namespace: <strong>kube-system</strong></h3>

</body>

</html>
</html>

0 comments on commit 700b645

Please sign in to comment.