Skip to content

Commit a496c54

Browse files
committed
add custom console driver
let the user define a console endpoint which is replaced by template for link showed on the pr results and others. * `tekton-dashboard-url` Set this to your dashboard URL, and the pipelinerun and tasklog will be expander there. You can configure a custom console with this settings: * `custom-console-name` Set this to the name of your custom console. example: `MyCorp Console` * `custom-console-url` Set this to the root URL of your custom console. example: `https://mycorp.com` * `custom-console-url-pr-details` Set this to the URL where to view the details of the `PipelineRun`. This is shown when the PipelineRun is started so the user can follow execution on your console or when to see more details about the pipelinerun ion result. The URL suports templating for these value: * `{{ namespace }}`: The target namespace where the pipelinerun is executed * `{{ pr }}`: The PipelineRun name. example: `https://mycorp.com/ns/{{ namespace }}/pipelienrun/{{ pr }}` * `custom-console-url-pr-tasklog` Set this to the URL where to view the log of the taskrun of the `PipelineRun`. This is shown when we post a result of the task breakdown to link to the logs of the taskrun. The URL suports templating for these value: * `{{ namespace }}`: The target namespace where the pipelinerun is executed * `{{ pr }}`: The PipelineRun name. * `{{ task }}`: The Task name in the PR example: `https://mycorp.com/ns/{{ namespace }}/pipelienrun/{{ pr }}/logs/{{ task }}` Signed-off-by: Chmouel Boudjnah <[email protected]>
1 parent c2bd1ce commit a496c54

File tree

21 files changed

+363
-52
lines changed

21 files changed

+363
-52
lines changed

config/302-pac-configmap.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ data:
110110
# https://github.com/owner/repo will be `owner-repo-ci`
111111
auto-configure-repo-namespace-template: ""
112112

113+
# Configure a custom console here
114+
#
115+
# custom-console-name: Console Name
116+
# custom-console-url: https://url
117+
# custom-console-url-pr-details: https://url/ns/{{ namespace }}/{{ pr }}
118+
# custom-console-url-pr-tasklog: https://url/ns/{{ namespace }}/{{ pr }}/logs/{{ task }}
119+
113120
kind: ConfigMap
114121
metadata:
115122
name: pipelines-as-code

docs/content/docs/install/settings.md

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ There is a few things you can configure through the config map
7070

7171
The [tekton hub](https://github.com/tektoncd/hub/) catalog name. default to `tekton`
7272

73-
* `tekton-dashboard-url`
74-
75-
When you are not running on Openshift using the [tekton
76-
dashboard](https://github.com/tektoncd/dashboard/) you will need to specify a
77-
dashboard url to have the logs tnd the pipelinerun details linked.
78-
7973
* `bitbucket-cloud-check-source-ip`
8074

8175
Public bitbucket doesn't have the concept of Secret, we need to be
@@ -137,6 +131,11 @@ There is a few things you can configure through the config map
137131

138132
`https://github.com/owner/repo` will be `owner-repo-ci`
139133

134+
### Error Detection
135+
136+
Pipelines as Code can show a snippet and optionally detect the error in the
137+
pipelinerun logs, you can configure the behaviour with these settings:
138+
140139
* `error-log-snippet`
141140

142141
Enable or disable the feature to show a log snippet of the failed task when
@@ -190,6 +189,51 @@ There is a few things you can configure through the config map
190189
You can configure the default regexp used for detection. You will need to
191190
keep the regexp groups: `<filename>`, `<line>`, `<error>` to make it works.
192191

192+
### Reporting logs URL to Tekton Dashboard or custom Console
193+
194+
Pipelines as Code have the ability to automatically detect the OpenShift Console and link the logs of the tasks to the
195+
public URL of the OpenShift Console. If you are using the Tekton Dashboard, you can configure this feature using the
196+
`tekton-dashboard-url` setting. Simply set this to your dashboard URL, and the pipelinerun status and tasklog will be
197+
displayed there.
198+
199+
Alternatively, you can also configure a custom console with the following settings:
200+
201+
* `custom-console-name`
202+
203+
Set this to the name of your custom console. example: `MyCorp Console`
204+
205+
* `custom-console-url`
206+
207+
Set this to the root URL of your custom console. example: `https://mycorp.com`
208+
209+
* `custom-console-url-pr-details`
210+
211+
Set this to the URL where to view the details of the `PipelineRun`. This is
212+
shown when the PipelineRun is started so the user can follow execution on your
213+
console or when to see more details about the pipelinerun ion result.
214+
215+
The URL suports templating for these value:
216+
217+
* `{{ namespace }}`: The target namespace where the pipelinerun is executed
218+
* `{{ pr }}`: The PipelineRun name.
219+
220+
example: `https://mycorp.com/ns/{{ namespace }}/pipelienrun/{{ pr }}`
221+
222+
* `custom-console-url-pr-tasklog`
223+
224+
Set this to the URL where to view the log of the taskrun of the `PipelineRun`. This is
225+
shown when we post a result of the task breakdown to link to the logs of the taskrun.
226+
227+
The URL suports templating for these value:
228+
229+
* `{{ namespace }}`: The target namespace where the pipelinerun is executed
230+
* `{{ pr }}`: The PipelineRun name.
231+
* `{{ task }}`: The Task name in the PR
232+
* `{{ pod }}`: The Pod name of the TaskRun
233+
* `{{ firstFailedStep }}`: The name of the first failed step in the TaskRun
234+
235+
example: `https://mycorp.com/ns/{{ namespace }}/pipelinerun/{{ pr }}/logs/{{ task }}#{{ pod }}-{{ firstFailedStep }}`
236+
193237
## Pipelines-As-Code Info
194238

195239
There are a settings exposed through a config map for which any authenticated
@@ -204,10 +248,10 @@ There is a few things you can configure through the config map
204248
The controller URL as set by the `tkn pac bootstrap` command while setting up
205249
the GitHub App or if Pipelines as code is installed
206250

207-
When using OpenShift Pipelines Operator then the operator sets the route created
251+
The OpenShift Pipelines Operator will automatically set the the route created
208252
for the controller.
209253

210-
This field is also used to detect the controller URL when using the `webhook add`
254+
This field is also used to detect the controller URL when using the `tkn pac webhook add`
211255
commands.
212256

213257
* `provider`

pkg/action/patch_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func getLogURLMergePatch(clients clients.Clients, pr *pipelinev1.PipelineRun) ma
4747
return map[string]interface{}{
4848
"metadata": map[string]interface{}{
4949
"annotations": map[string]string{
50-
keys.LogURL: clients.ConsoleUI.DetailURL(pr.GetNamespace(), pr.GetName()),
50+
keys.LogURL: clients.ConsoleUI.DetailURL(pr),
5151
},
5252
},
5353
}

pkg/cli/status/status.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ func MixLivePRandRepoStatus(ctx context.Context, cs *params.Run, repository pacv
6363
return sortrepostatus.RepositorySortRunStatus(repositorystatus)
6464
}
6565

66-
for _, pr := range prs.Items {
66+
for i := range prs.Items {
67+
pr := prs.Items[i]
6768
repositorystatus = RepositoryRunStatusRemoveSameSHA(repositorystatus, pr.GetLabels()["pipelinesascode.tekton.dev/sha"])
68-
logurl := cs.Clients.ConsoleUI.DetailURL(pr.GetNamespace(), pr.GetName())
69+
logurl := cs.Clients.ConsoleUI.DetailURL(&pr)
6970
repositorystatus = append(repositorystatus, convertPrStatusToRepositoryStatus(ctx, cs, pr, logurl))
7071
}
7172
return sortrepostatus.RepositorySortRunStatus(repositorystatus)

pkg/cmd/tknpac/logs/logs.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
2424
"github.com/openshift-pipelines/pipelines-as-code/pkg/sort"
2525
"github.com/spf13/cobra"
26+
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
2627

2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829
)
@@ -161,7 +162,7 @@ func getTknPath() (string, error) {
161162
return filepath.Abs(fname)
162163
}
163164

164-
// getPipelineRunsToRepo returns all pipelinesruns running in a namespace
165+
// getPipelineRunsToRepo returns all PipelineRuns running in a namespace
165166
func getPipelineRunsToRepo(ctx context.Context, lopt *logOption, repoName string) ([]string, error) {
166167
opts := metav1.ListOptions{
167168
LabelSelector: fmt.Sprintf("%s=%s",
@@ -248,13 +249,19 @@ func showLogsWithWebConsole(lo *logOption, pr string) error {
248249
lo.cs.Clients.ConsoleUI = &consoleui.TektonDashboard{BaseURL: os.Getenv("PAC_TEKTON_DASHBOARD_URL")}
249250
}
250251

251-
return browser.OpenWebBrowser(lo.cs.Clients.ConsoleUI.DetailURL(lo.cs.Info.Kube.Namespace, pr))
252+
prObj := &tektonv1.PipelineRun{
253+
ObjectMeta: metav1.ObjectMeta{
254+
Name: pr,
255+
Namespace: lo.cs.Info.Kube.Namespace,
256+
},
257+
}
258+
return browser.OpenWebBrowser(lo.cs.Clients.ConsoleUI.DetailURL(prObj))
252259
}
253260

254261
func showlogswithtkn(tknPath, pr, ns string) error {
255262
//nolint: gosec
256263
if err := syscall.Exec(tknPath, []string{tknPath, "pr", "logs", "-f", "-n", ns, pr}, os.Environ()); err != nil {
257-
fmt.Fprintf(os.Stderr, "Command finished with error: %v", err)
264+
_, _ = fmt.Fprintf(os.Stderr, "Command finished with error: %v", err)
258265
os.Exit(127)
259266
}
260267
return nil

pkg/consoleui/custom.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package consoleui
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
8+
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
9+
"github.com/openshift-pipelines/pipelines-as-code/pkg/templates"
10+
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
11+
"k8s.io/client-go/dynamic"
12+
)
13+
14+
type CustomConsole struct {
15+
info *info.Info
16+
}
17+
18+
func (o *CustomConsole) GetName() string {
19+
if o.info.Pac.CustomConsoleName == "" {
20+
return "Not configured"
21+
}
22+
return o.info.Pac.CustomConsoleName
23+
}
24+
25+
func (o *CustomConsole) URL() string {
26+
if o.info.Pac.CustomConsoleURL == "" {
27+
return fmt.Sprintf("https://setting.%s.is.not.configured", settings.CustomConsoleURLKey)
28+
}
29+
return o.info.Pac.CustomConsoleURL
30+
}
31+
32+
func (o *CustomConsole) DetailURL(pr *tektonv1.PipelineRun) string {
33+
if o.info.Pac.CustomConsolePRdetail == "" {
34+
return fmt.Sprintf("https://setting.%s.is.not.configured", settings.CustomConsolePRDetailKey)
35+
}
36+
return templates.ReplacePlaceHoldersVariables(o.info.Pac.CustomConsolePRdetail, map[string]string{
37+
"namespace": pr.GetNamespace(),
38+
"pr": pr.GetName(),
39+
})
40+
}
41+
42+
func (o *CustomConsole) TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatus *tektonv1.PipelineRunTaskRunStatus) string {
43+
if o.info.Pac.CustomConsolePRTaskLog == "" {
44+
return fmt.Sprintf("https://setting.%s.is.not.configured", settings.CustomConsolePRTaskLogKey)
45+
}
46+
firstFailedStep := ""
47+
// search for the first failed steps in taskrunstatus
48+
for _, step := range taskRunStatus.Status.Steps {
49+
if step.Terminated != nil && step.Terminated.ExitCode != 0 {
50+
firstFailedStep = step.Name
51+
break
52+
}
53+
}
54+
return templates.ReplacePlaceHoldersVariables(o.info.Pac.CustomConsolePRTaskLog, map[string]string{
55+
"namespace": pr.GetNamespace(),
56+
"pr": pr.GetName(),
57+
"task": taskRunStatus.PipelineTaskName,
58+
"pod": taskRunStatus.Status.PodName,
59+
"firstFailedStep": firstFailedStep,
60+
})
61+
}
62+
63+
// UI use dynamic client to get the route of the openshift
64+
// console where we can point to.
65+
func (o *CustomConsole) UI(_ context.Context, _ dynamic.Interface) error {
66+
return nil
67+
}

pkg/consoleui/custom_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package consoleui
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
8+
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
9+
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
10+
"gotest.tools/v3/assert"
11+
corev1 "k8s.io/api/core/v1"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
)
14+
15+
func TestCustomGood(t *testing.T) {
16+
consoleName := "MyCorp Console"
17+
consoleURL := "https://mycorp.console"
18+
consolePRdetail := "https://mycorp.console/{{ namespace }}/{{ pr }}"
19+
consolePRtasklog := "https://mycorp.console/{{ namespace }}/{{ pr }}/{{ task }}/{{ pod }}/{{ firstFailedStep }}"
20+
21+
c := CustomConsole{
22+
info: &info.Info{
23+
Pac: &info.PacOpts{
24+
Settings: &settings.Settings{
25+
CustomConsoleName: consoleName,
26+
CustomConsoleURL: consoleURL,
27+
CustomConsolePRdetail: consolePRdetail,
28+
CustomConsolePRTaskLog: consolePRtasklog,
29+
},
30+
},
31+
},
32+
}
33+
pr := &tektonv1.PipelineRun{
34+
ObjectMeta: metav1.ObjectMeta{
35+
Namespace: "ns",
36+
Name: "pr",
37+
},
38+
}
39+
trStatus := &tektonv1.PipelineRunTaskRunStatus{
40+
PipelineTaskName: "task",
41+
Status: &tektonv1.TaskRunStatus{
42+
TaskRunStatusFields: tektonv1.TaskRunStatusFields{
43+
PodName: "pod",
44+
Steps: []tektonv1.StepState{
45+
{
46+
Name: "failure",
47+
ContainerState: corev1.ContainerState{
48+
Terminated: &corev1.ContainerStateTerminated{
49+
ExitCode: 1,
50+
},
51+
},
52+
},
53+
{
54+
Name: "nextFailure",
55+
ContainerState: corev1.ContainerState{
56+
Terminated: &corev1.ContainerStateTerminated{
57+
ExitCode: 1,
58+
},
59+
},
60+
},
61+
},
62+
},
63+
},
64+
}
65+
assert.Equal(t, c.GetName(), consoleName)
66+
assert.Equal(t, c.URL(), consoleURL)
67+
assert.Equal(t, c.DetailURL(pr), "https://mycorp.console/ns/pr")
68+
assert.Equal(t, c.TaskLogURL(pr, trStatus), "https://mycorp.console/ns/pr/task/pod/failure")
69+
}
70+
71+
func TestCustomBad(t *testing.T) {
72+
c := CustomConsole{
73+
info: &info.Info{
74+
Pac: &info.PacOpts{
75+
Settings: &settings.Settings{},
76+
},
77+
},
78+
}
79+
pr := &tektonv1.PipelineRun{
80+
ObjectMeta: metav1.ObjectMeta{
81+
Namespace: "ns",
82+
Name: "pr",
83+
},
84+
}
85+
assert.Assert(t, strings.Contains(c.GetName(), "Not configured"))
86+
assert.Assert(t, strings.Contains(c.URL(), "is.not.configured"))
87+
assert.Assert(t, strings.Contains(c.DetailURL(pr), "is.not.configured"))
88+
assert.Assert(t, strings.Contains(c.TaskLogURL(pr, nil), "is.not.configured"))
89+
}

pkg/consoleui/interface.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import (
44
"context"
55

66
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
7+
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
78
"k8s.io/client-go/dynamic"
89
)
910

1011
const consoleIsnotConfiguredURL = "https://dashboard.is.not.configured"
1112

1213
type Interface interface {
13-
DetailURL(ns, pr string) string
14-
TaskLogURL(ns, pr, task string) string
14+
DetailURL(pr *tektonv1.PipelineRun) string
15+
TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatusstatus *tektonv1.PipelineRunTaskRunStatus) string
1516
UI(ctx context.Context, kdyn dynamic.Interface) error
1617
URL() string
1718
GetName() string
@@ -23,15 +24,15 @@ func (f FallBackConsole) GetName() string {
2324
return "Not configured"
2425
}
2526

26-
func (f FallBackConsole) DetailURL(ns, pr string) string {
27+
func (f FallBackConsole) DetailURL(_ *tektonv1.PipelineRun) string {
2728
return consoleIsnotConfiguredURL
2829
}
2930

30-
func (f FallBackConsole) TaskLogURL(ns, pr, task string) string {
31+
func (f FallBackConsole) TaskLogURL(_ *tektonv1.PipelineRun, _ *tektonv1.PipelineRunTaskRunStatus) string {
3132
return consoleIsnotConfiguredURL
3233
}
3334

34-
func (f FallBackConsole) UI(ctx context.Context, kdyn dynamic.Interface) error {
35+
func (f FallBackConsole) UI(_ context.Context, _ dynamic.Interface) error {
3536
return nil
3637
}
3738

pkg/consoleui/interface_test.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package consoleui
33
import (
44
"testing"
55

6+
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
67
"gotest.tools/v3/assert"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
79
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
810
"k8s.io/apimachinery/pkg/runtime"
911
dynamicfake "k8s.io/client-go/dynamic/fake"
@@ -19,8 +21,18 @@ func TestFallbackConsole(t *testing.T) {
1921
"kind": "Random",
2022
})
2123
dynClient := dynamicfake.NewSimpleDynamicClient(runtime.NewScheme(), unsf)
24+
25+
pr := &tektonv1.PipelineRun{
26+
ObjectMeta: metav1.ObjectMeta{
27+
Namespace: "ns",
28+
Name: "pr",
29+
},
30+
}
31+
trStatus := &tektonv1.PipelineRunTaskRunStatus{
32+
PipelineTaskName: "task",
33+
}
2234
assert.NilError(t, fbc.UI(ctx, dynClient))
2335
assert.Assert(t, fbc.URL() != "")
24-
assert.Assert(t, fbc.DetailURL("ns", "pr") != "")
25-
assert.Assert(t, fbc.TaskLogURL("ns", "pr", "task") != "")
36+
assert.Assert(t, fbc.DetailURL(pr) != "")
37+
assert.Assert(t, fbc.TaskLogURL(pr, trStatus) != "")
2638
}

0 commit comments

Comments
 (0)