diff --git a/backend/cmd/headlamp.go b/backend/cmd/headlamp.go index 36dc659ea59..3c3e728ea8d 100644 --- a/backend/cmd/headlamp.go +++ b/backend/cmd/headlamp.go @@ -400,57 +400,28 @@ func addPluginListRoute(config *HeadlampConfig, r *mux.Router) { }).Methods("GET") } -//nolint:gocognit,funlen,gocyclo -func createHeadlampHandler(config *HeadlampConfig) http.Handler { - kubeConfigPath := config.KubeConfigPath - - config.StaticPluginDir = os.Getenv("HEADLAMP_STATIC_PLUGINS_DIR") - - logger.Log(logger.LevelInfo, nil, nil, "Creating Headlamp handler") - logger.Log(logger.LevelInfo, nil, nil, "Listen address: "+fmt.Sprintf("%s:%d", config.ListenAddr, config.Port)) - logger.Log(logger.LevelInfo, nil, nil, "Kubeconfig path: "+kubeConfigPath) - logger.Log(logger.LevelInfo, nil, nil, "Static plugin dir: "+config.StaticPluginDir) - logger.Log(logger.LevelInfo, nil, nil, "Plugins dir: "+config.PluginDir) - logger.Log(logger.LevelInfo, nil, nil, "Dynamic clusters support: "+fmt.Sprint(config.EnableDynamicClusters)) - logger.Log(logger.LevelInfo, nil, nil, "Helm support: "+fmt.Sprint(config.EnableHelm)) - logger.Log(logger.LevelInfo, nil, nil, "Proxy URLs: "+fmt.Sprint(config.ProxyURLs)) - +// setupPluginHandlers initializes the plugin cache and sets up handlers for plugins. +func setupPluginHandlers(config *HeadlampConfig, skipFunc kubeconfig.ShouldBeSkippedFunc) { plugins.PopulatePluginsCache(config.StaticPluginDir, config.PluginDir, config.cache) - skipFunc := kubeconfig.SkipKubeContextInCommaSeparatedString(config.SkippedKubeContexts) - if !config.UseInCluster || config.WatchPluginsChanges { - // in-cluster mode is unlikely to want reloading plugins. pluginEventChan := make(chan string) go plugins.Watch(config.PluginDir, pluginEventChan) go plugins.HandlePluginEvents(config.StaticPluginDir, config.PluginDir, pluginEventChan, config.cache) - // in-cluster mode is unlikely to want reloading kubeconfig. - go kubeconfig.LoadAndWatchFiles(config.KubeConfigStore, kubeConfigPath, kubeconfig.KubeConfig, skipFunc) + go kubeconfig.LoadAndWatchFiles(config.KubeConfigStore, config.KubeConfigPath, kubeconfig.KubeConfig, skipFunc) } +} - // In-cluster - if config.UseInCluster { - context, err := kubeconfig.GetInClusterContext(config.oidcIdpIssuerURL, - config.oidcClientID, config.oidcClientSecret, - strings.Join(config.oidcScopes, ","), - config.oidcSkipTLSVerify, - config.oidcCACert) - if err != nil { - logger.Log(logger.LevelError, nil, err, "Failed to get in-cluster context") - } - - context.Source = kubeconfig.InCluster +//nolint:gocognit,funlen,gocyclo +func createHeadlampHandler(config *HeadlampConfig) http.Handler { + kubeConfigPath := config.KubeConfigPath - err = context.SetupProxy() - if err != nil { - logger.Log(logger.LevelError, nil, err, "Failed to setup proxy for in-cluster context") - } + config.StaticPluginDir = os.Getenv("HEADLAMP_STATIC_PLUGINS_DIR") - err = config.KubeConfigStore.AddContext(context) - if err != nil { - logger.Log(logger.LevelError, nil, err, "Failed to add in-cluster context") - } - } + logStartupInfo(config) + skipFunc := kubeconfig.SkipKubeContextInCommaSeparatedString(config.SkippedKubeContexts) + setupPluginHandlers(config, skipFunc) + setupInClusterContext(config) if config.StaticDir != "" { baseURLReplace(config.StaticDir, config.BaseURL) @@ -838,6 +809,45 @@ func createHeadlampHandler(config *HeadlampConfig) http.Handler { return r } +// logStartupInfo logs config information of the Headlamp server. +func logStartupInfo(config *HeadlampConfig) { + logger.Log(logger.LevelInfo, nil, nil, "Creating Headlamp handler") + logger.Log(logger.LevelInfo, nil, nil, "Listen address: "+fmt.Sprintf("%s:%d", config.ListenAddr, config.Port)) + logger.Log(logger.LevelInfo, nil, nil, "Kubeconfig path: "+config.KubeConfigPath) + logger.Log(logger.LevelInfo, nil, nil, "Static plugin dir: "+config.StaticPluginDir) + logger.Log(logger.LevelInfo, nil, nil, "Plugins dir: "+config.PluginDir) + logger.Log(logger.LevelInfo, nil, nil, "Dynamic clusters support: "+fmt.Sprint(config.EnableDynamicClusters)) + logger.Log(logger.LevelInfo, nil, nil, "Helm support: "+fmt.Sprint(config.EnableHelm)) + logger.Log(logger.LevelInfo, nil, nil, "Proxy URLs: "+fmt.Sprint(config.ProxyURLs)) +} + +// setupInClusterContext prepares in-cluster context with OIDC authentication, +// sets up a proxy and adds it to the kubeconfig store. +func setupInClusterContext(config *HeadlampConfig) { + if config.UseInCluster { + context, err := kubeconfig.GetInClusterContext(config.oidcIdpIssuerURL, + config.oidcClientID, config.oidcClientSecret, + strings.Join(config.oidcScopes, ","), + config.oidcSkipTLSVerify, + config.oidcCACert) + if err != nil { + logger.Log(logger.LevelError, nil, err, "Failed to get in-cluster context") + } + + context.Source = kubeconfig.InCluster + + err = context.SetupProxy() + if err != nil { + logger.Log(logger.LevelError, nil, err, "Failed to setup proxy for in-cluster context") + } + + err = config.KubeConfigStore.AddContext(context) + if err != nil { + logger.Log(logger.LevelError, nil, err, "Failed to add in-cluster context") + } + } +} + func parseClusterAndToken(r *http.Request) (string, string) { cluster := "" re := regexp.MustCompile(`^/clusters/([^/]+)/.*`) diff --git a/backend/pkg/kubeconfig/kubeconfig.go b/backend/pkg/kubeconfig/kubeconfig.go index e9cf13796a7..6c58b53026b 100644 --- a/backend/pkg/kubeconfig/kubeconfig.go +++ b/backend/pkg/kubeconfig/kubeconfig.go @@ -969,7 +969,7 @@ func GetInClusterContext(oidcIssuerURL string, // Func type for context filter, if the func return true, // the context will be skipped otherwise the context will be used. -type shouldBeSkippedFunc = func(kubeContext Context) bool +type ShouldBeSkippedFunc = func(kubeContext Context) bool // AllowAllKubeContext will keep all contexts. func AllowAllKubeContext(_ Context) bool { @@ -980,7 +980,7 @@ func AllowAllKubeContext(_ Context) bool { // whose names are in the given comma-separated string. // For example, if pass the "a,b" for blackKubeContextNameStr, // the contexts named "a" and "b" will be skipped. -func SkipKubeContextInCommaSeparatedString(blackKubeContextNameStr string) shouldBeSkippedFunc { +func SkipKubeContextInCommaSeparatedString(blackKubeContextNameStr string) ShouldBeSkippedFunc { blackKubeContextNameList := strings.Split(blackKubeContextNameStr, ",") blackKubeContextNameMap := map[string]bool{} @@ -999,7 +999,7 @@ func SkipKubeContextInCommaSeparatedString(blackKubeContextNameStr string) shoul // Note: No need to remove contexts from the store, since // adding a context with the same name will overwrite the old one. func LoadAndStoreKubeConfigs(kubeConfigStore ContextStore, kubeConfigs string, source int, - ignoreFunc shouldBeSkippedFunc, + ignoreFunc ShouldBeSkippedFunc, ) error { var errs []error //nolint:prealloc diff --git a/backend/pkg/kubeconfig/watcher.go b/backend/pkg/kubeconfig/watcher.go index adb31cd6ab9..6200a00ac4d 100644 --- a/backend/pkg/kubeconfig/watcher.go +++ b/backend/pkg/kubeconfig/watcher.go @@ -14,7 +14,7 @@ import ( const watchInterval = 10 * time.Second // LoadAndWatchFiles loads kubeconfig files and watches them for changes. -func LoadAndWatchFiles(kubeConfigStore ContextStore, paths string, source int, ignoreFunc shouldBeSkippedFunc) { +func LoadAndWatchFiles(kubeConfigStore ContextStore, paths string, source int, ignoreFunc ShouldBeSkippedFunc) { // create ticker ticker := time.NewTicker(watchInterval) @@ -106,7 +106,7 @@ func addFilesToWatcher(watcher *fsnotify.Watcher, paths []string) { } // syncContexts synchronizes the contexts in the store with the ones in the kubeconfig files. -func syncContexts(kubeConfigStore ContextStore, paths string, source int, ignoreFunc shouldBeSkippedFunc) error { +func syncContexts(kubeConfigStore ContextStore, paths string, source int, ignoreFunc ShouldBeSkippedFunc) error { // First read all kubeconfig files to get new contexts newContexts, _, err := LoadContextsFromMultipleFiles(paths, source) if err != nil {