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 script notification #147

Merged
merged 1 commit into from
Nov 29, 2020
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
1 change: 1 addition & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,5 @@ Can be transposed to:
* notif
* [mail](notif/mail.md)
* [slack](notif/slack.md)
* [script](notif/script.md)
* [webhook](notif/webhook.md)
50 changes: 50 additions & 0 deletions docs/config/notif/script.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Script notifications

You can call a script when a notification occured. Following environment variables will be passed:

```
FTPGRAB_VERSION=3.0.0
FTPGRAB_SERVER_IP=10.0.0.1
FTPGRAB_DEST_HOSTNAME=my-computer
FTPGRAB_JOURNAL_ENTRIES[0]_FILE=/test/test_changed/1GB.bin
FTPGRAB_JOURNAL_ENTRIES[0]_STATUS=Not included
FTPGRAB_JOURNAL_ENTRIES[0]_LEVEL=skip
FTPGRAB_JOURNAL_ENTRIES[0]_TEXT=
FTPGRAB_JOURNAL_ENTRIES[1]_FILE=/test/test_changed/56a42b12df8d27baa163536e7b10d3c7.png
FTPGRAB_JOURNAL_ENTRIES[1]_STATUS=Not included
FTPGRAB_JOURNAL_ENTRIES[1]_LEVEL=skip
FTPGRAB_JOURNAL_ENTRIES[1]_TEXT=
FTPGRAB_JOURNAL_ENTRIES[2]_FILE=/test/test_special_chars/1024.rnd
FTPGRAB_JOURNAL_ENTRIES[2]_STATUS=Never downloaded
FTPGRAB_JOURNAL_ENTRIES[2]_LEVEL=success
FTPGRAB_JOURNAL_ENTRIES[2]_TEXT=1.049MB successfully downloaded in 513 milliseconds
FTPGRAB_JOURNAL_COUNT_SUCCESS=1
FTPGRAB_JOURNAL_COUNT_SKIP=2
FTPGRAB_JOURNAL_COUNT_ERROR=0
FTPGRAB_JOURNAL_DURATION=12 seconds
```

## Configuration

!!! example "File"
```yaml
notif:
script:
cmd: "myprogram"
args:
- "--anarg"
- "another"
```

| Name | Default | Description |
|-----------------------|---------------|---------------|
| `cmd`[^1] | | Command or script to execute |
| `args` | | List of args to pass to `cmd` |
| `dir` | | Specifies the working directory of the command |

!!! abstract "Environment variables"
* `FTPGRAB_NOTIF_SCRIPT_CMD`
* `FTPGRAB_NOTIF_SCRIPT_ARGS`
* `FTPGRAB_NOTIF_SCRIPT_DIR`

[^1]: Value required
13 changes: 7 additions & 6 deletions docs/config/notif/webhook.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,19 @@ The JSON response will look like this:
"entries": [
{
"file": "/test/test_changed/1GB.bin",
"status_type": "skip",
"status_text": "Not included"
"status": "Not included",
"level": "skip"
},
{
"file": "/test/test_changed/56a42b12df8d27baa163536e7b10d3c7.png",
"status_type": "skip",
"status_text": "Not included"
"status": "Not included",
"level": "skip"
},
{
"file": "/test/test_special_chars/1024.rnd",
"status_type": "success",
"status_text": "1.049MB successfully downloaded in 513 milliseconds"
"status": "Never downloaded",
"level": "success",
"text": "1.049MB successfully downloaded in 513 milliseconds"
}
],
"count": {
Expand Down
6 changes: 6 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func TestLoadFile(t *testing.T) {
Slack: &NotifSlack{
WebhookURL: "https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij",
},
Script: &NotifScript{
Cmd: "uname",
Args: []string{
"-a",
},
},
Webhook: &NotifWebhook{
Endpoint: "http://webhook.foo.com/sd54qad89azd5a",
Method: "GET",
Expand Down
4 changes: 4 additions & 0 deletions internal/config/fixtures/config.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ notif:
to: [email protected]
slack:
webhookURL: https://hooks.slack.com/services/ABCD12EFG/HIJK34LMN/01234567890abcdefghij
script:
cmd: "uname"
args:
- "-a"
webhook:
endpoint: http://webhook.foo.com/sd54qad89azd5a
method: GET
Expand Down
1 change: 1 addition & 0 deletions internal/config/notif.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package config
type Notif struct {
Mail *NotifMail `yaml:"mail,omitempty" json:"mail,omitempty"`
Slack *NotifSlack `yaml:"slack,omitempty" json:"slack,omitempty"`
Script *NotifScript `yaml:"script,omitempty" json:"script,omitempty"`
Webhook *NotifWebhook `yaml:"webhook,omitempty" json:"webhook,omitempty"`
}

Expand Down
18 changes: 18 additions & 0 deletions internal/config/notif_script.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package config

// NotifScript holds script notification configuration details
type NotifScript struct {
Cmd string `yaml:"cmd,omitempty" json:"cmd,omitempty" validate:"required"`
Args []string `yaml:"args,omitempty" json:"args,omitempty" validate:"omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty" validate:"omitempty,dir"`
}

// GetDefaults gets the default values
func (s *NotifScript) GetDefaults() *NotifScript {
return nil
}

// SetDefaults sets the default values
func (s *NotifScript) SetDefaults() {
// noop
}
4 changes: 4 additions & 0 deletions internal/notif/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/crazy-max/ftpgrab/v7/internal/journal"
"github.com/crazy-max/ftpgrab/v7/internal/notif/mail"
"github.com/crazy-max/ftpgrab/v7/internal/notif/notifier"
"github.com/crazy-max/ftpgrab/v7/internal/notif/script"
"github.com/crazy-max/ftpgrab/v7/internal/notif/slack"
"github.com/crazy-max/ftpgrab/v7/internal/notif/webhook"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -37,6 +38,9 @@ func New(cfg *config.Notif, meta config.Meta) (*Client, error) {
if cfg.Slack != nil {
c.notifiers = append(c.notifiers, slack.New(cfg.Slack, meta))
}
if cfg.Script != nil {
c.notifiers = append(c.notifiers, script.New(cfg.Script, meta))
}
if cfg.Webhook != nil {
c.notifiers = append(c.notifiers, webhook.New(cfg.Webhook, meta))
}
Expand Down
83 changes: 83 additions & 0 deletions internal/notif/script/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package script

import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"

"github.com/crazy-max/ftpgrab/v7/internal/config"
"github.com/crazy-max/ftpgrab/v7/internal/journal"
"github.com/crazy-max/ftpgrab/v7/internal/notif/notifier"
"github.com/hako/durafmt"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)

// Client represents an active script notification object
type Client struct {
*notifier.Notifier
cfg *config.NotifScript
meta config.Meta
}

// New creates a new script notification instance
func New(config *config.NotifScript, meta config.Meta) notifier.Notifier {
return notifier.Notifier{
Handler: &Client{
cfg: config,
meta: meta,
},
}
}

// Name returns notifier's name
func (c *Client) Name() string {
return "script"
}

// Send creates and sends a slack notification with journal entries
func (c *Client) Send(jnl journal.Journal) error {
cmd := exec.Command(c.cfg.Cmd, c.cfg.Args...)
setSysProcAttr(cmd)

// Capture output
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

// Set working dir
if c.cfg.Dir != "" {
cmd.Dir = c.cfg.Dir
}

// Set env vars
cmd.Env = append(os.Environ(), []string{
fmt.Sprintf("FTPGRAB_VERSION=%s", c.meta.Version),
fmt.Sprintf("FTPGRAB_SERVER_IP=%s", jnl.ServerHost),
fmt.Sprintf("FTPGRAB_DEST_HOSTNAME=%s", c.meta.Hostname),
}...)
for idx, entry := range jnl.Entries {
cmd.Env = append(cmd.Env, []string{
fmt.Sprintf("FTPGRAB_JOURNAL_ENTRIES[%d]_FILE=%s", idx, entry.File),
fmt.Sprintf("FTPGRAB_JOURNAL_ENTRIES[%d]_STATUS=%s", idx, string(entry.Status)),
fmt.Sprintf("FTPGRAB_JOURNAL_ENTRIES[%d]_LEVEL=%s", idx, string(entry.Level)),
fmt.Sprintf("FTPGRAB_JOURNAL_ENTRIES[%d]_TEXT=%s", idx, entry.Text),
}...)
}
cmd.Env = append(cmd.Env, []string{
fmt.Sprintf("FTPGRAB_JOURNAL_COUNT_SUCCESS=%d", jnl.Count.Success),
fmt.Sprintf("FTPGRAB_JOURNAL_COUNT_ERROR=%d", jnl.Count.Error),
fmt.Sprintf("FTPGRAB_JOURNAL_COUNT_SKIP=%d", jnl.Count.Skip),
fmt.Sprintf("FTPGRAB_JOURNAL_DURATION=%s", durafmt.ParseShort(jnl.Duration).String()),
}...)

// Run
if err := cmd.Run(); err != nil {
return errors.Wrap(err, strings.TrimSpace(stderr.String()))
}

log.Debug().Msgf(strings.TrimSpace(stdout.String()))
return nil
}
10 changes: 10 additions & 0 deletions internal/notif/script/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// +build !windows

package script

import (
"os/exec"
)

func setSysProcAttr(cmd *exec.Cmd) {
}
10 changes: 10 additions & 0 deletions internal/notif/script/cmd_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package script

import (
"os/exec"
"syscall"
)

func setSysProcAttr(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}