notifier.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package slack
  2. import (
  3. "bytes"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/porter-dev/porter/internal/models/integrations"
  8. )
  9. type Notifier interface {
  10. Notify(opts *NotifyOpts) error
  11. }
  12. type DeploymentStatus string
  13. const (
  14. StatusDeployed string = "deployed"
  15. StatusFailed string = "failed"
  16. )
  17. type NotifyOpts struct {
  18. // ProjectID is the id of the Porter project that this deployment belongs to
  19. ProjectID uint
  20. // ClusterID is the id of the Porter cluster that this deployment belongs to
  21. ClusterID uint
  22. // ClusterName is the name of the cluster that this deployment was deployed in
  23. ClusterName string
  24. // Status is the current status of the deployment.
  25. Status string
  26. // Info is any additional information about this status, such as an error message if
  27. // the deployment failed.
  28. Info string
  29. // Name is the name of the deployment that this notification refers to.
  30. Name string
  31. // Namespace is the Kubernetes namespace of the deployment that this notification refers to.
  32. Namespace string
  33. URL string
  34. Version int
  35. }
  36. type SlackNotifier struct {
  37. slackInts []*integrations.SlackIntegration
  38. }
  39. func NewSlackNotifier(slackInts ...*integrations.SlackIntegration) Notifier {
  40. return &SlackNotifier{
  41. slackInts: slackInts,
  42. }
  43. }
  44. func (s *SlackNotifier) Notify(opts *NotifyOpts) error {
  45. var statusPayload string
  46. switch opts.Status {
  47. case StatusDeployed:
  48. statusPayload = getSuccessPayload(opts)
  49. case StatusFailed:
  50. statusPayload = getFailedPayload(opts)
  51. }
  52. payload := fmt.Sprintf(`
  53. {
  54. "blocks": [
  55. %s
  56. {
  57. "type": "divider"
  58. },
  59. {
  60. "type": "section",
  61. "text": {
  62. "type": "mrkdwn",
  63. "text": "*Name:* %s"
  64. }
  65. },
  66. {
  67. "type": "section",
  68. "text": {
  69. "type": "mrkdwn",
  70. "text": "*Namespace:* %s"
  71. }
  72. },
  73. {
  74. "type": "section",
  75. "text": {
  76. "type": "mrkdwn",
  77. "text": "*Version:* %d"
  78. }
  79. }
  80. ]
  81. }
  82. `, statusPayload, "`"+opts.Name+"`", "`"+opts.Namespace+"`", opts.Version)
  83. reqBody := bytes.NewReader([]byte(payload))
  84. client := &http.Client{
  85. Timeout: time.Second * 5,
  86. }
  87. for _, slackInt := range s.slackInts {
  88. client.Post(string(slackInt.Webhook), "application/json", reqBody)
  89. }
  90. return nil
  91. }
  92. func getSuccessPayload(opts *NotifyOpts) string {
  93. return fmt.Sprintf(`
  94. {
  95. "type": "section",
  96. "text": {
  97. "type": "mrkdwn",
  98. "text": ":rocket: Your application %s was successfully updated on Porter! <%s|View the new release.>"
  99. }
  100. },
  101. `, "`"+opts.Name+"`", opts.URL)
  102. }
  103. func getFailedPayload(opts *NotifyOpts) string {
  104. return fmt.Sprintf(`
  105. {
  106. "type": "section",
  107. "text": {
  108. "type": "mrkdwn",
  109. "text": ":x: Your application %s failed to deploy on Porter. <%s|View the status here.>"
  110. }
  111. },
  112. `, "`"+opts.Name+"`", opts.URL)
  113. }