WIP: tests, REST API support (auth).
This commit is contained in:
177
src/main/nim/strawbosspkg/configuration.nim
Normal file
177
src/main/nim/strawbosspkg/configuration.nim
Normal file
@ -0,0 +1,177 @@
|
||||
import logging, json, os, nre, sequtils, strtabs, tables, times
|
||||
import private/util
|
||||
|
||||
# Types
|
||||
#
|
||||
type
|
||||
BuildStatus* = object
|
||||
state*, details*: string
|
||||
|
||||
Step* = object
|
||||
name*, stepCmd*, workingDir*: string
|
||||
artifacts*, cmdInput*, depends*, expectedEnv*: seq[string]
|
||||
dontSkip*: bool
|
||||
|
||||
ProjectConfig* = object
|
||||
name*: string
|
||||
versionCmd*: string
|
||||
steps*: Table[string, Step]
|
||||
|
||||
ProjectDef* = object
|
||||
cfgFilePath*, defaultBranch*, name*, repo*: string
|
||||
envVars*: StringTableRef
|
||||
|
||||
RunRequest* = object
|
||||
projectName*, stepName*, buildRef*, workspaceDir*: string
|
||||
forceRebuild*: bool
|
||||
|
||||
User* = object
|
||||
name*: string
|
||||
hashedPwd*: string
|
||||
|
||||
UserRef* = ref User
|
||||
|
||||
StrawBossConfig* = object
|
||||
artifactsRepo*: string
|
||||
authSecret*: string
|
||||
projects*: seq[ProjectDef]
|
||||
users*: seq[UserRef]
|
||||
|
||||
# Equality on custom types
|
||||
proc `==`*(a, b: UserRef): bool = result = a.name == b.name
|
||||
|
||||
proc `==`*(a, b: ProjectDef): bool =
|
||||
if a.envVars.len != b.envVars.len: return false
|
||||
|
||||
for k, v in a.envVars:
|
||||
if not b.envVars.hasKey(k) or a.envVars[k] != b.envVars[k]: return false
|
||||
|
||||
return
|
||||
a.name == b.name and
|
||||
a.cfgFilePath == b.cfgFilePath and
|
||||
a.defaultBranch == b.defaultBranch and
|
||||
a.repo == b.repo
|
||||
|
||||
# internal utils
|
||||
|
||||
let nullNode = newJNull()
|
||||
proc getIfExists(n: JsonNode, key: string): JsonNode =
|
||||
# convenience method to get a key from a JObject or return null
|
||||
result = if n.hasKey(key): n[key]
|
||||
else: nullNode
|
||||
|
||||
proc getOrFail(n: JsonNode, key: string, objName: string = ""): JsonNode =
|
||||
# convenience method to get a key from a JObject or raise an exception
|
||||
if not n.hasKey(key): raiseEx objName & " missing key " & key
|
||||
return n[key]
|
||||
|
||||
# Configuration parsing code
|
||||
|
||||
proc loadStrawBossConfig*(cfgFile: string): StrawBossConfig =
|
||||
if not existsFile(cfgFile):
|
||||
raiseEx "strawboss config file not found: " & cfgFile
|
||||
|
||||
let jsonCfg = parseFile(cfgFile)
|
||||
|
||||
var projectDefs: seq[ProjectDef] = @[]
|
||||
|
||||
for pJson in jsonCfg.getIfExists("projects").getElems:
|
||||
var envVars = newStringTable(modeCaseSensitive)
|
||||
for k, v in pJson.getIfExists("envVars").getFields: envVars[k] = v.getStr("")
|
||||
|
||||
projectDefs.add(
|
||||
ProjectDef(
|
||||
cfgFilePath: pJson.getIfExists("cfgFilePath").getStr("strawboss.json"),
|
||||
defaultBranch: pJson.getIfExists("defaultBranch").getStr("master"),
|
||||
name: pJson.getOrFail("name", "project definition").getStr,
|
||||
envVars: envVars,
|
||||
repo: pJson.getOrFail("repo", "project definition").getStr))
|
||||
|
||||
var users: seq[UserRef] = @[]
|
||||
|
||||
for uJson in jsonCfg.getIfExists("users").getElems:
|
||||
users.add(UserRef(
|
||||
name: uJson.getOrFail("name", "user record").getStr,
|
||||
hashedPwd: uJson.getOrFail("hashedPwd", "user record").getStr))
|
||||
|
||||
result = StrawBossConfig(
|
||||
artifactsRepo: jsonCfg.getIfExists("artifactsRepo").getStr("artifacts"),
|
||||
authSecret: jsonCfg.getOrFail("authSecret", "strawboss config").getStr,
|
||||
projects: projectDefs,
|
||||
users: users)
|
||||
|
||||
proc loadProjectConfig*(cfgFile: string): ProjectConfig =
|
||||
if not existsFile(cfgFile):
|
||||
raiseEx "project config file not found: " & cfgFile
|
||||
|
||||
let jsonCfg = parseFile(cfgFile)
|
||||
|
||||
if not jsonCfg.hasKey("steps"):
|
||||
raiseEx "project configuration is missing steps definition"
|
||||
|
||||
var steps = initTable[string, Step]()
|
||||
for sName, pJson in jsonCfg.getOrFail("steps", "project configuration").getFields:
|
||||
steps[sName] = Step(
|
||||
name: sName,
|
||||
workingDir: pJson.getIfExists("workingDir").getStr("."),
|
||||
stepCmd: pJson.getIfExists("stepCmd").getStr("sh"),
|
||||
depends: pJson.getIfExists("depends").getElems.mapIt(it.getStr),
|
||||
artifacts: pJson.getIfExists("artifacts").getElems.mapIt(it.getStr),
|
||||
cmdInput: pJson.getIfExists("cmdInput").getElems.mapIt(it.getStr),
|
||||
expectedEnv: pJson.getIfExists("expectedEnv").getElems.mapIt(it.getStr),
|
||||
dontSkip: pJson.getIfExists("dontSkip").getBVal(false))
|
||||
|
||||
if steps[sName].stepCmd == "sh" and steps[sName].cmdInput.len == 0:
|
||||
warn "Step " & sName & " uses 'sh' as its command but has no cmdInput."
|
||||
|
||||
result = ProjectConfig(
|
||||
name: jsonCfg.getOrFail("name", "project configuration").getStr,
|
||||
versionCmd: jsonCfg.getIfExists("versionCmd").getStr("git describe --tags --always"),
|
||||
steps: steps)
|
||||
|
||||
proc loadBuildStatus*(statusFile: string): BuildStatus =
|
||||
if not existsFile(statusFile): raiseEx "status file not found: " & statusFile
|
||||
let jsonObj = parseFile(statusFile)
|
||||
|
||||
result = BuildStatus(
|
||||
state: jsonObj.getOrFail("state", "build status").getStr,
|
||||
details: jsonObj.getIfExists("details").getStr("") )
|
||||
|
||||
# TODO: unused and untested, add tests if we start using this
|
||||
proc parseRunRequest*(reqStr: string): RunRequest =
|
||||
let reqJson = parseJson(reqStr)
|
||||
|
||||
result = RunRequest(
|
||||
projectName: reqJson.getOrFail("projectName", "RunRequest").getStr,
|
||||
stepName: reqJson.getOrFail("stepName", "RunRequest").getStr,
|
||||
buildRef: reqJson.getOrFail("buildRef", "RunRequest").getStr,
|
||||
workspaceDir: reqJson.getOrFail("workspaceDir", "RunRequest").getStr,
|
||||
forceRebuild: reqJson.getOrFail("forceRebuild", "RunRequest").getBVal)
|
||||
|
||||
# TODO: can we use the marshal module for this?
|
||||
proc `%`*(s: BuildStatus): JsonNode =
|
||||
result = %* {
|
||||
"state": s.state,
|
||||
"details": s.details
|
||||
}
|
||||
|
||||
proc `%`*(p: ProjectDef): JsonNode =
|
||||
result = %* {
|
||||
"name": p.name,
|
||||
"cfgFilePath": p.cfgFilePath,
|
||||
"defaultBranch": p.defaultBranch,
|
||||
"repo": p.repo }
|
||||
|
||||
# TODO: envVars?
|
||||
|
||||
proc `%`*(req: RunRequest): JsonNode =
|
||||
result = %* {
|
||||
"projectName": req.projectName,
|
||||
"stepName": req.stepName,
|
||||
"buildRef": req.buildRef,
|
||||
"workspaceDir": req.workspaceDir,
|
||||
"forceRebuild": req.forceRebuild
|
||||
}
|
||||
|
||||
proc `$`*(s: BuildStatus): string = result = pretty(%s)
|
||||
proc `$`*(req: RunRequest): string = result = pretty(%req)
|
Reference in New Issue
Block a user