commit 57937a5845510119f36d230d955f763cf671e33d Author: Stanislav N. aka pztrn Date: Sat Oct 12 13:04:09 2024 +0500 Initial commit. 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