Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions go-postgres-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# go-postgres-react

This is for creating a Contacts application with the following components:

- [x] a Golang backend
- [x] a Postgresql database for saving data
-- the corresponding Volume for persistent storage
- [] a react frontend app for serving the UI

## Create a volume and deploy postgresql

```console
$ cd github.com/kraftcloud/examples/postgres

github.com/kraftcloud/examples/postgres$ kraft cloud volume create --name postgres --size 200

github.com/kraftcloud/examples/postgres$ kraft cloud deploy --metro fra0 -M 1024 -e POSTGRES_PASSWORD=unikraft -e PGDATA=/volume/postgres -v postgres:/volume -p 5432:5432/tls .

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
github.com/kraftcloud/examples/postgres$ kraft cloud deploy --metro fra0 -M 1024 -e POSTGRES_PASSWORD=unikraft -e PGDATA=/volume/postgres -v postgres:/volume -p 5432:5432/tls .
github.com/kraftcloud/examples/postgres$ kraft cloud deploy -M 1024 --name postgres-go-react -e POSTGRES_PASSWORD=unikraft -e PGDATA=/volume/postgres -v postgres:/volume -p 5432:5432/tls .

(Search for `service group` in the output)

# Port-forward postgresql to localhost
github.com/kraftcloud/examples/postgres$ kraft cloud tunnel <SERVICE_GROUP_FROM_ABOVE> 5432:5432


$ psql -U postgres -h localhost
Password for user postgres: unikraft
psql (16.2)
Type "help" for help.

postgres=# CREATE TABLE contacts (id SERIAL PRIMARY KEY, name TEXT, email TEXT);
CREATE TABLE
postgres=#


```

## Deploy the backend:

```console
github.com/kraftcloud/examples/go-postgres-react/go$ kraft cloud deploy --metro fra0 -p 443:8080 .
(Search for `domain` in the output)

# Ensure that the backend API works fine by talking to the db

# Empty response first
$ curl -vv https://DOMAIN_FROM_ABOVE.fra0.kraft.host/contacts
null

# Create contact next
$ curl -v https://DOMAIN_FROM_ABOVE.fra0.kraft.host/contacts -d '{ "name": "example1", "email": "one@example.com" }'

# Ensure the new contact is saved and retrieved from db via the backend
$ curl https://DOMAIN_FROM_ABOVE.fra0.kraft.host/contacts
[{"name":"example1","email":"one@example.com"}]

# Above backend API confirms data is saved, but let us check in DB also
$ psql -U postgres -h localhost
Password for user postgres: unikraft
psql (16.2)
Type "help" for help.

postgres=# select * from contacts;
id | name | email
----+----------+-----------------
1 | example1 | one@example.com
(1 row)

postgres=#

```
19 changes: 19 additions & 0 deletions go-postgres-react/go/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM --platform=linux/x86_64 golang:1.22.3-bookworm AS build

WORKDIR /src

COPY . /src/

RUN set -xe; \
go build \
-buildmode=pie \
-ldflags "-linkmode external -extldflags -static-pie" \
-tags netgo \
-o /server server.go \
;

FROM scratch

COPY --from=build /server /server
COPY --from=build /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/
COPY --from=build /lib64/ld-linux-x86-64.so.2 /lib64/
7 changes: 7 additions & 0 deletions go-postgres-react/go/Kraftfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
spec: v0.6

runtime: base:latest

rootfs: ./Dockerfile

cmd: ["/server"]
20 changes: 20 additions & 0 deletions go-postgres-react/go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Simple Go 1.21 HTTP Server

This is a simple HTTP server written in the [Go](https://go.dev/) programming language.

To run this example on KraftCloud, first [install the `kraft` CLI tool](https://unikraft.org/docs/cli).
Then clone this examples repository and `cd` into this directory, and invoke:

```console
kraft cloud deploy --metro fra0 -p 443:8080 .
```

The command will build and deploy the `server.go` source code file.

After deploying, you can query the service using the provided URL.

## Learn more

- [Go's Documentation](https://go.dev/doc/)
- [KraftCloud's Documentation](https://docs.kraft.cloud)
- [Building `Dockerfile` Images with `Buildkit`](https://unikraft.org/guides/building-dockerfile-images-with-buildkit)
5 changes: 5 additions & 0 deletions go-postgres-react/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/kraftcloud/examples/go-postgres-react/go

go 1.22.3

require github.com/lib/pq v1.10.9
2 changes: 2 additions & 0 deletions go-postgres-react/go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
74 changes: 74 additions & 0 deletions go-postgres-react/go/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package main

import (
"database/sql"
"encoding/json"
"fmt"
"net/http"

_ "github.com/lib/pq"
)

type Contact struct {
Name string `json:"name"`
Email string `json:"email"`
}

var db *sql.DB

func main() {
var err error
db, err = sql.Open(
"postgres",
"user=postgres dbname=postgres password=unikraft host=postgres-wahr0.internal sslmode=disable",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"user=postgres dbname=postgres password=unikraft host=postgres-wahr0.internal sslmode=disable",
"user=postgres dbname=postgres password=unikraft host=postgres-go-react.internal sslmode=disable",

)
if err != nil {
panic(err)
}

http.HandleFunc("/contacts", contactsHandler)

fmt.Println("Listening on :8080...")
http.ListenAndServe(":8080", nil)
}

func contactsHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
rows, err := db.Query("SELECT name, email FROM contacts")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()

var contacts []Contact
for rows.Next() {
var c Contact
if err := rows.Scan(&c.Name, &c.Email); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
contacts = append(contacts, c)
}

json.NewEncoder(w).Encode(contacts)
case http.MethodPost:
var c Contact
json.NewDecoder(r.Body).Decode(&c)

_, err := db.Exec(
"INSERT INTO contacts(name, email) VALUES($1, $2)",
c.Name,
c.Email,
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

json.NewEncoder(w).Encode(c)
default:
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
}
}