├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── api
├── .gitignore
├── Dockerfile
├── main.go
└── sanity_test.go
├── cover.jpg
├── docker-compose.yaml
└── web
├── Dockerfile
└── index.html
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.md]
11 | max_line_length = off
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, build with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.out
12 |
13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14 | .glide/
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Tin Rabzelj
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # How to Keep Your Development Environment Clean
2 |
3 |
4 |
5 |
6 |
7 | This is the underlying code for [How to Keep Your Development Environment Clean](https://outcrawl.com/clean-development-environment-docker/) article.
8 |
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | tmp
2 |
--------------------------------------------------------------------------------
/api/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.12.1
2 |
3 | RUN adduser --disabled-password --gecos '' api
4 | USER api
5 |
6 | WORKDIR /go/src/app
7 | COPY . .
8 |
9 | RUN go get github.com/pilu/fresh
10 | RUN go get ./...
11 |
12 | CMD [ "fresh" ]
13 |
--------------------------------------------------------------------------------
/api/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "log"
7 | "net/http"
8 | "os"
9 | "time"
10 |
11 | "github.com/rs/cors"
12 |
13 | "github.com/gorilla/mux"
14 | "gopkg.in/mgo.v2"
15 | )
16 |
17 | type Post struct {
18 | Text string `json:"text" bson:"text"`
19 | CreatedAt time.Time `json:"createdAt" bson:"created_at"`
20 | }
21 |
22 | var posts *mgo.Collection
23 |
24 | func main() {
25 | // Connect to mongo
26 | session, err := mgo.Dial("mongo:27017")
27 | if err != nil {
28 | log.Fatalln(err)
29 | log.Fatalln("mongo err")
30 | os.Exit(1)
31 | }
32 | defer session.Close()
33 | session.SetMode(mgo.Monotonic, true)
34 |
35 | // Get posts collection
36 | posts = session.DB("app").C("posts")
37 |
38 | // Set up routes
39 | r := mux.NewRouter()
40 | r.HandleFunc("/posts", createPost).
41 | Methods("POST")
42 | r.HandleFunc("/posts", readPosts).
43 | Methods("GET")
44 |
45 | http.ListenAndServe(":8080", cors.AllowAll().Handler(r))
46 | log.Println("Listening on port 8080...")
47 | }
48 |
49 | func createPost(w http.ResponseWriter, r *http.Request) {
50 | // Read body
51 | data, err := ioutil.ReadAll(r.Body)
52 | if err != nil {
53 | responseError(w, err.Error(), http.StatusBadRequest)
54 | return
55 | }
56 |
57 | // Read post
58 | post := &Post{}
59 | err = json.Unmarshal(data, post)
60 | if err != nil {
61 | responseError(w, err.Error(), http.StatusBadRequest)
62 | return
63 | }
64 | post.CreatedAt = time.Now().UTC()
65 |
66 | // Insert new post
67 | if err := posts.Insert(post); err != nil {
68 | responseError(w, err.Error(), http.StatusInternalServerError)
69 | return
70 | }
71 |
72 | responseJSON(w, post)
73 | }
74 |
75 | func readPosts(w http.ResponseWriter, r *http.Request) {
76 | result := []Post{}
77 | if err := posts.Find(nil).Sort("-created_at").All(&result); err != nil {
78 | responseError(w, err.Error(), http.StatusInternalServerError)
79 | } else {
80 | responseJSON(w, result)
81 | }
82 | }
83 |
84 | func responseError(w http.ResponseWriter, message string, code int) {
85 | w.Header().Set("Content-Type", "application/json")
86 | w.WriteHeader(code)
87 | json.NewEncoder(w).Encode(map[string]string{"error": message})
88 | }
89 |
90 | func responseJSON(w http.ResponseWriter, data interface{}) {
91 | w.Header().Set("Content-Type", "application/json")
92 | json.NewEncoder(w).Encode(data)
93 | }
94 |
--------------------------------------------------------------------------------
/api/sanity_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func TestSanity(t *testing.T) {
6 | t.Log("Has sanity")
7 | }
8 |
--------------------------------------------------------------------------------
/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinrab/go-mongo-docker-example/eae04464104e8685bbb3cc9c3861f9c07537f3a3/cover.jpg
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 | services:
3 | api:
4 | container_name: 'api'
5 | build: './api'
6 | ports:
7 | - '8080:8080'
8 | volumes:
9 | - './api:/go/src/app'
10 | depends_on:
11 | - 'mongo'
12 | web:
13 | container_name: 'web'
14 | image: 'nginx:latest'
15 | ports:
16 | - '8081:80'
17 | volumes:
18 | - './web:/usr/share/nginx/html'
19 | depends_on:
20 | - 'api'
21 | mongo:
22 | image: 'mongo:latest'
23 | container_name: 'mongo'
24 | ports:
25 | - '27100:27017'
26 |
--------------------------------------------------------------------------------
/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 |
3 | RUN adduser --disabled-password --gecos '' web
4 | USER web
5 |
6 | COPY . /usr/share/nginx/html
7 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dockerized Posts
7 |
8 |
9 |
10 |
11 | Dockerized Posts
12 |
17 |
18 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------