Skip to content

Commit 55406b5

Browse files
committed
Merge pull request #77 from buildkite/script-eval
Allow scripts sent from Buildkite to be evaluated
2 parents a3e9d13 + a3b5a4b commit 55406b5

File tree

6 files changed

+101
-38
lines changed

6 files changed

+101
-38
lines changed

buildkite/agent.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ type Agent struct {
3030
// fingerprints
3131
AutoSSHFingerprintVerification bool
3232

33+
// If this agent is allowed to perform script evaluation
34+
ScriptEval bool
35+
3336
// Run jobs in a PTY
3437
RunInPty bool
3538

buildkite/agent_registration.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ type AgentRegistration struct {
1515
// Operating system for this machine
1616
OS string `json:"os"`
1717

18+
// If this agent is allowed to perform script evaluation
19+
ScriptEval bool `json:"script_eval_enabled"`
20+
1821
// The priority of the agent
1922
Priority string `json:"priority,omitempty"`
2023

@@ -25,7 +28,7 @@ type AgentRegistration struct {
2528
MetaData []string `json:"meta_data"`
2629
}
2730

28-
func (c *Client) AgentRegister(name string, priority string, metaData []string) (string, error) {
31+
func (c *Client) AgentRegister(name string, priority string, metaData []string, scriptEval bool) (string, error) {
2932
os, err := MachineOSDump()
3033
hostname, err := MachineHostname()
3134

@@ -36,6 +39,7 @@ func (c *Client) AgentRegister(name string, priority string, metaData []string)
3639
registration.Hostname = hostname
3740
registration.OS = os
3841
registration.MetaData = metaData
42+
registration.ScriptEval = scriptEval
3943

4044
Logger.WithFields(logrus.Fields{
4145
"name": registration.Name,

buildkite/job.go

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -130,41 +130,52 @@ func (j *Job) Run(agent *Agent) error {
130130
// starts working during the actual build.
131131
}
132132

133-
// This callback is called every second the build is running. This lets
134-
// us do a lazy-person's method of streaming data to Buildkite.
135-
callback := func(process *Process) {
136-
j.Output = process.Output
133+
if env["BUILDKITE_SCRIPT_MODE"] == "eval" && !agent.ScriptEval {
134+
Logger.Infof("Job %s is not allowed to run, exiting.", j.ID)
137135

138-
// Post the update to the API
139-
updatedJob, err := agent.Client.JobUpdate(j)
140-
if err != nil {
141-
// We don't really care if the job couldn't update at this point.
142-
// This is just a partial update. We'll just let the job run
143-
// and hopefully the host will fix itself before we finish.
144-
Logger.Warnf("Problem with updating job %s (%s)", j.ID, err)
145-
} else if updatedJob.State == "canceled" {
146-
j.Kill()
136+
// Show an error in the output
137+
j.Output = "ERROR: This agent has not been allowed to evaluate scripts.\nRe-run this agent and remove the `--no-script-eval` option, or specify a script on the filesystem to run instead."
138+
139+
// Mark the build as finished
140+
j.FinishedAt = time.Now().Format(time.RFC3339)
141+
j.ExitStatus = "1"
142+
} else {
143+
// This callback is called every second the build is running. This lets
144+
// us do a lazy-person's method of streaming data to Buildkite.
145+
callback := func(process *Process) {
146+
j.Output = process.Output
147+
148+
// Post the update to the API
149+
updatedJob, err := agent.Client.JobUpdate(j)
150+
if err != nil {
151+
// We don't really care if the job couldn't update at this point.
152+
// This is just a partial update. We'll just let the job run
153+
// and hopefully the host will fix itself before we finish.
154+
Logger.Warnf("Problem with updating job %s (%s)", j.ID, err)
155+
} else if updatedJob.State == "canceled" {
156+
j.Kill()
157+
}
147158
}
148-
}
149159

150-
// Initialze our process to run
151-
process := InitProcess(agent.BootstrapScript, envSlice, agent.RunInPty, callback)
160+
// Initialze our process to run
161+
process := InitProcess(agent.BootstrapScript, envSlice, agent.RunInPty, callback)
152162

153-
// Store the process so we can cancel it later.
154-
j.process = process
163+
// Store the process so we can cancel it later.
164+
j.process = process
155165

156-
// Start the process. This will block until it finishes.
157-
err = process.Start()
158-
if err == nil {
159-
// Store the final output
160-
j.Output = j.process.Output
161-
} else {
162-
j.Output = fmt.Sprintf("%s", err)
163-
}
166+
// Start the process. This will block until it finishes.
167+
err = process.Start()
168+
if err == nil {
169+
// Store the final output
170+
j.Output = j.process.Output
171+
} else {
172+
j.Output = fmt.Sprintf("%s", err)
173+
}
164174

165-
// Mark the build as finished
166-
j.FinishedAt = time.Now().Format(time.RFC3339)
167-
j.ExitStatus = j.process.ExitStatus
175+
// Mark the build as finished
176+
j.FinishedAt = time.Now().Format(time.RFC3339)
177+
j.ExitStatus = j.process.ExitStatus
178+
}
168179

169180
// Keep trying this call until it works. This is the most important one.
170181
for {

command/agent_start.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,23 @@ func AgentStartCommandAction(c *cli.Context) {
8888
}
8989
}
9090

91-
// Register the agent
92-
agentAccessToken, err := client.AgentRegister(c.String("name"), c.String("priority"), agentMetaData)
93-
if err != nil {
94-
buildkite.Logger.Fatal(err)
95-
}
96-
9791
// Set the agent options
9892
var agent buildkite.Agent
9993
agent.BootstrapScript = bootstrapScript
10094
agent.BuildPath = buildPath
10195
agent.RunInPty = !c.Bool("no-pty")
10296
agent.AutoSSHFingerprintVerification = !c.Bool("no-automatic-ssh-fingerprint-verification")
97+
agent.ScriptEval = !c.Bool("no-script-eval")
98+
99+
if !agent.ScriptEval {
100+
buildkite.Logger.Info("Evaluating scripts has been disabled for this agent")
101+
}
102+
103+
// Register the agent
104+
agentAccessToken, err := client.AgentRegister(c.String("name"), c.String("priority"), agentMetaData, agent.ScriptEval)
105+
if err != nil {
106+
buildkite.Logger.Fatal(err)
107+
}
103108

104109
// Client specific options
105110
agent.Client.AuthorizationToken = agentAccessToken

commands.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ func init() {
168168
Name: "no-automatic-ssh-fingerprint-verification",
169169
Usage: "Don't automatically verify SSH fingerprints",
170170
},
171+
cli.BoolFlag{
172+
Name: "no-script-eval",
173+
Usage: "Don't allow this agent to evaluate scripts from Buildkite. Only scripts that exist on the file system will be allowed to run",
174+
},
171175
cli.StringFlag{
172176
Name: "endpoint",
173177
Value: "https://agent.buildkite.com/v2",

templates/bootstrap.sh

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ set -u
2323

2424
BUILDKITE_PROMPT="\033[90m$\033[0m"
2525

26+
# Shows the command being run, and runs it
2627
function buildkite-prompt-and-run {
2728
echo -e "$BUILDKITE_PROMPT $1"
2829
eval $1
2930
}
3031

32+
# Shows the command about to be run, and exits if it fails
3133
function buildkite-run {
3234
echo -e "$BUILDKITE_PROMPT $1"
3335
eval $1
@@ -38,6 +40,28 @@ function buildkite-run {
3840
fi
3941
}
4042

43+
# Only shows the command if DEBUG is on
44+
function buildkite-run-debug {
45+
if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
46+
echo -e "$BUILDKITE_PROMPT $1"
47+
eval $1
48+
else
49+
eval $1
50+
fi
51+
}
52+
53+
# Outputs a header
54+
function buildkite-header {
55+
echo -e "--- $1"
56+
}
57+
58+
# Outputs a header only if DEBUG is on
59+
function buildkite-header-debug {
60+
if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
61+
buildkite-header "$1"
62+
fi
63+
}
64+
4165
##############################################################
4266
#
4367
# PATH DEFAULTS
@@ -127,8 +151,9 @@ buildkite-run "git submodule update --init --recursive"
127151
buildkite-run "git submodule foreach --recursive git reset --hard"
128152

129153
# Grab author and commit information and send it back to Buildkite
130-
buildkite-agent build-data set "buildkite:git:commit" "`git show "$BUILDKITE_COMMIT" -s --format=fuller --no-color`"
131-
buildkite-agent build-data set "buildkite:git:branch" "`git branch --contains "$BUILDKITE_COMMIT" --no-color`"
154+
buildkite-header-debug "Saving Git information"
155+
buildkite-run-debug "buildkite-agent build-data set \"buildkite:git:commit\" \"\`git show \"$BUILDKITE_COMMIT\" -s --format=fuller --no-color\`\""
156+
buildkite-run-debug "buildkite-agent build-data set \"buildkite:git:branch\" \"\`git branch --contains \"$BUILDKITE_COMMIT\" --no-color\`\""
132157

133158
##############################################################
134159
#
@@ -137,11 +162,22 @@ buildkite-agent build-data set "buildkite:git:branch" "`git branch --contains "$
137162
#
138163
##############################################################
139164

165+
# If we're evaluating a script, save it to the filesystem first
166+
if [[ "$BUILDKITE_SCRIPT_MODE" == "eval" ]]; then
167+
BUILDKITE_SCRIPT_PATH="buildkite-script-$BUILDKITE_JOB_ID"
168+
169+
echo "$BUILDKITE_SCRIPT_TEXT" > $BUILDKITE_SCRIPT_PATH
170+
fi
171+
172+
# Double check the file exists we want to run
140173
if [[ "$BUILDKITE_SCRIPT_PATH" == "" ]]; then
141174
echo "ERROR: No script to run. Please go to \"Project Settings\" and configure your build step's \"Script to Run\""
142175
exit 1
143176
fi
144177

178+
# Make sure the script we're going to run is executable
179+
chmod +x $BUILDKITE_SCRIPT_PATH
180+
145181
## Docker
146182
if [[ ! -z "${BUILDKITE_DOCKER:-}" ]] && [[ "$BUILDKITE_DOCKER" != "" ]]; then
147183
DOCKER_CONTAINER="buildkite_"$BUILDKITE_JOB_ID"_container"

0 commit comments

Comments
 (0)