Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
92 changes: 51 additions & 41 deletions backend/cmd/headlamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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/([^/]+)/.*`)
Expand Down
6 changes: 3 additions & 3 deletions backend/pkg/kubeconfig/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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{}

Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions backend/pkg/kubeconfig/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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 {
Expand Down
Loading