Adds gcr notification webhook
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
parent
9450a9a893
commit
07bca13213
|
|
@ -27,7 +27,7 @@ import (
|
|||
type ReceiverSpec struct {
|
||||
// Type of webhook sender, used to determine
|
||||
// the validation procedure and payload deserialization.
|
||||
// +kubebuilder:validation:Enum=generic;github;gitlab;bitbucket;harbor;dockerhub;quay
|
||||
// +kubebuilder:validation:Enum=generic;github;gitlab;bitbucket;harbor;dockerhub;quay;gcr
|
||||
// +required
|
||||
Type string `json:"type"`
|
||||
|
||||
|
|
@ -70,6 +70,7 @@ const (
|
|||
HarborReceiver string = "harbor"
|
||||
DockerHubReceiver string = "dockerhub"
|
||||
QuayReceiver string = "quay"
|
||||
GCRReceiver string = "gcr"
|
||||
)
|
||||
|
||||
func ReceiverReady(receiver Receiver, reason, message, url string) Receiver {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ spec:
|
|||
- harbor
|
||||
- dockerhub
|
||||
- quay
|
||||
- gcr
|
||||
type: string
|
||||
required:
|
||||
- resources
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ const (
|
|||
HarborReceiver string = "harbor"
|
||||
DockerHubReceiver string = "dockerhub"
|
||||
QuayReceiver string = "quay"
|
||||
GCRReceiver string = "gcr"
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -199,6 +200,23 @@ spec:
|
|||
name: webapp
|
||||
```
|
||||
|
||||
### GCR receiver
|
||||
|
||||
```yaml
|
||||
apiVersion: notification.toolkit.fluxcd.io/v1beta1
|
||||
kind: Receiver
|
||||
metadata:
|
||||
name: gcr-receiver
|
||||
namespace: default
|
||||
spec:
|
||||
type: gcr
|
||||
secretRef:
|
||||
name: webhook-token
|
||||
resources:
|
||||
- kind: ImageRepository
|
||||
name: webapp
|
||||
```
|
||||
|
||||
### Generic receiver
|
||||
|
||||
```yaml
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
|
@ -215,6 +217,54 @@ func (s *ReceiverServer) validate(ctx context.Context, receiver v1beta1.Receiver
|
|||
fmt.Sprintf("handling event from %s for tag %s", p.Repository.URL, p.PushData.Tag),
|
||||
"receiver", receiver.Name)
|
||||
return nil
|
||||
case v1beta1.GCRReceiver:
|
||||
const (
|
||||
insert = "insert"
|
||||
tokenIndex = len("Bearer ")
|
||||
)
|
||||
|
||||
type data struct {
|
||||
Action string `json:"action"`
|
||||
Digest string `json:"digest"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type payload struct {
|
||||
Message struct {
|
||||
Data string `json:"data"`
|
||||
MessageID string `json:"messageId"`
|
||||
PublishTime time.Time `json:"publishTime"`
|
||||
Subscription string `json:"subscription"`
|
||||
} `json:"message"`
|
||||
}
|
||||
|
||||
err := authenticateGCRRequest(&http.Client{}, r.Header.Get("Authorization"), tokenIndex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot authenticate GCR request: %s", err)
|
||||
}
|
||||
|
||||
var p payload
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
return fmt.Errorf("cannot decode GCR webhook payload")
|
||||
}
|
||||
|
||||
raw, _ := base64.StdEncoding.DecodeString(p.Message.Data)
|
||||
|
||||
var d data
|
||||
err = json.Unmarshal(raw, &d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot decode GCR webhook body")
|
||||
}
|
||||
|
||||
if strings.ToLower(d.Action) != insert {
|
||||
s.logger.Info("action is not an insert, moving on")
|
||||
return nil
|
||||
}
|
||||
|
||||
s.logger.Info(
|
||||
fmt.Sprintf("handling event from %s for tag %s", d.Digest, d.Tag),
|
||||
"receiver", receiver.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("recevier type '%s' not supported", receiver.Spec.Type)
|
||||
|
|
@ -307,3 +357,28 @@ func (s *ReceiverServer) annotate(ctx context.Context, resource v1beta1.CrossNam
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func authenticateGCRRequest(c *http.Client, bearer string, tokenIndex int) (err error) {
|
||||
type auth struct {
|
||||
Aud string `json:"aud"`
|
||||
}
|
||||
|
||||
if len(bearer) < tokenIndex {
|
||||
return fmt.Errorf("Authorization header is missing or malformed: %v", bearer)
|
||||
}
|
||||
|
||||
token := bearer[tokenIndex:]
|
||||
url := fmt.Sprintf("https://oauth2.googleapis.com/tokeninfo?id_token=%s", token)
|
||||
|
||||
resp, err := c.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot verify authenticity of payload: %w", err)
|
||||
}
|
||||
|
||||
var p auth
|
||||
if err := json.NewDecoder(resp.Body).Decode(&p); err != nil {
|
||||
return fmt.Errorf("Cannot decode auth payload: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue