Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b487bed
chore: add optional validatingAdmissionWebhook, and prepare for a sep…
sunib Jun 24, 2026
ec49609
chore: creating plan and code to capture all the mechanisms in a more…
sunib Jun 24, 2026
876ff60
chore: here is M2!
sunib Jun 24, 2026
e0b6369
chore: Interesting findings on the shallow body problem
sunib Jun 25, 2026
758a629
chore: small fixes
sunib Jun 25, 2026
2ccf07a
chore: more improvements in the tests
sunib Jun 25, 2026
7c41668
chore: first draft on architecture update
sunib Jun 25, 2026
8dc3aaa
chore: finishing for now
sunib Jun 25, 2026
e39ca06
test(mutationlab): capture rows 10 (owner-ref cascade) and 13 (conflict)
sunib Jun 25, 2026
77e349f
feat(watch): parallel watch-state stream behind --watch-state-stream
sunib Jun 25, 2026
6d87865
feat(watch): diff watch-derived vs audit-derived desired sets (Phase …
sunib Jun 25, 2026
2535109
test(mutationlab): capture rows 16+17 (watch resync + bookmark)
sunib Jun 25, 2026
d41cfab
chore: finishing the design
sunib Jun 25, 2026
38f59f8
feat: let's get all testing to Kubernetes 1.36
sunib Jun 25, 2026
6dec610
chore: reran all mutatons on k8s v1.36.1
sunib Jun 25, 2026
e612fc6
chore: improving watch-ingestion document.
sunib Jun 25, 2026
d0e25a2
chore: getting the design docs better
sunib Jun 25, 2026
b2d5bc7
feat: watch-first ingestion
sunib Jun 25, 2026
5122032
chore: next steps
sunib Jun 26, 2026
79a9fe6
docs: moving architecture along with the rewrite
sunib Jun 26, 2026
b524d83
chore: relisten to a watch when possible
sunib Jun 26, 2026
7e011db
chore: details on how Redis is needed
sunib Jun 26, 2026
d7bdb16
docs: created new plan, and hopefully found why the tests are so flaky
sunib Jun 26, 2026
acf73d5
chore: easier status and streamsready
sunib Jun 26, 2026
acaea33
chore: e2e flake preventions
sunib Jun 26, 2026
04aa391
chore: overall improvements, fixing things and cleaning docs
sunib Jun 26, 2026
915b524
feat: reworking metrics to new architecture
sunib Jun 26, 2026
261c440
feat(manifestanalyzer,git): refuse unsupported GitTarget folder conte…
sunib Jun 26, 2026
cb8d4b0
feat(watch): surface a refused GitTarget folder as a Blocked stream
sunib Jun 26, 2026
d09ab73
test(e2e): prove unsupported-folder refusal end to end (Test D) + docs
sunib Jun 26, 2026
893e17f
test(e2e): apply sops-age-key in unsupported-folder test so Ready can…
sunib Jun 26, 2026
f2773a8
docs: adding skills and working on status design
sunib Jun 27, 2026
92fa490
chore: improve status, support kstatus
sunib Jun 27, 2026
12f3aa2
chore: refining names and more explicit e2e test for status behaviour
sunib Jun 27, 2026
419ab33
docs: designing gittargetignore
sunib Jun 27, 2026
1c61666
feat: refuse weird files in GitTarget path, but do allow .gittargetig…
sunib Jun 27, 2026
b895ef3
chore: removing settings and preparing merge
sunib Jun 28, 2026
a555d9d
chore: Support CommitRequest with clearer status and non-attribution …
sunib Jun 28, 2026
52a14e8
fix: Allow GitTarget to respond quickly to changes in the tracked Git…
sunib Jun 29, 2026
73376dd
feat: --author-attribution={true|false} is now allowing you to enable…
sunib Jun 29, 2026
bc887e0
chore: resolving pr feedback
sunib Jun 29, 2026
40a06e0
chore: refine the redis queue names / behaviour
sunib Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
ctrlwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"
ctrladmission "sigs.k8s.io/controller-runtime/pkg/webhook/admission"

configbutleraiv1alpha2 "github.com/ConfigButler/gitops-reverser/api/v1alpha2"
"github.com/ConfigButler/gitops-reverser/internal/controller"
Expand All @@ -68,6 +70,7 @@ var (

const (
flagParseFailureExitCode = 2
defaultAdmissionWebhookPort = 9443
defaultAuditPort = 9444
defaultAuditMaxBodyBytes = int64(10 * 1024 * 1024)
defaultAuditReadTimeout = 15 * time.Second
Expand Down Expand Up @@ -119,7 +122,9 @@ func main() {
"metricsAddr", cfg.metricsAddr,
"metricsInsecure", cfg.metricsInsecure,
"auditAddress", buildAuditServerAddress(cfg.auditListenAddress, cfg.auditPort),
"auditInsecure", cfg.auditInsecure)
"auditInsecure", cfg.auditInsecure,
"admissionWebhookEnabled", cfg.admissionWebhookEnabled,
"admissionWebhookPort", cfg.admissionWebhookPort)
setupLog.Info("Sensitive resource policy", "resources", cfg.sensitiveResources.Entries())

// Initialize metrics
Expand All @@ -138,7 +143,7 @@ func main() {
)

// Manager
mgr := newManager(metricsServerOptions, cfg.probeAddr)
mgr := newManager(metricsServerOptions, cfg.probeAddr, cfg, tlsOpts)

// Expose build metadata on the metrics server so an operator can confirm a
// running pod is the build they expect (also logged at startup above).
Expand Down Expand Up @@ -414,6 +419,9 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "CommitRequest")
os.Exit(1)
}
if cfg.admissionWebhookEnabled {
setupAdmissionWebhooks(mgr)
}
// +kubebuilder:scaffold:builder

// Cert watchers
Expand Down Expand Up @@ -445,6 +453,11 @@ type appConfig struct {
metricsCertKey string
probeAddr string
metricsInsecure bool
admissionWebhookEnabled bool
admissionWebhookPort int
admissionWebhookCertPath string
admissionWebhookCertName string
admissionWebhookCertKey string
enableHTTP2 bool
auditListenAddress string
auditPort int
Expand Down Expand Up @@ -496,6 +509,18 @@ func parseFlagsWithArgs(fs *flag.FlagSet, args []string) (appConfig, error) {
fs.StringVar(&cfg.probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
fs.BoolVar(&cfg.metricsInsecure, "metrics-insecure", false,
"If set, the metrics endpoint is served via HTTP instead of HTTPS.")
fs.BoolVar(&cfg.admissionWebhookEnabled, "admission-webhook-enabled", false,
"If set, serve the validating admission webhook endpoint.")
fs.IntVar(&cfg.admissionWebhookPort, "admission-webhook-port", defaultAdmissionWebhookPort,
"Port for the validating admission webhook HTTPS server.")
bindServerCertFlags(
fs,
"admission-webhook",
"validating admission webhook TLS",
&cfg.admissionWebhookCertPath,
&cfg.admissionWebhookCertName,
&cfg.admissionWebhookCertKey,
)
bindServerCertFlags(
fs,
"metrics",
Expand Down Expand Up @@ -594,6 +619,9 @@ func parseFlagsWithArgs(fs *flag.FlagSet, args []string) (appConfig, error) {
if err := validateAuditConfig(cfg); err != nil {
return appConfig{}, err
}
if err := validateAdmissionWebhookConfig(cfg); err != nil {
return appConfig{}, err
}

bufferQuantity, err := resource.ParseQuantity(branchBufferMaxBytesFlag)
if err != nil {
Expand Down Expand Up @@ -673,6 +701,19 @@ func validateAuditConfig(cfg appConfig) error {
return nil
}

func validateAdmissionWebhookConfig(cfg appConfig) error {
if !cfg.admissionWebhookEnabled {
return nil
}
if cfg.admissionWebhookPort <= 0 {
return fmt.Errorf("admission-webhook-port must be > 0, got %d", cfg.admissionWebhookPort)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
if strings.TrimSpace(cfg.admissionWebhookCertPath) == "" {
return errors.New("admission-webhook-cert-path is required when admission webhook is enabled")
}
return nil
}

// splitCSV splits a comma-separated flag value into trimmed, non-empty entries (nil when blank).
func splitCSV(s string) []string {
if strings.TrimSpace(s) == "" {
Expand Down Expand Up @@ -950,11 +991,24 @@ func loadCertPoolFromPEMFile(path string) (*x509.CertPool, error) {
func newManager(
metricsOptions metricsserver.Options,
probeAddr string,
cfg appConfig,
baseTLS []func(*tls.Config),
) ctrl.Manager {
var webhookServer ctrlwebhook.Server
if cfg.admissionWebhookEnabled {
webhookServer = ctrlwebhook.NewServer(ctrlwebhook.Options{
Port: cfg.admissionWebhookPort,
CertDir: cfg.admissionWebhookCertPath,
CertName: cfg.admissionWebhookCertName,
KeyName: cfg.admissionWebhookCertKey,
TLSOpts: baseTLS,
})
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: metricsOptions,
HealthProbeBindAddress: probeAddr,
WebhookServer: webhookServer,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
Expand All @@ -963,6 +1017,13 @@ func newManager(
return mgr
}

func setupAdmissionWebhooks(mgr ctrl.Manager) {
mgr.GetWebhookServer().Register(
webhookhandler.ValidateAdmissionWebhookPath,
&ctrladmission.Webhook{Handler: webhookhandler.AdmissionAllowHandler{}},
)
}

// addCertWatchersToManager attaches optional certificate watchers to the manager.
func addCertWatchersToManager(
mgr ctrl.Manager,
Expand Down
30 changes: 30 additions & 0 deletions cmd/main_audit_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func TestParseFlagsWithArgs_Defaults(t *testing.T) {
require.NoError(t, err)

assert.False(t, cfg.metricsInsecure)
assert.False(t, cfg.admissionWebhookEnabled)
assert.Equal(t, 9443, cfg.admissionWebhookPort)
assert.False(t, cfg.auditInsecure)
assert.Equal(t, "0.0.0.0", cfg.auditListenAddress)
assert.Equal(t, 9444, cfg.auditPort)
Expand All @@ -64,6 +66,26 @@ func TestParseFlagsWithArgs_Defaults(t *testing.T) {
assert.Equal(t, []string{"secrets"}, cfg.sensitiveResources.Entries())
}

func TestParseFlagsWithArgs_AdmissionWebhookValues(t *testing.T) {
fs := flag.NewFlagSet("test-admission-webhook", flag.ContinueOnError)
args := []string{
"--admission-webhook-enabled",
"--admission-webhook-port=9445",
"--admission-webhook-cert-path=/tmp/admission-certs",
"--admission-webhook-cert-name=cert.pem",
"--admission-webhook-cert-key=key.pem",
}

cfg, err := parseFlagsWithArgs(fs, args)
require.NoError(t, err)

assert.True(t, cfg.admissionWebhookEnabled)
assert.Equal(t, 9445, cfg.admissionWebhookPort)
assert.Equal(t, "/tmp/admission-certs", cfg.admissionWebhookCertPath)
assert.Equal(t, "cert.pem", cfg.admissionWebhookCertName)
assert.Equal(t, "key.pem", cfg.admissionWebhookCertKey)
}

func TestParseFlagsWithArgs_AuditUnsecure(t *testing.T) {
fs := flag.NewFlagSet("test-audit-insecure", flag.ContinueOnError)
args := []string{
Expand Down Expand Up @@ -191,6 +213,14 @@ func TestParseFlagsWithArgs_InvalidAuditSettings(t *testing.T) {
name: "invalid sensitive resource",
args: []string{"--additional-sensitive-resources=example.io/v1/credentials"},
},
{
name: "invalid admission webhook port",
args: []string{"--admission-webhook-enabled", "--admission-webhook-port=0"},
},
{
name: "missing admission webhook cert path",
args: []string{"--admission-webhook-enabled", "--admission-webhook-cert-path="},
},
}

for _, tt := range tests {
Expand Down
Loading