feat(api): backup

This commit is contained in:
samerbahri98 2024-01-20 23:46:43 +01:00
parent a1a47c9af3
commit 6a2cfcc3ac
No known key found for this signature in database
GPG Key ID: 0EFE47488753FEE6
5 changed files with 425 additions and 1 deletions

View File

@ -0,0 +1,39 @@
package projects
import (
"net/http"
log "github.com/Sirupsen/logrus"
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/db"
projectService "github.com/ansible-semaphore/semaphore/services/project"
"github.com/gorilla/context"
)
func GetBackup(w http.ResponseWriter, r *http.Request) {
project := context.Get(r, "project").(db.Project)
store := helpers.Store(r)
backup, err := projectService.GetBackup(project.ID, store)
if err != nil {
helpers.WriteError(w, err)
return
}
helpers.WriteJSON(w, http.StatusOK, backup)
}
func Restore(w http.ResponseWriter, r *http.Request) {
var backup projectService.BackupFormat
if !helpers.Bind(w, r, &backup) {
helpers.WriteJSON(w, http.StatusBadRequest, backup)
return
}
if err := projectService.Restore(backup); err != nil {
log.Error(*err)
helpers.WriteError(w, (*err))
return
}
helpers.WriteJSON(w, http.StatusOK, nil)
}

View File

@ -2,13 +2,13 @@ package api
import (
"fmt"
"github.com/ansible-semaphore/semaphore/api/runners"
"net/http"
"os"
"strings"
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/api/projects"
"github.com/ansible-semaphore/semaphore/api/runners"
"github.com/ansible-semaphore/semaphore/api/sockets"
"github.com/ansible-semaphore/semaphore/db"
"github.com/ansible-semaphore/semaphore/util"
@ -180,6 +180,8 @@ func Route() *mux.Router {
projectUserAPI.Path("/views").HandlerFunc(projects.AddView).Methods("POST")
projectUserAPI.Path("/views/positions").HandlerFunc(projects.SetViewPositions).Methods("POST")
projectUserAPI.Path("/backup").HandlerFunc(projects.GetBackup).Methods("GET", "HEAD")
//
// Updating and deleting project
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()

54
db/BackupEntity.go Normal file
View File

@ -0,0 +1,54 @@
package db
type BackupEntity interface {
GetID() int
GetName() string
}
func (e View) GetID() int {
return e.ID
}
func (e View) GetName() string {
return e.Title
}
func (e Template) GetID() int {
return e.ID
}
func (e Template) GetName() string {
return e.Name
}
func (e Inventory) GetID() int {
return e.ID
}
func (e Inventory) GetName() string {
return e.Name
}
func (e AccessKey) GetID() int {
return e.ID
}
func (e AccessKey) GetName() string {
return e.Name
}
func (e Repository) GetID() int {
return e.ID
}
func (e Repository) GetName() string {
return e.Name
}
func (e Environment) GetID() int {
return e.ID
}
func (e Environment) GetName() string {
return e.Name
}

214
services/project/backup.go Normal file
View File

@ -0,0 +1,214 @@
package project
import (
"fmt"
"github.com/ansible-semaphore/semaphore/db"
)
func findNameByID[T db.BackupEntity](ID int, items []T) (*string, error) {
for _, o := range items {
if o.GetID() == ID {
name := o.GetName()
return &name, nil
}
}
return nil, fmt.Errorf("item %d does not exist", ID)
}
func findEntityByName[T db.BackupEntity](name *string, items []T) *T {
if name == nil {
return nil
}
for _, o := range items {
if o.GetName() == *name {
return &o
}
}
return nil
}
func getSchedulesByProject(projectID int, schedules []db.Schedule) []db.Schedule {
result := make([]db.Schedule, 0)
for _, o := range schedules {
if o.ProjectID == projectID {
result = append(result, o)
}
}
return result
}
func getScheduleByTemplate(templateID int, schedules []db.Schedule) *string {
for _, o := range schedules {
if o.TemplateID == templateID {
return &o.CronFormat
}
}
return nil
}
func (b *BackupDB) new(projectID int, store db.Store) (*BackupDB, error) {
var err error
b.templates, err = store.GetTemplates(projectID, db.TemplateFilter{}, db.RetrieveQueryParams{})
if err != nil {
return nil, err
}
b.repositories, err = store.GetRepositories(projectID, db.RetrieveQueryParams{})
if err != nil {
return nil, err
}
b.keys, err = store.GetAccessKeys(projectID, db.RetrieveQueryParams{})
if err != nil {
return nil, err
}
b.views, err = store.GetViews(projectID)
if err != nil {
return nil, err
}
b.inventories, err = store.GetInventories(projectID, db.RetrieveQueryParams{})
if err != nil {
return nil, err
}
b.environments, err = store.GetEnvironments(projectID, db.RetrieveQueryParams{})
if err != nil {
return nil, err
}
schedules, err := store.GetSchedules()
if err != nil {
return nil, err
}
b.schedules = getSchedulesByProject(projectID, schedules)
b.meta, err = store.GetProject(projectID)
if err != nil {
return nil, err
}
return b, nil
}
func (b *BackupDB) format() (*BackupFormat, error) {
keys := make([]BackupKey, len(b.keys))
for i, o := range b.keys {
keys[i] = BackupKey{
Name: o.Name,
Type: o.Type,
}
}
environments := make([]BackupEnvironment, len(b.environments))
for i, o := range b.environments {
environments[i] = BackupEnvironment{
Name: o.Name,
ENV: o.ENV,
JSON: o.JSON,
Password: o.Password,
}
}
inventories := make([]BackupInventory, len(b.inventories))
for i, o := range b.inventories {
var SSHKey *string = nil
if o.SSHKeyID != nil {
SSHKey, _ = findNameByID[db.AccessKey](*o.SSHKeyID, b.keys)
}
var BecomeKey *string = nil
if o.BecomeKeyID != nil {
BecomeKey, _ = findNameByID[db.AccessKey](*o.BecomeKeyID, b.keys)
}
inventories[i] = BackupInventory{
Name: o.Name,
Inventory: o.Inventory,
Type: o.Type,
SSHKey: SSHKey,
BecomeKey: BecomeKey,
}
}
views := make([]BackupView, len(b.views))
for i, o := range b.views {
views[i] = BackupView{
Name: o.Title,
Position: o.Position,
}
}
repositories := make([]BackupRepository, len(b.repositories))
for i, o := range b.repositories {
SSHKey, _ := findNameByID[db.AccessKey](o.SSHKeyID, b.keys)
repositories[i] = BackupRepository{
Name: o.Name,
SSHKey: SSHKey,
GitURL: o.GitURL,
GitBranch: o.GitBranch,
}
}
templates := make([]BackupTemplate, len(b.templates))
for i, o := range b.templates {
var View *string = nil
if o.ViewID != nil {
View, _ = findNameByID[db.View](*o.ViewID, b.views)
}
var VaultKey *string = nil
if o.VaultKeyID != nil {
VaultKey, _ = findNameByID[db.AccessKey](*o.VaultKeyID, b.keys)
}
var Environment *string = nil
if o.EnvironmentID != nil {
Environment, _ = findNameByID[db.Environment](*o.EnvironmentID, b.environments)
}
var BuildTemplate *string = nil
if o.BuildTemplateID != nil {
BuildTemplate, _ = findNameByID[db.Template](*o.BuildTemplateID, b.templates)
}
Repository, _ := findNameByID[db.Repository](o.RepositoryID, b.repositories)
Inventory, _ := findNameByID[db.Inventory](o.InventoryID, b.inventories)
templates[i] = BackupTemplate{
Name: o.Name,
AllowOverrideArgsInTask: o.AllowOverrideArgsInTask,
Arguments: o.Arguments,
Autorun: o.Autorun,
Description: o.Description,
Playbook: o.Playbook,
StartVersion: o.StartVersion,
SuppressSuccessAlerts: o.SuppressSuccessAlerts,
SurveyVars: o.SurveyVarsJSON,
Type: o.Type,
View: View,
VaultKey: VaultKey,
Repository: *Repository,
Inventory: *Inventory,
Environment: Environment,
BuildTemplate: BuildTemplate,
Cron: getScheduleByTemplate(o.ID, b.schedules),
}
}
return &BackupFormat{
Meta: BackupMeta{
Name: b.meta.Name,
MaxParallelTasks: b.meta.MaxParallelTasks,
Alert: b.meta.Alert,
AlertChat: b.meta.AlertChat,
},
Inventories: inventories,
Environments: environments,
Views: views,
Repositories: repositories,
Keys: keys,
Templates: templates,
}, nil
}
func GetBackup(projectID int, store db.Store) (*BackupFormat, error) {
backup := BackupDB{}
if _, err := backup.new(projectID, store); err != nil {
return nil, err
}
return backup.format()
}

115
services/project/types.go Normal file
View File

@ -0,0 +1,115 @@
package project
import (
"github.com/ansible-semaphore/semaphore/db"
)
type BackupDB struct {
meta db.Project
templates []db.Template
repositories []db.Repository
keys []db.AccessKey
views []db.View
inventories []db.Inventory
environments []db.Environment
schedules []db.Schedule
}
type BackupFormat struct {
Meta BackupMeta `json:"meta"`
Templates []BackupTemplate `json:"templates"`
Repositories []BackupRepository `json:"repositories"`
Keys []BackupKey `json:"keys"`
Views []BackupView `json:"views"`
Inventories []BackupInventory `json:"inventories"`
Environments []BackupEnvironment `json:"environments"`
}
type BackupMeta struct {
Name string `json:"name"`
Alert bool `json:"alert"`
AlertChat *string `json:"alert_chat"`
MaxParallelTasks int `json:"max_parallel_tasks"`
}
type BackupEnvironment struct {
Name string `json:"name"`
Password *string `json:"password"`
JSON string `json:"json"`
ENV *string `json:"env"`
}
type BackupKey struct {
Name string `json:"name"`
Type db.AccessKeyType `json:"type"`
}
type BackupView struct {
Name string `json:"name"`
Position int `json:"position"`
}
type BackupInventory struct {
Name string `json:"name"`
Inventory string `json:"inventory"`
SSHKey *string `json:"ssh_key"`
BecomeKey *string `json:"become_key"`
Type string `json:"type"`
}
type BackupRepository struct {
Name string `json:"name"`
GitURL string `json:"git_url"`
GitBranch string `json:"git_branch"`
SSHKey *string `json:"ssh_key"`
}
type BackupTemplate struct {
Inventory string `json:"inventory"`
Repository string `json:"repository"`
Environment *string `json:"environment"`
Name string `json:"name"`
Playbook string `json:"playbook"`
Arguments *string `json:"arguments"`
AllowOverrideArgsInTask bool `json:"allow_override_args_in_task"`
Description *string `json:"description"`
VaultKey *string `json:"vault_key"`
Type db.TemplateType `json:"type"`
StartVersion *string `json:"start_version"`
BuildTemplate *string `json:"build_template"`
View *string `json:"view"`
Autorun bool `json:"autorun"`
SurveyVars *string `json:"survey_vars"`
SuppressSuccessAlerts bool `json:"suppress_success_alerts"`
Cron *string `json:"cron"`
}
type BackupEntry interface {
GetName() string
Verify(backup *BackupFormat) error
Restore(store db.Store, b *BackupDB) error
}
func (e BackupEnvironment) GetName() string {
return e.Name
}
func (e BackupInventory) GetName() string {
return e.Name
}
func (e BackupKey) GetName() string {
return e.Name
}
func (e BackupRepository) GetName() string {
return e.Name
}
func (e BackupView) GetName() string {
return e.Name
}
func (e BackupTemplate) GetName() string {
return e.Name
}