Melkeydev / go-blueprint

Go-blueprint allows users to spin up a quick Go project using a popular framework
https://docs.go-blueprint.dev/
MIT License
5.94k stars 335 forks source link

Feature/Docker Compose for a quick database spin-up #113

Closed Ujstor closed 1 year ago

Ujstor commented 1 year ago

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

Feature

Hi, I've added Docker Compose for the database when the user chooses one of the four drives. There is no way to spin up SQLite with Docker. I created a simple app in Go that creates 'test.db' in the root of the project. For now, this is in my DockerHub. For security reasons, you can't push it to your repository. Here is code:

package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"

    _ "github.com/mattn/go-sqlite3"
)

func main() {
    db, err := sql.Open("sqlite3", "/app/db/test.db")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    _, err = db.Exec(`CREATE TABLE IF NOT EXISTS test_data (id INTEGER PRIMARY KEY, name TEXT);
                      INSERT INTO test_data (name) VALUES ('Sample Data 1'), ('Sample Data 2');`)
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        rows, err := db.Query("SELECT id, name FROM test_data")
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        defer rows.Close()

        for rows.Next() {
            var id int
            var name string
            err = rows.Scan(&id, &name)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            fmt.Fprintf(w, "ID: %d, Name: %s\n", id, name)
        }
    })

    log.Println("Server started on port 6969")
    log.Fatal(http.ListenAndServe(":6969", nil))
}
FROM golang:1.21-alpine

WORKDIR /app

RUN apk add --no-cache gcc musl-dev

ENV CGO_ENABLED=1

COPY . .

RUN go mod init sqlite-test && \
    go get github.com/mattn/go-sqlite3 && \
    go build -o main .

EXPOSE 6969

CMD ["./main"]

Description of Changes:

Checklist

Melkeydev commented 1 year ago

There is no way to spin up SQLite with Docker.

I wonder if this can be explored - I will not approve a PR that is pulling test Docker hubs, that is just a smell.

A workaround is to have a Dockerfile for sqlite, and a docker-compose that runs it. Something like this:

FROM alpine:latest

RUN apk add --no-cache sqlite

RUN mkdir /db
WORKDIR /db

CMD ["sqlite3"]

and in a docker-compose file:

version: '3'

services:
  sqlite:
    build: .
    volumes:
      - ./data:/db
    stdin_open: true
    tty: true
Ujstor commented 1 year ago

Let me know if you agree with this comment above. I made a small hack to ensure everything works as expected. I left an empty SQLite template to maintain the simplicity of the implementation. The empty docker-compose file is deleted at the end if the user chooses SQLite driver. I hope this is okay, if not, I will find another solution.

Ujstor commented 1 year ago

You can ignore the most recent comment. I rewrote the logic and removed the SQLite template entirely.

Melkeydev commented 1 year ago

Can we also include spinning up the docker-compose instance in a make command?

Ujstor commented 1 year ago

I made the requested changes. Now, if a user chooses SQLite, they will receive this message. I messed up the formatting, which is the reason for so many commits.

sqlite
Ujstor commented 1 year ago

Requested changes are ready.

Ujstor commented 1 year ago

@Melkeydev I found a bug, or rather something that's not exactly a bug per se. If a user creates a Postgres db with Docker Compose for the first time and binds the environment variables, everything works perfectly. A volume is automatically created for data persistence. However, when these variables are changed, Postgres will not initialize another database, user, or password.

Log: 'PostgreSQL Database directory appears to contain a database; Skipping initialization.'

Performing a curl /health will break the app, but only if the environment variables are changed on an existing volume. The user needs to delete this volume, or change the volume name in docker-compose.

PostgreSQL doesn't automatically reinitialize the database when it finds existing data in the volume. This behavior is different from other databases. MySQL or MongoDB are more flexible in handling changes in environment variables.

You could force reinitialization of db by clearing the existing volume. This is not recommended as it leads to data loss.