hw12_13_14_15_calendar
Andrey Ivanov 2020-11-26 19:42:29 +03:00 committed by tiburon
parent b9b297500e
commit cdf7bf9441
21 changed files with 235 additions and 130 deletions

View File

@ -16,5 +16,4 @@ linters:
- gofumpt
- gosec
- nlreturn
- gocritic
- exhaustive

View File

@ -9,8 +9,11 @@ start: build-all rabbit-start calendar-start scheduler-start sender-start
stop: scheduler-stop sender-stop calendar-stop rabbit-stop
integration:
go test -v -race ./cmd/calendar/...
test:
go test -race ./internal/... ./pkg/... --tags calendar
go test -race ./internal/... ./pkg/...
lint: install-lint-deps
golangci-lint run .cmd/... ./internal/... ./pkg/...
@ -48,5 +51,4 @@ sender-stop:
.PHONY: build run test lint

View File

@ -3,7 +3,7 @@ RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d ./cmd/calendar/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o calendar ./cmd/calendar/.
RUN GOOS=linux GOARCH=amd64 go build -a -o calendar ./cmd/calendar/.
FROM alpine:latest
ENV APP_GRPC_ADDRESS=0.0.0.0

View File

@ -1,4 +1,4 @@
version: '2.0'
version: '3'
services:
rabbitmq:
image: rabbitmq:3-management-alpine
@ -12,28 +12,28 @@ services:
ports:
- "5672:5672"
- "15672:15672"
expose:
- 5672
- 15672
calendar:
build:
context: ..
dockerfile: ./cicd/calendar/Dockerfile
ports:
- 50051:50051
- 50053:50053
- 8888:8888
volumes:
- ../calendar.log:/calendar.log
scheduler:
build:
context: ..
dockerfile: ./cicd/scheduler/Dockerfile
volumes:
- ../calendar.log:/calendar.log
sender:
build:
context: ..
dockerfile: ./cicd/sender/Dockerfile
volumes:
- ../calendar.log:/calendar.log
#calendar:
# build:
# context: ..
# dockerfile: ./cicd/calendar/Dockerfile
# ports:
# - "8888:8888"
# volumes:
# - ../calendar.log:/calendar.log
#scheduler:
# build:
# context: ..
# dockerfile: ./cicd/scheduler/Dockerfile
# volumes:
# - ../calendar.log:/calendar.log
# depends_on:
# - rabbitmq
# - calendar
#sender:
# build:
# context: ..
# dockerfile: ./cicd/sender/Dockerfile
# volumes:
# - ../calendar.log:/calendar.log
# depends_on:
# - rabbitmq

View File

@ -3,7 +3,7 @@ RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d ./cmd/scheduler/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o scheduler ./cmd/scheduler/.
RUN GOOS=linux GOARCH=amd64 go build -a -o scheduler ./cmd/scheduler/.
FROM alpine:latest
ENV APP_RABBITMQ_ADDRESS=rabbitmq

View File

@ -3,7 +3,7 @@ RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d ./cmd/sender/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o sender ./cmd/sender/.
RUN GOOS=linux GOARCH=amd64 go build -a -o sender ./cmd/sender/.
FROM alpine:latest
ENV APP_RABBITMQ_ADDRESS=rabbitmq

View File

@ -32,7 +32,7 @@ func main() {
var conf config.Calendar
err := config.New(configFile, &conf)
if err != nil {
oslog.Fatal("не удалось открыть файл конфигурации:", err.Error())
oslog.Fatal("can't get config:", err.Error())
}
oslog.Printf("Переменная APP_GRPC_PORT: %#v", os.Getenv("APP_GRPC_PORT"))
oslog.Printf("Конфиг приложения: %#v", conf)
@ -69,8 +69,8 @@ func main() {
defer grpcDiler.Close()
grpcGwRouter := runtime.NewServeMux()
if err = public.RegisterGrpcHandler(context.Background(), grpcGwRouter, grpcDiler); err != nil {
ctx, cancel := context.WithCancel(context.Background())
if err = public.RegisterGrpcHandler(ctx, grpcGwRouter, grpcDiler); err != nil {
log.Errorf("can't register handlers for grpc-gateway: " + err.Error())
os.Exit(1)
}
@ -90,4 +90,6 @@ func main() {
<-signals
signal.Stop(signals)
serverGRPC.Stop()
serverAPI.Stop()
cancel()
}

View File

@ -0,0 +1,95 @@
package main
import (
"context"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/duration"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/stretchr/testify/require"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"log"
"sync"
"testing"
"time"
)
var _ = func() bool {
testing.Init()
return true
}()
const testPortBase = 3000
var testEvent01 = public.CreateReq{
Title: "Test event 01",
Date: time2pbtimestamp(time.Now().Add(30 * time.Second)),
Latency: dur2pbduration(24 * time.Hour),
Note: "Note of test event 01",
NotifyTime: dur2pbduration(5 * time.Minute),
UserID: 1111,
}
var testEvent02 = public.CreateReq{
Title: "Test event 02",
Date: time2pbtimestamp(time.Now().Add(60 * time.Second)),
Latency: dur2pbduration(2 * 24 * time.Hour),
Note: "Note of test event 02",
NotifyTime: dur2pbduration(5 * time.Minute),
UserID: 2222,
}
func TestSimple(t *testing.T) {
wg := sync.WaitGroup{}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
go func(ctx context.Context) {
main()
}(ctx)
publicAPI, err := public.NewClient("localhost", "50051")
require.NoError(t, err)
wg.Add(2)
// Реализовать тесты логики приложения:
t.Run("test public GRPC.Create with GRPC.GetById", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
resp2, err := publicAPI.GetByID(ctx, &public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.Equal(t, testEvent01.Title, resp2.Events[0].Title)
require.Equal(t, testEvent01.UserID, resp2.Events[0].UserID)
require.Equal(t, testEvent01.Date.Seconds, resp2.Events[0].Date.Seconds)
require.Equal(t, testEvent01.Note, resp2.Events[0].Note)
})
t.Run("test public GRPC.Update with GRPC.GetById", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
_, err = publicAPI.Update(ctx, &public.UpdateReq{ID: resp1.ID, Event: &public.Event{ID: resp1.ID, Title: testEvent02.Title, Date: testEvent02.Date, Latency: testEvent02.Latency, Note: testEvent02.Note, UserID: testEvent02.UserID, NotifyTime: testEvent02.NotifyTime}})
require.NoError(t, err)
resp2, err := publicAPI.GetByID(ctx, &public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.Equal(t, testEvent02.Title, resp2.Events[0].Title)
require.Equal(t, testEvent02.UserID, resp2.Events[0].UserID)
require.Equal(t, testEvent02.Date.Seconds, resp2.Events[0].Date.Seconds)
require.Equal(t, testEvent02.Note, resp2.Events[0].Note)
})
wg.Wait()
}
func time2pbtimestamp(t time.Time) *timestamp.Timestamp {
r, err := ptypes.TimestampProto(t)
if err != nil {
log.Fatalf("cant convert Time to Timestamp: %s", err.Error())
}
return r
}
func dur2pbduration(t time.Duration) *duration.Duration {
return ptypes.DurationProto(t)
}

View File

@ -1,7 +1,6 @@
package main
import (
"context"
"flag"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/sheduler"
"log"
@ -24,12 +23,11 @@ func main() {
var conf sheduler.Config
err := config.New(configFile, &conf)
if err != nil {
log.Fatal("не удалось открыть файл конфигурации:", err.Error())
log.Fatal("can't get config:", err.Error())
}
app := sheduler.New(conf)
ctx, cancel := context.WithCancel(context.Background())
if err = app.Start(ctx); err != nil {
if err = app.Start(); err != nil {
app.Logger.Errorf("failed to start scheduler: ", err.Error())
os.Exit(1)
}
@ -38,5 +36,6 @@ func main() {
signal.Notify(signals, syscall.SIGINT)
<-signals
signal.Stop(signals)
app.Stop(cancel)
app.Stop()
log.Println("scheduler shutdown gracefully")
}

View File

@ -1,7 +1,6 @@
package main
import (
"context"
"flag"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/sender"
"log"
@ -16,7 +15,7 @@ import (
var configFile string
func init() {
flag.StringVar(&configFile, "config", "/etc/sender.conf", "Path to configuration file")
flag.StringVar(&configFile, "config", "", "Path to configuration file")
flag.Parse()
}
@ -24,12 +23,11 @@ func main() {
var conf sender.Config
err := config.New(configFile, &conf)
if err != nil {
log.Fatal("не удалось открыть файл конфигурации:", err.Error())
log.Fatal("can't get config:", err.Error())
}
app := sender.New(conf)
ctx, cancel := context.WithCancel(context.Background())
if err = app.Start(ctx); err != nil {
if err = app.Start(); err != nil {
app.Logger.Errorf("failed to start sender: ", err.Error())
os.Exit(1)
}
@ -38,5 +36,6 @@ func main() {
signal.Notify(signals, syscall.SIGINT)
<-signals
signal.Stop(signals)
app.Stop(cancel)
app.Stop()
log.Println("sender shutdown gracefully")
}

View File

@ -9,12 +9,16 @@ require (
github.com/golang/protobuf v1.4.2
github.com/grpc-ecosystem/grpc-gateway v1.15.0
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.8.0 // indirect
github.com/mattn/go-sqlite3 v1.14.5 // indirect
github.com/mxschmitt/golang-env-struct v0.0.0-20181017075525-0c54aeca8397
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1
github.com/pressly/goose v2.6.0+incompatible // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/streadway/amqp v1.0.0
github.com/stretchr/testify v1.6.1
github.com/ziutek/mymysql v1.5.4 // indirect
go.uber.org/zap v1.15.0 // indirect
golang.org/x/net v0.0.0-20200625001655-4c5254603344
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect

View File

@ -54,6 +54,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mxschmitt/golang-env-struct v0.0.0-20181017075525-0c54aeca8397 h1:fbcg7+DIrrjxSsS78ARgJu/qZOO928w4mCj1KBEO7xM=
github.com/mxschmitt/golang-env-struct v0.0.0-20181017075525-0c54aeca8397/go.mod h1:BvJngicNxsNmAUzt7zXpaoWKwuBiyxnD7DxzxfrrPyY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
@ -64,6 +68,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pressly/goose v2.6.0+incompatible h1:3f8zIQ8rfgP9tyI0Hmcs2YNAqUCL1c+diLe3iU8Qd/k=
github.com/pressly/goose v2.6.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
@ -84,6 +90,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=

View File

@ -2,18 +2,44 @@ package private
import (
"context"
"net"
"github.com/golang/protobuf/ptypes/empty"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
googrpc "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type Service struct {
S *googrpc.Server
App calendar.App
}
type Config struct {
Address string
Port string
}
func New(app *calendar.App) Service {
return Service{S: googrpc.NewServer(), App: *app}
}
func (s *Service) Start(conf Config) error {
s.App.Logger.Infof("private GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.Address, conf.Port))
RegisterGrpcServer(s.S, s)
if err != nil {
return err
}
return s.S.Serve(listnGrpc)
}
func (s *Service) Stop() {
s.S.GracefulStop()
}
func (s Service) GetNotifications(ctx context.Context, e *empty.Empty) (*GetRsp, error) {
tmp, err := s.App.Storage.GetNotifications()
if err != nil {

View File

@ -1,36 +0,0 @@
package private
import (
"net"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
googrpc "google.golang.org/grpc"
)
type Server struct {
s *googrpc.Server
app calendar.App
}
type Config struct {
Address string
Port string
}
func New(app *calendar.App) Server {
return Server{s: googrpc.NewServer(), app: *app}
}
func (s *Server) Start(conf Config) error {
s.app.Logger.Infof("private GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.Address, conf.Port))
RegisterGrpcServer(s.s, &Service{})
if err != nil {
return err
}
return s.s.Serve(listnGrpc)
}
func (s *Server) Stop() {
s.s.GracefulStop()
}

View File

@ -15,6 +15,7 @@ type Sender struct {
Logger logger.Interface
Rabbit *rabbit.Rabbit
Queue string
Stop context.CancelFunc
}
type Config struct {
@ -34,7 +35,9 @@ func New(conf Config) Sender {
return Sender{Logger: log, Rabbit: rb, Queue: conf.Rabbitmq.Queue}
}
func (s *Sender) Start(ctx context.Context) error {
func (s *Sender) Start() error {
ctx, cancel := context.WithCancel(context.Background())
s.Stop = cancel
msg, err := s.Rabbit.Consume(ctx, s.Queue)
if err != nil {
return err
@ -59,7 +62,3 @@ func (s *Sender) Start(ctx context.Context) error {
}()
return nil
}
func (s *Sender) Stop(cancel context.CancelFunc) {
cancel()
}

View File

@ -14,6 +14,7 @@ type Scheduler struct {
CalendarAPI config.Server
Logger logger.Interface
Rabbit *rabbit.Rabbit
Stop context.CancelFunc
}
type Config struct {
@ -35,7 +36,9 @@ func New(conf Config) Scheduler {
return Scheduler{CalendarAPI: conf.CalendarAPI, Logger: log, Rabbit: rb}
}
func (s *Scheduler) Start(ctx context.Context) error {
func (s *Scheduler) Start() error {
ctx, cancel := context.WithCancel(context.Background())
s.Stop = cancel
fetcher := riseOnTick(ctx, s.Logger, func() interface{} { return worker(ctx, s.CalendarAPI, s.Rabbit, s.Logger) }, 1*time.Minute)
go func() {
for {
@ -52,7 +55,3 @@ func (s *Scheduler) Start(ctx context.Context) error {
}()
return nil
}
func (s *Scheduler) Stop(cancel context.CancelFunc) {
cancel()
}

View File

@ -3,6 +3,7 @@ package sheduler
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/api/private"
@ -40,25 +41,25 @@ func riseOnTick(ctx context.Context, log logger.Interface, fn func() interface{}
func worker(ctx context.Context, calendarAPI config.Server, rb *rabbit.Rabbit, log logger.Interface) error {
cli, err := private.NewClient(calendarAPI.Address, calendarAPI.Port)
if err != nil {
return err
return fmt.Errorf("can't get GRPC client: %w", err)
}
resp, err := cli.GetNotifications(ctx, nil)
if err != nil {
return err
return fmt.Errorf("can't get events from GRPC endpoint: %w", err)
}
for event := range resp.Events {
b, err := json.Marshal(event)
if err != nil {
return err
return fmt.Errorf("can't marshal events into JSON: %w", err)
}
err = rb.Publish(string(b))
if err != nil {
return err
return fmt.Errorf("can't publish serialized data to RabbitMQ: %w", err)
}
}
resp2, err := cli.PurgeOldEvents(ctx, &private.PurgeReq{OlderThenDays: 365})
if err != nil {
return err
return fmt.Errorf("can't purge old events: %w", err)
}
log.Infof("Scheduler successfully purges %s events from storage", resp2.Qty)
return nil

View File

@ -0,0 +1,16 @@
package public
import (
"net"
"google.golang.org/grpc"
)
func NewClient(addr, port string) (GrpcClient, error) {
conn, err := grpc.Dial(net.JoinHostPort(addr, port), grpc.WithInsecure())
if err != nil {
return nil, err
}
client := NewGrpcClient(conn)
return client, nil
}

View File

@ -2,18 +2,40 @@ package public
import (
"context"
"net"
"github.com/golang/protobuf/ptypes/empty"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
googrpc "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type Service struct {
S *googrpc.Server
App calendar.App
}
func New(app *calendar.App) Service {
return Service{S: googrpc.NewServer(), App: *app}
}
func (s *Service) Start(conf config.Calendar) error {
s.App.Logger.Infof("public GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.GRPC.Address, conf.GRPC.Port))
if err != nil {
return err
}
RegisterGrpcServer(s.S, s)
return s.S.Serve(listnGrpc)
}
func (s *Service) Stop() {
s.S.GracefulStop()
}
func (s Service) Create(ctx context.Context, e *CreateReq) (*CreateRsp, error) {
var res CreateRsp
ce, err := s.buildStorageEvent(e)

View File

@ -733,7 +733,9 @@ func _Grpc_Create_Handler(srv interface{}, ctx context.Context, dec func(interfa
return nil, err
}
if interceptor == nil {
return srv.(GrpcServer).Create(ctx, in)
sss := srv.(GrpcServer)
rrrrr, err := sss.Create(ctx, in)
return rrrrr, err
}
info := &grpc.UnaryServerInfo{
Server: srv,

View File

@ -1,32 +0,0 @@
package public
import (
"net"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
googrpc "google.golang.org/grpc"
)
type Server struct {
s *googrpc.Server
app calendar.App
}
func New(app *calendar.App) Server {
return Server{s: googrpc.NewServer(), app: *app}
}
func (s *Server) Start(conf config.Calendar) error {
s.app.Logger.Infof("public GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.GRPC.Address, conf.GRPC.Port))
RegisterGrpcServer(s.s, &Service{})
if err != nil {
return err
}
return s.s.Serve(listnGrpc)
}
func (s *Server) Stop() {
s.s.GracefulStop()
}