Latest messages
22 |-
28 |
29 |
├── .gitignore ├── Dockerfile ├── config.json ├── configmap.yml ├── deployment.yml ├── docker-compose.yml ├── frontend-svc.yml ├── ingress.yml ├── main.go ├── redis-headless.yml ├── redis.yml └── static ├── Dockerfile ├── deployment-static.yml ├── index.html ├── js └── script.js └── static-svc.yml /.gitignore: -------------------------------------------------------------------------------- 1 | go.mod 2 | go.sum -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS build-env 2 | RUN mkdir /go/src/app && apk update && apk add git 3 | ADD main.go go.mod go.sum /go/src/app/ 4 | WORKDIR /go/src/app 5 | RUN go mod download && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o app . 6 | 7 | FROM scratch 8 | WORKDIR /app 9 | COPY --from=build-env /go/src/app/app . 10 | ENTRYPOINT [ "./app" ] -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "RedisHost": "redis" 3 | } -------------------------------------------------------------------------------- /configmap.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: app-config 5 | data: 6 | config.json: |- 7 | { 8 | "RedisHost": "redis-svc" 9 | } -------------------------------------------------------------------------------- /deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: frontend 6 | name: frontend 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: frontend 12 | template: 13 | metadata: 14 | labels: 15 | app: frontend 16 | spec: 17 | containers: 18 | - image: magalixcorp/sample-api:v1 19 | imagePullPolicy: IfNotPresent 20 | name: frontend 21 | env: 22 | - name: REDIS_PASSWORD 23 | valueFrom: 24 | secretKeyRef: 25 | name: redis-password 26 | key: redis-password 27 | volumeMounts: 28 | - name: config-volume 29 | mountPath: /app/config.json 30 | subPath: config.json 31 | volumes: 32 | - name: config-volume 33 | configMap: 34 | name: app-config -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | build: . 5 | ports: 6 | - "3000:3000" 7 | environment: 8 | REDIS_PASSWORD: password123 9 | volumes: 10 | - "$PWD/config.json:/app/config.json" 11 | redis: 12 | image: "bitnami/redis:latest" 13 | environment: 14 | REDIS_PASSWORD: password123 15 | -------------------------------------------------------------------------------- /frontend-svc.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: frontend 6 | name: frontend-svc 7 | spec: 8 | ports: 9 | - port: 3000 10 | protocol: TCP 11 | targetPort: 3000 12 | selector: 13 | app: frontend 14 | type: ClusterIP -------------------------------------------------------------------------------- /ingress.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: frontend-ingress 5 | spec: 6 | rules: 7 | - http: 8 | paths: 9 | - path: /api 10 | backend: 11 | serviceName: frontend-svc 12 | servicePort: 3000 13 | - path: / 14 | backend: 15 | serviceName: static-svc 16 | servicePort: 80 -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/gomodule/redigo/redis" 11 | "github.com/gorilla/handlers" 12 | "github.com/gorilla/mux" 13 | ) 14 | 15 | var redisHost string 16 | var redisPort string 17 | var redisPassword string 18 | 19 | //PostData a struct for holding the the data sent in the request 20 | type PostData struct { 21 | Username string `json:"username"` 22 | Message string `json:"message"` 23 | } 24 | 25 | //Configuration is a struct for holding the application configuration data read from a JSON file 26 | type Configuration struct { 27 | RedisHost string 28 | RedisPort string 29 | } 30 | 31 | func setEnv() { 32 | //filename is the path to the json config file 33 | file, err := os.Open("config.json") 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | decoder := json.NewDecoder(file) 38 | var configuration Configuration 39 | err = decoder.Decode(&configuration) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | if redisHost = os.Getenv("REDIS_HOST"); redisHost == "" { 44 | if redisHost = configuration.RedisHost; redisHost == "" { 45 | redisHost = "localhost" 46 | } 47 | } 48 | if redisPort = os.Getenv("REDIS_PORT"); redisPort == "" { 49 | if redisPort = configuration.RedisPort; redisPort == "" { 50 | redisPort = "6379" 51 | } 52 | } 53 | redisPassword = os.Getenv("REDIS_PASSWORD") 54 | } 55 | 56 | func newServer() http.Handler { 57 | r := mux.NewRouter().StrictSlash(true) 58 | r.Use(commonMiddleware) 59 | r.HandleFunc("/api", handlePost).Methods("POST") 60 | r.HandleFunc("/api", handleQuery).Methods("GET") 61 | return r 62 | } 63 | 64 | func main() { 65 | setEnv() 66 | var router = newServer() 67 | log.Println("Server starting on port 3000") 68 | // log.Fatal("Application is running", http.ListenAndServe(":3000", router)) 69 | log.Fatal(http.ListenAndServe(":3000", handlers.CORS(handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}), handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}), handlers.AllowedOrigins([]string{"*"}))(router))) 70 | } 71 | 72 | func handlePost(w http.ResponseWriter, r *http.Request) { 73 | b, err := ioutil.ReadAll(r.Body) 74 | if err != nil { 75 | log.Println("Error while reading the request body:", err) 76 | } 77 | var newData PostData 78 | json.Unmarshal(b, &newData) 79 | username := newData.Username 80 | message := newData.Message 81 | if err != nil { 82 | log.Println("Error while marhsalling the data:", err) 83 | } 84 | pool := newPool(true) 85 | conn := pool.Get() 86 | defer conn.Close() 87 | err = set(conn, username, message) 88 | if err != nil { 89 | log.Println("Error while saving record to Redis:", err) 90 | } 91 | w.WriteHeader(204) 92 | } 93 | 94 | func handleQuery(w http.ResponseWriter, r *http.Request) { 95 | pool := newPool(false) 96 | conn := pool.Get() 97 | defer conn.Close() 98 | content, _ := get(conn) 99 | json.NewEncoder(w).Encode(content) 100 | } 101 | 102 | func respondWithError(w http.ResponseWriter, msg string, status int) { 103 | w.WriteHeader(status) 104 | json.NewEncoder(w).Encode(map[string]string{"message": msg}) 105 | } 106 | 107 | func newPool(write bool) *redis.Pool { 108 | // We need to set the Redis connection settings for testing functions individually (not passing through main() function) 109 | setEnv() 110 | return &redis.Pool{ 111 | // Maximum number of idle connections in the pool. 112 | MaxIdle: 80, 113 | // max number of connections 114 | MaxActive: 12000, 115 | Dial: func() (redis.Conn, error) { 116 | c, err := redis.Dial("tcp", redisHost+":"+redisPort) 117 | if err != nil { 118 | log.Println("Could not reach Redis", err) 119 | } 120 | _, err = c.Do("AUTH", redisPassword) 121 | if err != nil { 122 | log.Println("Could not authenticate to Redis", err) 123 | } 124 | return c, err 125 | }, 126 | } 127 | } 128 | func set(c redis.Conn, key string, value string) error { 129 | _, err := c.Do("AUTH", redisPassword) 130 | if err != nil { 131 | log.Println("Could not authenticate to Redis", err) 132 | } 133 | _, err = c.Do("SET", key, value) 134 | if err != nil { 135 | return err 136 | } 137 | return nil 138 | } 139 | 140 | // get executes the redis GET command 141 | func get(c redis.Conn) ([]PostData, error) { 142 | results := []PostData{} 143 | _, err := c.Do("AUTH", redisPassword) 144 | if err != nil { 145 | log.Println("Could not authenticate to Redis", err) 146 | } 147 | keys, err := redis.Strings(c.Do("KEYS", "*")) 148 | if err != nil { 149 | return results, err 150 | } 151 | for _, k := range keys { 152 | if v, err := redis.String(c.Do("GET", k)); err == nil { 153 | results = append(results, PostData{k, v}) 154 | } 155 | } 156 | return results, nil 157 | } 158 | 159 | func commonMiddleware(next http.Handler) http.Handler { 160 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 161 | w.Header().Add("Content-Type", "application/json") 162 | next.ServeHTTP(w, r) 163 | }) 164 | } 165 | -------------------------------------------------------------------------------- /redis-headless.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: redis 6 | name: redis-svc 7 | spec: 8 | clusterIP: None 9 | ports: 10 | - port: 6379 11 | selector: 12 | app: redis -------------------------------------------------------------------------------- /redis.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: redis 5 | spec: 6 | serviceName: redis-svc 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: redis 11 | template: 12 | metadata: 13 | labels: 14 | app: redis 15 | spec: 16 | containers: 17 | - name: redis 18 | image: bitnami/redis:latest 19 | env: 20 | - name: REDIS_PASSWORD 21 | valueFrom: 22 | secretKeyRef: 23 | name: redis-password 24 | key: redis-password 25 | ports: 26 | - containerPort: 6379 27 | name: redis-port 28 | volumeMounts: 29 | - name: data 30 | mountPath: /bitnami/redis/data 31 | volumeClaimTemplates: 32 | - metadata: 33 | name: data 34 | spec: 35 | accessModes: ["ReadWriteOnce"] 36 | resources: 37 | requests: 38 | storage: 5Gi -------------------------------------------------------------------------------- /static/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | ADD index.html /usr/share/nginx/html/ 3 | ADD js /usr/share/nginx/html/js -------------------------------------------------------------------------------- /static/deployment-static.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: static 6 | name: static 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: static 12 | template: 13 | metadata: 14 | labels: 15 | app: static 16 | spec: 17 | containers: 18 | - image: magalixcorp/static:v1 19 | imagePullPolicy: IfNotPresent 20 | name: static -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 8 | 11 | 13 |