From 6693ceabccf5f705343fd90caad88cda91a71bb0 Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Tue, 24 Dec 2024 22:40:37 +0300 Subject: [PATCH] refactor --- .dockerignore | 1 - Dockerfile | 5 +- Makefile | 14 +- cmd/aides-repo-api/main.go | 2 +- internal/app/app.go | 14 +- internal/services/cronservice/service.go | 27 ++- internal/services/reposervice/service.go | 288 +++++++++++------------ internal/services/reposervice/utils.go | 91 +++++++ 8 files changed, 275 insertions(+), 167 deletions(-) create mode 100644 internal/services/reposervice/utils.go diff --git a/.dockerignore b/.dockerignore index cb1343b..86628a8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ -.git app.db uploads Dockerfile \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index fb39e3d..d940766 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,10 @@ WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN make release + +ARG APP_VERSION + +RUN make build FROM registry.altlinux.org/sisyphus/alt:20241211 diff --git a/Makefile b/Makefile index c58d73b..818b0c7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,10 @@ -GIT_VERSION = $(shell git describe --tags ) GOLANGCI_LINT := go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2 SWAG := go run github.com/swaggo/swag/cmd/swag@v1.16.4 +GIT_VERSION = $(shell git describe --tags) +APP_VERSION ?= $(GIT_VERSION) +DOCKER_TAG ?= $(APP_VERSION) + format: @echo "🛠️ Format and Lint code with golangci-lint" $(GOLANGCI_LINT) run @@ -19,16 +22,19 @@ swag: build: go build \ - -ldflags="-X 'code.alt-gnome.ru/aides-infra/aides-repo-api/internal/config.Version=$(GIT_VERSION)'" \ + -ldflags="-X 'code.alt-gnome.ru/aides-infra/aides-repo-api/internal/config.Version=$(APP_VERSION)'" \ -o aides-repo-api \ ./cmd/aides-repo-api/main.go release: go build \ - -ldflags="-s -w -X 'code.alt-gnome.ru/aides-infra/aides-repo-api/internal/config.Version=$(GIT_VERSION)'" \ + -ldflags="-s -w -X 'code.alt-gnome.ru/aides-infra/aides-repo-api/internal/config.Version=$(APP_VERSION)'" \ -o aides-repo-api \ ./cmd/aides-repo-api/main.go build-docker: - docker build -t ghcr.io/aides-infra/aides-repo-api . + docker build \ + --build-arg APP_VERSION=${APP_VERSION} \ + -t ghcr.io/aides-infra/aides-repo-api:${DOCKER_TAG}\ + . diff --git a/cmd/aides-repo-api/main.go b/cmd/aides-repo-api/main.go index 411a2b1..61ce37c 100644 --- a/cmd/aides-repo-api/main.go +++ b/cmd/aides-repo-api/main.go @@ -22,6 +22,6 @@ func main() { } app.Init() - + defer app.Shutdown() app.Run() } diff --git a/internal/app/app.go b/internal/app/app.go index 0a39477..6bea852 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -121,10 +121,20 @@ func (app *App) Run() { }, ) - app.repo.ForceUpdate() + err := app.repo.ForceUpdate() + if err != nil { + panic(err) + } - err := http.ListenAndServe(fmt.Sprintf(":%d", app.config.Port), app.router.Setup()) + err = http.ListenAndServe( + fmt.Sprintf(":%d", app.config.Port), + app.router.Setup(), + ) if err != nil { panic(err) } } + +func (app *App) Shutdown() { + app.cron.Shutdown() +} diff --git a/internal/services/cronservice/service.go b/internal/services/cronservice/service.go index 02e8f7d..3b85f7e 100644 --- a/internal/services/cronservice/service.go +++ b/internal/services/cronservice/service.go @@ -1,15 +1,13 @@ package cronservice import ( - "log" - "github.com/go-co-op/gocron/v2" "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/logger" ) type RepoService interface { - ForceUpdate() + ForceUpdate() error } type Service struct { @@ -19,9 +17,12 @@ type Service struct { } func New(repo RepoService) *Service { + log := logger.GetLogger() scheduler, err := gocron.NewScheduler() if err != nil { - log.Fatalf("Не удалось создать планировщик: %v", err) + log.Fatal("Не удалось создать планировщик", map[string]interface{}{ + "err": err, + }) } return &Service{ @@ -31,18 +32,25 @@ func New(repo RepoService) *Service { } func (s *Service) SetupCronJobs() { + log := logger.GetLogger() _, err := s.scheduler.NewJob( gocron.CronJob("0 4 * * *", false), gocron.NewTask(func() { - log := logger.GetLogger() log.Info( "[cron] force update is started", ) - s.repoService.ForceUpdate() + err := s.repoService.ForceUpdate() + if err != nil { + log.Error("[cron] force update error", map[string]interface{}{ + "err": err, + }) + } }), ) if err != nil { - log.Printf("Не удалось создать задание cron: %v", err) + log.Fatal("Не удалось создать задание cron", map[string]interface{}{ + "err": err, + }) } } @@ -51,8 +59,11 @@ func (s *Service) Start() { } func (s *Service) Shutdown() { + log := logger.GetLogger() err := s.scheduler.Shutdown() if err != nil { - log.Printf("Не удалось корректно завершить работу планировщика: %v", err) + log.Error("Не удалось корректно завершить работу планировщика", map[string]interface{}{ + "err": err, + }) } } diff --git a/internal/services/reposervice/service.go b/internal/services/reposervice/service.go index 856d071..0548672 100644 --- a/internal/services/reposervice/service.go +++ b/internal/services/reposervice/service.go @@ -1,9 +1,7 @@ package reposervice import ( - "fmt" "os" - "os/exec" "path" "strconv" @@ -29,170 +27,160 @@ func New(db *gorm.DB, cfg Config) *Service { } } -func createSymlink(target, link string) error { - if _, err := os.Lstat(link); err == nil { - if err := os.Remove(link); err != nil { - return fmt.Errorf("failed to remove existing file or symlink: %w", err) +func (s *Service) futureRepoPathPrefix() string { + return path.Join( + s.config.GetUploadDir(), + ".future_repo", + ) +} + +func (s *Service) currentRepoPathPrefix() string { + return path.Join( + s.config.GetUploadDir(), + "repo", + ) +} + +func (s *Service) oldRepoPathPrefix() string { + return path.Join( + s.config.GetUploadDir(), + ".old_repo", + ) +} + +func (s *Service) ForceUpdate() error { + const REPO_NAME = "aides" + + architectures := []string{ + "x86_64", + "noarch", + } + + repos := []string{ + "Sisyphus", + } + + log := logger.GetLogger() + log.Info("Start repo update") + + err := os.MkdirAll(s.futureRepoPathPrefix(), os.ModePerm) + if err != nil { + return err + } + defer os.RemoveAll(s.futureRepoPathPrefix()) + + err = os.MkdirAll(s.oldRepoPathPrefix(), os.ModePerm) + if err != nil { + return err + } + defer os.RemoveAll(s.oldRepoPathPrefix()) + + err = os.MkdirAll(s.currentRepoPathPrefix(), os.ModePerm) + if err != nil { + return err + } + + for _, r := range repos { + var tasks []models.Task + altRepo := models.ALTRepo{ + Name: r, } - } - if err := os.Symlink(target, link); err != nil { - return fmt.Errorf("failed to create symlink: %w", err) - } + s.db. + Where(&altRepo). + First(&altRepo) - return nil -} + s.db. + Model(&models.GitRepoAltRepoTask{}). + Select("tasks.*"). + Joins("JOIN tasks ON tasks.id = git_repo_alt_repo_tasks.last_task_id"). + Where(&models.GitRepoAltRepoTask{ + ALTRepoID: altRepo.ID, + }). + Preload("Files"). + Find(&tasks) -func runGenbasedir(repoDir, arch, repoName string) { - log := logger.GetLogger() - cmd := exec.Command( - "genbasedir", - "--bloat", - "--progress", - fmt.Sprintf("--topdir=%s", repoDir), - arch, - repoName, - ) - cmd.Stdout = nil - cmd.Stderr = nil - err := cmd.Run() - if err != nil { - log.Error( - "Failed to run genbasedir", - map[string]interface{}{ - "arch": arch, - "error": err, - }, + futureRepoPath := path.Join( + s.futureRepoPathPrefix(), + altRepo.Name, ) - os.Exit(1) - } - log.Info("Successfully ran genbasedir", map[string]interface{}{ - "arch": arch, - }) -} -func createRepoDirs(repoDir, repoName, arch string) { - log := logger.GetLogger() - // Create the 'base' directory - baseDir := path.Join(repoDir, arch, "base") - err := os.MkdirAll(baseDir, os.ModePerm) - if err != nil { - log.Error("Failed to create directory", map[string]interface{}{ - "directory": baseDir, - "err": err, - }) - os.Exit(1) - } + err := os.MkdirAll( + futureRepoPath, + os.ModePerm, + ) + if err != nil { + return err + } - // Create the 'RPMS.' directory - rpmsDir := path.Join(repoDir, arch, fmt.Sprintf("RPMS.%s", repoName)) - err = os.MkdirAll(rpmsDir, os.ModePerm) - if err != nil { - log.Error("Failed to create directory", map[string]interface{}{ - "directory": rpmsDir, - "err": err, - }) - os.Exit(1) - } -} - -func (s *Service) ForceUpdate() { - var tasks []models.Task - altRepo := models.ALTRepo{ - Name: "Sisyphus", - } - - s.db. - Where(&altRepo). - First(&altRepo) - - s.db. - Model(&models.GitRepoAltRepoTask{}). - Select("tasks.*"). - Joins("JOIN tasks ON tasks.id = git_repo_alt_repo_tasks.last_task_id"). - Where(&models.GitRepoAltRepoTask{ - ALTRepoID: altRepo.ID, - }). - Preload("Files"). - Find(&tasks) - - repoPath := path.Join(s.config.GetUploadDir(), "future_repo", "Sisyphus") - - err := os.MkdirAll( - repoPath, - os.ModePerm, - ) - if err != nil { - panic(err) - } - - repoName := "aides" - architectures := []string{"x86_64", "noarch"} - - for _, arch := range architectures { - createRepoDirs(repoPath, repoName, arch) - } - - for _, el := range tasks { - for _, fileInfo := range el.Files { - localFilePath := path.Join( - strconv.FormatUint(uint64(el.ID), 10), fileInfo.Name, - ) - symLink := path.Join( - s.config.GetUploadDir(), - "future_repo", - "Sisyphus", - fileInfo.Arch, - "RPMS.aides", - fileInfo.Name, - ) - targetPath := path.Join("../../../../tasks/", localFilePath) - err := createSymlink(targetPath, symLink) + for _, arch := range architectures { + err = createRepoDirs(futureRepoPath, REPO_NAME, arch) if err != nil { - panic(err) + return err } } - } - for _, arch := range architectures { - runGenbasedir(repoPath, arch, repoName) - } + for _, el := range tasks { + for _, fileInfo := range el.Files { + localFilePath := path.Join( + strconv.FormatUint(uint64(el.ID), 10), fileInfo.Name, + ) + symLink := path.Join( + futureRepoPath, + fileInfo.Arch, + "RPMS.aides", + fileInfo.Name, + ) + targetPath := path.Join("../../../../tasks/", localFilePath) + err := createSymlink(targetPath, symLink) + if err != nil { + return err + } + } + } - s.db. - Model(&models.GitRepoAltRepoTask{}). - Where(&models.GitRepoAltRepoTask{ - ALTRepoID: altRepo.ID, - }). - Update( - "current_task_id", gorm.Expr("last_task_id"), + for _, arch := range architectures { + err = runGenbasedir(futureRepoPath, arch, REPO_NAME) + if err != nil { + return err + } + } + + s.db. + Model(&models.GitRepoAltRepoTask{}). + Where(&models.GitRepoAltRepoTask{ + ALTRepoID: altRepo.ID, + }). + Update( + "current_task_id", gorm.Expr("last_task_id"), + ) + + currentRepoPath := path.Join( + s.currentRepoPathPrefix(), + altRepo.Name, ) - err = os.MkdirAll(path.Join(s.config.GetUploadDir(), "repo"), os.ModePerm) - if err != nil { - panic(err) - } + oldRepoPath := path.Join( + s.oldRepoPathPrefix(), + altRepo.Name, + ) - aPath := path.Join(s.config.GetUploadDir(), "future_repo", "Sisyphus") - bPath := path.Join(s.config.GetUploadDir(), "repo", "Sisyphus") - cPath := path.Join(s.config.GetUploadDir(), "repo", ".Sisyphus") - - if _, err := os.Stat(bPath); err == nil { - if err := os.Rename(bPath, cPath); err != nil { - panic(err) + if err := renameIfExists( + currentRepoPath, + oldRepoPath, + ); err != nil { + return err } - } else if !os.IsNotExist(err) { - panic(err) + + if err := os.Rename( + futureRepoPath, + currentRepoPath, + ); err != nil { + return err + } + } - if err := os.Rename(aPath, bPath); err != nil { - panic(err) - } - - if err := os.RemoveAll(cPath); err != nil { - panic(err) - } - - if err := os.RemoveAll(path.Join(s.config.GetUploadDir(), "future_repo")); err != nil { - panic(err) - } + log.Info("Successful repo update") + return nil } diff --git a/internal/services/reposervice/utils.go b/internal/services/reposervice/utils.go new file mode 100644 index 0000000..971ff95 --- /dev/null +++ b/internal/services/reposervice/utils.go @@ -0,0 +1,91 @@ +package reposervice + +import ( + "fmt" + "os" + "os/exec" + "path" + + "code.alt-gnome.ru/aides-infra/aides-repo-api/internal/logger" +) + +func createRepoDirs(repoDir, repoName, arch string) error { + log := logger.GetLogger() + // Create the 'base' directory + baseDir := path.Join(repoDir, arch, "base") + err := os.MkdirAll(baseDir, os.ModePerm) + if err != nil { + log.Error("Failed to create directory", map[string]interface{}{ + "directory": baseDir, + "err": err, + }) + return err + } + + // Create the 'RPMS.' directory + rpmsDir := path.Join(repoDir, arch, fmt.Sprintf("RPMS.%s", repoName)) + err = os.MkdirAll(rpmsDir, os.ModePerm) + if err != nil { + log.Error("Failed to create directory", map[string]interface{}{ + "directory": rpmsDir, + "err": err, + }) + return err + } + + return nil +} + +func runGenbasedir(repoDir, arch, repoName string) error { + log := logger.GetLogger() + cmd := exec.Command( + "genbasedir", + "--bloat", + "--progress", + fmt.Sprintf("--topdir=%s", repoDir), + arch, + repoName, + ) + cmd.Stdout = nil + cmd.Stderr = nil + err := cmd.Run() + if err != nil { + log.Error( + "Failed to run genbasedir", + map[string]interface{}{ + "arch": arch, + "error": err, + "cmd_error": cmd.Stderr, + }, + ) + return err + } + log.Debug("Successfully ran genbasedir", map[string]interface{}{ + "arch": arch, + }) + + return nil +} + +func createSymlink(target, link string) error { + if _, err := os.Lstat(link); err == nil { + if err := os.Remove(link); err != nil { + return fmt.Errorf("failed to remove existing file or symlink: %w", err) + } + } + + if err := os.Symlink(target, link); err != nil { + return fmt.Errorf("failed to create symlink: %w", err) + } + + return nil +} + +func renameIfExists(oldPath, newPath string) error { + if _, err := os.Stat(oldPath); err == nil { + return os.Rename(oldPath, newPath) + } else if !os.IsNotExist(err) { + return err + } + return nil +}