additional fields in JSON response

This commit is contained in:
Łukasz Budnik
2021-08-24 21:25:44 +02:00
parent 54942ada28
commit f35f940d90
4 changed files with 88 additions and 41 deletions

113
README.md
View File

@@ -2,39 +2,49 @@
yosoy is a HTTP service for stubbing and prototyping distributed applications. It is a service which will introduce itself to the caller and print some useful information about its environment. "Yo soy" in español means "I am". yosoy is a HTTP service for stubbing and prototyping distributed applications. It is a service which will introduce itself to the caller and print some useful information about its environment. "Yo soy" in español means "I am".
yosoy is extremely useful when creating a distributed application stub and you need to see a more meaningful responses than a default nginx welcome page. yosoy is extremely useful when creating a distributed application stub and you need to see more meaningful responses than a default nginx welcome page.
Typical use cases include: Typical use cases include:
* testing HTTP routing & ingress - testing HTTP routing & ingress
* testing HTTP load balancing - testing HTTP load balancing
* testing HTTP caching - testing HTTP caching
* stubbing and prototyping distributed applications - stubbing and prototyping distributed applications
## API ## API
yosoy responds to all requests with a JSON containing the information about: yosoy responds to all requests with a JSON containing the information about:
* HTTP request: - HTTP request:
* Host - Host
* Request URI - Request URI
* Remote IP - Method
* HTTP headers - Scheme
* HTTP proxy headers - Proto
* host: - URL
* Hostname - Remote IP
* How many times it was called - HTTP headers
* Env variables if `YOSOY_SHOW_ENVS` is set to `true`, `yes`, `on`, or `1` - HTTP proxy headers
* Files' contents if `YOSOY_SHOW_FILES` is set to a comma-separated list of (valid) files - host:
- Hostname
- How many times it was called
- Env variables if `YOSOY_SHOW_ENVS` is set to `true`, `yes`, `on`, or `1`
- Files' contents if `YOSOY_SHOW_FILES` is set to a comma-separated list of (valid) files
See [Kubernetes example](#kubernetes-example) below. Checkout out [Sample JSON response](#sample-json-response) below to see how useful yosoy is when troubleshooting/stubbing/prototyping distributed applications.
## Docker image ## Docker image
The docker image is available on docker hub: The docker image is available on docker hub:
```sh
docker pull lukasz/yosoy
``` ```
lukasz/yosoy
and ghcr.io:
```sh
docker pull ghcr.io/lukaszbudnik/yosoy
``` ```
It exposes HTTP service on port 80. It exposes HTTP service on port 80.
@@ -59,40 +69,65 @@ curl -H "Host: gateway.myapp.com" $URL/camarero/abc
curl -H "Host: gateway.myapp.com" $URL/camarero/abc curl -H "Host: gateway.myapp.com" $URL/camarero/abc
``` ```
A sample response looks like this: ## Sample JSON response
A sample yosoy JSON response to a request made from a single page application (SPA) to a backend API deployed in Kubernetes behind nginx ingress and [haproxy-auth-gateway](https://github.com/lukaszbudnik/haproxy-auth-gateway) looks like this:
```json ```json
{ {
"host": "gateway.myapp.com", "host": "api.localtest.me",
"requestUri": "/camarero/abc", "proto": "HTTP/1.1",
"remoteAddr": "172.17.0.1", "method": "GET",
"counter": 4, "scheme": "https",
"requestUri": "/camarero",
"url": "https:///camarero",
"remoteAddr": "192.168.65.3",
"counter": 1,
"headers": { "headers": {
"Accept": [ "Accept": ["*/*"],
"*/*" "Accept-Encoding": ["gzip, deflate, br"],
"Accept-Language": ["en-US,en;q=0.9,pl-PL;q=0.8,pl;q=0.7,es;q=0.6"],
"Authorization": [
"Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJXejFuaDNCWDI4UHMxVEMzSDRoOW52Q1VWRXpjVVBzQms4Z1NmeEp4ZS1JIn0.eyJleHAiOjE2Mjk4MjM3OTMsImlhdCI6MTYyOTgyMjg5MywiYXV0aF90aW1lIjoxNjI5ODIyODkyLCJqdGkiOiI3ZmQzMjkwZi05NjMyLTQ0NzEtYjRjOS1lNTFjZDYwMjllYjgiLCJpc3MiOiJodHRwczovL2F1dGgubG9jYWx0ZXN0Lm1lL2F1dGgvcmVhbG1zL2hvdGVsIiwic3ViIjoiMDdmYzM3YmYtMmJjNy00ZTRmLWE3MDUtYzRjNjgzNTIwYmU1IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoicmVhY3QiLCJub25jZSI6IjQzNDhmMjU5LTliYTYtNDk2ZC04N2I5LWZmZGYzNDMwN2UzOSIsInNlc3Npb25fc3RhdGUiOiJmNTM5OGI3Ny01OTNhLTQ3OWYtOTc5NS00NGIyNGJjMjhkYjQiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vbHVrYXN6YnVkbmlrLmdpdGh1Yi5pbyJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiY2FtYXJlcm8iXX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiJmNTM5OGI3Ny01OTNhLTQ3OWYtOTc5NS00NGIyNGJjMjhkYjQiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJKdWxpbyIsInByZWZlcnJlZF91c2VybmFtZSI6Imp1bGlvIiwiZ2l2ZW5fbmFtZSI6Ikp1bGlvIn0.t5y3L4FzGxM0zwI3fskDI8Kemxz_izcvPPKciSEvNHnZWGQK-9AclGNFz_A9cLFSkpc6l6lBmt7WaC0i04c4h1a9G9AOFImmVXPMPDdTXOQ4aah4CvlN6Fy8ShvSHrQA-wMHEELBpIFsKFx2WP3QHiy27ycr3kqQzW4QymyU7J8tM4-qKR_H1_8aiNOrm5fIED-nEP096V2zvWXiGZX7ts6XE2-annhKphCABLdmIiwgDUnhlAz0hdiDrDbIjzr0ldW4AnUkSQxIZY0PnoEnGVuUvkOYvJpFx10gjORMnRgHSEj9Mk5dtyVGHcihZ5TntCL40WoymNxae6K4-FH3Lw"
], ],
"Origin": ["https://lukaszbudnik.github.io"],
"Referer": ["https://lukaszbudnik.github.io/"],
"Sec-Ch-Ua": [
"\" Not;A Brand\";v=\"99\", \"Google Chrome\";v=\"91\", \"Chromium\";v=\"91\""
],
"Sec-Ch-Ua-Mobile": ["?0"],
"Sec-Fetch-Dest": ["empty"],
"Sec-Fetch-Mode": ["cors"],
"Sec-Fetch-Site": ["cross-site"],
"User-Agent": [ "User-Agent": [
"curl/7.64.1" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"
] ],
"X-Forwarded-For": ["192.168.65.3", "10.1.3.9"],
"X-Forwarded-Host": ["api.localtest.me"],
"X-Forwarded-Port": ["443"],
"X-Forwarded-Proto": ["https"],
"X-Real-Ip": ["192.168.65.3"],
"X-Request-Id": ["48a77564d88ca8a893610b9458bfd885"],
"X-Scheme": ["https"]
}, },
"hostname": "camarero-77787464ff-hjdkq", "hostname": "camarero-cf7c95ccd-cz5lh",
"envVariables": [ "envVariables": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=camarero-77787464ff-hjdkq", "HOSTNAME=camarero-cf7c95ccd-cz5lh",
"YOSOY_SHOW_ENVS=true",
"YOSOY_SHOW_FILES=/etc/podinfo/labels,/etc/podinfo/annotations", "YOSOY_SHOW_FILES=/etc/podinfo/labels,/etc/podinfo/annotations",
"CAMARERO_SERVICE_HOST=10.97.113.33", "YOSOY_SHOW_ENVS=true",
"CAMARERO_PORT=tcp://10.97.113.33:80", "KUBERNETES_SERVICE_PORT=443",
"CAMARERO_PORT_80_TCP=tcp://10.97.113.33:80", "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
"CAMARERO_PORT_80_TCP_ADDR=10.97.113.33", "KUBERNETES_PORT=tcp://10.96.0.1:443",
"CAMARERO_SERVICE_PORT=80", "KUBERNETES_PORT_443_TCP_PORT=443",
"CAMARERO_PORT_80_TCP_PROTO=tcp", "KUBERNETES_SERVICE_HOST=10.96.0.1",
"CAMARERO_PORT_80_TCP_PORT=80", "KUBERNETES_PORT_443_TCP_PROTO=tcp",
"KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
"HOME=/root" "HOME=/root"
], ],
"files": { "files": {
"/etc/podinfo/annotations": "kubernetes.io/config.seen=\"2021-02-03T13:18:34.563751030Z\"\nkubernetes.io/config.source=\"api\"", "/etc/podinfo/annotations": "kubernetes.io/config.seen=\"2021-08-24T15:12:19.555374430Z\"\nkubernetes.io/config.source=\"api\"",
"/etc/podinfo/labels": "app.kubernetes.io/name=\"camarero\"\npod-template-hash=\"77787464ff\"" "/etc/podinfo/labels": "app.kubernetes.io/component=\"api\"\napp.kubernetes.io/name=\"camarero\"\napp.kubernetes.io/part-of=\"hotel\"\napp.kubernetes.io/version=\"0.0.1\"\npod-template-hash=\"cf7c95ccd\""
} }
} }
``` ```

View File

@@ -14,7 +14,11 @@ import (
type response struct { type response struct {
Host string `json:"host"` Host string `json:"host"`
Proto string `json:"proto"`
Method string `json:"method"`
Scheme string `json:"scheme"`
RequestURI string `json:"requestUri"` RequestURI string `json:"requestUri"`
URL string `json:"url"`
RemoteAddr string `json:"remoteAddr"` RemoteAddr string `json:"remoteAddr"`
Counter int `json:"counter"` Counter int `json:"counter"`
Headers map[string][]string `json:"headers"` Headers map[string][]string `json:"headers"`
@@ -52,7 +56,11 @@ func handler(w http.ResponseWriter, req *http.Request) {
response.RequestURI = req.RequestURI response.RequestURI = req.RequestURI
response.Host = req.Host response.Host = req.Host
response.Proto = req.Proto
response.Method = req.Method
response.Scheme = req.URL.Scheme
response.Headers = req.Header response.Headers = req.Header
response.URL = req.URL.String()
response.Hostname = hostname response.Hostname = hostname

View File

@@ -14,7 +14,7 @@ func TestHandler(t *testing.T) {
os.Setenv("YOSOY_SHOW_ENVS", "true") os.Setenv("YOSOY_SHOW_ENVS", "true")
os.Setenv("YOSOY_SHOW_FILES", ".gitignore") os.Setenv("YOSOY_SHOW_FILES", ".gitignore")
req, err := http.NewRequest("GET", "https://example.org/sample/path", nil) req, err := http.NewRequest("GET", "https://example.org/sample/path?one=jeden&two=dwa", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -35,6 +35,10 @@ func TestHandler(t *testing.T) {
// test response // test response
assert.Equal(t, 1, response.Counter) assert.Equal(t, 1, response.Counter)
assert.Equal(t, "example.org", response.Host) assert.Equal(t, "example.org", response.Host)
assert.Equal(t, "GET", response.Method)
assert.Equal(t, "https", response.Scheme)
assert.Equal(t, "HTTP/1.1", response.Proto)
assert.Equal(t, "https://example.org/sample/path?one=jeden&two=dwa", response.URL)
assert.NotEmpty(t, response.EnvVariables) assert.NotEmpty(t, response.EnvVariables)
assert.NotEmpty(t, response.Files[".gitignore"]) assert.NotEmpty(t, response.Files[".gitignore"])

View File

@@ -16,7 +16,7 @@ spec:
spec: spec:
containers: containers:
- name: yosoy - name: yosoy
image: lukasz/yosoy image: lukasz/yosoy:2.0.3
env: env:
- name: YOSOY_SHOW_ENVS - name: YOSOY_SHOW_ENVS
value: "true" value: "true"