Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: provide jsonnet context top level argument #141

Merged
merged 1 commit into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"regexp"
"strings"

itypes "agola.io/agola/internal/services/types"
"agola.io/agola/internal/util"
"agola.io/agola/services/types"

Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestParseConfig(t *testing.T) {
runs:
- name: run01
tasks:
-
-
`,
err: fmt.Errorf(`run "run01": task at index 0 is empty`),
},
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down
12 changes: 11 additions & 1 deletion internal/services/gateway/action/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
186 changes: 186 additions & 0 deletions tests/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
})
}
}