Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add auth stmp mailer #339

Merged
merged 4 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
75 changes: 63 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ It runs <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAGs (Dir
- Install by placing just a single binary file
- Schedule executions of DAGs with Cron expressions
- Define dependencies between related jobs and represent them as a single DAG (unit of execution)
- Send email notifications upon DAG execution error/success with simple configuration

## Contents

Expand Down Expand Up @@ -66,6 +67,7 @@ It runs <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAGs (Dir
- [HTTP Executor](#http-executor)
- [Admin Configuration](#admin-configuration)
- [Environment Variable](#environment-variable)
- [Sending email notifications](#sending-email-notifications)
- [Base Configuration for all DAGs](#base-configuration-for-all-dags)
- [Scheduler](#scheduler)
- [Execution Schedule](#execution-schedule)
Expand Down Expand Up @@ -488,24 +490,73 @@ command: <Absolute path to the dagu binary> # default: dagu

You can configure the dagu's internal work directory by defining `DAGU_HOME` environment variables. Default path is `~/.dagu/`.

## Sending email notifications

Email notifications can be sent when a DAG finished with an error or successfully. To do so, you can set the `stmp` field and related fields in the DAG specs. You can use any email delivery services (e.g., Sendgrid, Mailgun, etc).

```yaml
# Eamil notification settings
mailOn:
failure: true
success: true

# SMTP server settings
smtp:
host: "smtp.foo.bar"
port: "587"
username: "<username>"
password: "<password>"

# Error mail configuration
errorMail:
from: "[email protected]"
to: "[email protected]"
prefix: "[Error]"

# Info mail configuration
infoMail:
from: "[email protected]"
to: "[email protected]"
prefix: "[Info]"
```

If you want to use the same settings for all DAGs, set them to the [base configuration](#base-configuration-for-all-dags).


## Base Configuration for all DAGs

Creating a base configuration (default path: `~/.dagu/config.yaml`) is a convenient way to organize shared settings among all DAGs. The path to the base configuration file can be configured. See [Admin Configuration](#admin-configuration) for more details.

```yaml
logDir: <path-to-write-log> # log directory to write standard output
histRetentionDays: 3 # history retention days
smtp: # [optional] mail server configuration to send notifications
host: <smtp server host>
port: <stmp server port>
errorMail: # [optional] mail configuration for error-level
from: <from address>
to: <to address>
prefix: <prefix of mail subject>
# directory path to save logs from standard output
logDir: /path/to/stdout-logs/

# history retention days (default: 30)
histRetentionDays: 3

# Eamil notification settings
mailOn:
failure: true
success: true

# SMTP server settings
smtp:
host: "smtp.foo.bar"
port: "587"
username: "<username>"
password: "<password>"

# Error mail configuration
errorMail:
from: "[email protected]"
to: "[email protected]"
prefix: "[Error]"

# Info mail configuration
infoMail:
from: <from address> # [optional] mail configuration for info-level
to: <to address>
prefix: <prefix of mail subject>
from: "[email protected]"
to: "[email protected]"
prefix: "[Info]"
```

## Scheduler
Expand Down
6 changes: 4 additions & 2 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ func (a *Agent) init() {
Config: &reporter.Config{
Mailer: &mailer.Mailer{
Config: &mailer.Config{
Host: a.DAG.Smtp.Host,
Port: a.DAG.Smtp.Port,
Host: a.DAG.Smtp.Host,
Port: a.DAG.Smtp.Port,
Username: a.DAG.Smtp.Username,
Password: a.DAG.Smtp.Password,
},
},
}}
Expand Down
21 changes: 21 additions & 0 deletions examples/email_example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
steps:
- name: S1
command: echo "hello"

mailOn:
failure: true
success: true

smtp:
host: "smtp.foo.bar"
port: "587"
username: "<username>"
password: "<password>"
errorMail:
from: "[email protected]"
to: "[email protected]"
prefix: "[Error]"
infoMail:
from: "[email protected]"
to: "[email protected]"
prefix: "[Info]"
2 changes: 1 addition & 1 deletion examples/minimal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ steps:
- name: step 1
command: echo $1
- name: step 2
command: sleep 50
command: sleep 1
depends:
- step 1
2 changes: 2 additions & 0 deletions internal/dag/dag.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,8 @@ func buildSmtpConfigFromDefinition(def *configDefinition, d *DAG) (err error) {
smtp := &SmtpConfig{}
smtp.Host = def.Smtp.Host
smtp.Port = def.Smtp.Port
smtp.Username = def.Smtp.Username
smtp.Password = def.Smtp.Password
d.Smtp = smtp
return nil
}
Expand Down
6 changes: 4 additions & 2 deletions internal/dag/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ type retryPolicyDef struct {
}

type smtpConfigDef struct {
Host string
Port string
Host string
Port string
Username string
Password string
}

type mailConfigDef struct {
Expand Down
6 changes: 4 additions & 2 deletions internal/dag/mail.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dag

type SmtpConfig struct {
Host string
Port string
Host string
Port string
Username string
Password string
}

type MailConfig struct {
Expand Down
33 changes: 26 additions & 7 deletions internal/mailer/mailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,38 @@ type Mailer struct {

// Config is a config for SMTP mailer.
type Config struct {
// Host is a hostname of a mail server.
Host string
// Port is a port of a mail server.
Port string
Host string
Port string
Username string
Password string
}

var (
replacer = strings.NewReplacer("\r\n", "", "\r", "", "\n", "", "%0a", "", "%0d", "")
)

// SendMail sends an email.
func (m *Mailer) SendMail(from string, to []string, subject, body string) error {
log.Printf("Sending an email to %s, subject is \"%s\"", strings.Join(to, ","), subject)
r := strings.NewReplacer("\r\n", "", "\r", "", "\n", "", "%0a", "", "%0d", "")
if m.Username == "" && m.Password == "" {
return m.sendWithNoAuth(from, to, subject, body)
}
return m.sendWithAuth(from, to, subject, body)
}

func (m *Mailer) sendWithNoAuth(from string, to []string, subject, body string) error {
c, err := smtp.Dial(m.Host + ":" + m.Port)
if err != nil {
return err
}
defer func() {
_ = c.Close()
}()
if err = c.Mail(r.Replace(from)); err != nil {
if err = c.Mail(replacer.Replace(from)); err != nil {
return err
}
for i := range to {
to[i] = r.Replace(to[i])
to[i] = replacer.Replace(to[i])
if err = c.Rcpt(to[i]); err != nil {
return err
}
Expand All @@ -60,3 +69,13 @@ func (m *Mailer) SendMail(from string, to []string, subject, body string) error
}
return c.Quit()
}

func (m *Mailer) sendWithAuth(from string, to []string, subject, body string) error {
auth := smtp.PlainAuth("", m.Username, m.Password, m.Host)
return smtp.SendMail(m.Host+":"+m.Port, auth, from, to, []byte("To: "+strings.Join(to, ",")+"\r\n"+
"From: "+from+"\r\n"+
"Subject: "+subject+"\r\n"+
"Content-Type: text/html; charset=\"UTF-8\"\r\n"+
"Content-Transfer-Encoding: base64\r\n"+
"\r\n"+base64.StdEncoding.EncodeToString([]byte(body))))
}