├── .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 |
13 | 16 |
17 |
18 | 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------