feat: Artifactory Webhook Receiver (#10918)

This commit is contained in:
sammcadams-8451 2022-05-16 16:20:54 -04:00 committed by GitHub
parent 09a863bd3d
commit df6d4e7972
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1501 additions and 6 deletions

View File

@ -63,6 +63,9 @@ sudo service telegraf start
## HTTP basic auth
#username = ""
#password = ""
[inputs.webhooks.artifactory]
path = "/artifactory"
```
## Available webhooks
@ -73,6 +76,7 @@ sudo service telegraf start
- [Rollbar](rollbar/)
- [Papertrail](papertrail/)
- [Particle](particle/)
- [Artifactory](artifactory/)
## Adding new webhooks plugin

View File

@ -0,0 +1,511 @@
# artifactory webhook
You need to configure to orginizations artifactory instance/s as detailed via the artifactory webhook documentation: <https://www.jfrog.com/confluence/display/JFROG/Webhooks>. 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.
## 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 seperated 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 seperated 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
* 'achitecture' 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
* 'achitecture' 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
* 'achitecture' 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

View File

@ -0,0 +1,120 @@
package artifactory
import (
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/influxdata/telegraf"
)
type ArtifactoryWebhook struct {
Path string
Secret string
acc telegraf.Accumulator
log telegraf.Logger
}
func (awh *ArtifactoryWebhook) 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 *ArtifactoryWebhook) 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 *ArtifactoryWebhook) NewEvent(data []byte, et string, 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{})
} else if et == "moved" || et == "copied" {
return generateEvent(data, &ArtifactMovedOrCopiedEvent{})
} else {
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)
}

View File

@ -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"
}`
}

View File

@ -0,0 +1,260 @@
package artifactory
import (
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/metric"
)
const meas = "artifactory_webhooks"
type CommonFields struct {
Domain string `json:"domain"`
Event string `json:"event_type"`
}
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:"achitecture"`
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())
}

View File

@ -0,0 +1,152 @@
package artifactory
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/influxdata/telegraf/testutil"
)
func ArtifactoryWebhookRequest(domain string, event string, jsonString string, t *testing.T) {
var acc testutil.Accumulator
awh := &ArtifactoryWebhook{Path: "/artifactory", acc: &acc, log: testutil.Logger{}}
req, _ := http.NewRequest("POST", "/artifactory", strings.NewReader(jsonString))
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(event string, jsonString string, t *testing.T, signature string, expectedStatus int) {
var acc testutil.Accumulator
awh := &ArtifactoryWebhook{Path: "/artifactory", acc: &acc, log: testutil.Logger{}}
req, _ := http.NewRequest("POST", "/artifactory", strings.NewReader(jsonString))
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 := &ArtifactoryWebhook{Path: "/artifactory", acc: &acc, log: testutil.Logger{}}
req, _ := http.NewRequest("POST", "/artifactory", strings.NewReader(UnsupportedEventJSON()))
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("artifact", "deployed", ArtifactDeployedEventJSON(), t)
}
func TestArtifactDeleted(t *testing.T) {
ArtifactoryWebhookRequest("artifact", "deleted", ArtifactDeletedEventJSON(), t)
}
func TestArtifactMovedEvent(t *testing.T) {
ArtifactoryWebhookRequest("artifact", "moved", ArtifactMovedEventJSON(), t)
}
func TestArtifactCopiedEvent(t *testing.T) {
ArtifactoryWebhookRequest("artifact", "copied", ArtifactCopiedEventJSON(), t)
}
func TestArtifactPropertiesAddedEvent(t *testing.T) {
ArtifactoryWebhookRequest("artifact_property", "added", ArtifactPropertiesAddedEventJSON(), t)
}
func TestArtifactPropertiesDeletedEvent(t *testing.T) {
ArtifactoryWebhookRequest("artifact_property", "deleted", ArtifactPropertiesDeletedEventJSON(), t)
}
func TestDockerPushedEvent(t *testing.T) {
ArtifactoryWebhookRequest("docker", "pushed", DockerPushedEventJSON(), t)
}
func TestDockerDeletedEvent(t *testing.T) {
ArtifactoryWebhookRequest("docker", "deleted", DockerDeletedEventJSON(), t)
}
func TestDockerPromotedEvent(t *testing.T) {
ArtifactoryWebhookRequest("docker", "promoted", DockerPromotedEventJSON(), t)
}
func TestBuildUploadedEvent(t *testing.T) {
ArtifactoryWebhookRequest("build", "uploaded", BuildUploadedEventJSON(), t)
}
func TestBuildDeletedEvent(t *testing.T) {
ArtifactoryWebhookRequest("build", "deleted", BuildDeletedEventJSON(), t)
}
func TestBuildPromotedEvent(t *testing.T) {
ArtifactoryWebhookRequest("build", "promoted", BuildPromotedEventJSON(), t)
}
func TestReleaseBundleCreatedEvent(t *testing.T) {
ArtifactoryWebhookRequest("release_bundle", "created", ReleaseBundleCreatedEventJSON(), t)
}
func TestReleaseBundleSignedEvent(t *testing.T) {
ArtifactoryWebhookRequest("release_bundle", "signed", ReleaseBundleSignedEventJSON(), t)
}
func TestReleaseBundleDeletedEvent(t *testing.T) {
ArtifactoryWebhookRequest("release_bundle", "deleted", ReleaseBundleDeletedEventJSON(), t)
}
func TestDistributionStartedEvent(t *testing.T) {
ArtifactoryWebhookRequest("distribution", "distribute_started", DistributionStartedEventJSON(), t)
}
func TestDistributionCompletedEvent(t *testing.T) {
ArtifactoryWebhookRequest("distribution", "distribute_started", DistributionCompletedEventJSON(), t)
}
func TestDistributionAbortedEvent(t *testing.T) {
ArtifactoryWebhookRequest("distribution", "distribute_aborted", DistributionAbortedEventJSON(), t)
}
func TestDistributionFailedEvent(t *testing.T) {
ArtifactoryWebhookRequest("distribution", "distribute_failed", DistributionFailedEventJSON(), t)
}
func TestDestinationReceivedEvent(t *testing.T) {
ArtifactoryWebhookRequest("destination", "received", DestinationReceivedEventJSON(), t)
}
func TestDestinationDeletedStartedEvent(t *testing.T) {
ArtifactoryWebhookRequest("destination", "delete_started", DestinationDeleteStartedEventJSON(), t)
}
func TestDestinationDeletedCompletedEvent(t *testing.T) {
ArtifactoryWebhookRequest("destination", "delete_completed", DestinationDeleteCompletedEventJSON(), t)
}
func TestDestinationDeleteFailedEvent(t *testing.T) {
ArtifactoryWebhookRequest("destination", "delete_failed", DestinationDeleteFailedEventJSON(), t)
}
func TestEventWithSignatureSuccess(t *testing.T) {
ArtifactoryWebhookRequestWithSignature("watch", ArtifactDeployedEventJSON(), t, 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")
}
}

View File

@ -10,6 +10,7 @@ import (
"github.com/influxdata/telegraf"
"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"
@ -29,12 +30,13 @@ func init() {
type Webhooks struct {
ServiceAddress string `toml:"service_address"`
Github *github.GithubWebhook `toml:"github"`
Filestack *filestack.FilestackWebhook `toml:"filestack"`
Mandrill *mandrill.MandrillWebhook `toml:"mandrill"`
Rollbar *rollbar.RollbarWebhook `toml:"rollbar"`
Papertrail *papertrail.PapertrailWebhook `toml:"papertrail"`
Particle *particle.ParticleWebhook `toml:"particle"`
Github *github.GithubWebhook `toml:"github"`
Filestack *filestack.FilestackWebhook `toml:"filestack"`
Mandrill *mandrill.MandrillWebhook `toml:"mandrill"`
Rollbar *rollbar.RollbarWebhook `toml:"rollbar"`
Papertrail *papertrail.PapertrailWebhook `toml:"papertrail"`
Particle *particle.ParticleWebhook `toml:"particle"`
Artifactory *artifactory.ArtifactoryWebhook `toml:"artifactory"`
Log telegraf.Logger `toml:"-"`

View File

@ -4,6 +4,7 @@ import (
"reflect"
"testing"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/artifactory"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/github"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/papertrail"
"github.com/influxdata/telegraf/plugins/inputs/webhooks/particle"
@ -40,4 +41,10 @@ func TestAvailableWebhooks(t *testing.T) {
if !reflect.DeepEqual(wb.AvailableWebhooks(), expected) {
t.Errorf("expected to be %v.\nGot %v", expected, wb.AvailableWebhooks())
}
wb.Artifactory = &artifactory.ArtifactoryWebhook{Path: "/artifactory"}
expected = append(expected, wb.Artifactory)
if !reflect.DeepEqual(wb.AvailableWebhooks(), expected) {
t.Errorf("expected to be %v.\nGot %v", expected, wb.AvailableWebhooks())
}
}