-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Matheus Pimenta <[email protected]>
- Loading branch information
1 parent
b81755d
commit 534ee7e
Showing
4 changed files
with
192 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/* | ||
Copyright 2024 The Flux authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package notifier | ||
|
||
import ( | ||
"context" | ||
"crypto/x509" | ||
"fmt" | ||
"net/url" | ||
"strings" | ||
|
||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" | ||
) | ||
|
||
// MSAdapativeCard holds the configuration for talking to an endpoint that accepts | ||
// an MS Adaptive Card payload. | ||
type MSAdapativeCard struct { | ||
URL string | ||
ProxyURL string | ||
CertPool *x509.CertPool | ||
} | ||
|
||
// The payload structures below reflect this documentation: | ||
// https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL%2Ctext1#send-adaptive-cards-using-an-incoming-webhook | ||
|
||
// MSAdaptiveCardMessage is the message payload for MS Adaptive Card. | ||
type MSAdaptiveCardMessage struct { | ||
Type string `json:"type"` | ||
Attachments []MSAdaptiveCardAttachment `json:"attachments"` | ||
} | ||
|
||
// MSAdaptiveCardAttachment is the attachment payload for MS Adaptive Card. | ||
type MSAdaptiveCardAttachment struct { | ||
ContentType string `json:"contentType"` | ||
ContentURL *string `json:"contentUrl"` | ||
Content MSAdaptiveCardContent `json:"content"` | ||
} | ||
|
||
// MSAdaptiveCardContent is the content payload for MS Adaptive Card. | ||
type MSAdaptiveCardContent struct { | ||
Schema string `json:"$schema"` | ||
Type string `json:"type"` | ||
Version string `json:"version"` | ||
Body []MSAdaptiveCardBodyElement `json:"body"` | ||
} | ||
|
||
// MSAdaptiveCardBodyElement is the body element payload for MS Adaptive Card. | ||
type MSAdaptiveCardBodyElement struct { | ||
Type string `json:"type"` | ||
|
||
*MSAdaptiveCardContainer `json:",inline"` | ||
*MSAdaptiveCardTextBlock `json:",inline"` | ||
*MSAdaptiveCardFactSet `json:",inline"` | ||
} | ||
|
||
// MSAdaptiveCardContainer is the container body element payload for MS Adaptive Card. | ||
type MSAdaptiveCardContainer struct { | ||
Items []MSAdaptiveCardBodyElement `json:"items,omitempty"` | ||
} | ||
|
||
// MSAdaptiveCardTextBlock is the text block body element payload for MS Adaptive Card. | ||
type MSAdaptiveCardTextBlock struct { | ||
Text string `json:"text,omitempty"` | ||
Size string `json:"size,omitempty"` | ||
Style string `json:"style,omitempty"` | ||
Color string `json:"color,omitempty"` | ||
Wrap bool `json:"wrap,omitempty"` | ||
} | ||
|
||
// MSAdaptiveCardFactSet is the fact set body element payload for MS Adaptive Card. | ||
type MSAdaptiveCardFactSet struct { | ||
Facts []MSAdaptiveCardFact `json:"facts,omitempty"` | ||
} | ||
|
||
// MSAdaptiveCardFact is the fact body element payload for MS Adaptive Card. | ||
type MSAdaptiveCardFact struct { | ||
Title string `json:"title"` | ||
Value string `json:"value"` | ||
} | ||
|
||
// NewMSAdaptiveCard validates the MS Adaptive Card endpoint URL and returns an MSAdaptiveCard object. | ||
func NewMSAdaptiveCard(hookURL string, proxyURL string, certPool *x509.CertPool) (*MSAdapativeCard, error) { | ||
_, err := url.ParseRequestURI(hookURL) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid MS Adaptive Card endpoint URL %s: '%w'", hookURL, err) | ||
} | ||
|
||
return &MSAdapativeCard{ | ||
URL: hookURL, | ||
ProxyURL: proxyURL, | ||
CertPool: certPool, | ||
}, nil | ||
} | ||
|
||
// Post sends the MS Adaptive Card message. | ||
func (m *MSAdapativeCard) Post(ctx context.Context, event eventv1.Event) error { | ||
// Skip Git commit status update event. | ||
if event.HasMetadata(eventv1.MetaCommitStatusKey, eventv1.MetaCommitStatusUpdateValue) { | ||
return nil | ||
} | ||
|
||
objName := fmt.Sprintf("%s/%s.%s", strings.ToLower(event.InvolvedObject.Kind), event.InvolvedObject.Name, event.InvolvedObject.Namespace) | ||
|
||
message := &MSAdaptiveCardTextBlock{ | ||
Text: event.Message, | ||
Wrap: true, | ||
} | ||
if event.Severity == eventv1.EventSeverityError { | ||
message.Color = "attention" | ||
} | ||
|
||
facts := make([]MSAdaptiveCardFact, 0, len(event.Metadata)) | ||
for k, v := range event.Metadata { | ||
facts = append(facts, MSAdaptiveCardFact{ | ||
Title: k, | ||
Value: v, | ||
}) | ||
} | ||
|
||
// the card below was built with help from https://adaptivecards.io/designer using the Microsoft Teams host app | ||
payload := &MSAdaptiveCardMessage{ | ||
Type: "message", | ||
Attachments: []MSAdaptiveCardAttachment{ | ||
{ | ||
ContentType: "application/vnd.microsoft.card.adaptive", | ||
ContentURL: nil, // should always be nil according to the docs referenced on the top of the file | ||
Content: MSAdaptiveCardContent{ | ||
Schema: "http://adaptivecards.io/schemas/adaptive-card.json", | ||
Type: "AdaptiveCard", | ||
Version: "1.5", | ||
Body: []MSAdaptiveCardBodyElement{ | ||
{ | ||
Type: "Container", | ||
MSAdaptiveCardContainer: &MSAdaptiveCardContainer{ | ||
Items: []MSAdaptiveCardBodyElement{ | ||
{ | ||
Type: "TextBlock", | ||
MSAdaptiveCardTextBlock: &MSAdaptiveCardTextBlock{ | ||
Text: objName, | ||
Size: "medium", | ||
Style: "heading", | ||
Wrap: true, | ||
}, | ||
}, | ||
{ | ||
Type: "TextBlock", | ||
MSAdaptiveCardTextBlock: message, | ||
}, | ||
{ | ||
Type: "FactSet", | ||
MSAdaptiveCardFactSet: &MSAdaptiveCardFactSet{ | ||
Facts: facts, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
err := postMessage(ctx, m.URL, m.ProxyURL, m.CertPool, payload) | ||
if err != nil { | ||
return fmt.Errorf("postMessage failed: %w", err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters