From 2e51ab7f1f04584df096c54242d0e90833d2548c Mon Sep 17 00:00:00 2001 From: Robin Malik <8790561+robinmalik@users.noreply.github.com> Date: Sat, 21 Oct 2023 10:47:11 +0100 Subject: [PATCH 1/6] Add support for Microsoft Team Channel Webhook --- cli/setup/setup.go | 5 +++++ services/tasks/TaskRunner.go | 1 + util/config.go | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cli/setup/setup.go b/cli/setup/setup.go index 3245d8b2..ba4dc4cc 100644 --- a/cli/setup/setup.go +++ b/cli/setup/setup.go @@ -70,6 +70,11 @@ func InteractiveSetup(conf *util.ConfigType) { askValue("Slack Webhook URL", "", &conf.SlackUrl) } + askConfirmation("Enable Microsoft Team Channel alerts?", false, &conf.MicrosoftTeamsAlert) + if conf.MicrosoftTeamsAlert { + askValue("Microsoft Teams Webhook URL", "", &conf.MicrosoftTeamsUrl) + } + askConfirmation("Enable LDAP authentication?", false, &conf.LdapEnable) if conf.LdapEnable { askValue("LDAP server host", "localhost:389", &conf.LdapServer) diff --git a/services/tasks/TaskRunner.go b/services/tasks/TaskRunner.go index 7ebaa731..115aff1e 100644 --- a/services/tasks/TaskRunner.go +++ b/services/tasks/TaskRunner.go @@ -96,6 +96,7 @@ func (t *TaskRunner) SetStatus(status lib.TaskStatus) { if status == lib.TaskSuccessStatus || status == lib.TaskFailStatus { t.sendTelegramAlert() t.sendSlackAlert() + t.sendMicrosoftTeamsAlert() } } diff --git a/util/config.go b/util/config.go index 3d053da3..5bb0f4bb 100644 --- a/util/config.go +++ b/util/config.go @@ -159,12 +159,15 @@ type ConfigType struct { LdapMappings ldapMappings `json:"ldap_mappings"` LdapNeedTLS bool `json:"ldap_needtls" env:"SEMAPHORE_LDAP_NEEDTLS"` - // telegram and slack alerting + // Telegram, Slack and Microsoft Teams alerting TelegramAlert bool `json:"telegram_alert" env:"SEMAPHORE_TELEGRAM_ALERT"` TelegramChat string `json:"telegram_chat" env:"SEMAPHORE_TELEGRAM_CHAT"` TelegramToken string `json:"telegram_token" env:"SEMAPHORE_TELEGRAM_TOKEN"` SlackAlert bool `json:"slack_alert" env:"SEMAPHORE_SLACK_ALERT"` SlackUrl string `json:"slack_url" env:"SEMAPHORE_SLACK_URL"` + MicrosoftTeamsAlert bool `json:"microsoft_teams_alert" env:"SEMAPHORE_MICROSOFT_TEAMS_ALERT"` + MicrosoftTeamsUrl string `json:"microsoft_teams_url" env:"SEMAPHORE_MICROSOFT_TEAMS_URL"` + // oidc settings OidcProviders map[string]OidcProvider `json:"oidc_providers"` From da516132360a085431b3365d0b8c998ba2b30932 Mon Sep 17 00:00:00 2001 From: Robin Malik <8790561+robinmalik@users.noreply.github.com> Date: Sat, 21 Oct 2023 10:50:08 +0100 Subject: [PATCH 2/6] Add Adaptive Card template (not compressed) --- services/tasks/alert.go | 156 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/services/tasks/alert.go b/services/tasks/alert.go index 4f833a54..71ba1f58 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -20,6 +20,59 @@ const telegramTemplate = `{"chat_id": "{{ .ChatID }}","parse_mode":"HTML","text" const slackTemplate = `{ "attachments": [ { "title": "Task: {{ .Name }}", "title_link": "{{ .TaskURL }}", "text": "execution ID #{{ .TaskID }}, status: {{ .TaskResult }}!", "color": "{{ .Color }}", "mrkdwn_in": ["text"], "fields": [ { "title": "Author", "value": "{{ .Author }}", "short": true }] } ]}` +const microsoftTeamsTemplate = `{ + "type": "message", + "attachments": [ + { + "contentType": "application/vnd.microsoft.card.adaptive", + "content": { + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.5", + "body": [ + { + "type": "TextBlock", + "text": "Ansible Task Template Execution by: {{ .Author }}", + }, + { + "type": "FactSet", + "facts": [ + { + "title": "Task:", + "value": "{{ .Name }}" + }, + { + "title": "Status:", + "value": "{{ .TaskResult }}" + }, + { + "title": "Task ID:", + "value": "{{ .TaskID }}" + } + ], + "separator": true + } + ], + "actions": [ + { + "type": "Action.OpenUrl", + "title": "Task URL", + "url": "http://localhost:3000{{ .TaskURL }}" + } + ], + "msteams": { + "width": "Full" + }, + "backgroundImage": { + "horizontalAlignment": "Center", + "url": "data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAABSgAAAAFCAYAAABGmwLHAAAARUlEQVR4nO3YMQEAIBDEsAM1GEErMnkZtyQSOnadd38AAAAAAAq26AAAAABAi0EJAAAAANQYlAAAAABAjUEJAAAAAHQkGWMEAh35OF6mAAAAAElFTkSuQmCC", + "fillMode": "RepeatHorizontally" + } + } + } + ] +}` + // Alert represents an alert that will be templated and sent to the appropriate service type Alert struct { TaskID string @@ -244,3 +297,106 @@ func (t *TaskRunner) sendSlackAlert() { t.Log("Can't send slack alert! Response code: " + strconv.Itoa(resp.StatusCode)) } } + +func (t *TaskRunner) sendMicrosoftTeamsAlert() { + if !util.Config.MicrosoftTeamsAlert || !t.alert { + return + } + + if t.Template.SuppressSuccessAlerts && t.Task.Status == lib.TaskSuccessStatus { + return + } + + MicrosoftTeamsUrl := util.Config.MicrosoftTeamsUrl + + var microsoftTeamsBuffer bytes.Buffer + + var version string + if t.Task.Version != nil { + version = *t.Task.Version + } else if t.Task.BuildTaskID != nil { + version = "build " + strconv.Itoa(*t.Task.BuildTaskID) + } else { + version = "" + } + + var message string + if t.Task.Message != "" { + message = "- " + t.Task.Message + } + + var author string + if t.Task.UserID != nil { + user, err := t.pool.store.GetUser(*t.Task.UserID) + if err != nil { + panic(err) + } + author = user.Name + } + + var color string + if t.Task.Status == lib.TaskSuccessStatus { + color = "good" + } else if t.Task.Status == lib.TaskFailStatus { + color = "bad" + } else if t.Task.Status == lib.TaskRunningStatus { + color = "#333CFF" + } else if t.Task.Status == lib.TaskWaitingStatus { + color = "#FFFC33" + } else if t.Task.Status == lib.TaskStoppingStatus { + color = "#BEBEBE" + } else if t.Task.Status == lib.TaskStoppedStatus { + color = "#5B5B5B" + } + + // Instantiate an alert object + alert := Alert{ + TaskID: strconv.Itoa(t.Task.ID), + Name: t.Template.Name, + TaskURL: util.Config.WebHost + "/project/" + strconv.Itoa(t.Template.ProjectID) + "/templates/" + strconv.Itoa(t.Template.ID) + "?t=" + strconv.Itoa(t.Task.ID), + TaskResult: strings.ToUpper(string(t.Task.Status)), + TaskVersion: version, + TaskDescription: message, + Author: author, + Color: color, + } + + tpl := template.New("MicrosoftTeams body template") + + tpl, err := tpl.Parse(microsoftTeamsTemplate) + if err != nil { + t.Log("Can't parse MicrosoftTeams template!") + panic(err) + } + + // The tpl.Execute(µsoftTeamsBuffer, alert) line is used to apply the data from the alert struct to the template. + // This operation fills in the placeholders in the template with the corresponding values from the alert struct + // and writes the result to the microsoftTeamsBuffer. In essence, it generates a JSON message based on the template and the data in the alert struct. + err = tpl.Execute(µsoftTeamsBuffer, alert) + if err != nil { + t.Log("Can't generate alert template!") + panic(err) + } + + // test if buffer is empty + if microsoftTeamsBuffer.Len() == 0 { + t.Log("MicrosoftTeams buffer is empty!") + return + } + + t.Log("Attempting to send MicrosoftTeams alert") + + resp, err := http.Post(MicrosoftTeamsUrl, "application/json", µsoftTeamsBuffer) + + if err != nil { + t.Log("Can't send MicrosoftTeams alert! Error: " + err.Error()) + } else if resp.StatusCode != 200 { + t.Log("Can't send MicrosoftTeams alert! Response code: " + strconv.Itoa(resp.StatusCode)) + } + + // Output to console + fmt.Println("MicrosoftTeams failed") + jsonString := microsoftTeamsBuffer.String() + fmt.Println(jsonString) + +} \ No newline at end of file From 2abf648d9a5172752c5850850edd7b3acdce5ab9 Mon Sep 17 00:00:00 2001 From: Robin Malik <8790561+robinmalik@users.noreply.github.com> Date: Sat, 21 Oct 2023 10:51:30 +0100 Subject: [PATCH 3/6] Remove some debug logging --- services/tasks/alert.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/services/tasks/alert.go b/services/tasks/alert.go index 71ba1f58..e46a6d8a 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -393,10 +393,4 @@ func (t *TaskRunner) sendMicrosoftTeamsAlert() { } else if resp.StatusCode != 200 { t.Log("Can't send MicrosoftTeams alert! Response code: " + strconv.Itoa(resp.StatusCode)) } - - // Output to console - fmt.Println("MicrosoftTeams failed") - jsonString := microsoftTeamsBuffer.String() - fmt.Println(jsonString) - } \ No newline at end of file From f9011986a0ed0d37349ac370adbcc2f59888588f Mon Sep 17 00:00:00 2001 From: Robin Malik <8790561+robinmalik@users.noreply.github.com> Date: Sat, 21 Oct 2023 10:53:35 +0100 Subject: [PATCH 4/6] Add empty final line in keeping with project code --- services/tasks/alert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/tasks/alert.go b/services/tasks/alert.go index e46a6d8a..d418b6a0 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -393,4 +393,4 @@ func (t *TaskRunner) sendMicrosoftTeamsAlert() { } else if resp.StatusCode != 200 { t.Log("Can't send MicrosoftTeams alert! Response code: " + strconv.Itoa(resp.StatusCode)) } -} \ No newline at end of file +} From 2c36d7e60352be91d9e974d4931a6854ee57fe25 Mon Sep 17 00:00:00 2001 From: Robin Malik <8790561+robinmalik@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:03:30 +0100 Subject: [PATCH 5/6] Update to use red image for indicating failure --- services/tasks/alert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/tasks/alert.go b/services/tasks/alert.go index d418b6a0..c6dc1a99 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -65,7 +65,7 @@ const microsoftTeamsTemplate = `{ }, "backgroundImage": { "horizontalAlignment": "Center", - "url": "data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAABSgAAAAFCAYAAABGmwLHAAAARUlEQVR4nO3YMQEAIBDEsAM1GEErMnkZtyQSOnadd38AAAAAAAq26AAAAABAi0EJAAAAANQYlAAAAABAjUEJAAAAAHQkGWMEAh35OF6mAAAAAElFTkSuQmCC", + "url": "data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAABSgAAAAFCAYAAABGmwLHAAAARklEQVR4nO3YMQEAIBDEsANPSMC/AbzwMm5JJHTseuf+AAAAAAAUbNEBAAAAgBaDEgAAAACoMSgBAAAAgBqDEgAAAADoSDL8RAJfcbcsoQAAAABJRU5ErkJggg==", "fillMode": "RepeatHorizontally" } } From dd15b7feae786163d1417f23cb59cec98d30fbcc Mon Sep 17 00:00:00 2001 From: Robin Malik <8790561+robinmalik@users.noreply.github.com> Date: Sat, 21 Oct 2023 16:33:33 +0100 Subject: [PATCH 6/6] Remove localhost debugging line --- services/tasks/alert.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/tasks/alert.go b/services/tasks/alert.go index c6dc1a99..a510eb38 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -57,7 +57,7 @@ const microsoftTeamsTemplate = `{ { "type": "Action.OpenUrl", "title": "Task URL", - "url": "http://localhost:3000{{ .TaskURL }}" + "url": "{{ .TaskURL }}" } ], "msteams": { @@ -393,4 +393,6 @@ func (t *TaskRunner) sendMicrosoftTeamsAlert() { } else if resp.StatusCode != 200 { t.Log("Can't send MicrosoftTeams alert! Response code: " + strconv.Itoa(resp.StatusCode)) } + + t.Log("MicrosoftTeams alert sent successfully") }