diff --git a/README.md b/README.md
index ef03fec..a1376f2 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# Faces Demo
This is the Faces demo application. It has a single-page web GUI that presents
-a grid of cells, each of which _should_ show a smiling face on a green
+a grid of cells, each of which _should_ show a grinning face on a light blue
background. Spoiler alert: installed exactly as committed to this repo, that
isn't what you'll get -- many, many things can go wrong, and will. The point
of the demo is let you try to fix things.
@@ -16,10 +16,14 @@ In here you will find:
- These things are installed in a demo configuration: read and think
**carefully** before using this demo as background for a production
installation! In particular:
- - We use `sed` to force everything to just one replica when installing
- Emissary -- **DON'T** do that in production.
- - We only configure HTTP, not HTTPS. Again, **DON'T** do this in
- production.
+
+ - We deploy Emissary with only one replica of everything, using a
+ currently-unofficial chart to also skip support for `v1` and `v2`
+ Emissary CRDs.
+
+ - We only configure HTTP, not HTTPS.
+
+ These are likely both bad ideas for a production installation.
- `DEMO.md`, a Markdown file for the resilience demo presented live for a
couple of events. The easiest way to use `DEMO.md` is to run it with
@@ -53,6 +57,35 @@ In here you will find:
- To run the demo as we've given it before, check out [DEMO.md]. The easiest
way to use that is to run it with [demosh].
+## Architecture
+
+The Faces architecture is fairly simple:
+
+- The `faces-gui` workload, reached on the `/faces/` path, just returns the
+ HTML and Javascript for the GUI. The GUI is a single-page webapp that
+ displays a grid of cells: for each cell, the GUI calls the `face` workload.
+
+- The `face` workload, reached on the `/face/` path, calls the `smiley`
+ workload to get a smiley face and the `color` workload to get a color. It
+ then composes the responses together and returns the smiley/color
+ combination to the GUI for display.
+
+- The `smiley` workload returns a smiley face. By default, this is a grinning
+ smiley, U+1F603, but you can set the `SMILEY` environment variable to any
+ key in the `Smileys` map from `constants.go` to get a different smiley.
+
+- The `color` workload returns a color. By default, this is a light blue, but
+ you can set the `COLOR` environment variable to any key in the `Colors` map
+ from `constants.go` to get a different color, or to any arbitrary hex color
+ code (e.g. `#ff0000` for bright red).
+
+ The named colors in the `Colors` map are meant to work for normal color
+ vision and for various kinds of colorblindness, and are taken from the
+ "Bright" color scheme shown in the "Qualitative // Color Schemes" section of
+ https://personal.sron.nl/~pault/. For (much) more information, read the
+ comments in `pkg/faces/constants.go`. Feedback here is welcome, since the
+ Faces authors have normal color vision...
+
[Linkerd]: https://linkerd.io
[Emissary-ingress]: https://www.getambassador.io/docs/emissary/
[DEMO.md]: DEMO.md
diff --git a/assets/html/index.html b/assets/html/index.html
index b80af38..710f123 100644
--- a/assets/html/index.html
+++ b/assets/html/index.html
@@ -25,22 +25,6 @@
font-family: sans-serif;
}
- .green {
- color: rgb(55 117 59);
- }
-
- .red {
- color: rgb(125 42 83); /* dark magenta */
- }
-
- .blue {
- color: rgb(151 202 234); /* light blue */
- }
-
- .grey {
- color: grey;
- }
-
.key {
font-family: sans-serif;
font-size: 32px;
@@ -74,7 +58,7 @@
.cell {
display: inline-block;
- border: 1px solid grey;
+ border: 2px solid grey;
border-radius: 16px;
height: 120px;
width: 120px;
@@ -96,16 +80,16 @@
}
.cell-pod-info {
- height: 120px;
- width: 120px;
- border-radius: 16px;
+ display: inline-block;
+ position: relative;
+ text-align: center;
}
.cell-pod-id {
position: absolute;
- top: 1px;
- left: 1px;
- width: 118px;
+ top: 4px;
+ left: 4px;
+ width: 120px;
border-radius: 14px 14px 0px 0px;
font-family: sans-serif;
background: white;
@@ -179,24 +163,8 @@
border-right: 2px solid grey;
padding-left: .5em;
padding-right: .5em;
- max-width: 252px !important;
- width: 252px !important;
- }
-
- .bg-green {
- background-color: rgb(55 117 59);
- }
-
- .bg-red {
- background-color: rgb(125 42 83); /* dark magenta */
- }
-
- .bg-blue {
- background-color: rgb(151 202 234); /* light blue */
- }
-
- .bg-grey {
- background-color: grey;
+ max-width: 258px !important;
+ width: 258px !important;
}
.inline {
@@ -297,7 +265,7 @@
Faces
logmsg(color, msg) {
let now = new Date().toISOString()
console.log(`${now} ${msg}`)
- // this.logdiv.innerHTML = `${now}: ${msg} ` + this.logdiv.innerHTML
+ // this.logdiv.innerHTML = `${now}: ${msg} ` + this.logdiv.innerHTML
}
// success, fail, and info are wrappers around logmsg to avoid
@@ -308,7 +276,7 @@ Faces
}
fail(msg) {
- this.logmsg(Cell.colors.red, msg)
+ this.logmsg(Cell.colors.purple, msg)
}
info(msg) {
@@ -369,7 +337,7 @@ Faces
}
start() {
- this.userDiv.innerHTML = `User: ${this.user} `
+ this.userDiv.innerHTML = `User: ${this.user} `
}
// stop() {
@@ -435,12 +403,12 @@ Faces
if (status == 599) {
// Latched error state.
smiley = Cell.smilies.neutral
- bgColor = Cell.colors.hotpink
+ bgColor = Cell.colors.yellow
bumpCounter = true
}
else if (status == 429) {
smiley = Cell.smilies.sleeping
- bgColor = Cell.colors.pink
+ bgColor = Cell.colors.red
bumpCounter = true
}
else if (status == 200) {
@@ -449,7 +417,7 @@ Faces
}
if (bgColor == "504") {
- bgColor = Cell.colors.pink
+ bgColor = Cell.colors.red
}
this.countDiv.innerHTML = 0
@@ -457,9 +425,9 @@ Faces
}
else {
// This should probably never happen.
- borderColor = Cell.colors.red
+ borderColor = Cell.colors.purple
smiley = Cell.smilies.upset
- bgColor = Cell.colors.red
+ bgColor = Cell.colors.purple
bumpCounter = true
}
@@ -510,7 +478,7 @@ Faces
// New pod!
let podDiv = document.createElement("div")
podDiv.id = `cell-pod-${shortName}`
- podDiv.className = "cell"
+ podDiv.className = "cell-pod-info"
let podIDDiv = document.createElement("div")
podIDDiv.id = `cell-pod-id-${shortName}`
@@ -520,7 +488,7 @@ Faces
let podInfoDiv = document.createElement("div")
podInfoDiv.id = `cell-pod-info-${shortName}`
- podInfoDiv.className = "cell-pod-info"
+ podInfoDiv.className = "cell"
podDiv.appendChild(podInfoDiv)
let podSmileySpan = document.createElement("span")
@@ -563,23 +531,26 @@ Faces
"neutral": "😐",
"screaming": "😱",
"sleeping": "😴",
- "smiling": "😃",
+ "grinning": "😃",
"thinking": "🤔",
"tongue": "😛",
"upset": "😬",
"yay": "🎉",
};
+ // There are many many notes about these colors in pkg/faces/constants.go.
+ // Go read that: the short version is that colorblindness is a thing, so
+ // don't muck with these too much.
static colors = {
- "grey": "grey",
- "purple": "rgb(48 34 130)",
- "green": "rgb(55 117 59)",
- "cyan": "rgb(55 117 59)", // too similar to pink and grey, avoid
- "blue": "rgb(151 202 234)", // light blue
- "yellow": "rgb(218 204 130)",
- "pink": "rgb(191 108 120)",
- "hotpink": "rgb(158 75 149)",
- "red": "rgb(125 42 83)", // dark magenta
+ "grey": "#BBBBBB",
+ "black": "#000000",
+ "white": "#FFFFFF",
+ "darkblue": "#4477AA",
+ "blue": "#66CCEE",
+ "green": "#228833",
+ "yellow": "#CCBB44",
+ "red": "#EE6677",
+ "purple": "#AA3377",
}
constructor(logger, sw, podSet, fetchURL, enclosingDiv, row, col) {
@@ -596,7 +567,7 @@ Faces
let cellDiv = document.createElement("div")
cellDiv.id = `cell-${row}-${col}`
cellDiv.className = "cell"
- cellDiv.style.background = "grey"
+ cellDiv.style.background = Cell.colors.grey
cellDiv.style.opacity = 0.5
let smileySpan = document.createElement("span")
@@ -672,7 +643,10 @@ Faces
// ...then figure out what we got.
let { curStatus, anyTimeouts,
- smiley, bgColor, borderColor } = this.parseResults(xhr);
+ smiley, bgColor, borderColor, errors } = this.parseResults(xhr);
+
+ // let msg = `[${xhrName}] (${latency}ms): ${smiley} ${bgColor} ${borderColor} -- ${errors}`
+ // this.success(msg);
// Update the pod, if we can...
let pod = xhr.getResponseHeader("x-faces-pod");
@@ -721,10 +695,6 @@ Faces
$(`cell-count-${this.row}-${this.col}`).innerHTML = ""
}
- // FINALLY: show 'em what we got.
- // let msg = `[${xhrName}] XHR result (${latency}ms): ${errors} -- ${text}`
- // this.success(msg);
-
if ((smiley != undefined) || (bgColor != undefined)) {
$(`cell-${this.row}-${this.col}`).style.opacity = 0.0
@@ -767,7 +737,7 @@ Faces
setTimeout(() => {
$(`smiley-${this.row}-${this.col}`).innerHTML = Cell.smilies.confused
$(`cell-${this.row}-${this.col}`).style.opacity = 1.0
- $(`cell-${this.row}-${this.col}`).style.background = Cell.colors.red
+ $(`cell-${this.row}-${this.col}`).style.background = Cell.colors.purple
$(`cell-${this.row}-${this.col}`).style.borderColor = "grey"
}, 50)
@@ -818,9 +788,9 @@ Faces
smiley = obj.smiley;
bgColor = obj.color;
- if (obj.errors != undefined) {
+ if ((obj.errors != undefined) && (obj.errors.length > 0)) {
errors = obj.errors.join(",");
- borderColor = Cell.colors.red
+ borderColor = Cell.colors.purple
}
else {
errors = "success!";
@@ -836,9 +806,9 @@ Faces
catch (e) {
// Whoops, something went wrong. If it's a SyntaxError, that
// probably means we got bad JSON. Otherwise, it's... who knows?
- borderColor = Cell.colors.red
+ borderColor = Cell.colors.purple
smiley = Cell.smilies.confused;
- bgColor = Cell.colors.red;
+ bgColor = Cell.colors.purple;
if (e instanceof SyntaxError) {
errors = "parse error";
@@ -860,16 +830,16 @@ Faces
}
else if (curStatus == 599) {
// This is our latched-error status. Show it as a neutral face
- // on a hot pink background.
+ // on a yellow background.
smiley = Cell.smilies.neutral;
- bgColor = Cell.colors.hotpink;
+ bgColor = Cell.colors.yellow;
}
else if (Math.floor(curStatus / 100) == 5) {
// Some other 5yz, so an unknown kind of server error. (In
// practice, this is probably a 503 because there's some kind
// of connectivity error, but whatever, we don't care.
smiley = Cell.smilies.confused;
- bgColor = Cell.colors.red;
+ bgColor = Cell.colors.purple;
errors = "server error";
}
@@ -881,24 +851,37 @@ Faces
bgColor = Cell.colors.purple
}
- return { curStatus, anyTimeouts, smiley, bgColor, borderColor };
+ return { curStatus, anyTimeouts, smiley, bgColor, borderColor, errors };
}
}
class Key {
constructor(keyDiv) {
let keyEntries = [
- [ "smiling", Cell.colors.blue, Cell.colors.grey, "24px", "Success!" ],
- [ "confused", Cell.colors.red, Cell.colors.grey, "", "Face service error" ],
- [ "sleeping", Cell.colors.pink, Cell.colors.grey, "", "Timeout" ],
- [ "kaboom", Cell.colors.red, Cell.colors.grey, "24px", "Service overwhelmed" ],
- [ "smiling", Cell.colors.grey, "transparent", "", "Color service error" ],
- [ "", Cell.colors.blue, Cell.colors.red, "24px", "Smiley service error" ],
- [ "-", "-", "-", "", "Slow service" ]
+ [ "Success!",
+ "grinning", Cell.colors.blue, Cell.colors.grey, "24px" ],
+
+ [ "Face service error",
+ "confused", Cell.colors.purple, Cell.colors.grey, "" ],
+
+ [ "Timeout",
+ "sleeping", Cell.colors.red, Cell.colors.grey, "" ],
+
+ [ "Service overwhelmed",
+ "kaboom", Cell.colors.yellow, Cell.colors.purple, "24px" ],
+
+ [ "Color service error",
+ "grinning", Cell.colors.grey, Cell.colors.purple, "" ],
+
+ [ "Smiley service error",
+ "", Cell.colors.blue, Cell.colors.purple, "24px" ],
+
+ [ "Slow service",
+ "-", "-", "-", "" ]
]
for (let i = 0; i < keyEntries.length; i++) {
- let [ smileyName, bgColor, borderColor, margin, text ] = keyEntries[i]
+ let [ text, smileyName, bgColor, borderColor, margin ] = keyEntries[i]
if (smileyName != "-") {
let smiley = ""
@@ -998,11 +981,11 @@ Faces
if (cellCount > 4) {
let smiley = Cell.smilies.sleeping
- let bgColor = Cell.colors.pink
+ let bgColor = Cell.colors.red
if (cell.lastStatus == 599) {
- smiley = Cell.smilies.screaming
- bgColor = Cell.colors.hotpink
+ smiley = Cell.smilies.neutral
+ bgColor = Cell.colors.yellow
}
$(`smiley-${cell.row}-${cell.col}`).innerHTML = smiley
@@ -1044,7 +1027,7 @@ Faces
mark(shape, fgColor, bgColor) {
this.markerdiv.innerHTML += `
-
+
@@ -1114,7 +1097,7 @@ Faces
else if (xhr.status != 200) {
text = `Unknown status ${xhr.status} after ${latency}ms`
shape = "19.000,49.000 31.000,49.000 31.000,1.000 19.000,1.000"
- fgColor = Cell.colors.red
+ fgColor = Cell.colors.purple
}
else {
// Parse JSON!
@@ -1130,12 +1113,12 @@ Faces
if (e instanceof SyntaxError) {
text = `Could not parse QotM: ${e.message}\n${xhr.responseText}`
shape = "19.000,49.000 31.000,49.000 31.000,1.000 19.000,1.000"
- fgColor = Cell.colors.red
+ fgColor = Cell.colors.purple
}
else {
text = `Missing QotM? ${e.message}`
shape = "19.000,49.000 31.000,49.000 31.000,1.000 19.000,1.000"
- fgColor = Cell.colors.red
+ fgColor = Cell.colors.purple
}
}
}
@@ -1160,7 +1143,7 @@ Faces
// this.success(msg);
let nowISO = now.toISOString()
- this.xhrdiv.innerHTML = `${nowISO}: ${msg}
`
+ this.xhrdiv.innerHTML = `${nowISO}: ${msg}
`
this.markers.mark(shape, fgColor, bgColor)
})
@@ -1178,7 +1161,7 @@ Faces
// this.fail(msg);
let nowISO = now.toISOString()
- this.xhrdiv.innerHTML = `${nowISO}: Failed!
`
+ this.xhrdiv.innerHTML = `${nowISO}: Failed!
`
// FIXME: this looks like the wrong arguments for calling this.markers.mark
this.markers.mark("red")
diff --git a/assets/logo-128.png b/assets/logo-128.png
index 70f696e..46445f8 100644
Binary files a/assets/logo-128.png and b/assets/logo-128.png differ
diff --git a/faces-chart/Chart.yaml b/faces-chart/Chart.yaml
index 11a64a9..e6eeadf 100644
--- a/faces-chart/Chart.yaml
+++ b/faces-chart/Chart.yaml
@@ -3,8 +3,8 @@ apiVersion: "v2"
appVersion: %VERSION%
description: |
This is the Faces demo application. It has a single-page web GUI that
- presents a grid of cells, each of which _should_ show a smiling face on
- a green background. Spoiler alert: installed exactly as committed to this
+ presents a grid of cells, each of which _should_ show a grinning face on a
+ light blue background. Spoiler alert: installed exactly as committed to this
repo, that isn't what you'll get -- many, many things can go wrong, and
will. The point of the demo is let you try to fix things.
type: application
diff --git a/faces-chart/templates/color.yaml b/faces-chart/templates/color.yaml
index 1b6ae5c..a91d436 100644
--- a/faces-chart/templates/color.yaml
+++ b/faces-chart/templates/color.yaml
@@ -37,8 +37,10 @@ spec:
env:
- name: FACES_SERVICE
value: "color"
+ {{- if .Values.color.color }}
- name: COLOR
value: {{ .Values.color.color }}
+ {{- end -}}
{{- include "partials.color-errorFraction" . }}
{{- include "partials.color-delayBuckets" . }}
resources:
diff --git a/faces-chart/templates/color2.yaml b/faces-chart/templates/color2.yaml
index f7a76af..95c83ea 100644
--- a/faces-chart/templates/color2.yaml
+++ b/faces-chart/templates/color2.yaml
@@ -38,8 +38,10 @@ spec:
env:
- name: FACES_SERVICE
value: "color"
+ {{- if .Values.color2.color }}
- name: COLOR
value: {{ .Values.color2.color }}
+ {{- end -}}
{{- include "partials.color2-errorFraction" . }}
{{- include "partials.color2-delayBuckets" . }}
resources:
diff --git a/faces-chart/templates/smiley.yaml b/faces-chart/templates/smiley.yaml
index 37ba530..ae5b66e 100644
--- a/faces-chart/templates/smiley.yaml
+++ b/faces-chart/templates/smiley.yaml
@@ -37,8 +37,10 @@ spec:
env:
- name: FACES_SERVICE
value: "smiley"
+ {{- if .Values.smiley.smiley }}
- name: SMILEY
value: {{ .Values.smiley.smiley }}
+ {{- end -}}
{{- include "partials.smiley-errorFraction" . }}
{{- include "partials.smiley-delayBuckets" . }}
resources:
diff --git a/faces-chart/templates/smiley2.yaml b/faces-chart/templates/smiley2.yaml
index 771127d..f61477c 100644
--- a/faces-chart/templates/smiley2.yaml
+++ b/faces-chart/templates/smiley2.yaml
@@ -38,8 +38,10 @@ spec:
env:
- name: FACES_SERVICE
value: "smiley"
+ {{- if .Values.smiley2.smiley }}
- name: SMILEY
value: {{ .Values.smiley2.smiley }}
+ {{- end -}}
{{- include "partials.smiley2-errorFraction" . }}
{{- include "partials.smiley2-delayBuckets" . }}
resources:
diff --git a/faces-chart/values.yaml b/faces-chart/values.yaml
index c5c2ca7..feeeda0 100644
--- a/faces-chart/values.yaml
+++ b/faces-chart/values.yaml
@@ -39,7 +39,7 @@ smiley:
imagePullPolicy: "" # If not set, uses backend.imagePullPolicy
errorFraction: "" # If not set, uses backend.errorFraction
delayBuckets: "" # If not set, uses backend.delayBuckets
- smiley: "Smiling" # Override if desired
+ smiley: "" # Override if desired
smiley2:
enabled: False # If set to True, enables the second smiley workload
@@ -58,7 +58,7 @@ color:
imagePullPolicy: "" # If not set, uses backend.imagePullPolicy
errorFraction: "" # If not set, uses backend.errorFraction
delayBuckets: "" # If not set, uses backend.delayBuckets
- color: "rgb(151 202 234)" # Override if desired, defaults to colorblind-friendly light blue from the Tol palette
+ color: "" # Override if desired, defaults to colorblind-friendly light blue from the Tol palette
color2:
enabled: False # If set to True, enables the second color workload
@@ -68,4 +68,4 @@ color2:
imagePullPolicy: "" # If not set, uses backend.imagePullPolicy
errorFraction: "" # If not set, uses backend.errorFraction
delayBuckets: "" # If not set, uses backend.delayBuckets
- color: "rgb(55 117 59)" # Override if desired, defaults to colorblind-friendly green from the Tol palette
+ color: "green" # Override if desired, defaults to colorblind-friendly green from the Tol palette
diff --git a/pkg/faces/colorserver.go b/pkg/faces/colorserver.go
index 6b7f613..d59806c 100644
--- a/pkg/faces/colorserver.go
+++ b/pkg/faces/colorserver.go
@@ -48,9 +48,10 @@ func NewColorServer(serverName string) *ColorServer {
func (srv *ColorServer) SetupFromEnvironment() {
srv.BaseServer.SetupFromEnvironment()
- srv.color = utils.StringFromEnv("COLOR", "green")
+ colorName := utils.StringFromEnv("COLOR", "blue")
+ srv.color = Colors.Lookup(colorName)
- fmt.Printf("%s %s: color %s\n", time.Now().Format(time.RFC3339), srv.Name, srv.color)
+ fmt.Printf("%s %s: color %s => %s\n", time.Now().Format(time.RFC3339), srv.Name, colorName, srv.color)
}
func (srv *ColorServer) colorGetHandler(r *http.Request, rstat *BaseRequestStatus) *BaseServerResponse {
@@ -61,7 +62,7 @@ func (srv *ColorServer) colorGetHandler(r *http.Request, rstat *BaseRequestStatu
return &BaseServerResponse{
StatusCode: http.StatusTooManyRequests,
Data: map[string]interface{}{
- "color": Defaults["color-ratelimit"],
+ "color": Colors.Lookup(Defaults["color-ratelimit"]),
"rate": fmt.Sprintf("%.1f RPS", srv.CurrentRate()),
"errors": []string{errstr},
},
diff --git a/pkg/faces/constants.go b/pkg/faces/constants.go
index b55a5b8..a02772f 100644
--- a/pkg/faces/constants.go
+++ b/pkg/faces/constants.go
@@ -17,42 +17,116 @@
package faces
-var Smileys = map[string]string{
- "Smiling": "😃",
- "Sleeping": "😴",
- "Cursing": "🤬",
- "Kaboom": "🤯",
- "HeartEyes": "😍",
- "Neutral": "😐",
- "RollingEyes": "🙄",
- "Screaming": "😱",
+type SmileyMap struct {
+ smileys map[string]string
}
-// colorblind-friendly colors from the Tol palette
-var Colors = map[string]string{
- "grey": "grey",
- "purple": "rgb(48 34 130)",
- "green": "rgb(55 117 59)",
- "cyan": "rgb(55 117 59)", // too similar to pink and grey, avoid
- "blue": "rgb(151 202 234)", // light blue
- "yellow": "rgb(218 204 130)",
- "pink": "rgb(191 108 120)",
- "hotpink": "rgb(158 75 149)",
- "red": "rgb(125 42 83)", // dark magenta
+var Smileys = SmileyMap{
+ smileys: map[string]string{
+ "Grinning": "😃",
+ "Sleeping": "😴",
+ "Cursing": "🤬",
+ "Kaboom": "🤯",
+ "HeartEyes": "😍",
+ "Neutral": "😐",
+ "RollingEyes": "🙄",
+ "Screaming": "😱",
+ "Vomiting": "🤮",
+ },
+}
+
+// Lookup a smiley by name. If found, return the HTML entity for the smiley
+// and true; if not found, return an empty string and false.
+func (sm *SmileyMap) Lookup(name string) (string, bool) {
+ if smiley, ok := sm.smileys[name]; ok {
+ return smiley, true
+ }
+
+ return "", false
+}
+
+type Palette struct {
+ colors map[string]string
+}
+
+// These colors are from the "Bright" color scheme shown in the "Qualitative
+// Color Schemes" section of https://personal.sron.nl/~pault/. The notes about
+// color pairs to avoid are from using https://davidmathlogic.com/colorblind/
+// and from using the Python colorspacious module to compute distances between
+// color pairs.
+//
+// The color names are from https://personal.sron.nl/~pault _except_ that I'm
+// using "darkblue" for 4477AA, and "blue" for 66CCEE, because overall 4477AA
+// turns out to cause more trouble for colorblind folks than 66CCEE.
+//
+// Specific problematic pairs:
+//
+// Protanopia/deuteranopia: darkblue/purple, green/red, grey/red, and maybe
+// blue/grey (the math says it's a problem, looking at davidmathlogic seems
+// like probably not?)
+//
+// Deuteranopia: yellow/red might be a problem, according to the math
+//
+// Tritanopia: this is more rare than the others, but darkblue and green are
+// almost identical to this crowd, and the yellow/grey pair is troubling too.
+//
+// So, by default, Faces uses:
+//
+// blue (66CCEE) for color workload success
+// grey (BBBBBB) for color workload error
+// purple (AA3377) for when the face workload can't talk to the color
+// workload at all
+// red (EE6677) for a color timeout and
+// yellow (CCBB44) for a latched error state
+//
+// and, hopefully, that's a decent compromise.
+
+var Colors = Palette{
+ colors: map[string]string{
+ // Include grey/black/white because they're sometimes convenient.
+ "grey": "#BBBBBB",
+ "black": "#000000",
+ "white": "#FFFFFF",
+
+ // See lots of notes above.
+ "darkblue": "#4477AA",
+ "blue": "#66CCEE",
+ "green": "#228833",
+ "yellow": "#CCBB44",
+ "red": "#EE6677",
+ "purple": "#AA3377",
+ },
+}
+
+func (p *Palette) Lookup(name string) string {
+ if color, ok := p.colors[name]; ok {
+ return color
+ }
+
+ // If the color starts with '#', assume it's a hex color code and
+ // return it as-is.
+
+ if name[0] == '#' {
+ return name
+ }
+
+ // It doesn't look like a hex code and it's not a color code we know,
+ // so just return yellow as a fallback.
+ return p.colors["yellow"]
}
var Defaults = map[string]string{
// Default to grey background, cursing face.
- "color": Colors["grey"],
- "smiley": Smileys["Cursing"],
+ "color": "grey",
+ "smiley": "Cursing",
// 504 errors (GatewayTimeout) from the face workload will get handled in
// the GUI, but from the color & smiley workloads, they should get
- // translated to a pink color and a sleeping face.
- "color-504": Colors["pink"],
- "smiley-504": Smileys["Sleeping"],
+ // translated to a red color and a sleeping face.
+ "color-504": "red",
+ "smiley-504": "Sleeping",
- // Ratelimits are pink with an exploding head.
- "color-ratelimit": Colors["pink"],
- "smiley-ratelimit": Smileys["Kaboom"],
+ // Ratelimits are yellow with an exploding head.
+ "color-ratelimit": "yellow",
+ "smiley-ratelimit": "Kaboom",
}
diff --git a/pkg/faces/faceserver.go b/pkg/faces/faceserver.go
index 01f833e..8980e5f 100644
--- a/pkg/faces/faceserver.go
+++ b/pkg/faces/faceserver.go
@@ -181,13 +181,14 @@ func (srv *FaceServer) faceGetHandler(r *http.Request, rstat *BaseRequestStatus)
errors := []string{}
var smiley string
var color string
+ var smileyOK bool
rateStr := fmt.Sprintf("%.1f RPS", srv.CurrentRate())
if rstat.IsRateLimited() {
errors = append(errors, rstat.Message())
- smiley = Defaults["smiley-ratelimit"]
- color = Defaults["color-ratelimit"]
+ smiley, smileyOK = Smileys.Lookup(Defaults["smiley-ratelimit"])
+ color = Colors.Lookup(Defaults["color-ratelimit"])
} else {
user := r.Header.Get("X-Faces-User")
@@ -218,21 +219,33 @@ func (srv *FaceServer) faceGetHandler(r *http.Request, rstat *BaseRequestStatus)
if smileyResp.statusCode != http.StatusOK {
errors = append(errors, fmt.Sprintf("smiley: %s", smileyResp.data))
- smiley = mapStatus("smiley", smileyResp.statusCode)
+ mapped := mapStatus("smiley", smileyResp.statusCode)
+ smiley, smileyOK = Smileys.Lookup(mapped)
+
+ if srv.debugEnabled {
+ fmt.Printf("%s %s: mapped smiley %d to %s (%s, %v)\n",
+ time.Now().Format(time.RFC3339), srv.Name, smileyResp.statusCode, mapped, smiley, smileyOK)
+ }
} else {
smiley = smileyResp.data
+ smileyOK = true
}
colorResp := <-colorCh
if colorResp.statusCode != http.StatusOK {
errors = append(errors, fmt.Sprintf("color: %s", colorResp.data))
- color = mapStatus("color", colorResp.statusCode)
+ color = Colors.Lookup(mapStatus("color", colorResp.statusCode))
} else {
color = colorResp.data
}
}
+ if !smileyOK {
+ // Something bizarre happened with the smiley lookup?
+ smiley, _ = Smileys.Lookup("Vomiting")
+ }
+
end := time.Now()
latency := end.Sub(start)
diff --git a/pkg/faces/guiserver.go b/pkg/faces/guiserver.go
index 6c927cb..e1dfa03 100644
--- a/pkg/faces/guiserver.go
+++ b/pkg/faces/guiserver.go
@@ -118,7 +118,7 @@ func (srv *GUIServer) guiGetHandler(w http.ResponseWriter, r *http.Request) {
rtext = strings.ReplaceAll(rtext, "%%{hide_key}", fmt.Sprintf("%v", srv.hideKey))
rtext = strings.ReplaceAll(rtext, "%%{show_pods}", fmt.Sprintf("%v", srv.showPods))
rtext = strings.ReplaceAll(rtext, "%%{user}", user)
- rtext = strings.ReplaceAll(rtext, "%%{user_Agent}", userAgent)
+ rtext = strings.ReplaceAll(rtext, "%%{user_agent}", userAgent)
}
} else if strings.HasPrefix(r.URL.Path, "/face/") {
// /face/ is a special case: we forward it to the face workload. This is
diff --git a/pkg/faces/smileyserver.go b/pkg/faces/smileyserver.go
index d8b0aac..3993660 100644
--- a/pkg/faces/smileyserver.go
+++ b/pkg/faces/smileyserver.go
@@ -48,28 +48,35 @@ func NewSmileyServer(serverName string) *SmileyServer {
func (srv *SmileyServer) SetupFromEnvironment() {
srv.BaseServer.SetupFromEnvironment()
- smileyKey := utils.StringFromEnv("SMILEY", "Smiling")
+ smileyKey := utils.StringFromEnv("SMILEY", "Grinning")
- smiley, ok := Smileys[smileyKey]
+ smiley, ok := Smileys.Lookup(smileyKey)
if !ok {
smileyKey = "Neutral"
- smiley = Smileys[smileyKey]
+ smiley, _ = Smileys.Lookup(smileyKey)
}
srv.smiley = smiley
- fmt.Printf("%s %s: smiley %s\n", time.Now().Format(time.RFC3339), srv.Name, smileyKey)
+ fmt.Printf("%s %s: smiley %s (%s)\n", time.Now().Format(time.RFC3339), srv.Name, smileyKey, srv.smiley)
}
func (srv *SmileyServer) smileyGetHandler(r *http.Request, rstat *BaseRequestStatus) *BaseServerResponse {
// The only error we need to handle here is the internal rate limiter.
if rstat.ratelimited {
+ smiley, ok := Smileys.Lookup(Defaults["smiley-ratelimit"])
+
+ if !ok {
+ // This isn't good.
+ smiley, _ = Smileys.Lookup("Vomiting")
+ }
+
errstr := fmt.Sprintf("Rate limited (%.1f RPS > max %.1f RPS)", srv.CurrentRate(), srv.maxRate)
return &BaseServerResponse{
StatusCode: http.StatusTooManyRequests,
Data: map[string]interface{}{
- "smiley": Defaults["smiley-ratelimit"],
+ "smiley": smiley,
"rate": fmt.Sprintf("%.1f RPS", srv.CurrentRate()),
"errors": []string{errstr},
},