Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
118
plugins/inputs/webhooks/README.md
Normal file
118
plugins/inputs/webhooks/README.md
Normal file
|
@ -0,0 +1,118 @@
|
|||
# Webhooks Input Plugin
|
||||
|
||||
This is a Telegraf service plugin that start a http server and register
|
||||
multiple webhook listeners.
|
||||
|
||||
```sh
|
||||
telegraf config -input-filter webhooks -output-filter influxdb > config.conf.new
|
||||
```
|
||||
|
||||
Change the config file to point to the InfluxDB server you are using and adjust
|
||||
the settings to match your environment. Once that is complete:
|
||||
|
||||
```sh
|
||||
cp config.conf.new /etc/telegraf/telegraf.conf
|
||||
sudo service telegraf start
|
||||
```
|
||||
|
||||
## Service Input <!-- @/docs/includes/service_input.md -->
|
||||
|
||||
This plugin is a service input. Normal plugins gather metrics determined by the
|
||||
interval setting. Service plugins start a service to listen and wait for
|
||||
metrics or events to occur. Service plugins have two key differences from
|
||||
normal plugins:
|
||||
|
||||
1. The global or plugin specific `interval` setting may not apply
|
||||
2. The CLI options of `--test`, `--test-wait`, and `--once` may not produce
|
||||
output for this plugin
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# A Webhooks Event collector
|
||||
[[inputs.webhooks]]
|
||||
## Address and port to host Webhook listener on
|
||||
service_address = ":1619"
|
||||
|
||||
## Maximum duration before timing out read of the request
|
||||
# read_timeout = "10s"
|
||||
## Maximum duration before timing out write of the response
|
||||
# write_timeout = "10s"
|
||||
|
||||
[inputs.webhooks.filestack]
|
||||
path = "/filestack"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.github]
|
||||
path = "/github"
|
||||
# secret = ""
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.mandrill]
|
||||
path = "/mandrill"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.rollbar]
|
||||
path = "/rollbar"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.papertrail]
|
||||
path = "/papertrail"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.particle]
|
||||
path = "/particle"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.artifactory]
|
||||
path = "/artifactory"
|
||||
```
|
||||
|
||||
## Available webhooks
|
||||
|
||||
- [Filestack](filestack/)
|
||||
- [Github](github/)
|
||||
- [Mandrill](mandrill/)
|
||||
- [Rollbar](rollbar/)
|
||||
- [Papertrail](papertrail/)
|
||||
- [Particle](particle/)
|
||||
- [Artifactory](artifactory/)
|
||||
|
||||
## Adding new webhooks plugin
|
||||
|
||||
1. Add your webhook plugin inside the `webhooks` folder
|
||||
1. Your plugin must implement the `Webhook` interface
|
||||
1. Import your plugin in the `webhooks.go` file and add it to the `Webhooks` struct
|
||||
|
||||
Both [Github](github/) and [Rollbar](rollbar/) are good example to follow.
|
||||
|
||||
## Metrics
|
||||
|
||||
## Example Output
|
528
plugins/inputs/webhooks/artifactory/README.md
Normal file
528
plugins/inputs/webhooks/artifactory/README.md
Normal file
|
@ -0,0 +1,528 @@
|
|||
# Artifactory Webhook
|
||||
|
||||
You need to configure the organization's artifactory instance(s) as detailed
|
||||
via the artifactory [webhook documentation][webhook docs]. Multiple webhooks may
|
||||
need be needed to configure different domains.
|
||||
|
||||
You can also add a secret that will be used by telegraf to verify the
|
||||
authenticity of the requests.
|
||||
|
||||
[webhook docs]: https://www.jfrog.com/confluence/display/JFROG/Webhooks
|
||||
|
||||
## Events
|
||||
|
||||
The different events type can be found found in the webhook documentation:
|
||||
<https://www.jfrog.com/confluence/display/JFROG/Webhooks>.
|
||||
Events are identified by their `domain` and `event`.
|
||||
The following sections break down each event by domain.
|
||||
|
||||
### Artifact Domain
|
||||
|
||||
#### Artifact Deployed Event
|
||||
|
||||
The Webhook is triggered when an artifact is deployed to a repository.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' int
|
||||
* 'sha256' string
|
||||
|
||||
#### Artifact Deleted Event
|
||||
|
||||
The Webhook is triggered when an artifact is deleted from a repository.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' int
|
||||
* 'sha256' string
|
||||
|
||||
#### Artifact Moved Event
|
||||
|
||||
The Webhook is triggered when an artifact is moved from a repository.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' int
|
||||
* 'source_path' string
|
||||
* 'target_path' string
|
||||
|
||||
#### Artifact Copied Event
|
||||
|
||||
The Webhook is triggered when an artifact is copied from a repository.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' int
|
||||
* 'source_path' string
|
||||
* 'target_path' string
|
||||
|
||||
### Artifact Properties Domain
|
||||
|
||||
#### Properties Added Event
|
||||
|
||||
The Webhook is triggered when a property is added to an artifact/folder
|
||||
in a repository, or the repository itself.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
**Fields**
|
||||
* 'property_key' string
|
||||
* 'property_values' string (joined comma separated list)
|
||||
|
||||
#### Properties Deleted Event
|
||||
|
||||
The Webhook is triggered when a property is deleted from an artifact/folder in a
|
||||
repository, or the repository itself.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'property_key' string
|
||||
* 'property_values' string (joined comma separated list)
|
||||
|
||||
### Docker Domain
|
||||
|
||||
#### Docker Pushed Event
|
||||
|
||||
The Webhook is triggered when a new tag of a Docker image is pushed to a Docker
|
||||
repository.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
* 'image_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' string
|
||||
* 'sha256' string
|
||||
* 'tag' string
|
||||
* 'platforms' []object
|
||||
* 'architecture' string
|
||||
* 'os' string
|
||||
|
||||
#### Docker Deleted Event
|
||||
|
||||
The Webhook is triggered when a tag of a Docker image is deleted from a Docker
|
||||
repository.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
* 'image_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' string
|
||||
* 'sha256' string
|
||||
* 'tag' string
|
||||
* 'platforms' []object
|
||||
* 'architecture' string
|
||||
* 'os' string
|
||||
|
||||
#### Docker Promoted Event
|
||||
|
||||
The Webhook is triggered when a tag of a Docker image is promoted.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'repo' string
|
||||
* 'path' string
|
||||
* 'name' string
|
||||
* 'image_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'size' string
|
||||
* 'sha256' string
|
||||
* 'tag' string
|
||||
* 'platforms' []object
|
||||
* 'architecture' string
|
||||
* 'os' string
|
||||
|
||||
### Build Domain
|
||||
|
||||
#### Build Uploaded Event
|
||||
|
||||
The Webhook is triggered when a new build is uploaded.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'build_name' string
|
||||
* 'build_number' string
|
||||
* 'build_started' string
|
||||
|
||||
#### Build Deleted Event
|
||||
|
||||
The Webhook is triggered when a build is deleted.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'build_name' string
|
||||
* 'build_number' string
|
||||
* 'build_started' string
|
||||
|
||||
#### Build Promoted Event
|
||||
|
||||
The Webhook is triggered when a build is promoted.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'build_name' string
|
||||
* 'build_number' string
|
||||
* 'build_started' string
|
||||
|
||||
### Release Bundle Domain
|
||||
|
||||
#### Release Bundle Created Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle is created.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_name' string
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Signed Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle is signed.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_name' string
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Deleted Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle is deleted.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
### Release Bundle Distribution Domain
|
||||
|
||||
#### Release Bundle Distribution Started Event
|
||||
|
||||
The Webhook is triggered when Release Bundle distribution has started
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Distribution Completed Event
|
||||
|
||||
The Webhook is triggered when Release Bundle distribution has completed.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Distribution Aborted Event
|
||||
|
||||
The Webhook is triggered when Release Bundle distribution has been aborted.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Distribution Failed Event
|
||||
|
||||
The Webhook is triggered when Release Bundle distribution has failed.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
### Release Bundle Version Domain
|
||||
|
||||
#### Release Bundle Version Deletion Started EVent
|
||||
|
||||
The Webhook is triggered when a Release Bundle version deletion has started on
|
||||
one or more Edge nodes.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Version Deletion Completed Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle version deletion has completed
|
||||
from one or more Edge nodes.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Version Deletion Failed Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle version deletion has failed on
|
||||
one or more Edge nodes.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_size' string
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'transaction_id' string
|
||||
* 'edge_node_info_list' []object
|
||||
* 'edge_node_address' string
|
||||
* 'edge_node_name' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
### Release Bundle Destination Domain
|
||||
|
||||
#### Release Bundle Received Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle was received on an Edge Node.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
### Release Bundle Delete Started Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle deletion from an Edge Node
|
||||
completed.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Delete Completed Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle deletion from an Edge Node
|
||||
completed.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'jpd_origin' string
|
||||
|
||||
#### Release Bundle Delete Failed Event
|
||||
|
||||
The Webhook is triggered when a Release Bundle deletion from an Edge Node fails.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'domain' string
|
||||
* 'event_type' string
|
||||
* 'destination' string
|
||||
* 'release_bundle_name' string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'release_bundle_version' string
|
||||
* 'status_message' string
|
||||
* 'jpd_origin' string
|
122
plugins/inputs/webhooks/artifactory/artifactory_webhook.go
Normal file
122
plugins/inputs/webhooks/artifactory/artifactory_webhook.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package artifactory
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1" //nolint:gosec // G505: Blocklisted import crypto/sha1: weak cryptographic primitive - sha1 hash is what is desired in this case
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
Secret string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (awh *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(awh.Path, awh.eventHandler).Methods("POST")
|
||||
|
||||
awh.log = log
|
||||
awh.log.Infof("Started webhooks_artifactory on %s", awh.Path)
|
||||
awh.acc = acc
|
||||
}
|
||||
|
||||
func (awh *Webhook) eventHandler(rw http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if awh.Secret != "" && !checkSignature(awh.Secret, data, r.Header.Get("x-jfrog-event-auth")) {
|
||||
awh.log.Error("Failed to check the artifactory webhook auth signature")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
bodyFields := make(map[string]interface{})
|
||||
err = json.Unmarshal(data, &bodyFields)
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
et := fmt.Sprintf("%v", bodyFields["event_type"])
|
||||
ed := fmt.Sprintf("%v", bodyFields["domain"])
|
||||
ne, err := awh.newEvent(data, et, ed)
|
||||
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
if ne != nil {
|
||||
nm := ne.newMetric()
|
||||
awh.acc.AddFields("artifactory_webhooks", nm.Fields(), nm.Tags(), nm.Time())
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
type newEventError struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (e *newEventError) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
func (awh *Webhook) newEvent(data []byte, et, ed string) (event, error) {
|
||||
awh.log.Debugf("New %v domain %v event received", ed, et)
|
||||
switch ed {
|
||||
case "artifact":
|
||||
if et == "deployed" || et == "deleted" {
|
||||
return generateEvent(data, &artifactDeploymentOrDeletedEvent{})
|
||||
}
|
||||
if et == "moved" || et == "copied" {
|
||||
return generateEvent(data, &artifactMovedOrCopiedEvent{})
|
||||
}
|
||||
return nil, &newEventError{"Not a recognized event type"}
|
||||
case "artifact_property":
|
||||
return generateEvent(data, &artifactPropertiesEvent{})
|
||||
case "docker":
|
||||
return generateEvent(data, &dockerEvent{})
|
||||
case "build":
|
||||
return generateEvent(data, &buildEvent{})
|
||||
case "release_bundle":
|
||||
return generateEvent(data, &releaseBundleEvent{})
|
||||
case "distribution":
|
||||
return generateEvent(data, &distributionEvent{})
|
||||
case "destination":
|
||||
return generateEvent(data, &destinationEvent{})
|
||||
}
|
||||
|
||||
return nil, &newEventError{"Not a recognized event type"}
|
||||
}
|
||||
|
||||
func generateEvent(data []byte, event event) (event, error) {
|
||||
err := json.Unmarshal(data, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
||||
func checkSignature(secret string, data []byte, signature string) bool {
|
||||
return hmac.Equal([]byte(signature), []byte(generateSignature(secret, data)))
|
||||
}
|
||||
|
||||
func generateSignature(secret string, data []byte) string {
|
||||
mac := hmac.New(sha1.New, []byte(secret))
|
||||
if _, err := mac.Write(data); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
result := mac.Sum(nil)
|
||||
return "sha1=" + hex.EncodeToString(result)
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
package artifactory
|
||||
|
||||
func unsupportedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "not_supported",
|
||||
"event_type": "not_supported",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func artifactDeployedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "artifact",
|
||||
"event_type": "deployed",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func artifactDeletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "artifact",
|
||||
"event_type": "deleted",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func artifactMovedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "artifact",
|
||||
"event_type": "moved",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0,
|
||||
"source_repo_path": "sample_repo",
|
||||
"target_repo_path": "sample_target_repo"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func artifactCopiedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "artifact",
|
||||
"event_type": "copied",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0,
|
||||
"source_repo_path": "sample_repo",
|
||||
"target_repo_path": "sample_target_repo"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func artifactPropertiesAddedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "artifact_property",
|
||||
"event_type": "added",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"property_key": "sample_key",
|
||||
"property_values": [
|
||||
"sample_value1"
|
||||
],
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func artifactPropertiesDeletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "artifact_property",
|
||||
"event_type": "deleted",
|
||||
"data": {
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"property_key": "sample_key",
|
||||
"property_values": [
|
||||
"sample_value1"
|
||||
],
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func dockerPushedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "docker",
|
||||
"event_type": "pushed",
|
||||
"data": {
|
||||
"image_name": "sample_arch",
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"platforms": [
|
||||
{
|
||||
"architecture": "sample_os",
|
||||
"os": "sample_tag"
|
||||
}
|
||||
],
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0,
|
||||
"tag": "sample_image"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func dockerDeletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "docker",
|
||||
"event_type": "deleted",
|
||||
"data": {
|
||||
"image_name": "sample_arch",
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"platforms": [
|
||||
{
|
||||
"architecture": "sample_os",
|
||||
"os": "sample_tag"
|
||||
}
|
||||
],
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0,
|
||||
"tag": "sample_image"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func dockerPromotedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "docker",
|
||||
"event_type": "promoted",
|
||||
"data": {
|
||||
"image_name": "sample_arch",
|
||||
"name": "sample.txt",
|
||||
"path": "sample_dir/sample.txt",
|
||||
"platforms": [
|
||||
{
|
||||
"architecture": "sample_os",
|
||||
"os": "sample_tag"
|
||||
}
|
||||
],
|
||||
"repo_key": "sample_repo",
|
||||
"sha256": "sample_checksum",
|
||||
"size": 0,
|
||||
"tag": "sample_image"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func buildUploadedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "build",
|
||||
"event_type": "uploaded",
|
||||
"data": {
|
||||
"build_name": "sample_build_name",
|
||||
"build_number": "1",
|
||||
"build_started": "1970-01-01T00:00:00.000+0000"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func buildDeletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "build",
|
||||
"event_type": "deleted",
|
||||
"data": {
|
||||
"build_name": "sample_build_name",
|
||||
"build_number": "1",
|
||||
"build_started": "1970-01-01T00:00:00.000+0000"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func buildPromotedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "build",
|
||||
"event_type": "promoted",
|
||||
"data": {
|
||||
"build_name": "sample_build_name",
|
||||
"build_number": "1",
|
||||
"build_started": "1970-01-01T00:00:00.000+0000"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func releaseBundleCreatedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "release_bundle",
|
||||
"event_type": "created",
|
||||
"destination": "release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "sample_name",
|
||||
"release_bundle_size": 9800,
|
||||
"release_bundle_version": "1.0.0"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func releaseBundleSignedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "release_bundle",
|
||||
"event_type": "signed",
|
||||
"destination": "release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "sample_name",
|
||||
"release_bundle_size": 9800,
|
||||
"release_bundle_version": "1.0.0"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func releaseBundleDeletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "release_bundle",
|
||||
"event_type": "signed",
|
||||
"destination": "release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "sample_name",
|
||||
"release_bundle_size": 9800,
|
||||
"release_bundle_version": "1.0.0"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func distributionStartedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "distribution",
|
||||
"event_type": "distribute_started",
|
||||
"destination": "distribution",
|
||||
"data": {
|
||||
"edge_node_info_list": [
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge2-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge2"
|
||||
},
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge1-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge1"
|
||||
}
|
||||
],
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_size": 1037976,
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "CREATED",
|
||||
"transaction_id": 395969746957422600
|
||||
},
|
||||
"jpd_origin": "https://ga-dev.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func distributionCompletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "distribution",
|
||||
"event_type": "distribute_completed",
|
||||
"destination": "distribution",
|
||||
"data": {
|
||||
"edge_node_info_list": [
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge2-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge2"
|
||||
},
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge1-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge1"
|
||||
}
|
||||
],
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_size": 1037976,
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "CREATED",
|
||||
"transaction_id": 395969746957422600
|
||||
},
|
||||
"jpd_origin": "https://ga-dev.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func distributionAbortedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "distribution",
|
||||
"event_type": "distribute_aborted",
|
||||
"destination": "distribution",
|
||||
"data": {
|
||||
"edge_node_info_list": [
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge2-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge2"
|
||||
},
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge1-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge1"
|
||||
}
|
||||
],
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_size": 1037976,
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "CREATED",
|
||||
"transaction_id": 395969746957422600
|
||||
},
|
||||
"jpd_origin": "https://ga-dev.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func distributionFailedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "distribution",
|
||||
"event_type": "distribute_failed",
|
||||
"destination": "distribution",
|
||||
"data": {
|
||||
"edge_node_info_list": [
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge2-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge2"
|
||||
},
|
||||
{
|
||||
"edge_node_address": "https://artifactory-edge1-dev.jfrogdev.co/artifactory",
|
||||
"edge_node_name": "artifactory-edge1"
|
||||
}
|
||||
],
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_size": 1037976,
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "CREATED",
|
||||
"transaction_id": 395969746957422600
|
||||
},
|
||||
"jpd_origin": "https://ga-dev.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func destinationReceivedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "destination",
|
||||
"event_type": "received",
|
||||
"destination": "artifactory_release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "COMPLETED"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func destinationDeleteStartedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "destination",
|
||||
"event_type": "delete_started",
|
||||
"destination": "artifactory_release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "COMPLETED"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func destinationDeleteCompletedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "destination",
|
||||
"event_type": "delete_completed",
|
||||
"destination": "artifactory_release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "COMPLETED"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
||||
|
||||
func destinationDeleteFailedEventJSON() string {
|
||||
return `
|
||||
{
|
||||
"domain": "destination",
|
||||
"event_type": "delete_failed",
|
||||
"destination": "artifactory_release_bundle",
|
||||
"data": {
|
||||
"release_bundle_name": "test",
|
||||
"release_bundle_version": "1.0.0",
|
||||
"status_message": "COMPLETED"
|
||||
},
|
||||
"jpd_origin": "https://dist-pipe2.jfrogdev.co/artifactory"
|
||||
}`
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
package artifactory
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
)
|
||||
|
||||
const meas = "artifactory_webhooks"
|
||||
|
||||
type event interface {
|
||||
newMetric() telegraf.Metric
|
||||
}
|
||||
|
||||
type artifactDeploymentOrDeletedEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Data struct {
|
||||
Repo string `json:"repo_key"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Sha string `json:"sha256"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (e artifactDeploymentOrDeletedEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"repo": e.Data.Repo,
|
||||
"path": e.Data.Path,
|
||||
"name": e.Data.Name,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"size": e.Data.Size,
|
||||
"sha256": e.Data.Sha,
|
||||
}
|
||||
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type artifactMovedOrCopiedEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Data struct {
|
||||
Repo string `json:"repo_key"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
SourcePath string `json:"source_repo_path"`
|
||||
TargetPath string `json:"target_repo_path"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (e artifactMovedOrCopiedEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"repo": e.Data.Repo,
|
||||
"path": e.Data.Path,
|
||||
"name": e.Data.Name,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"size": e.Data.Size,
|
||||
"source_path": e.Data.SourcePath,
|
||||
"target_path": e.Data.TargetPath,
|
||||
}
|
||||
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type artifactPropertiesEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Data struct {
|
||||
Repo string `json:"repo_key"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
PropertyKey string `json:"property_key"`
|
||||
PropertyValues []string `json:"property_values"`
|
||||
}
|
||||
}
|
||||
|
||||
func (e artifactPropertiesEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"repo": e.Data.Repo,
|
||||
"path": e.Data.Path,
|
||||
"name": e.Data.Name,
|
||||
}
|
||||
|
||||
f := map[string]interface{}{
|
||||
"property_key": e.Data.PropertyKey,
|
||||
"property_values": strings.Join(e.Data.PropertyValues, ","),
|
||||
}
|
||||
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type dockerEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Data struct {
|
||||
Repo string `json:"repo_key"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Sha string `json:"sha256"`
|
||||
ImageName string `json:"image_name"`
|
||||
Tag string `json:"tag"`
|
||||
Platforms []struct {
|
||||
Architecture string `json:"architecture"`
|
||||
Os string `json:"os"`
|
||||
} `json:"platforms"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (e dockerEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"repo": e.Data.Repo,
|
||||
"path": e.Data.Path,
|
||||
"name": e.Data.Name,
|
||||
"image_name": e.Data.ImageName,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"size": e.Data.Size,
|
||||
"sha256": e.Data.Sha,
|
||||
"tag": e.Data.Tag,
|
||||
"platforms": e.Data.Platforms,
|
||||
}
|
||||
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type buildEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Data struct {
|
||||
BuildName string `json:"build_name"`
|
||||
BuildNumber string `json:"build_number"`
|
||||
BuildStarted string `json:"build_started"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func (e buildEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"build_name": e.Data.BuildName,
|
||||
"build_number": e.Data.BuildNumber,
|
||||
"build_started": e.Data.BuildStarted,
|
||||
}
|
||||
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type releaseBundleEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Destination string `json:"destination"`
|
||||
Data struct {
|
||||
ReleaseBundleName string `json:"release_bundle_name"`
|
||||
ReleaseBundleSize int64 `json:"release_bundle_size"`
|
||||
ReleaseBundleVersion string `json:"release_bundle_version"`
|
||||
} `json:"data"`
|
||||
JpdOrigin string `json:"jpd_origin"`
|
||||
}
|
||||
|
||||
func (e releaseBundleEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"destination": e.Destination,
|
||||
"release_bundle_name": e.Data.ReleaseBundleName,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"release_bundle_size": e.Data.ReleaseBundleSize,
|
||||
"release_bundle_version": e.Data.ReleaseBundleVersion,
|
||||
"jpd_origin": e.JpdOrigin,
|
||||
}
|
||||
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type distributionEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Destination string `json:"destination"`
|
||||
Data struct {
|
||||
EdgeNodeInfoList []struct {
|
||||
EdgeNodeAddress string `json:"edge_node_address"`
|
||||
EdgeNodeName string `json:"edge_node_name"`
|
||||
} `json:"edge_node_info_list"`
|
||||
Name string `json:"release_bundle_name"`
|
||||
Size int64 `json:"release_bundle_size"`
|
||||
Version string `json:"release_bundle_version"`
|
||||
Message string `json:"status_message"`
|
||||
TransactionID int64 `json:"transaction_id"`
|
||||
} `json:"data"`
|
||||
OriginURL string `json:"jpd_origin"`
|
||||
}
|
||||
|
||||
func (e distributionEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"destination": e.Destination,
|
||||
"release_bundle_name": e.Data.Name,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"release_bundle_size": e.Data.Size,
|
||||
"release_bundle_version": e.Data.Version,
|
||||
"status_message": e.Data.Message,
|
||||
"transaction_id": e.Data.TransactionID,
|
||||
"edge_node_info_list": e.Data.EdgeNodeInfoList,
|
||||
"jpd_origin": e.OriginURL,
|
||||
}
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
||||
|
||||
type destinationEvent struct {
|
||||
Domain string `json:"domain"`
|
||||
Event string `json:"event_type"`
|
||||
Destination string `json:"destination"`
|
||||
Data struct {
|
||||
Name string `json:"release_bundle_name"`
|
||||
Version string `json:"release_bundle_version"`
|
||||
Message string `json:"status_message"`
|
||||
} `json:"data"`
|
||||
OriginURL string `json:"jpd_origin"`
|
||||
}
|
||||
|
||||
func (e destinationEvent) newMetric() telegraf.Metric {
|
||||
t := map[string]string{
|
||||
"domain": e.Domain,
|
||||
"event_type": e.Event,
|
||||
"destination": e.Destination,
|
||||
"release_bundle_name": e.Data.Name,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"release_bundle_version": e.Data.Version,
|
||||
"status_message": e.Data.Message,
|
||||
"jpd_origin": e.OriginURL,
|
||||
}
|
||||
return metric.New(meas, t, f, time.Now())
|
||||
}
|
163
plugins/inputs/webhooks/artifactory/artifactory_webhook_test.go
Normal file
163
plugins/inputs/webhooks/artifactory/artifactory_webhook_test.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
package artifactory
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func artifactoryWebhookRequest(t *testing.T, domain, event, jsonString string) {
|
||||
var acc testutil.Accumulator
|
||||
awh := &Webhook{Path: "/artifactory", acc: &acc, log: testutil.Logger{}}
|
||||
req, err := http.NewRequest("POST", "/artifactory", strings.NewReader(jsonString))
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
awh.eventHandler(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("POST "+domain+":"+event+" returned HTTP status code %v.\nExpected %v", w.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func artifactoryWebhookRequestWithSignature(t *testing.T, event, jsonString, signature string, expectedStatus int) {
|
||||
var acc testutil.Accumulator
|
||||
awh := &Webhook{Path: "/artifactory", acc: &acc, log: testutil.Logger{}}
|
||||
req, err := http.NewRequest("POST", "/artifactory", strings.NewReader(jsonString))
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("x-jfrog-event-auth", signature)
|
||||
w := httptest.NewRecorder()
|
||||
awh.eventHandler(w, req)
|
||||
if w.Code != expectedStatus {
|
||||
t.Errorf("POST "+event+" returned HTTP status code %v.\nExpected %v", w.Code, expectedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsupportedEvent(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
awh := &Webhook{Path: "/artifactory", acc: &acc, log: testutil.Logger{}}
|
||||
req, err := http.NewRequest("POST", "/artifactory", strings.NewReader(unsupportedEventJSON()))
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
awh.eventHandler(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("POST returned HTTP status code %v.\nExpected %v", w.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArtifactDeployedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "artifact", "deployed", artifactDeployedEventJSON())
|
||||
}
|
||||
|
||||
func TestArtifactDeleted(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "artifact", "deleted", artifactDeletedEventJSON())
|
||||
}
|
||||
|
||||
func TestArtifactMovedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "artifact", "moved", artifactMovedEventJSON())
|
||||
}
|
||||
|
||||
func TestArtifactCopiedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "artifact", "copied", artifactCopiedEventJSON())
|
||||
}
|
||||
|
||||
func TestArtifactPropertiesAddedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "artifact_property", "added", artifactPropertiesAddedEventJSON())
|
||||
}
|
||||
|
||||
func TestArtifactPropertiesDeletedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "artifact_property", "deleted", artifactPropertiesDeletedEventJSON())
|
||||
}
|
||||
|
||||
func TestDockerPushedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "docker", "pushed", dockerPushedEventJSON())
|
||||
}
|
||||
|
||||
func TestDockerDeletedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "docker", "deleted", dockerDeletedEventJSON())
|
||||
}
|
||||
|
||||
func TestDockerPromotedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "docker", "promoted", dockerPromotedEventJSON())
|
||||
}
|
||||
|
||||
func TestBuildUploadedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "build", "uploaded", buildUploadedEventJSON())
|
||||
}
|
||||
|
||||
func TestBuildDeletedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "build", "deleted", buildDeletedEventJSON())
|
||||
}
|
||||
|
||||
func TestBuildPromotedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "build", "promoted", buildPromotedEventJSON())
|
||||
}
|
||||
|
||||
func TestReleaseBundleCreatedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "release_bundle", "created", releaseBundleCreatedEventJSON())
|
||||
}
|
||||
|
||||
func TestReleaseBundleSignedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "release_bundle", "signed", releaseBundleSignedEventJSON())
|
||||
}
|
||||
|
||||
func TestReleaseBundleDeletedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "release_bundle", "deleted", releaseBundleDeletedEventJSON())
|
||||
}
|
||||
|
||||
func TestDistributionStartedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "distribution", "distribute_started", distributionStartedEventJSON())
|
||||
}
|
||||
|
||||
func TestDistributionCompletedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "distribution", "distribute_started", distributionCompletedEventJSON())
|
||||
}
|
||||
|
||||
func TestDistributionAbortedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "distribution", "distribute_aborted", distributionAbortedEventJSON())
|
||||
}
|
||||
|
||||
func TestDistributionFailedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "distribution", "distribute_failed", distributionFailedEventJSON())
|
||||
}
|
||||
|
||||
func TestDestinationReceivedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "destination", "received", destinationReceivedEventJSON())
|
||||
}
|
||||
|
||||
func TestDestinationDeletedStartedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "destination", "delete_started", destinationDeleteStartedEventJSON())
|
||||
}
|
||||
|
||||
func TestDestinationDeletedCompletedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "destination", "delete_completed", destinationDeleteCompletedEventJSON())
|
||||
}
|
||||
|
||||
func TestDestinationDeleteFailedEvent(t *testing.T) {
|
||||
artifactoryWebhookRequest(t, "destination", "delete_failed", destinationDeleteFailedEventJSON())
|
||||
}
|
||||
|
||||
func TestEventWithSignatureSuccess(t *testing.T) {
|
||||
artifactoryWebhookRequestWithSignature(
|
||||
t,
|
||||
"watch",
|
||||
artifactDeployedEventJSON(),
|
||||
generateSignature("signature", []byte(artifactDeployedEventJSON())),
|
||||
http.StatusOK,
|
||||
)
|
||||
}
|
||||
|
||||
func TestCheckSignatureSuccess(t *testing.T) {
|
||||
if !checkSignature("my_little_secret", []byte("random-signature-body"), "sha1=3dca279e731c97c38e3019a075dee9ebbd0a99f0") {
|
||||
t.Errorf("check signature failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckSignatureFailed(t *testing.T) {
|
||||
if checkSignature("m_little_secret", []byte("random-signature-body"), "sha1=3dca279e731c97c38e3019a075dee9ebbd0a99f0") {
|
||||
t.Errorf("check signature failed")
|
||||
}
|
||||
}
|
22
plugins/inputs/webhooks/filestack/README.md
Normal file
22
plugins/inputs/webhooks/filestack/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Filestack webhook
|
||||
|
||||
You should configure your Filestack's Webhooks to point at the `webhooks`
|
||||
service. To do this go to [filestack.com](https://www.filestack.com/), select
|
||||
your app and click `Credentials > Webhooks`. In the resulting page, set the
|
||||
`URL` to `http://<my_ip>:1619/filestack`, and click on `Add`.
|
||||
|
||||
## Events
|
||||
|
||||
See the [webhook doc](https://www.filestack.com/docs/webhooks).
|
||||
|
||||
*Limitations*: It stores all events except video conversions events.
|
||||
|
||||
All events for logs the original timestamp, the action and the id.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'action' = `event.action` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'id' = `event.id` string
|
55
plugins/inputs/webhooks/filestack/filestack_webhooks.go
Normal file
55
plugins/inputs/webhooks/filestack/filestack_webhooks.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package filestack
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/auth"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
auth.BasicAuth
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (fs *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(fs.Path, fs.eventHandler).Methods("POST")
|
||||
|
||||
fs.log = log
|
||||
fs.log.Infof("Started the webhooks_filestack on %s", fs.Path)
|
||||
fs.acc = acc
|
||||
}
|
||||
|
||||
func (fs *Webhook) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if !fs.Verify(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
event := &filestackEvent{}
|
||||
err = json.Unmarshal(body, event)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fs.acc.AddFields("filestack_webhooks", event.fields(), event.tags(), time.Unix(event.TimeStamp, 0))
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package filestack
|
||||
|
||||
import "strconv"
|
||||
|
||||
type filestackEvent struct {
|
||||
Action string `json:"action"`
|
||||
TimeStamp int64 `json:"timestamp"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
func (fe *filestackEvent) tags() map[string]string {
|
||||
return map[string]string{
|
||||
"action": fe.Action,
|
||||
}
|
||||
}
|
||||
|
||||
func (fe *filestackEvent) fields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": strconv.Itoa(fe.ID),
|
||||
}
|
||||
}
|
86
plugins/inputs/webhooks/filestack/filestack_webhooks_test.go
Normal file
86
plugins/inputs/webhooks/filestack/filestack_webhooks_test.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package filestack
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func postWebhooks(t *testing.T, md *Webhook, eventBodyFile io.Reader) *httptest.ResponseRecorder {
|
||||
req, err := http.NewRequest("POST", "/filestack", eventBodyFile)
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
md.eventHandler(w, req)
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func TestDialogEvent(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
fs := &Webhook{Path: "/filestack", acc: &acc}
|
||||
resp := postWebhooks(t, fs, getFile(t, "testdata/dialog_open.json"))
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": "102",
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"action": "fp.dialog",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "filestack_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func TestParseError(t *testing.T) {
|
||||
fs := &Webhook{Path: "/filestack"}
|
||||
resp := postWebhooks(t, fs, strings.NewReader(""))
|
||||
if resp.Code != http.StatusBadRequest {
|
||||
t.Errorf("POST returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadEvent(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
fs := &Webhook{Path: "/filestack", acc: &acc}
|
||||
resp := postWebhooks(t, fs, getFile(t, "testdata/upload.json"))
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": "100946",
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"action": "fp.upload",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "filestack_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func TestVideoConversionEvent(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
fs := &Webhook{Path: "/filestack", acc: &acc}
|
||||
resp := postWebhooks(t, fs, getFile(t, "testdata/video_conversion.json"))
|
||||
if resp.Code != http.StatusBadRequest {
|
||||
t.Errorf("POST returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func getFile(t *testing.T, filePath string) io.Reader {
|
||||
file, err := os.Open(filePath)
|
||||
require.NoErrorf(t, err, "could not read from file %s", filePath)
|
||||
|
||||
return file
|
||||
}
|
39
plugins/inputs/webhooks/filestack/testdata/dialog_open.json
vendored
Normal file
39
plugins/inputs/webhooks/filestack/testdata/dialog_open.json
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"action": "fp.dialog",
|
||||
"timestamp": 1435584646,
|
||||
"id": 102,
|
||||
"text": {
|
||||
"mimetypes": [
|
||||
"*/*"
|
||||
],
|
||||
"iframe": false,
|
||||
"language": "en",
|
||||
"id": "1435584650723",
|
||||
"mobile": false,
|
||||
"app": {
|
||||
"upsell": "false",
|
||||
"apikey": "YOUR_API_KEY",
|
||||
"customization": {
|
||||
"saveas_subheader": "Save it down to your local device or onto the Cloud",
|
||||
"folder_subheader": "Choose a folder to share with this application",
|
||||
"open_subheader": "Choose from the files on your local device or the ones you have online",
|
||||
"folder_header": "Select a folder",
|
||||
"help_text": "",
|
||||
"saveas_header": "Save your file",
|
||||
"open_header": "Upload a file"
|
||||
}
|
||||
},
|
||||
"dialogType": "open",
|
||||
"auth": false,
|
||||
"welcome_header": "Upload a file",
|
||||
"welcome_subheader": "Choose from the files on your local device or the ones you have online",
|
||||
"help_text": "",
|
||||
"recent_path": "/",
|
||||
"extensions": null,
|
||||
"maxSize": 0,
|
||||
"signature": null,
|
||||
"policy": null,
|
||||
"custom_providers": "imgur,cloudapp",
|
||||
"intra": false
|
||||
}
|
||||
}
|
12
plugins/inputs/webhooks/filestack/testdata/upload.json
vendored
Normal file
12
plugins/inputs/webhooks/filestack/testdata/upload.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"action": "fp.upload",
|
||||
"timestamp": 1443444905,
|
||||
"id": 100946,
|
||||
"text": {
|
||||
"url": "https://www.filestackapi.com/api/file/WAunDTTqQfCNWwUUyf6n",
|
||||
"client": "Facebook",
|
||||
"type": "image/jpeg",
|
||||
"filename": "1579337399020824.jpg",
|
||||
"size": 139154
|
||||
}
|
||||
}
|
51
plugins/inputs/webhooks/filestack/testdata/video_conversion.json
vendored
Normal file
51
plugins/inputs/webhooks/filestack/testdata/video_conversion.json
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"status":"completed",
|
||||
"message":"Done",
|
||||
"data":{
|
||||
"thumb":"https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
|
||||
"thumb100x100":"https://process.filestackapi.com/AhTgLagciQByzXpFGRI0Az/resize=w:100,h:100,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
|
||||
"thumb200x200":"https://process.filestackapi.com/AhTgLagciQByzXpFGRI0Az/resize=w:200,h:200,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
|
||||
"thumb300x300":"https://process.filestackapi.com/AhTgLagciQByzXpFGRI0Az/resize=w:300,h:300,f:crop/output=f:jpg,q:66/https://cdn.filestackcontent.com/f1e8V88QDuxzOvtOAq1W",
|
||||
"url":"https://cdn.filestackcontent.com/VgvFVdvvTkml0WXPIoGn"
|
||||
},
|
||||
"metadata":{
|
||||
"result":{
|
||||
"audio_channels":2,
|
||||
"audio_codec":"vorbis",
|
||||
"audio_sample_rate":44100,
|
||||
"created_at":"2015/12/21 20:45:19 +0000",
|
||||
"duration":10587,
|
||||
"encoding_progress":100,
|
||||
"encoding_time":8,
|
||||
"extname":".webm",
|
||||
"file_size":293459,
|
||||
"fps":24,
|
||||
"height":260,
|
||||
"mime_type":"video/webm",
|
||||
"started_encoding_at":"2015/12/21 20:45:22 +0000",
|
||||
"updated_at":"2015/12/21 20:45:32 +0000",
|
||||
"video_bitrate":221,
|
||||
"video_codec":"vp8",
|
||||
"width":300
|
||||
},
|
||||
"source":{
|
||||
"audio_bitrate":125,
|
||||
"audio_channels":2,
|
||||
"audio_codec":"aac",
|
||||
"audio_sample_rate":44100,
|
||||
"created_at":"2015/12/21 20:45:19 +0000",
|
||||
"duration":10564,
|
||||
"extname":".mp4",
|
||||
"file_size":875797,
|
||||
"fps":24,
|
||||
"height":360,
|
||||
"mime_type":"video/mp4",
|
||||
"updated_at":"2015/12/21 20:45:32 +0000",
|
||||
"video_bitrate":196,
|
||||
"video_codec":"h264",
|
||||
"width":480
|
||||
}
|
||||
},
|
||||
"timestamp":"1453850583",
|
||||
"uuid":"638311d89d2bc849563a674a45809b7c"
|
||||
}
|
456
plugins/inputs/webhooks/github/README.md
Normal file
456
plugins/inputs/webhooks/github/README.md
Normal file
|
@ -0,0 +1,456 @@
|
|||
# github webhooks
|
||||
|
||||
You should configure your Organization's Webhooks to point at the `webhooks`
|
||||
service. To do this go to `github.com/{my_organization}` and click
|
||||
`Settings > Webhooks > Add webhook`. In the resulting menu set `Payload URL` to
|
||||
`http://<my_ip>:1619/github`, `Content type` to `application/json` and under
|
||||
the section `Which events would you like to trigger this webhook?` select
|
||||
'Send me **everything**'. By default all of the events will write to the
|
||||
`github_webhooks` measurement, this is configurable by setting the
|
||||
`measurement_name` in the config file.
|
||||
|
||||
You can also add a secret that will be used by telegraf to verify the
|
||||
authenticity of the requests.
|
||||
|
||||
## Metrics
|
||||
|
||||
The titles of the following sections are links to the full payloads and details
|
||||
for each event. The body contains what information from the event is persisted.
|
||||
The format is as follows:
|
||||
|
||||
```toml
|
||||
# TAGS
|
||||
* 'tagKey' = `tagValue` type
|
||||
# FIELDS
|
||||
* 'fieldKey' = `fieldValue` type
|
||||
```
|
||||
|
||||
The tag values and field values show the place on the incoming JSON object
|
||||
where the data is sourced from.
|
||||
|
||||
### [`commit_comment` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#commit_comment)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'commit' = `event.comment.commit_id` string
|
||||
* 'comment' = `event.comment.body` string
|
||||
|
||||
### [`create` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#create)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'ref' = `event.ref` string
|
||||
* 'refType' = `event.ref_type` string
|
||||
|
||||
### [`delete` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#delete)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'ref' = `event.ref` string
|
||||
* 'refType' = `event.ref_type` string
|
||||
|
||||
### [`deployment` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#deployment)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'commit' = `event.deployment.sha` string
|
||||
* 'task' = `event.deployment.task` string
|
||||
* 'environment' = `event.deployment.environment` string
|
||||
* 'description' = `event.deployment.description` string
|
||||
|
||||
### [`deployment_status` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#deployment_status)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'commit' = `event.deployment.sha` string
|
||||
* 'task' = `event.deployment.task` string
|
||||
* 'environment' = `event.deployment.environment` string
|
||||
* 'description' = `event.deployment.description` string
|
||||
* 'depState' = `event.deployment_status.state` string
|
||||
* 'depDescription' = `event.deployment_status.description` string
|
||||
|
||||
### [`fork` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#fork)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'forkee' = `event.forkee.repository` string
|
||||
|
||||
### [`gollum` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#gollum)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
|
||||
### [`issue_comment` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#issue_comment)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'issue' = `event.issue.number` int
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'title' = `event.issue.title` string
|
||||
* 'comments' = `event.issue.comments` int
|
||||
* 'body' = `event.comment.body` string
|
||||
|
||||
### [`issues` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#issues)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'issue' = `event.issue.number` int
|
||||
* 'action' = `event.action` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'title' = `event.issue.title` string
|
||||
* 'comments' = `event.issue.comments` int
|
||||
|
||||
### [`member` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#member)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'newMember' = `event.sender.login` string
|
||||
* 'newMemberStatus' = `event.sender.site_admin` bool
|
||||
|
||||
### [`membership` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#membership)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'action' = `event.action` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'newMember' = `event.sender.login` string
|
||||
* 'newMemberStatus' = `event.sender.site_admin` bool
|
||||
|
||||
### [`page_build` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#page_build)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
|
||||
### [`public` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#public)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
|
||||
### [`pull_request_review_comment` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request_review_comment)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'action' = `event.action` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'prNumber' = `event.pull_request.number` int
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'state' = `event.pull_request.state` string
|
||||
* 'title' = `event.pull_request.title` string
|
||||
* 'comments' = `event.pull_request.comments` int
|
||||
* 'commits' = `event.pull_request.commits` int
|
||||
* 'additions' = `event.pull_request.additions` int
|
||||
* 'deletions' = `event.pull_request.deletions` int
|
||||
* 'changedFiles' = `event.pull_request.changed_files` int
|
||||
* 'commentFile' = `event.comment.file` string
|
||||
* 'comment' = `event.comment.body` string
|
||||
|
||||
### [`pull_request` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'action' = `event.action` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'prNumber' = `event.pull_request.number` int
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'state' = `event.pull_request.state` string
|
||||
* 'title' = `event.pull_request.title` string
|
||||
* 'comments' = `event.pull_request.comments` int
|
||||
* 'commits' = `event.pull_request.commits` int
|
||||
* 'additions' = `event.pull_request.additions` int
|
||||
* 'deletions' = `event.pull_request.deletions` int
|
||||
* 'changedFiles' = `event.pull_request.changed_files` int
|
||||
|
||||
### [`push` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#push)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'ref' = `event.ref` string
|
||||
* 'before' = `event.before` string
|
||||
* 'after' = `event.after` string
|
||||
|
||||
### [`repository` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#repository)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
|
||||
### [`release` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#release)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'tagName' = `event.release.tag_name` string
|
||||
|
||||
### [`status` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#status)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'commit' = `event.sha` string
|
||||
* 'state' = `event.state` string
|
||||
|
||||
### [`team_add` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#team_add)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
* 'teamName' = `event.team.name` string
|
||||
|
||||
### [`watch` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#watch)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'stars' = `event.repository.stargazers_count` int
|
||||
* 'forks' = `event.repository.forks_count` int
|
||||
* 'issues' = `event.repository.open_issues_count` int
|
||||
|
||||
### [`workflow_job` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_job)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'action' = `event.action` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'name' = `event.workflow_job.name` string
|
||||
* 'conclusion' = `event.workflow_job.conclusion` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'run_attempt' = `event.workflow_job.run_attempt` int
|
||||
* 'queue_time' = `event.workflow_job.started_at - event.workflow_job.created_at at event.action = in_progress in milliseconds` int
|
||||
* 'run_time' = `event.workflow_job.completed_at - event.workflow_job.started_at at event.action = completed in milliseconds` int
|
||||
* 'head_branch' = `event.workflow_job.head_branch` string
|
||||
|
||||
### [`workflow_run` event](https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_run)
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `headers[X-Github-Event]` string
|
||||
* 'action' = `event.action` string
|
||||
* 'repository' = `event.repository.full_name` string
|
||||
* 'private' = `event.repository.private` bool
|
||||
* 'user' = `event.sender.login` string
|
||||
* 'admin' = `event.sender.site_admin` bool
|
||||
* 'name' = `event.workflow_run.name` string
|
||||
* 'conclusion' = `event.workflow_run.conclusion` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'run_attempt' = `event.workflow_run.run_attempt` int
|
||||
* 'run_time' = `event.workflow_run.completed_at - event.workflow_run.run_started_at at event.action = completed in milliseconds` int
|
||||
* 'head_branch' = `event.workflow_run.head_branch` string
|
150
plugins/inputs/webhooks/github/github_webhooks.go
Normal file
150
plugins/inputs/webhooks/github/github_webhooks.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1" //nolint:gosec // G505: Blocklisted import crypto/sha1: weak cryptographic primitive - sha1 hash is what is desired in this case
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/auth"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
secret string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
auth.BasicAuth
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (gh *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(gh.Path, gh.eventHandler).Methods("POST")
|
||||
|
||||
gh.log = log
|
||||
gh.log.Infof("Started the webhooks_github on %s", gh.Path)
|
||||
gh.acc = acc
|
||||
}
|
||||
|
||||
func (gh *Webhook) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if !gh.Verify(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
eventType := r.Header.Get("X-Github-Event")
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if gh.secret != "" && !checkSignature(gh.secret, data, r.Header.Get("X-Hub-Signature")) {
|
||||
gh.log.Error("Fail to check the github webhook signature")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
e, err := gh.newEvent(data, eventType)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if e != nil {
|
||||
p := e.newMetric()
|
||||
gh.acc.AddFields("github_webhooks", p.Fields(), p.Tags(), p.Time())
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func generateEvent(data []byte, event event) (event, error) {
|
||||
err := json.Unmarshal(data, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
||||
type newEventError struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (e *newEventError) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
func (gh *Webhook) newEvent(data []byte, name string) (event, error) {
|
||||
gh.log.Debugf("New %v event received", name)
|
||||
switch name {
|
||||
case "commit_comment":
|
||||
return generateEvent(data, &commitCommentEvent{})
|
||||
case "create":
|
||||
return generateEvent(data, &createEvent{})
|
||||
case "delete":
|
||||
return generateEvent(data, &deleteEvent{})
|
||||
case "deployment":
|
||||
return generateEvent(data, &deploymentEvent{})
|
||||
case "deployment_status":
|
||||
return generateEvent(data, &deploymentStatusEvent{})
|
||||
case "fork":
|
||||
return generateEvent(data, &forkEvent{})
|
||||
case "gollum":
|
||||
return generateEvent(data, &gollumEvent{})
|
||||
case "issue_comment":
|
||||
return generateEvent(data, &issueCommentEvent{})
|
||||
case "issues":
|
||||
return generateEvent(data, &issuesEvent{})
|
||||
case "member":
|
||||
return generateEvent(data, &memberEvent{})
|
||||
case "membership":
|
||||
return generateEvent(data, &membershipEvent{})
|
||||
case "page_build":
|
||||
return generateEvent(data, &pageBuildEvent{})
|
||||
case "ping":
|
||||
return nil, nil
|
||||
case "public":
|
||||
return generateEvent(data, &publicEvent{})
|
||||
case "pull_request":
|
||||
return generateEvent(data, &pullRequestEvent{})
|
||||
case "pull_request_review_comment":
|
||||
return generateEvent(data, &pullRequestReviewCommentEvent{})
|
||||
case "push":
|
||||
return generateEvent(data, &pushEvent{})
|
||||
case "release":
|
||||
return generateEvent(data, &releaseEvent{})
|
||||
case "repository":
|
||||
return generateEvent(data, &repositoryEvent{})
|
||||
case "status":
|
||||
return generateEvent(data, &statusEvent{})
|
||||
case "team_add":
|
||||
return generateEvent(data, &teamAddEvent{})
|
||||
case "watch":
|
||||
return generateEvent(data, &watchEvent{})
|
||||
case "workflow_job":
|
||||
return generateEvent(data, &workflowJobEvent{})
|
||||
case "workflow_run":
|
||||
return generateEvent(data, &workflowRunEvent{})
|
||||
}
|
||||
return nil, &newEventError{"Not a recognized event type"}
|
||||
}
|
||||
|
||||
func checkSignature(secret string, data []byte, signature string) bool {
|
||||
return hmac.Equal([]byte(signature), []byte(generateSignature(secret, data)))
|
||||
}
|
||||
|
||||
func generateSignature(secret string, data []byte) string {
|
||||
mac := hmac.New(sha1.New, []byte(secret))
|
||||
if _, err := mac.Write(data); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
result := mac.Sum(nil)
|
||||
return "sha1=" + hex.EncodeToString(result)
|
||||
}
|
3572
plugins/inputs/webhooks/github/github_webhooks_mock_json_test.go
Normal file
3572
plugins/inputs/webhooks/github/github_webhooks_mock_json_test.go
Normal file
File diff suppressed because it is too large
Load diff
739
plugins/inputs/webhooks/github/github_webhooks_models.go
Normal file
739
plugins/inputs/webhooks/github/github_webhooks_models.go
Normal file
|
@ -0,0 +1,739 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
)
|
||||
|
||||
const meas = "github_webhooks"
|
||||
|
||||
type event interface {
|
||||
newMetric() telegraf.Metric
|
||||
}
|
||||
|
||||
type repository struct {
|
||||
Repository string `json:"full_name"`
|
||||
Private bool `json:"private"`
|
||||
Stars int `json:"stargazers_count"`
|
||||
Forks int `json:"forks_count"`
|
||||
Issues int `json:"open_issues_count"`
|
||||
}
|
||||
|
||||
type sender struct {
|
||||
User string `json:"login"`
|
||||
Admin bool `json:"site_admin"`
|
||||
}
|
||||
|
||||
type commitComment struct {
|
||||
Commit string `json:"commit_id"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type deployment struct {
|
||||
Commit string `json:"sha"`
|
||||
Task string `json:"task"`
|
||||
Environment string `json:"environment"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type page struct {
|
||||
Name string `json:"page_name"`
|
||||
Title string `json:"title"`
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
type issue struct {
|
||||
Number int `json:"number"`
|
||||
Title string `json:"title"`
|
||||
Comments int `json:"comments"`
|
||||
}
|
||||
|
||||
type issueComment struct {
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type team struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type pullRequest struct {
|
||||
Number int `json:"number"`
|
||||
State string `json:"state"`
|
||||
Title string `json:"title"`
|
||||
Comments int `json:"comments"`
|
||||
Commits int `json:"commits"`
|
||||
Additions int `json:"additions"`
|
||||
Deletions int `json:"deletions"`
|
||||
ChangedFiles int `json:"changed_files"`
|
||||
}
|
||||
|
||||
type pullRequestReviewComment struct {
|
||||
File string `json:"path"`
|
||||
Comment string `json:"body"`
|
||||
}
|
||||
|
||||
type workflowJob struct {
|
||||
RunAttempt int `json:"run_attempt"`
|
||||
HeadBranch string `json:"head_branch"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
Name string `json:"name"`
|
||||
Conclusion string `json:"conclusion"`
|
||||
}
|
||||
|
||||
type workflowRun struct {
|
||||
HeadBranch string `json:"head_branch"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
RunStartedAt time.Time `json:"run_started_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
RunAttempt int `json:"run_attempt"`
|
||||
Name string `json:"name"`
|
||||
Conclusion string `json:"conclusion"`
|
||||
}
|
||||
|
||||
type release struct {
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
type deploymentStatus struct {
|
||||
State string `json:"state"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type commitCommentEvent struct {
|
||||
Comment commitComment `json:"comment"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s commitCommentEvent) newMetric() telegraf.Metric {
|
||||
event := "commit_comment"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"commit": s.Comment.Commit,
|
||||
"comment": s.Comment.Body,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type createEvent struct {
|
||||
Ref string `json:"ref"`
|
||||
RefType string `json:"ref_type"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s createEvent) newMetric() telegraf.Metric {
|
||||
event := "create"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"ref": s.Ref,
|
||||
"refType": s.RefType,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type deleteEvent struct {
|
||||
Ref string `json:"ref"`
|
||||
RefType string `json:"ref_type"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s deleteEvent) newMetric() telegraf.Metric {
|
||||
event := "delete"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"ref": s.Ref,
|
||||
"refType": s.RefType,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type deploymentEvent struct {
|
||||
Deployment deployment `json:"deployment"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s deploymentEvent) newMetric() telegraf.Metric {
|
||||
event := "deployment"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"commit": s.Deployment.Commit,
|
||||
"task": s.Deployment.Task,
|
||||
"environment": s.Deployment.Environment,
|
||||
"description": s.Deployment.Description,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type deploymentStatusEvent struct {
|
||||
Deployment deployment `json:"deployment"`
|
||||
DeploymentStatus deploymentStatus `json:"deployment_status"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s deploymentStatusEvent) newMetric() telegraf.Metric {
|
||||
event := "delete"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"commit": s.Deployment.Commit,
|
||||
"task": s.Deployment.Task,
|
||||
"environment": s.Deployment.Environment,
|
||||
"description": s.Deployment.Description,
|
||||
"depState": s.DeploymentStatus.State,
|
||||
"depDescription": s.DeploymentStatus.Description,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type forkEvent struct {
|
||||
Forkee repository `json:"forkee"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s forkEvent) newMetric() telegraf.Metric {
|
||||
event := "fork"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"fork": s.Forkee.Repository,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type gollumEvent struct {
|
||||
Pages []page `json:"pages"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
// REVIEW: Going to be lazy and not deal with the pages.
|
||||
func (s gollumEvent) newMetric() telegraf.Metric {
|
||||
event := "gollum"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type issueCommentEvent struct {
|
||||
Issue issue `json:"issue"`
|
||||
Comment issueComment `json:"comment"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s issueCommentEvent) newMetric() telegraf.Metric {
|
||||
event := "issue_comment"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"issue": strconv.Itoa(s.Issue.Number),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"title": s.Issue.Title,
|
||||
"comments": s.Issue.Comments,
|
||||
"body": s.Comment.Body,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type issuesEvent struct {
|
||||
Action string `json:"action"`
|
||||
Issue issue `json:"issue"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s issuesEvent) newMetric() telegraf.Metric {
|
||||
event := "issue"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"issue": strconv.Itoa(s.Issue.Number),
|
||||
"action": s.Action,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"title": s.Issue.Title,
|
||||
"comments": s.Issue.Comments,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type memberEvent struct {
|
||||
Member sender `json:"member"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s memberEvent) newMetric() telegraf.Metric {
|
||||
event := "member"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"newMember": s.Member.User,
|
||||
"newMemberStatus": s.Member.Admin,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type membershipEvent struct {
|
||||
Action string `json:"action"`
|
||||
Member sender `json:"member"`
|
||||
Sender sender `json:"sender"`
|
||||
Team team `json:"team"`
|
||||
}
|
||||
|
||||
func (s membershipEvent) newMetric() telegraf.Metric {
|
||||
event := "membership"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"action": s.Action,
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"newMember": s.Member.User,
|
||||
"newMemberStatus": s.Member.Admin,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type pageBuildEvent struct {
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s pageBuildEvent) newMetric() telegraf.Metric {
|
||||
event := "page_build"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type publicEvent struct {
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s publicEvent) newMetric() telegraf.Metric {
|
||||
event := "public"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type pullRequestEvent struct {
|
||||
Action string `json:"action"`
|
||||
PullRequest pullRequest `json:"pull_request"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s pullRequestEvent) newMetric() telegraf.Metric {
|
||||
event := "pull_request"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"action": s.Action,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"prNumber": strconv.Itoa(s.PullRequest.Number),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"state": s.PullRequest.State,
|
||||
"title": s.PullRequest.Title,
|
||||
"comments": s.PullRequest.Comments,
|
||||
"commits": s.PullRequest.Commits,
|
||||
"additions": s.PullRequest.Additions,
|
||||
"deletions": s.PullRequest.Deletions,
|
||||
"changedFiles": s.PullRequest.ChangedFiles,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type pullRequestReviewCommentEvent struct {
|
||||
Comment pullRequestReviewComment `json:"comment"`
|
||||
PullRequest pullRequest `json:"pull_request"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s pullRequestReviewCommentEvent) newMetric() telegraf.Metric {
|
||||
event := "pull_request_review_comment"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"prNumber": strconv.Itoa(s.PullRequest.Number),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"state": s.PullRequest.State,
|
||||
"title": s.PullRequest.Title,
|
||||
"comments": s.PullRequest.Comments,
|
||||
"commits": s.PullRequest.Commits,
|
||||
"additions": s.PullRequest.Additions,
|
||||
"deletions": s.PullRequest.Deletions,
|
||||
"changedFiles": s.PullRequest.ChangedFiles,
|
||||
"commentFile": s.Comment.File,
|
||||
"comment": s.Comment.Comment,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type pushEvent struct {
|
||||
Ref string `json:"ref"`
|
||||
Before string `json:"before"`
|
||||
After string `json:"after"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s pushEvent) newMetric() telegraf.Metric {
|
||||
event := "push"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"ref": s.Ref,
|
||||
"before": s.Before,
|
||||
"after": s.After,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type releaseEvent struct {
|
||||
Release release `json:"release"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s releaseEvent) newMetric() telegraf.Metric {
|
||||
event := "release"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"tagName": s.Release.TagName,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type repositoryEvent struct {
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s repositoryEvent) newMetric() telegraf.Metric {
|
||||
event := "repository"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type statusEvent struct {
|
||||
Commit string `json:"sha"`
|
||||
State string `json:"state"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s statusEvent) newMetric() telegraf.Metric {
|
||||
event := "status"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"commit": s.Commit,
|
||||
"state": s.State,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type teamAddEvent struct {
|
||||
Team team `json:"team"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s teamAddEvent) newMetric() telegraf.Metric {
|
||||
event := "team_add"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
"teamName": s.Team.Name,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type watchEvent struct {
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s watchEvent) newMetric() telegraf.Metric {
|
||||
event := "delete"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"stars": s.Repository.Stars,
|
||||
"forks": s.Repository.Forks,
|
||||
"issues": s.Repository.Issues,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type workflowJobEvent struct {
|
||||
Action string `json:"action"`
|
||||
WorkflowJob workflowJob `json:"workflow_job"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s workflowJobEvent) newMetric() telegraf.Metric {
|
||||
event := "workflow_job"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"action": s.Action,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"name": s.WorkflowJob.Name,
|
||||
"conclusion": s.WorkflowJob.Conclusion,
|
||||
}
|
||||
|
||||
var runTimeMs int64
|
||||
var queueTimeMs int64
|
||||
if s.Action == "in_progress" {
|
||||
queueTimeMs = s.WorkflowJob.StartedAt.Sub(s.WorkflowJob.CreatedAt).Milliseconds()
|
||||
}
|
||||
if s.Action == "completed" {
|
||||
runTimeMs = s.WorkflowJob.CompletedAt.Sub(s.WorkflowJob.StartedAt).Milliseconds()
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"run_attempt": s.WorkflowJob.RunAttempt,
|
||||
"queue_time": queueTimeMs,
|
||||
"run_time": runTimeMs,
|
||||
"head_branch": s.WorkflowJob.HeadBranch,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
||||
|
||||
type workflowRunEvent struct {
|
||||
Action string `json:"action"`
|
||||
WorkflowRun workflowRun `json:"workflow_run"`
|
||||
Repository repository `json:"repository"`
|
||||
Sender sender `json:"sender"`
|
||||
}
|
||||
|
||||
func (s workflowRunEvent) newMetric() telegraf.Metric {
|
||||
event := "workflow_run"
|
||||
t := map[string]string{
|
||||
"event": event,
|
||||
"action": s.Action,
|
||||
"repository": s.Repository.Repository,
|
||||
"private": strconv.FormatBool(s.Repository.Private),
|
||||
"user": s.Sender.User,
|
||||
"admin": strconv.FormatBool(s.Sender.Admin),
|
||||
"name": s.WorkflowRun.Name,
|
||||
"conclusion": s.WorkflowRun.Conclusion,
|
||||
}
|
||||
var runTimeMs int64
|
||||
|
||||
if s.Action == "completed" {
|
||||
runTimeMs = s.WorkflowRun.UpdatedAt.Sub(s.WorkflowRun.RunStartedAt).Milliseconds()
|
||||
}
|
||||
f := map[string]interface{}{
|
||||
"run_attempt": s.WorkflowRun.RunAttempt,
|
||||
"run_time": runTimeMs,
|
||||
"head_branch": s.WorkflowRun.HeadBranch,
|
||||
}
|
||||
m := metric.New(meas, t, f, time.Now())
|
||||
return m
|
||||
}
|
147
plugins/inputs/webhooks/github/github_webhooks_test.go
Normal file
147
plugins/inputs/webhooks/github/github_webhooks_test.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
package github
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func githubWebhookRequest(t *testing.T, event, jsonString string) {
|
||||
var acc testutil.Accumulator
|
||||
gh := &Webhook{Path: "/github", acc: &acc, log: testutil.Logger{}}
|
||||
req, err := http.NewRequest("POST", "/github", strings.NewReader(jsonString))
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("X-Github-Event", event)
|
||||
w := httptest.NewRecorder()
|
||||
gh.eventHandler(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("POST "+event+" returned HTTP status code %v.\nExpected %v", w.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func githubWebhookRequestWithSignature(t *testing.T, event, jsonString, signature string, expectedStatus int) {
|
||||
var acc testutil.Accumulator
|
||||
gh := &Webhook{Path: "/github", secret: "signature", acc: &acc, log: testutil.Logger{}}
|
||||
req, err := http.NewRequest("POST", "/github", strings.NewReader(jsonString))
|
||||
require.NoError(t, err)
|
||||
req.Header.Add("X-Github-Event", event)
|
||||
req.Header.Add("X-Hub-Signature", signature)
|
||||
w := httptest.NewRecorder()
|
||||
gh.eventHandler(w, req)
|
||||
if w.Code != expectedStatus {
|
||||
t.Errorf("POST "+event+" returned HTTP status code %v.\nExpected %v", w.Code, expectedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitCommentEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "commit_comment", commitCommentEventJSON())
|
||||
}
|
||||
|
||||
func TestPingEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "ping", "")
|
||||
}
|
||||
|
||||
func TestDeleteEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "delete", deleteEventJSON())
|
||||
}
|
||||
|
||||
func TestDeploymentEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "deployment", deploymentEventJSON())
|
||||
}
|
||||
|
||||
func TestDeploymentStatusEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "deployment_status", deploymentStatusEventJSON())
|
||||
}
|
||||
|
||||
func TestForkEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "fork", forkEventJSON())
|
||||
}
|
||||
|
||||
func TestGollumEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "gollum", gollumEventJSON())
|
||||
}
|
||||
|
||||
func TestIssueCommentEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "issue_comment", issueCommentEventJSON())
|
||||
}
|
||||
|
||||
func TestIssuesEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "issues", issuesEventJSON())
|
||||
}
|
||||
|
||||
func TestMemberEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "member", memberEventJSON())
|
||||
}
|
||||
|
||||
func TestMembershipEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "membership", membershipEventJSON())
|
||||
}
|
||||
|
||||
func TestPageBuildEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "page_build", pageBuildEventJSON())
|
||||
}
|
||||
|
||||
func TestPublicEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "public", publicEventJSON())
|
||||
}
|
||||
|
||||
func TestPullRequestReviewCommentEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "pull_request_review_comment", pullRequestReviewCommentEventJSON())
|
||||
}
|
||||
|
||||
func TestPushEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "push", pushEventJSON())
|
||||
}
|
||||
|
||||
func TestReleaseEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "release", releaseEventJSON())
|
||||
}
|
||||
|
||||
func TestRepositoryEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "repository", repositoryEventJSON())
|
||||
}
|
||||
|
||||
func TestStatusEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "status", statusEventJSON())
|
||||
}
|
||||
|
||||
func TestTeamAddEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "team_add", teamAddEventJSON())
|
||||
}
|
||||
|
||||
func TestWatchEvent(t *testing.T) {
|
||||
githubWebhookRequest(t, "watch", watchEventJSON())
|
||||
}
|
||||
|
||||
func TestEventWithSignatureFail(t *testing.T) {
|
||||
githubWebhookRequestWithSignature(t, "watch", watchEventJSON(), "signature", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func TestEventWithSignatureSuccess(t *testing.T) {
|
||||
githubWebhookRequestWithSignature(t, "watch", watchEventJSON(), generateSignature("signature", []byte(watchEventJSON())), http.StatusOK)
|
||||
}
|
||||
|
||||
func TestWorkflowJob(t *testing.T) {
|
||||
githubWebhookRequest(t, "workflow_job", WorkflowJobJSON())
|
||||
}
|
||||
|
||||
func TestWorkflowRun(t *testing.T) {
|
||||
githubWebhookRequest(t, "workflow_run", WorkflowRunJSON())
|
||||
}
|
||||
|
||||
func TestCheckSignatureSuccess(t *testing.T) {
|
||||
if !checkSignature("my_little_secret", []byte("random-signature-body"), "sha1=3dca279e731c97c38e3019a075dee9ebbd0a99f0") {
|
||||
t.Errorf("check signature failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckSignatureFailed(t *testing.T) {
|
||||
if checkSignature("m_little_secret", []byte("random-signature-body"), "sha1=3dca279e731c97c38e3019a075dee9ebbd0a99f0") {
|
||||
t.Errorf("check signature failed")
|
||||
}
|
||||
}
|
22
plugins/inputs/webhooks/mandrill/README.md
Normal file
22
plugins/inputs/webhooks/mandrill/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# mandrill webhook
|
||||
|
||||
You should configure your Mandrill's Webhooks to point at the `webhooks`
|
||||
service. To do this go to [mandrillapp.com](https://mandrillapp.com) and click
|
||||
`Settings > Webhooks`. In the resulting page, click on `Add a Webhook`, select
|
||||
all events, and set the `URL` to `http://<my_ip>:1619/mandrill`, and click on
|
||||
`Create Webhook`.
|
||||
|
||||
## Events
|
||||
|
||||
See the [webhook doc](https://mandrill.zendesk.com/hc/en-us/articles/205583307-Message-Event-Webhook-format).
|
||||
|
||||
All events for logs the original timestamp, the event name and the unique
|
||||
identifier of the message that generated the event.
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `event.event` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'id' = `event._id` string
|
67
plugins/inputs/webhooks/mandrill/mandrill_webhooks.go
Normal file
67
plugins/inputs/webhooks/mandrill/mandrill_webhooks.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package mandrill
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/auth"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
auth.BasicAuth
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (md *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(md.Path, returnOK).Methods("HEAD")
|
||||
router.HandleFunc(md.Path, md.eventHandler).Methods("POST")
|
||||
|
||||
md.log = log
|
||||
md.log.Infof("Started the webhooks_mandrill on %s", md.Path)
|
||||
md.acc = acc
|
||||
}
|
||||
|
||||
func returnOK(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (md *Webhook) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if !md.Verify(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
data, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var events []mandrillEvent
|
||||
err = json.Unmarshal([]byte(data.Get("mandrill_events")), &events)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
md.acc.AddFields("mandrill_webhooks", event.fields(), event.tags(), time.Unix(event.TimeStamp, 0))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
19
plugins/inputs/webhooks/mandrill/mandrill_webhooks_events.go
Normal file
19
plugins/inputs/webhooks/mandrill/mandrill_webhooks_events.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package mandrill
|
||||
|
||||
type mandrillEvent struct {
|
||||
EventName string `json:"event"`
|
||||
TimeStamp int64 `json:"ts"`
|
||||
ID string `json:"_id"`
|
||||
}
|
||||
|
||||
func (me *mandrillEvent) tags() map[string]string {
|
||||
return map[string]string{
|
||||
"event": me.EventName,
|
||||
}
|
||||
}
|
||||
|
||||
func (me *mandrillEvent) fields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": me.ID,
|
||||
}
|
||||
}
|
97
plugins/inputs/webhooks/mandrill/mandrill_webhooks_test.go
Normal file
97
plugins/inputs/webhooks/mandrill/mandrill_webhooks_test.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package mandrill
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func postWebhooks(t *testing.T, md *Webhook, eventBody string) *httptest.ResponseRecorder {
|
||||
body := url.Values{}
|
||||
body.Set("mandrill_events", eventBody)
|
||||
req, err := http.NewRequest("POST", "/mandrill", strings.NewReader(body.Encode()))
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
md.eventHandler(w, req)
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func headRequest(t *testing.T) *httptest.ResponseRecorder {
|
||||
req, err := http.NewRequest("HEAD", "/mandrill", strings.NewReader(""))
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
returnOK(w, req)
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func TestHead(t *testing.T) {
|
||||
resp := headRequest(t)
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("HEAD returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendEvent(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
md := &Webhook{Path: "/mandrill", acc: &acc}
|
||||
resp := postWebhooks(t, md, "["+readFile(t, "testdata/send_event.json")+"]")
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST send returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": "id1",
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"event": "send",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "mandrill_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func TestMultipleEvents(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
md := &Webhook{Path: "/mandrill", acc: &acc}
|
||||
resp := postWebhooks(t, md, "["+readFile(t, "testdata/send_event.json")+","+readFile(t, "testdata/hard_bounce_event.json")+"]")
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST send returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": "id1",
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"event": "send",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "mandrill_webhooks", fields, tags)
|
||||
|
||||
fields = map[string]interface{}{
|
||||
"id": "id2",
|
||||
}
|
||||
|
||||
tags = map[string]string{
|
||||
"event": "hard_bounce",
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "mandrill_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func readFile(t *testing.T, filePath string) string {
|
||||
data, err := os.ReadFile(filePath)
|
||||
require.NoErrorf(t, err, "could not read from file %s", filePath)
|
||||
|
||||
return string(data)
|
||||
}
|
23
plugins/inputs/webhooks/mandrill/testdata/hard_bounce_event.json
vendored
Normal file
23
plugins/inputs/webhooks/mandrill/testdata/hard_bounce_event.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"event": "hard_bounce",
|
||||
"msg": {
|
||||
"ts": 1365109999,
|
||||
"subject": "This an example webhook message",
|
||||
"email": "example.webhook@mandrillapp.com",
|
||||
"sender": "example.sender@mandrillapp.com",
|
||||
"tags": [
|
||||
"webhook-example"
|
||||
],
|
||||
"state": "bounced",
|
||||
"metadata": {
|
||||
"user_id": 111
|
||||
},
|
||||
"_id": "exampleaaaaaaaaaaaaaaaaaaaaaaaaa2",
|
||||
"_version": "exampleaaaaaaaaaaaaaaa",
|
||||
"bounce_description": "bad_mailbox",
|
||||
"bgtools_code": 10,
|
||||
"diag": "smtp;550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces."
|
||||
},
|
||||
"_id": "id2",
|
||||
"ts": 1384954004
|
||||
}
|
26
plugins/inputs/webhooks/mandrill/testdata/send_event.json
vendored
Normal file
26
plugins/inputs/webhooks/mandrill/testdata/send_event.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"event": "send",
|
||||
"msg": {
|
||||
"ts": 1365109999,
|
||||
"subject": "This an example webhook message",
|
||||
"email": "example.webhook@mandrillapp.com",
|
||||
"sender": "example.sender@mandrillapp.com",
|
||||
"tags": [
|
||||
"webhook-example"
|
||||
],
|
||||
"opens": [
|
||||
|
||||
],
|
||||
"clicks": [
|
||||
|
||||
],
|
||||
"state": "sent",
|
||||
"metadata": {
|
||||
"user_id": 111
|
||||
},
|
||||
"_id": "exampleaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"_version": "exampleaaaaaaaaaaaaaaa"
|
||||
},
|
||||
"_id": "id1",
|
||||
"ts": 1384954004
|
||||
}
|
49
plugins/inputs/webhooks/papertrail/README.md
Normal file
49
plugins/inputs/webhooks/papertrail/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# papertrail webhooks
|
||||
|
||||
Enables Telegraf to act as a [Papertrail Webhook](http://help.papertrailapp.com/kb/how-it-works/web-hooks/).
|
||||
|
||||
## Events
|
||||
|
||||
[Full documentation](http://help.papertrailapp.com/kb/how-it-works/web-hooks/#callback).
|
||||
|
||||
Events from Papertrail come in two forms:
|
||||
|
||||
* The [event-based callback](http://help.papertrailapp.com/kb/how-it-works/web-hooks/#callback):
|
||||
|
||||
* A point is created per event, with the timestamp as `received_at`
|
||||
* Each point has a field counter (`count`), which is set to `1` (signifying
|
||||
the event occurred)
|
||||
* Each event "hostname" object is converted to a `host` tag
|
||||
* The "saved_search" name in the payload is added as an `event` tag
|
||||
* The "saved_search" id in the payload is added as a `search_id` field
|
||||
* The papertrail url to view the event is built and added as a `url` field
|
||||
* The rest of the event data is converted directly to fields on the point:
|
||||
* `id`
|
||||
* `source_ip`
|
||||
* `source_name`
|
||||
* `source_id`
|
||||
* `program`
|
||||
* `severity`
|
||||
* `facility`
|
||||
* `message`
|
||||
|
||||
When a callback is received, an event-based point will look similar to:
|
||||
|
||||
```shell
|
||||
papertrail,host=myserver.example.com,event=saved_search_name count=1i,source_name="abc",program="CROND",severity="Info",source_id=2i,message="message body",source_ip="208.75.57.121",id=7711561783320576i,facility="Cron",url="https://papertrailapp.com/searches/42?centered_on_id=7711561783320576",search_id=42i 1453248892000000000
|
||||
```
|
||||
|
||||
* The [count-based callback](http://help.papertrailapp.com/kb/how-it-works/web-hooks/#count-only-webhooks)
|
||||
|
||||
* A point is created per timeseries object per count, with the timestamp as
|
||||
the "timeseries" key (the unix epoch of the event)
|
||||
* Each point has a field counter (`count`), which is set to the value of each
|
||||
"timeseries" object
|
||||
* Each count "source_name" object is converted to a `host` tag
|
||||
* The "saved_search" name in the payload is added as an `event` tag
|
||||
|
||||
When a callback is received, a count-based point will look similar to:
|
||||
|
||||
```shell
|
||||
papertrail,host=myserver.example.com,event=saved_search_name count=3i 1453248892000000000
|
||||
```
|
207
plugins/inputs/webhooks/papertrail/papertrail_test.go
Normal file
207
plugins/inputs/webhooks/papertrail/papertrail_test.go
Normal file
|
@ -0,0 +1,207 @@
|
|||
package papertrail
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
func post(t *testing.T, pt *Webhook, contentType, body string) *httptest.ResponseRecorder {
|
||||
req, err := http.NewRequest("POST", "/", strings.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
w := httptest.NewRecorder()
|
||||
pt.eventHandler(w, req)
|
||||
return w
|
||||
}
|
||||
|
||||
func TestWrongContentType(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
pt := &Webhook{Path: "/papertrail", acc: &acc}
|
||||
form := url.Values{}
|
||||
form.Set("payload", sampleEventPayload)
|
||||
data := form.Encode()
|
||||
|
||||
resp := post(t, pt, "", data)
|
||||
require.Equal(t, http.StatusUnsupportedMediaType, resp.Code)
|
||||
}
|
||||
|
||||
func TestMissingPayload(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
pt := &Webhook{Path: "/papertrail", acc: &acc}
|
||||
|
||||
resp := post(t, pt, contentType, "")
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
}
|
||||
|
||||
func TestPayloadNotJSON(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
pt := &Webhook{Path: "/papertrail", acc: &acc}
|
||||
|
||||
resp := post(t, pt, contentType, "payload={asdf]")
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
}
|
||||
|
||||
func TestPayloadInvalidJSON(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
pt := &Webhook{Path: "/papertrail", acc: &acc}
|
||||
|
||||
resp := post(t, pt, contentType, `payload={"value": 42}`)
|
||||
require.Equal(t, http.StatusBadRequest, resp.Code)
|
||||
}
|
||||
|
||||
func TestEventPayload(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
pt := &Webhook{Path: "/papertrail", acc: &acc}
|
||||
|
||||
form := url.Values{}
|
||||
form.Set("payload", sampleEventPayload)
|
||||
resp := post(t, pt, contentType, form.Encode())
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
fields1 := map[string]interface{}{
|
||||
"count": uint64(1),
|
||||
"id": int64(7711561783320576),
|
||||
"source_ip": "208.75.57.121",
|
||||
"source_name": "abc",
|
||||
"source_id": int64(2),
|
||||
"program": "CROND",
|
||||
"severity": "Info",
|
||||
"facility": "Cron",
|
||||
"message": "message body",
|
||||
"url": "https://papertrailapp.com/searches/42?centered_on_id=7711561783320576",
|
||||
"search_id": int64(42),
|
||||
}
|
||||
|
||||
fields2 := map[string]interface{}{
|
||||
"count": uint64(1),
|
||||
"id": int64(7711562567655424),
|
||||
"source_ip": "208.75.57.120",
|
||||
"source_name": "server1",
|
||||
"source_id": int64(19),
|
||||
"program": "CROND",
|
||||
"severity": "Info",
|
||||
"facility": "Cron",
|
||||
"message": "A short event",
|
||||
"url": "https://papertrailapp.com/searches/42?centered_on_id=7711562567655424",
|
||||
"search_id": int64(42),
|
||||
}
|
||||
|
||||
tags1 := map[string]string{
|
||||
"event": "Important stuff",
|
||||
"host": "abc",
|
||||
}
|
||||
tags2 := map[string]string{
|
||||
"event": "Important stuff",
|
||||
"host": "def",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "papertrail", fields1, tags1)
|
||||
acc.AssertContainsTaggedFields(t, "papertrail", fields2, tags2)
|
||||
}
|
||||
|
||||
func TestCountPayload(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
pt := &Webhook{Path: "/papertrail", acc: &acc}
|
||||
form := url.Values{}
|
||||
form.Set("payload", sampleCountPayload)
|
||||
resp := post(t, pt, contentType, form.Encode())
|
||||
require.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
fields1 := map[string]interface{}{
|
||||
"count": uint64(5),
|
||||
}
|
||||
fields2 := map[string]interface{}{
|
||||
"count": uint64(3),
|
||||
}
|
||||
|
||||
tags1 := map[string]string{
|
||||
"event": "Important stuff",
|
||||
"host": "arthur",
|
||||
}
|
||||
tags2 := map[string]string{
|
||||
"event": "Important stuff",
|
||||
"host": "ford",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "papertrail", fields1, tags1)
|
||||
acc.AssertContainsTaggedFields(t, "papertrail", fields2, tags2)
|
||||
}
|
||||
|
||||
const sampleEventPayload = `{
|
||||
"events": [
|
||||
{
|
||||
"id": 7711561783320576,
|
||||
"received_at": "2011-05-18T20:30:02-07:00",
|
||||
"display_received_at": "May 18 20:30:02",
|
||||
"source_ip": "208.75.57.121",
|
||||
"source_name": "abc",
|
||||
"source_id": 2,
|
||||
"hostname": "abc",
|
||||
"program": "CROND",
|
||||
"severity": "Info",
|
||||
"facility": "Cron",
|
||||
"message": "message body"
|
||||
},
|
||||
{
|
||||
"id": 7711562567655424,
|
||||
"received_at": "2011-05-18T20:30:02-07:00",
|
||||
"display_received_at": "May 18 20:30:02",
|
||||
"source_ip": "208.75.57.120",
|
||||
"source_name": "server1",
|
||||
"source_id": 19,
|
||||
"hostname": "def",
|
||||
"program": "CROND",
|
||||
"severity": "Info",
|
||||
"facility": "Cron",
|
||||
"message": "A short event"
|
||||
}
|
||||
],
|
||||
"saved_search": {
|
||||
"id": 42,
|
||||
"name": "Important stuff",
|
||||
"query": "cron OR server1",
|
||||
"html_edit_url": "https://papertrailapp.com/searches/42/edit",
|
||||
"html_search_url": "https://papertrailapp.com/searches/42"
|
||||
},
|
||||
"max_id": "7711582041804800",
|
||||
"min_id": "7711561783320576"
|
||||
}`
|
||||
|
||||
const sampleCountPayload = `{
|
||||
"counts": [
|
||||
{
|
||||
"source_name": "arthur",
|
||||
"source_id": 4,
|
||||
"timeseries": {
|
||||
"1453248895": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_name": "ford",
|
||||
"source_id": 3,
|
||||
"timeseries": {
|
||||
"1453248927": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"saved_search": {
|
||||
"id": 42,
|
||||
"name": "Important stuff",
|
||||
"query": "cron OR server1",
|
||||
"html_edit_url": "https://papertrailapp.com/searches/42/edit",
|
||||
"html_search_url": "https://papertrailapp.com/searches/42"
|
||||
},
|
||||
"max_id": "7711582041804800",
|
||||
"min_id": "7711561783320576"
|
||||
}`
|
97
plugins/inputs/webhooks/papertrail/papertrail_webhooks.go
Normal file
97
plugins/inputs/webhooks/papertrail/papertrail_webhooks.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package papertrail
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/auth"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
auth.BasicAuth
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (pt *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(pt.Path, pt.eventHandler).Methods("POST")
|
||||
pt.log = log
|
||||
pt.log.Infof("Started the papertrail_webhook on %s", pt.Path)
|
||||
pt.acc = acc
|
||||
}
|
||||
|
||||
func (pt *Webhook) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Content-Type") != "application/x-www-form-urlencoded" {
|
||||
http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
|
||||
return
|
||||
}
|
||||
|
||||
if !pt.Verify(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
data := r.PostFormValue("payload")
|
||||
if data == "" {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var payload payload
|
||||
err := json.Unmarshal([]byte(data), &payload)
|
||||
if err != nil {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if payload.Events != nil {
|
||||
// Handle event-based payload
|
||||
for _, e := range payload.Events {
|
||||
// Warning: Duplicate event timestamps will overwrite each other
|
||||
tags := map[string]string{
|
||||
"host": e.Hostname,
|
||||
"event": payload.SavedSearch.Name,
|
||||
}
|
||||
fields := map[string]interface{}{
|
||||
"count": uint64(1),
|
||||
"id": e.ID,
|
||||
"source_ip": e.SourceIP,
|
||||
"source_name": e.SourceName,
|
||||
"source_id": int64(e.SourceID),
|
||||
"program": e.Program,
|
||||
"severity": e.Severity,
|
||||
"facility": e.Facility,
|
||||
"message": e.Message,
|
||||
"url": fmt.Sprintf("%s?centered_on_id=%d", payload.SavedSearch.SearchURL, e.ID),
|
||||
"search_id": payload.SavedSearch.ID,
|
||||
}
|
||||
pt.acc.AddFields("papertrail", fields, tags, e.ReceivedAt)
|
||||
}
|
||||
} else if payload.Counts != nil {
|
||||
// Handle count-based payload
|
||||
for _, c := range payload.Counts {
|
||||
for ts, count := range *c.TimeSeries {
|
||||
tags := map[string]string{
|
||||
"host": c.SourceName,
|
||||
"event": payload.SavedSearch.Name,
|
||||
}
|
||||
fields := map[string]interface{}{
|
||||
"count": count,
|
||||
}
|
||||
pt.acc.AddFields("papertrail", fields, tags, time.Unix(ts, 0))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package papertrail
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type event struct {
|
||||
ID int64 `json:"id"`
|
||||
ReceivedAt time.Time `json:"received_at"`
|
||||
DisplayReceivedAt string `json:"display_received_at"`
|
||||
SourceIP string `json:"source_ip"`
|
||||
SourceName string `json:"source_name"`
|
||||
SourceID int `json:"source_id"`
|
||||
Hostname string `json:"hostname"`
|
||||
Program string `json:"program"`
|
||||
Severity string `json:"severity"`
|
||||
Facility string `json:"facility"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type count struct {
|
||||
SourceName string `json:"source_name"`
|
||||
SourceID int64 `json:"source_id"`
|
||||
TimeSeries *map[int64]uint64 `json:"timeseries"`
|
||||
}
|
||||
|
||||
type savedSearch struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
EditURL string `json:"html_edit_url"`
|
||||
SearchURL string `json:"html_search_url"`
|
||||
}
|
||||
|
||||
type payload struct {
|
||||
Events []*event `json:"events"`
|
||||
Counts []*count `json:"counts"`
|
||||
SavedSearch *savedSearch `json:"saved_search"`
|
||||
MaxID string `json:"max_id"`
|
||||
MinID string `json:"min_id"`
|
||||
}
|
45
plugins/inputs/webhooks/particle/README.md
Normal file
45
plugins/inputs/webhooks/particle/README.md
Normal file
|
@ -0,0 +1,45 @@
|
|||
# particle webhooks
|
||||
|
||||
You should configure your Particle.io's Webhooks to point at the `webhooks`
|
||||
service. To do this go to [https://console.particle.io][particle.io]
|
||||
and click `Integrations > New Integration > Webhook`. In the resulting page set
|
||||
`URL` to `http://<my_ip>:1619/particle`, and under `Advanced Settings` click
|
||||
on `JSON` and add:
|
||||
|
||||
```json
|
||||
{
|
||||
"measurement": "your_measurement_name"
|
||||
}
|
||||
```
|
||||
|
||||
If required, enter your username and password, etc. and then click `Save`
|
||||
|
||||
[particle.io]: https://console.particle.io/
|
||||
|
||||
## Events
|
||||
|
||||
Your Particle device should publish an event that contains a JSON in the form
|
||||
of:
|
||||
|
||||
```json
|
||||
String data = String::format("{ \"tags\" : {
|
||||
\"tag_name\": \"tag_value\",
|
||||
\"other_tag\": \"other_value\"
|
||||
},
|
||||
\"values\": {
|
||||
\"value_name\": %f,
|
||||
\"other_value\": %f,
|
||||
}
|
||||
}", value_value, other_value
|
||||
);
|
||||
Particle.publish("event_name", data, PRIVATE);
|
||||
```
|
||||
|
||||
Escaping the "" is required in the source file.
|
||||
The number of tag values and field values is not restricted so you can send as
|
||||
many values per webhook call as you'd like.
|
||||
|
||||
You will need to enable JSON messages in the Webhooks setup of Particle.io, and
|
||||
make sure to check the "include default data" box as well.
|
||||
|
||||
See [webhook doc](https://docs.particle.io/reference/webhooks/)
|
83
plugins/inputs/webhooks/particle/particle_webhooks.go
Normal file
83
plugins/inputs/webhooks/particle/particle_webhooks.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package particle
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/auth"
|
||||
)
|
||||
|
||||
type event struct {
|
||||
Name string `json:"event"`
|
||||
Data data `json:"data"`
|
||||
TTL int `json:"ttl"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
Measurement string `json:"measurement"`
|
||||
}
|
||||
|
||||
type data struct {
|
||||
Tags map[string]string `json:"tags"`
|
||||
Fields map[string]interface{} `json:"values"`
|
||||
}
|
||||
|
||||
func newEvent() *event {
|
||||
return &event{
|
||||
Data: data{
|
||||
Tags: make(map[string]string),
|
||||
Fields: make(map[string]interface{}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (e *event) time() (time.Time, error) {
|
||||
return time.Parse("2006-01-02T15:04:05Z", e.PublishedAt)
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
auth.BasicAuth
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (rb *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(rb.Path, rb.eventHandler).Methods("POST")
|
||||
rb.log = log
|
||||
rb.log.Infof("Started the webhooks_particle on %s", rb.Path)
|
||||
rb.acc = acc
|
||||
}
|
||||
|
||||
func (rb *Webhook) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if !rb.Verify(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
e := newEvent()
|
||||
if err := json.NewDecoder(r.Body).Decode(e); err != nil {
|
||||
rb.acc.AddError(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
pTime, err := e.time()
|
||||
if err != nil {
|
||||
pTime = time.Now()
|
||||
}
|
||||
|
||||
// Use 'measurement' event field as the measurement, or default to the event name.
|
||||
measurementName := e.Measurement
|
||||
if measurementName == "" {
|
||||
measurementName = e.Name
|
||||
}
|
||||
|
||||
rb.acc.AddFields(measurementName, e.Data.Fields, e.Data.Tags, pTime)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
144
plugins/inputs/webhooks/particle/particle_webhooks_test.go
Normal file
144
plugins/inputs/webhooks/particle/particle_webhooks_test.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package particle
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func postWebhooks(t *testing.T, rb *Webhook, eventBody string) *httptest.ResponseRecorder {
|
||||
req, err := http.NewRequest("POST", "/", strings.NewReader(eventBody))
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
w.Code = 500
|
||||
|
||||
rb.eventHandler(w, req)
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func TestNewItem(t *testing.T) {
|
||||
t.Parallel()
|
||||
var acc testutil.Accumulator
|
||||
rb := &Webhook{Path: "/particle", acc: &acc}
|
||||
resp := postWebhooks(t, rb, newItemJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST new_item returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"temp_c": 26.680000,
|
||||
"temp_f": 80.024001,
|
||||
"infrared": 528.0,
|
||||
"lux": 0.0,
|
||||
"humidity": 44.937500,
|
||||
"pressure": 998.998901,
|
||||
"altitude": 119.331436,
|
||||
"broadband": 1266.0,
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"id": "230035001147343438323536",
|
||||
"location": "TravelingWilbury",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "mydata", fields, tags)
|
||||
}
|
||||
|
||||
func TestUnknowItem(t *testing.T) {
|
||||
t.Parallel()
|
||||
var acc testutil.Accumulator
|
||||
rb := &Webhook{Path: "/particle", acc: &acc}
|
||||
resp := postWebhooks(t, rb, unknownJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST unknown returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultMeasurementName(t *testing.T) {
|
||||
t.Parallel()
|
||||
var acc testutil.Accumulator
|
||||
rb := &Webhook{Path: "/particle", acc: &acc}
|
||||
resp := postWebhooks(t, rb, blankMeasurementJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST new_item returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"temp_c": 26.680000,
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"id": "230035001147343438323536",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "eventName", fields, tags)
|
||||
}
|
||||
|
||||
func blankMeasurementJSON() string {
|
||||
return `
|
||||
{
|
||||
"event": "eventName",
|
||||
"data": {
|
||||
"tags": {
|
||||
"id": "230035001147343438323536"
|
||||
},
|
||||
"values": {
|
||||
"temp_c": 26.680000
|
||||
}
|
||||
},
|
||||
"ttl": 60,
|
||||
"published_at": "2017-09-28T21:54:10.897Z",
|
||||
"coreid": "123456789938323536",
|
||||
"userid": "1234ee123ac8e5ec1231a123d",
|
||||
"version": 10,
|
||||
"public": false,
|
||||
"productID": 1234,
|
||||
"name": "sensor",
|
||||
"measurement": ""
|
||||
}`
|
||||
}
|
||||
|
||||
func newItemJSON() string {
|
||||
return `
|
||||
{
|
||||
"event": "temperature",
|
||||
"data": {
|
||||
"tags": {
|
||||
"id": "230035001147343438323536",
|
||||
"location": "TravelingWilbury"
|
||||
},
|
||||
"values": {
|
||||
"temp_c": 26.680000,
|
||||
"temp_f": 80.024001,
|
||||
"humidity": 44.937500,
|
||||
"pressure": 998.998901,
|
||||
"altitude": 119.331436,
|
||||
"broadband": 1266.0,
|
||||
"infrared": 528.0,
|
||||
"lux": 0.0
|
||||
}
|
||||
},
|
||||
"ttl": 60,
|
||||
"published_at": "2017-09-28T21:54:10.897Z",
|
||||
"coreid": "123456789938323536",
|
||||
"userid": "1234ee123ac8e5ec1231a123d",
|
||||
"version": 10,
|
||||
"public": false,
|
||||
"productID": 1234,
|
||||
"name": "sensor",
|
||||
"measurement": "mydata"
|
||||
}`
|
||||
}
|
||||
|
||||
func unknownJSON() string {
|
||||
return `
|
||||
{
|
||||
"event": "roger"
|
||||
}`
|
||||
}
|
64
plugins/inputs/webhooks/rollbar/README.md
Normal file
64
plugins/inputs/webhooks/rollbar/README.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# rollbar webhooks
|
||||
|
||||
You should configure your Rollbar's Webhooks to point at the `webhooks` service.
|
||||
To do this go to [rollbar.com](https://rollbar.com/) and click
|
||||
`Settings > Notifications > Webhook`. In the resulting page set `URL` to
|
||||
`http://<my_ip>:1619/rollbar`, and click on `Enable Webhook Integration`.
|
||||
|
||||
## Events
|
||||
|
||||
The titles of the following sections are links to the full payloads and details
|
||||
for each event. The body contains what information from the event is persisted.
|
||||
The format is as follows:
|
||||
|
||||
```toml
|
||||
# TAGS
|
||||
* 'tagKey' = `tagValue` type
|
||||
# FIELDS
|
||||
* 'fieldKey' = `fieldValue` type
|
||||
```
|
||||
|
||||
The tag values and field values show the place on the incoming JSON object where
|
||||
the data is sourced from.
|
||||
|
||||
See [webhook doc](https://rollbar.com/docs/webhooks/)
|
||||
|
||||
### `new_item` event
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `event.event_name` string
|
||||
* 'environment' = `event.data.item.environment` string
|
||||
* 'project_id = `event.data.item.project_id` int
|
||||
* 'language' = `event.data.item.last_occurrence.language` string
|
||||
* 'level' = `event.data.item.last_occurrence.level` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'id' = `event.data.item.id` int
|
||||
|
||||
### `occurrence` event
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `event.event_name` string
|
||||
* 'environment' = `event.data.item.environment` string
|
||||
* 'project_id = `event.data.item.project_id` int
|
||||
* 'language' = `event.data.occurrence.language` string
|
||||
* 'level' = `event.data.occurrence.level` string
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'id' = `event.data.item.id` int
|
||||
|
||||
### `deploy` event
|
||||
|
||||
**Tags:**
|
||||
|
||||
* 'event' = `event.event_name` string
|
||||
* 'environment' = `event.data.deploy.environment` string
|
||||
* 'project_id = `event.data.deploy.project_id` int
|
||||
|
||||
**Fields:**
|
||||
|
||||
* 'id' = `event.data.item.id` int
|
82
plugins/inputs/webhooks/rollbar/rollbar_webhooks.go
Normal file
82
plugins/inputs/webhooks/rollbar/rollbar_webhooks.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package rollbar
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/auth"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Path string
|
||||
acc telegraf.Accumulator
|
||||
log telegraf.Logger
|
||||
auth.BasicAuth
|
||||
}
|
||||
|
||||
// Register registers the webhook with the provided router
|
||||
func (rb *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
|
||||
router.HandleFunc(rb.Path, rb.eventHandler).Methods("POST")
|
||||
rb.log = log
|
||||
rb.log.Infof("Started the webhooks_rollbar on %s", rb.Path)
|
||||
rb.acc = acc
|
||||
}
|
||||
|
||||
func (rb *Webhook) eventHandler(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if !rb.Verify(r) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
dummyEvent := &dummyEvent{}
|
||||
err = json.Unmarshal(data, dummyEvent)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
event, err := newEvent(dummyEvent, data)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
rb.acc.AddFields("rollbar_webhooks", event.fields(), event.tags(), time.Now())
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func generateEvent(event event, data []byte) (event, error) {
|
||||
err := json.Unmarshal(data, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event, nil
|
||||
}
|
||||
|
||||
func newEvent(dummyEvent *dummyEvent, data []byte) (event, error) {
|
||||
switch dummyEvent.EventName {
|
||||
case "new_item":
|
||||
return generateEvent(&newItem{}, data)
|
||||
case "occurrence":
|
||||
return generateEvent(&occurrence{}, data)
|
||||
case "deploy":
|
||||
return generateEvent(&deploy{}, data)
|
||||
default:
|
||||
return nil, errors.New("Not implemented type: " + dummyEvent.EventName)
|
||||
}
|
||||
}
|
115
plugins/inputs/webhooks/rollbar/rollbar_webhooks_events.go
Normal file
115
plugins/inputs/webhooks/rollbar/rollbar_webhooks_events.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package rollbar
|
||||
|
||||
import "strconv"
|
||||
|
||||
type event interface {
|
||||
tags() map[string]string
|
||||
fields() map[string]interface{}
|
||||
}
|
||||
|
||||
type dummyEvent struct {
|
||||
EventName string `json:"event_name"`
|
||||
}
|
||||
|
||||
type newItemDataItemLastOccurrence struct {
|
||||
Language string `json:"language"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
type newItemDataItem struct {
|
||||
ID int `json:"id"`
|
||||
Environment string `json:"environment"`
|
||||
ProjectID int `json:"project_id"`
|
||||
LastOccurrence newItemDataItemLastOccurrence `json:"last_occurrence"`
|
||||
}
|
||||
|
||||
type newItemData struct {
|
||||
Item newItemDataItem `json:"item"`
|
||||
}
|
||||
|
||||
type newItem struct {
|
||||
EventName string `json:"event_name"`
|
||||
Data newItemData `json:"data"`
|
||||
}
|
||||
|
||||
func (ni *newItem) tags() map[string]string {
|
||||
return map[string]string{
|
||||
"event": ni.EventName,
|
||||
"environment": ni.Data.Item.Environment,
|
||||
"project_id": strconv.Itoa(ni.Data.Item.ProjectID),
|
||||
"language": ni.Data.Item.LastOccurrence.Language,
|
||||
"level": ni.Data.Item.LastOccurrence.Level,
|
||||
}
|
||||
}
|
||||
|
||||
func (ni *newItem) fields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": ni.Data.Item.ID,
|
||||
}
|
||||
}
|
||||
|
||||
type occurrenceDataOccurrence struct {
|
||||
Language string `json:"language"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
type occurrenceDataItem struct {
|
||||
ID int `json:"id"`
|
||||
Environment string `json:"environment"`
|
||||
ProjectID int `json:"project_id"`
|
||||
}
|
||||
|
||||
type occurrenceData struct {
|
||||
Item occurrenceDataItem `json:"item"`
|
||||
Occurrence occurrenceDataOccurrence `json:"occurrence"`
|
||||
}
|
||||
|
||||
type occurrence struct {
|
||||
EventName string `json:"event_name"`
|
||||
Data occurrenceData `json:"data"`
|
||||
}
|
||||
|
||||
func (o *occurrence) tags() map[string]string {
|
||||
return map[string]string{
|
||||
"event": o.EventName,
|
||||
"environment": o.Data.Item.Environment,
|
||||
"project_id": strconv.Itoa(o.Data.Item.ProjectID),
|
||||
"language": o.Data.Occurrence.Language,
|
||||
"level": o.Data.Occurrence.Level,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *occurrence) fields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": o.Data.Item.ID,
|
||||
}
|
||||
}
|
||||
|
||||
type deployDataDeploy struct {
|
||||
ID int `json:"id"`
|
||||
Environment string `json:"environment"`
|
||||
ProjectID int `json:"project_id"`
|
||||
}
|
||||
|
||||
type deployData struct {
|
||||
Deploy deployDataDeploy `json:"deploy"`
|
||||
}
|
||||
|
||||
type deploy struct {
|
||||
EventName string `json:"event_name"`
|
||||
Data deployData `json:"data"`
|
||||
}
|
||||
|
||||
func (ni *deploy) tags() map[string]string {
|
||||
return map[string]string{
|
||||
"event": ni.EventName,
|
||||
"environment": ni.Data.Deploy.Environment,
|
||||
"project_id": strconv.Itoa(ni.Data.Deploy.ProjectID),
|
||||
}
|
||||
}
|
||||
|
||||
func (ni *deploy) fields() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": ni.Data.Deploy.ID,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package rollbar
|
||||
|
||||
func newItemJSON() string {
|
||||
return `
|
||||
{
|
||||
"event_name": "new_item",
|
||||
"data": {
|
||||
"item": {
|
||||
"public_item_id": null,
|
||||
"integrations_data": {},
|
||||
"last_activated_timestamp": 1382655421,
|
||||
"unique_occurrences": null,
|
||||
"id": 272716944,
|
||||
"environment": "production",
|
||||
"title": "testing aobg98wrwe",
|
||||
"last_occurrence_id": 481761639,
|
||||
"last_occurrence_timestamp": 1382655421,
|
||||
"platform": 0,
|
||||
"first_occurrence_timestamp": 1382655421,
|
||||
"project_id": 90,
|
||||
"resolved_in_version": null,
|
||||
"status": 1,
|
||||
"hash": "c595b2ae0af9b397bb6bdafd57104ac4d5f6b382",
|
||||
"last_occurrence": {
|
||||
"body": {
|
||||
"message": {
|
||||
"body": "testing aobg98wrwe"
|
||||
}
|
||||
},
|
||||
"uuid": "d2036647-e0b7-4cad-bc98-934831b9b6d1",
|
||||
"language": "python",
|
||||
"level": "error",
|
||||
"timestamp": 1382655421,
|
||||
"server": {
|
||||
"host": "dev",
|
||||
"argv": [
|
||||
""
|
||||
]
|
||||
},
|
||||
"environment": "production",
|
||||
"framework": "unknown",
|
||||
"notifier": {
|
||||
"version": "0.5.12",
|
||||
"name": "pyrollbar"
|
||||
},
|
||||
"metadata": {
|
||||
"access_token": "",
|
||||
"debug": {
|
||||
"routes": {
|
||||
"start_time": 1382212080401,
|
||||
"counters": {
|
||||
"post_item": 3274122
|
||||
}
|
||||
}
|
||||
},
|
||||
"customer_timestamp": 1382655421,
|
||||
"api_server_hostname": "web6"
|
||||
}
|
||||
},
|
||||
"framework": 0,
|
||||
"total_occurrences": 1,
|
||||
"level": 40,
|
||||
"counter": 4,
|
||||
"first_occurrence_id": 481761639,
|
||||
"activating_occurrence_id": 481761639
|
||||
}
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func occurrenceJSON() string {
|
||||
return `
|
||||
{
|
||||
"event_name": "occurrence",
|
||||
"data": {
|
||||
"item": {
|
||||
"public_item_id": null,
|
||||
"integrations_data": {},
|
||||
"level_lock": 0,
|
||||
"last_activated_timestamp": 1471624512,
|
||||
"assigned_user_id": null,
|
||||
"hash": "188fc37fa6e641a4d4a3d0198938a1937d31ddbe",
|
||||
"id": 402860571,
|
||||
"environment": "production",
|
||||
"title": "Exception: test exception",
|
||||
"last_occurrence_id": 16298872829,
|
||||
"last_occurrence_timestamp": 1472226345,
|
||||
"platform": 0,
|
||||
"first_occurrence_timestamp": 1471624512,
|
||||
"project_id": 78234,
|
||||
"resolved_in_version": null,
|
||||
"status": 1,
|
||||
"unique_occurrences": null,
|
||||
"title_lock": 0,
|
||||
"framework": 6,
|
||||
"total_occurrences": 8,
|
||||
"level": 40,
|
||||
"counter": 2,
|
||||
"last_modified_by": 8247,
|
||||
"first_occurrence_id": 16103102935,
|
||||
"activating_occurrence_id": 16103102935
|
||||
},
|
||||
"occurrence": {
|
||||
"body": {
|
||||
"trace": {
|
||||
"frames": [{"method": "<main>", "lineno": 27, "filename": "/Users/rebeccastandig/Desktop/Dev/php-rollbar-app/index.php"}], "exception": {
|
||||
"message": "test 2",
|
||||
"class": "Exception"}
|
||||
}
|
||||
},
|
||||
"uuid": "84d4eccd-b24d-47ae-a42b-1a2f9a82fb82",
|
||||
"language": "php",
|
||||
"level": "error",
|
||||
"timestamp": 1472226345,
|
||||
"php_context": "cli",
|
||||
"environment": "production",
|
||||
"framework": "php",
|
||||
"person": null,
|
||||
"server": {
|
||||
"host": "Rebeccas-MacBook-Pro.local",
|
||||
"argv": ["index.php"]
|
||||
},
|
||||
"notifier": {
|
||||
"version": "0.18.2",
|
||||
"name": "rollbar-php"
|
||||
},
|
||||
"metadata": {
|
||||
"customer_timestamp": 1472226359
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func deployJSON() string {
|
||||
return `
|
||||
{
|
||||
"event_name": "deploy",
|
||||
"data": {
|
||||
"deploy": {
|
||||
"comment": "deploying webs",
|
||||
"user_id": 1,
|
||||
"finish_time": 1382656039,
|
||||
"start_time": 1382656038,
|
||||
"id": 187585,
|
||||
"environment": "production",
|
||||
"project_id": 90,
|
||||
"local_username": "brian",
|
||||
"revision": "e4b9b7db860b2e5ac799f8c06b9498b71ab270bb"
|
||||
}
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
func unknownJSON() string {
|
||||
return `
|
||||
{
|
||||
"event_name": "roger"
|
||||
}`
|
||||
}
|
98
plugins/inputs/webhooks/rollbar/rollbar_webhooks_test.go
Normal file
98
plugins/inputs/webhooks/rollbar/rollbar_webhooks_test.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package rollbar
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func postWebhooks(t *testing.T, rb *Webhook, eventBody string) *httptest.ResponseRecorder {
|
||||
req, err := http.NewRequest("POST", "/", strings.NewReader(eventBody))
|
||||
require.NoError(t, err)
|
||||
w := httptest.NewRecorder()
|
||||
w.Code = 500
|
||||
|
||||
rb.eventHandler(w, req)
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func TestNewItem(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
rb := &Webhook{Path: "/rollbar", acc: &acc}
|
||||
resp := postWebhooks(t, rb, newItemJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST new_item returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": 272716944,
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"event": "new_item",
|
||||
"environment": "production",
|
||||
"project_id": "90",
|
||||
"language": "python",
|
||||
"level": "error",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "rollbar_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func TestOccurrence(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
rb := &Webhook{Path: "/rollbar", acc: &acc}
|
||||
resp := postWebhooks(t, rb, occurrenceJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST occurrence returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": 402860571,
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"event": "occurrence",
|
||||
"environment": "production",
|
||||
"project_id": "78234",
|
||||
"language": "php",
|
||||
"level": "error",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "rollbar_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func TestDeploy(t *testing.T) {
|
||||
var acc testutil.Accumulator
|
||||
rb := &Webhook{Path: "/rollbar", acc: &acc}
|
||||
resp := postWebhooks(t, rb, deployJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST deploy returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"id": 187585,
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"event": "deploy",
|
||||
"environment": "production",
|
||||
"project_id": "90",
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "rollbar_webhooks", fields, tags)
|
||||
}
|
||||
|
||||
func TestUnknowItem(t *testing.T) {
|
||||
rb := &Webhook{Path: "/rollbar"}
|
||||
resp := postWebhooks(t, rb, unknownJSON())
|
||||
if resp.Code != http.StatusOK {
|
||||
t.Errorf("POST unknow returned HTTP status code %v.\nExpected %v", resp.Code, http.StatusOK)
|
||||
}
|
||||
}
|
55
plugins/inputs/webhooks/sample.conf
Normal file
55
plugins/inputs/webhooks/sample.conf
Normal file
|
@ -0,0 +1,55 @@
|
|||
# A Webhooks Event collector
|
||||
[[inputs.webhooks]]
|
||||
## Address and port to host Webhook listener on
|
||||
service_address = ":1619"
|
||||
|
||||
## Maximum duration before timing out read of the request
|
||||
# read_timeout = "10s"
|
||||
## Maximum duration before timing out write of the response
|
||||
# write_timeout = "10s"
|
||||
|
||||
[inputs.webhooks.filestack]
|
||||
path = "/filestack"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.github]
|
||||
path = "/github"
|
||||
# secret = ""
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.mandrill]
|
||||
path = "/mandrill"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.rollbar]
|
||||
path = "/rollbar"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.papertrail]
|
||||
path = "/papertrail"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.particle]
|
||||
path = "/particle"
|
||||
|
||||
## HTTP basic auth
|
||||
#username = ""
|
||||
#password = ""
|
||||
|
||||
[inputs.webhooks.artifactory]
|
||||
path = "/artifactory"
|
136
plugins/inputs/webhooks/webhooks.go
Normal file
136
plugins/inputs/webhooks/webhooks.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/artifactory"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/filestack"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/github"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/mandrill"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/papertrail"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/particle"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/rollbar"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
const (
|
||||
defaultReadTimeout = 10 * time.Second
|
||||
defaultWriteTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
type Webhooks struct {
|
||||
ServiceAddress string `toml:"service_address"`
|
||||
ReadTimeout config.Duration `toml:"read_timeout"`
|
||||
WriteTimeout config.Duration `toml:"write_timeout"`
|
||||
|
||||
Artifactory *artifactory.Webhook `toml:"artifactory"`
|
||||
Filestack *filestack.Webhook `toml:"filestack"`
|
||||
Github *github.Webhook `toml:"github"`
|
||||
Mandrill *mandrill.Webhook `toml:"mandrill"`
|
||||
Papertrail *papertrail.Webhook `toml:"papertrail"`
|
||||
Particle *particle.Webhook `toml:"particle"`
|
||||
Rollbar *rollbar.Webhook `toml:"rollbar"`
|
||||
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
srv *http.Server
|
||||
}
|
||||
|
||||
// Webhook is an interface that all webhooks must implement
|
||||
type Webhook interface {
|
||||
// Register registers the webhook with the provided router
|
||||
Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger)
|
||||
}
|
||||
|
||||
func (*Webhooks) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (wb *Webhooks) Start(acc telegraf.Accumulator) error {
|
||||
if wb.ReadTimeout < config.Duration(time.Second) {
|
||||
wb.ReadTimeout = config.Duration(defaultReadTimeout)
|
||||
}
|
||||
if wb.WriteTimeout < config.Duration(time.Second) {
|
||||
wb.WriteTimeout = config.Duration(defaultWriteTimeout)
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
||||
for _, webhook := range wb.availableWebhooks() {
|
||||
webhook.Register(r, acc, wb.Log)
|
||||
}
|
||||
|
||||
wb.srv = &http.Server{
|
||||
Handler: r,
|
||||
ReadTimeout: time.Duration(wb.ReadTimeout),
|
||||
WriteTimeout: time.Duration(wb.WriteTimeout),
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", wb.ServiceAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting server: %w", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := wb.srv.Serve(ln); err != nil {
|
||||
if err != http.ErrServerClosed {
|
||||
acc.AddError(fmt.Errorf("error listening: %w", err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
wb.Log.Infof("Started the webhooks service on %s", wb.ServiceAddress)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*Webhooks) Gather(telegraf.Accumulator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wb *Webhooks) Stop() {
|
||||
wb.srv.Close()
|
||||
wb.Log.Infof("Stopping the Webhooks service")
|
||||
}
|
||||
|
||||
// availableWebhooks Looks for fields which implement Webhook interface
|
||||
func (wb *Webhooks) availableWebhooks() []Webhook {
|
||||
webhooks := make([]Webhook, 0)
|
||||
s := reflect.ValueOf(wb).Elem()
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
|
||||
if !f.CanInterface() {
|
||||
continue
|
||||
}
|
||||
|
||||
if wbPlugin, ok := f.Interface().(Webhook); ok {
|
||||
if !reflect.ValueOf(wbPlugin).IsNil() {
|
||||
webhooks = append(webhooks, wbPlugin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return webhooks
|
||||
}
|
||||
|
||||
func newWebhooks() *Webhooks {
|
||||
return &Webhooks{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("webhooks", func() telegraf.Input { return newWebhooks() })
|
||||
}
|
64
plugins/inputs/webhooks/webhooks_test.go
Normal file
64
plugins/inputs/webhooks/webhooks_test.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/artifactory"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/filestack"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/github"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/mandrill"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/papertrail"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/particle"
|
||||
"github.com/influxdata/telegraf/plugins/inputs/webhooks/rollbar"
|
||||
)
|
||||
|
||||
func TestAvailableWebhooks(t *testing.T) {
|
||||
wb := newWebhooks()
|
||||
expected := make([]Webhook, 0)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Artifactory = &artifactory.Webhook{Path: "/artifactory"}
|
||||
expected = append(expected, wb.Artifactory)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Filestack = &filestack.Webhook{Path: "/filestack"}
|
||||
expected = append(expected, wb.Filestack)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Github = &github.Webhook{Path: "/github"}
|
||||
expected = append(expected, wb.Github)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Mandrill = &mandrill.Webhook{Path: "/mandrill"}
|
||||
expected = append(expected, wb.Mandrill)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Papertrail = &papertrail.Webhook{Path: "/papertrail"}
|
||||
expected = append(expected, wb.Papertrail)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Particle = &particle.Webhook{Path: "/particle"}
|
||||
expected = append(expected, wb.Particle)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
|
||||
wb.Rollbar = &rollbar.Webhook{Path: "/rollbar"}
|
||||
expected = append(expected, wb.Rollbar)
|
||||
if !reflect.DeepEqual(wb.availableWebhooks(), expected) {
|
||||
t.Errorf("expected to be %v.\nGot %v", expected, wb.availableWebhooks())
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue