From 7c0cbe764f61ebafa4585be523108cfbec46b1a5 Mon Sep 17 00:00:00 2001 From: Chmouel Boudjnah Date: Tue, 31 Oct 2023 18:49:05 +0100 Subject: [PATCH] Add a better status message We are adding a better status message that will include the Namespace and the PipelineRun. We link the Namespace to the Namespace view in the consoles. Introducing a new setting for that `custom-console-url-namespace` that would need to be setup when using a custom console. Signed-off-by: Chmouel Boudjnah --- docs/content/docs/install/settings.md | 10 ++++++++++ pkg/consoleui/custom.go | 14 ++++++++++++++ pkg/consoleui/custom_test.go | 11 +++++++---- pkg/consoleui/interface.go | 5 +++++ pkg/consoleui/openshift.go | 21 +++++++++++++-------- pkg/consoleui/openshift_test.go | 11 ++++++----- pkg/consoleui/tektondashboard.go | 4 ++++ pkg/formatting/starting.go | 10 ++++++++-- pkg/formatting/starting_test.go | 7 +++++++ pkg/formatting/templates/failures.tmpl | 10 ++++++++++ pkg/formatting/templates/starting.go.tmpl | 2 +- pkg/params/settings/config.go | 23 +++++++++++++++-------- pkg/reconciler/status.go | 20 ++++++++++++++++++-- 13 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 pkg/formatting/templates/failures.tmpl diff --git a/docs/content/docs/install/settings.md b/docs/content/docs/install/settings.md index b9a2d98a5..0eaa95e96 100644 --- a/docs/content/docs/install/settings.md +++ b/docs/content/docs/install/settings.md @@ -250,6 +250,16 @@ A few settings are available to configure this feature: Set this to the root URL of your custom console. example: `https://mycorp.com` +* `custom-console-url-namespace` + + Set this to the URL where to view the details of the `Namespace`. + + The URL supports all the standard variables as exposed on the Pipelinerun (refer to + the documentation on [Authoring PipelineRuns](../authoringprs)) with the added + variable: + + * `{{ namespace }}`: The target namespace where the pipelinerun is executed + * `custom-console-url-pr-details` Set this to the URL where to view the details of the `PipelineRun`. This is diff --git a/pkg/consoleui/custom.go b/pkg/consoleui/custom.go index 8bff661fe..0894590f5 100644 --- a/pkg/consoleui/custom.go +++ b/pkg/consoleui/custom.go @@ -71,6 +71,20 @@ func (o *CustomConsole) DetailURL(pr *tektonv1.PipelineRun) string { return o.generateURL(o.Info.Pac.CustomConsolePRdetail, nm) } +func (o *CustomConsole) NameSpaceURL(pr *tektonv1.PipelineRun) string { + if o.Info.Pac.CustomConsoleNamespaceURL == "" { + return fmt.Sprintf("https://detailurl.setting.%s.is.not.configured", settings.CustomConsoleNamespaceURLKey) + } + nm := o.params + // make sure the map is not nil before setting this up + // there is a case where SetParams is not called before DetailURL and this would crash the container + if nm == nil { + nm = make(map[string]string) + } + nm["namespace"] = pr.GetNamespace() + return o.generateURL(o.Info.Pac.CustomConsoleNamespaceURL, nm) +} + func (o *CustomConsole) TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatus *tektonv1.PipelineRunTaskRunStatus) string { if o.Info.Pac.CustomConsolePRTaskLog == "" { return fmt.Sprintf("https://tasklogurl.setting.%s.is.not.configured", settings.CustomConsolePRTaskLogKey) diff --git a/pkg/consoleui/custom_test.go b/pkg/consoleui/custom_test.go index f4f048caf..6a8f97578 100644 --- a/pkg/consoleui/custom_test.go +++ b/pkg/consoleui/custom_test.go @@ -92,16 +92,18 @@ func TestCustomGood(t *testing.T) { Info: &info.Info{ Pac: &info.PacOpts{ Settings: &settings.Settings{ - CustomConsoleName: consoleName, - CustomConsoleURL: consoleURL, - CustomConsolePRdetail: "{{ notthere}}", - CustomConsolePRTaskLog: "{{ notthere}}", + CustomConsoleName: consoleName, + CustomConsoleURL: consoleURL, + CustomConsolePRdetail: "{{ notthere}}", + CustomConsolePRTaskLog: "{{ notthere}}", + CustomConsoleNamespaceURL: "https://mycorp.console/{{ namespace }}", }, }, }, } assert.Assert(t, strings.Contains(o.DetailURL(pr), consoleURL)) assert.Assert(t, strings.Contains(o.TaskLogURL(pr, trStatus), consoleURL)) + assert.Assert(t, strings.Contains(o.NameSpaceURL(pr), "https://mycorp.console/ns")) } func TestCustomBad(t *testing.T) { @@ -122,4 +124,5 @@ func TestCustomBad(t *testing.T) { assert.Assert(t, strings.Contains(c.URL(), "is.not.configured")) assert.Assert(t, strings.Contains(c.DetailURL(pr), "is.not.configured")) assert.Assert(t, strings.Contains(c.TaskLogURL(pr, nil), "is.not.configured")) + assert.Assert(t, strings.Contains(c.NameSpaceURL(pr), "is.not.configured"), c.NameSpaceURL(pr)) } diff --git a/pkg/consoleui/interface.go b/pkg/consoleui/interface.go index 459fb9a96..002dee1be 100644 --- a/pkg/consoleui/interface.go +++ b/pkg/consoleui/interface.go @@ -13,6 +13,7 @@ const consoleIsnotConfiguredURL = "https://dashboard.is.not.configured" type Interface interface { DetailURL(pr *tektonv1.PipelineRun) string TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatusstatus *tektonv1.PipelineRunTaskRunStatus) string + NameSpaceURL(pr *tektonv1.PipelineRun) string UI(ctx context.Context, kdyn dynamic.Interface) error URL() string GetName() string @@ -33,6 +34,10 @@ func (f FallBackConsole) TaskLogURL(_ *tektonv1.PipelineRun, _ *tektonv1.Pipelin return consoleIsnotConfiguredURL } +func (f FallBackConsole) NameSpaceURL(_ *tektonv1.PipelineRun) string { + return consoleIsnotConfiguredURL +} + func (f FallBackConsole) UI(_ context.Context, _ dynamic.Interface) error { return nil } diff --git a/pkg/consoleui/openshift.go b/pkg/consoleui/openshift.go index 8d9f14ada..6e116e58b 100644 --- a/pkg/consoleui/openshift.go +++ b/pkg/consoleui/openshift.go @@ -11,14 +11,15 @@ import ( ) const ( - openShiftConsoleNS = "openshift-console" - openShiftConsoleRouteName = "console" - openShiftPipelineDetailViewURL = "https://%s/k8s/ns/%s/tekton.dev~v1beta1~PipelineRun/%s" - openShiftPipelineTaskLogURL = "%s/logs/%s" - openShiftRouteGroup = "route.openshift.io" - openShiftRouteVersion = "v1" - openShiftRouteResource = "routes" - openshiftConsoleName = "OpenShift Console" + openShiftConsoleNS = "openshift-console" + openShiftConsoleRouteName = "console" + openShiftPipelineNamespaceViewURL = "https://%s/pipelines/ns/%s/pipeline-runs" + openShiftPipelineDetailViewURL = "https://%s/k8s/ns/%s/tekton.dev~v1beta1~PipelineRun/%s" + openShiftPipelineTaskLogURL = "%s/logs/%s" + openShiftRouteGroup = "route.openshift.io" + openShiftRouteVersion = "v1" + openShiftRouteResource = "routes" + openshiftConsoleName = "OpenShift Console" ) type OpenshiftConsole struct { @@ -44,6 +45,10 @@ func (o *OpenshiftConsole) TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatus *t return fmt.Sprintf(openShiftPipelineTaskLogURL, o.DetailURL(pr), taskRunStatus.PipelineTaskName) } +func (o *OpenshiftConsole) NameSpaceURL(pr *tektonv1.PipelineRun) string { + return fmt.Sprintf(openShiftPipelineNamespaceViewURL, o.host, pr.GetNamespace()) +} + // UI use dynamic client to get the route of the openshift // console where we can point to. func (o *OpenshiftConsole) UI(ctx context.Context, kdyn dynamic.Interface) error { diff --git a/pkg/consoleui/openshift_test.go b/pkg/consoleui/openshift_test.go index d8a0b3626..783597b31 100644 --- a/pkg/consoleui/openshift_test.go +++ b/pkg/consoleui/openshift_test.go @@ -103,15 +103,16 @@ func TestOpenshiftConsoleUI(t *testing.T) { func TestOpenshiftConsoleURLs(t *testing.T) { pr := &tektonv1.PipelineRun{ ObjectMeta: metav1.ObjectMeta{ - Namespace: "ns", + Namespace: "theNS", Name: "pr", }, } trStatus := &tektonv1.PipelineRunTaskRunStatus{ PipelineTaskName: "task", } - o := OpenshiftConsole{host: "http://fakeconsole"} - assert.Assert(t, o.URL() != "") - assert.Assert(t, o.DetailURL(pr) != "") - assert.Assert(t, o.TaskLogURL(pr, trStatus) != "") + o := OpenshiftConsole{host: "fakeconsole"} + assert.Equal(t, o.URL(), "https://fakeconsole") + assert.Equal(t, o.DetailURL(pr), "https://fakeconsole/k8s/ns/theNS/tekton.dev~v1beta1~PipelineRun/pr") + assert.Equal(t, o.TaskLogURL(pr, trStatus), "https://fakeconsole/k8s/ns/theNS/tekton.dev~v1beta1~PipelineRun/pr/logs/task") + assert.Equal(t, o.NameSpaceURL(pr), "https://fakeconsole/pipelines/ns/theNS/pipeline-runs") } diff --git a/pkg/consoleui/tektondashboard.go b/pkg/consoleui/tektondashboard.go index 8bbf7b60c..4c64021e1 100644 --- a/pkg/consoleui/tektondashboard.go +++ b/pkg/consoleui/tektondashboard.go @@ -22,6 +22,10 @@ func (t *TektonDashboard) DetailURL(pr *tektonv1.PipelineRun) string { return fmt.Sprintf("%s/#/namespaces/%s/pipelineruns/%s", t.BaseURL, pr.GetNamespace(), pr.GetName()) } +func (t *TektonDashboard) NameSpaceURL(pr *tektonv1.PipelineRun) string { + return fmt.Sprintf("%s/#/namespaces/%s/pipelineruns", t.BaseURL, pr.GetNamespace()) +} + func (t *TektonDashboard) TaskLogURL(pr *tektonv1.PipelineRun, taskRunStatus *tektonv1.PipelineRunTaskRunStatus) string { return fmt.Sprintf("%s?pipelineTask=%s", t.DetailURL(pr), taskRunStatus.PipelineTaskName) } diff --git a/pkg/formatting/starting.go b/pkg/formatting/starting.go index a3181d988..1db37b346 100644 --- a/pkg/formatting/starting.go +++ b/pkg/formatting/starting.go @@ -12,18 +12,24 @@ var StartingPipelineRunText string //go:embed templates/queuing.go.tmpl var QueuingPipelineRunText string +//go:embed templates/failures.tmpl +var FailurePipelineRunText string + type MessageTemplate struct { PipelineRunName string Namespace string + NamespaceURL string ConsoleName string ConsoleURL string TknBinary string TknBinaryURL string + TaskStatus string + FailureSnippet string } -func (mt MessageTemplate) MakeTemplate(msg string) (string, error) { +func (mt MessageTemplate) MakeTemplate(tmpl string) (string, error) { outputBuffer := bytes.Buffer{} - t := template.Must(template.New("Message").Parse(msg)) + t := template.Must(template.New("Message").Parse(tmpl)) data := struct{ Mt MessageTemplate }{Mt: mt} if err := t.Execute(&outputBuffer, data); err != nil { return "", err diff --git a/pkg/formatting/starting_test.go b/pkg/formatting/starting_test.go index 923f78ef2..1ec36582c 100644 --- a/pkg/formatting/starting_test.go +++ b/pkg/formatting/starting_test.go @@ -12,6 +12,7 @@ func TestMessageTemplate_MakeTemplate(t *testing.T) { ConsoleURL: "https://test-console-url.com", TknBinary: "test-tkn", TknBinaryURL: "https://test-tkn-url.com", + FailureSnippet: "such a failure", } tests := []struct { @@ -33,6 +34,12 @@ func TestMessageTemplate_MakeTemplate(t *testing.T) { msg: "Starting Pipelinerun {{.Mt.PipelineRunName}} in namespace {{.FOOOBAR }}", wantErr: true, }, + { + name: "Failure template", + mt: mt, + msg: "I am {{ .Mt.FailureSnippet }}", + want: "I am such a failure", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/formatting/templates/failures.tmpl b/pkg/formatting/templates/failures.tmpl new file mode 100644 index 000000000..0519f6e2b --- /dev/null +++ b/pkg/formatting/templates/failures.tmpl @@ -0,0 +1,10 @@ + +
+

Task Statuses:

+{{ .Mt.TaskStatus }} +
+

Failure snippet:

+{{ .Mt.FailureSnippet }} diff --git a/pkg/formatting/templates/starting.go.tmpl b/pkg/formatting/templates/starting.go.tmpl index 6e6fc73b0..0d5ed50d1 100644 --- a/pkg/formatting/templates/starting.go.tmpl +++ b/pkg/formatting/templates/starting.go.tmpl @@ -1,5 +1,5 @@ Starting Pipelinerun {{ .Mt.PipelineRunName }} in namespace {{ .Mt.Namespace }}
You can monitor the execution using the [{{ .Mt.ConsoleName }}]({{ .Mt.ConsoleURL }}) PipelineRun viewer or through the command line by using the [{{ .Mt.TknBinary }}]({{ .Mt.TknBinaryURL }}) CLI with the following command: -
+ {{ .Mt.TknBinary }} pr logs -n {{ .Mt.Namespace }} {{ .Mt.PipelineRunName }} -f diff --git a/pkg/params/settings/config.go b/pkg/params/settings/config.go index b42603d6b..df2e97c48 100644 --- a/pkg/params/settings/config.go +++ b/pkg/params/settings/config.go @@ -24,10 +24,11 @@ const ( AutoConfigureNewGitHubRepoKey = "auto-configure-new-github-repo" AutoConfigureRepoNamespaceTemplateKey = "auto-configure-repo-namespace-template" - CustomConsoleNameKey = "custom-console-name" - CustomConsoleURLKey = "custom-console-url" - CustomConsolePRDetailKey = "custom-console-url-pr-details" - CustomConsolePRTaskLogKey = "custom-console-url-pr-tasklog" + CustomConsoleNameKey = "custom-console-name" + CustomConsoleURLKey = "custom-console-url" + CustomConsolePRDetailKey = "custom-console-url-pr-details" + CustomConsolePRTaskLogKey = "custom-console-url-pr-tasklog" + CustomConsoleNamespaceURLKey = "custom-console-url-namespace" SecretAutoCreateKey = "secret-auto-create" secretAutoCreateDefaultValue = "true" @@ -94,10 +95,11 @@ type Settings struct { ErrorDetectionNumberOfLines int ErrorDetectionSimpleRegexp string - CustomConsoleName string - CustomConsoleURL string - CustomConsolePRdetail string - CustomConsolePRTaskLog string + CustomConsoleName string + CustomConsoleURL string + CustomConsolePRdetail string + CustomConsolePRTaskLog string + CustomConsoleNamespaceURL string RememberOKToTest bool } @@ -227,6 +229,11 @@ func ConfigToSettings(logger *zap.SugaredLogger, setting *Settings, config map[s setting.CustomConsolePRdetail = config[CustomConsolePRDetailKey] } + if setting.CustomConsoleNamespaceURL != config[CustomConsoleNamespaceURLKey] { + logger.Infof("CONFIG: setting custom console namespace URL to %v", config[CustomConsoleNamespaceURLKey]) + setting.CustomConsoleNamespaceURL = config[CustomConsoleNamespaceURLKey] + } + if setting.CustomConsolePRTaskLog != config[CustomConsolePRTaskLogKey] { logger.Infof("CONFIG: setting custom console pr task log URL to %v", config[CustomConsolePRTaskLogKey]) setting.CustomConsolePRTaskLog = config[CustomConsolePRTaskLogKey] diff --git a/pkg/reconciler/status.go b/pkg/reconciler/status.go index 034467973..2470df328 100644 --- a/pkg/reconciler/status.go +++ b/pkg/reconciler/status.go @@ -13,6 +13,7 @@ import ( "github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction" kstatus "github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction/status" "github.com/openshift-pipelines/pipelines-as-code/pkg/params/info" + "github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings" "github.com/openshift-pipelines/pipelines-as-code/pkg/provider" "github.com/openshift-pipelines/pipelines-as-code/pkg/secrets" "github.com/openshift-pipelines/pipelines-as-code/pkg/sort" @@ -26,7 +27,6 @@ import ( const ( maxPipelineRunStatusRun = 5 logSnippetNumLines = 3 - failureReasonText = "%s

Failure reason


%s" ) var backoffSchedule = []time.Duration{ @@ -127,7 +127,23 @@ func (r *Reconciler) postFinalStatus(ctx context.Context, logger *zap.SugaredLog if failures != "" { secretValues := secrets.GetSecretsAttachedToPipelineRun(ctx, r.kinteract, pr) failures = secrets.ReplaceSecretsInText(failures, secretValues) - taskStatusText = fmt.Sprintf(failureReasonText, taskStatusText, failures) + + consoleURL := r.run.Clients.ConsoleUI.DetailURL(pr) + namespaceURL := r.run.Clients.ConsoleUI.NameSpaceURL(pr) + mt := formatting.MessageTemplate{ + PipelineRunName: pr.GetName(), + Namespace: pr.GetNamespace(), + NamespaceURL: namespaceURL, + ConsoleName: r.run.Clients.ConsoleUI.GetName(), + ConsoleURL: consoleURL, + TknBinary: settings.TknBinaryName, + TknBinaryURL: settings.TknBinaryURL, + TaskStatus: taskStatusText, + FailureSnippet: failures, + } + if taskStatusText, err = mt.MakeTemplate(formatting.FailurePipelineRunText); err != nil { + return nil, fmt.Errorf("cannot create message template: %w", err) + } } }