diff --git a/Makefile b/Makefile index 516a00a..3ca512f 100644 --- a/Makefile +++ b/Makefile @@ -14,3 +14,6 @@ install-tools: install-golangci-lint .PHONY: install install: install-tools install-git-hooks + +generate: + go generate ./... \ No newline at end of file diff --git a/build/docker/dcbot/dev/Dockerfile b/build/docker/dcbot/dev/Dockerfile new file mode 100644 index 0000000..0054a8c --- /dev/null +++ b/build/docker/dcbot/dev/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1.19 as builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations +ARG SKAFFOLD_GO_GCFLAGS +RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o dcbot ./cmd/dcbot/main.go + +FROM ubuntu:22.04 +# Define GOTRACEBACK to mark this container as using the Go language runtime +# for `skaffold debug` (https://skaffold.dev/docs/workflows/debug/). +WORKDIR /root +ENV GOTRACEBACK=single +RUN apt update +RUN apt install -y ca-certificates tzdata +COPY --from=builder /app/dcbot . +ENTRYPOINT ["./dcbot"] \ No newline at end of file diff --git a/cmd/dcbot/internal/run/run.go b/cmd/dcbot/internal/run/run.go new file mode 100644 index 0000000..3b3cd1b --- /dev/null +++ b/cmd/dcbot/internal/run/run.go @@ -0,0 +1,70 @@ +package run + +import ( + "context" + "fmt" + "os" + "os/signal" + "syscall" + + "gitea.dwysokinski.me/twhelp/dcbot/internal/discord" + "github.com/kelseyhightower/envconfig" + "github.com/urfave/cli/v2" + "go.uber.org/zap" +) + +const ( + healthyFilePath = "/tmp/healthy" + healthyFilePerm = 0644 +) + +func New() *cli.Command { + return &cli.Command{ + Name: "run", + Usage: "Runs the dc bot", + Action: func(c *cli.Context) error { + logger := zap.L() + + cfg, err := newBotConfig() + if err != nil { + return fmt.Errorf("newBotConfig: %w", err) + } + + bot, err := discord.NewBot(cfg.Token) + if err != nil { + return fmt.Errorf("discord.NewBot: %w", err) + } + defer func() { + _ = bot.Close() + }() + + if err := os.WriteFile(healthyFilePath, []byte("healthy"), healthyFilePerm); err != nil { + return fmt.Errorf("couldn't create file (path=%s): %w", healthyFilePath, err) + } + + logger.Info("Bot is up and running") + + waitForSignal(c.Context) + + return nil + }, + } +} + +type botConfig struct { + Token string `envconfig:"TOKEN" required:"true"` +} + +func newBotConfig() (botConfig, error) { + var cfg botConfig + if err := envconfig.Process("BOT", &cfg); err != nil { + return botConfig{}, fmt.Errorf("envconfig.Process: %w", err) + } + return cfg, nil +} + +func waitForSignal(ctx context.Context) { + ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + defer stop() + <-ctx.Done() +} diff --git a/cmd/dcbot/main.go b/cmd/dcbot/main.go index 21d541e..84b8cab 100644 --- a/cmd/dcbot/main.go +++ b/cmd/dcbot/main.go @@ -7,6 +7,7 @@ import ( "gitea.dwysokinski.me/twhelp/dcbot/cmd/dcbot/internal" "gitea.dwysokinski.me/twhelp/dcbot/cmd/dcbot/internal/appmode" "gitea.dwysokinski.me/twhelp/dcbot/cmd/dcbot/internal/db" + "gitea.dwysokinski.me/twhelp/dcbot/cmd/dcbot/internal/run" "github.com/urfave/cli/v2" ) @@ -50,6 +51,7 @@ func newApp(mode string) *cli.App { } app.Commands = []*cli.Command{ db.New(), + run.New(), } return app } diff --git a/go.mod b/go.mod index 8992c4a..4118d75 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module gitea.dwysokinski.me/twhelp/dcbot go 1.19 require ( + github.com/bwmarrin/discordgo v0.26.1 github.com/kelseyhightower/envconfig v1.4.0 github.com/stretchr/testify v1.8.0 github.com/uptrace/bun v1.1.8 @@ -18,6 +19,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 8dfd078..9b61dbf 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/bwmarrin/discordgo v0.26.1 h1:AIrM+g3cl+iYBr4yBxCBp9tD9jR3K7upEjl0d89FRkE= +github.com/bwmarrin/discordgo v0.26.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -10,6 +12,8 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= @@ -59,10 +63,16 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/discord/bot.go b/internal/discord/bot.go new file mode 100644 index 0000000..784c1fd --- /dev/null +++ b/internal/discord/bot.go @@ -0,0 +1,26 @@ +package discord + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" +) + +type Bot struct { + s *discordgo.Session +} + +func NewBot(token string) (*Bot, error) { + s, err := discordgo.New("Bot " + token) + if err != nil { + return nil, fmt.Errorf("discordgo.New: %w", err) + } + if err = s.Open(); err != nil { + return nil, fmt.Errorf("s.Open: %w", err) + } + return &Bot{s: s}, nil +} + +func (b *Bot) Close() error { + return b.s.Close() +} diff --git a/k8s/base/bot.yml b/k8s/base/bot.yml new file mode 100644 index 0000000..131b05c --- /dev/null +++ b/k8s/base/bot.yml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: twhelp-dcbot-deployment +spec: + selector: + matchLabels: + app: twhelp-dcbot + template: + metadata: + labels: + app: twhelp-dcbot + spec: + containers: + - name: twhelp-dcbot + image: dcbot + args: ["run"] + env: + - name: APP_MODE + value: development + - name: DB_DSN + valueFrom: + secretKeyRef: + name: twhelp-dcbot-secret + key: db-dsn + - name: DB_MAX_OPEN_CONNECTIONS + value: "10" + - name: DB_MAX_IDLE_CONNECTIONS + value: "2" + - name: BOT_TOKEN + valueFrom: + secretKeyRef: + name: twhelp-dcbot-secret + key: token + livenessProbe: + exec: + command: [ "cat", "/tmp/healthy" ] + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 400m + memory: 400Mi \ No newline at end of file diff --git a/k8s/base/kustomization.yml b/k8s/base/kustomization.yml new file mode 100644 index 0000000..079eab1 --- /dev/null +++ b/k8s/base/kustomization.yml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - bot.yml +images: + - name: dcbot + newName: dcbot + newTag: latest \ No newline at end of file diff --git a/k8s/overlays/dev/.gitignore b/k8s/overlays/dev/.gitignore new file mode 100644 index 0000000..56bd904 --- /dev/null +++ b/k8s/overlays/dev/.gitignore @@ -0,0 +1 @@ +secret.yml \ No newline at end of file diff --git a/k8s/overlays/dev/kustomization.yml b/k8s/overlays/dev/kustomization.yml new file mode 100644 index 0000000..ade095e --- /dev/null +++ b/k8s/overlays/dev/kustomization.yml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +nameSuffix: -dev +resources: + - secret.yml + - ../../base \ No newline at end of file diff --git a/k8s/overlays/dev/secret.example.yml b/k8s/overlays/dev/secret.example.yml new file mode 100644 index 0000000..532cc0e --- /dev/null +++ b/k8s/overlays/dev/secret.example.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: twhelp-dcbot-secret +type: Opaque +data: + # postgres://dcbot:dcbot@twdcbotdb-postgresql:5432/dcbot?sslmode=disable + db-dsn: cG9zdGdyZXM6Ly9kY2JvdDpkY2JvdEB0d2RjYm90ZGItcG9zdGdyZXNxbDo1NDMyL2RjYm90P3NzbG1vZGU9ZGlzYWJsZQ== + token: tokenhere \ No newline at end of file diff --git a/skaffold.yml b/skaffold.yml new file mode 100644 index 0000000..4cabd1d --- /dev/null +++ b/skaffold.yml @@ -0,0 +1,26 @@ +apiVersion: skaffold/v2beta27 +kind: Config +build: + tagPolicy: + customTemplate: + template: latest + artifacts: + - image: dcbot + context: . + docker: + dockerfile: ./build/docker/dcbot/dev/Dockerfile +deploy: + helm: + releases: + - name: twdcbotdb + repo: https://charts.bitnami.com/bitnami + remoteChart: postgresql + version: 11.6.19 + wait: true + setValues: + auth.username: dcbot + auth.password: dcbot + auth.database: dcbot + kustomize: + paths: + - k8s/overlays/dev