This repository has been archived by the owner on Mar 8, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Major breaking change: Always apply resources on events (#5)
* Refactor, always run workflow * Add -ignore flag, remove broken reconciler Allows ignoring refs by regex * Support more events, add dry-run, update README
- Loading branch information
1 parent
8b57a9d
commit efabec1
Showing
13 changed files
with
379 additions
and
618 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,28 @@ | ||
# k8s-webhook-handler | ||
The k8s-webhook-handler listens for (GitHub) webhooks and acts on various events | ||
|
||
## Event Handlers | ||
### DeleteEvent | ||
On branch deletion and deletes all resources in a kubernetes cluster that have a | ||
label matching the repo name and are in a namespace matching the branch name. | ||
If there are no other objects with the given label key in the namespace, it also | ||
deletes the namespace and all remaining objects. | ||
|
||
### PushEvent | ||
On push events, k8s-webhook-handler will checkout `.ci/workflow.yaml` from the | ||
repo the push was and submit it to the k8s api with the following annotations | ||
added: | ||
|
||
- `k8s-webhook-handler.io/ref`: event.Ref | ||
- `k8s-webhook-handler.io/before`: event.Before | ||
- `k8s-webhook-handler.io/revision`: event.HeadCommit.ID | ||
- `k8s-webhook-handler.io/repo_name`: event.Repo.FullName | ||
- `k8s-webhook-handler.io/repo_url`: event.Repo.GitURL | ||
- `k8s-webhook-handler.io/repo_ssh`: event.Repo.SSHURL | ||
Create Kubernetes resources in response to (GitHub) webhooks! | ||
|
||
## How does it work? | ||
When the k8s-webhook-handler receives a webhook, it downloads a manifest | ||
(`.ci/workflow.yaml` by default) from the repository. | ||
|
||
For push events, it downloads the manifest from the given revision. Otherwise | ||
it's checked out from the repository's default branch. | ||
|
||
After that, it applies the manifest and adds the following annotations: | ||
|
||
- `k8s-webhook-handler.io/ref`: Git reference (e.g. `refs/heads/master`) | ||
- `k8s-webhook-handler.io/revision`: Revision of HEAD | ||
- `k8s-webhook-handler.io/repo_name`: Repo name including user | ||
(e.g. `itskoko/k8s-webhook-handler`) | ||
- `k8s-webhook-handler.io/repo_url`: git URL (e.g. | ||
`git://github.com/itskoko/k8s-webhook-handler.git`) | ||
- `k8s-webhook-handler.io/repo_ssh`: ssh URL (e.g. | ||
`[email protected]:itskoko/k8s-webhook-handler.git`) | ||
|
||
## Binaries | ||
- cmd/webhook is the actual webhook handling server | ||
- cmd/reconciler iterates over all k8s namespaces and deletes all objects that | ||
are labeled for which there is no remote branch anymore. | ||
|
||
## Usage | ||
Currently only github delete webhooks in json format are supported. | ||
Beside the manifests and templates in `deploy/`, a secret 'webhook-handler' with | ||
the following fields is expected: | ||
|
||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"flag" | ||
"net/http" | ||
"os" | ||
"regexp" | ||
"time" | ||
|
||
"github.com/go-kit/kit/log" | ||
|
@@ -15,75 +16,77 @@ import ( | |
) | ||
|
||
var ( | ||
listenAddr = flag.String("l", ":8080", "Address to listen on for webhook requests") | ||
sourceSelectorKey = flag.String("sk", "ci-source-repo", "Label key that identifies source repo") | ||
namespace = flag.String("ns", "ci", "Namespace to deploy workflows to") | ||
kubeconfig = flag.String("kubeconfig", "", "If set, use this kubeconfig to connect to kubernetes") | ||
dryRun = flag.Bool("dry", false, "Enable dry-run, print resources instead of deleting them") | ||
baseURL = flag.String("gh-base-url", "", "GitHub Enterprise: Base URL") | ||
uploadURL = flag.String("gh-upload-url", "", "GitHub Enterprise: Upload URL") | ||
gitAddress = flag.String("git", "[email protected]", "Git address") | ||
debug = flag.Bool("debug", false, "Enable debug logging") | ||
insecure = flag.Bool("insecure", false, "Allow omitting WEBHOOK_SECRET for testing") | ||
listenAddr = flag.String("l", ":8080", "Address to listen on for webhook requests") | ||
namespace = flag.String("ns", "ci", "Namespace to deploy workflows to") | ||
resoucePath = flag.String("p", ".ci/workflow.yaml", "Path to resource manifest in repository") | ||
kubeconfig = flag.String("kubeconfig", "", "If set, use this kubeconfig to connect to kubernetes") | ||
baseURL = flag.String("gh-base-url", "", "GitHub Enterprise: Base URL") | ||
uploadURL = flag.String("gh-upload-url", "", "GitHub Enterprise: Upload URL") | ||
gitAddress = flag.String("git", "[email protected]", "Git address") | ||
debug = flag.Bool("debug", false, "Enable debug logging") | ||
dryRun = flag.Bool("dry", false, "Dry run; Do not apply resouce manifest") | ||
insecure = flag.Bool("insecure", false, "Allow omitting WEBHOOK_SECRET for testing") | ||
ignoreRef = flag.String("ignore", "", "Ignore refs matching this regex") | ||
|
||
statsdAddress = flag.String("statsd.address", "localhost:8125", "Address to send statsd metrics to") | ||
statsdProto = flag.String("statsd.proto", "udp", "Protocol to use for statsd") | ||
statsdInterval = flag.Duration("statsd.interval", 30*time.Second, "statsd flush interval") | ||
|
||
logger = log.With(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), "caller", log.Caller(5)) | ||
) | ||
|
||
func fatal(err error) { | ||
func fatal(logger log.Logger, err error) { | ||
// FIXME: override caller, not add it | ||
logger := log.With(logger, "caller", log.Caller(4)) | ||
level.Error(logger).Log("msg", err.Error()) | ||
level.Error(logger).Log("msg", err.Error(), "caller", log.Caller(4)) | ||
os.Exit(1) | ||
} | ||
|
||
func main() { | ||
logger := log.With(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), "caller", log.Caller(5)) | ||
flag.Parse() | ||
githubSecret := os.Getenv("WEBHOOK_SECRET") | ||
if githubSecret == "" && !*insecure { | ||
fatal(errors.New("WEBHOOK_SECRET not set. Use -insecure to disable webhook verification")) | ||
fatal(logger, errors.New("WEBHOOK_SECRET not set. Use -insecure to disable webhook verification")) | ||
} | ||
if *debug { | ||
logger = level.NewFilter(logger, level.AllowAll()) | ||
} else { | ||
logger = level.NewFilter(logger, level.AllowInfo()) | ||
} | ||
|
||
kconfig, err := handler.NewKubernetesConfig(*kubeconfig) | ||
if err != nil { | ||
fatal(err) | ||
config := &handler.Config{ | ||
Namespace: *namespace, | ||
ResourcePath: *resoucePath, | ||
Secret: []byte(githubSecret), | ||
DryRun: *dryRun, | ||
} | ||
|
||
dh, err := handler.NewDeleteHandler(logger, kconfig, *sourceSelectorKey, *dryRun) | ||
dh.GitAddress = *gitAddress | ||
if err != nil { | ||
fatal(err) | ||
if *ignoreRef != "" { | ||
level.Debug(logger).Log("msg", "Parsing regex", "regex", *ignoreRef) | ||
regex, err := regexp.Compile(*ignoreRef) | ||
if err != nil { | ||
fatal(logger, err) | ||
} | ||
config.IgnoreRefRegex = regex | ||
} | ||
|
||
ghClient, err := handler.NewGitHubClient(os.Getenv("GITHUB_TOKEN"), *baseURL, *uploadURL) | ||
level.Info(logger).Log("msg", "Connecting to kubernetes", "kubeconfig", *kubeconfig) | ||
kClient, err := handler.NewKubernetesClient(*kubeconfig) | ||
if err != nil { | ||
fatal(err) | ||
fatal(logger, err) | ||
} | ||
|
||
ph, err := handler.NewPushHandler(logger, kconfig, ghClient) | ||
loader, err := handler.NewGithubLoader(os.Getenv("GITHUB_TOKEN"), *baseURL, *uploadURL) | ||
if err != nil { | ||
fatal(err) | ||
fatal(logger, err) | ||
} | ||
ph.Namespace = *namespace | ||
|
||
ticker := time.NewTicker(*statsdInterval) | ||
defer ticker.Stop() | ||
statsdClient := statsd.New("k8s-ci-purger.", logger) | ||
go statsdClient.SendLoop(ticker.C, *statsdProto, *statsdAddress) | ||
|
||
h := handler.NewGithubHookHandler([]byte(githubSecret), statsdClient) | ||
h.DeleteHandler = dh | ||
h.PushHandler = ph | ||
server := handler.NewGithubHookHandler(logger, config, kClient, loader, statsdClient) | ||
|
||
http.Handle("/", h) | ||
http.Handle("/", server) | ||
level.Info(logger).Log("msg", "Start listening", "addr", *listenAddr) | ||
fatal(http.ListenAndServe(*listenAddr, nil)) | ||
fatal(logger, http.ListenAndServe(*listenAddr, nil)) | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.