Semaphore/.dredd/hooks/capabilities.go

267 lines
7.7 KiB
Go
Raw Permalink Normal View History

package main
import (
"encoding/json"
2020-12-04 23:22:05 +01:00
"regexp"
"strconv"
"strings"
2024-01-11 14:03:09 +01:00
"github.com/semaphoreui/semaphore/db"
2024-01-11 14:03:09 +01:00
trans "github.com/snikch/goodman/transaction"
)
// STATE
2021-09-09 20:56:15 +02:00
// 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
2021-09-06 16:26:35 +02:00
var schedule *db.Schedule
var view *db.View
2024-02-11 20:52:14 +01:00
var integration *db.Integration
var integrationextractvalue *db.IntegrationExtractValue
var integrationmatch *db.IntegrationMatcher
// 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
2024-02-11 20:52:14 +01:00
var integrationID int
var integrationExtractValueID int
var integrationMatchID int
var capabilities = map[string][]string{
2024-02-11 20:52:14 +01:00
"user": {},
"project": {"user"},
"repository": {"access_key"},
"inventory": {"repository"},
"environment": {"repository"},
"template": {"repository", "inventory", "environment", "view"},
"task": {"template"},
"schedule": {"template"},
"view": {},
"integration": {"project", "template"},
"integrationextractvalue": {"integration"},
"integrationmatcher": {"integration"},
}
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 {
2021-09-06 16:26:35 +02:00
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":
2022-11-06 22:12:39 +01:00
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":
2022-11-06 22:15:22 +01:00
res, err := store.CreateInventory(db.Inventory{
2024-03-20 10:44:16 +01:00
ProjectID: userProject.ID,
Name: "ITI-" + uid,
Type: "static",
SSHKeyID: &userKey.ID,
BecomeKeyID: &userKey.ID,
Inventory: "Test Inventory",
2022-11-06 22:15:22 +01:00
})
printError(err)
inventoryID = res.ID
case "environment":
2022-11-06 22:20:53 +01:00
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!"
branch := "main"
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,
App: db.AppAnsible,
GitBranch: &branch,
})
printError(err)
templateID = res.ID
case "task":
task = addTask()
2024-02-11 20:52:14 +01:00
case "integration":
integration = addIntegration()
integrationID = integration.ID
case "integrationextractvalue":
integrationextractvalue = addIntegrationExtractValue()
integrationExtractValueID = integrationextractvalue.ID
case "integrationmatcher":
integrationmatch = addIntegrationMatcher()
integrationMatchID = integrationmatch.ID
2022-06-29 18:25:28 +02:00
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) },
2023-08-27 00:03:04 +02:00
func() string { return strconv.Itoa(repoID) },
func() string { return strconv.Itoa(inventoryID) },
func() string { return strconv.Itoa(environmentID) },
func() string { return strconv.Itoa(templateID) },
func() string { return strconv.Itoa(task.ID) },
2021-09-06 16:26:35 +02:00
func() string { return strconv.Itoa(schedule.ID) },
func() string { return strconv.Itoa(view.ID) },
2024-02-11 20:52:14 +01:00
func() string { return strconv.Itoa(integration.ID) },
func() string { return strconv.Itoa(integrationextractvalue.ID) },
func() string { return strconv.Itoa(integrationmatch.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 {
2024-01-15 20:35:47 +01:00
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)
2024-03-20 13:00:21 +01:00
bodyFieldProcessor("become_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)
}
2021-09-06 16:26:35 +02:00
if schedule != nil {
bodyFieldProcessor("schedule_id", schedule.ID, &request)
}
if view != nil {
bodyFieldProcessor("view_id", view.ID, &request)
}
2024-01-15 20:35:47 +01:00
2024-02-11 20:52:14 +01:00
if integration != nil {
bodyFieldProcessor("integration_id", integration.ID, &request)
2024-01-11 14:03:09 +01:00
}
2024-02-11 20:52:14 +01:00
if integrationextractvalue != nil {
bodyFieldProcessor("value_id", integrationextractvalue.ID, &request)
2024-01-11 14:03:09 +01:00
}
2024-02-11 20:52:14 +01:00
if integrationmatch != nil {
bodyFieldProcessor("matcher_id", integrationmatch.ID, &request)
2024-01-11 14:03:09 +01:00
}
2020-12-04 23:22:05 +01:00
// Inject object ID to body for PUT requests
if strings.ToLower(t.Request.Method) == "put" {
2024-03-04 20:05:18 +01:00
putRequestPathRE := regexp.MustCompile(`\w+/(\d+)/?$`)
2020-12-04 23:22:05 +01:00
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
}
}