-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #270 from stefafafan/add-channels
Implement `mkr channels pull` command
- Loading branch information
Showing
6 changed files
with
323 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package channels | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
|
||
"github.com/mackerelio/mackerel-client-go" | ||
"github.com/mackerelio/mkr/format" | ||
"github.com/mackerelio/mkr/logger" | ||
"github.com/mackerelio/mkr/mackerelclient" | ||
) | ||
|
||
type channelsApp struct { | ||
client mackerelclient.Client | ||
outStream io.Writer | ||
} | ||
|
||
func (app *channelsApp) run() error { | ||
channels, err := app.client.FindChannels() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
format.PrettyPrintJSON(app.outStream, channels) | ||
return nil | ||
} | ||
|
||
func (app *channelsApp) pullChannels(isVerbose bool, optFilePath string) error { | ||
channels, err := app.client.FindChannels() | ||
logger.DieIf(err) | ||
|
||
filePath := "channels.json" | ||
if optFilePath != "" { | ||
filePath = optFilePath | ||
} | ||
|
||
saveChannels(channels, filePath) | ||
|
||
if isVerbose { | ||
format.PrettyPrintJSON(os.Stdout, channels) | ||
} | ||
|
||
logger.Log("info", fmt.Sprintf("Channels are saved to '%s' (%d rules).", filePath, len(channels))) | ||
return nil | ||
} | ||
|
||
var saveChannels = channelSaveRules | ||
|
||
func channelSaveRules(rules []*mackerel.Channel, filePath string) error { | ||
file, err := os.Create(filePath) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer file.Close() | ||
|
||
channels := map[string]interface{}{"channels": rules} | ||
data := format.JSONMarshalIndent(channels, "", " ") + "\n" | ||
|
||
_, err = file.WriteString(data) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package channels | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/mackerelio/mackerel-client-go" | ||
"github.com/mackerelio/mkr/mackerelclient" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// boolPointer is a helper function to initialize a bool pointer | ||
func boolPointer(b bool) *bool { | ||
return &b | ||
} | ||
|
||
func TestChannelsApp_Run(t *testing.T) { | ||
testCases := []struct { | ||
id string | ||
channels []*mackerel.Channel | ||
expected string | ||
}{ | ||
{ | ||
id: "default", | ||
channels: []*mackerel.Channel{ | ||
&mackerel.Channel{ | ||
ID: "abcdefabc", | ||
Name: "email channel", | ||
Type: "email", | ||
Emails: &[]string{"test@example.com", "test2@example.com"}, | ||
UserIDs: &[]string{"1234", "2345"}, | ||
Events: &[]string{"alert"}, | ||
}, | ||
&mackerel.Channel{ | ||
ID: "bcdefabcd", | ||
Name: "slack channel", | ||
Type: "slack", | ||
URL: "https://hooks.slack.com/services/TAAAA/BBBB/XXXXX", | ||
Mentions: mackerel.Mentions{ | ||
OK: "ok message", | ||
Warning: "warning message", | ||
}, | ||
EnabledGraphImage: boolPointer(true), | ||
Events: &[]string{"alert"}, | ||
}, | ||
&mackerel.Channel{ | ||
ID: "cdefabcde", | ||
Name: "webhook channel", | ||
Type: "webhook", | ||
URL: "http://example.com/webhook", | ||
Events: &[]string{"alertGroup"}, | ||
}, | ||
&mackerel.Channel{ | ||
ID: "defabcdef", | ||
Name: "line channel", | ||
Type: "line", | ||
}, | ||
}, | ||
expected: `[ | ||
{ | ||
"id": "abcdefabc", | ||
"name": "email channel", | ||
"type": "email", | ||
"emails": [ | ||
"test@example.com", | ||
"test2@example.com" | ||
], | ||
"userIds": [ | ||
"1234", | ||
"2345" | ||
], | ||
"mentions": {}, | ||
"events": [ | ||
"alert" | ||
] | ||
}, | ||
{ | ||
"id": "bcdefabcd", | ||
"name": "slack channel", | ||
"type": "slack", | ||
"mentions": { | ||
"ok": "ok message", | ||
"warning": "warning message" | ||
}, | ||
"enabledGraphImage": true, | ||
"url": "https://hooks.slack.com/services/TAAAA/BBBB/XXXXX", | ||
"events": [ | ||
"alert" | ||
] | ||
}, | ||
{ | ||
"id": "cdefabcde", | ||
"name": "webhook channel", | ||
"type": "webhook", | ||
"mentions": {}, | ||
"url": "http://example.com/webhook", | ||
"events": [ | ||
"alertGroup" | ||
] | ||
}, | ||
{ | ||
"id": "defabcdef", | ||
"name": "line channel", | ||
"type": "line", | ||
"mentions": {} | ||
} | ||
] | ||
`, | ||
}, | ||
{ | ||
id: "no channels", | ||
channels: []*mackerel.Channel{}, | ||
expected: `[] | ||
`, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
client := mackerelclient.NewMockClient( | ||
mackerelclient.MockFindChannels(func() ([]*mackerel.Channel, error) { | ||
return tc.channels, nil | ||
}), | ||
) | ||
t.Run(tc.id, func(t *testing.T) { | ||
out := new(bytes.Buffer) | ||
app := &channelsApp{ | ||
client: client, | ||
outStream: out, | ||
} | ||
assert.NoError(t, app.run()) | ||
assert.Equal(t, tc.expected, out.String()) | ||
}) | ||
} | ||
} | ||
|
||
func TestChannelsApp_PullChannels(t *testing.T) { | ||
testCases := []struct { | ||
input string | ||
expected string | ||
}{ | ||
{ | ||
input: "", | ||
expected: "channels.json", | ||
}, | ||
{ | ||
input: "some_file.json", | ||
expected: "some_file.json", | ||
}, | ||
{ | ||
input: "hoge.txt", | ||
expected: "hoge.txt", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
out := new(bytes.Buffer) | ||
|
||
// override saveChannels to simply print filePath | ||
saveChannels = func(rules []*mackerel.Channel, filePath string) error { | ||
fmt.Fprint(out, filePath) | ||
return nil | ||
} | ||
|
||
client := mackerelclient.NewMockClient( | ||
mackerelclient.MockFindChannels(func() ([]*mackerel.Channel, error) { | ||
return []*mackerel.Channel{}, nil | ||
}), | ||
) | ||
app := &channelsApp{ | ||
client: client, | ||
outStream: out, | ||
} | ||
|
||
assert.NoError(t, app.pullChannels(false, tc.input)) | ||
assert.Equal(t, tc.expected, out.String()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package channels | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/mackerelio/mkr/mackerelclient" | ||
"github.com/urfave/cli" | ||
) | ||
|
||
// Command is the definition of channels subcommand | ||
var Command = cli.Command{ | ||
Name: "channels", | ||
Usage: "List notification channels", | ||
Description: ` | ||
Lists notification channels. With no subcommand specified, this will show all channels. | ||
Requests APIs under "/api/v0/channels". See https://mackerel.io/api-docs/entry/channels . | ||
`, | ||
Action: doChannels, | ||
Subcommands: []cli.Command{ | ||
{ | ||
Name: "pull", | ||
Usage: "pull channel settings", | ||
ArgsUsage: "[--file-path | -F <file>] [--verbose | -v]", | ||
Description: ` | ||
Pull channels settings from Mackerel server and save them to a file. The file can be specified by filepath argument <file>. The default is 'channels.json'. | ||
`, | ||
Action: doChannelsPull, | ||
Flags: []cli.Flag{ | ||
cli.StringFlag{Name: "file-path, F", Value: "", Usage: "Filename to store channel settings. default: channels.json"}, | ||
cli.BoolFlag{Name: "verbose, v", Usage: "Verbose output mode"}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
func doChannels(c *cli.Context) error { | ||
client, err := mackerelclient.New(c.GlobalString("conf"), c.GlobalString("apibase")) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return (&channelsApp{ | ||
client: client, | ||
outStream: os.Stdout, | ||
}).run() | ||
} | ||
|
||
func doChannelsPull(c *cli.Context) error { | ||
client, err := mackerelclient.New(c.GlobalString("conf"), c.GlobalString("apibase")) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
isVerbose := c.Bool("verbose") | ||
filePath := c.String("file-path") | ||
|
||
return (&channelsApp{ | ||
client: client, | ||
outStream: os.Stdout, | ||
}).pullChannels(isVerbose, filePath) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters