Skip to content
This repository was archived by the owner on Mar 21, 2025. It is now read-only.

Commit 3368333

Browse files
author
Michael Sauter
committed
WIP
1 parent d649596 commit 3368333

34 files changed

+1741
-121
lines changed

cmd/taskdoc/main.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Package taskdoc implements documentation rendering for tasks.
2+
// It is intended to be run via `go run`, passing a task YAML manifest
3+
// and a description in Asciidoctor format. The combined result will be
4+
// written to the specified destination.
5+
//
6+
// Example invocation:
7+
//
8+
// go run github.com/opendevstack/ods-pipeline/cmd/taskdoc \
9+
// -task tasks/my-task.yaml \
10+
// -description build/docs/my-task.adoc \
11+
// -destination docs/my-task.adoc
12+
//
13+
// By default, taskdoc will use the template located at
14+
// docs/tasks/template.adoc.tmpl to produce the resulting file. Another
15+
// template can be specified via -template:
16+
//
17+
// go run github.com/opendevstack/ods-pipeline/cmd/taskdoc \
18+
// -task tasks/my-task.yaml \
19+
// -description build/docs/my-task.adoc \
20+
// -template /path/to/my-custom-template.adoc.tmpl \
21+
// -destination docs/my-task.adoc
22+
package main
23+
24+
import (
25+
"flag"
26+
"log"
27+
"os"
28+
"text/template"
29+
30+
"github.com/opendevstack/ods-pipeline/internal/projectpath"
31+
"github.com/opendevstack/ods-pipeline/pkg/taskdoc"
32+
)
33+
34+
func main() {
35+
taskFile := flag.String("task", "", "Task manifest")
36+
descriptionFile := flag.String("description", "", "Description snippet")
37+
templateFile := flag.String("template", projectpath.RootedPath("docs/tasks/template.adoc.tmpl"), "Template file")
38+
destinationFile := flag.String("destination", "", "Destination file")
39+
flag.Parse()
40+
if err := render(*taskFile, *descriptionFile, *templateFile, *destinationFile); err != nil {
41+
log.Fatal(err)
42+
}
43+
}
44+
45+
func render(taskFile, descriptionFile, templateFile, destinationFile string) error {
46+
t, err := os.ReadFile(taskFile)
47+
if err != nil {
48+
return err
49+
}
50+
d, err := os.ReadFile(descriptionFile)
51+
if err != nil {
52+
return err
53+
}
54+
tmpl, err := template.ParseFiles(templateFile)
55+
if err != nil {
56+
return err
57+
}
58+
59+
task, err := taskdoc.ParseTask(t, d)
60+
if err != nil {
61+
return err
62+
}
63+
64+
w, err := os.Create(destinationFile)
65+
if err != nil {
66+
return err
67+
}
68+
return taskdoc.RenderTaskDocumentation(w, tmpl, task)
69+
}

cmd/taskmanifest/main.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Package taskmanifest implements manifest rendering for tasks.
2+
// It is intended to be run via `go run`, passing a task YAML template
3+
// and data to be rendered. The combined result will be
4+
// written to the specified destination. The -data flag can be passed
5+
// multiple times and may specify any key-value combination, which can then
6+
// be consumed in the template through Go's text/template package. E.g.
7+
// passing -data Foo=bar will replace {{.Foo}} in the template with bar.
8+
//
9+
// Example invocation:
10+
//
11+
// go run github.com/opendevstack/ods-pipeline/cmd/taskmanifest \
12+
// -data ImageRepository=ghcr.io/my-org/my-repo \
13+
// -data Version=latest \
14+
// -template build/tasks/my-task.yaml \
15+
// -destination tasks/my-task.yaml
16+
package main
17+
18+
import (
19+
"flag"
20+
"fmt"
21+
"log"
22+
"os"
23+
"strings"
24+
"text/template"
25+
26+
"github.com/opendevstack/ods-pipeline/pkg/taskmanifest"
27+
"github.com/opendevstack/ods-pipeline/pkg/tektontaskrun"
28+
)
29+
30+
func main() {
31+
templateFile := flag.String("template", "", "Template file")
32+
destinationFile := flag.String("destination", "", "Destination file")
33+
cc := tektontaskrun.NewClusterConfig()
34+
mf := &MapFlag{v: cc.DefaultTaskTemplateData()}
35+
flag.Var(mf, "data", "Key-value pairs")
36+
flag.Parse()
37+
if err := render(*templateFile, *destinationFile, mf.v); err != nil {
38+
log.Fatal(err)
39+
}
40+
}
41+
42+
func render(templateFile, destinationFile string, data map[string]string) error {
43+
tmpl, err := template.ParseFiles(templateFile)
44+
if err != nil {
45+
return err
46+
}
47+
48+
w, err := os.Create(destinationFile)
49+
if err != nil {
50+
return err
51+
}
52+
return taskmanifest.RenderTask(w, tmpl, data)
53+
}
54+
55+
type MapFlag struct {
56+
v map[string]string
57+
}
58+
59+
func (mf *MapFlag) String() string {
60+
return fmt.Sprintf("%v", mf.v)
61+
}
62+
func (mf *MapFlag) Set(v string) error {
63+
key, value, ok := strings.Cut(v, "=")
64+
if !ok {
65+
return fmt.Errorf("must have = sign")
66+
}
67+
mf.v[key] = value
68+
return nil
69+
}

internal/docs/tasks.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package docs
22

33
import (
44
"bytes"
5+
"errors"
56
"fmt"
7+
"io"
68
"log"
79
"os"
810
"path/filepath"
9-
"strings"
1011
"text/template"
1112

1213
"github.com/opendevstack/ods-pipeline/internal/command"
@@ -37,20 +38,11 @@ func renderTemplate(targetDir, targetFilename string, data Task) error {
3738
if err != nil {
3839
return err
3940
}
40-
templateFilename := filepath.Join(targetDir, "template.adoc.tmpl")
41-
templateFileParts := strings.Split(templateFilename, "/")
42-
templateDisplayname := templateFileParts[len(templateFileParts)-1]
43-
_, err = targetFile.WriteString(
44-
"// Document generated by internal/documentation/tasks.go from " + templateDisplayname + "; DO NOT EDIT.\n\n",
45-
)
41+
tmpl, err := template.ParseFiles(filepath.Join(targetDir, "template.adoc.tmpl"))
4642
if err != nil {
4743
return err
4844
}
49-
tmpl, err := template.ParseFiles(templateFilename)
50-
if err != nil {
51-
return err
52-
}
53-
return tmpl.Execute(targetFile, data)
45+
return RenderTaskDocumentation(targetFile, tmpl, &data)
5446
}
5547

5648
func parseTasks(helmTemplateOutput []byte) ([]*tekton.Task, error) {
@@ -131,3 +123,47 @@ func RenderTasks(tasksSourceDir, descriptionsSourceDir, targetDir string) error
131123
}
132124
return nil
133125
}
126+
127+
func ParseTask(f []byte, desc []byte) (*Task, error) {
128+
var t tekton.Task
129+
err := yaml.Unmarshal(f, &t)
130+
if err != nil {
131+
return nil, err
132+
}
133+
if t.Name == "" {
134+
return nil, errors.New("encountered empty name, something is wrong with the task")
135+
}
136+
task := &Task{
137+
Name: t.Name,
138+
Description: string(desc),
139+
Params: []Param{},
140+
Results: []Result{},
141+
}
142+
for _, p := range t.Spec.Params {
143+
defaultValue := ""
144+
if p.Default != nil {
145+
defaultValue = p.Default.StringVal
146+
}
147+
task.Params = append(task.Params, Param{
148+
Name: p.Name,
149+
Default: defaultValue,
150+
Description: p.Description,
151+
})
152+
}
153+
for _, r := range t.Spec.Results {
154+
task.Results = append(task.Results, Result{
155+
Name: r.Name,
156+
Description: r.Description,
157+
})
158+
}
159+
return task, nil
160+
}
161+
162+
func RenderTaskDocumentation(w io.Writer, tmpl *template.Template, task *Task) error {
163+
if _, err := w.Write(
164+
[]byte("// File is generated; DO NOT EDIT.\n\n"),
165+
); err != nil {
166+
return err
167+
}
168+
return tmpl.Execute(w, task)
169+
}

internal/projectpath/root.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ var (
1111
// Root folder of this project
1212
Root = filepath.Join(filepath.Dir(b), "../..")
1313
)
14+
15+
func RootedPath(path string) string {
16+
return filepath.Join(Root, path)
17+
}

pkg/odstasktest/assertions.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package odstasktest
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
// AssertFilesExist checks that all files named by wantFiles exist in wsDir.
11+
// Any files that do not exist will report a test error.
12+
func AssertFilesExist(t *testing.T, wsDir string, wantFiles ...string) {
13+
for _, wf := range wantFiles {
14+
filename := filepath.Join(wsDir, wf)
15+
if _, err := os.Stat(filename); os.IsNotExist(err) {
16+
t.Errorf("Want %s, but got nothing", filename)
17+
}
18+
}
19+
}
20+
21+
// AssertFileContent checks that the file named by filename in the directory
22+
// wsDir has the exact context specified by want.
23+
func AssertFileContent(t *testing.T, wsDir, filename, want string) {
24+
got, err := getTrimmedFileContent(filepath.Join(wsDir, filename))
25+
if err != nil {
26+
t.Errorf("get content of %s: %s", filename, err)
27+
return
28+
}
29+
if got != want {
30+
t.Errorf("got '%s', want '%s' in file %s", got, want, filename)
31+
}
32+
}
33+
34+
func getTrimmedFileContent(filename string) (string, error) {
35+
content, err := os.ReadFile(filename)
36+
if err != nil {
37+
return "", err
38+
}
39+
return strings.TrimSpace(string(content)), nil
40+
}

pkg/odstasktest/doc.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Package odstasktest implements ODS Pipeline specific functionality to run
3+
Tekton tasks in a KinD cluster on top of package tektontaskrun.
4+
5+
odstasktest is intended to be used as a library for testing ODS Pipeline
6+
tasks using Go.
7+
8+
Example usage:
9+
10+
package test
11+
12+
import (
13+
"log"
14+
"os"
15+
"path/filepath"
16+
"testing"
17+
18+
ott "github.com/opendevstack/ods-pipeline/pkg/odstasktest"
19+
ttr "github.com/opendevstack/ods-pipeline/pkg/tektontaskrun"
20+
)
21+
22+
var (
23+
namespaceConfig *ttr.NamespaceConfig
24+
rootPath = "../.."
25+
)
26+
27+
func TestMain(m *testing.M) {
28+
cc, err := ttr.StartKinDCluster(
29+
ttr.LoadImage(ttr.ImageBuildConfig{
30+
Dockerfile: "build/images/Dockerfile.my-task",
31+
ContextDir: rootPath,
32+
}),
33+
)
34+
if err != nil {
35+
log.Fatal("Could not start KinD cluster: ", err)
36+
}
37+
nc, cleanup, err := ttr.SetupTempNamespace(
38+
cc,
39+
ott.StartNexus(),
40+
ott.InstallODSPipeline(),
41+
ttr.InstallTaskFromPath(
42+
filepath.Join(rootPath, "build/tasks/my-task.yaml"),
43+
nil,
44+
),
45+
)
46+
if err != nil {
47+
log.Fatal("Could not setup temporary namespace: ", err)
48+
}
49+
defer cleanup()
50+
namespaceConfig = nc
51+
os.Exit(m.Run())
52+
}
53+
54+
func TestMyTask(t *testing.T) {
55+
if err := ttr.RunTask(
56+
ttr.InNamespace(namespaceConfig.Name),
57+
ttr.UsingTask("my-task"),
58+
ttr.WithStringParams(map[string]string{
59+
"go-os": runtime.GOOS,
60+
"go-arch": runtime.GOARCH,
61+
}),
62+
ott.WithGitSourceWorkspace(t, "../testdata/workspaces/go-sample-app"),
63+
ttr.AfterRun(func(config *ttr.TaskRunConfig, run *tekton.TaskRun) {
64+
ott.AssertFilesExist(
65+
t, config.WorkspaceConfigs["source"].Dir,
66+
"docker/Dockerfile",
67+
"docker/app",
68+
)
69+
}),
70+
); err != nil {
71+
t.Fatal(err)
72+
}
73+
}
74+
75+
// further tests here ...
76+
*/
77+
package odstasktest

pkg/odstasktest/install.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package odstasktest
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/opendevstack/ods-pipeline/internal/command"
10+
"github.com/opendevstack/ods-pipeline/internal/projectpath"
11+
ttr "github.com/opendevstack/ods-pipeline/pkg/tektontaskrun"
12+
)
13+
14+
var privateCertFlag = flag.Bool("ods-private-cert", false, "Whether to use a private cert")
15+
16+
// InstallODSPipeline installs the ODS Pipeline Helm chart in the namespace
17+
// given in NamespaceConfig.
18+
func InstallODSPipeline() ttr.NamespaceOpt {
19+
flag.Parse()
20+
return func(cc *ttr.ClusterConfig, nc *ttr.NamespaceConfig) error {
21+
return installCDNamespaceResources(nc.Name, "pipeline", *privateCertFlag)
22+
}
23+
}
24+
25+
func installCDNamespaceResources(ns, serviceaccount string, privateCert bool) error {
26+
scriptArgs := []string{filepath.Join(projectpath.Root, "scripts/install-inside-kind.sh"), "-n", ns, "-s", serviceaccount, "--no-diff"}
27+
// if testing.Verbose() {
28+
// scriptArgs = append(scriptArgs, "-v")
29+
// }
30+
if privateCert {
31+
// Insert as first flag because install-inside-kind.sh won't recognize it otherwise.
32+
scriptArgs = append(
33+
[]string{fmt.Sprintf("--private-cert=%s", filepath.Join(projectpath.Root, "test/testdata/private-cert/tls.crt"))},
34+
scriptArgs...,
35+
)
36+
}
37+
38+
return command.Run(
39+
"sh",
40+
scriptArgs,
41+
[]string{},
42+
os.Stdout,
43+
os.Stderr,
44+
)
45+
}

0 commit comments

Comments
 (0)