Skip to content

Commit

Permalink
feat: initial app and instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
astromechza committed May 23, 2024
1 parent 1a79c1a commit 7267082
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 1 deletion.
66 changes: 66 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Docker

on:
push:
branches: [ "main" ]
paths-ignore:
- README.md
- score.yaml

env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}

jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0

# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@

# Go workspace file
go.work

# Score
.score-compose/
compose.yaml
.score-k8s/
manifests.yaml
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM golang:1.22-alpine AS builder

WORKDIR /app
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOWORK=off
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
RUN --mount=target=. \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /sample .

FROM gcr.io/distroless/static as final
COPY --from=builder /sample .
ENTRYPOINT ["/sample"]
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,43 @@
# sample-app-gif
The sample app used for the gif and video embedded in the docs

The sample app used for the gif and video embedded in the docs. The purpose is for it to be complex enough to show the core value propositions of Score:

- Convert to more than one deployment format
- Provision ingress and database resources with dynamic credentials
- Launch locally or remotely
- Show evidence that the request was routed correctly and hit the target database

All with as few lines of code as possible. We're going to for simple and short rather than _correct_.

## How to record the sample gif

Use <https://docs.asciinema.org/>.

Preparation:

```
$ rm -rfv .score-compose .score-k8s compose.yaml manifests.yaml
$ docker pull ghcr.io/score-spec/sample-app-gif:main
$ kind create cluster
$ kubectl use-context kind-kind
$ kubectl --context kind-kind apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yam
$ helm --kube-context kind-kind install ngf oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric --create-namespace -n nginx-gateway --set service.type=ClusterIP
```

Instructions to record:

```
$ score-compose init
$ score-compose generate score.yaml
$ docker compose up -d
$ curl http://$(score-compose resources get-outputs 'dns.default#sample.dns' --format '{{ .host }}'):8080/ -i
$ docker logs sample-app-gif-sample-main-1
$ docker compose down -v
$ score-k8s init
$ score-k8s generate score.yaml
$ kubectl apply -f manifests.yaml
$ kubectl wait deployments/sample --for=condition=Ready
$ curl http://$(score-compose resources get-outputs 'dns.default#sample.dns' --format '{{ .host }}')
```
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/score-spec/sample-app-gif

go 1.22.0

require github.com/lib/pq v1.10.9
2 changes: 2 additions & 0 deletions 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=
50 changes: 50 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

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

_ "github.com/lib/pq"
)

const sqlConnectionStringVar = "PG_CONNECTION_STRING"
const listenAddress = ":8080"

func main() {
// First, get the connection string and attempt a connection or fail
connStr := os.Getenv(sqlConnectionStringVar)
conn, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatalf("failed to open the database %s=%s: %v", sqlConnectionStringVar, connStr, err)
}
defer conn.Close()

// Second, wait until we get a successful connection to the db
if err := conn.PingContext(context.TODO()); err != nil {
log.Fatalf("failed to connect to database %s=%s: %v", sqlConnectionStringVar, connStr, err)
}
log.Printf("successfully connected to database %s=%s", sqlConnectionStringVar, connStr)

// Third, link the handler function
http.HandleFunc("GET /{$}", func(writer http.ResponseWriter, request *http.Request) {
var versionOutput string
if err := conn.QueryRowContext(request.Context(), "SELECT version()").Scan(&versionOutput); err != nil {
writer.WriteHeader(http.StatusInternalServerError)
_, _ = fmt.Fprintf(writer, "Failed to get sql version: %v", err)
log.Printf("%s %s %s status=500: %v", request.Host, request.Method, request.RequestURI, err)
} else {
_, _ = fmt.Fprintf(writer, "SQL VERSION: %s", versionOutput)
log.Printf("%s %s %s status=200", request.Host, request.Method, request.RequestURI)
}
})

// Finally, run the http server
log.Print("starting server")
if err := http.ListenAndServe(listenAddress, http.DefaultServeMux); err != nil {
log.Fatal(err.Error())
}
}
26 changes: 26 additions & 0 deletions score.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: score.dev/v1b1
metadata:
name: sample

containers:
main:
image: ghcr.io/score-spec/sample-app-gif:main
variables:
PG_CONNECTION_STRING: "postgresql://${resources.db.username}:${resources.db.password}@${resources.db.host}:${resources.db.port}/${resources.db.database}?sslmode=disable"

service:
ports:
web:
port: 8080

resources:
db:
type: postgres
dns:
type: dns
route:
type: route
params:
host: ${resources.dns.host}
path: /
port: 8080

0 comments on commit 7267082

Please sign in to comment.