Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Rework email plugin #25

Merged
merged 11 commits into from
Nov 22, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,14 @@ Browse [builtin actions](./pkg/plugins/builtin)
| || `smtp_password`: password of SMTP server
| || `smtp_port`: port of SMTP server
| || `smtp_hostname`: hostname of SMTP server
| || `from`: from which email you want to send the message
|**`email`** | Send an email
w3st3ry marked this conversation as resolved.
Show resolved Hide resolved
| || `smtp_username`: username of SMTP server
| || `smtp_password`: password of SMTP server
| || `smtp_port`: port of SMTP server
| || `smtp_hostname`: hostname of SMTP server
| || `smtp_skip_tls_verif`: Skip or not TLS insecure verify
| || `from_address`: from which email you want to send the message
| || `from_name`: from which name you want to send the message
| || `to`: receiver(s) of your email
| || `subject`: subject of your email
| || `body`: content of your email
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ require (
golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72 // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
google.golang.org/appengine v1.6.3 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/ini.v1 v1.46.0 // indirect
gopkg.in/mail.v2 v2.3.1
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
)
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
Expand All @@ -340,6 +342,8 @@ gopkg.in/go-playground/validator.v9 v9.26.0 h1:2NPPsBpD0ZoxshmLWewQru8rWmbT5JqSz
gopkg.in/go-playground/validator.v9 v9.26.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
Expand Down
20 changes: 18 additions & 2 deletions pkg/plugins/builtin/email/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ action:
smtp_username: {{.config.smtp.username}}
# mandatory, string
smtp_password: {{.config.smtp.password}}
# optional, uint (default is 25)
# mandatory, string as uint
smtp_port: {{.config.smtp.port}}
# mandatory, string
smtp_hostname: {{.config.smtp.hostname}}
# optional, string as boolean
smtp_skip_tls_verify: "true"
# mandatory, string
from: foo@example.org
from_address: foo@example.org
# optional, string
from_name: uTask bot
# mandatory, string collection
to: [bar@example.org, hey@example.org]
# mandatory, string
Expand All @@ -31,4 +35,16 @@ action:

## Note

The plugin returns an object to reuse the parameters in a future component:

```json
{
"from_address":"foo@example.org",
"from_name":"uTask bot",
"to": ["bar@example.org", "hey@example.org"],
"subject":"Hello from µTask",
"body":"I love baguette"
}
```

Sensitive data should be retrieved from configstore and accessed through `{{.config.[itemKey]}}` rather than hardcoded in your template.
119 changes: 67 additions & 52 deletions pkg/plugins/builtin/email/email.go
Original file line number Diff line number Diff line change
@@ -1,65 +1,75 @@
package email

import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"mime"
"net/smtp"
"strings"
"text/template"
"net/http"
"strconv"

mail "gopkg.in/mail.v2"

"github.com/ovh/utask/pkg/plugins/taskplugin"
)

// the email plugin send email
var (
Plugin = taskplugin.New("email", "0.1", exec,
Plugin = taskplugin.New("email", "0.2", exec,
taskplugin.WithConfig(validConfig, Config{}),
)
)

// Config is the configuration needed to send an email
type Config struct {
SMTPUsername string `json:"smtp_username"`
SMTPPassword string `json:"smtp_password"`
SMTPPort uint16 `json:"smtp_port,omitempty"`
SMTPHostname string `json:"smtp_hostname"`
From string `json:"from"`
To []string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
SMTPUsername string `json:"smtp_username"`
SMTPPassword string `json:"smtp_password"`
SMTPPort string `json:"smtp_port"`
SMTPHostname string `json:"smtp_hostname"`
SMTPSkipTLSVerify string `json:"smtp_skip_tls_verify,omitempty"`
FromAddress string `json:"from_address"`
FromName string `json:"from_name,omitempty"`
To []string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
}

const emailTemplate = "From: {{.From}}\r\n" +
"To: {{.To}}\r\n" +
"Subject: {{.Subject}}\r\n" +
"MIME-version: 1.0\r\n" +
"Content-Type: text/html; charset=\"UTF-8\"\r\n" +
"\r\n" +
"{{.Body}}"

func validConfig(config interface{}) error {
cfg := config.(*Config)

if cfg.SMTPUsername == "" {
return errors.New("smtp_username is missing")
}

if cfg.SMTPPassword == "" {
return errors.New("smtp_password is missing")
}
if cfg.From == "" {
return errors.New("from is missing")
}
if len(cfg.To) == 0 {
return errors.New("to is missing")

if _, err := strconv.ParseUint(cfg.SMTPPort, 10, 64); err != nil {
return fmt.Errorf("smtp_port is missing or wrong %s", err.Error())
}

if cfg.SMTPHostname == "" {
return errors.New("smtp_hostname is missing")
}

if cfg.SMTPSkipTLSVerify != "" {
if _, err := strconv.ParseBool(cfg.SMTPSkipTLSVerify); err != nil {
return fmt.Errorf("smtp_skip_tls_verify is wrong %s", err)
}
}

if cfg.FromAddress == "" {
return errors.New("from_address is missing")
}

if len(cfg.To) == 0 {
return errors.New("to is missing")
}

if cfg.Subject == "" {
return errors.New("subject is missing")
}

if cfg.Body == "" {
return errors.New("body is missing")
}
Expand All @@ -70,38 +80,43 @@ func validConfig(config interface{}) error {
func exec(stepName string, config interface{}, ctx interface{}) (interface{}, interface{}, error) {
cfg := config.(*Config)

parameters := struct {
From string `json:"from"`
To string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
}{
cfg.From,
strings.Join(cfg.To, ","),
mime.BEncoding.Encode("UTF-8", cfg.Subject),
cfg.Body,
}
message := mail.NewMessage()

buffer := new(bytes.Buffer)
recipients := make([]string, len(cfg.To))
for i, recipient := range cfg.To {
recipients[i] = message.FormatAddress(recipient, "")
w3st3ry marked this conversation as resolved.
Show resolved Hide resolved
}

template := template.Must(template.New("emailTemplate").Parse(emailTemplate))
template.Execute(buffer, &parameters)
message.SetAddressHeader("From", cfg.FromAddress, cfg.FromName)
message.SetHeader("To", recipients...)
message.SetHeader("Subject", cfg.Subject)
message.SetBody(
http.DetectContentType([]byte(cfg.Body)),
cfg.Body,
)

auth := smtp.PlainAuth("", cfg.SMTPUsername, cfg.SMTPPassword, cfg.SMTPHostname)
port, _ := strconv.ParseUint(cfg.SMTPPort, 10, 64)
skipTLS, _ := strconv.ParseBool(cfg.SMTPSkipTLSVerify)
w3st3ry marked this conversation as resolved.
Show resolved Hide resolved

port := cfg.SMTPPort
if port == 0 {
port = 25
d := mail.NewDialer(cfg.SMTPHostname, int(port), cfg.SMTPUsername, cfg.SMTPPassword)
d.TLSConfig = &tls.Config{InsecureSkipVerify: skipTLS}
w3st3ry marked this conversation as resolved.
Show resolved Hide resolved
if err := d.DialAndSend(message); err != nil {
fmt.Errorf("Send email failed: %s", err.Error())
w3st3ry marked this conversation as resolved.
Show resolved Hide resolved
}

err := smtp.SendMail(
fmt.Sprintf("%s:%d", cfg.SMTPHostname, cfg.SMTPPort),
auth,
cfg.From,
// to reuse configuration
parameters := struct {
w3st3ry marked this conversation as resolved.
Show resolved Hide resolved
FromAddress string `json:"from_address"`
FromName string `json:"from_name"`
To []string `json:"to"`
Subject string `json:"subject"`
Body string `json:"body"`
}{
cfg.FromAddress,
cfg.FromName,
cfg.To,
buffer.Bytes())
if err != nil {
return nil, nil, fmt.Errorf("Send email failed: %s", err.Error())
cfg.Subject,
cfg.Body,
}

return &parameters, nil, nil
Expand Down
61 changes: 0 additions & 61 deletions vendor/golang.org/x/sys/unix/mkasm_darwin.go

This file was deleted.

Loading