mirror of
https://github.com/semaphoreui/semaphore.git
synced 2025-01-20 15:29:28 +01:00
feat: remove soft delete functionality
This commit is contained in:
parent
6cc3d0f250
commit
05dd7c5653
@ -39,7 +39,7 @@ aliases:
|
|||||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash
|
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash
|
||||||
export NVM_DIR="$HOME/.nvm"
|
export NVM_DIR="$HOME/.nvm"
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||||
nvm install 12.15.0 && nvm alias default 12.15.0
|
nvm install 12.15.0 && nvm name default 12.15.0
|
||||||
# Each step uses the same `$BASH_ENV`, so need to modify it
|
# Each step uses the same `$BASH_ENV`, so need to modify it
|
||||||
echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
|
echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
|
||||||
echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV
|
echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV
|
||||||
|
@ -100,7 +100,7 @@ func resolveCapability(caps []string, resolved []string, uid string) {
|
|||||||
case "template":
|
case "template":
|
||||||
res, err := store.Sql().Exec(
|
res, err := store.Sql().Exec(
|
||||||
"insert into project__template "+
|
"insert into project__template "+
|
||||||
"(project_id, inventory_id, repository_id, environment_id, alias, playbook, arguments, allow_override_args_in_task, description, view_id) "+
|
"(project_id, inventory_id, repository_id, environment_id, name, playbook, arguments, allow_override_args_in_task, description, view_id) "+
|
||||||
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
userProject.ID, inventoryID, repoID, environmentID, "Test-"+uid, "test-playbook.yml", "", false, "Hello, World!", view.ID)
|
userProject.ID, inventoryID, repoID, environmentID, "Test-"+uid, "test-playbook.yml", "", false, "Hello, World!", view.ID)
|
||||||
printError(err)
|
printError(err)
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,7 +14,8 @@ web2/dist/**/*
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
.idea/
|
/.idea/
|
||||||
|
/semaphore.iml
|
||||||
/bin/
|
/bin/
|
||||||
|
|
||||||
*-packr.go
|
*-packr.go
|
||||||
|
@ -326,7 +326,7 @@ definitions:
|
|||||||
view_id:
|
view_id:
|
||||||
type: integer
|
type: integer
|
||||||
minimum: 1
|
minimum: 1
|
||||||
alias:
|
name:
|
||||||
type: string
|
type: string
|
||||||
example: Test
|
example: Test
|
||||||
playbook:
|
playbook:
|
||||||
@ -361,7 +361,7 @@ definitions:
|
|||||||
view_id:
|
view_id:
|
||||||
type: integer
|
type: integer
|
||||||
minimum: 1
|
minimum: 1
|
||||||
alias:
|
name:
|
||||||
type: string
|
type: string
|
||||||
example: Test
|
example: Test
|
||||||
playbook:
|
playbook:
|
||||||
@ -1221,7 +1221,7 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
description: sorting name
|
description: sorting name
|
||||||
enum: [alias, playbook, ssh_key, inventory, environment, repository]
|
enum: [name, playbook, ssh_key, inventory, environment, repository]
|
||||||
- name: order
|
- name: order
|
||||||
in: query
|
in: query
|
||||||
required: true
|
required: true
|
||||||
|
@ -31,6 +31,17 @@ func EnvironmentMiddleware(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetEnvironmentRefs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
env := context.Get(r, "environment").(db.Environment)
|
||||||
|
refs, err := helpers.Store(r).GetEnvironmentRefs(env.ProjectID, env.ID)
|
||||||
|
if err != nil {
|
||||||
|
helpers.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers.WriteJSON(w, http.StatusOK, refs)
|
||||||
|
}
|
||||||
|
|
||||||
// GetEnvironment retrieves sorted environments from the database
|
// GetEnvironment retrieves sorted environments from the database
|
||||||
func GetEnvironment(w http.ResponseWriter, r *http.Request) {
|
func GetEnvironment(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
@ -129,11 +140,6 @@ func RemoveEnvironment(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
softDeletion := r.URL.Query().Get("setRemoved") == "1"
|
|
||||||
|
|
||||||
if softDeletion {
|
|
||||||
err = helpers.Store(r).DeleteEnvironmentSoft(env.ProjectID, env.ID)
|
|
||||||
} else {
|
|
||||||
err = helpers.Store(r).DeleteEnvironment(env.ProjectID, env.ID)
|
err = helpers.Store(r).DeleteEnvironment(env.ProjectID, env.ID)
|
||||||
if err == db.ErrInvalidOperation {
|
if err == db.ErrInvalidOperation {
|
||||||
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
||||||
@ -142,7 +148,6 @@ func RemoveEnvironment(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.WriteError(w, err)
|
helpers.WriteError(w, err)
|
||||||
|
@ -34,6 +34,17 @@ func InventoryMiddleware(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetInventoryRefs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
inventory := context.Get(r, "inventory").(db.Inventory)
|
||||||
|
refs, err := helpers.Store(r).GetInventoryRefs(inventory.ProjectID, inventory.ID)
|
||||||
|
if err != nil {
|
||||||
|
helpers.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers.WriteJSON(w, http.StatusOK, refs)
|
||||||
|
}
|
||||||
|
|
||||||
// GetInventory returns an inventory from the database
|
// GetInventory returns an inventory from the database
|
||||||
func GetInventory(w http.ResponseWriter, r *http.Request) {
|
func GetInventory(w http.ResponseWriter, r *http.Request) {
|
||||||
if inventory := context.Get(r, "inventory"); inventory != nil {
|
if inventory := context.Get(r, "inventory"); inventory != nil {
|
||||||
@ -180,11 +191,6 @@ func RemoveInventory(w http.ResponseWriter, r *http.Request) {
|
|||||||
inventory := context.Get(r, "inventory").(db.Inventory)
|
inventory := context.Get(r, "inventory").(db.Inventory)
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
softDeletion := r.URL.Query().Get("setRemoved") == "1"
|
|
||||||
|
|
||||||
if softDeletion {
|
|
||||||
err = helpers.Store(r).DeleteInventorySoft(inventory.ProjectID, inventory.ID)
|
|
||||||
} else {
|
|
||||||
err = helpers.Store(r).DeleteInventory(inventory.ProjectID, inventory.ID)
|
err = helpers.Store(r).DeleteInventory(inventory.ProjectID, inventory.ID)
|
||||||
if err == db.ErrInvalidOperation {
|
if err == db.ErrInvalidOperation {
|
||||||
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
||||||
@ -193,7 +199,6 @@ func RemoveInventory(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.WriteError(w, err)
|
helpers.WriteError(w, err)
|
||||||
|
@ -30,6 +30,17 @@ func KeyMiddleware(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetKeyRefs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
key := context.Get(r, "accessKey").(db.AccessKey)
|
||||||
|
refs, err := helpers.Store(r).GetAccessKeyRefs(*key.ProjectID, key.ID)
|
||||||
|
if err != nil {
|
||||||
|
helpers.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers.WriteJSON(w, http.StatusOK, refs)
|
||||||
|
}
|
||||||
|
|
||||||
// GetKeys retrieves sorted keys from the database
|
// GetKeys retrieves sorted keys from the database
|
||||||
func GetKeys(w http.ResponseWriter, r *http.Request) {
|
func GetKeys(w http.ResponseWriter, r *http.Request) {
|
||||||
if key := context.Get(r, "accessKey"); key != nil {
|
if key := context.Get(r, "accessKey"); key != nil {
|
||||||
@ -161,11 +172,6 @@ func RemoveKey(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
softDeletion := r.URL.Query().Get("setRemoved") == "1"
|
|
||||||
|
|
||||||
if softDeletion {
|
|
||||||
err = helpers.Store(r).DeleteAccessKeySoft(*key.ProjectID, key.ID)
|
|
||||||
} else {
|
|
||||||
err = helpers.Store(r).DeleteAccessKey(*key.ProjectID, key.ID)
|
err = helpers.Store(r).DeleteAccessKey(*key.ProjectID, key.ID)
|
||||||
if err == db.ErrInvalidOperation {
|
if err == db.ErrInvalidOperation {
|
||||||
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
||||||
@ -174,7 +180,6 @@ func RemoveKey(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.WriteError(w, err)
|
helpers.WriteError(w, err)
|
||||||
|
@ -30,6 +30,17 @@ func RepositoryMiddleware(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetRepositoryRefs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
repo := context.Get(r, "repository").(db.Repository)
|
||||||
|
refs, err := helpers.Store(r).GetRepositoryRefs(repo.ProjectID, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
helpers.WriteError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers.WriteJSON(w, http.StatusOK, refs)
|
||||||
|
}
|
||||||
|
|
||||||
// GetRepositories returns all repositories in a project sorted by type
|
// GetRepositories returns all repositories in a project sorted by type
|
||||||
func GetRepositories(w http.ResponseWriter, r *http.Request) {
|
func GetRepositories(w http.ResponseWriter, r *http.Request) {
|
||||||
if repo := context.Get(r, "repository"); repo != nil {
|
if repo := context.Get(r, "repository"); repo != nil {
|
||||||
@ -152,11 +163,6 @@ func RemoveRepository(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
softDeletion := r.URL.Query().Get("setRemoved") == "1"
|
|
||||||
|
|
||||||
if softDeletion {
|
|
||||||
err = helpers.Store(r).DeleteRepositorySoft(repository.ProjectID, repository.ID)
|
|
||||||
} else {
|
|
||||||
err = helpers.Store(r).DeleteRepository(repository.ProjectID, repository.ID)
|
err = helpers.Store(r).DeleteRepository(repository.ProjectID, repository.ID)
|
||||||
if err == db.ErrInvalidOperation {
|
if err == db.ErrInvalidOperation {
|
||||||
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
helpers.WriteJSON(w, http.StatusBadRequest, map[string]interface{}{
|
||||||
@ -165,7 +171,6 @@ func RemoveRepository(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helpers.WriteError(w, err)
|
helpers.WriteError(w, err)
|
||||||
|
@ -161,6 +161,7 @@ func Route() *mux.Router {
|
|||||||
projectKeyManagement.Use(projects.KeyMiddleware)
|
projectKeyManagement.Use(projects.KeyMiddleware)
|
||||||
|
|
||||||
projectKeyManagement.HandleFunc("/{key_id}", projects.GetKeys).Methods("GET", "HEAD")
|
projectKeyManagement.HandleFunc("/{key_id}", projects.GetKeys).Methods("GET", "HEAD")
|
||||||
|
projectKeyManagement.HandleFunc("/{key_id}/refs", projects.GetKeyRefs).Methods("GET", "HEAD")
|
||||||
projectKeyManagement.HandleFunc("/{key_id}", projects.UpdateKey).Methods("PUT")
|
projectKeyManagement.HandleFunc("/{key_id}", projects.UpdateKey).Methods("PUT")
|
||||||
projectKeyManagement.HandleFunc("/{key_id}", projects.RemoveKey).Methods("DELETE")
|
projectKeyManagement.HandleFunc("/{key_id}", projects.RemoveKey).Methods("DELETE")
|
||||||
|
|
||||||
@ -168,6 +169,7 @@ func Route() *mux.Router {
|
|||||||
projectRepoManagement.Use(projects.RepositoryMiddleware)
|
projectRepoManagement.Use(projects.RepositoryMiddleware)
|
||||||
|
|
||||||
projectRepoManagement.HandleFunc("/{repository_id}", projects.GetRepositories).Methods("GET", "HEAD")
|
projectRepoManagement.HandleFunc("/{repository_id}", projects.GetRepositories).Methods("GET", "HEAD")
|
||||||
|
projectRepoManagement.HandleFunc("/{repository_id}/refs", projects.GetRepositoryRefs).Methods("GET", "HEAD")
|
||||||
projectRepoManagement.HandleFunc("/{repository_id}", projects.UpdateRepository).Methods("PUT")
|
projectRepoManagement.HandleFunc("/{repository_id}", projects.UpdateRepository).Methods("PUT")
|
||||||
projectRepoManagement.HandleFunc("/{repository_id}", projects.RemoveRepository).Methods("DELETE")
|
projectRepoManagement.HandleFunc("/{repository_id}", projects.RemoveRepository).Methods("DELETE")
|
||||||
|
|
||||||
@ -175,6 +177,7 @@ func Route() *mux.Router {
|
|||||||
projectInventoryManagement.Use(projects.InventoryMiddleware)
|
projectInventoryManagement.Use(projects.InventoryMiddleware)
|
||||||
|
|
||||||
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.GetInventory).Methods("GET", "HEAD")
|
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.GetInventory).Methods("GET", "HEAD")
|
||||||
|
projectInventoryManagement.HandleFunc("/{inventory_id}/refs", projects.GetInventoryRefs).Methods("GET", "HEAD")
|
||||||
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.UpdateInventory).Methods("PUT")
|
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.UpdateInventory).Methods("PUT")
|
||||||
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.RemoveInventory).Methods("DELETE")
|
projectInventoryManagement.HandleFunc("/{inventory_id}", projects.RemoveInventory).Methods("DELETE")
|
||||||
|
|
||||||
@ -182,6 +185,7 @@ func Route() *mux.Router {
|
|||||||
projectEnvManagement.Use(projects.EnvironmentMiddleware)
|
projectEnvManagement.Use(projects.EnvironmentMiddleware)
|
||||||
|
|
||||||
projectEnvManagement.HandleFunc("/{environment_id}", projects.GetEnvironment).Methods("GET", "HEAD")
|
projectEnvManagement.HandleFunc("/{environment_id}", projects.GetEnvironment).Methods("GET", "HEAD")
|
||||||
|
projectEnvManagement.HandleFunc("/{environment_id}/refs", projects.GetEnvironmentRefs).Methods("GET", "HEAD")
|
||||||
projectEnvManagement.HandleFunc("/{environment_id}", projects.UpdateEnvironment).Methods("PUT")
|
projectEnvManagement.HandleFunc("/{environment_id}", projects.UpdateEnvironment).Methods("PUT")
|
||||||
projectEnvManagement.HandleFunc("/{environment_id}", projects.RemoveEnvironment).Methods("DELETE")
|
projectEnvManagement.HandleFunc("/{environment_id}", projects.RemoveEnvironment).Methods("DELETE")
|
||||||
|
|
||||||
|
@ -38,8 +38,6 @@ type AccessKey struct {
|
|||||||
// You should use methods SerializeSecret to fill this field.
|
// You should use methods SerializeSecret to fill this field.
|
||||||
Secret *string `db:"secret" json:"-"`
|
Secret *string `db:"secret" json:"-"`
|
||||||
|
|
||||||
Removed bool `db:"removed" json:"removed"`
|
|
||||||
|
|
||||||
LoginPassword LoginPassword `db:"-" json:"login_password"`
|
LoginPassword LoginPassword `db:"-" json:"login_password"`
|
||||||
SshKey SshKey `db:"-" json:"ssh"`
|
SshKey SshKey `db:"-" json:"ssh"`
|
||||||
PAT string `db:"-" json:"pat"`
|
PAT string `db:"-" json:"pat"`
|
||||||
|
@ -11,7 +11,6 @@ type Environment struct {
|
|||||||
ProjectID int `db:"project_id" json:"project_id"`
|
ProjectID int `db:"project_id" json:"project_id"`
|
||||||
Password *string `db:"password" json:"password"`
|
Password *string `db:"password" json:"password"`
|
||||||
JSON string `db:"json" json:"json" binding:"required"`
|
JSON string `db:"json" json:"json" binding:"required"`
|
||||||
Removed bool `db:"removed" json:"removed"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *Environment) Validate() error {
|
func (env *Environment) Validate() error {
|
||||||
|
@ -4,6 +4,7 @@ const (
|
|||||||
InventoryStatic = "static"
|
InventoryStatic = "static"
|
||||||
InventoryFile = "file"
|
InventoryFile = "file"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Inventory is the model of an ansible inventory file
|
// Inventory is the model of an ansible inventory file
|
||||||
type Inventory struct {
|
type Inventory struct {
|
||||||
ID int `db:"id" json:"id"`
|
ID int `db:"id" json:"id"`
|
||||||
@ -20,8 +21,6 @@ type Inventory struct {
|
|||||||
|
|
||||||
// static/file
|
// static/file
|
||||||
Type string `db:"type" json:"type"`
|
Type string `db:"type" json:"type"`
|
||||||
|
|
||||||
Removed bool `db:"removed" json:"removed"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FillInventory(d Store, inventory *Inventory) (err error) {
|
func FillInventory(d Store, inventory *Inventory) (err error) {
|
||||||
|
@ -54,6 +54,7 @@ func GetMigrations() []Migration {
|
|||||||
{Version: "2.8.36"},
|
{Version: "2.8.36"},
|
||||||
{Version: "2.8.38"},
|
{Version: "2.8.38"},
|
||||||
{Version: "2.8.39"},
|
{Version: "2.8.39"},
|
||||||
|
{Version: "2.8.40"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
203
db/Store.go
203
db/Store.go
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,18 +40,38 @@ type RetrieveQueryParams struct {
|
|||||||
SortInverted bool
|
SortInverted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectProperties describe database entities.
|
type ObjectReferrerType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ObjectReferrerTemplate ObjectReferrerType = "template"
|
||||||
|
ObjectReferrerInventory ObjectReferrerType = "inventory"
|
||||||
|
ObjectReferrerRepository ObjectReferrerType = "repository"
|
||||||
|
ObjectReferrerSchedule ObjectReferrerType = "schedules"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ObjectReferrer struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjectReferrers struct {
|
||||||
|
Templates []ObjectReferrer `json:"templates"`
|
||||||
|
Inventories []ObjectReferrer `json:"inventories"`
|
||||||
|
Repositories []ObjectReferrer `json:"repositories"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectProps describe database entities.
|
||||||
// It mainly used for NoSQL implementations (currently BoltDB) to preserve same
|
// It mainly used for NoSQL implementations (currently BoltDB) to preserve same
|
||||||
// data structure of different implementations and easy change it if required.
|
// data structure of different implementations and easy change it if required.
|
||||||
type ObjectProperties struct {
|
type ObjectProps struct {
|
||||||
TableName string
|
TableName string
|
||||||
|
Type reflect.Type // to which type the table bust be mapped.
|
||||||
IsGlobal bool // doesn't belong to other table, for example to project or user.
|
IsGlobal bool // doesn't belong to other table, for example to project or user.
|
||||||
ForeignColumnSuffix string
|
ReferringColumnSuffix string
|
||||||
PrimaryColumnName string
|
PrimaryColumnName string
|
||||||
SortableColumns []string
|
SortableColumns []string
|
||||||
DefaultSortingColumn string
|
DefaultSortingColumn string
|
||||||
SortInverted bool // sort from high to low object ID by default. It is useful for some NoSQL implementations.
|
SortInverted bool // sort from high to low object ID by default. It is useful for some NoSQL implementations.
|
||||||
Type reflect.Type // to which type the table bust be mapped.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNotFound = errors.New("no rows in result set")
|
var ErrNotFound = errors.New("no rows in result set")
|
||||||
@ -76,38 +97,38 @@ type Store interface {
|
|||||||
IsMigrationApplied(version Migration) (bool, error)
|
IsMigrationApplied(version Migration) (bool, error)
|
||||||
// ApplyMigration runs executes a database migration
|
// ApplyMigration runs executes a database migration
|
||||||
ApplyMigration(version Migration) error
|
ApplyMigration(version Migration) error
|
||||||
// TryRollbackMigration attempts to rollback the database to an earlier version
|
// TryRollbackMigration attempts to roll back the database to an earlier version
|
||||||
// if a rollback exists
|
// if a rollback exists
|
||||||
TryRollbackMigration(version Migration)
|
TryRollbackMigration(version Migration)
|
||||||
|
|
||||||
GetEnvironment(projectID int, environmentID int) (Environment, error)
|
GetEnvironment(projectID int, environmentID int) (Environment, error)
|
||||||
|
GetEnvironmentRefs(projectID int, environmentID int) (ObjectReferrers, error)
|
||||||
GetEnvironments(projectID int, params RetrieveQueryParams) ([]Environment, error)
|
GetEnvironments(projectID int, params RetrieveQueryParams) ([]Environment, error)
|
||||||
UpdateEnvironment(env Environment) error
|
UpdateEnvironment(env Environment) error
|
||||||
CreateEnvironment(env Environment) (Environment, error)
|
CreateEnvironment(env Environment) (Environment, error)
|
||||||
DeleteEnvironment(projectID int, templateID int) error
|
DeleteEnvironment(projectID int, templateID int) error
|
||||||
DeleteEnvironmentSoft(projectID int, templateID int) error
|
|
||||||
|
|
||||||
GetInventory(projectID int, inventoryID int) (Inventory, error)
|
GetInventory(projectID int, inventoryID int) (Inventory, error)
|
||||||
|
GetInventoryRefs(projectID int, inventoryID int) (ObjectReferrers, error)
|
||||||
GetInventories(projectID int, params RetrieveQueryParams) ([]Inventory, error)
|
GetInventories(projectID int, params RetrieveQueryParams) ([]Inventory, error)
|
||||||
UpdateInventory(inventory Inventory) error
|
UpdateInventory(inventory Inventory) error
|
||||||
CreateInventory(inventory Inventory) (Inventory, error)
|
CreateInventory(inventory Inventory) (Inventory, error)
|
||||||
DeleteInventory(projectID int, inventoryID int) error
|
DeleteInventory(projectID int, inventoryID int) error
|
||||||
DeleteInventorySoft(projectID int, inventoryID int) error
|
|
||||||
|
|
||||||
GetRepository(projectID int, repositoryID int) (Repository, error)
|
GetRepository(projectID int, repositoryID int) (Repository, error)
|
||||||
|
GetRepositoryRefs(projectID int, repositoryID int) (ObjectReferrers, error)
|
||||||
GetRepositories(projectID int, params RetrieveQueryParams) ([]Repository, error)
|
GetRepositories(projectID int, params RetrieveQueryParams) ([]Repository, error)
|
||||||
UpdateRepository(repository Repository) error
|
UpdateRepository(repository Repository) error
|
||||||
CreateRepository(repository Repository) (Repository, error)
|
CreateRepository(repository Repository) (Repository, error)
|
||||||
DeleteRepository(projectID int, repositoryID int) error
|
DeleteRepository(projectID int, repositoryID int) error
|
||||||
DeleteRepositorySoft(projectID int, repositoryID int) error
|
|
||||||
|
|
||||||
GetAccessKey(projectID int, accessKeyID int) (AccessKey, error)
|
GetAccessKey(projectID int, accessKeyID int) (AccessKey, error)
|
||||||
|
GetAccessKeyRefs(projectID int, accessKeyID int) (ObjectReferrers, error)
|
||||||
GetAccessKeys(projectID int, params RetrieveQueryParams) ([]AccessKey, error)
|
GetAccessKeys(projectID int, params RetrieveQueryParams) ([]AccessKey, error)
|
||||||
|
|
||||||
UpdateAccessKey(accessKey AccessKey) error
|
UpdateAccessKey(accessKey AccessKey) error
|
||||||
CreateAccessKey(accessKey AccessKey) (AccessKey, error)
|
CreateAccessKey(accessKey AccessKey) (AccessKey, error)
|
||||||
DeleteAccessKey(projectID int, accessKeyID int) error
|
DeleteAccessKey(projectID int, accessKeyID int) error
|
||||||
DeleteAccessKeySoft(projectID int, accessKeyID int) error
|
|
||||||
|
|
||||||
GetUsers(params RetrieveQueryParams) ([]User, error)
|
GetUsers(params RetrieveQueryParams) ([]User, error)
|
||||||
CreateUserWithoutPassword(user User) (User, error)
|
CreateUserWithoutPassword(user User) (User, error)
|
||||||
@ -179,104 +200,128 @@ type Store interface {
|
|||||||
SetViewPositions(projectID int, viewPositions map[int]int) error
|
SetViewPositions(projectID int, viewPositions map[int]int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
var AccessKeyProps = ObjectProperties{
|
var AccessKeyProps = ObjectProps{
|
||||||
TableName: "access_key",
|
TableName: "access_key",
|
||||||
SortableColumns: []string{"name", "type"},
|
|
||||||
ForeignColumnSuffix: "key_id",
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(AccessKey{}),
|
Type: reflect.TypeOf(AccessKey{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
ReferringColumnSuffix: "key_id",
|
||||||
|
SortableColumns: []string{"name", "type"},
|
||||||
DefaultSortingColumn: "name",
|
DefaultSortingColumn: "name",
|
||||||
}
|
}
|
||||||
|
|
||||||
var EnvironmentProps = ObjectProperties{
|
var EnvironmentProps = ObjectProps{
|
||||||
TableName: "project__environment",
|
TableName: "project__environment",
|
||||||
SortableColumns: []string{"name"},
|
|
||||||
ForeignColumnSuffix: "environment_id",
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(Environment{}),
|
Type: reflect.TypeOf(Environment{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
ReferringColumnSuffix: "environment_id",
|
||||||
|
SortableColumns: []string{"name"},
|
||||||
DefaultSortingColumn: "name",
|
DefaultSortingColumn: "name",
|
||||||
}
|
}
|
||||||
|
|
||||||
var InventoryProps = ObjectProperties{
|
var InventoryProps = ObjectProps{
|
||||||
TableName: "project__inventory",
|
TableName: "project__inventory",
|
||||||
SortableColumns: []string{"name"},
|
|
||||||
ForeignColumnSuffix: "inventory_id",
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(Inventory{}),
|
Type: reflect.TypeOf(Inventory{}),
|
||||||
DefaultSortingColumn: "name",
|
|
||||||
}
|
|
||||||
|
|
||||||
var RepositoryProps = ObjectProperties{
|
|
||||||
TableName: "project__repository",
|
|
||||||
ForeignColumnSuffix: "repository_id",
|
|
||||||
PrimaryColumnName: "id",
|
PrimaryColumnName: "id",
|
||||||
Type: reflect.TypeOf(Repository{}),
|
ReferringColumnSuffix: "inventory_id",
|
||||||
DefaultSortingColumn: "name",
|
|
||||||
}
|
|
||||||
|
|
||||||
var TemplateProps = ObjectProperties{
|
|
||||||
TableName: "project__template",
|
|
||||||
SortableColumns: []string{"name"},
|
SortableColumns: []string{"name"},
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(Template{}),
|
|
||||||
DefaultSortingColumn: "alias",
|
|
||||||
}
|
|
||||||
|
|
||||||
var ScheduleProps = ObjectProperties{
|
|
||||||
TableName: "project__schedule",
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(Schedule{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProjectUserProps = ObjectProperties{
|
|
||||||
TableName: "project__user",
|
|
||||||
PrimaryColumnName: "user_id",
|
|
||||||
Type: reflect.TypeOf(ProjectUser{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProjectProps = ObjectProperties{
|
|
||||||
TableName: "project",
|
|
||||||
IsGlobal: true,
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(Project{}),
|
|
||||||
DefaultSortingColumn: "name",
|
DefaultSortingColumn: "name",
|
||||||
}
|
}
|
||||||
|
|
||||||
var UserProps = ObjectProperties{
|
var RepositoryProps = ObjectProps{
|
||||||
|
TableName: "project__repository",
|
||||||
|
Type: reflect.TypeOf(Repository{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
ReferringColumnSuffix: "repository_id",
|
||||||
|
DefaultSortingColumn: "name",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TemplateProps = ObjectProps{
|
||||||
|
TableName: "project__template",
|
||||||
|
Type: reflect.TypeOf(Template{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
SortableColumns: []string{"name"},
|
||||||
|
DefaultSortingColumn: "name",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ScheduleProps = ObjectProps{
|
||||||
|
TableName: "project__schedule",
|
||||||
|
Type: reflect.TypeOf(Schedule{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ProjectUserProps = ObjectProps{
|
||||||
|
TableName: "project__user",
|
||||||
|
Type: reflect.TypeOf(ProjectUser{}),
|
||||||
|
PrimaryColumnName: "user_id",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ProjectProps = ObjectProps{
|
||||||
|
TableName: "project",
|
||||||
|
Type: reflect.TypeOf(Project{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
DefaultSortingColumn: "name",
|
||||||
|
IsGlobal: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var UserProps = ObjectProps{
|
||||||
TableName: "user",
|
TableName: "user",
|
||||||
IsGlobal: true,
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(User{}),
|
Type: reflect.TypeOf(User{}),
|
||||||
}
|
|
||||||
|
|
||||||
var SessionProps = ObjectProperties{
|
|
||||||
TableName: "session",
|
|
||||||
PrimaryColumnName: "id",
|
PrimaryColumnName: "id",
|
||||||
Type: reflect.TypeOf(Session{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
var TokenProps = ObjectProperties{
|
|
||||||
TableName: "user__token",
|
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(APIToken{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
var TaskProps = ObjectProperties{
|
|
||||||
TableName: "task",
|
|
||||||
IsGlobal: true,
|
IsGlobal: true,
|
||||||
PrimaryColumnName: "id",
|
|
||||||
SortInverted: true,
|
|
||||||
Type: reflect.TypeOf(Task{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var TaskOutputProps = ObjectProperties{
|
var SessionProps = ObjectProps{
|
||||||
|
TableName: "session",
|
||||||
|
Type: reflect.TypeOf(Session{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TokenProps = ObjectProps{
|
||||||
|
TableName: "user__token",
|
||||||
|
Type: reflect.TypeOf(APIToken{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TaskProps = ObjectProps{
|
||||||
|
TableName: "task",
|
||||||
|
Type: reflect.TypeOf(Task{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
|
IsGlobal: true,
|
||||||
|
SortInverted: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var TaskOutputProps = ObjectProps{
|
||||||
TableName: "task__output",
|
TableName: "task__output",
|
||||||
Type: reflect.TypeOf(TaskOutput{}),
|
Type: reflect.TypeOf(TaskOutput{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
var ViewProps = ObjectProperties{
|
var ViewProps = ObjectProps{
|
||||||
TableName: "project__view",
|
TableName: "project__view",
|
||||||
PrimaryColumnName: "id",
|
|
||||||
Type: reflect.TypeOf(View{}),
|
Type: reflect.TypeOf(View{}),
|
||||||
|
PrimaryColumnName: "id",
|
||||||
DefaultSortingColumn: "position",
|
DefaultSortingColumn: "position",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p ObjectProps) GetReferringFieldsFrom(t reflect.Type) (fields []string, err error) {
|
||||||
|
n := t.NumField()
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if !strings.HasSuffix(t.Field(i).Tag.Get("db"), p.ReferringColumnSuffix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields = append(fields, t.Field(i).Tag.Get("db"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if t.Field(i).Tag != "" || t.Field(i).Type.Kind() != reflect.Struct {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var nested []string
|
||||||
|
nested, err = p.GetReferringFieldsFrom(t.Field(i).Type)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields = append(fields, nested...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -42,8 +42,8 @@ type Template struct {
|
|||||||
RepositoryID int `db:"repository_id" json:"repository_id"`
|
RepositoryID int `db:"repository_id" json:"repository_id"`
|
||||||
EnvironmentID *int `db:"environment_id" json:"environment_id"`
|
EnvironmentID *int `db:"environment_id" json:"environment_id"`
|
||||||
|
|
||||||
// Alias as described in https://github.com/ansible-semaphore/semaphore/issues/188
|
// Name as described in https://github.com/ansible-semaphore/semaphore/issues/188
|
||||||
Alias string `db:"alias" json:"alias"`
|
Name string `db:"name" json:"name"`
|
||||||
// playbook name in the form of "some_play.yml"
|
// playbook name in the form of "some_play.yml"
|
||||||
Playbook string `db:"playbook" json:"playbook"`
|
Playbook string `db:"playbook" json:"playbook"`
|
||||||
// to fit into []string
|
// to fit into []string
|
||||||
@ -74,8 +74,8 @@ type Template struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tpl *Template) Validate() error {
|
func (tpl *Template) Validate() error {
|
||||||
if tpl.Alias == "" {
|
if tpl.Name == "" {
|
||||||
return &ValidationError{"template alias can not be empty"}
|
return &ValidationError{"template name can not be empty"}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tpl.Playbook == "" {
|
if tpl.Playbook == "" {
|
||||||
|
@ -52,7 +52,7 @@ func (d strObjectID) ToBytes() []byte {
|
|||||||
return []byte(d)
|
return []byte(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeBucketId(props db.ObjectProperties, ids ...int) []byte {
|
func makeBucketId(props db.ObjectProps, ids ...int) []byte {
|
||||||
n := len(ids)
|
n := len(ids)
|
||||||
|
|
||||||
id := props.TableName
|
id := props.TableName
|
||||||
@ -107,7 +107,7 @@ func (d *BoltDb) IsInitialized() (initialized bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) getObject(bucketID int, props db.ObjectProperties, objectID objectID, object interface{}) (err error) {
|
func (d *BoltDb) getObject(bucketID int, props db.ObjectProps, objectID objectID, object interface{}) (err error) {
|
||||||
err = d.db.View(func(tx *bbolt.Tx) error {
|
err = d.db.View(func(tx *bbolt.Tx) error {
|
||||||
b := tx.Bucket(makeBucketId(props, bucketID))
|
b := tx.Bucket(makeBucketId(props, bucketID))
|
||||||
if b == nil {
|
if b == nil {
|
||||||
@ -259,7 +259,7 @@ func marshalObject(obj interface{}) ([]byte, error) {
|
|||||||
return json.Marshal(copyObject(obj, newType))
|
return json.Marshal(copyObject(obj, newType))
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalObjects(rawData enumerable, props db.ObjectProperties, params db.RetrieveQueryParams, filter func(interface{}) bool, objects interface{}) (err error) {
|
func unmarshalObjects(rawData enumerable, props db.ObjectProps, params db.RetrieveQueryParams, filter func(interface{}) bool, objects interface{}) (err error) {
|
||||||
objectsValue := reflect.ValueOf(objects).Elem()
|
objectsValue := reflect.ValueOf(objects).Elem()
|
||||||
objType := objectsValue.Type().Elem()
|
objType := objectsValue.Type().Elem()
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ func unmarshalObjects(rawData enumerable, props db.ObjectProperties, params db.R
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) getObjects(bucketID int, props db.ObjectProperties, params db.RetrieveQueryParams, filter func(interface{}) bool, objects interface{}) error {
|
func (d *BoltDb) getObjects(bucketID int, props db.ObjectProps, params db.RetrieveQueryParams, filter func(interface{}) bool, objects interface{}) error {
|
||||||
return d.db.View(func(tx *bbolt.Tx) error {
|
return d.db.View(func(tx *bbolt.Tx) error {
|
||||||
b := tx.Bucket(makeBucketId(props, bucketID))
|
b := tx.Bucket(makeBucketId(props, bucketID))
|
||||||
var c enumerable
|
var c enumerable
|
||||||
@ -330,74 +330,8 @@ func (d *BoltDb) getObjects(bucketID int, props db.ObjectProperties, params db.R
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func isObjectBelongTo(props db.ObjectProperties, objID objectID, tpl interface{}) bool {
|
func (d *BoltDb) deleteObject(bucketID int, props db.ObjectProps, objectID objectID, tx *bbolt.Tx) error {
|
||||||
if props.ForeignColumnSuffix == "" {
|
for _, u := range []db.ObjectProps{db.TemplateProps, db.EnvironmentProps, db.InventoryProps, db.RepositoryProps} {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldName, err := getFieldNameByTagSuffix(reflect.TypeOf(tpl), "db", props.ForeignColumnSuffix)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
f := reflect.ValueOf(tpl).FieldByName(fieldName)
|
|
||||||
|
|
||||||
if f.IsZero() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Kind() == reflect.Ptr {
|
|
||||||
if f.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
f = f.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
var fVal objectID
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Int,
|
|
||||||
reflect.Int8,
|
|
||||||
reflect.Int16,
|
|
||||||
reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint,
|
|
||||||
reflect.Uint8,
|
|
||||||
reflect.Uint16,
|
|
||||||
reflect.Uint32,
|
|
||||||
reflect.Uint64:
|
|
||||||
fVal = intObjectID(f.Int())
|
|
||||||
case reflect.String:
|
|
||||||
fVal = strObjectID(f.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if fVal == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.Equal(fVal.ToBytes(), objID.ToBytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// isObjectInUse checks if objID associated with any object in foreignTableProps.
|
|
||||||
func (d *BoltDb) isObjectInUse(bucketID int, objProps db.ObjectProperties, objID objectID, foreignTableProps db.ObjectProperties) (inUse bool, err error) {
|
|
||||||
templates := reflect.New(reflect.SliceOf(foreignTableProps.Type))
|
|
||||||
|
|
||||||
err = d.getObjects(bucketID, foreignTableProps, db.RetrieveQueryParams{}, func(foreignObj interface{}) bool {
|
|
||||||
return isObjectBelongTo(objProps, objID, foreignObj)
|
|
||||||
}, templates.Interface())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
inUse = templates.Elem().Len() > 0
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *BoltDb) deleteObject(bucketID int, props db.ObjectProperties, objectID objectID, tx *bbolt.Tx) error {
|
|
||||||
for _, u := range []db.ObjectProperties{db.TemplateProps, db.EnvironmentProps, db.InventoryProps, db.RepositoryProps} {
|
|
||||||
inUse, err := d.isObjectInUse(bucketID, props, objectID, u)
|
inUse, err := d.isObjectInUse(bucketID, props, objectID, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -422,55 +356,8 @@ func (d *BoltDb) deleteObject(bucketID int, props db.ObjectProperties, objectID
|
|||||||
return d.db.Update(fn)
|
return d.db.Update(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) deleteObjectSoft(bucketID int, props db.ObjectProperties, objectID objectID) error {
|
|
||||||
var data map[string]interface{}
|
|
||||||
|
|
||||||
// load data
|
|
||||||
err := d.db.View(func(tx *bbolt.Tx) error {
|
|
||||||
b := tx.Bucket(makeBucketId(props, bucketID))
|
|
||||||
if b == nil {
|
|
||||||
return db.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
j := b.Get(objectID.ToBytes())
|
|
||||||
|
|
||||||
if j == nil {
|
|
||||||
return db.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Unmarshal(j, &data)
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark as removed if "removed" exists
|
|
||||||
if _, ok := data["removed"]; !ok {
|
|
||||||
return fmt.Errorf("removed field not exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
data["removed"] = true
|
|
||||||
|
|
||||||
// store data back
|
|
||||||
res, err := json.Marshal(data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.db.Update(func(tx *bbolt.Tx) error {
|
|
||||||
b := tx.Bucket(makeBucketId(props, bucketID))
|
|
||||||
if b == nil {
|
|
||||||
return db.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Put(objectID.ToBytes(), res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateObject updates data for object in database.
|
// updateObject updates data for object in database.
|
||||||
func (d *BoltDb) updateObject(bucketID int, props db.ObjectProperties, object interface{}) error {
|
func (d *BoltDb) updateObject(bucketID int, props db.ObjectProps, object interface{}) error {
|
||||||
return d.db.Update(func(tx *bbolt.Tx) error {
|
return d.db.Update(func(tx *bbolt.Tx) error {
|
||||||
b := tx.Bucket(makeBucketId(props, bucketID))
|
b := tx.Bucket(makeBucketId(props, bucketID))
|
||||||
if b == nil {
|
if b == nil {
|
||||||
@ -520,7 +407,7 @@ func (d *BoltDb) updateObject(bucketID int, props db.ObjectProperties, object in
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) createObject(bucketID int, props db.ObjectProperties, object interface{}) (interface{}, error) {
|
func (d *BoltDb) createObject(bucketID int, props db.ObjectProps, object interface{}) (interface{}, error) {
|
||||||
err := d.db.Update(func(tx *bbolt.Tx) error {
|
err := d.db.Update(func(tx *bbolt.Tx) error {
|
||||||
b, err := tx.CreateBucketIfNotExists(makeBucketId(props, bucketID))
|
b, err := tx.CreateBucketIfNotExists(makeBucketId(props, bucketID))
|
||||||
|
|
||||||
@ -608,6 +495,119 @@ func (d *BoltDb) createObject(bucketID int, props db.ObjectProperties, object in
|
|||||||
return object, err
|
return object, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *BoltDb) getObjectRefs(projectID int, objectProps db.ObjectProps, objectID int) (refs db.ObjectReferrers, err error) {
|
||||||
|
refs.Templates, err = d.getObjectRefsFrom(projectID, objectProps, intObjectID(objectID), db.TemplateProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.Repositories, err = d.getObjectRefsFrom(projectID, objectProps, intObjectID(objectID), db.RepositoryProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.Inventories, err = d.getObjectRefsFrom(projectID, objectProps, intObjectID(objectID), db.InventoryProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//refs.Schedules, err = d.getObjectRefsFrom(projectID, objectProps, intObjectID(objectID), db.ScheduleProps)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BoltDb) getObjectRefsFrom(projectID int, objProps db.ObjectProps, objID objectID, referringObjectProps db.ObjectProps) (referringObjs []db.ObjectReferrer, err error) {
|
||||||
|
_, err = objProps.GetReferringFieldsFrom(referringObjectProps.Type)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
referringObjects := reflect.New(reflect.SliceOf(referringObjectProps.Type))
|
||||||
|
|
||||||
|
err = d.getObjects(projectID, referringObjectProps, db.RetrieveQueryParams{}, func(referringObj interface{}) bool {
|
||||||
|
return isObjectReferredBy(objProps, objID, referringObj)
|
||||||
|
}, referringObjects.Interface())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < referringObjects.Elem().Len(); i++ {
|
||||||
|
referringObjs = append(referringObjs, db.ObjectReferrer{
|
||||||
|
ID: int(referringObjects.Elem().Index(i).FieldByName("ID").Int()),
|
||||||
|
Name: referringObjects.Elem().Index(i).FieldByName("Name").String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isObjectReferredBy(props db.ObjectProps, objID objectID, referringObj interface{}) bool {
|
||||||
|
if props.ReferringColumnSuffix == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldName, err := getFieldNameByTagSuffix(reflect.TypeOf(referringObj), "db", props.ReferringColumnSuffix)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
f := reflect.ValueOf(referringObj).FieldByName(fieldName)
|
||||||
|
|
||||||
|
if f.IsZero() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Kind() == reflect.Ptr {
|
||||||
|
if f.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
f = f.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
var fVal objectID
|
||||||
|
switch f.Kind() {
|
||||||
|
case reflect.Int,
|
||||||
|
reflect.Int8,
|
||||||
|
reflect.Int16,
|
||||||
|
reflect.Int32,
|
||||||
|
reflect.Int64,
|
||||||
|
reflect.Uint,
|
||||||
|
reflect.Uint8,
|
||||||
|
reflect.Uint16,
|
||||||
|
reflect.Uint32,
|
||||||
|
reflect.Uint64:
|
||||||
|
fVal = intObjectID(f.Int())
|
||||||
|
case reflect.String:
|
||||||
|
fVal = strObjectID(f.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if fVal == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.Equal(fVal.ToBytes(), objID.ToBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// isObjectInUse checks if objID associated with any object in foreignTableProps.
|
||||||
|
func (d *BoltDb) isObjectInUse(bucketID int, objProps db.ObjectProps, objID objectID, referringObjectProps db.ObjectProps) (inUse bool, err error) {
|
||||||
|
referringObjects := reflect.New(reflect.SliceOf(referringObjectProps.Type))
|
||||||
|
|
||||||
|
err = d.getObjects(bucketID, referringObjectProps, db.RetrieveQueryParams{}, func(referringObj interface{}) bool {
|
||||||
|
return isObjectReferredBy(objProps, objID, referringObj)
|
||||||
|
}, referringObjects.Interface())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inUse = referringObjects.Elem().Len() > 0
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func CreateTestStore() BoltDb {
|
func CreateTestStore() BoltDb {
|
||||||
r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
|
r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
|
||||||
fn := "/tmp/test_semaphore_db_" + strconv.Itoa(r.Int())
|
fn := "/tmp/test_semaphore_db_" + strconv.Itoa(r.Int())
|
||||||
|
@ -17,48 +17,48 @@ type test1 struct {
|
|||||||
Removed bool `db:"removed"`
|
Removed bool `db:"removed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var test1props = db.ObjectProperties{
|
//var test1props = db.ObjectProps{
|
||||||
IsGlobal: true,
|
// IsGlobal: true,
|
||||||
TableName: "test1",
|
// TableName: "test1",
|
||||||
PrimaryColumnName: "ID",
|
// PrimaryColumnName: "ID",
|
||||||
}
|
//}
|
||||||
|
|
||||||
func TestDeleteObjectSoft(t *testing.T) {
|
//func TestDeleteObjectSoft(t *testing.T) {
|
||||||
store := CreateTestStore()
|
// store := CreateTestStore()
|
||||||
|
//
|
||||||
obj := test1{
|
// obj := test1{
|
||||||
FirstName: "Denis",
|
// FirstName: "Denis",
|
||||||
LastName: "Gukov",
|
// LastName: "Gukov",
|
||||||
}
|
// }
|
||||||
newObj, err := store.createObject(0, test1props, obj)
|
// newObj, err := store.createObject(0, test1props, obj)
|
||||||
|
//
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err.Error())
|
// t.Fatal(err.Error())
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
objID := intObjectID(newObj.(test1).ID)
|
// objID := intObjectID(newObj.(test1).ID)
|
||||||
|
//
|
||||||
err = store.deleteObjectSoft(0, test1props, objID)
|
// err = store.deleteObjectSoft(0, test1props, objID)
|
||||||
|
//
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err.Error())
|
// t.Fatal(err.Error())
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
var found test1
|
// var found test1
|
||||||
err = store.getObject(0, test1props, objID, &found)
|
// err = store.getObject(0, test1props, objID, &found)
|
||||||
|
//
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err.Error())
|
// t.Fatal(err.Error())
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if found.ID != int(objID) ||
|
// if found.ID != int(objID) ||
|
||||||
found.Removed != true ||
|
// found.Removed != true ||
|
||||||
found.Password != obj.Password ||
|
// found.Password != obj.Password ||
|
||||||
found.LastName != obj.LastName {
|
// found.LastName != obj.LastName {
|
||||||
|
//
|
||||||
t.Fatal()
|
// t.Fatal()
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
func TestMarshalObject_UserWithPwd(t *testing.T) {
|
func TestMarshalObject_UserWithPwd(t *testing.T) {
|
||||||
user := db.UserWithPwd{
|
user := db.UserWithPwd{
|
||||||
@ -200,7 +200,7 @@ func TestIsObjectInUse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = store.CreateTemplate(db.Template{
|
_, err = store.CreateTemplate(db.Template{
|
||||||
Alias: "Test",
|
Name: "Test",
|
||||||
Playbook: "test.yml",
|
Playbook: "test.yml",
|
||||||
ProjectID: proj.ID,
|
ProjectID: proj.ID,
|
||||||
InventoryID: 10,
|
InventoryID: 10,
|
||||||
@ -236,7 +236,7 @@ func TestIsObjectInUse_Environment(t *testing.T) {
|
|||||||
envID := 10
|
envID := 10
|
||||||
|
|
||||||
_, err = store.CreateTemplate(db.Template{
|
_, err = store.CreateTemplate(db.Template{
|
||||||
Alias: "Test",
|
Name: "Test",
|
||||||
Playbook: "test.yml",
|
Playbook: "test.yml",
|
||||||
ProjectID: proj.ID,
|
ProjectID: proj.ID,
|
||||||
EnvironmentID: &envID,
|
EnvironmentID: &envID,
|
||||||
@ -270,7 +270,7 @@ func TestIsObjectInUse_EnvironmentNil(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = store.CreateTemplate(db.Template{
|
_, err = store.CreateTemplate(db.Template{
|
||||||
Alias: "Test",
|
Name: "Test",
|
||||||
Playbook: "test.yml",
|
Playbook: "test.yml",
|
||||||
ProjectID: proj.ID,
|
ProjectID: proj.ID,
|
||||||
EnvironmentID: nil,
|
EnvironmentID: nil,
|
||||||
@ -351,3 +351,37 @@ func TestBoltDb_CreateAPIToken(t *testing.T) {
|
|||||||
t.Fatal()
|
t.Fatal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBoltDb_GetRepositoryRefs(t *testing.T) {
|
||||||
|
store := CreateTestStore()
|
||||||
|
|
||||||
|
repo1, err := store.CreateRepository(db.Repository{
|
||||||
|
Name: "repo1",
|
||||||
|
ProjectID: 1,
|
||||||
|
GitURL: "git@example.com/repo1",
|
||||||
|
GitBranch: "master",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = store.CreateTemplate(db.Template{
|
||||||
|
ProjectID: 1,
|
||||||
|
Type: db.TemplateBuild,
|
||||||
|
Name: "tpl1",
|
||||||
|
Playbook: "build.yml",
|
||||||
|
RepositoryID: repo1.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
refs, err := store.GetRepositoryRefs(1, repo1.ID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(refs.Templates) != 1 {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
package bolt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"go.etcd.io/bbolt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Migration_2_8_28 struct {
|
|
||||||
DB *bbolt.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Migration_2_8_28) getProjectRepositories(projectID string) (map[string]map[string]interface{}, error) {
|
|
||||||
repos := make(map[string]map[string]interface{})
|
|
||||||
err := d.DB.View(func(tx *bbolt.Tx) error {
|
|
||||||
b := tx.Bucket([]byte("project__repository_" + projectID))
|
|
||||||
if b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return b.ForEach(func(id, body []byte) error {
|
|
||||||
r := make(map[string]interface{})
|
|
||||||
repos[string(id)] = r
|
|
||||||
return json.Unmarshal(body, &r)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return repos, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Migration_2_8_28) setProjectRepository(projectID string, repoID string, repo map[string]interface{}) error {
|
|
||||||
return d.DB.Update(func(tx *bbolt.Tx) error {
|
|
||||||
b, err := tx.CreateBucketIfNotExists([]byte("project__repository_" + projectID))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
j, err := json.Marshal(repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return b.Put([]byte(repoID), j)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d Migration_2_8_28) Apply() (err error) {
|
|
||||||
var projectIDs []string
|
|
||||||
|
|
||||||
err = d.DB.View(func(tx *bbolt.Tx) error {
|
|
||||||
b := tx.Bucket([]byte("project"))
|
|
||||||
if b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return b.ForEach(func(id, _ []byte) error {
|
|
||||||
projectIDs = append(projectIDs, string(id))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
projectsRepositories := make(map[string]map[string]map[string]interface{})
|
|
||||||
|
|
||||||
for _, projectID := range projectIDs {
|
|
||||||
var err2 error
|
|
||||||
projectsRepositories[projectID], err2 = d.getProjectRepositories(projectID)
|
|
||||||
if err2 != nil {
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for projectID, repositories := range projectsRepositories {
|
|
||||||
for repoID, repo := range repositories {
|
|
||||||
branch := "master"
|
|
||||||
url := repo["git_url"].(string)
|
|
||||||
parts := strings.Split(url, "#")
|
|
||||||
if len(parts) > 1 {
|
|
||||||
url, branch = parts[0], parts[1]
|
|
||||||
}
|
|
||||||
repo["git_url"] = url
|
|
||||||
repo["git_branch"] = branch
|
|
||||||
err = d.setProjectRepository(projectID, repoID, repo)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -13,7 +13,7 @@ func TestTask_GetVersion(t *testing.T) {
|
|||||||
build, err := store.CreateTemplate(db.Template{
|
build, err := store.CreateTemplate(db.Template{
|
||||||
ProjectID: 0,
|
ProjectID: 0,
|
||||||
Type: db.TemplateBuild,
|
Type: db.TemplateBuild,
|
||||||
Alias: "Build",
|
Name: "Build",
|
||||||
Playbook: "build.yml",
|
Playbook: "build.yml",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -24,7 +24,7 @@ func TestTask_GetVersion(t *testing.T) {
|
|||||||
ProjectID: 0,
|
ProjectID: 0,
|
||||||
Type: db.TemplateDeploy,
|
Type: db.TemplateDeploy,
|
||||||
BuildTemplateID: &build.ID,
|
BuildTemplateID: &build.ID,
|
||||||
Alias: "Deploy",
|
Name: "Deploy",
|
||||||
Playbook: "deploy.yml",
|
Playbook: "deploy.yml",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -35,7 +35,7 @@ func TestTask_GetVersion(t *testing.T) {
|
|||||||
ProjectID: 0,
|
ProjectID: 0,
|
||||||
Type: db.TemplateDeploy,
|
Type: db.TemplateDeploy,
|
||||||
BuildTemplateID: &deploy.ID,
|
BuildTemplateID: &deploy.ID,
|
||||||
Alias: "Deploy2",
|
Name: "Deploy2",
|
||||||
Playbook: "deploy2.yml",
|
Playbook: "deploy2.yml",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -13,6 +13,10 @@ func (d *BoltDb) GetAccessKey(projectID int, accessKeyID int) (key db.AccessKey,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *BoltDb) GetAccessKeyRefs(projectID int, accessKeyID int) (db.ObjectReferrers, error) {
|
||||||
|
return d.getObjectRefs(projectID, db.AccessKeyProps, accessKeyID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *BoltDb) GetAccessKeys(projectID int, params db.RetrieveQueryParams) ([]db.AccessKey, error) {
|
func (d *BoltDb) GetAccessKeys(projectID int, params db.RetrieveQueryParams) ([]db.AccessKey, error) {
|
||||||
var keys []db.AccessKey
|
var keys []db.AccessKey
|
||||||
err := d.getObjects(projectID, db.AccessKeyProps, params, nil, &keys)
|
err := d.getObjects(projectID, db.AccessKeyProps, params, nil, &keys)
|
||||||
@ -55,7 +59,3 @@ func (d *BoltDb) CreateAccessKey(key db.AccessKey) (db.AccessKey, error) {
|
|||||||
func (d *BoltDb) DeleteAccessKey(projectID int, accessKeyID int) error {
|
func (d *BoltDb) DeleteAccessKey(projectID int, accessKeyID int) error {
|
||||||
return d.deleteObject(projectID, db.AccessKeyProps, intObjectID(accessKeyID), nil)
|
return d.deleteObject(projectID, db.AccessKeyProps, intObjectID(accessKeyID), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) DeleteAccessKeySoft(projectID int, accessKeyID int) error {
|
|
||||||
return d.deleteObjectSoft(projectID, db.AccessKeyProps, intObjectID(accessKeyID))
|
|
||||||
}
|
|
||||||
|
@ -7,6 +7,10 @@ func (d *BoltDb) GetEnvironment(projectID int, environmentID int) (environment d
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *BoltDb) GetEnvironmentRefs(projectID int, environmentID int) (db.ObjectReferrers, error) {
|
||||||
|
return d.getObjectRefs(projectID, db.EnvironmentProps, environmentID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *BoltDb) GetEnvironments(projectID int, params db.RetrieveQueryParams) (environment []db.Environment, err error) {
|
func (d *BoltDb) GetEnvironments(projectID int, params db.RetrieveQueryParams) (environment []db.Environment, err error) {
|
||||||
err = d.getObjects(projectID, db.EnvironmentProps, params, nil, &environment)
|
err = d.getObjects(projectID, db.EnvironmentProps, params, nil, &environment)
|
||||||
return
|
return
|
||||||
@ -36,7 +40,3 @@ func (d *BoltDb) CreateEnvironment(env db.Environment) (db.Environment, error) {
|
|||||||
func (d *BoltDb) DeleteEnvironment(projectID int, environmentID int) error {
|
func (d *BoltDb) DeleteEnvironment(projectID int, environmentID int) error {
|
||||||
return d.deleteObject(projectID, db.EnvironmentProps, intObjectID(environmentID), nil)
|
return d.deleteObject(projectID, db.EnvironmentProps, intObjectID(environmentID), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) DeleteEnvironmentSoft(projectID int, environmentID int) error {
|
|
||||||
return d.deleteObjectSoft(projectID, db.EnvironmentProps, intObjectID(environmentID))
|
|
||||||
}
|
|
||||||
|
@ -20,12 +20,12 @@ func (d *BoltDb) GetInventories(projectID int, params db.RetrieveQueryParams) (i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) DeleteInventory(projectID int, inventoryID int) error {
|
func (d *BoltDb) GetInventoryRefs(projectID int, inventoryID int) (db.ObjectReferrers, error) {
|
||||||
return d.deleteObject(projectID, db.InventoryProps, intObjectID(inventoryID), nil)
|
return d.getObjectRefs(projectID, db.InventoryProps, inventoryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) DeleteInventorySoft(projectID int, inventoryID int) error {
|
func (d *BoltDb) DeleteInventory(projectID int, inventoryID int) error {
|
||||||
return d.deleteObjectSoft(projectID, db.InventoryProps, intObjectID(inventoryID))
|
return d.deleteObject(projectID, db.InventoryProps, intObjectID(inventoryID), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) UpdateInventory(inventory db.Inventory) error {
|
func (d *BoltDb) UpdateInventory(inventory db.Inventory) error {
|
||||||
|
@ -33,10 +33,10 @@ func (d *BoltDb) IsMigrationApplied(migration db.Migration) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) ApplyMigration(migration db.Migration) (err error) {
|
func (d *BoltDb) ApplyMigration(m db.Migration) (err error) {
|
||||||
switch migration.Version {
|
switch m.Version {
|
||||||
case "2.8.26":
|
case "2.8.26":
|
||||||
err = Migration_2_8_28{DB: d.db}.Apply()
|
err = migration_2_8_28{migration{d.db}}.Apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -50,18 +50,66 @@ func (d *BoltDb) ApplyMigration(migration db.Migration) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(migration)
|
j, err := json.Marshal(m)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.Put([]byte(migration.Version), j)
|
return b.Put([]byte(m.Version), j)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) TryRollbackMigration(migration db.Migration) {
|
func (d *BoltDb) TryRollbackMigration(m db.Migration) {
|
||||||
switch migration.Version {
|
switch m.Version {
|
||||||
case "2.8.26":
|
case "2.8.26":
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type migration struct {
|
||||||
|
db *bbolt.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d migration) getProjectIDs() (projectIDs []string, err error) {
|
||||||
|
err = d.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
b := tx.Bucket([]byte("project"))
|
||||||
|
if b == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return b.ForEach(func(id, _ []byte) error {
|
||||||
|
projectIDs = append(projectIDs, string(id))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d migration) getObjects(projectID string, objectPrefix string) (map[string]map[string]interface{}, error) {
|
||||||
|
repos := make(map[string]map[string]interface{})
|
||||||
|
err := d.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
b := tx.Bucket([]byte("project__" + objectPrefix + "_" + projectID))
|
||||||
|
if b == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return b.ForEach(func(id, body []byte) error {
|
||||||
|
r := make(map[string]interface{})
|
||||||
|
repos[string(id)] = r
|
||||||
|
return json.Unmarshal(body, &r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return repos, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d migration) setObject(projectID string, objectPrefix string, objectID string, object map[string]interface{}) error {
|
||||||
|
return d.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
b, err := tx.CreateBucketIfNotExists([]byte("project__" + objectPrefix + "_" + projectID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
j, err := json.Marshal(object)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return b.Put([]byte(objectID), j)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
46
db/bolt/migration_2_8_28.go
Normal file
46
db/bolt/migration_2_8_28.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package bolt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type migration_2_8_28 struct {
|
||||||
|
migration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d migration_2_8_28) Apply() (err error) {
|
||||||
|
projectIDs, err := d.getProjectIDs()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repos := make(map[string]map[string]map[string]interface{})
|
||||||
|
|
||||||
|
for _, projectID := range projectIDs {
|
||||||
|
var err2 error
|
||||||
|
repos[projectID], err2 = d.getObjects(projectID, "repository")
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for projectID, projectRepos := range repos {
|
||||||
|
for repoID, repo := range projectRepos {
|
||||||
|
branch := "master"
|
||||||
|
url := repo["git_url"].(string)
|
||||||
|
parts := strings.Split(url, "#")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
url, branch = parts[0], parts[1]
|
||||||
|
}
|
||||||
|
repo["git_url"] = url
|
||||||
|
repo["git_branch"] = branch
|
||||||
|
err = d.setObject(projectID, "repository", repoID, repo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -35,7 +35,7 @@ func TestMigration_2_8_28_Apply(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Migration_2_8_28{DB: store.db}.Apply()
|
err = migration_2_8_28{migration{store.db}}.Apply()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ func TestMigration_2_8_28_Apply2(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Migration_2_8_28{DB: store.db}.Apply()
|
err = migration_2_8_28{migration{store.db}}.Apply()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
36
db/bolt/migration_2_8_40.go
Normal file
36
db/bolt/migration_2_8_40.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package bolt
|
||||||
|
|
||||||
|
type migration_2_8_40 struct {
|
||||||
|
migration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d migration_2_8_40) Apply() (err error) {
|
||||||
|
projectIDs, err := d.getProjectIDs()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
templates := make(map[string]map[string]map[string]interface{})
|
||||||
|
|
||||||
|
for _, projectID := range projectIDs {
|
||||||
|
var err2 error
|
||||||
|
templates[projectID], err2 = d.getObjects(projectID, "template")
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for projectID, projectTemplates := range templates {
|
||||||
|
for repoID, tpl := range projectTemplates {
|
||||||
|
tpl["name"] = tpl["alias"]
|
||||||
|
delete(tpl, "alias")
|
||||||
|
err = d.setObject(projectID, "template", repoID, tpl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
84
db/bolt/migration_2_8_40_test.go
Normal file
84
db/bolt/migration_2_8_40_test.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package bolt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"go.etcd.io/bbolt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMigration_2_8_40_Apply(t *testing.T) {
|
||||||
|
store := CreateTestStore()
|
||||||
|
|
||||||
|
err := store.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
b, err := tx.CreateBucketIfNotExists([]byte("project"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = b.Put([]byte("0000000001"), []byte("{}"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := tx.CreateBucketIfNotExists([]byte("project__template_0000000001"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Put([]byte("0000000001"),
|
||||||
|
[]byte("{\"id\":\"1\",\"project_id\":\"1\",\"name\": \"test123\"}"))
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = migration_2_8_40{migration{store.db}}.Apply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var repo map[string]interface{}
|
||||||
|
err = store.db.View(func(tx *bbolt.Tx) error {
|
||||||
|
b := tx.Bucket([]byte("project__template_0000000001"))
|
||||||
|
str := string(b.Get([]byte("0000000001")))
|
||||||
|
return json.Unmarshal([]byte(str), &repo)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo["name"].(string) != "test123" {
|
||||||
|
t.Fatal("invalid name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo["name"] != nil {
|
||||||
|
t.Fatal("name must be deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMigration_2_8_40_Apply2(t *testing.T) {
|
||||||
|
store := CreateTestStore()
|
||||||
|
|
||||||
|
err := store.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
b, err := tx.CreateBucketIfNotExists([]byte("project"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = b.Put([]byte("0000000001"), []byte("{}"))
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = migration_2_8_28{migration{store.db}}.Apply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,10 @@ func (d *BoltDb) GetRepository(projectID int, repositoryID int) (repository db.R
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *BoltDb) GetRepositoryRefs(projectID int, repositoryID int) (db.ObjectReferrers, error) {
|
||||||
|
return d.getObjectRefs(projectID, db.RepositoryProps, repositoryID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *BoltDb) GetRepositories(projectID int, params db.RetrieveQueryParams) (repositories []db.Repository, err error) {
|
func (d *BoltDb) GetRepositories(projectID int, params db.RetrieveQueryParams) (repositories []db.Repository, err error) {
|
||||||
err = d.getObjects(projectID, db.RepositoryProps, params, nil, &repositories)
|
err = d.getObjects(projectID, db.RepositoryProps, params, nil, &repositories)
|
||||||
return
|
return
|
||||||
@ -38,7 +42,3 @@ func (d *BoltDb) CreateRepository(repository db.Repository) (db.Repository, erro
|
|||||||
func (d *BoltDb) DeleteRepository(projectID int, repositoryId int) error {
|
func (d *BoltDb) DeleteRepository(projectID int, repositoryId int) error {
|
||||||
return d.deleteObject(projectID, db.RepositoryProps, intObjectID(repositoryId), nil)
|
return d.deleteObject(projectID, db.RepositoryProps, intObjectID(repositoryId), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *BoltDb) DeleteRepositorySoft(projectID int, repositoryId int) error {
|
|
||||||
return d.deleteObjectSoft(projectID, db.RepositoryProps, intObjectID(repositoryId))
|
|
||||||
}
|
|
||||||
|
@ -11,7 +11,7 @@ type globalToken struct {
|
|||||||
UserID int `db:"user_id" json:"user_id"`
|
UserID int `db:"user_id" json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalTokenObject = db.ObjectProperties{
|
var globalTokenObject = db.ObjectProps{
|
||||||
TableName: "token",
|
TableName: "token",
|
||||||
PrimaryColumnName: "id",
|
PrimaryColumnName: "id",
|
||||||
Type: reflect.TypeOf(globalToken{}),
|
Type: reflect.TypeOf(globalToken{}),
|
||||||
|
@ -65,7 +65,7 @@ func (d *BoltDb) getTasks(projectID int, templateID *int, params db.RetrieveQuer
|
|||||||
}
|
}
|
||||||
tasksWithTpl[i] = db.TaskWithTpl{Task: task}
|
tasksWithTpl[i] = db.TaskWithTpl{Task: task}
|
||||||
tasksWithTpl[i].TemplatePlaybook = tpl.Playbook
|
tasksWithTpl[i].TemplatePlaybook = tpl.Playbook
|
||||||
tasksWithTpl[i].TemplateAlias = tpl.Alias
|
tasksWithTpl[i].TemplateAlias = tpl.Name
|
||||||
tasksWithTpl[i].TemplateType = tpl.Type
|
tasksWithTpl[i].TemplateType = tpl.Type
|
||||||
if task.UserID != nil {
|
if task.UserID != nil {
|
||||||
usr, ok := users[*task.UserID]
|
usr, ok := users[*task.UserID]
|
||||||
|
127
db/sql/SqlDb.go
127
db/sql/SqlDb.go
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/gobuffalo/packr"
|
"github.com/gobuffalo/packr"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"github.com/masterminds/squirrel"
|
"github.com/masterminds/squirrel"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -182,7 +183,7 @@ func createDb() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) getObject(projectID int, props db.ObjectProperties, objectID int, object interface{}) (err error) {
|
func (d *SqlDb) getObject(projectID int, props db.ObjectProps, objectID int, object interface{}) (err error) {
|
||||||
q := squirrel.Select("*").
|
q := squirrel.Select("*").
|
||||||
From(props.TableName).
|
From(props.TableName).
|
||||||
Where("id=?", objectID)
|
Where("id=?", objectID)
|
||||||
@ -208,7 +209,7 @@ func (d *SqlDb) getObject(projectID int, props db.ObjectProperties, objectID int
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) getObjects(projectID int, props db.ObjectProperties, params db.RetrieveQueryParams, objects interface{}) (err error) {
|
func (d *SqlDb) getObjects(projectID int, props db.ObjectProps, params db.RetrieveQueryParams, objects interface{}) (err error) {
|
||||||
q := squirrel.Select("*").
|
q := squirrel.Select("*").
|
||||||
From(props.TableName+" pe").
|
From(props.TableName+" pe").
|
||||||
Where("pe.project_id=?", projectID)
|
Where("pe.project_id=?", projectID)
|
||||||
@ -238,7 +239,7 @@ func (d *SqlDb) getObjects(projectID int, props db.ObjectProperties, params db.R
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) deleteObject(projectID int, props db.ObjectProperties, objectID int) error {
|
func (d *SqlDb) deleteObject(projectID int, props db.ObjectProps, objectID int) error {
|
||||||
return validateMutationResult(
|
return validateMutationResult(
|
||||||
d.exec(
|
d.exec(
|
||||||
"delete from "+props.TableName+" where project_id=? and id=?",
|
"delete from "+props.TableName+" where project_id=? and id=?",
|
||||||
@ -246,14 +247,6 @@ func (d *SqlDb) deleteObject(projectID int, props db.ObjectProperties, objectID
|
|||||||
objectID))
|
objectID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) deleteObjectSoft(projectID int, props db.ObjectProperties, objectID int) error {
|
|
||||||
return validateMutationResult(
|
|
||||||
d.exec(
|
|
||||||
"update "+props.TableName+" set removed=1 where project_id=? and id=?",
|
|
||||||
projectID,
|
|
||||||
objectID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *SqlDb) Close() error {
|
func (d *SqlDb) Close() error {
|
||||||
return d.sql.Db.Close()
|
return d.sql.Db.Close()
|
||||||
}
|
}
|
||||||
@ -338,6 +331,118 @@ func getSqlForTable(tableName string, p db.RetrieveQueryParams) (string, []inter
|
|||||||
return q.ToSql()
|
return q.ToSql()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *SqlDb) getObjectRefs(projectID int, objectProps db.ObjectProps, objectID int) (refs db.ObjectReferrers, err error) {
|
||||||
|
refs.Templates, err = d.getObjectRefsFrom(projectID, objectProps, objectID, db.TemplateProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.Repositories, err = d.getObjectRefsFrom(projectID, objectProps, objectID, db.RepositoryProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.Inventories, err = d.getObjectRefsFrom(projectID, objectProps, objectID, db.InventoryProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
templates, err := d.getObjectRefsFrom(projectID, objectProps, objectID, db.ScheduleProps)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, st := range templates {
|
||||||
|
exists := false
|
||||||
|
for _, tpl := range refs.Templates {
|
||||||
|
if tpl.ID == st.ID {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
refs.Templates = append(refs.Templates, st)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SqlDb) getObjectRefCount(projectID int, objectProps db.ObjectProps, objectID int) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SqlDb) getObjectRefsFrom(
|
||||||
|
projectID int,
|
||||||
|
objectProps db.ObjectProps,
|
||||||
|
objectID int,
|
||||||
|
referringObjectProps db.ObjectProps,
|
||||||
|
) (referringObjs []db.ObjectReferrer, err error) {
|
||||||
|
referringObjs = make([]db.ObjectReferrer, 0)
|
||||||
|
|
||||||
|
fields, err := objectProps.GetReferringFieldsFrom(referringObjectProps.Type)
|
||||||
|
|
||||||
|
cond := ""
|
||||||
|
vals := []interface{}{projectID}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
if cond != "" {
|
||||||
|
cond += ", "
|
||||||
|
}
|
||||||
|
|
||||||
|
cond += f + " = ?"
|
||||||
|
|
||||||
|
vals = append(vals, objectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cond == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var referringObjects reflect.Value
|
||||||
|
|
||||||
|
if referringObjectProps.Type == db.ScheduleProps.Type {
|
||||||
|
var referringSchedules []db.Schedule
|
||||||
|
_, err = d.selectAll(&referringSchedules, "select template_id id from project__schedule where project_id = ? and "+cond, vals...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(referringSchedules) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids []string
|
||||||
|
for _, schedule := range referringSchedules {
|
||||||
|
ids = append(ids, strconv.Itoa(schedule.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
referringObjects = reflect.New(reflect.SliceOf(db.TemplateProps.Type))
|
||||||
|
_, err = d.selectAll(referringObjects.Interface(),
|
||||||
|
"select id, name from project__template where id in ("+strings.Join(ids, ",")+")")
|
||||||
|
} else {
|
||||||
|
referringObjects = reflect.New(reflect.SliceOf(referringObjectProps.Type))
|
||||||
|
_, err = d.selectAll(
|
||||||
|
referringObjects.Interface(),
|
||||||
|
"select id, name from "+referringObjectProps.TableName+" where project_id = ? and "+cond,
|
||||||
|
vals...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < referringObjects.Elem().Len(); i++ {
|
||||||
|
id := int(referringObjects.Elem().Index(i).FieldByName("ID").Int())
|
||||||
|
name := referringObjects.Elem().Index(i).FieldByName("Name").String()
|
||||||
|
referringObjs = append(referringObjs, db.ObjectReferrer{ID: id, Name: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (d *SqlDb) Sql() *gorp.DbMap {
|
func (d *SqlDb) Sql() *gorp.DbMap {
|
||||||
return d.sql
|
return d.sql
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,10 @@ func (d *SqlDb) GetAccessKey(projectID int, accessKeyID int) (key db.AccessKey,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *SqlDb) GetAccessKeyRefs(projectID int, keyID int) (db.ObjectReferrers, error) {
|
||||||
|
return d.getObjectRefs(projectID, db.AccessKeyProps, keyID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *SqlDb) GetAccessKeys(projectID int, params db.RetrieveQueryParams) ([]db.AccessKey, error) {
|
func (d *SqlDb) GetAccessKeys(projectID int, params db.RetrieveQueryParams) ([]db.AccessKey, error) {
|
||||||
var keys []db.AccessKey
|
var keys []db.AccessKey
|
||||||
err := d.getObjects(projectID, db.AccessKeyProps, params, &keys)
|
err := d.getObjects(projectID, db.AccessKeyProps, params, &keys)
|
||||||
@ -83,7 +87,3 @@ func (d *SqlDb) CreateAccessKey(key db.AccessKey) (newKey db.AccessKey, err erro
|
|||||||
func (d *SqlDb) DeleteAccessKey(projectID int, accessKeyID int) error {
|
func (d *SqlDb) DeleteAccessKey(projectID int, accessKeyID int) error {
|
||||||
return d.deleteObject(projectID, db.AccessKeyProps, accessKeyID)
|
return d.deleteObject(projectID, db.AccessKeyProps, accessKeyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) DeleteAccessKeySoft(projectID int, accessKeyID int) error {
|
|
||||||
return d.deleteObjectSoft(projectID, db.AccessKeyProps, accessKeyID)
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
package sql
|
package sql
|
||||||
|
|
||||||
import "github.com/ansible-semaphore/semaphore/db"
|
import (
|
||||||
|
"github.com/ansible-semaphore/semaphore/db"
|
||||||
|
)
|
||||||
|
|
||||||
func (d *SqlDb) GetEnvironment(projectID int, environmentID int) (db.Environment, error) {
|
func (d *SqlDb) GetEnvironment(projectID int, environmentID int) (db.Environment, error) {
|
||||||
var environment db.Environment
|
var environment db.Environment
|
||||||
@ -8,6 +10,10 @@ func (d *SqlDb) GetEnvironment(projectID int, environmentID int) (db.Environment
|
|||||||
return environment, err
|
return environment, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *SqlDb) GetEnvironmentRefs(projectID int, environmentID int) (db.ObjectReferrers, error) {
|
||||||
|
return d.getObjectRefs(projectID, db.EnvironmentProps, environmentID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *SqlDb) GetEnvironments(projectID int, params db.RetrieveQueryParams) ([]db.Environment, error) {
|
func (d *SqlDb) GetEnvironments(projectID int, params db.RetrieveQueryParams) ([]db.Environment, error) {
|
||||||
var environment []db.Environment
|
var environment []db.Environment
|
||||||
err := d.getObjects(projectID, db.EnvironmentProps, params, &environment)
|
err := d.getObjects(projectID, db.EnvironmentProps, params, &environment)
|
||||||
@ -56,7 +62,3 @@ func (d *SqlDb) CreateEnvironment(env db.Environment) (newEnv db.Environment, er
|
|||||||
func (d *SqlDb) DeleteEnvironment(projectID int, environmentID int) error {
|
func (d *SqlDb) DeleteEnvironment(projectID int, environmentID int) error {
|
||||||
return d.deleteObject(projectID, db.EnvironmentProps, environmentID)
|
return d.deleteObject(projectID, db.EnvironmentProps, environmentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) DeleteEnvironmentSoft(projectID int, environmentID int) error {
|
|
||||||
return d.deleteObjectSoft(projectID, db.EnvironmentProps, environmentID)
|
|
||||||
}
|
|
||||||
|
@ -18,12 +18,12 @@ func (d *SqlDb) GetInventories(projectID int, params db.RetrieveQueryParams) ([]
|
|||||||
return inventories, err
|
return inventories, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) DeleteInventory(projectID int, inventoryID int) error {
|
func (d *SqlDb) GetInventoryRefs(projectID int, inventoryID int) (db.ObjectReferrers, error) {
|
||||||
return d.deleteObject(projectID, db.InventoryProps, inventoryID)
|
return d.getObjectRefs(projectID, db.InventoryProps, inventoryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) DeleteInventorySoft(projectID int, inventoryID int) error {
|
func (d *SqlDb) DeleteInventory(projectID int, inventoryID int) error {
|
||||||
return d.deleteObjectSoft(projectID, db.InventoryProps, inventoryID)
|
return d.deleteObject(projectID, db.InventoryProps, inventoryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) UpdateInventory(inventory db.Inventory) error {
|
func (d *SqlDb) UpdateInventory(inventory db.Inventory) error {
|
||||||
@ -58,6 +58,3 @@ func (d *SqlDb) CreateInventory(inventory db.Inventory) (newInventory db.Invento
|
|||||||
newInventory.ID = insertID
|
newInventory.ID = insertID
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ var (
|
|||||||
longtextRE = regexp.MustCompile(`(?i)\blongtext\b`)
|
longtextRE = regexp.MustCompile(`(?i)\blongtext\b`)
|
||||||
ifExistsRE = regexp.MustCompile(`(?i)\bif exists\b`)
|
ifExistsRE = regexp.MustCompile(`(?i)\bif exists\b`)
|
||||||
dropForeignKey = regexp.MustCompile(`(?i)\bdrop foreign key\b`)
|
dropForeignKey = regexp.MustCompile(`(?i)\bdrop foreign key\b`)
|
||||||
|
changeRE = regexp.MustCompile(`^alter table \x60(\w+)\x60 change \x60(\w+)\x60 \x60(\w+)\x60 ([\w\(\)]+)( not null)?$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// getVersionPath is the humanoid version with the file format appended
|
// getVersionPath is the humanoid version with the file format appended
|
||||||
@ -38,6 +39,9 @@ func getVersionSQL(path string) (queries []string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
queries = strings.Split(strings.ReplaceAll(sql, ";\r\n", ";\n"), ";\n")
|
queries = strings.Split(strings.ReplaceAll(sql, ";\r\n", ";\n"), ";\n")
|
||||||
|
for i := range queries {
|
||||||
|
queries[i] = strings.Trim(queries[i], "\n\t ")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,12 +53,41 @@ func (d *SqlDb) prepareMigration(query string) string {
|
|||||||
query = autoIncrementRE.ReplaceAllString(query, "auto_increment")
|
query = autoIncrementRE.ReplaceAllString(query, "auto_increment")
|
||||||
query = ifExistsRE.ReplaceAllString(query, "")
|
query = ifExistsRE.ReplaceAllString(query, "")
|
||||||
case gorp.PostgresDialect:
|
case gorp.PostgresDialect:
|
||||||
query = serialRE.ReplaceAllString(query, "serial primary key")
|
m := changeRE.FindStringSubmatch(query)
|
||||||
query = identifierQuoteRE.ReplaceAllString(query, "\"")
|
var queries []string
|
||||||
|
|
||||||
|
if m != nil {
|
||||||
|
tableName := m[1]
|
||||||
|
oldColumnName := m[2]
|
||||||
|
newColumnName := m[3]
|
||||||
|
columnType := m[4]
|
||||||
|
columnNotNull := m[5] != ""
|
||||||
|
|
||||||
|
queries = append(queries,
|
||||||
|
"alter table `"+tableName+"` alter column `"+oldColumnName+"` type "+columnType)
|
||||||
|
|
||||||
|
if columnNotNull {
|
||||||
|
queries = append(queries,
|
||||||
|
"alter table `"+tableName+"` alter column `"+oldColumnName+"` set not null")
|
||||||
|
} else {
|
||||||
|
queries = append(queries,
|
||||||
|
"alter table `"+tableName+"` alter column `"+oldColumnName+"` drop not null")
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldColumnName != newColumnName {
|
||||||
|
queries = append(queries,
|
||||||
|
"alter table `"+tableName+"` rename column `"+oldColumnName+"` to `"+newColumnName+"`")
|
||||||
|
}
|
||||||
|
|
||||||
|
query = strings.Join(queries, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
query = dateTimeTypeRE.ReplaceAllString(query, "timestamp")
|
query = dateTimeTypeRE.ReplaceAllString(query, "timestamp")
|
||||||
query = tinyintRE.ReplaceAllString(query, "smallint")
|
query = tinyintRE.ReplaceAllString(query, "smallint")
|
||||||
query = longtextRE.ReplaceAllString(query, "text")
|
query = longtextRE.ReplaceAllString(query, "text")
|
||||||
|
query = serialRE.ReplaceAllString(query, "serial primary key")
|
||||||
query = dropForeignKey.ReplaceAllString(query, "drop constraint")
|
query = dropForeignKey.ReplaceAllString(query, "drop constraint")
|
||||||
|
query = identifierQuoteRE.ReplaceAllString(query, "\"")
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
@ -130,7 +163,7 @@ func (d *SqlDb) ApplyMigration(migration db.Migration) error {
|
|||||||
|
|
||||||
switch migration.Version {
|
switch migration.Version {
|
||||||
case "2.8.26":
|
case "2.8.26":
|
||||||
err = Migration_2_8_26{DB: d}.Apply(tx)
|
err = migration_2_8_26{db: d}.Apply(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -5,12 +5,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Migration_2_8_26 struct {
|
type migration_2_8_26 struct {
|
||||||
DB *SqlDb
|
db *SqlDb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Migration_2_8_26) Apply(tx *gorp.Transaction) error {
|
func (m migration_2_8_26) Apply(tx *gorp.Transaction) error {
|
||||||
rows, err := tx.Query(m.DB.PrepareQuery("SELECT id, git_url FROM project__repository"))
|
rows, err := tx.Query(m.db.PrepareQuery("SELECT id, git_url FROM project__repository"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ func (m Migration_2_8_26) Apply(tx *gorp.Transaction) error {
|
|||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
url, branch = parts[0], parts[1]
|
url, branch = parts[0], parts[1]
|
||||||
}
|
}
|
||||||
q := m.DB.PrepareQuery("UPDATE project__repository " +
|
q := m.db.PrepareQuery("UPDATE project__repository " +
|
||||||
"SET git_url = ?, git_branch = ? " +
|
"SET git_url = ?, git_branch = ? " +
|
||||||
"WHERE id = ?")
|
"WHERE id = ?")
|
||||||
_, err = tx.Exec(q, url, branch, id)
|
_, err = tx.Exec(q, url, branch, id)
|
@ -1,3 +1,3 @@
|
|||||||
delete from project__template where removed = true;
|
delete from `project__template` where `removed` = true;
|
||||||
|
|
||||||
alter table `project__template` drop column `removed`;
|
alter table `project__template` drop column `removed`;
|
||||||
|
11
db/sql/migrations/v2.8.40.sql
Normal file
11
db/sql/migrations/v2.8.40.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
alter table `project` change `alert_chat` `alert_chat` varchar(30);
|
||||||
|
|
||||||
|
alter table `project__template` change `alias` `name` varchar(100) not null;
|
||||||
|
|
||||||
|
alter table `project__inventory` drop column `removed`;
|
||||||
|
|
||||||
|
alter table `project__environment` drop column `removed`;
|
||||||
|
|
||||||
|
alter table `access_key` drop column `removed`;
|
||||||
|
|
||||||
|
alter table `project__repository` drop column `removed`;
|
@ -18,6 +18,10 @@ func (d *SqlDb) GetRepository(projectID int, repositoryID int) (db.Repository, e
|
|||||||
return repository, err
|
return repository, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *SqlDb) GetRepositoryRefs(projectID int, repositoryID int) (db.ObjectReferrers, error) {
|
||||||
|
return d.getObjectRefs(projectID, db.RepositoryProps, repositoryID)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *SqlDb) GetRepositories(projectID int, params db.RetrieveQueryParams) (repositories []db.Repository, err error) {
|
func (d *SqlDb) GetRepositories(projectID int, params db.RetrieveQueryParams) (repositories []db.Repository, err error) {
|
||||||
q := squirrel.Select("*").
|
q := squirrel.Select("*").
|
||||||
From("project__repository pr")
|
From("project__repository pr")
|
||||||
@ -97,7 +101,3 @@ func (d *SqlDb) CreateRepository(repository db.Repository) (newRepo db.Repositor
|
|||||||
func (d *SqlDb) DeleteRepository(projectID int, repositoryId int) error {
|
func (d *SqlDb) DeleteRepository(projectID int, repositoryId int) error {
|
||||||
return d.deleteObject(projectID, db.RepositoryProps, repositoryId)
|
return d.deleteObject(projectID, db.RepositoryProps, repositoryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SqlDb) DeleteRepositorySoft(projectID int, repositoryId int) error {
|
|
||||||
return d.deleteObjectSoft(projectID, db.RepositoryProps, repositoryId)
|
|
||||||
}
|
|
||||||
|
@ -35,7 +35,7 @@ func (d *SqlDb) getTasks(projectID int, templateID *int, params db.RetrieveQuery
|
|||||||
fields := "task.*"
|
fields := "task.*"
|
||||||
fields += ", tpl.playbook as tpl_playbook" +
|
fields += ", tpl.playbook as tpl_playbook" +
|
||||||
", `user`.name as user_name" +
|
", `user`.name as user_name" +
|
||||||
", tpl.alias as tpl_alias" +
|
", tpl.name as tpl_alias" +
|
||||||
", tpl.type as tpl_type"
|
", tpl.type as tpl_type"
|
||||||
|
|
||||||
q := squirrel.Select(fields).
|
q := squirrel.Select(fields).
|
||||||
|
@ -16,14 +16,14 @@ func (d *SqlDb) CreateTemplate(template db.Template) (newTemplate db.Template, e
|
|||||||
insertID, err := d.insert(
|
insertID, err := d.insert(
|
||||||
"id",
|
"id",
|
||||||
"insert into project__template (project_id, inventory_id, repository_id, environment_id, "+
|
"insert into project__template (project_id, inventory_id, repository_id, environment_id, "+
|
||||||
"alias, playbook, arguments, allow_override_args_in_task, description, vault_key_id, `type`, start_version,"+
|
"name, playbook, arguments, allow_override_args_in_task, description, vault_key_id, `type`, start_version,"+
|
||||||
"build_template_id, view_id, autorun, survey_vars)"+
|
"build_template_id, view_id, autorun, survey_vars)"+
|
||||||
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
template.ProjectID,
|
template.ProjectID,
|
||||||
template.InventoryID,
|
template.InventoryID,
|
||||||
template.RepositoryID,
|
template.RepositoryID,
|
||||||
template.EnvironmentID,
|
template.EnvironmentID,
|
||||||
template.Alias,
|
template.Name,
|
||||||
template.Playbook,
|
template.Playbook,
|
||||||
template.Arguments,
|
template.Arguments,
|
||||||
template.AllowOverrideArgsInTask,
|
template.AllowOverrideArgsInTask,
|
||||||
@ -63,7 +63,7 @@ func (d *SqlDb) UpdateTemplate(template db.Template) error {
|
|||||||
"inventory_id=?, "+
|
"inventory_id=?, "+
|
||||||
"repository_id=?, "+
|
"repository_id=?, "+
|
||||||
"environment_id=?, "+
|
"environment_id=?, "+
|
||||||
"alias=?, "+
|
"name=?, "+
|
||||||
"playbook=?, "+
|
"playbook=?, "+
|
||||||
"arguments=?, "+
|
"arguments=?, "+
|
||||||
"allow_override_args_in_task=?, "+
|
"allow_override_args_in_task=?, "+
|
||||||
@ -79,7 +79,7 @@ func (d *SqlDb) UpdateTemplate(template db.Template) error {
|
|||||||
template.InventoryID,
|
template.InventoryID,
|
||||||
template.RepositoryID,
|
template.RepositoryID,
|
||||||
template.EnvironmentID,
|
template.EnvironmentID,
|
||||||
template.Alias,
|
template.Name,
|
||||||
template.Playbook,
|
template.Playbook,
|
||||||
template.Arguments,
|
template.Arguments,
|
||||||
template.AllowOverrideArgsInTask,
|
template.AllowOverrideArgsInTask,
|
||||||
@ -103,7 +103,7 @@ func (d *SqlDb) GetTemplates(projectID int, filter db.TemplateFilter, params db.
|
|||||||
"pt.inventory_id",
|
"pt.inventory_id",
|
||||||
"pt.repository_id",
|
"pt.repository_id",
|
||||||
"pt.environment_id",
|
"pt.environment_id",
|
||||||
"pt.alias",
|
"pt.name",
|
||||||
"pt.playbook",
|
"pt.playbook",
|
||||||
"pt.arguments",
|
"pt.arguments",
|
||||||
"pt.allow_override_args_in_task",
|
"pt.allow_override_args_in_task",
|
||||||
@ -129,7 +129,7 @@ func (d *SqlDb) GetTemplates(projectID int, filter db.TemplateFilter, params db.
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch params.SortBy {
|
switch params.SortBy {
|
||||||
case "alias", "playbook":
|
case "name", "playbook":
|
||||||
q = q.Where("pt.project_id=?", projectID).
|
q = q.Where("pt.project_id=?", projectID).
|
||||||
OrderBy("pt." + params.SortBy + " " + order)
|
OrderBy("pt." + params.SortBy + " " + order)
|
||||||
case "inventory":
|
case "inventory":
|
||||||
@ -146,7 +146,7 @@ func (d *SqlDb) GetTemplates(projectID int, filter db.TemplateFilter, params db.
|
|||||||
OrderBy("pr.name " + order)
|
OrderBy("pr.name " + order)
|
||||||
default:
|
default:
|
||||||
q = q.Where("pt.project_id=?", projectID).
|
q = q.Where("pt.project_id=?", projectID).
|
||||||
OrderBy("pt.alias " + order)
|
OrderBy("pt.name " + order)
|
||||||
}
|
}
|
||||||
|
|
||||||
query, args, err := q.ToSql()
|
query, args, err := q.ToSql()
|
||||||
|
@ -10,17 +10,17 @@ import (
|
|||||||
"github.com/ansible-semaphore/semaphore/util"
|
"github.com/ansible-semaphore/semaphore/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const emailTemplate = `Subject: Task '{{ .Alias }}' failed
|
const emailTemplate = `Subject: Task '{{ .Name }}' failed
|
||||||
|
|
||||||
Task {{ .TaskID }} with template '{{ .Alias }}' has failed!
|
Task {{ .TaskID }} with template '{{ .Name }}' has failed!
|
||||||
Task log: <a href='{{ .TaskURL }}'>{{ .TaskURL }}</a>`
|
Task log: <a href='{{ .TaskURL }}'>{{ .TaskURL }}</a>`
|
||||||
|
|
||||||
const telegramTemplate = `{"chat_id": "{{ .ChatID }}","parse_mode":"HTML","text":"<code>{{ .Alias }}</code>\n#{{ .TaskID }} <b>{{ .TaskResult }}</b> <code>{{ .TaskVersion }}</code> {{ .TaskDescription }}\nby {{ .Author }}\n{{ .TaskURL }}"}`
|
const telegramTemplate = `{"chat_id": "{{ .ChatID }}","parse_mode":"HTML","text":"<code>{{ .Name }}</code>\n#{{ .TaskID }} <b>{{ .TaskResult }}</b> <code>{{ .TaskVersion }}</code> {{ .TaskDescription }}\nby {{ .Author }}\n{{ .TaskURL }}"}`
|
||||||
|
|
||||||
// Alert represents an alert that will be templated and sent to the appropriate service
|
// Alert represents an alert that will be templated and sent to the appropriate service
|
||||||
type Alert struct {
|
type Alert struct {
|
||||||
TaskID string
|
TaskID string
|
||||||
Alias string
|
Name string
|
||||||
TaskURL string
|
TaskURL string
|
||||||
ChatID string
|
ChatID string
|
||||||
TaskResult string
|
TaskResult string
|
||||||
@ -39,7 +39,7 @@ func (t *TaskRunner) sendMailAlert() {
|
|||||||
var mailBuffer bytes.Buffer
|
var mailBuffer bytes.Buffer
|
||||||
alert := Alert{
|
alert := Alert{
|
||||||
TaskID: strconv.Itoa(t.task.ID),
|
TaskID: strconv.Itoa(t.task.ID),
|
||||||
Alias: t.template.Alias,
|
Name: t.template.Name,
|
||||||
TaskURL: util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID),
|
TaskURL: util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID),
|
||||||
}
|
}
|
||||||
tpl := template.New("mail body template")
|
tpl := template.New("mail body template")
|
||||||
@ -103,7 +103,7 @@ func (t *TaskRunner) sendTelegramAlert() {
|
|||||||
|
|
||||||
alert := Alert{
|
alert := Alert{
|
||||||
TaskID: strconv.Itoa(t.task.ID),
|
TaskID: strconv.Itoa(t.task.ID),
|
||||||
Alias: t.template.Alias,
|
Name: t.template.Name,
|
||||||
TaskURL: util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID) + "/templates/" + strconv.Itoa(t.template.ID) + "?t=" + strconv.Itoa(t.task.ID),
|
TaskURL: util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID) + "/templates/" + strconv.Itoa(t.template.ID) + "?t=" + strconv.Itoa(t.task.ID),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
TaskResult: strings.ToUpper(t.task.Status),
|
TaskResult: strings.ToUpper(t.task.Status),
|
||||||
|
@ -18,13 +18,24 @@ type logRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TaskPool struct {
|
type TaskPool struct {
|
||||||
|
// queue contains list of tasks in status TaskWaitingStatus.
|
||||||
queue []*TaskRunner
|
queue []*TaskRunner
|
||||||
|
|
||||||
|
// register channel used to put tasks to queue.
|
||||||
register chan *TaskRunner
|
register chan *TaskRunner
|
||||||
|
|
||||||
activeProj map[int]*TaskRunner
|
activeProj map[int]*TaskRunner
|
||||||
activeNodes map[string]*TaskRunner
|
activeNodes map[string]*TaskRunner
|
||||||
|
|
||||||
|
// running is a number of task with status TaskRunningStatus.
|
||||||
running int
|
running int
|
||||||
|
|
||||||
|
// runningTasks contains tasks with status TaskRunningStatus.
|
||||||
runningTasks map[int]*TaskRunner
|
runningTasks map[int]*TaskRunner
|
||||||
|
|
||||||
|
// logger channel used to putting log records to database.
|
||||||
logger chan logRecord
|
logger chan logRecord
|
||||||
|
|
||||||
store db.Store
|
store db.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ func (t *TaskRunner) destroyKeys() {
|
|||||||
|
|
||||||
func (t *TaskRunner) createTaskEvent() {
|
func (t *TaskRunner) createTaskEvent() {
|
||||||
objType := db.EventTask
|
objType := db.EventTask
|
||||||
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " finished - " + strings.ToUpper(t.task.Status)
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Name + ")" + " finished - " + strings.ToUpper(t.task.Status)
|
||||||
|
|
||||||
_, err := t.pool.store.CreateEvent(db.Event{
|
_, err := t.pool.store.CreateEvent(db.Event{
|
||||||
UserID: t.task.UserID,
|
UserID: t.task.UserID,
|
||||||
@ -190,7 +190,7 @@ func (t *TaskRunner) prepareRun() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
objType := db.EventTask
|
objType := db.EventTask
|
||||||
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " is preparing"
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Name + ")" + " is preparing"
|
||||||
_, err = t.pool.store.CreateEvent(db.Event{
|
_, err = t.pool.store.CreateEvent(db.Event{
|
||||||
UserID: t.task.UserID,
|
UserID: t.task.UserID,
|
||||||
ProjectID: &t.task.ProjectID,
|
ProjectID: &t.task.ProjectID,
|
||||||
@ -204,7 +204,7 @@ func (t *TaskRunner) prepareRun() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Prepare TaskRunner with template: " + t.template.Alias + "\n")
|
t.Log("Prepare TaskRunner with template: " + t.template.Name + "\n")
|
||||||
|
|
||||||
t.updateStatus()
|
t.updateStatus()
|
||||||
|
|
||||||
@ -287,7 +287,7 @@ func (t *TaskRunner) run() {
|
|||||||
t.setStatus(db.TaskRunningStatus)
|
t.setStatus(db.TaskRunningStatus)
|
||||||
|
|
||||||
objType := db.EventTask
|
objType := db.EventTask
|
||||||
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Alias + ")" + " is running"
|
desc := "Task ID " + strconv.Itoa(t.task.ID) + " (" + t.template.Name + ")" + " is running"
|
||||||
|
|
||||||
_, err := t.pool.store.CreateEvent(db.Event{
|
_, err := t.pool.store.CreateEvent(db.Event{
|
||||||
UserID: t.task.UserID,
|
UserID: t.task.UserID,
|
||||||
@ -303,7 +303,7 @@ func (t *TaskRunner) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Started: " + strconv.Itoa(t.task.ID))
|
t.Log("Started: " + strconv.Itoa(t.task.ID))
|
||||||
t.Log("Run TaskRunner with template: " + t.template.Alias + "\n")
|
t.Log("Run TaskRunner with template: " + t.template.Name + "\n")
|
||||||
|
|
||||||
// TODO: ?????
|
// TODO: ?????
|
||||||
if t.task.Status == db.TaskStoppingStatus {
|
if t.task.Status == db.TaskStoppingStatus {
|
||||||
|
@ -65,7 +65,7 @@ func TestPopulateDetails(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tpl, err := store.CreateTemplate(db.Template{
|
tpl, err := store.CreateTemplate(db.Template{
|
||||||
Alias: "Test",
|
Name: "Test",
|
||||||
Playbook: "test.yml",
|
Playbook: "test.yml",
|
||||||
ProjectID: proj.ID,
|
ProjectID: proj.ID,
|
||||||
RepositoryID: repo.ID,
|
RepositoryID: repo.ID,
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
class="breadcrumbs__item breadcrumbs__item--link"
|
class="breadcrumbs__item breadcrumbs__item--link"
|
||||||
:to="`/project/${projectId}/templates/${template ? template.id : null}`"
|
:to="`/project/${projectId}/templates/${template ? template.id : null}`"
|
||||||
@click="taskLogDialog = false"
|
@click="taskLogDialog = false"
|
||||||
>{{ template ? template.alias : null }}
|
>{{ template ? template.name : null }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<v-icon>mdi-chevron-right</v-icon>
|
<v-icon>mdi-chevron-right</v-icon>
|
||||||
<span class="breadcrumbs__item">Task #{{ task ? task.id : null }}</span>
|
<span class="breadcrumbs__item">Task #{{ task ? task.id : null }}</span>
|
||||||
|
@ -41,6 +41,7 @@ Can use used in tandem with ItemFormBase.js. See KeyForm.vue for example.
|
|||||||
color="blue darken-1"
|
color="blue darken-1"
|
||||||
text
|
text
|
||||||
@click="needSave = true"
|
@click="needSave = true"
|
||||||
|
v-if="saveButtonText != null"
|
||||||
>
|
>
|
||||||
{{ saveButtonText }}
|
{{ saveButtonText }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
@ -2,12 +2,15 @@ import axios from 'axios';
|
|||||||
import EventBus from '@/event-bus';
|
import EventBus from '@/event-bus';
|
||||||
import EditDialog from '@/components/EditDialog.vue';
|
import EditDialog from '@/components/EditDialog.vue';
|
||||||
import YesNoDialog from '@/components/YesNoDialog.vue';
|
import YesNoDialog from '@/components/YesNoDialog.vue';
|
||||||
|
import ObjectRefsView from '@/components/ObjectRefsView.vue';
|
||||||
|
|
||||||
import { getErrorMessage } from '@/lib/error';
|
import { getErrorMessage } from '@/lib/error';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
YesNoDialog,
|
YesNoDialog,
|
||||||
EditDialog,
|
EditDialog,
|
||||||
|
ObjectRefsView,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@ -19,9 +22,13 @@ export default {
|
|||||||
return {
|
return {
|
||||||
headers: this.getHeaders(),
|
headers: this.getHeaders(),
|
||||||
items: null,
|
items: null,
|
||||||
|
|
||||||
itemId: null,
|
itemId: null,
|
||||||
editDialog: null,
|
editDialog: null,
|
||||||
deleteItemDialog: null,
|
deleteItemDialog: null,
|
||||||
|
|
||||||
|
itemRefs: null,
|
||||||
|
itemRefsDialog: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -32,7 +39,8 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// eslint-disable-next-line no-empty-function
|
// eslint-disable-next-line no-empty-function
|
||||||
async beforeLoadItems() { },
|
async beforeLoadItems() {
|
||||||
|
},
|
||||||
|
|
||||||
getSingleItemUrl() {
|
getSingleItemUrl() {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
@ -54,8 +62,23 @@ export default {
|
|||||||
await this.loadItems();
|
await this.loadItems();
|
||||||
},
|
},
|
||||||
|
|
||||||
askDeleteItem(itemId) {
|
async askDeleteItem(itemId) {
|
||||||
this.itemId = itemId;
|
this.itemId = itemId;
|
||||||
|
|
||||||
|
this.itemRefs = (await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `${this.getSingleItemUrl()}/refs`,
|
||||||
|
responseType: 'json',
|
||||||
|
})).data;
|
||||||
|
|
||||||
|
if (this.itemRefs.templates.length > 0
|
||||||
|
|| this.itemRefs.repositories.length > 0
|
||||||
|
|| this.itemRefs.inventories.length > 0
|
||||||
|
|| this.itemRefs.schedules.length > 0) {
|
||||||
|
this.itemRefsDialog = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.deleteItemDialog = true;
|
this.deleteItemDialog = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
33
web2/src/components/ObjectRefsView.vue
Normal file
33
web2/src/components/ObjectRefsView.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>{{ title }}</div>
|
||||||
|
|
||||||
|
<div v-if="objectRefs.templates.length > 0">
|
||||||
|
<div>Templates</div>
|
||||||
|
<router-link
|
||||||
|
v-for="t in objectRefs.templates"
|
||||||
|
:key="t.id"
|
||||||
|
:to="`/project/${projectId}/templates/${t.id}`">{{ t.name }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="objectRefs.inventories.length > 0">
|
||||||
|
<div>Inventories</div>
|
||||||
|
<a v-for="referrer in objectRefs.inventories" :key="referrer.id">{{ referrer.name }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="objectRefs.repositories.length > 0">
|
||||||
|
<div>Repositories</div>
|
||||||
|
<a v-for="referrer in objectRefs.repositories" :key="referrer.id">{{ referrer.name }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
objectRefs: Object,
|
||||||
|
projectId: Number,
|
||||||
|
title: String,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<template v-slot:title={}>
|
<template v-slot:title={}>
|
||||||
<v-icon class="mr-4">{{ TEMPLATE_TYPE_ICONS[template.type] }}</v-icon>
|
<v-icon class="mr-4">{{ TEMPLATE_TYPE_ICONS[template.type] }}</v-icon>
|
||||||
<span class="breadcrumbs__item">{{ template.alias }}</span>
|
<span class="breadcrumbs__item">{{ template.name }}</span>
|
||||||
<v-icon>mdi-chevron-right</v-icon>
|
<v-icon>mdi-chevron-right</v-icon>
|
||||||
<span class="breadcrumbs__item">New Task</span>
|
<span class="breadcrumbs__item">New Task</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -98,7 +98,7 @@
|
|||||||
label="Build Template"
|
label="Build Template"
|
||||||
:items="buildTemplates"
|
:items="buildTemplates"
|
||||||
item-value="id"
|
item-value="id"
|
||||||
item-text="alias"
|
item-text="name"
|
||||||
:rules="[v => !!v || 'Build Template is required']"
|
:rules="[v => !!v || 'Build Template is required']"
|
||||||
required
|
required
|
||||||
:disabled="formSaving"
|
:disabled="formSaving"
|
||||||
@ -117,9 +117,9 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="item.alias"
|
v-model="item.name"
|
||||||
label="Playbook Alias"
|
label="Playbook Name"
|
||||||
:rules="[v => !!v || 'Playbook Alias is required']"
|
:rules="[v => !!v || 'Playbook Name is required']"
|
||||||
required
|
required
|
||||||
:disabled="formSaving"
|
:disabled="formSaving"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
@ -19,6 +19,17 @@
|
|||||||
</template>
|
</template>
|
||||||
</EditDialog>
|
</EditDialog>
|
||||||
|
|
||||||
|
<EditDialog
|
||||||
|
v-model="itemRefsDialog"
|
||||||
|
title="Environment in use"
|
||||||
|
:max-width="500"
|
||||||
|
hide-buttons
|
||||||
|
>
|
||||||
|
<template>
|
||||||
|
<ObjectRefsView :object-refs="itemRefs" />
|
||||||
|
</template>
|
||||||
|
</EditDialog>
|
||||||
|
|
||||||
<YesNoDialog
|
<YesNoDialog
|
||||||
title="Delete environment"
|
title="Delete environment"
|
||||||
text="Are you really want to delete this environment?"
|
text="Are you really want to delete this environment?"
|
||||||
|
@ -18,6 +18,20 @@
|
|||||||
</template>
|
</template>
|
||||||
</EditDialog>
|
</EditDialog>
|
||||||
|
|
||||||
|
<EditDialog
|
||||||
|
v-model="itemRefsDialog"
|
||||||
|
title="Can't delete the repository"
|
||||||
|
:max-width="500"
|
||||||
|
>
|
||||||
|
<template v-slot:form="{}">
|
||||||
|
<ObjectRefsView
|
||||||
|
title="The repository used by following resources:"
|
||||||
|
:object-refs="itemRefs"
|
||||||
|
:project-id="projectId"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</EditDialog>
|
||||||
|
|
||||||
<YesNoDialog
|
<YesNoDialog
|
||||||
title="Delete repository"
|
title="Delete repository"
|
||||||
text="Are you really want to delete this repository?"
|
text="Are you really want to delete this repository?"
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
: `/project/${projectId}/templates/`"
|
: `/project/${projectId}/templates/`"
|
||||||
>Task Templates</router-link>
|
>Task Templates</router-link>
|
||||||
<v-icon>mdi-chevron-right</v-icon>
|
<v-icon>mdi-chevron-right</v-icon>
|
||||||
<span class="breadcrumbs__item">{{ item.alias }}</span>
|
<span class="breadcrumbs__item">{{ item.name }}</span>
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
@ -277,7 +277,7 @@ export default {
|
|||||||
|
|
||||||
EventBus.$emit('i-snackbar', {
|
EventBus.$emit('i-snackbar', {
|
||||||
color: 'success',
|
color: 'success',
|
||||||
text: `Template "${this.item.alias}" deleted`,
|
text: `Template "${this.item.name}" deleted`,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.$router.push({
|
await this.$router.push({
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
opacity: viewItemsLoading ? 0.3 : 1,
|
opacity: viewItemsLoading ? 0.3 : 1,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template v-slot:item.alias="{ item }">
|
<template v-slot:item.name="{ item }">
|
||||||
<v-icon class="mr-3" small>
|
<v-icon class="mr-3" small>
|
||||||
{{ TEMPLATE_TYPE_ICONS[item.type] }}
|
{{ TEMPLATE_TYPE_ICONS[item.type] }}
|
||||||
</v-icon>
|
</v-icon>
|
||||||
@ -140,7 +140,7 @@
|
|||||||
:to="viewId
|
:to="viewId
|
||||||
? `/project/${projectId}/views/${viewId}/templates/${item.id}`
|
? `/project/${projectId}/views/${viewId}/templates/${item.id}`
|
||||||
: `/project/${projectId}/templates/${item.id}`"
|
: `/project/${projectId}/templates/${item.id}`"
|
||||||
>{{ item.alias }}</router-link>
|
>{{ item.name }}</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:item.version="{ item }">
|
<template v-slot:item.version="{ item }">
|
||||||
@ -300,7 +300,7 @@ export default {
|
|||||||
if (this.itemId == null || this.itemId === 'new') {
|
if (this.itemId == null || this.itemId === 'new') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return this.items.find((x) => x.id === this.itemId).alias;
|
return this.items.find((x) => x.id === this.itemId).name;
|
||||||
},
|
},
|
||||||
|
|
||||||
isLoaded() {
|
isLoaded() {
|
||||||
@ -403,8 +403,8 @@ export default {
|
|||||||
getHeaders() {
|
getHeaders() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
text: 'Alias',
|
text: 'Name',
|
||||||
value: 'alias',
|
value: 'name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Version',
|
text: 'Version',
|
||||||
|
Loading…
Reference in New Issue
Block a user