diff --git a/.gitignore b/.gitignore index e62eda10..fbc4daf7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ fairwinds Tiltfile main +.DS_Store diff --git a/config.yml b/config.yml new file mode 100644 index 00000000..6f1d3f1c --- /dev/null +++ b/config.yml @@ -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 diff --git a/main.go b/main.go index d42d26b6..df1d8409 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( "os" conf "github.com/reactiveops/fairwinds/pkg/config" + "github.com/reactiveops/fairwinds/pkg/dashboard" "github.com/reactiveops/fairwinds/pkg/validator" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -49,7 +50,8 @@ func main() { c, err := conf.ParseFile("config.yml") if err != nil { - return + glog.Println("Error parsing config.yml:", err) + os.Exit(1) } if *webhook { @@ -62,9 +64,18 @@ func main() { } func startDashboardServer(c conf.Configuration) { - http.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { validator.DeployHandler(w, r, c) }) - http.HandleFunc("/ping", validator.PingHandler) - 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)) } diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go new file mode 100644 index 00000000..d2caebde --- /dev/null +++ b/pkg/dashboard/dashboard.go @@ -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/charts.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.Spec, 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", + }}, + }}, + }}, + } +} diff --git a/pkg/dashboard/templates/charts.gohtml b/pkg/dashboard/templates/charts.gohtml new file mode 100644 index 00000000..ecbf61f1 --- /dev/null +++ b/pkg/dashboard/templates/charts.gohtml @@ -0,0 +1,177 @@ + + + + + + + Fairwinds + + + + + + + + + + + +
+

Fairwinds

+
+ +
+
+
+

Overall Score:

+
+ +
+
+
+

Scores By Namespace:

+ +
+
+ + {{ range .NamespacedResults }} +
+

Namespace: {{ .Namespace }}

+ + + {{ range .Results }} + + + + + {{ end }} +
+
{{ .Type }}: {{ .Name }}
+
    + {{ range .Messages}} +
  • &#{{ .HTMLSpecialCharCode }}; {{ .Message }}
  • + {{ end }} +
+
+
+
+
+
+
+
+
+
+
+ {{ end }} +
+ + + + + diff --git a/pkg/dashboard/templates/dashboard.gohtml b/pkg/dashboard/templates/dashboard.gohtml new file mode 100644 index 00000000..6a6d73a1 --- /dev/null +++ b/pkg/dashboard/templates/dashboard.gohtml @@ -0,0 +1,78 @@ + + + + + + + Fairwinds + + + + + + + + + +
+

Fairwinds

+
+ +
+

Overall Score:

+

{{ .ClusterSummary.Score }}%

+
+
+
+
+
+
+
+
+
+ Successes: + {{ .ClusterSummary.Successes }} +
+
+ Warnings: + {{ .ClusterSummary.Warnings }} +
+
+ Failures: + {{ .ClusterSummary.Failures }} +
+
+
+ +{{ range .NamespacedResults }} +
+

Namespace: {{ .Namespace }}

+
+ + + {{ range .Results }} + + + + + {{ end }} +
+
{{ .Type }}: {{ .Name }}
+
    + {{ range .Messages}} +
  • &#{{ .HTMLSpecialCharCode }}; {{ .Message }}
  • + {{ end }} +
+
+
+
+
+
+
+
+
+
+{{ end }} + + + diff --git a/pkg/validator/types.go b/pkg/validator/types.go new file mode 100644 index 00000000..a65cfd14 --- /dev/null +++ b/pkg/validator/types.go @@ -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" + } +} diff --git a/public/charts.html b/public/charts.html new file mode 100644 index 00000000..c42add4a --- /dev/null +++ b/public/charts.html @@ -0,0 +1,192 @@ + + + + + + + Fairwinds + + + + + + + + + + + +
+

Fairwinds

+
+ +
+
+
+

Overall Score:

+
+ +
+
+
+

Scores By Namespace:

+ +
+
+ +
+

Namespace: kube-system

+ + + + + + + + + + +
+
DaemonSet: datadog-agent
+
+
+
+
+
+
+
+
+
+
Deployment: tiller-deployment
+
    +
  • Image Tag Specified
  • +
  • Resource Limits
  • +
  • Resource Requests
  • +
  • Run As Non Root User
  • +
  • Liveness Probe
  • +
  • Readiness Probe
  • +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + diff --git a/public/css/charts.css b/public/css/charts.css new file mode 100644 index 00000000..72e5d8bf --- /dev/null +++ b/public/css/charts.css @@ -0,0 +1,193 @@ +body { + margin: 0; + font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + background: #eee; +} + +.header { + background-color: #fff; + color: #006369; + padding: 20px 30px; + box-shadow: 0 2px 5px rgba(0,0,0,0.3); +} + +.header h1 { + font-family: 'Lobster', sans-serif; + font-size: 60px; + font-weight: normal; + margin: 0 auto; + width: 900px; +} + +.dashboard-content { + width: 960px; + margin: 0 auto; +} + +.charts { + height: 400px; +} + +.chart-section { + float: left; + margin: 20px; + padding: 30px; + width: 300px; + height: 320px; + color: #333; + background-color: #fff; + box-shadow: 0 2px 5px rgba(0,0,0,0.3); + border-radius: 5px; +} + +.chart-section.namespace-score { + margin-left: 0px; + width: 480px; +} + +.chart-section h3 { + margin: 0 0 15px; + padding: 0 0 15px; + font-weight: 300; + font-size: 30px; + border-bottom: 1px solid #eee; +} + +#clusterScoreChart { + width: 260px; + height: 260px; +} + +#namespaceScoreChart { + width: 520px; + height: 260px; +} + +.namespace { + margin: 20px; + padding: 10px 20px; + color: #333; + background-color: #fff; + box-shadow: 0 2px 5px rgba(0,0,0,0.3); + border-radius: 5px; +} + +.namespace h3 { + margin: 0; + font-weight: 300; + font-size: 30px; + padding: 20px; +} + +.namespace h3 strong { + margin: 0; + font-weight: bold; + font-size: 30px; +} + +.cluster-score .status { + width: 400px; + height: 30px; + margin: 30px auto; +} + +.namespace-content { + width: 100%; + border-spacing: 0; + border-collapse: collapse; +} + +.namespace-content tr { + height: 50px; +} + +.namespace-content td { + padding: 15px 20px; + margin: 0; + font-size: 18px; + border-top: 1px solid #eee; +} + +.namespace-content .caret-expander { + display: inline-block; + width: 15px; + height: 15px; + margin-right: 10px; + background-image: url('../images/caret-right.svg'); + background-size: 15px auto; + background-repeat: no-repeat; + background-position: 2px center; +} + +.namespace-content .caret-expander.expanded { + background-image: url('../images/caret-bottom.svg'); + background-position: center center; +} + +.namespace-content .extra { + list-style-type: none; + font-size: 14px; + line-height: 19px; + margin: 10px 30px; + padding: 0; +} + +.namespace-content .extra span { + display: inline-block; + text-align: center; + width: 20px; + font-size: 20px; +} + +.namespace-content .extra .success { + color: #006469; +} + +.namespace-content .extra .warning { + color: #AE7500; +} + +.namespace-content .extra .warning span { + font-size: 15px; +} + +.namespace-content .extra .failure { + color: #AE0400; +} + +.namespace-content td.status-bar { + vertical-align: top; + padding-top: 18px; +} + +.namespace-content .status { + float: right; + width: 200px; +} + +.namespace-content .status div { + height: 15px; + border-radius: 10px; +} + +.namespace-content .status .passing { + background-color: #006469; + float: left; +} + +.namespace-content .status .warning { + background-color: #AE7500; + float: left; +} + +@keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1;} +} + +.namespace-content .status .failing { + background-color: #AE0400; + width: 200px; + animation: fadeIn 2s; +} + diff --git a/public/css/main.css b/public/css/main.css new file mode 100644 index 00000000..d587aa48 --- /dev/null +++ b/public/css/main.css @@ -0,0 +1,198 @@ +body { + margin: 0; + font-family: 'Helvetica', 'Arial', sans-serif; +} + +.header { + background-color: #006369; + color: #fff; + padding: 50px; +} + +.header h1 { + font-family: 'Lobster', sans-serif; + font-size: 100px; + font-weight: normal; + text-align: center; + margin: 0; +} + +.cluster-score { + margin: 0; + padding: 30px; + color: #003339; + background-color: #eaf3f9; + text-align: center; +} + +.cluster-score h2 { + margin: 0; + font-size: 80px; +} + +.cluster-score .status { + width: 400px; + height: 30px; + margin: 30px auto; +} + +.cluster-score .status div { + height: 30px; + border-radius: 8px; +} + +.cluster-score .status .passing { + background-color: #4ab977; + float: left; +} + +.cluster-score .status .warning { + background-color: #bdbd63; + float: left; +} + +.cluster-score .status .failing { + background-color: #c37575; + width: 400px; +} + +.cluster-score p { + margin: 10px 0; + font-weight: 300; + font-size: 24px; +} + +.cluster-score .summary { + margin: 30px auto; + font-weight: 300; + font-size: 24px; + width: 300px; + text-align: left; + line-height: 40px; +} + +.cluster-score .summary .value { + float: right; + font-size: 28px; +} + +.namespace-header { + background-color: #006369; + color: #fff; + font-size: 50px; + font-weight: normal; + padding: 20px; +} + +.namespace-header h3 { + margin: 0; + font-weight: 300; + font-size: 30px; +} + +.namespace-header h3 strong { + margin: 0; + font-weight: bold; + font-size: 30px; +} + +.cluster-score .status { + width: 400px; + height: 30px; + margin: 30px auto; +} + +.namespace-content { + width: 100%; + background-color: #eaf3f9; + border-spacing: 0; + border-collapse: collapse; +} + +.namespace-content tr { + height: 50px; +} + +.namespace-content td { + padding: 15px 20px; + margin: 0; + font-size: 18px; + border-bottom: 1px solid #aac3c9; +} + +.namespace-content .caret-expander { + display: inline-block; + width: 15px; + height: 15px; + margin-right: 10px; + background-image: url('../images/caret-right.svg'); + background-size: 15px auto; + background-repeat: no-repeat; + background-position: 2px center; +} + +.namespace-content .caret-expander.expanded { + background-image: url('../images/caret-bottom.svg'); + background-position: center center; +} + +.namespace-content .extra { + list-style-type: none; + font-size: 14px; + line-height: 19px; + margin: 10px 30px; + padding: 0; +} + +.namespace-content .extra span { + display: inline-block; + text-align: center; + width: 20px; +} + +.namespace-content .extra .success { + color: #2a9957; +} + +.namespace-content .extra .warning { + color: #adad43; +} + +.namespace-content .extra .warning span { + font-size: 11px; +} + +.namespace-content .extra .failure { + color: #a35555; +} + +.namespace-content td.status-bar { + vertical-align: top; + padding-top: 18px; +} + +.namespace-content .status { + float: right; + width: 200px; +} + +.namespace-content .status div { + height: 15px; + border-radius: 4px; +} + +.namespace-content .status .passing { + background-color: #4ab977; + float: left; +} + +.namespace-content .status .warning { + background-color: #bdbd63; + float: left; +} + +.namespace-content .status .failing { + background-color: #c37575; + width: 200px; +} + diff --git a/public/css/normalize.css b/public/css/normalize.css new file mode 100644 index 00000000..c4699894 --- /dev/null +++ b/public/css/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + + html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/public/images/caret-bottom.svg b/public/images/caret-bottom.svg new file mode 100755 index 00000000..2b4c5253 --- /dev/null +++ b/public/images/caret-bottom.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/caret-right.svg b/public/images/caret-right.svg new file mode 100755 index 00000000..23b33a08 --- /dev/null +++ b/public/images/caret-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/circle-check.svg b/public/images/circle-check.svg new file mode 100755 index 00000000..5df0f156 --- /dev/null +++ b/public/images/circle-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/circle-x.svg b/public/images/circle-x.svg new file mode 100755 index 00000000..0244752c --- /dev/null +++ b/public/images/circle-x.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/images/warning.svg b/public/images/warning.svg new file mode 100755 index 00000000..e3594a88 --- /dev/null +++ b/public/images/warning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 00000000..97ac32ef --- /dev/null +++ b/public/index.html @@ -0,0 +1,92 @@ + + + + + + + Fairwinds + + + + + + + + + +
+

Fairwinds

+
+ +
+

Overall Score:

+

64%

+
+
+
+
+
+
+
+
+
+ Successes: + 118 +
+
+ Warnings: + 34 +
+
+ Failures: + 28 +
+
+
+ +
+

Namespace: kube-system

+
+ + + + + + + + + + +
+
DaemonSet: datadog-agent
+
+
+
+
+
+
+
+
+
+
Deployment: tiller-deployment
+
    +
  • Image Tag Specified
  • +
  • Resource Limits
  • +
  • Resource Requests
  • +
  • Run As Non Root User
  • +
  • Liveness Probe
  • +
  • Readiness Probe
  • +
+
+
+
+
+
+
+
+
+
+ + + +