WIP Adding native support for docker.
This commit is contained in:
parent
0574f0ec6a
commit
c827beab5e
@ -13,17 +13,16 @@ type
|
|||||||
complete, failed, queued, rejected, running, setup, stepComplete
|
complete, failed, queued, rejected, running, setup, stepComplete
|
||||||
|
|
||||||
BuildStatus* = object
|
BuildStatus* = object
|
||||||
runId*, details*: string
|
runId*, details*, version*: string
|
||||||
state*: BuildState
|
state*: BuildState
|
||||||
|
|
||||||
Step* = object
|
Step* = object
|
||||||
name*, stepCmd*, workingDir*: string
|
containerImage, name*, stepCmd*, workingDir*: string
|
||||||
artifacts*, cmdInput*, depends*, expectedEnv*: seq[string]
|
artifacts*, cmdInput*, depends*, expectedEnv*: seq[string]
|
||||||
dontSkip*: bool
|
dontSkip*: bool
|
||||||
|
|
||||||
ProjectConfig* = object
|
ProjectConfig* = object
|
||||||
name*: string
|
containerImage*, name*, versionCmd*: string
|
||||||
versionCmd*: string
|
|
||||||
steps*: Table[string, Step]
|
steps*: Table[string, Step]
|
||||||
|
|
||||||
ProjectDef* = object
|
ProjectDef* = object
|
||||||
@ -170,14 +169,15 @@ proc loadProjectConfig*(cfgFile: string): ProjectConfig =
|
|||||||
var steps = initTable[string, Step]()
|
var steps = initTable[string, Step]()
|
||||||
for sName, pJson in jsonCfg.getOrFail("steps", "project configuration").getFields:
|
for sName, pJson in jsonCfg.getOrFail("steps", "project configuration").getFields:
|
||||||
steps[sName] = Step(
|
steps[sName] = Step(
|
||||||
name: sName,
|
name: sName,
|
||||||
workingDir: pJson.getIfExists("workingDir").getStr("."),
|
workingDir: pJson.getIfExists("workingDir").getStr("."),
|
||||||
stepCmd: pJson.getIfExists("stepCmd").getStr("NOT GIVEN"),
|
stepCmd: pJson.getIfExists("stepCmd").getStr("NOT GIVEN"),
|
||||||
depends: pJson.getIfExists("depends").getElems.mapIt(it.getStr),
|
depends: pJson.getIfExists("depends").getElems.mapIt(it.getStr),
|
||||||
artifacts: pJson.getIfExists("artifacts").getElems.mapIt(it.getStr),
|
artifacts: pJson.getIfExists("artifacts").getElems.mapIt(it.getStr),
|
||||||
cmdInput: pJson.getIfExists("cmdInput").getElems.mapIt(it.getStr),
|
cmdInput: pJson.getIfExists("cmdInput").getElems.mapIt(it.getStr),
|
||||||
expectedEnv: pJson.getIfExists("expectedEnv").getElems.mapIt(it.getStr),
|
expectedEnv: pJson.getIfExists("expectedEnv").getElems.mapIt(it.getStr),
|
||||||
dontSkip: pJson.getIfExists("dontSkip").getBVal(false))
|
containerImage: pJson.getIfExists("containerImage").getStr(""),
|
||||||
|
dontSkip: pJson.getIfExists("dontSkip").getBVal(false))
|
||||||
|
|
||||||
# cmdInput and stepCmd are related, so we have a conditional defaulting.
|
# cmdInput and stepCmd are related, so we have a conditional defaulting.
|
||||||
# Four possibilities:
|
# Four possibilities:
|
||||||
@ -194,6 +194,7 @@ proc loadProjectConfig*(cfgFile: string): ProjectConfig =
|
|||||||
|
|
||||||
result = ProjectConfig(
|
result = ProjectConfig(
|
||||||
name: jsonCfg.getOrFail("name", "project configuration").getStr,
|
name: jsonCfg.getOrFail("name", "project configuration").getStr,
|
||||||
|
containerImage: jsonCfg.getIfExists("containerImage").getStr(""),
|
||||||
versionCmd: jsonCfg.getIfExists("versionCmd").getStr("git describe --tags --always"),
|
versionCmd: jsonCfg.getIfExists("versionCmd").getStr("git describe --tags --always"),
|
||||||
steps: steps)
|
steps: steps)
|
||||||
|
|
||||||
@ -259,6 +260,9 @@ proc `%`*(s: Step): JsonNode =
|
|||||||
"expectedEnv": s.expectedEnv,
|
"expectedEnv": s.expectedEnv,
|
||||||
"dontSkip": s.dontSkip }
|
"dontSkip": s.dontSkip }
|
||||||
|
|
||||||
|
if not s.containerImage.isNullOrEmpty:
|
||||||
|
result["containerImage"] = %s.containerImage
|
||||||
|
|
||||||
proc `%`*(p: ProjectConfig): JsonNode =
|
proc `%`*(p: ProjectConfig): JsonNode =
|
||||||
result = %* {
|
result = %* {
|
||||||
"name": p.name,
|
"name": p.name,
|
||||||
@ -268,6 +272,9 @@ proc `%`*(p: ProjectConfig): JsonNode =
|
|||||||
for name, step in p.steps:
|
for name, step in p.steps:
|
||||||
result["steps"][name] = %step
|
result["steps"][name] = %step
|
||||||
|
|
||||||
|
if not p.containerImage.isNilOrEmpty:
|
||||||
|
result["containerImage"] = %p.containerImage
|
||||||
|
|
||||||
proc `%`*(req: RunRequest): JsonNode =
|
proc `%`*(req: RunRequest): JsonNode =
|
||||||
result = %* {
|
result = %* {
|
||||||
"runId": $(req.runId),
|
"runId": $(req.runId),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import cliutils, logging, json, os, ospaths, osproc, sequtils, streams,
|
import cliutils, logging, json, os, ospaths, osproc, sequtils, streams,
|
||||||
strtabs, strutils, tables, times, uuids
|
strtabs, strutils, tables, tempfile, times, uuids
|
||||||
|
|
||||||
import ./configuration
|
import ./configuration
|
||||||
import nre except toSeq
|
import nre except toSeq
|
||||||
@ -49,7 +49,51 @@ proc newCopy(w: Workspace): Workspace =
|
|||||||
step: w.step,
|
step: w.step,
|
||||||
version: w.version)
|
version: w.version)
|
||||||
|
|
||||||
# Logging wrappers around
|
const WKSP_ROOT = "/strawboss/wksp"
|
||||||
|
const ARTIFACTS_ROOT = "/strawboss/artifacts"
|
||||||
|
|
||||||
|
proc execWithOutput(w: Workspace, cmd, workingDir: string,
|
||||||
|
args: openarray[string], env: StringTableRef,
|
||||||
|
options: set[ProcessOption] = {poUsePath},
|
||||||
|
msgCB: HandleProcMsgCB = nil):
|
||||||
|
tuple[output: TaintedString, error: TaintedString, exitCode: int]
|
||||||
|
{.tags: [ExecIOEffect, ReadIOEffect, RootEffect] .} =
|
||||||
|
|
||||||
|
# Look for a container image to use
|
||||||
|
let containerImage =
|
||||||
|
if not isNilOrEmpty(w.step.containerImage): w.step.containerImage
|
||||||
|
else: w.project.containerImage
|
||||||
|
|
||||||
|
if containerImage.isNilOrEmpty:
|
||||||
|
return exec(cmd, workingDir, args, env, options, msgCB
|
||||||
|
|
||||||
|
var fullEnv = newStringTable(modeCaseSensitive)
|
||||||
|
for k,v in env: fullEnv[k] = v
|
||||||
|
|
||||||
|
var fullArgs = @["run", "-w", WKSP_ROOT, "-v", wksp.dir & ":" & WKSP_ROOT ]
|
||||||
|
|
||||||
|
if w.step.name.isNilOrEmpty:
|
||||||
|
for depStep in step.depends:
|
||||||
|
fullArgs.add("-v", ARTIFACTS_ROOT / depStep)
|
||||||
|
fullEnv[depStep & "_DIR"] = ARTIFACTS_DIR / depStep)
|
||||||
|
|
||||||
|
let envFile = mkstemp()[0]
|
||||||
|
writeFile(envFile, toSeq(fullEnv.pairs()).mapIt(it[0] & "=" & it[1]).join("\n"))
|
||||||
|
|
||||||
|
fullArgs.add("--env-file", envFile)
|
||||||
|
fullArgs.add(containerImage)
|
||||||
|
fullArgs.add(cmd)
|
||||||
|
|
||||||
|
echo "Executing docker command: \n\t" & "docker " & $(fullArgs & args)
|
||||||
|
return execWithOutput("docker", wksp.dir, fullArgs & args, fullEnv, options, msgCB)
|
||||||
|
|
||||||
|
proc exec(w: Workspace, cmd, workingDir: string, args: openarray[string],
|
||||||
|
env: StringTableRef, options: set[ProcessingOption] = {poUsePath},
|
||||||
|
msgCB: HandleProcMsgCG = nil): int
|
||||||
|
{.tags: [ExecIOEffect, ReadIOEffect, RootEffect] .} =
|
||||||
|
|
||||||
|
return execWithOutput(w, cmd, workingDir, args, env, options, msgCB)[2]
|
||||||
|
|
||||||
# Utility methods for Workspace activities
|
# Utility methods for Workspace activities
|
||||||
proc sendStatusMsg(oh: HandleProcMsgCB, status: BuildStatus): void =
|
proc sendStatusMsg(oh: HandleProcMsgCB, status: BuildStatus): void =
|
||||||
if not oh.isNil:
|
if not oh.isNil:
|
||||||
@ -89,7 +133,7 @@ proc publishStatus(wksp: Workspace, state: BuildState, details: string): void =
|
|||||||
$wksp.runRequest.runId & ".status.json", $wksp.status)
|
$wksp.runRequest.runId & ".status.json", $wksp.status)
|
||||||
|
|
||||||
# If we have our step we can save status to the step status
|
# If we have our step we can save status to the step status
|
||||||
if not wksp.step.name.isNilOrEmpty():
|
if not wksp.step.name.isNilOrEmpty:
|
||||||
let stepStatusDir = wksp.buildDataDir / "status" / wksp.step.name
|
let stepStatusDir = wksp.buildDataDir / "status" / wksp.step.name
|
||||||
if not existsDir(stepStatusDir): createDir(stepStatusDir)
|
if not existsDir(stepStatusDir): createDir(stepStatusDir)
|
||||||
writeFile(stepStatusDir / wksp.version & ".json", $wksp.status)
|
writeFile(stepStatusDir / wksp.version & ".json", $wksp.status)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "dummy-project",
|
"name": "dummy-project",
|
||||||
"versionCmd": "git describe --all --always",
|
"versionCmd": "git describe --all --always",
|
||||||
|
"containerImage": "ubuntu",
|
||||||
"steps": {
|
"steps": {
|
||||||
"build": {
|
"build": {
|
||||||
|
"containerImage": "alpine",
|
||||||
"depends": ["test"],
|
"depends": ["test"],
|
||||||
"workingDir": "dir1",
|
"workingDir": "dir1",
|
||||||
"stepCmd": "cust-build",
|
"stepCmd": "cust-build",
|
||||||
|
@ -99,6 +99,7 @@ suite "load and save configuration objects":
|
|||||||
check:
|
check:
|
||||||
pc.name == "dummy-project"
|
pc.name == "dummy-project"
|
||||||
pc.versionCmd == "git describe --all --always"
|
pc.versionCmd == "git describe --all --always"
|
||||||
|
pc.containerImage == "ubuntu"
|
||||||
pc.steps.len == 2
|
pc.steps.len == 2
|
||||||
|
|
||||||
# Explicitly set properties
|
# Explicitly set properties
|
||||||
@ -106,6 +107,7 @@ suite "load and save configuration objects":
|
|||||||
pc.steps["build"].dontSkip == true
|
pc.steps["build"].dontSkip == true
|
||||||
pc.steps["build"].stepCmd == "cust-build"
|
pc.steps["build"].stepCmd == "cust-build"
|
||||||
pc.steps["build"].workingDir == "dir1"
|
pc.steps["build"].workingDir == "dir1"
|
||||||
|
pc.steps["containerImage"] == "alpine"
|
||||||
sameContents(pc.steps["build"].artifacts, @["bin1", "doc1"])
|
sameContents(pc.steps["build"].artifacts, @["bin1", "doc1"])
|
||||||
sameContents(pc.steps["build"].depends, @["test"])
|
sameContents(pc.steps["build"].depends, @["test"])
|
||||||
sameContents(pc.steps["build"].expectedEnv, @["VAR1"])
|
sameContents(pc.steps["build"].expectedEnv, @["VAR1"])
|
||||||
@ -115,7 +117,8 @@ suite "load and save configuration objects":
|
|||||||
pc.steps["test"].name == "test"
|
pc.steps["test"].name == "test"
|
||||||
pc.steps["test"].dontSkip == false
|
pc.steps["test"].dontSkip == false
|
||||||
pc.steps["test"].stepCmd == "true"
|
pc.steps["test"].stepCmd == "true"
|
||||||
pc.steps["test"].workingDir == "."
|
pc.steps["test"].workingDir == ".:
|
||||||
|
pc.steps["test"].containerImage.isNilOrEmpty
|
||||||
sameContents(pc.steps["test"].artifacts, @[])
|
sameContents(pc.steps["test"].artifacts, @[])
|
||||||
sameContents(pc.steps["test"].depends, @[])
|
sameContents(pc.steps["test"].depends, @[])
|
||||||
sameContents(pc.steps["test"].expectedEnv, @[])
|
sameContents(pc.steps["test"].expectedEnv, @[])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user