Skip to content

Commit

Permalink
enhance: remote slack attachment (#56)
Browse files Browse the repository at this point in the history
* enhance: adding functionality to access slack attachment in a different repo

* adding tests for new functionality

* making linter happy

* more lint fixes

* Update main.go

* fix

* fix pls

* removing logging statements

* more fiexes

* fixing comments

* fixing

* fixing

---------

Co-authored-by: Claire.Nicholas <Z00B3R3@w6nxmk4t6t.target.com>
  • Loading branch information
claire1618 and Claire.Nicholas authored Nov 28, 2023
1 parent 1213574 commit ae5cb77
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 37 deletions.
18 changes: 17 additions & 1 deletion DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ steps:
+ text: "Hello Repo {{ .RepositoryName }}!"
```
Sample of sending a message with attachment file:
Sample of sending a message with local attachment file:
```diff
steps:
Expand All @@ -47,6 +47,22 @@ steps:
parameters:
- text: "Hello World!"
filepath: slack_attachment.json
remote: false
registry: https://github.com
```
Sample of sending a message with remote attachment file:
```diff
steps:
- name: message-with-remote-attachment
image: target/vela-slack:latest
secrets: [ webhook ]
parameters:
- text: "Hello World!"
filepath: slack_attachment.json
remote: true
registry: https://github.com
```
content of `slack_attachment.json`:
Expand Down
28 changes: 25 additions & 3 deletions cmd/vela-slack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"

"time"
Expand All @@ -24,7 +23,7 @@ import (
_ "github.com/joho/godotenv/autoload"
)

// nolint: funlen // ignore length for main
//nolint:funlen // ignore length for main
func main() {
// capture application version information
v := version.New()
Expand Down Expand Up @@ -92,6 +91,12 @@ func main() {
Name: "webhook",
Usage: "slack webhook used to post log messages to channel",
},
&cli.BoolFlag{
EnvVars: []string{"PARAMETER_REMOTE", "SLACK_REMOTE"},
FilePath: "/vela/parameters/slack/remote,/vela/secrets/slack/remote",
Name: "remote",
Usage: "if filepath is remote or not",
},

// Webhook Flags

Expand Down Expand Up @@ -289,6 +294,14 @@ func main() {
Usage: "environment variable reference for reading in repository trusted",
},

// Registry
&cli.StringFlag{
EnvVars: []string{"PARAMETER_REGISTRY", "REGISTRY_URL"},
FilePath: "/vela/parameters/slack/registry-url,/vela/secrets/slack/registry-url",
Name: "registry-url",
Usage: "registry url",
},

// Optional LDAP config flags

&cli.StringFlag{
Expand Down Expand Up @@ -321,6 +334,12 @@ func main() {
Name: "ldap-search-base",
Usage: "environment variable for enterprise LDAP search base",
},
&cli.StringFlag{
EnvVars: []string{"PARAMETER_TOKEN", "GITHUB_TOKEN"},
FilePath: "/vela/parameters/slack/token,/vela/secrets/slack/token",
Name: "token",
Usage: "github token from user",
},
}

err = app.Run(os.Args)
Expand Down Expand Up @@ -370,6 +389,7 @@ func run(c *cli.Context) error {
Text: c.String("text"),
Parse: c.String("parse"),
},
Remote: c.Bool("remote"),
Env: &Env{
BuildAuthor: c.String("build-author"),
BuildAuthorEmail: c.String("build-author-email"),
Expand All @@ -392,6 +412,7 @@ func run(c *cli.Context) error {
BuildTag: c.String("build-tag"),
BuildTitle: c.String("build-title"),
BuildWorkspace: c.String("build-workspace"),
RegistryURL: c.String("registry-url"),
RepositoryBranch: c.String("repo-branch"),
RepoBranch: c.String("repo-branch"),
RepositoryClone: c.String("repo-clone"),
Expand All @@ -410,6 +431,7 @@ func run(c *cli.Context) error {
RepoTimeout: c.Int("repo-timeout"),
RepositoryTrusted: c.String("repo-trusted"),
RepoTrusted: c.String("repo-trusted"),
Token: c.String("token"),
},
}

Expand Down Expand Up @@ -440,7 +462,7 @@ func getSAMAccountName(c *cli.Context) string {

// create LDAP client
roots := x509.NewCertPool()
caCerts, err := ioutil.ReadFile(c.String("sslcert.path"))
caCerts, err := os.ReadFile(c.String("sslcert.path"))

if err != nil {
logrus.Errorf("%s", err)
Expand Down
101 changes: 82 additions & 19 deletions cmd/vela-slack/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"os"
"regexp"
"strconv"
"strings"
"text/template"

registry "github.com/go-vela/server/compiler/registry/github"

"github.com/Masterminds/sprig/v3"
"github.com/sirupsen/logrus"
"github.com/slack-go/slack"
Expand All @@ -26,6 +28,7 @@ type (
Env *Env
Path string
WebhookMsg *slack.WebhookMessage
Remote bool
}

// Env struct represents the environment variables the Vela injects
Expand All @@ -52,6 +55,7 @@ type (
BuildTag string
BuildTitle string
BuildWorkspace string
RegistryURL string
RepositoryBranch string
RepoBranch string
RepositoryClone string
Expand All @@ -70,11 +74,17 @@ type (
RepoTimeout int
RepositoryTrusted string
RepoTrusted string
Token string
}
)

// Exec formats and runs the commands for sending a message via Slack.
func (p *Plugin) Exec() error {
var (
attachments []slack.Attachment
err error
)

logrus.Debug("running plugin with provided configuration")

// clean up newlines that could invalidate JSON
Expand All @@ -95,8 +105,13 @@ func (p *Plugin) Exec() error {

// parse the slack message file
if len(p.Path) != 0 {
logrus.Infof("Parsing provided template file %s, ", p.Path)
attachments, err := getAttachmentFromFile(p)
logrus.Infof("Parsing provided template file, %s", p.Path)

if p.Remote {
attachments, err = getRemoteAttachment(p)
} else {
attachments, err = getAttachmentFromFile(p)
}

if err != nil {
return fmt.Errorf("unable to parse attachment file: %w", err)
Expand Down Expand Up @@ -189,6 +204,22 @@ func (p *Plugin) Validate() error {
return nil
}

// replaceString function converts bytes into string and replaces variables
// such as {{ .BuildCreated }} with a correct values before returning it back into bytes again.
func replaceString(bytes []byte, p *Plugin) []byte {
bStr := string(bytes)
bStr = strings.ReplaceAll(bStr, "{{ .BuildCreated }}", strconv.Itoa(p.Env.BuildCreated))
bStr = strings.ReplaceAll(bStr, "{{ .BuildEnqueued }}", strconv.Itoa(p.Env.BuildEnqueued))
bStr = strings.ReplaceAll(bStr, "{{ .BuildFinished }}", strconv.Itoa(p.Env.BuildFinished))
bStr = strings.ReplaceAll(bStr, "{{ .BuildNumber }}", strconv.Itoa(p.Env.BuildNumber))
bStr = strings.ReplaceAll(bStr, "{{ .BuildParent }}", strconv.Itoa(p.Env.BuildParent))
bStr = strings.ReplaceAll(bStr, "{{ .BuildStarted }}", strconv.Itoa(p.Env.BuildStarted))
bStr = strings.ReplaceAll(bStr, "{{ .RepositoryTimeout }}", strconv.Itoa(p.Env.RepositoryTimeout))
bytes = []byte(bStr)

return bytes
}

// getAttachmentFromFile function to open and parse json file into
// slack webhook message payload.
func getAttachmentFromFile(p *Plugin) ([]slack.Attachment, error) {
Expand All @@ -201,27 +232,59 @@ func getAttachmentFromFile(p *Plugin) ([]slack.Attachment, error) {
defer jsonFile.Close()

// read the contents of the json template
bytes, err := ioutil.ReadAll(jsonFile)
bytes, err := io.ReadAll(jsonFile)
if err != nil {
return nil, fmt.Errorf("unable to read json file: %w", err)
}

// Converts bytes into string and replaces {{ .BuildCreated }}
// with a timestamp before returning it back into bytes again.
bStr := string(bytes)
// x := strconv.Itoa(p.Env.BuildCreated)
bStr = strings.Replace(bStr, "{{ .BuildCreated }}", strconv.Itoa(p.Env.BuildCreated), -1)
bStr = strings.Replace(bStr, "{{ .BuildEnqueued }}", strconv.Itoa(p.Env.BuildEnqueued), -1)
bStr = strings.Replace(bStr, "{{ .BuildFinished }}", strconv.Itoa(p.Env.BuildFinished), -1)
bStr = strings.Replace(bStr, "{{ .BuildNumber }}", strconv.Itoa(p.Env.BuildNumber), -1)
bStr = strings.Replace(bStr, "{{ .BuildParent }}", strconv.Itoa(p.Env.BuildParent), -1)
bStr = strings.Replace(bStr, "{{ .BuildStarted }}", strconv.Itoa(p.Env.BuildStarted), -1)
bStr = strings.Replace(bStr, "{{ .RepositoryTimeout }}", strconv.Itoa(p.Env.RepositoryTimeout), -1)
bytes = []byte(bStr)
bytes = replaceString(bytes, p)

// create a variable to hold our message
var msg slack.WebhookMessage

// cast bytes to go struct
err = json.Unmarshal(bytes, &msg)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal json file: %w", err)
}

return msg.Attachments, err
}

// getRemoteAttachment function to open and parse slack attachment json file into
// slack webhook message payload.
func getRemoteAttachment(p *Plugin) ([]slack.Attachment, error) {
var (
bytes []byte
err error
)

reg, err := registry.New(p.Env.RegistryURL, p.Env.Token)
if err != nil {
return nil, err
}

// parse source from slack attachment
src, err := reg.Parse(p.Path)
if err != nil {
return nil, fmt.Errorf("invalid slack attachment source provided: %w", err)
}

logrus.WithFields(logrus.Fields{
"org": src.Org,
"repo": src.Repo,
"path": src.Name,
"host": src.Host,
}).Tracef("Using authenticated GitHub client to pull template")

// use private (authenticated) github instance to pull from
bytes, err = reg.Template(nil, src)
if err != nil {
return nil, err
}

bytes = replaceString(bytes, p)

logrus.Info("pip")
logrus.Info(bStr)
logrus.Info("pip")
// create a variable to hold our message
var msg slack.WebhookMessage

Expand Down
Loading

0 comments on commit ae5cb77

Please sign in to comment.