Files
zeekayandHanzo Dev 8894a6712c Let one binary serve any universe's KMS group
The group moved to kms.hanzo.ai/v1 here already, which is the right name — but
it is still a compile-time constant, so this binary can serve a Hanzo install
and no other. A Lux cluster needs kms.lux.cloud and there is no way to ask for
it short of a rebuild.

SetGroup makes it an argument behind --api-group, defaulting to kms.hanzo.ai,
the way hanzoai/operator and luxfi/operator already take theirs.

Registration moves out of init and into main, after the flag is read. The
SchemeBuilder captures its GroupVersion when the types register, so registering
first and setting the group afterwards leaves the scheme on one group while the
manager watches another. That controller starts clean, logs nothing, and never
sees an object — it reads as "no work to do" rather than as a misconfiguration,
so the ordering is asserted in a test rather than left to whoever edits main
next.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-01 08:06:56 -07:00

166 lines
5.8 KiB
Go

package main
import (
"flag"
"os"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
secretsv1 "github.com/hanzoai/kms-operator/api/v1"
kmsPushSecretController "github.com/hanzoai/kms-operator/controllers/kmspushsecret"
kmsSecretController "github.com/hanzoai/kms-operator/controllers/kmssecret"
"github.com/hanzoai/kms-operator/internal/bootstrap"
"github.com/hanzoai/kms-operator/packages/template"
//+kubebuilder:scaffold:imports
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
// apiWarningToDebug routes Kubernetes API-server warning headers to V(1).
// KMSSecret CRD schema drift makes the server emit an "unknown field spec.X"
// warning on every reconcile of every CR — thousands of lines/min, the
// operator's dominant log volume. Demoting keeps them at --zap-log-level=debug
// while the default (info) stream stays clean. Implements rest.WarningHandler.
type apiWarningToDebug struct{}
func (apiWarningToDebug) HandleWarningHeader(code int, _ string, message string) {
if code == 299 && message != "" {
ctrl.Log.WithName("kube-api-warning").V(1).Info(message)
}
}
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
// The KMS types register in main, AFTER --api-group is read. The
// SchemeBuilder captures its GroupVersion at registration, so registering
// here would pin the default and leave the scheme on one group while the
// manager watched another — a controller that starts clean and never sees
// an object.
//+kubebuilder:scaffold:scheme
}
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var namespace string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.StringVar(&namespace, "namespace", "", "Watch KMSSecrets scoped in the provided namespace only")
var apiGroup string
flag.StringVar(&apiGroup, "api-group", secretsv1.DefaultGroup,
"The KMS API group to serve: kms.<universe>, e.g. kms.hanzo.ai or kms.lux.cloud.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
opts := zap.Options{
Development: false,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
// Group first, then register: see the note in init.
secretsv1.SetGroup(apiGroup)
utilruntime.Must(secretsv1.AddToScheme(scheme))
setupLog.Info("serving KMS group", "group", secretsv1.GroupVersion.String())
ctrlOpts := ctrl.Options{
Scheme: scheme,
Metrics: metricsserver.Options{
BindAddress: metricsAddr,
},
WebhookServer: webhook.NewServer(webhook.Options{Port: 9443}),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "cf2b8c44.lux.network",
}
if namespace != "" {
ctrlOpts.Cache.DefaultNamespaces = map[string]cache.Config{
namespace: {},
}
}
cfg := ctrl.GetConfigOrDie()
cfg.WarningHandler = apiWarningToDebug{}
mgr, err := ctrl.NewManager(cfg, ctrlOpts)
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
template.InitializeTemplateFunctions()
// kms-consensus-authority reconciler — bootstrap the operator's own
// service identity, walk per-service mnemonic Secrets, and pull the
// upstream luxd validator set on the configured TTL. The same
// instance is wired into the KMSSecret reconciler so a CR addition
// auto-enrols its consumer's identity within one reconcile tick.
bootstrapCfg, err := bootstrap.LoadConfigFromEnv()
if err != nil {
setupLog.Error(err, "load bootstrap config")
os.Exit(1)
}
authorityRec := bootstrap.NewReconciler(mgr.GetClient(), ctrl.Log, bootstrapCfg, nil)
if err = (&kmsSecretController.KMSSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
IdentityEnsurer: authorityRec,
DefaultMnemonicNamespace: bootstrapCfg.DefaultMnemonicNamespace,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "KMSSecret")
os.Exit(1)
}
if err = (&kmsPushSecretController.KMSPushSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
IsNamespaceScoped: namespace != "",
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "KMSPushSecret")
os.Exit(1)
}
if err := mgr.Add(authorityRec); err != nil {
setupLog.Error(err, "unable to register kms-consensus-authority reconciler")
os.Exit(1)
}
//+kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}