main.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. Copyright 2026 LocoStack.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package main
  14. import (
  15. "crypto/tls"
  16. "flag"
  17. "os"
  18. // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
  19. // to ensure that exec-entrypoint and run can make use of them.
  20. _ "k8s.io/client-go/plugin/pkg/client/auth"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  23. clientgoscheme "k8s.io/client-go/kubernetes/scheme"
  24. ctrl "sigs.k8s.io/controller-runtime"
  25. "sigs.k8s.io/controller-runtime/pkg/healthz"
  26. "sigs.k8s.io/controller-runtime/pkg/log/zap"
  27. "sigs.k8s.io/controller-runtime/pkg/metrics/filters"
  28. metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
  29. "sigs.k8s.io/controller-runtime/pkg/webhook"
  30. "github.com/LocoStack/loco-operator/api/v1alpha1"
  31. "github.com/LocoStack/loco-operator/internal/controller"
  32. // +kubebuilder:scaffold:imports
  33. )
  34. var (
  35. scheme = runtime.NewScheme()
  36. setupLog = ctrl.Log.WithName("setup")
  37. )
  38. func init() {
  39. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  40. utilruntime.Must(v1alpha1.AddToScheme(scheme))
  41. // +kubebuilder:scaffold:scheme
  42. }
  43. // nolint:gocyclo
  44. func main() {
  45. var metricsAddr string
  46. var metricsCertPath, metricsCertName, metricsCertKey string
  47. var webhookCertPath, webhookCertName, webhookCertKey string
  48. var enableLeaderElection bool
  49. var probeAddr string
  50. var secureMetrics bool
  51. var enableHTTP2 bool
  52. var tlsOpts []func(*tls.Config)
  53. flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
  54. "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
  55. flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
  56. flag.BoolVar(&enableLeaderElection, "leader-elect", false,
  57. "Enable leader election for controller manager. "+
  58. "Enabling this will ensure there is only one active controller manager.")
  59. flag.BoolVar(&secureMetrics, "metrics-secure", true,
  60. "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
  61. flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
  62. flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
  63. flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
  64. flag.StringVar(&metricsCertPath, "metrics-cert-path", "",
  65. "The directory that contains the metrics server certificate.")
  66. flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.")
  67. flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.")
  68. flag.BoolVar(&enableHTTP2, "enable-http2", false,
  69. "If set, HTTP/2 will be enabled for the metrics and webhook servers")
  70. opts := zap.Options{
  71. Development: true,
  72. }
  73. opts.BindFlags(flag.CommandLine)
  74. flag.Parse()
  75. ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
  76. // if the enable-http2 flag is false (the default), http/2 should be disabled
  77. // due to its vulnerabilities. More specifically, disabling http/2 will
  78. // prevent from being vulnerable to the HTTP/2 Stream Cancellation and
  79. // Rapid Reset CVEs. For more information see:
  80. // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
  81. // - https://github.com/advisories/GHSA-4374-p667-p6c8
  82. disableHTTP2 := func(c *tls.Config) {
  83. setupLog.Info("Disabling HTTP/2")
  84. c.NextProtos = []string{"http/1.1"}
  85. }
  86. if !enableHTTP2 {
  87. tlsOpts = append(tlsOpts, disableHTTP2)
  88. }
  89. // Initial webhook TLS options
  90. webhookTLSOpts := tlsOpts
  91. webhookServerOptions := webhook.Options{
  92. TLSOpts: webhookTLSOpts,
  93. }
  94. if len(webhookCertPath) > 0 {
  95. setupLog.Info("Initializing webhook certificate watcher using provided certificates",
  96. "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
  97. webhookServerOptions.CertDir = webhookCertPath
  98. webhookServerOptions.CertName = webhookCertName
  99. webhookServerOptions.KeyName = webhookCertKey
  100. }
  101. webhookServer := webhook.NewServer(webhookServerOptions)
  102. // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
  103. // More info:
  104. // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.3/pkg/metrics/server
  105. // - https://book.kubebuilder.io/reference/metrics.html
  106. metricsServerOptions := metricsserver.Options{
  107. BindAddress: metricsAddr,
  108. SecureServing: secureMetrics,
  109. TLSOpts: tlsOpts,
  110. }
  111. if secureMetrics {
  112. // FilterProvider is used to protect the metrics endpoint with authn/authz.
  113. // These configurations ensure that only authorized users and service accounts
  114. // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
  115. // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.3/pkg/metrics/filters#WithAuthenticationAndAuthorization
  116. metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
  117. }
  118. // If the certificate is not specified, controller-runtime will automatically
  119. // generate self-signed certificates for the metrics server. While convenient for development and testing,
  120. // this setup is not recommended for production.
  121. //
  122. // TODO(user): If you enable certManager, uncomment the following lines:
  123. // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates
  124. // managed by cert-manager for the metrics server.
  125. // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification.
  126. if len(metricsCertPath) > 0 {
  127. setupLog.Info("Initializing metrics certificate watcher using provided certificates",
  128. "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey)
  129. metricsServerOptions.CertDir = metricsCertPath
  130. metricsServerOptions.CertName = metricsCertName
  131. metricsServerOptions.KeyName = metricsCertKey
  132. }
  133. mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
  134. Scheme: scheme,
  135. Metrics: metricsServerOptions,
  136. WebhookServer: webhookServer,
  137. HealthProbeBindAddress: probeAddr,
  138. LeaderElection: enableLeaderElection,
  139. LeaderElectionID: "3fcced06.locostack.com",
  140. // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
  141. // when the Manager ends. This requires the binary to immediately end when the
  142. // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
  143. // speeds up voluntary leader transitions as the new leader don't have to wait
  144. // LeaseDuration time first.
  145. //
  146. // In the default scaffold provided, the program ends immediately after
  147. // the manager stops, so would be fine to enable this option. However,
  148. // if you are doing or is intended to do any operation such as perform cleanups
  149. // after the manager stops then its usage might be unsafe.
  150. // LeaderElectionReleaseOnCancel: true,
  151. })
  152. if err != nil {
  153. setupLog.Error(err, "Failed to start manager")
  154. os.Exit(1)
  155. }
  156. if err := (&controller.ComponentReconciler{
  157. Client: mgr.GetClient(),
  158. Scheme: mgr.GetScheme(),
  159. }).SetupWithManager(mgr); err != nil {
  160. setupLog.Error(err, "Failed to create controller", "controller", "Component")
  161. os.Exit(1)
  162. }
  163. if err := (&controller.StackReconciler{
  164. Client: mgr.GetClient(),
  165. Scheme: mgr.GetScheme(),
  166. }).SetupWithManager(mgr); err != nil {
  167. setupLog.Error(err, "Failed to create controller", "controller", "Stack")
  168. os.Exit(1)
  169. }
  170. if err := (&controller.ExternalModelReconciler{
  171. Client: mgr.GetClient(),
  172. Scheme: mgr.GetScheme(),
  173. }).SetupWithManager(mgr); err != nil {
  174. setupLog.Error(err, "Failed to create controller", "controller", "ExternalModel")
  175. os.Exit(1)
  176. }
  177. // +kubebuilder:scaffold:builder
  178. if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
  179. setupLog.Error(err, "Failed to set up health check")
  180. os.Exit(1)
  181. }
  182. if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
  183. setupLog.Error(err, "Failed to set up ready check")
  184. os.Exit(1)
  185. }
  186. setupLog.Info("Starting manager")
  187. if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
  188. setupLog.Error(err, "Failed to run manager")
  189. os.Exit(1)
  190. }
  191. }