From 57937a5845510119f36d230d955f763cf671e33d Mon Sep 17 00:00:00 2001 From: "Stanislav N. aka pztrn" Date: Sat, 12 Oct 2024 13:04:09 +0500 Subject: [PATCH] Initial commit. --- .drone.yml | 59 +++++++++ .gitignore | 2 + .golangci.yml | 71 +++++++++++ .markdownlint.json | 4 + .yamllint | 9 ++ README.md | 3 + Taskfile.yml | 58 +++++++++ client/go/client.go | 4 + go.mod | 3 + scripts/version-data-generator.sh | 42 +++++++ server/Dockerfile.CMS | 18 +++ server/Dockerfile.featurer | 18 +++ server/Taskfile.yml | 6 + server/cmd/Taskfile.yml | 6 + server/cmd/cms/Taskfile.yml | 21 ++++ server/cmd/cms/main.go | 42 +++++++ server/cmd/featurer/Taskfile.yml | 21 ++++ server/cmd/featurer/main.go | 42 +++++++ server/entrypoint-cms.sh | 8 ++ server/entrypoint-featurer.sh | 8 ++ server/internal/application/application.go | 114 ++++++++++++++++++ server/internal/application/service.go | 54 +++++++++ server/internal/application/signals.go | 24 ++++ server/internal/services/core/datastore.go | 16 +++ .../services/core/datastore/datastore.go | 48 ++++++++ server/localdev/Taskfile.yml | 19 +++ server/localdev/common/Taskfile.yml | 15 +++ server/localdev/common/network.yaml | 18 +++ server/localdev/featurer/Taskfile.yml | 47 ++++++++ server/localdev/featurer/docker-compose.yaml | 16 +++ 30 files changed, 816 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .markdownlint.json create mode 100644 .yamllint create mode 100644 README.md create mode 100644 Taskfile.yml create mode 100644 client/go/client.go create mode 100644 go.mod create mode 100755 scripts/version-data-generator.sh create mode 100644 server/Dockerfile.CMS create mode 100644 server/Dockerfile.featurer create mode 100644 server/Taskfile.yml create mode 100644 server/cmd/Taskfile.yml create mode 100644 server/cmd/cms/Taskfile.yml create mode 100644 server/cmd/cms/main.go create mode 100644 server/cmd/featurer/Taskfile.yml create mode 100644 server/cmd/featurer/main.go create mode 100755 server/entrypoint-cms.sh create mode 100755 server/entrypoint-featurer.sh create mode 100644 server/internal/application/application.go create mode 100644 server/internal/application/service.go create mode 100644 server/internal/application/signals.go create mode 100644 server/internal/services/core/datastore.go create mode 100644 server/internal/services/core/datastore/datastore.go create mode 100644 server/localdev/Taskfile.yml create mode 100644 server/localdev/common/Taskfile.yml create mode 100644 server/localdev/common/network.yaml create mode 100644 server/localdev/featurer/Taskfile.yml create mode 100644 server/localdev/featurer/docker-compose.yaml diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..99e9300 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,59 @@ +--- +kind: pipeline +type: docker +name: "Tests and linting" + +trigger: + event: + exclude: + - pull_request + +steps: + - name: Linting + image: code.pztrn.name/containers/go-toolbox:v4 + pull: if-not-exists + commands: + - task lint + + - name: Tests + image: code.pztrn.name/containers/go-toolbox:v4 + pull: if-not-exists + commands: + - task test + +--- +kind: pipeline +type: docker +name: "Build Release" + +depends_on: + - "Tests and linting" + +trigger: + event: + - tag + +steps: + - name: Build Docker image for Featurer + image: code.pztrn.name/containers/mirror/plugins/buildx:1.1.11 + settings: + registry: code.pztrn.name + username: drone + password: + from_secret: drone_secret + repo: code.pztrn.name/pztrn/notificator + dockerfile: server/Dockerfile.featurer + auto_tag: true + force_tag: true + + - name: Build Docker image for CMS + image: code.pztrn.name/containers/mirror/plugins/buildx:1.1.11 + settings: + registry: code.pztrn.name + username: drone + password: + from_secret: drone_secret + repo: code.pztrn.name/pztrn/notificator + dockerfile: server/Dockerfile.CMS + auto_tag: true + force_tag: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83c3088 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*DS_Store* +_build diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..5058ba2 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,71 @@ +--- +run: + timeout: 5m + +linters: + enable-all: true + disable: + # deprecated + - varnamelen + - testpackage + - containedctx # very strange linter + - gomnd + - execinquery + # meaningfully disabled + - gochecknoglobals + - exhaustruct + - depguard # wtf is this linter + - tparallel # duplicates paralleltest + - unused # not very clever lint for generics + - ireturn # not very clever lint for generics + - interfacebloat # love big interfaces :) + - gci # whines about imports too much. + +linters-settings: + cyclop: + skip-tests: true + max-complexity: 20 + package-average: 10 + forbidigo: + forbid: + - '^(fmt\.Print(|f|ln)|print|println)$' + - '^time\.Now\(\)($|\.F|\.A|\.B|\.L|\.UTC\(\)\.I|,|\))(# Calls of time\.Now() without \.UTC() is prohibited\.)?' + funlen: + ignore-comments: true + lines: 200 + statements: 60 + gocyclo: + min-complexity: 20 + gofumpt: + extra-rules: true + govet: + enable-all: true + lll: + line-length: 120 + nestif: + min-complexity: 20 + tagliatelle: + case: + use-field-name: true + rules: + json: snake + yaml: camel + +issues: + exclude-use-default: false + exclude: + - ST1000 # package comment + - package-comments + exclude-rules: + - path: .+_test\.go + linters: + - gosec + - linters: + - godox + text: "TODO" + # Default error checking pattern "if err := ... ;err != nil {}" multiple times makes go vet to whine. + - linters: + - govet + text: 'declaration of "err" shadows' + max-issues-per-linter: 0 + max-same-issues: 0 diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..34f7fce --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,4 @@ +{ + "line-length": false, + "first-line-h1": false +} diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..5e930a3 --- /dev/null +++ b/.yamllint @@ -0,0 +1,9 @@ +--- +extends: default + +rules: + line-length: disable + braces: + min-spaces-inside: 1 + max-spaces-inside: 1 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6fac688 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Featurer + +Features toggling server. diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..9c6dfcd --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,58 @@ +--- +version: "3" + +vars: + BRANCH: + sh: ./scripts/version-data-generator.sh branch + BUILD_DATE: + sh: date '+%Y-%m-%d %H:%M:%S %z' + BUILD_NUMBER: + sh: ./scripts/version-data-generator.sh build_number + COMMIT: + sh: ./scripts/version-data-generator.sh commit + VERSION: + sh: ./scripts/version-data-generator.sh version + BASIC_LDFLAGS: "-X 'go.dev.pztrn.name/featurer/server/internal/application.Version={{ .VERSION }}' -X 'go.dev.pztrn.name/featurer/server/internal/application.Branch={{ .BRANCH }}' -X 'go.dev.pztrn.name/featurer/server/internal/application.Commit={{ .COMMIT }}' -X 'go.dev.pztrn.name/featurer/server/internal/application.Build={{ .BUILD_NUMBER }}' -X 'go.dev.pztrn.name/featurer/server/internal/application.BuildDate={{ .BUILD_DATE }}'" + LDFLAGS: "{{ .BASIC_LDFLAGS }}" + +includes: + server: ./server + +tasks: + cleanup: + desc: "Cleanup _build directory." + cmds: + - task: server:cmd:cms:cleanup + - task: server:cmd:featurer:cleanup + + default: + desc: "Default help." + cmds: + - echo "Run \"task -l\" for list of available tasks." + + lint: + desc: "Runs linters." + cmds: + - golangci-lint run ./... + - go vet ./... + + pre-commit: + desc: "Runs some tasks before comitting." + cmds: + - task: lint + - task: test + + test: + desc: "Run tests." + cmds: + - go test ./... + + vars: + desc: "Show vars which will be used for building or running." + silent: true + cmds: + - echo "BRANCH={{ .BRANCH }}" + - echo "BUILD_DATE={{ .BUILD_DATE }}" + - echo "BUILD_NUMBER={{ .BUILD_NUMBER }}" + - echo "COMMIT={{ .COMMIT }}" + - echo "VERSION={{ .VERSION }}" diff --git a/client/go/client.go b/client/go/client.go new file mode 100644 index 0000000..3f92920 --- /dev/null +++ b/client/go/client.go @@ -0,0 +1,4 @@ +package client + +// Client is a Featurer client for Golang application. +type Client struct{} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7dd476e --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module go.dev.pztrn.name/featurer + +go 1.23 diff --git a/scripts/version-data-generator.sh b/scripts/version-data-generator.sh new file mode 100755 index 0000000..38e8da2 --- /dev/null +++ b/scripts/version-data-generator.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +VERSION_PREFIX="v" + +# shellcheck disable=SC1083 +BUILD_NUMBER=$(git rev-list --count --all) +BRANCH_NAME=$(git branch --show-current) +COMMIT=$(git rev-parse HEAD) +VERSION=$(git tag | sort | grep "${VERSION_PREFIX}" | tail -n 1 | cut -c$((${#VERSION_PREFIX} + 1))-) + +if [ "${VERSION}" == "" ]; then + VERSION="0.1.0-dev" +fi + +function show_help() { + echo "$0 [param]" + echo "" + echo "Parameters:" + echo "" + echo " branch Show branch." + echo " build_number Show build number (commits count)." + echo " commit Show commit hash." + echo " version Show version (real or '0.1.0-dev' if no version tags present)." +} + +case $1 in +branch) + echo "${BRANCH_NAME}" + ;; +build_number) + echo "${BUILD_NUMBER}" + ;; +commit) + echo "${COMMIT}" + ;; +version) + echo "${VERSION}" + ;; +*) + show_help + ;; +esac diff --git a/server/Dockerfile.CMS b/server/Dockerfile.CMS new file mode 100644 index 0000000..7fad055 --- /dev/null +++ b/server/Dockerfile.CMS @@ -0,0 +1,18 @@ +FROM code.pztrn.name/containers/go-toolbox:v4 AS build + +WORKDIR /featurer +COPY . /featurer +RUN --mount=type=cache,target="$GOCACHE" task server:cmd:cms:build + +FROM debian:12.6-slim + +RUN apt-get update && \ + apt-get install -y ca-certificates iputils-ping && \ + rm -rf /var/lib/apt/* /var/cache/apt/* + +COPY --from=build /featurer/_build/featurer-cms /featurer-cms +COPY --from=build /usr/local/bin/dlv /dlv + +COPY server/entrypoint-cms.sh /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/server/Dockerfile.featurer b/server/Dockerfile.featurer new file mode 100644 index 0000000..34cb14a --- /dev/null +++ b/server/Dockerfile.featurer @@ -0,0 +1,18 @@ +FROM code.pztrn.name/containers/go-toolbox:v4 AS build + +WORKDIR /featurer +COPY . /featurer +RUN --mount=type=cache,target="$GOCACHE" task server:cmd:featurer:build + +FROM debian:12.6-slim + +RUN apt-get update && \ + apt-get install -y ca-certificates iputils-ping && \ + rm -rf /var/lib/apt/* /var/cache/apt/* + +COPY --from=build /featurer/_build/featurer /featurer +COPY --from=build /usr/local/bin/dlv /dlv + +COPY server/entrypoint-featurer.sh /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/server/Taskfile.yml b/server/Taskfile.yml new file mode 100644 index 0000000..bbe20e6 --- /dev/null +++ b/server/Taskfile.yml @@ -0,0 +1,6 @@ +--- +version: "3" + +includes: + cmd: ./cmd + localdev: ./localdev diff --git a/server/cmd/Taskfile.yml b/server/cmd/Taskfile.yml new file mode 100644 index 0000000..5ba3e49 --- /dev/null +++ b/server/cmd/Taskfile.yml @@ -0,0 +1,6 @@ +--- +version: "3" + +includes: + cms: ./cms + featurer: ./featurer diff --git a/server/cmd/cms/Taskfile.yml b/server/cmd/cms/Taskfile.yml new file mode 100644 index 0000000..72642d7 --- /dev/null +++ b/server/cmd/cms/Taskfile.yml @@ -0,0 +1,21 @@ +--- +version: "3" + +tasks: + build: + desc: "Builds Featurer's CMS binary." + cmds: + - task: cleanup + - go build -ldflags="{{ .LDFLAGS }}" -tags netgo -o _build/featurer-cms{{exeExt}} ./server/cmd/cms/main.go + sources: + - ./**/*.go + - ./Taskfile.yml + - ./go.mod + generates: + - ./_build/featurer-cms{{exeExt}} + method: none + + cleanup: + desc: "Deletes Featurer's CMS binary from local build cache." + cmds: + - rm -f _build/featurer-cms{{exeExt}} diff --git a/server/cmd/cms/main.go b/server/cmd/cms/main.go new file mode 100644 index 0000000..1ff5881 --- /dev/null +++ b/server/cmd/cms/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "log/slog" + + "go.dev.pztrn.name/featurer/server/internal/application" + "go.dev.pztrn.name/featurer/server/internal/services/core/datastore" +) + +func main() { + _ = slog.SetLogLoggerLevel(slog.LevelDebug) + + app := application.New() + + slog.Info( + "Launching Featurer's CMS...", + "version", application.Version, + "build", application.Build, + "branch", application.Branch, + "commit", application.Commit, + "build date", application.BuildDate, + ) + + // Initializing core services first. + checkError(datastore.Initialize(app)) + + // Then - features services. + + // Start application. + checkError(app.Start()) + + <-app.GetShutdownDoneChannel() + slog.Info("Featurer's CMS.") +} + +func checkError(err error) { + if err == nil { + return + } + + panic(err) +} diff --git a/server/cmd/featurer/Taskfile.yml b/server/cmd/featurer/Taskfile.yml new file mode 100644 index 0000000..aa6444f --- /dev/null +++ b/server/cmd/featurer/Taskfile.yml @@ -0,0 +1,21 @@ +--- +version: "3" + +tasks: + build: + desc: "Builds Featurer main binary." + cmds: + - task: cleanup + - go build -ldflags="{{ .LDFLAGS }}" -tags netgo -o _build/featurer{{exeExt}} ./server/cmd/featurer/main.go + sources: + - ./**/*.go + - ./Taskfile.yml + - ./go.mod + generates: + - ./_build/featurer{{exeExt}} + method: none + + cleanup: + desc: "Deletes Featurer main binary from local build cache." + cmds: + - rm -f _build/featurer{{exeExt}} diff --git a/server/cmd/featurer/main.go b/server/cmd/featurer/main.go new file mode 100644 index 0000000..7f39aeb --- /dev/null +++ b/server/cmd/featurer/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "log/slog" + + "go.dev.pztrn.name/featurer/server/internal/application" + "go.dev.pztrn.name/featurer/server/internal/services/core/datastore" +) + +func main() { + _ = slog.SetLogLoggerLevel(slog.LevelDebug) + + app := application.New() + + slog.Info( + "Launching Featurer server...", + "version", application.Version, + "build", application.Build, + "branch", application.Branch, + "commit", application.Commit, + "build date", application.BuildDate, + ) + + // Initializing core services first. + checkError(datastore.Initialize(app)) + + // Then - features services. + + // Start application. + checkError(app.Start()) + + <-app.GetShutdownDoneChannel() + slog.Info("Featurer stopped.") +} + +func checkError(err error) { + if err == nil { + return + } + + panic(err) +} diff --git a/server/entrypoint-cms.sh b/server/entrypoint-cms.sh new file mode 100755 index 0000000..650af3f --- /dev/null +++ b/server/entrypoint-cms.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +echo "* Starting Featurer's CMS..." +if [ -n "${FEATURER_DEBUG}" ]; then + exec /dlv --listen=:4000 --headless=true --log=true --accept-multiclient --api-version=2 exec /featurer-cms --continue +else + exec /featurer-cms +fi diff --git a/server/entrypoint-featurer.sh b/server/entrypoint-featurer.sh new file mode 100755 index 0000000..060feab --- /dev/null +++ b/server/entrypoint-featurer.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +echo "* Starting Featurer..." +if [ -n "${FEATURER_DEBUG}" ]; then + exec /dlv --listen=:4000 --headless=true --log=true --accept-multiclient --api-version=2 exec /featurer --continue +else + exec /featurer +fi diff --git a/server/internal/application/application.go b/server/internal/application/application.go new file mode 100644 index 0000000..22c742d --- /dev/null +++ b/server/internal/application/application.go @@ -0,0 +1,114 @@ +package application + +import ( + "context" + "errors" + "fmt" + "log/slog" + "os" + "os/signal" + "sync" + "syscall" +) + +var ( + // ErrApplication indicates that error appeared somewhere in application's lifecycle controller. + ErrApplication = errors.New("application") + + // Version is application's version. + Version string + // Branch is a branch name from which application was built. + Branch string + // Commit is a commit hash from which application was built. + Commit string + // Build is a build number. + Build string + // BuildDate is a date on which application was built. + BuildDate string +) + +// Application is an application's lifecycle controlling structure. +type Application struct { + ctx context.Context + shutdownDone chan struct{} + cancelFunc context.CancelFunc + services map[string]Service + dataPath string + servicesMutex sync.RWMutex +} + +// New creates new application's lifecycle controlling structure. +func New() *Application { + appl := &Application{} + appl.initialize() + + return appl +} + +// GetContext returns application's global context. +func (a *Application) GetContext() context.Context { + return a.ctx +} + +// GetShutdownDoneChannel returns channel on which lifecycle controller will tell about shutdown completion. +// Should be used only in main()! +func (a *Application) GetShutdownDoneChannel() chan struct{} { + slog.Debug("Returning shutdown completion channel.") + + return a.shutdownDone +} + +func (a *Application) initialize() { + a.ctx, a.cancelFunc = context.WithCancel(context.Background()) + a.ctx, a.cancelFunc = signal.NotifyContext(a.ctx, os.Interrupt, syscall.SIGTERM) + + a.services = make(map[string]Service) + + a.shutdownDone = make(chan struct{}, 1) +} + +// Shutdown stops application and all registered services. +func (a *Application) Shutdown() error { + a.cancelFunc() + + a.servicesMutex.RLock() + defer a.servicesMutex.RUnlock() + + for _, service := range a.services { + if err := service.Shutdown(); err != nil { + slog.Error("Error appeared when trying to shut down service", "service", service.GetName(), "error", err.Error()) + } + } + + a.shutdownDone <- struct{}{} + + return nil +} + +// Start starts application and all registered services. +func (a *Application) Start() error { + go a.signalsListener() + + a.servicesMutex.RLock() + defer a.servicesMutex.RUnlock() + + // First pass - connecting dependencies. + for _, service := range a.services { + if err := service.ConnectDependencies(); err != nil { + return fmt.Errorf("%w: connecting dependencies for service '%s': %w", ErrApplication, service.GetName(), err) + } + } + + // Second pass - launching startup tasks. + for name, service := range a.services { + slog.Debug("Launching startup tasks for service", "service", name) + + if err := service.LaunchStartupTasks(); err != nil { + return fmt.Errorf("%w: launching startup tasks for '%s': %w", ErrApplication, service.GetName(), err) + } + } + + slog.Debug("Application started.") + + return nil +} diff --git a/server/internal/application/service.go b/server/internal/application/service.go new file mode 100644 index 0000000..f4f02d6 --- /dev/null +++ b/server/internal/application/service.go @@ -0,0 +1,54 @@ +package application + +import ( + "errors" + "fmt" +) + +var ( + // ErrServiceAlreadyRegistered appears when trying to register a service with already taken name. + ErrServiceAlreadyRegistered = errors.New("service already registered") + // ErrServiceNotRegistered appears when trying to get a registered service with unknown name. + ErrServiceNotRegistered = errors.New("unknown service") +) + +// Service это интерфейс, которому должны соответствовать все сервисы, используемые в приложении. +type Service interface { + ConnectDependencies() error + GetName() string + Initialize() error + LaunchStartupTasks() error + Shutdown() error +} + +// GetService возвращает сервис по имени, или же ошибку, если сервис не был зарегистрирован ранее. +func (a *Application) GetService(name string) (Service, error) { + a.servicesMutex.RLock() + service, found := a.services[name] + a.servicesMutex.RUnlock() + + if !found { + return nil, fmt.Errorf("%w: get service '%s': %w", ErrApplication, name, ErrServiceNotRegistered) + } + + return service, nil +} + +// RegisterService регистрирует сервис, или возвращает ошибку, если сервис с таким именем уже был +// зарегистрирован ранее. +func (a *Application) RegisterService(service Service) error { + _, found := a.services[service.GetName()] + if found { + return fmt.Errorf("%w: register service '%s': %w", ErrApplication, service.GetName(), ErrServiceAlreadyRegistered) + } + + if err := service.Initialize(); err != nil { + return fmt.Errorf("%w: initializing service '%s': %w", ErrApplication, service.GetName(), err) + } + + a.servicesMutex.Lock() + a.services[service.GetName()] = service + a.servicesMutex.Unlock() + + return nil +} diff --git a/server/internal/application/signals.go b/server/internal/application/signals.go new file mode 100644 index 0000000..dd539c4 --- /dev/null +++ b/server/internal/application/signals.go @@ -0,0 +1,24 @@ +package application + +import ( + "log/slog" + "os" + "os/signal" + "syscall" +) + +func (a *Application) signalsListener() { + slog.Info("Starting listening for SIGTERM signal...") + + listener := make(chan os.Signal, 1) + + signal.Notify(listener, syscall.SIGTERM, os.Interrupt) + + <-listener + slog.Info("Got SIGTERM, stopping Featurer...") + + if err := a.Shutdown(); err != nil { + slog.Error("Something went wrong when trying to shutdown application", "error", err.Error()) + slog.Error("!!! APPLICATION CANNOT BE STOPPED NORMALLY, KILL IT MANUALLY !!!") + } +} diff --git a/server/internal/services/core/datastore.go b/server/internal/services/core/datastore.go new file mode 100644 index 0000000..77ac53b --- /dev/null +++ b/server/internal/services/core/datastore.go @@ -0,0 +1,16 @@ +package core + +import "errors" + +// ServiceNameDatastore is a service name for data storage implementation. +const ServiceNameDatastore = "datastore" + +var ( + // ErrDatastore indicates that error appeared somewhere in data storage service. + ErrDatastore = errors.New("datastore") + // ErrDatastoreServiceIsInvalid appears when data storage implementation isn't conforming to interface. + ErrDatastoreServiceIsInvalid = errors.New("invalid datastore implementation") +) + +// DataStore is an interface for data storage implementations. +type DataStore interface{} diff --git a/server/internal/services/core/datastore/datastore.go b/server/internal/services/core/datastore/datastore.go new file mode 100644 index 0000000..84d6579 --- /dev/null +++ b/server/internal/services/core/datastore/datastore.go @@ -0,0 +1,48 @@ +package datastore + +import ( + "fmt" + "log/slog" + + "go.dev.pztrn.name/featurer/server/internal/application" + "go.dev.pztrn.name/featurer/server/internal/services/core" +) + +type datastore struct { + app *application.Application +} + +// Initialize initializes service. +func Initialize(app *application.Application) error { + plan := &datastore{ + app: app, + } + + if err := app.RegisterService(plan); err != nil { + return fmt.Errorf("%w: %w", core.ErrDatastore, err) + } + + return nil +} + +func (p *datastore) ConnectDependencies() error { + return nil +} + +func (p *datastore) GetName() string { + return core.ServiceNameDatastore +} + +func (p *datastore) Initialize() error { + slog.Info("Initializing data storage...") + + return nil +} + +func (p *datastore) LaunchStartupTasks() error { + return nil +} + +func (p *datastore) Shutdown() error { + return nil +} diff --git a/server/localdev/Taskfile.yml b/server/localdev/Taskfile.yml new file mode 100644 index 0000000..be6625e --- /dev/null +++ b/server/localdev/Taskfile.yml @@ -0,0 +1,19 @@ +--- +version: 3 + +includes: + common: ./common + featurer: ./featurer + +tasks: + down: + desc: "Removes development environment." + cmds: + - task: featurer:down + - task: common:network-down + + up: + desc: "Creates development environment." + cmds: + - task: common:network-up + - task: featurer:up diff --git a/server/localdev/common/Taskfile.yml b/server/localdev/common/Taskfile.yml new file mode 100644 index 0000000..a20fb53 --- /dev/null +++ b/server/localdev/common/Taskfile.yml @@ -0,0 +1,15 @@ +--- +version: "3" + +tasks: + network-down: + desc: "Deletes Docker network for development." + dir: "./server/localdev/common" + cmds: + - docker compose -p featurer-network -f network.yaml down + + network-up: + desc: "Creates Docker network for development" + dir: "./server/localdev/common" + cmds: + - docker compose -p featurer-network -f network.yaml up -d diff --git a/server/localdev/common/network.yaml b/server/localdev/common/network.yaml new file mode 100644 index 0000000..4438de9 --- /dev/null +++ b/server/localdev/common/network.yaml @@ -0,0 +1,18 @@ +--- +services: + dummy: + image: busybox + container_name: featurer-dummy + hostname: dummy + networks: + featurer: + ipv4_address: 248.248.0.254 + +networks: + featurer: + driver: bridge + name: "featurer" + ipam: + config: + - subnet: 248.248.0.0/24 + gateway: 248.248.0.1 diff --git a/server/localdev/featurer/Taskfile.yml b/server/localdev/featurer/Taskfile.yml new file mode 100644 index 0000000..95c3d30 --- /dev/null +++ b/server/localdev/featurer/Taskfile.yml @@ -0,0 +1,47 @@ +--- +version: "3" + +tasks: + build: + desc: "Builds Featurer's container." + dir: "./server/localdev/featurer" + cmds: + - docker compose -p featurer -f docker-compose.yaml build + + down: + desc: "Deletes all Featurer's data (down)." + dir: "./server/localdev/featurer" + cmds: + - docker compose -p featurer -f docker-compose.yaml down --volumes + + logs: + desc: "Show Featurer logs." + dir: "./server/localdev/featurer" + cmds: + - docker compose -p featurer -f docker-compose.yaml logs -f + + restart: + desc: "Restart Featurer." + dir: "./server/localdev/featurer" + cmds: + - docker compose -p featurer -f docker-compose.yaml restart + + start: + desc: "Start Featurer." + dir: "./server/localdev/featurer" + cmds: + - docker compose -p featurer -f docker-compose.yaml start + + stop: + desc: "Stop Featurer without deleting it's data." + dir: "./server/localdev/featurer" + cmds: + - docker compose -p featurer -f docker-compose.yaml stop + + up: + desc: "Start Featurer (up -d)." + dir: "./server/localdev/featurer" + cmds: + - task: :common:network-up + - task: build + - docker compose -p featurer -f docker-compose.yaml up -d diff --git a/server/localdev/featurer/docker-compose.yaml b/server/localdev/featurer/docker-compose.yaml new file mode 100644 index 0000000..cdfdb0e --- /dev/null +++ b/server/localdev/featurer/docker-compose.yaml @@ -0,0 +1,16 @@ +--- +services: + featurer: + container_name: "featurer" + build: + context: ../../../ + dockerfile: server/Dockerfile.featurer + networks: + featurer: + ipv4_address: 248.248.0.2 + cap_add: + - SYS_PTRACE + +networks: + featurer: + external: true