Merge pull request #141 from sgotti/jsonnet_ctx

config: provide jsonnet context top level argument
This commit is contained in:
Simone Gotti 2020-03-06 13:52:52 +01:00 committed by GitHub
commit ac1b0dcb73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 219 additions and 5 deletions

View File

@ -20,6 +20,7 @@ import (
"regexp"
"strings"
itypes "agola.io/agola/internal/services/types"
"agola.io/agola/internal/util"
"agola.io/agola/services/types"
@ -645,11 +646,28 @@ func (r *Run) Task(taskName string) *Task {
var DefaultConfig = Config{}
func ParseConfig(configData []byte, format ConfigFormat) (*Config, error) {
// ConfigContext is the context to pass to the config generator. Fields are not marked as omitempty since
// we want to provide all of them with empty value if not existing in such context
// (i.e. pull_request_id will be an empty string when not a pull request)
type ConfigContext struct {
RefType itypes.RunRefType `json:"ref_type"`
Ref string `json:"ref"`
Branch string `json:"branch"`
Tag string `json:"tag"`
PullRequestID string `json:"pull_request_id"`
CommitSHA string `json:"commit_sha"`
}
func ParseConfig(configData []byte, format ConfigFormat, configContext *ConfigContext) (*Config, error) {
// Generate json from jsonnet
if format == ConfigFormatJsonnet {
// TODO(sgotti) support custom import files inside the configdir ???
vm := jsonnet.MakeVM()
cj, err := json.Marshal(configContext)
if err != nil {
return nil, errors.Errorf("failed to marshal config context: %w", err)
}
vm.TLACode("ctx", string(cj))
out, err := vm.EvaluateSnippet("", string(configData))
if err != nil {
return nil, errors.Errorf("failed to evaluate jsonnet config: %w", err)

View File

@ -58,7 +58,7 @@ func TestParseConfig(t *testing.T) {
runs:
- name: run01
tasks:
-
-
`,
err: fmt.Errorf(`run "run01": task at index 0 is empty`),
},
@ -203,7 +203,7 @@ func TestParseConfig(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, err := ParseConfig([]byte(tt.in), ConfigFormatJSON); err != nil {
if _, err := ParseConfig([]byte(tt.in), ConfigFormatJSON, &ConfigContext{}); err != nil {
if tt.err == nil {
t.Fatalf("got error: %v, expected no error", err)
}
@ -593,7 +593,7 @@ func TestParseOutput(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out, err := ParseConfig([]byte(tt.in), ConfigFormatJSON)
out, err := ParseConfig([]byte(tt.in), ConfigFormatJSON, &ConfigContext{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@ -495,7 +495,17 @@ func (h *ActionHandler) CreateRuns(ctx context.Context, req *CreateRunRequest) e
configFormat = config.ConfigFormatJSON
}
config, err := config.ParseConfig([]byte(data), configFormat)
configContext := &config.ConfigContext{
RefType: req.RefType,
Ref: req.Ref,
Branch: req.Branch,
Tag: req.Tag,
PullRequestID: req.PullRequestID,
CommitSHA: req.CommitSHA,
}
config, err := config.ParseConfig([]byte(data), configFormat, configContext)
if err != nil {
h.log.Errorf("failed to parse config: %+v", err)

View File

@ -1680,3 +1680,189 @@ func TestPullRequest(t *testing.T) {
})
}
}
func TestConfigContext(t *testing.T) {
config := `
function(ctx) {
runs: [
{
name: 'run01',
tasks: [
{
name: 'task01',
runtime: {
containers: [
{
image: 'alpine/git',
},
],
},
environment: {
REF_TYPE: ctx.ref_type,
REF: ctx.ref,
BRANCH: ctx.branch,
TAG: ctx.tag,
PULL_REQUEST_ID: ctx.pull_request_id,
COMMIT_SHA: ctx.commit_sha,
},
steps: [
{ type: 'clone' },
{ type: 'run', command: 'env' },
],
},
],
},
],
}
`
tests := []struct {
name string
args []string
env map[string]string
}{
{
name: "test direct run branch",
env: map[string]string{
"REF_TYPE": "branch",
"REF": "refs/heads/master",
"BRANCH": "master",
"TAG": "",
"PULL_REQUEST_ID": "",
"COMMIT_SHA": "",
},
},
{
name: "test direct run tag",
args: []string{"--tag", "v0.1.0"},
env: map[string]string{
"REF_TYPE": "tag",
"REF": "refs/tags/v0.1.0",
"BRANCH": "",
"TAG": "v0.1.0",
"PULL_REQUEST_ID": "",
"COMMIT_SHA": "",
},
},
{
name: "test direct run with pr",
args: []string{"--ref", "refs/pull/1/head"},
env: map[string]string{
"REF_TYPE": "pull_request",
"REF": "refs/pull/1/head",
"BRANCH": "",
"TAG": "",
"PULL_REQUEST_ID": "1",
"COMMIT_SHA": "",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir, err := ioutil.TempDir("", "agola")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
defer os.RemoveAll(dir)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tetcd, tgitea, c := setup(ctx, t, dir)
defer shutdownGitea(tgitea)
defer shutdownEtcd(tetcd)
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, "admintoken")
user, _, err := gwClient.CreateUser(ctx, &gwapitypes.CreateUserRequest{UserName: agolaUser01})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
t.Logf("created agola user: %s", user.UserName)
token := createAgolaUserToken(ctx, t, c)
// From now use the user token
gwClient = gwclient.NewClient(c.Gateway.APIExposedURL, token)
directRun(t, dir, config, c.Gateway.APIExposedURL, token, tt.args...)
// TODO(sgotti) add an util to wait for a run phase
_ = testutil.Wait(30*time.Second, func() (bool, error) {
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
if err != nil {
return false, nil
}
if len(runs) != 1 {
return false, nil
}
run := runs[0]
if run.Phase != rstypes.RunPhaseFinished {
return false, nil
}
return true, nil
})
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/user", user.ID)}, nil, "", 0, false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
t.Logf("runs: %s", util.Dump(runs))
if len(runs) != 1 {
t.Fatalf("expected 1 run got: %d", len(runs))
}
run, _, err := gwClient.GetRun(ctx, runs[0].ID)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if run.Phase != rstypes.RunPhaseFinished {
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
}
if run.Result != rstypes.RunResultSuccess {
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
}
var task *gwapitypes.RunResponseTask
for _, t := range run.Tasks {
if t.Name == "task01" {
task = t
break
}
}
resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1, false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
defer resp.Body.Close()
logs, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
curEnv, err := testutil.ParseEnvs(bytes.NewReader(logs))
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
// update commit sha from annotations since it will change at every test
tt.env["COMMIT_SHA"] = run.Annotations["commit_sha"]
for n, e := range tt.env {
if ce, ok := curEnv[n]; !ok {
t.Fatalf("missing env var %s", n)
} else {
if ce != e {
t.Fatalf("different env var %s value, want: %q, got %q", n, e, ce)
}
}
}
})
}
}