mirror of
https://github.com/semaphoreui/semaphore.git
synced 2024-12-03 14:51:05 +01:00
feat(api): backup
This commit is contained in:
parent
a1a47c9af3
commit
6a2cfcc3ac
39
api/projects/backupRestore.go
Normal file
39
api/projects/backupRestore.go
Normal 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)
|
||||
}
|
@ -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
54
db/BackupEntity.go
Normal 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
214
services/project/backup.go
Normal 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
115
services/project/types.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user