Semaphore/.dredd/hooks/capabilities.go
2022-11-09 21:30:35 +05:00

226 lines
6.2 KiB
Go

package main
import (
"encoding/json"
"github.com/ansible-semaphore/semaphore/db"
trans "github.com/snikch/goodman/transaction"
"regexp"
"strconv"
"strings"
)
// STATE
// Runtime created objects we need to reference in test setups
var testRunnerUser *db.User
var userPathTestUser *db.User
var userProject *db.Project
var userKey *db.AccessKey
var task *db.Task
var schedule *db.Schedule
var view *db.View
// Runtime created simple ID values for some items we need to reference in other objects
var repoID int
var inventoryID int
var environmentID int
var templateID int
var capabilities = map[string][]string{
"user": {},
"project": {"user"},
"repository": {"access_key"},
"inventory": {"repository"},
"environment": {"repository"},
"template": {"repository", "inventory", "environment", "view"},
"task": {"template"},
"schedule": {"template"},
"view": {},
}
func capabilityWrapper(cap string) func(t *trans.Transaction) {
return func(t *trans.Transaction) {
addCapabilities([]string{cap})
}
}
func addCapabilities(caps []string) {
dbConnect()
defer store.Close("")
resolved := make([]string, 0)
uid := getUUID()
resolveCapability(caps, resolved, uid)
}
func resolveCapability(caps []string, resolved []string, uid string) {
for _, v := range caps {
//if cap has deps resolve them
if val, ok := capabilities[v]; ok {
resolveCapability(val, resolved, uid)
}
//skip if already resolved
if _, exists := stringInSlice(v, resolved); exists {
continue
}
//Add dep specific stuff
switch v {
case "schedule":
schedule = addSchedule()
case "view":
view = addView()
case "user":
userPathTestUser = addUser()
case "project":
userProject = addProject()
//allow the admin user (test executor) to manipulate the project
addUserProjectRelation(userProject.ID, testRunnerUser.ID)
addUserProjectRelation(userProject.ID, userPathTestUser.ID)
case "access_key":
userKey = addAccessKey(&userProject.ID)
case "repository":
pRepo, err := store.CreateRepository(db.Repository{
ProjectID: userProject.ID,
GitURL: "git@github.com/ansible,semaphore/semaphore",
GitBranch: "develop",
SSHKeyID: userKey.ID,
Name: "ITR-" + uid,
})
printError(err)
repoID = pRepo.ID
case "inventory":
res, err := store.CreateInventory(db.Inventory{
ProjectID: userProject.ID,
Name: "ITI-" + uid,
Type: "static",
SSHKeyID: &userKey.ID,
Inventory: "Test Inventory",
})
printError(err)
inventoryID = res.ID
case "environment":
pwd := "test-pass"
env := "{}"
res, err := store.CreateEnvironment(db.Environment{
ProjectID: userProject.ID,
Name: "ITI-" + uid,
JSON: "{}",
Password: &pwd,
ENV: &env,
})
printError(err)
environmentID = res.ID
case "template":
args := "[]"
desc := "Hello, World!"
res, err := store.CreateTemplate(db.Template{
ProjectID: userProject.ID,
InventoryID: inventoryID,
RepositoryID: repoID,
EnvironmentID: &environmentID,
Name: "Test-" + uid,
Playbook: "test-playbook.yml",
Arguments: &args,
AllowOverrideArgsInTask: false,
Description: &desc,
ViewID: &view.ID,
})
printError(err)
templateID = res.ID
case "task":
task = addTask()
default:
panic("unknown capability " + v)
}
resolved = append(resolved, v)
}
}
// HOOKS
var skipTest = func(t *trans.Transaction) {
t.Skip = true
}
// Contains all the substitutions for paths under test
// The parameter example value in the api-doc should respond to the index+1 of the function in this slice
// ie the project id, with example value 1, will be replaced by the return value of pathSubPatterns[0]
var pathSubPatterns = []func() string{
func() string { return strconv.Itoa(userProject.ID) },
func() string { return strconv.Itoa(userPathTestUser.ID) },
func() string { return strconv.Itoa(userKey.ID) },
func() string { return strconv.Itoa(int(repoID)) },
func() string { return strconv.Itoa(int(inventoryID)) },
func() string { return strconv.Itoa(int(environmentID)) },
func() string { return strconv.Itoa(int(templateID)) },
func() string { return strconv.Itoa(task.ID) },
func() string { return strconv.Itoa(schedule.ID) },
func() string { return strconv.Itoa(view.ID) },
}
// alterRequestPath with the above slice of functions
func alterRequestPath(t *trans.Transaction) {
pathArgs := strings.Split(t.FullPath, "/")
exploded := make([]string, len(pathArgs))
copy(exploded, pathArgs)
for k, v := range pathSubPatterns {
pos, exists := stringInSlice(strconv.Itoa(k+1), exploded)
if exists {
pathArgs[pos] = v()
}
}
t.FullPath = strings.Join(pathArgs, "/")
t.Request.URI = t.FullPath
}
func alterRequestBody(t *trans.Transaction) {
var request map[string]interface{}
json.Unmarshal([]byte(t.Request.Body), &request)
if userProject != nil {
bodyFieldProcessor("project_id", userProject.ID, &request)
}
bodyFieldProcessor("json", "{}", &request)
if userKey != nil {
bodyFieldProcessor("ssh_key_id", userKey.ID, &request)
}
bodyFieldProcessor("environment_id", environmentID, &request)
bodyFieldProcessor("inventory_id", inventoryID, &request)
bodyFieldProcessor("repository_id", repoID, &request)
bodyFieldProcessor("template_id", templateID, &request)
if task != nil {
bodyFieldProcessor("task_id", task.ID, &request)
}
if schedule != nil {
bodyFieldProcessor("schedule_id", schedule.ID, &request)
}
if view != nil {
bodyFieldProcessor("view_id", view.ID, &request)
}
// Inject object ID to body for PUT requests
if strings.ToLower(t.Request.Method) == "put" {
putRequestPathRE := regexp.MustCompile(`/api/(?:project/\d+/)?\w+/(\d+)/?$`)
m := putRequestPathRE.FindStringSubmatch(t.FullPath)
if len(m) > 0 {
objectID, err := strconv.Atoi(m[1])
if err != nil {
panic("Invalid object ID in PUT request " + t.FullPath)
}
request["id"] = objectID
} else {
panic("Unexpected PUT request " + t.FullPath)
}
}
out, _ := json.Marshal(request)
t.Request.Body = string(out)
}
func bodyFieldProcessor(id string, sub interface{}, request *map[string]interface{}) {
if _, ok := (*request)[id]; ok {
(*request)[id] = sub
}
}