Files
kubesphere/pkg/config/config.go
2025-04-30 15:53:51 +08:00

165 lines
6.4 KiB
Go

/*
* Copyright 2024 the KubeSphere Authors.
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package config
import (
"fmt"
"strings"
"sync"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
"kubesphere.io/utils/s3"
"kubesphere.io/kubesphere/pkg/apiserver/auditing"
"kubesphere.io/kubesphere/pkg/apiserver/authentication"
"kubesphere.io/kubesphere/pkg/apiserver/authorization"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/controller/options"
"kubesphere.io/kubesphere/pkg/models/composedapp"
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
"kubesphere.io/kubesphere/pkg/models/terminal"
"kubesphere.io/kubesphere/pkg/multicluster"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
)
// Package config saves configuration for running KubeSphere components
//
// Config can be configured from command line flags and configuration file.
// Command line flags hold higher priority than configuration file. But if
// component Endpoint/Host/APIServer was left empty, all of that component
// command line flags will be ignored, use configuration file instead.
// For example, we have configuration file
//
// mysql:
// host: mysql.kubesphere-system.svc
// username: root
// password: password
//
// At the same time, have command line flags like following:
//
// --mysql-host mysql.openpitrix-system.svc --mysql-username king --mysql-password 1234
//
// We will use `king:1234@mysql.openpitrix-system.svc` from command line flags rather
// than `root:password@mysql.kubesphere-system.svc` from configuration file,
// cause command line has higher priority. But if command line flags like following:
//
// --mysql-username root --mysql-password password
//
// we will `root:password@mysql.kubesphere-system.svc` as input, cause
// mysql-host is missing in command line flags, all other mysql command line flags
// will be ignored.
var (
// singleton instance of config package
_config = defaultConfig()
)
const (
// DefaultConfigurationName is the default name of configuration
defaultConfigurationName = "kubesphere"
// DefaultConfigurationPath the default location of the configuration file
defaultConfigurationPath = "/etc/kubesphere"
envPrefix = defaultConfigurationName
)
type config struct {
cfg *Config
loadOnce sync.Once
}
func (c *config) loadFromDisk() (*Config, error) {
var err error
c.loadOnce.Do(func() {
if err = viper.ReadInConfig(); err != nil {
return
}
err = viper.Unmarshal(c.cfg)
})
return c.cfg, err
}
func defaultConfig() *config {
viper.SetConfigName(defaultConfigurationName)
viper.AddConfigPath(defaultConfigurationPath)
// Load from current working directory, only used for debugging
viper.AddConfigPath(".")
// Load from Environment variables
viper.SetEnvPrefix(envPrefix)
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
return &config{
cfg: New(),
loadOnce: sync.Once{},
}
}
// Config defines everything needed for apiserver to deal with external services
type Config struct {
KubernetesOptions *k8s.Options `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"`
CacheOptions *cache.Options `json:"cache,omitempty" yaml:"cache,omitempty" mapstructure:"cache"`
AuthenticationOptions *authentication.Options `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"`
AuthorizationOptions *authorization.Options `json:"authorization,omitempty" yaml:"authorization,omitempty" mapstructure:"authorization"`
MultiClusterOptions *multicluster.Options `json:"multicluster,omitempty" yaml:"multicluster,omitempty" mapstructure:"multicluster"`
AuditingOptions *auditing.Options `json:"auditing,omitempty" yaml:"auditing,omitempty" mapstructure:"auditing"`
KubeconfigOptions *kubeconfig.Options `json:"kubeconfig,omitempty" yaml:"kubeconfig,omitempty" mapstructure:"kubeconfig"`
TerminalOptions *terminal.Options `json:"terminal,omitempty" yaml:"terminal,omitempty" mapstructure:"terminal"`
HelmExecutorOptions *options.HelmExecutorOptions `json:"helmExecutor,omitempty" yaml:"helmExecutor,omitempty" mapstructure:"helmExecutor"`
ExtensionOptions *options.ExtensionOptions `json:"extension,omitempty" yaml:"extension,omitempty" mapstructure:"extension"`
S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"`
KubeSphereOptions *options.KubeSphereOptions `json:"kubesphere,omitempty" yaml:"kubesphere,omitempty" mapstructure:"kubesphere"`
ComposedAppOptions *composedapp.Options `json:"composedApp,omitempty" yaml:"composedApp,omitempty" mapstructure:"composedApp"`
ExperimentalOptions *ExperimentalOptions `json:"experimental,omitempty" yaml:"experimental,omitempty" mapstructure:"experimental"`
}
// New config creates a default non-empty Config
func New() *Config {
return &Config{
KubernetesOptions: k8s.NewKubernetesOptions(),
CacheOptions: cache.NewCacheOptions(),
AuthenticationOptions: authentication.NewOptions(),
AuthorizationOptions: authorization.NewOptions(),
MultiClusterOptions: multicluster.NewOptions(),
TerminalOptions: terminal.NewOptions(),
KubeconfigOptions: kubeconfig.NewOptions(),
AuditingOptions: auditing.NewAuditingOptions(),
HelmExecutorOptions: options.NewHelmExecutorOptions(),
ExtensionOptions: options.NewExtensionOptions(),
S3Options: s3.NewS3Options(),
KubeSphereOptions: options.NewKubeSphereOptions(),
ComposedAppOptions: composedapp.NewOptions(),
ExperimentalOptions: NewExperimentalOptions(),
}
}
// TryLoadFromDisk loads configuration from default location after server startup
// return nil error if configuration file not exists
func TryLoadFromDisk() (*Config, error) {
return _config.loadFromDisk()
}
// FromConfigMap returns KubeSphere running config by the given ConfigMap.
func FromConfigMap(cm *corev1.ConfigMap) (*Config, error) {
c := &Config{}
value, ok := cm.Data[constants.KubeSphereConfigMapDataKey]
if !ok {
return nil, fmt.Errorf("failed to get configmap kubesphere.yaml value")
}
if err := yaml.Unmarshal([]byte(value), c); err != nil {
return nil, fmt.Errorf("failed to unmarshal value from configmap. err: %s", err)
}
return c, nil
}