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

Commit c99cead

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

32 files changed

+1554
-121
lines changed

cmd/taskdoc/main.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"log"
6+
"os"
7+
"text/template"
8+
9+
"github.com/opendevstack/ods-pipeline/internal/projectpath"
10+
"github.com/opendevstack/ods-pipeline/pkg/taskdoc"
11+
)
12+
13+
func main() {
14+
taskFile := flag.String("task", "", "Task manifest")
15+
descriptionFile := flag.String("description", "", "Description snippet")
16+
templateFile := flag.String("template", projectpath.RootedPath("docs/tasks/template.adoc.tmpl"), "Template file")
17+
destinationFile := flag.String("destination", "", "Destination file")
18+
flag.Parse()
19+
if err := render(*taskFile, *descriptionFile, *templateFile, *destinationFile); err != nil {
20+
log.Fatal(err)
21+
}
22+
}
23+
24+
func render(taskFile, descriptionFile, templateFile, destinationFile string) error {
25+
t, err := os.ReadFile(taskFile)
26+
if err != nil {
27+
return err
28+
}
29+
d, err := os.ReadFile(descriptionFile)
30+
if err != nil {
31+
return err
32+
}
33+
tmpl, err := template.ParseFiles(templateFile)
34+
if err != nil {
35+
return err
36+
}
37+
38+
task, err := taskdoc.ParseTask(t, d)
39+
if err != nil {
40+
return err
41+
}
42+
43+
w, err := os.Create(destinationFile)
44+
if err != nil {
45+
return err
46+
}
47+
return taskdoc.RenderTaskDocumentation(w, tmpl, task)
48+
}

cmd/taskmanifest/main.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"log"
7+
"os"
8+
"strings"
9+
"text/template"
10+
11+
"github.com/opendevstack/ods-pipeline/pkg/taskmanifest"
12+
"github.com/opendevstack/ods-pipeline/pkg/tektontaskrun"
13+
)
14+
15+
func main() {
16+
templateFile := flag.String("template", "", "Template file")
17+
destinationFile := flag.String("destination", "", "Destination file")
18+
cc := tektontaskrun.NewClusterConfig()
19+
mf := &MapFlag{v: cc.DefaultTaskTemplateData()}
20+
flag.Var(mf, "data", "Key-value pairs")
21+
flag.Parse()
22+
if err := render(*templateFile, *destinationFile, mf.v); err != nil {
23+
log.Fatal(err)
24+
}
25+
}
26+
27+
func render(templateFile, destinationFile string, data map[string]string) error {
28+
tmpl, err := template.ParseFiles(templateFile)
29+
if err != nil {
30+
return err
31+
}
32+
33+
w, err := os.Create(destinationFile)
34+
if err != nil {
35+
return err
36+
}
37+
return taskmanifest.RenderTask(w, tmpl, data)
38+
}
39+
40+
type MapFlag struct {
41+
v map[string]string
42+
}
43+
44+
func (mf *MapFlag) String() string {
45+
return fmt.Sprintf("%v", mf.v)
46+
}
47+
func (mf *MapFlag) Set(v string) error {
48+
key, value, ok := strings.Cut(v, "=")
49+
if !ok {
50+
return fmt.Errorf("must have = sign")
51+
}
52+
mf.v[key] = value
53+
return nil
54+
}

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/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+
}

pkg/odstasktest/services.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package odstasktest
2+
3+
import (
4+
"flag"
5+
"os"
6+
7+
"github.com/opendevstack/ods-pipeline/internal/command"
8+
"github.com/opendevstack/ods-pipeline/internal/projectpath"
9+
ttr "github.com/opendevstack/ods-pipeline/pkg/tektontaskrun"
10+
)
11+
12+
var restartNexusFlag = flag.Bool("ods-restart-nexus", false, "Whether to force a restart of Nexus")
13+
var restartSonarQubeFlag = flag.Bool("ods-restart-sonarqube", false, "Whether to force a restart of SonarQube")
14+
var restartBitbucketFlag = flag.Bool("ods-restart-bitbucket", false, "Whether to force a restart of Bitbucket")
15+
var skipSonarQubeFlag = flag.Bool("ods-skip-sonar", false, "Whether to skip SonarQube steps")
16+
17+
// StartNexus starts a Nexus instance in a Docker container (named
18+
// ods-test-nexus). If a container of the same name already exists, it will be
19+
// reused unless -ods-restart-nexus is passed.
20+
func StartNexus() ttr.NamespaceOpt {
21+
flag.Parse()
22+
return runService("run-nexus.sh", *restartNexusFlag)
23+
}
24+
25+
// StartSonarQube starts a SonarQube instance in a Docker container (named
26+
// ods-test-sonarqube). If a container of the same name already exists, it will
27+
// be reused unless -ods-restart-sonarqube is passed.
28+
func StartSonarQube() ttr.NamespaceOpt {
29+
flag.Parse()
30+
if *skipSonarQubeFlag {
31+
return func(cc *ttr.ClusterConfig, nc *ttr.NamespaceConfig) error { return nil }
32+
}
33+
return runService("run-sonarqube.sh", *restartSonarQubeFlag)
34+
}
35+
36+
// StartBitbucket starts a Bitbucket instance in a Docker container (named
37+
// ods-test-bitbucket-server). If a container of the same name already exists,
38+
// it will be reused unless -ods-restart-bitbucket is passed.
39+
func StartBitbucket() ttr.NamespaceOpt {
40+
flag.Parse()
41+
return runService("run-bitbucket.sh", *restartBitbucketFlag)
42+
}
43+
44+
func runService(script string, restart bool) ttr.NamespaceOpt {
45+
return func(cc *ttr.ClusterConfig, nc *ttr.NamespaceConfig) error {
46+
args := []string{projectpath.RootedPath("scripts/" + script)}
47+
if !restart {
48+
args = append(args, "--reuse")
49+
}
50+
return command.Run("sh", args, []string{}, os.Stdout, os.Stderr)
51+
}
52+
}

pkg/odstasktest/workspace.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+
"testing"
5+
6+
"github.com/opendevstack/ods-pipeline/pkg/pipelinectxt"
7+
"github.com/opendevstack/ods-pipeline/pkg/tasktesting"
8+
ttr "github.com/opendevstack/ods-pipeline/pkg/tektontaskrun"
9+
)
10+
11+
func GetSourceWorkspaceContext(t *testing.T, config *ttr.TaskRunConfig) (dir string, ctxt *pipelinectxt.ODSContext) {
12+
dir = config.WorkspaceConfigs["source"].Dir
13+
ctxt, err := pipelinectxt.NewFromCache(dir)
14+
if err != nil {
15+
t.Fatal(err)
16+
}
17+
return
18+
}
19+
20+
// InitGitRepo initialises a Git repository inside the given workspace.
21+
// The workspace will also be setup with an ODS context directory in .ods.
22+
func InitGitRepo(t *testing.T) ttr.WorkspaceOpt {
23+
return func(c *ttr.WorkspaceConfig) error {
24+
// TODO: namespace is not the real name of the namespace ...
25+
// TODO: do we need the ODS context later? if so, how to transport?
26+
_ = tasktesting.SetupGitRepo(t, "namespace", c.Dir)
27+
return nil
28+
}
29+
}
30+
31+
// WithGitSourceWorkspace configures the task run with a workspace named
32+
// "source", mapped to the directory sourced from sourceDir. The directory is
33+
// initialised as a Git repository with an ODS context.
34+
func WithGitSourceWorkspace(t *testing.T, sourceDir string, opts ...ttr.WorkspaceOpt) ttr.TaskRunOpt {
35+
return WithSourceWorkspace(
36+
t, sourceDir,
37+
append([]ttr.WorkspaceOpt{InitGitRepo(t)}, opts...)...,
38+
)
39+
}
40+
41+
// WithSourceWorkspace configures the task run with a workspace named
42+
// "source", mapped to the directory sourced from sourceDir.
43+
func WithSourceWorkspace(t *testing.T, sourceDir string, opts ...ttr.WorkspaceOpt) ttr.TaskRunOpt {
44+
return ttr.WithWorkspace("source", sourceDir, opts...)
45+
}

pkg/pipelinectxt/context.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ func (o *ODSContext) WriteCache(wsDir string) error {
6060
return nil
6161
}
6262

63+
func NewFromCache(wsDir string) (o *ODSContext, err error) {
64+
o = &ODSContext{}
65+
err = o.ReadCache(wsDir)
66+
return
67+
}
68+
6369
// ReadCache reads ODS context from .ods
6470
// TODO: test that this works
6571
func (o *ODSContext) ReadCache(wsDir string) error {

0 commit comments

Comments
 (0)