Skip to content

Commit

Permalink
Merge pull request #2 from evg555/week2
Browse files Browse the repository at this point in the history
week2
  • Loading branch information
evg555 committed Oct 14, 2023
2 parents b0593e6 + e4f4d2d commit 3cf75c9
Show file tree
Hide file tree
Showing 13 changed files with 502 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
POSTGRES_DB=auth
POSTGRES_USER=auth
POSTGRES_PASSWORD=auth
POSTGRES_PORT=5432
MIGRATION_DIR=./migrations
69 changes: 47 additions & 22 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ on:
pull_request:
branches: [ main, master ]

env:
REGISTRY: "cr.selcloud.ru/ontropos42"
IMAGE_NAME: "grpc-server"
CONTAINER_NAME: "grpc-server-container"

jobs:
build-and-test:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -36,32 +41,52 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Require: The version of golangci-lint to use.
# When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version.
# When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit.
version: v1.54
args: --timeout=30m --config=./.golangci.pipeline.yaml --issues-exit-code=0

# Optional: working directory, useful for monorepos
# working-directory: somedir
image-build-and-push:
runs-on: ubuntu-latest

# Optional: golangci-lint command line arguments.
#
# Note: By default, the `.golangci.yml` file should be at the root of the repository.
# The location of the configuration file can be changed by using `--config=`
args: --timeout=30m --config=./.golangci.pipeline.yaml --issues-exit-code=0
steps:
- name: Checkout master
uses: actions/checkout@v3

# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

# Optional: if set to true, then all caching functionality will be completely disabled,
# takes precedence over all other caching options.
# skip-cache: true
- name: Login to Docker Registry
run: docker login -u ${{ secrets.REGISTRY_USERNAME }} -p ${{ secrets.REGISTRY_PASSWORD }} $REGISTRY

# Optional: if set to true, then the action won't cache or restore ~/go/pkg.
# skip-pkg-cache: true
- name: Build and Push Docker Image
run: |
TAG_NAME=$(echo $GITHUB_SHA | head -c7)
docker buildx create --use
docker buildx build --no-cache --push --tag $REGISTRY/$IMAGE_NAME:$TAG_NAME -f docker/app/Dockerfile .
# Optional: if set to true, then the action won't cache or restore ~/.cache/go-build.
# skip-build-cache: true
deploy-image:
runs-on: ubuntu-latest
needs: image-build-and-push

# Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'.
# install-mode: "goinstall"
steps:
- name: Deploy to Selectel Cloud via SSH action
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
envs: IMAGE_NAME,REGISTRY,GITHUB_SHA,CONTAINER_NAME
script: |
# Set up variables
TAG_NAME=$(echo $GITHUB_SHA | head -c7)
# Login into Selectel Registry
docker login -u ${{ secrets.REGISTRY_USERNAME }} -p ${{ secrets.REGISTRY_PASSWORD }} $REGISTRY
# Stop running container
docker stop $CONTAINER_NAME
# Remove old container
docker rm $CONTAINER_NAME
# Run a new container from a new image
docker run -d -p 8000:8000 --name $CONTAINER_NAME -t $REGISTRY/$IMAGE_NAME:$TAG_NAME
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
/bin/
docker/postgres/data
21 changes: 17 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
include .env

LOCAL_BIN:=$(CURDIR)/bin

LOCAL_MIGRATION_DIR=$(MIGRATION_DIR)
LOCAL_MIGRATION_DSN="host=localhost port=$(POSTGRES_PORT) dbname=$(POSTGRES_DB) user=$(POSTGRES_USER) password=$(POSTGRES_PASSWORD) sslmode=disable"

install-golangci-lint:
GOBIN=$(LOCAL_BIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54
GOBIN=${LOCAL_BIN} go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54

install-deps:
GOBIN=$(LOCAL_BIN) go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
GOBIN=$(LOCAL_BIN) go install -mod=mod google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
GOBIN=${LOCAL_BIN} go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
GOBIN=${LOCAL_BIN} go install -mod=mod google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

generate:
make generate-note-api
Expand All @@ -20,4 +25,12 @@ generate-user-api:
api/user_v1/user.proto

lint:
./bin/golangci-lint run ./... --config .golangci.pipeline.yaml
./bin/golangci-lint run ./... --config .golangci.pipeline.yaml

build:
GOOS=linux GOARCH=amd64 go build -o ./bin/grpc-server ./cmd/grpc-server/main.go

docker-build-and-push:
docker buildx build --no-cache --platform linux/amd64 -t cr.selcloud.ru/ontropos42/test-server:v0.0.1 .
docker login -u token -p CRgAAAAAT7M2IVc1bUBai6HdzbxITsRZGKhct7XO cr.selcloud.ru/ontropos42
docker push cr.selcloud.ru/ontropos42/test-server:v0.0.1
141 changes: 123 additions & 18 deletions cmd/grpc-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,49 @@ package main

import (
"context"
"database/sql"
"fmt"
"google.golang.org/protobuf/types/known/timestamppb"

Check failure on line 7 in cmd/grpc-server/main.go

View workflow job for this annotation

GitHub Actions / lint

File is not `goimports`-ed (goimports)
"log"
"math/rand"
"net"
"time"

"github.com/brianvoe/gofakeit"
sq "github.com/Masterminds/squirrel"
service "github.com/evg555/auth/pkg/user_v1"
"github.com/jackc/pgx/v4"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"

service "github.com/evg555/auth/pkg/user_v1"
)

const grpcPort = 8000
const (
grpcPort = 8000
dbDSN = "host=localhost port=5432 dbname=auth user=auth password=auth sslmode=disable"
table = "users"
)

type server struct {
service.UnimplementedUserV1Server
db *pgx.Conn
}

func main() {
ctx := context.Background()

con, err := pgx.Connect(ctx, dbDSN)
if err != nil {
log.Fatalf("failed to connect to database: %v", err)
}
defer con.Close(ctx)

Check failure on line 38 in cmd/grpc-server/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `con.Close` is not checked (errcheck)

listener, err := net.Listen("tcp", fmt.Sprintf(":%d", grpcPort))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer()
reflection.Register(s)
service.RegisterUserV1Server(s, &server{})
service.RegisterUserV1Server(s, &server{db: con})

log.Printf("server listening at %v", listener.Addr())

Expand All @@ -39,39 +53,130 @@ func main() {
}
}

func (s *server) Create(_ context.Context, req *service.CreateRequest) (*service.CreateResponse, error) {
func (s *server) Create(ctx context.Context, req *service.CreateRequest) (*service.CreateResponse, error) {
log.Println("creating user...")
log.Printf("req: %+v", req)

id := rand.Int63()
var id int64

builderInsert := sq.Insert(table).
PlaceholderFormat(sq.Dollar).
Columns("name", "email", "password", "role").
Values(
req.GetName(),
req.GetEmail(),
req.GetPassword(),
req.GetRole(),
).
Suffix("RETURNING id")

query, args, err := builderInsert.ToSql()
if err != nil {
log.Printf("failed to insert to database: %v", err)
return nil, err
}

res := s.db.QueryRow(ctx, query, args...)

err = res.Scan(&id)
if err != nil {
log.Printf("failed to insert to database: %v", err)
return nil, err
}

return &service.CreateResponse{
Id: id,
}, nil
}

func (s *server) Get(_ context.Context, req *service.GetRequest) (*service.GetResponse, error) {
func (s *server) Get(ctx context.Context, req *service.GetRequest) (*service.GetResponse, error) {
log.Printf("getting user with id: %d\n", req.GetId())

var (
id int64
name string
email string
role int32
createdAt time.Time
updatedAt sql.NullTime
)

builderSelect := sq.Select("id", "name", "email", "role", "created_at", "updated_at").
From(table).
PlaceholderFormat(sq.Dollar).
Where(sq.Eq{"id": req.GetId()})

query, args, err := builderSelect.ToSql()
if err != nil {
log.Printf("failed to select from database: %v", err)
return nil, err
}

err = s.db.QueryRow(ctx, query, args...).Scan(&id, &name, &email, &role, &createdAt, &updatedAt)
if err != nil {
log.Printf("failed to select from database: %v", err)
return nil, err
}

return &service.GetResponse{
Id: req.GetId(),
Name: gofakeit.Name(),
Email: gofakeit.Email(),
Role: service.Role_USER,
CreatedAt: timestamppb.New(gofakeit.Date()),
UpdatedAt: timestamppb.New(gofakeit.Date()),
Id: id,
Name: name,
Email: email,
Role: service.Role(role),
CreatedAt: timestamppb.New(createdAt),
UpdatedAt: timestamppb.New(updatedAt.Time),
}, nil
}

func (s *server) Update(_ context.Context, req *service.UpdateRequest) (*emptypb.Empty, error) {
func (s *server) Update(ctx context.Context, req *service.UpdateRequest) (*emptypb.Empty, error) {
log.Println("updating user...")
log.Printf("req: %+v", req)

builderUpdate := sq.Update(table).PlaceholderFormat(sq.Dollar)

if req.GetName() != nil {
builderUpdate = builderUpdate.Set("name", req.GetName().Value)
}

if req.GetEmail() != nil {
builderUpdate = builderUpdate.Set("email", req.GetEmail().Value)
}

builderUpdate = builderUpdate.Where(sq.Eq{"id": req.GetId()})

query, args, err := builderUpdate.ToSql()
if err != nil {
log.Printf("failed to update database: %v", err)
return nil, err
}

_, err = s.db.Exec(ctx, query, args...)
if err != nil {
log.Printf("failed to update database: %v", err)
return nil, err
}

return &emptypb.Empty{}, nil
}

func (s *server) Delete(_ context.Context, req *service.DeleteRequest) (*emptypb.Empty, error) {
func (s *server) Delete(ctx context.Context, req *service.DeleteRequest) (*emptypb.Empty, error) {
log.Printf("deleting user with id: %d\n", req.GetId())

builderDelete := sq.Delete(table).
PlaceholderFormat(sq.Dollar).
Where(sq.Eq{"id": req.GetId()})

query, args, err := builderDelete.ToSql()
if err != nil {
log.Printf("failed to delete from database: %v", err)
return nil, err
}

_, err = s.db.Exec(ctx, query, args...)
if err != nil {
log.Printf("failed to delete from database: %v", err)
return nil, err
}

return &emptypb.Empty{}, nil
}
7 changes: 7 additions & 0 deletions docker/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
POSTGRES_DB=auth
POSTGRES_USER=auth
POSTGRES_PASSWORD=auth
POSTGRES_PORT=5432
MIGRATION_DIR=./migrations

APP_PORT=8000
14 changes: 14 additions & 0 deletions docker/app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM golang:1.21.2-alpine AS builder

COPY . /app
WORKDIR /app

RUN go mod download
RUN go build -o ./bin/grpc-server ./cmd/grpc-server/main.go

FROM alpine:latest

WORKDIR /root
COPY --from=builder /app/bin/grpc-server .

CMD ["./grpc-server"]
33 changes: 33 additions & 0 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: "3"

services:
app:
container_name: auth.app
build:
context: ..
dockerfile: docker/app/Dockerfile
ports:
- "${APP_PORT}:8000"
depends_on:
- pg

pg:
container_name: auth.pg
image: postgres:14-alpine3.18
environment:
- "POSTGRES_DB=${POSTGRES_DB}"
- "POSTGRES_USER=${POSTGRES_USER}"
- "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}"
ports:
- "${POSTGRES_PORT}:5432"
volumes:
- ./postgres/data:/var/lib/postgresql/data

migrator:
build:
context: ..
dockerfile: docker/migrator/Dockerfile
restart: on-failure
environment:
DB_HOST: pg

Loading

0 comments on commit 3cf75c9

Please sign in to comment.