From 7c1bc4d86afa0d496b4e404fa863373eeafad732 Mon Sep 17 00:00:00 2001 From: William Kennedy Date: Sat, 3 Feb 2024 14:15:49 +0000 Subject: [PATCH] have a version working with a database --- app/services/engine/main.go | 2 +- app/tooling/admin/cmd/contract.go | 1 - app/tooling/admin/cmd/deploy.go | 1 - app/tooling/admin/cmd/migrate.go | 52 +++++++++ app/tooling/admin/cmd/root.go | 2 +- app/tooling/admin/cmd/transaction.go | 4 +- app/tooling/admin/cmd/wallet.go | 4 +- business/core/game/stores/gamedb/model.go | 19 ++-- makefile | 67 ++++++++---- zarf/docker/dockerfile.engine | 2 +- zarf/k8s/base/engine/base-engine.yaml | 19 +++- zarf/k8s/dev/database/dev-database.yaml | 122 ++++++++++++++++++++++ zarf/k8s/dev/database/kustomization.yaml | 4 + zarf/k8s/dev/engine/dev-engine-patch.yaml | 14 ++- zarf/k8s/dev/engine/kustomization.yaml | 6 +- zarf/k8s/dev/geth/dev-geth.yaml | 2 +- zarf/k8s/dev/kind-config.yaml | 6 +- 17 files changed, 279 insertions(+), 48 deletions(-) create mode 100644 app/tooling/admin/cmd/migrate.go create mode 100644 zarf/k8s/dev/database/dev-database.yaml create mode 100644 zarf/k8s/dev/database/kustomization.yaml diff --git a/app/services/engine/main.go b/app/services/engine/main.go index ae9119bd..0ffdaada 100644 --- a/app/services/engine/main.go +++ b/app/services/engine/main.go @@ -104,7 +104,7 @@ func run(ctx context.Context, log *logger.Logger) error { DB struct { User string `conf:"default:postgres"` Password string `conf:"default:postgres,mask"` - HostPort string `conf:"default:database-service.sales-system.svc.cluster.local"` + HostPort string `conf:"default:database-service.liars-system.svc.cluster.local"` Name string `conf:"default:postgres"` MaxIdleConns int `conf:"default:2"` MaxOpenConns int `conf:"default:0"` diff --git a/app/tooling/admin/cmd/contract.go b/app/tooling/admin/cmd/contract.go index a47e8d6c..c2671f1f 100644 --- a/app/tooling/admin/cmd/contract.go +++ b/app/tooling/admin/cmd/contract.go @@ -14,7 +14,6 @@ import ( "github.com/ardanlabs/liarsdice/business/core/bank" ) -// contractCmd represents the contract command var contractCmd = &cobra.Command{ Use: "contract", Short: "Manage contract related items", diff --git a/app/tooling/admin/cmd/deploy.go b/app/tooling/admin/cmd/deploy.go index 16751d2b..20a1e025 100644 --- a/app/tooling/admin/cmd/deploy.go +++ b/app/tooling/admin/cmd/deploy.go @@ -14,7 +14,6 @@ import ( "github.com/spf13/cobra" ) -// keyCmd represents the key command var deployCmd = &cobra.Command{ Use: "deploy", Short: "Deploy the bank contract", diff --git a/app/tooling/admin/cmd/migrate.go b/app/tooling/admin/cmd/migrate.go new file mode 100644 index 00000000..6b7f3e0d --- /dev/null +++ b/app/tooling/admin/cmd/migrate.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "context" + "fmt" + "time" + + "github.com/ardanlabs/liarsdice/business/data/migrate" + "github.com/ardanlabs/liarsdice/business/data/sqldb" + "github.com/spf13/cobra" +) + +var migrateCmd = &cobra.Command{ + Use: "migrate", + Short: "Migrate the database", + Long: `Migrates the database to its most current schema.`, + RunE: func(cmd *cobra.Command, args []string) error { + dbConfig := sqldb.Config{ + User: "postgres", + Password: "postgres", + HostPort: "database-service.liars-system.svc.cluster.local", + Name: "postgres", + MaxIdleConns: 2, + MaxOpenConns: 0, + DisableTLS: true, + } + + return performMigrate(dbConfig) + }, +} + +func init() { + rootCmd.AddCommand(migrateCmd) +} + +func performMigrate(cfg sqldb.Config) error { + db, err := sqldb.Open(cfg) + if err != nil { + return fmt.Errorf("connect database: %w", err) + } + defer db.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := migrate.Migrate(ctx, db); err != nil { + return fmt.Errorf("migrate database: %w", err) + } + + fmt.Println("migrations complete") + return nil +} diff --git a/app/tooling/admin/cmd/root.go b/app/tooling/admin/cmd/root.go index b92cac27..fe136776 100644 --- a/app/tooling/admin/cmd/root.go +++ b/app/tooling/admin/cmd/root.go @@ -21,7 +21,7 @@ import ( "github.com/ardanlabs/liarsdice/foundation/web" ) -// rootCmd represents the base command when called without any subcommands +// rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "admin", Short: "A small tool to manage liars dice", diff --git a/app/tooling/admin/cmd/transaction.go b/app/tooling/admin/cmd/transaction.go index d7144e61..34b3695b 100644 --- a/app/tooling/admin/cmd/transaction.go +++ b/app/tooling/admin/cmd/transaction.go @@ -4,16 +4,16 @@ import ( "context" "errors" "fmt" + "time" + "github.com/ardanlabs/ethereum" "github.com/ardanlabs/ethereum/currency" - "time" "github.com/spf13/cobra" "github.com/ethereum/go-ethereum/common" ) -// transactionCmd represents the transaction command var transactionCmd = &cobra.Command{ Use: "transaction", Short: "Examine transaction", diff --git a/app/tooling/admin/cmd/wallet.go b/app/tooling/admin/cmd/wallet.go index cb28b8d1..a73683cf 100644 --- a/app/tooling/admin/cmd/wallet.go +++ b/app/tooling/admin/cmd/wallet.go @@ -3,14 +3,14 @@ package cmd import ( "context" "fmt" + "time" + "github.com/ardanlabs/ethereum" "github.com/ardanlabs/ethereum/currency" "github.com/ethereum/go-ethereum/common" "github.com/spf13/cobra" - "time" ) -// walletCmd represents the wallet command var walletCmd = &cobra.Command{ Use: "wallet", Short: "Show the wallet balance", diff --git a/business/core/game/stores/gamedb/model.go b/business/core/game/stores/gamedb/model.go index 82100a05..509383b6 100644 --- a/business/core/game/stores/gamedb/model.go +++ b/business/core/game/stores/gamedb/model.go @@ -26,12 +26,12 @@ type dbState struct { } type dbCup struct { - ID uuid.UUID `db:"game_id"` - Round int `db:"round"` - Player string `db:"player"` - OrderIdx int `db:"order_idx"` - Outs int `db:"outs"` - Dice any `db:"dice"` + ID uuid.UUID `db:"game_id"` + Round int `db:"round"` + Player string `db:"player"` + OrderIdx int `db:"order_idx"` + Outs int `db:"outs"` + Dice dbarray.Int64 `db:"dice"` } type dbBet struct { @@ -53,13 +53,18 @@ type dbBalance struct { func toDBState(state game.State) dbState { cups := make([]dbCup, 0, len(state.Cups)) for _, cup := range state.Cups { + dice := make([]int64, len(cup.Dice)) + for i, d := range cup.Dice { + dice[i] = int64(d) + } + cups = append(cups, dbCup{ ID: state.GameID, Round: state.Round, Player: cup.Player.String(), OrderIdx: cup.OrderIdx, Outs: cup.Outs, - Dice: dbarray.Array(cup.Dice), + Dice: dice, }) } diff --git a/makefile b/makefile index 6bf8ce16..f16a2746 100644 --- a/makefile +++ b/makefile @@ -9,15 +9,22 @@ # The coinbase address is given a LOT of money to start. # -GOLANG := golang:1.21.6 -NODE := node:16 -ALPINE := alpine:3.19 -CADDY := caddy:2.6-alpine -KIND := kindest/node:v1.29.0@sha256:eaa1450915475849a73a9227b8f201df25e55e268e5d619312131292e324d570 -GETH := ethereum/client-go:stable - -KIND_CLUSTER := liars-game-cluster -VERSION := 1.0 +GOLANG := golang:1.22rc +NODE := node:16 +ALPINE := alpine:3.19 +CADDY := caddy:2.6-alpine +KIND := kindest/node:v1.29.0@sha256:eaa1450915475849a73a9227b8f201df25e55e268e5d619312131292e324d570 +BUSYBOX := busybox:stable +GETH := ethereum/client-go:stable +POSTGRES := postgres:16.1 + +KIND_CLUSTER := liars-game-cluster +NAMESPACE := liars-system +APP := engine +BASE_IMAGE_NAME := ardanlabs/liars +SERVICE_NAME := engine +VERSION := 0.0.1 +SERVICE_IMAGE := $(BASE_IMAGE_NAME)/$(SERVICE_NAME):$(VERSION) # ============================================================================== # Install dependencies @@ -29,6 +36,7 @@ dev-setup: brew list kustomize || brew install kustomize brew list ethereum || brew install ethereum brew list solidity || brew install solidity + brew list pgcli || brew install pgcli dev-docker: docker pull $(GOLANG) @@ -36,7 +44,9 @@ dev-docker: docker pull $(ALPINE) docker pull $(CADDY) docker pull $(KIND) + docker pull $(BUSYBOX) docker pull $(GETH) + docker pull $(POSTGRES) # ============================================================================== # Game UI @@ -52,16 +62,13 @@ game-tuio: # ============================================================================== # Building containers -# -# The new docker buildx build system is required for these docker build commands On systems other than Docker Desktop -# buildx is not the default build system. You will need to enable it with: docker buildx install all: game-engine game-engine: docker build \ -f zarf/docker/dockerfile.engine \ - -t liarsdice-game-engine:$(VERSION) \ + -t $(SERVICE_IMAGE) \ --build-arg BUILD_REF=$(VERSION) \ --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ . @@ -72,7 +79,6 @@ game-engine: # To start the system for the first time, run these two commands: # make dev-up # make dev-update-apply -# Expect the building of the FE to take a wee bit of time :( dev-up: kind create cluster \ @@ -81,14 +87,16 @@ dev-up: --config zarf/k8s/dev/kind-config.yaml kubectl wait --timeout=120s --namespace=local-path-storage --for=condition=Available deployment/local-path-provisioner + kind load docker-image $(BUSYBOX) --name $(KIND_CLUSTER) kind load docker-image $(GETH) --name $(KIND_CLUSTER) + kind load docker-image $(POSTGRES) --name $(KIND_CLUSTER) dev-down: kind delete cluster --name $(KIND_CLUSTER) rm -f /tmp/credentials.json dev-load: - kind load docker-image liarsdice-game-engine:$(VERSION) --name $(KIND_CLUSTER) + kind load docker-image $(SERVICE_IMAGE) --name $(KIND_CLUSTER) dev-deploy: @zarf/k8s/dev/geth/setup-contract-k8s @@ -98,14 +106,17 @@ dev-deploy-force: dev-apply: go build -o admin app/tooling/admin/main.go - + kustomize build zarf/k8s/dev/geth | kubectl apply -f - - kubectl wait --timeout=120s --namespace=liars-system --for=condition=Available deployment/geth + kubectl wait --timeout=120s --namespace=$(NAMESPACE) --for=condition=Available deployment/geth @zarf/k8s/dev/geth/setup-contract-k8s.sh + kustomize build zarf/k8s/dev/database | kubectl apply -f - + kubectl rollout status --namespace=$(NAMESPACE) --watch --timeout=120s sts/database + kustomize build zarf/k8s/dev/engine | kubectl apply -f - - kubectl wait --timeout=120s --namespace=liars-system --for=condition=Available deployment/engine + kubectl wait --timeout=120s --namespace=$(NAMESPACE) --for=condition=Available deployment/engine dev-restart: kubectl rollout restart deployment engine --namespace=liars-system @@ -114,11 +125,14 @@ dev-update: all dev-load dev-restart dev-update-apply: all dev-load dev-apply +dev-logs-init: + kubectl logs --namespace=$(NAMESPACE) -l app=$(APP) -f --tail=100 -c init-ge-migrate + dev-logs: - kubectl logs --namespace=liars-system -l app=engine --all-containers=true -f --tail=100 | go run app/tooling/logfmt/main.go + kubectl logs --namespace=$(NAMESPACE) -l app=$(APP) --all-containers=true -f --tail=100 | go run app/tooling/logfmt/main.go dev-logs-geth: - kubectl logs --namespace=liars-system -l app=geth --all-containers=true -f --tail=1000 + kubectl logs --namespace=$(NAMESPACE) -l app=geth --all-containers=true -f --tail=1000 dev-status: kubectl get nodes -o wide @@ -130,10 +144,19 @@ dev-describe: kubectl describe svc dev-describe-deployment-engine: - kubectl describe deployment --namespace=liars-system engine + kubectl describe deployment --namespace=$(NAMESPACE) $(APP) dev-describe-engine: - kubectl describe pod --namespace=liars-system -l app=engine + kubectl describe pod --namespace=$(NAMESPACE) -l app=$(APP) + +# ============================================================================== +# Administration + +migrate: + export SALES_DB_HOST=localhost; go run app/tooling/sales-admin/main.go migrate + +pgcli: + pgcli postgresql://postgres:postgres@localhost # ============================================================================== # Running tests within the local computer diff --git a/zarf/docker/dockerfile.engine b/zarf/docker/dockerfile.engine index 32405b7f..a3082d09 100644 --- a/zarf/docker/dockerfile.engine +++ b/zarf/docker/dockerfile.engine @@ -1,5 +1,5 @@ # Build the Go Binary. -FROM golang:1.21.6 as build_engine +FROM golang:1.22rc2 as build_engine ENV CGO_ENABLED 0 ARG BUILD_REF diff --git a/zarf/k8s/base/engine/base-engine.yaml b/zarf/k8s/base/engine/base-engine.yaml index 8ed2bcb3..f258a8fd 100644 --- a/zarf/k8s/base/engine/base-engine.yaml +++ b/zarf/k8s/base/engine/base-engine.yaml @@ -8,24 +8,35 @@ kind: Deployment metadata: name: engine namespace: liars-system + spec: selector: matchLabels: app: engine + template: metadata: labels: app: engine + spec: terminationGracePeriodSeconds: 60 + + initContainers: + - name: init-ge-migrate + image: engine-image + command: ['./admin', 'migrate'] + containers: - name: game-engine - image: game-engine-image + image: engine-image + ports: - name: game-engine containerPort: 3000 - - name: ge-debug + - name: game-engine-dbg containerPort: 4000 + readinessProbe: httpGet: path: /v1/readiness @@ -35,6 +46,7 @@ spec: timeoutSeconds: 5 successThreshold: 1 failureThreshold: 2 + livenessProbe: httpGet: path: /v1/liveness @@ -44,6 +56,7 @@ spec: timeoutSeconds: 5 successThreshold: 1 failureThreshold: 2 + env: - name: GAME_CONTRACT_ID valueFrom: @@ -66,7 +79,9 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + --- + apiVersion: v1 kind: Service metadata: diff --git a/zarf/k8s/dev/database/dev-database.yaml b/zarf/k8s/dev/database/dev-database.yaml new file mode 100644 index 00000000..9a14bccb --- /dev/null +++ b/zarf/k8s/dev/database/dev-database.yaml @@ -0,0 +1,122 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: liars-system +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: database-data + namespace: liars-system +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: database + namespace: liars-system +spec: + selector: + matchLabels: + app: database + serviceName: database-service + replicas: 1 + template: + metadata: + labels: + app: database + spec: + dnsPolicy: ClusterFirstWithHostNet + hostNetwork: true + terminationGracePeriodSeconds: 60 + volumes: + - name: data + persistentVolumeClaim: + claimName: database-data + - name: config + configMap: + name: pghbaconf + #defaultMode: 0400 + items: + - key: "pg_hba.conf" + path: "pg_hba.conf" + containers: + - name: postgres + image: postgres:16.1 + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + - name: config + readOnly: false + mountPath: "/etc/pg_hba.conf" + subPath: "pg_hba.conf" + args: ['-c', 'hba_file=/etc/pg_hba.conf'] + resources: + requests: + cpu: 100m + limits: + cpu: 3750m + env: + - name: POSTGRES_PASSWORD + value: postgres + ports: + - name: postgres + containerPort: 5432 + livenessProbe: + exec: + command: + - pg_isready + - -h + - localhost + - -U + - postgres + initialDelaySeconds: 30 + timeoutSeconds: 5 + readinessProbe: + exec: + command: + - pg_isready + - -h + - localhost + - -U + - postgres + initialDelaySeconds: 5 + timeoutSeconds: 1 +--- +apiVersion: v1 +kind: Service +metadata: + name: database-service + namespace: liars-system +spec: + type: ClusterIP + selector: + app: database + ports: + - name: postgres + port: 5432 + targetPort: postgres +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: pghbaconf + namespace: liars-system +data: + # file-like keys + pg_hba.conf: | + local all all trust + # IPv4 local connections: + host all all 0.0.0.0/0 trust + # IPv6 local connections: + host all all ::1/128 trust + # Allow replication connections from localhost, by a user with the + # replication privilege. + local replication all trust + host replication all 0.0.0.0/0 trust + host replication all ::1/128 trust diff --git a/zarf/k8s/dev/database/kustomization.yaml b/zarf/k8s/dev/database/kustomization.yaml new file mode 100644 index 00000000..71f662f4 --- /dev/null +++ b/zarf/k8s/dev/database/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ./dev-database.yaml diff --git a/zarf/k8s/dev/engine/dev-engine-patch.yaml b/zarf/k8s/dev/engine/dev-engine-patch.yaml index 04534507..591e20a5 100644 --- a/zarf/k8s/dev/engine/dev-engine-patch.yaml +++ b/zarf/k8s/dev/engine/dev-engine-patch.yaml @@ -3,20 +3,25 @@ kind: Deployment metadata: name: engine namespace: liars-system + spec: selector: matchLabels: app: engine + replicas: 1 strategy: type: Recreate + template: metadata: labels: app: engine + spec: dnsPolicy: ClusterFirstWithHostNet hostNetwork: true + containers: - name: game-engine resources: @@ -24,20 +29,25 @@ spec: cpu: "2000m" # Up to 2 full cores requests: cpu: "1000m" # Use 1 full cores + --- + apiVersion: v1 kind: Service metadata: name: engine-service namespace: liars-system + spec: selector: app: engine + type: ClusterIP + ports: - name: game-engine port: 3000 targetPort: game-engine - - name: game-engine-debug + - name: game-engine-dbg port: 4000 - targetPort: ge-debug + targetPort: game-engine-dbg diff --git a/zarf/k8s/dev/engine/kustomization.yaml b/zarf/k8s/dev/engine/kustomization.yaml index cde600b3..5e29cac8 100644 --- a/zarf/k8s/dev/engine/kustomization.yaml +++ b/zarf/k8s/dev/engine/kustomization.yaml @@ -5,6 +5,6 @@ resources: patches: - path: ./dev-engine-patch.yaml images: - - name: game-engine-image - newName: liarsdice-game-engine - newTag: "1.0" + - name: engine-image + newName: ardanlabs/liars/engine + newTag: 0.0.1 diff --git a/zarf/k8s/dev/geth/dev-geth.yaml b/zarf/k8s/dev/geth/dev-geth.yaml index 58ae8ada..e5a9cc29 100644 --- a/zarf/k8s/dev/geth/dev-geth.yaml +++ b/zarf/k8s/dev/geth/dev-geth.yaml @@ -48,7 +48,7 @@ spec: terminationGracePeriodSeconds: 60 initContainers: - name: copy-keystore - image: busybox + image: busybox:stable command: ["sh", "-c", "mkdir -p /ethereum/keystore; cp /secrets/keystore/* /ethereum/keystore"] volumeMounts: - name: vol-ethereum diff --git a/zarf/k8s/dev/kind-config.yaml b/zarf/k8s/dev/kind-config.yaml index aac4fa93..7a5bb90a 100644 --- a/zarf/k8s/dev/kind-config.yaml +++ b/zarf/k8s/dev/kind-config.yaml @@ -5,9 +5,11 @@ nodes: extraPortMappings: - containerPort: 80 hostPort: 80 - - containerPort: 8545 - hostPort: 8545 - containerPort: 3000 hostPort: 3000 - containerPort: 4000 hostPort: 4000 + - containerPort: 8545 + hostPort: 8545 + - containerPort: 5432 + hostPort: 5432 \ No newline at end of file