diff --git a/README.md b/README.md index 419bdec..015b11e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,117 @@ # yosoy -Yo soy is a tool for stubbing distributed applications. + +yosoy is a HTTP service for stubbing 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 will provide information like (see example below): + +* Request URI +* 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` +* Files contents if `YOSOY_SHOW_FILES` is set to a comma-separated list of (valid) files + +## Docker image + +The docker image is available on docker hub: + +``` +lukasz/yosoy +``` + +It exposes HTTP service on port 80. + +## Kubernetes example + +Let's take a look at a sample Kubernetes deployment file. It uses both `YOSOY_SHOW_ENVS` and `YOSOY_SHOW_FILES`. To illustrate `YOSOY_SHOW_FILES` functionality it uses Kubernetes Downward API to expose labels and annotations as volume files which are then read by yosoy. + +``` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: camarero + labels: + app.kubernetes.io/name: camarero +spec: + replicas: 2 + selector: + matchLabels: + app.kubernetes.io/name: camarero + template: + metadata: + labels: + app.kubernetes.io/name: camarero + spec: + containers: + - name: yosoy + image: lukasz/yosoy + env: + - name: YOSOY_SHOW_ENVS + value: "true" + - name: YOSOY_SHOW_FILES + value: "/etc/podinfo/labels,/etc/podinfo/annotations" + ports: + - containerPort: 80 + volumeMounts: + - name: podinfo + mountPath: /etc/podinfo + volumes: + - name: podinfo + downwardAPI: + items: + - path: "labels" + fieldRef: + fieldPath: metadata.labels + - path: "annotations" + fieldRef: + fieldPath: metadata.annotations +``` + +Execute curl a couple of times and you should see: + +``` +Request URI: / +Hostname: camarero-7fd97dc5f5-545mt +Remote IP: 172.18.0.1 +Called: 4 + +HTTP headers: +User-Agent: curl/7.58.0 +Accept: */* + +Env variables: +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +HOSTNAME=camarero-7fd97dc5f5-545mt +YOSOY_SHOW_ENVS=true +YOSOY_SHOW_FILES=/etc/podinfo/labels,/etc/podinfo/annotations +KUBERNETES_PORT=tcp://10.96.0.1:443 +KUBERNETES_PORT_443_TCP_PROTO=tcp +CAMARERO_SERVICE_HOST=10.96.183.207 +KUBERNETES_SERVICE_HOST=10.96.0.1 +KUBERNETES_SERVICE_PORT=443 +KUBERNETES_SERVICE_PORT_HTTPS=443 +CAMARERO_SERVICE_PORT=80 +CAMARERO_PORT_80_TCP_PROTO=tcp +KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 +CAMARERO_PORT_80_TCP=tcp://10.96.183.207:80 +CAMARERO_PORT_80_TCP_PORT=80 +KUBERNETES_PORT_443_TCP_PORT=443 +KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 +CAMARERO_PORT=tcp://10.96.183.207:80 +CAMARERO_PORT_80_TCP_ADDR=10.96.183.207 +HOME=/root + +File /etc/podinfo/labels: +app.kubernetes.io/component="api" +app.kubernetes.io/name="camarero" +app.kubernetes.io/part-of="camarero" +app.kubernetes.io/version="0.0.1" +pod-template-hash="7fd97dc5f5" + +File /etc/podinfo/annotations: +kubernetes.io/config.seen="2020-11-13T14:36:17.234235115Z" +kubernetes.io/config.source="api" +``` diff --git a/server.go b/server.go index 74fc538..3066b6e 100644 --- a/server.go +++ b/server.go @@ -15,12 +15,17 @@ var showEnvs = os.Getenv("YOSOY_SHOW_ENVS") var showFiles = os.Getenv("YOSOY_SHOW_FILES") func handler(w http.ResponseWriter, req *http.Request) { - fmt.Printf("[%v] - %v - %v - \"%v %v\"\n", hostname, time.Now().Format(time.RFC3339), req.RemoteAddr, req.Method, req.RequestURI) + remoteAddr := req.RemoteAddr + // LastIndex works better with IPv6 + if index := strings.LastIndex(remoteAddr, ":"); index > 0 { + remoteAddr = remoteAddr[0:index] + } + fmt.Printf("[%v] - %v - %v - \"%v %v\"\n", hostname, time.Now().Format(time.RFC3339), remoteAddr, req.Method, req.RequestURI) w.WriteHeader(200) w.Header().Add("Content-Type", "text/plain") fmt.Fprintf(w, "Request URI: %v\n", req.RequestURI) fmt.Fprintf(w, "Hostname: %v\n", hostname) - fmt.Fprintf(w, "Remote IP: %v\n", req.RemoteAddr) + fmt.Fprintf(w, "Remote IP: %v\n", remoteAddr) counter++ fmt.Fprintf(w, "Called: %v\n", counter) fmt.Fprintln(w) @@ -30,7 +35,7 @@ func handler(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "%v: %v\n", name, h) } } - if showEnvs == "1" || strings.ToLower(showEnvs) == "yes" || strings.ToLower(showEnvs) == "true" { + if strings.ToLower(showEnvs) == "true" || strings.ToLower(showEnvs) == "yes" || strings.ToLower(showEnvs) == "on" || showEnvs == "1" { fmt.Fprintln(w) fmt.Fprintf(w, "Env variables:\n") for _, e := range os.Environ() { @@ -54,7 +59,7 @@ func handler(w http.ResponseWriter, req *http.Request) { } func main() { - fmt.Printf("[%v] - %v - YoSoy is up!\n", hostname, time.Now().Format(time.RFC3339)) + fmt.Printf("[%v] - %v - yosoy is up!\n", hostname, time.Now().Format(time.RFC3339)) http.HandleFunc("/", handler) http.ListenAndServe(":80", nil) }