mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-24 10:37:32 +08:00
Add POC of GitHub Webhook Delivery Forwarder (#682)
* Add POC of GitHub Webhook Delivery Forwarder * multi-forwarder and ctrl-c existing and fix for non-woring http post * Rename source files * Extract signal handling into a dedicated source file * Faster ctrl-c handling * Enable automatic creation of repo hook on startup * Add support for forwarding org hook deliveries * Set hook secret on hook creation via envvar (HOOK_SECRET) * Fix org hook support * Fix HOOK_SECRET for consistency * Refactor to prepare for custom log position provider * Refactor to extract inmemory log position provider * Add configmap-based log position provider * Rename githubwebhookdeliveryforwarder to hookdeliveryforwarder * Refactor to rename LogPositionProvider to Checkpointer and extract ConfigMap checkpointer into a dedicated pkg * Refactor to extract logger initialization * Add hookdeliveryforwarder README and bump go-github to unreleased ver
This commit is contained in:
100
pkg/hookdeliveryforwarder/configmap/checkpointer.go
Normal file
100
pkg/hookdeliveryforwarder/configmap/checkpointer.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package configmap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/actions-runner-controller/actions-runner-controller/pkg/hookdeliveryforwarder"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type ConfigMapCheckpointer struct {
|
||||
Name string
|
||||
NS string
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
type state struct {
|
||||
DeliveredAt time.Time `json:"delivered_at"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
func (p *ConfigMapCheckpointer) GetOrCreate(hookID int64) (*hookdeliveryforwarder.State, error) {
|
||||
var cm corev1.ConfigMap
|
||||
|
||||
if err := p.Client.Get(context.Background(), types.NamespacedName{Namespace: p.NS, Name: p.Name}, &cm); err != nil {
|
||||
if !kerrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cm.Name = p.Name
|
||||
cm.Namespace = p.NS
|
||||
|
||||
if err := p.Client.Create(context.Background(), &cm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
idStr := fmt.Sprintf("hook_%d", hookID)
|
||||
|
||||
var unmarshalled state
|
||||
|
||||
data, ok := cm.Data[idStr]
|
||||
|
||||
if ok {
|
||||
if err := json.Unmarshal([]byte(data), &unmarshalled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pos := &hookdeliveryforwarder.State{
|
||||
DeliveredAt: unmarshalled.DeliveredAt,
|
||||
ID: unmarshalled.ID,
|
||||
}
|
||||
|
||||
if pos.DeliveredAt.IsZero() {
|
||||
pos.DeliveredAt = time.Now()
|
||||
}
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
func (p *ConfigMapCheckpointer) Update(hookID int64, pos *hookdeliveryforwarder.State) error {
|
||||
var cm corev1.ConfigMap
|
||||
|
||||
if err := p.Client.Get(context.Background(), types.NamespacedName{Namespace: p.NS, Name: p.Name}, &cm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var posData state
|
||||
|
||||
posData.DeliveredAt = pos.DeliveredAt
|
||||
posData.ID = pos.ID
|
||||
|
||||
idStr := fmt.Sprintf("hook_%d", hookID)
|
||||
|
||||
data, err := json.Marshal(posData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy := cm.DeepCopy()
|
||||
|
||||
if copy.Data == nil {
|
||||
copy.Data = map[string]string{}
|
||||
}
|
||||
|
||||
copy.Data[idStr] = string(data)
|
||||
|
||||
if err := p.Client.Patch(context.Background(), copy, client.MergeFrom(&cm)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
42
pkg/hookdeliveryforwarder/configmap/config.go
Normal file
42
pkg/hookdeliveryforwarder/configmap/config.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package configmap
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Logger logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (c *Config) InitFlags(fs *flag.FlagSet) {
|
||||
fs.StringVar(&c.Name, "configmap-name", "gh-webhook-forwarder", `The name of the Kubernetes ConfigMap to which store state for check-pointing.`)
|
||||
fs.StringVar(&c.Namespace, "namespace", "default", `The Kubernetes namespace to store configmap for check-pointing.`)
|
||||
}
|
||||
|
||||
func New(checkpointerConfig *Config) (*ConfigMapCheckpointer, manager.Manager, error) {
|
||||
ctrl.SetLogger(checkpointerConfig.Logger)
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: checkpointerConfig.Scheme,
|
||||
LeaderElectionID: "hookdeliveryforwarder",
|
||||
Port: 9443,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to start manager: %v", err)
|
||||
}
|
||||
|
||||
return &ConfigMapCheckpointer{
|
||||
Client: mgr.GetClient(),
|
||||
Name: checkpointerConfig.Name,
|
||||
NS: checkpointerConfig.Namespace,
|
||||
}, mgr, nil
|
||||
}
|
||||
Reference in New Issue
Block a user