Adds gcr notification webhook

Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
Somtochi Onyekwere 2021-01-18 10:31:24 +01:00
parent 9450a9a893
commit 07bca13213
4 changed files with 96 additions and 1 deletions

View File

@ -27,7 +27,7 @@ import (
type ReceiverSpec struct { type ReceiverSpec struct {
// Type of webhook sender, used to determine // Type of webhook sender, used to determine
// the validation procedure and payload deserialization. // 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 // +required
Type string `json:"type"` Type string `json:"type"`
@ -70,6 +70,7 @@ const (
HarborReceiver string = "harbor" HarborReceiver string = "harbor"
DockerHubReceiver string = "dockerhub" DockerHubReceiver string = "dockerhub"
QuayReceiver string = "quay" QuayReceiver string = "quay"
GCRReceiver string = "gcr"
) )
func ReceiverReady(receiver Receiver, reason, message, url string) Receiver { func ReceiverReady(receiver Receiver, reason, message, url string) Receiver {

View File

@ -110,6 +110,7 @@ spec:
- harbor - harbor
- dockerhub - dockerhub
- quay - quay
- gcr
type: string type: string
required: required:
- resources - resources

View File

@ -45,6 +45,7 @@ const (
HarborReceiver string = "harbor" HarborReceiver string = "harbor"
DockerHubReceiver string = "dockerhub" DockerHubReceiver string = "dockerhub"
QuayReceiver string = "quay" QuayReceiver string = "quay"
GCRReceiver string = "gcr"
) )
``` ```
@ -199,6 +200,23 @@ spec:
name: webapp 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 ### Generic receiver
```yaml ```yaml

View File

@ -18,11 +18,13 @@ package server
import ( import (
"context" "context"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"time"
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
"github.com/fluxcd/pkg/apis/meta" "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), fmt.Sprintf("handling event from %s for tag %s", p.Repository.URL, p.PushData.Tag),
"receiver", receiver.Name) "receiver", receiver.Name)
return nil 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) 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 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
}