Compare commits

...

14 Commits

Author SHA1 Message Date
Ayush Kumar 4470a6d414
Merge 7cea5962d7 into f196d66b5e 2025-10-20 17:36:22 +05:30
Ayush Kumar 7cea5962d7 refactor: Standardize flag formatting in TestCoreOptions_AllConfigModulesHaveFlags
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 17:36:11 +05:30
Ayush Kumar 13289a9f54 feat: Add backward compatibility notes to sync methods and enhance CLI override tests for configuration values
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 411a68a454 feat: Enhance CUE configuration flags with detailed descriptions and add comprehensive tests for core options
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 2fbc8bb5be refactor: Remove unnecessary line in ControllerConfig and update test assertions for CUE options
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 9fe67e70ec refactor: Remove ConfigureKLog method and apply klog settings directly in server run function
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 4d94f90c9c refactor: Update ControllerConfig to embed Args struct and simplify flag registration
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 1aa47b47d6 feat: Implement sync methods for configuration values across various modules
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 54b3c9982a feat: Introduce ControllerConfig for improved controller configuration management
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 9c6dbaef11 refactor: Clean up legacy field synchronization and improve configuration handling in CoreOptions
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 1b67bf9df0 chore: Add missing newlines at the end of multiple configuration files
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar 3612bedba3 chore: Remove debug logging for webhook configuration in server command
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar c5a86bed38 feat: Sync config module values to legacy fields and add debug logging for webhook configuration
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
Ayush Kumar d8c98f0910 feat: Introduce comprehensive configuration management for KubeVela
- Added multiple configuration files under `cmd/core/app/config` to encapsulate various aspects of KubeVela's functionality, including:
  - Feature gates
  - Logging (KLog)
  - Kubernetes API client settings
  - Multi-cluster management
  - OAM-specific configurations
  - Observability settings (metrics and logging)
  - Performance optimizations
  - Profiling settings
  - Reconciliation settings
  - Resource management
  - Server-level configurations
  - Sharding configurations
  - Webhook settings
  - Workflow engine configurations

- Refactored `CoreOptions` to utilize the new configuration modules, ensuring a clean delegation pattern for flag registration.
- Updated tests to validate the new configuration structure and ensure backward compatibility with legacy fields.

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
2025-10-20 16:58:59 +05:30
23 changed files with 1971 additions and 244 deletions

View File

@ -0,0 +1,38 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
standardcontroller "github.com/oam-dev/kubevela/pkg/controller"
)
// AdmissionConfig contains admission control configuration.
type AdmissionConfig struct {
// Fields will be populated based on what standardcontroller.AddAdmissionFlags sets
}
// NewAdmissionConfig creates a new AdmissionConfig with defaults.
func NewAdmissionConfig() *AdmissionConfig {
return &AdmissionConfig{}
}
// AddFlags registers admission configuration flags.
func (c *AdmissionConfig) AddFlags(fs *pflag.FlagSet) {
standardcontroller.AddAdmissionFlags(fs)
}

View File

@ -0,0 +1,56 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"time"
"github.com/spf13/pflag"
commonconfig "github.com/oam-dev/kubevela/pkg/controller/common"
)
// ApplicationConfig contains application-specific configuration.
type ApplicationConfig struct {
ReSyncPeriod time.Duration
}
// NewApplicationConfig creates a new ApplicationConfig with defaults.
func NewApplicationConfig() *ApplicationConfig {
return &ApplicationConfig{
ReSyncPeriod: commonconfig.ApplicationReSyncPeriod,
}
}
// AddFlags registers application configuration flags.
func (c *ApplicationConfig) AddFlags(fs *pflag.FlagSet) {
fs.DurationVar(&c.ReSyncPeriod,
"application-re-sync-period",
c.ReSyncPeriod,
"Re-sync period for application to re-sync, also known as the state-keep interval.")
}
// SyncToApplicationGlobals syncs the parsed configuration values to application package global variables.
// This should be called after flag parsing to ensure the application controller uses the configured values.
//
// NOTE: This method exists for backward compatibility with legacy code that depends on global
// variables in the commonconfig package. Ideally, configuration should be injected rather than using globals.
//
// The flow is: CLI flags -> ApplicationConfig struct fields -> commonconfig globals (via this method)
func (c *ApplicationConfig) SyncToApplicationGlobals() {
commonconfig.ApplicationReSyncPeriod = c.ReSyncPeriod
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
pkgclient "github.com/kubevela/pkg/controller/client"
"github.com/spf13/pflag"
)
// ClientConfig contains controller client configuration.
// This wraps the external package's client configuration flags.
type ClientConfig struct {
// Note: The actual configuration is managed by the pkgclient package
// This is a wrapper to maintain consistency with our config pattern
}
// NewClientConfig creates a new ClientConfig with defaults.
func NewClientConfig() *ClientConfig {
return &ClientConfig{}
}
// AddFlags registers client configuration flags.
// Delegates to the external package's flag registration.
func (c *ClientConfig) AddFlags(fs *pflag.FlagSet) {
pkgclient.AddTimeoutControllerClientFlags(fs)
}

View File

@ -0,0 +1,67 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
oamcontroller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
)
// ControllerConfig wraps the oamcontroller.Args configuration.
// While this appears to duplicate the Args struct, it serves as the new home for
// controller flag registration after the AddFlags method was moved here from
// the oamcontroller package during refactoring.
type ControllerConfig struct {
// Embed the existing Args struct to reuse its fields
oamcontroller.Args
}
// NewControllerConfig creates a new ControllerConfig with defaults.
func NewControllerConfig() *ControllerConfig {
return &ControllerConfig{
Args: oamcontroller.Args{
RevisionLimit: 50,
AppRevisionLimit: 10,
DefRevisionLimit: 20,
AutoGenWorkloadDefinition: true,
ConcurrentReconciles: 4,
IgnoreAppWithoutControllerRequirement: false,
IgnoreDefinitionWithoutControllerRequirement: false,
},
}
}
// AddFlags registers controller configuration flags.
// This method was moved here from oamcontroller.Args during refactoring
// to centralize configuration management.
func (c *ControllerConfig) AddFlags(fs *pflag.FlagSet) {
fs.IntVar(&c.RevisionLimit, "revision-limit", c.RevisionLimit,
"RevisionLimit is the maximum number of revisions that will be maintained. The default value is 50.")
fs.IntVar(&c.AppRevisionLimit, "application-revision-limit", c.AppRevisionLimit,
"application-revision-limit is the maximum number of application useless revisions that will be maintained, if the useless revisions exceed this number, older ones will be GCed first.The default value is 10.")
fs.IntVar(&c.DefRevisionLimit, "definition-revision-limit", c.DefRevisionLimit,
"definition-revision-limit is the maximum number of component/trait definition useless revisions that will be maintained, if the useless revisions exceed this number, older ones will be GCed first.The default value is 20.")
fs.BoolVar(&c.AutoGenWorkloadDefinition, "autogen-workload-definition", c.AutoGenWorkloadDefinition,
"Automatic generated workloadDefinition which componentDefinition refers to.")
fs.IntVar(&c.ConcurrentReconciles, "concurrent-reconciles", c.ConcurrentReconciles,
"concurrent-reconciles is the concurrent reconcile number of the controller. The default value is 4")
fs.BoolVar(&c.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", c.IgnoreAppWithoutControllerRequirement,
"If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation")
fs.BoolVar(&c.IgnoreDefinitionWithoutControllerRequirement, "ignore-definition-without-controller-version", c.IgnoreDefinitionWithoutControllerRequirement,
"If true, trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation")
}

View File

@ -0,0 +1,61 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/kubevela/pkg/cue/cuex"
"github.com/spf13/pflag"
)
// CUEConfig contains CUE language configuration.
type CUEConfig struct {
EnableExternalPackage bool
EnableExternalPackageWatch bool
}
// NewCUEConfig creates a new CUEConfig with defaults.
func NewCUEConfig() *CUEConfig {
return &CUEConfig{
EnableExternalPackage: cuex.EnableExternalPackageForDefaultCompiler,
EnableExternalPackageWatch: cuex.EnableExternalPackageWatchForDefaultCompiler,
}
}
// AddFlags registers CUE configuration flags.
func (c *CUEConfig) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&c.EnableExternalPackage,
"enable-external-package-for-default-compiler",
c.EnableExternalPackage,
"Enable loading third-party CUE packages into the default CUE compiler. When enabled, external CUE packages can be imported and used in CUE templates.")
fs.BoolVar(&c.EnableExternalPackageWatch,
"enable-external-package-watch-for-default-compiler",
c.EnableExternalPackageWatch,
"Enable watching for changes in external CUE packages and automatically reload them when modified. Requires enable-external-package-for-default-compiler to be enabled.")
}
// SyncToCUEGlobals syncs the parsed configuration values to CUE package global variables.
// This should be called after flag parsing to ensure the CUE compiler uses the configured values.
//
// NOTE: This method exists for backward compatibility with legacy code that depends on global
// variables in the cuex package. Ideally, the CUE compiler configuration should be injected
// rather than relying on globals.
//
// The flow is: CLI flags -> CUEConfig struct fields -> cuex globals (via this method)
func (c *CUEConfig) SyncToCUEGlobals() {
cuex.EnableExternalPackageForDefaultCompiler = c.EnableExternalPackage
cuex.EnableExternalPackageWatchForDefaultCompiler = c.EnableExternalPackageWatch
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
// FeatureConfig contains feature gate configuration.
// This wraps the Kubernetes feature gate system.
type FeatureConfig struct {
// Note: The actual configuration is managed by the utilfeature package
// This is a wrapper to maintain consistency with our config pattern
}
// NewFeatureConfig creates a new FeatureConfig with defaults.
func NewFeatureConfig() *FeatureConfig {
return &FeatureConfig{}
}
// AddFlags registers feature gate configuration flags.
// Delegates to the Kubernetes feature gate system.
func (c *FeatureConfig) AddFlags(fs *pflag.FlagSet) {
utilfeature.DefaultMutableFeatureGate.AddFlag(fs)
}

View File

@ -0,0 +1,42 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
utillog "github.com/kubevela/pkg/util/log"
"github.com/spf13/pflag"
)
// KLogConfig contains klog configuration.
// This wraps the Kubernetes logging configuration.
type KLogConfig struct {
// Reference to observability config for log settings
observability *ObservabilityConfig
}
// NewKLogConfig creates a new KLogConfig.
func NewKLogConfig(observability *ObservabilityConfig) *KLogConfig {
return &KLogConfig{
observability: observability,
}
}
// AddFlags registers klog configuration flags.
func (c *KLogConfig) AddFlags(fs *pflag.FlagSet) {
// Add base klog flags
utillog.AddFlags(fs)
}

View File

@ -0,0 +1,49 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"time"
"github.com/spf13/pflag"
)
// KubernetesConfig contains Kubernetes API client configuration.
type KubernetesConfig struct {
QPS float64
Burst int
InformerSyncPeriod time.Duration
}
// NewKubernetesConfig creates a new KubernetesConfig with defaults.
func NewKubernetesConfig() *KubernetesConfig {
return &KubernetesConfig{
QPS: 50,
Burst: 100,
InformerSyncPeriod: 10 * time.Hour,
}
}
// AddFlags registers Kubernetes configuration flags.
func (c *KubernetesConfig) AddFlags(fs *pflag.FlagSet) {
fs.Float64Var(&c.QPS, "kube-api-qps", c.QPS,
"the qps for reconcile clients. Low qps may lead to low throughput. High qps may give stress to api-server. Raise this value if concurrent-reconciles is set to be high.")
fs.IntVar(&c.Burst, "kube-api-burst", c.Burst,
"the burst for reconcile clients. Recommend setting it qps*2.")
fs.DurationVar(&c.InformerSyncPeriod, "informer-sync-period", c.InformerSyncPeriod,
"The re-sync period for informer in controller-runtime. This is a system-level configuration.")
}

View File

@ -0,0 +1,53 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"time"
pkgmulticluster "github.com/kubevela/pkg/multicluster"
"github.com/spf13/pflag"
)
// MultiClusterConfig contains multi-cluster configuration.
type MultiClusterConfig struct {
EnableClusterGateway bool
EnableClusterMetrics bool
ClusterMetricsInterval time.Duration
}
// NewMultiClusterConfig creates a new MultiClusterConfig with defaults.
func NewMultiClusterConfig() *MultiClusterConfig {
return &MultiClusterConfig{
EnableClusterGateway: false,
EnableClusterMetrics: false,
ClusterMetricsInterval: 15 * time.Second,
}
}
// AddFlags registers multi-cluster configuration flags.
func (c *MultiClusterConfig) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&c.EnableClusterGateway, "enable-cluster-gateway", c.EnableClusterGateway,
"Enable cluster-gateway to use multicluster, disabled by default.")
fs.BoolVar(&c.EnableClusterMetrics, "enable-cluster-metrics", c.EnableClusterMetrics,
"Enable cluster-metrics-management to collect metrics from clusters with cluster-gateway, disabled by default. When this param is enabled, enable-cluster-gateway should be enabled")
fs.DurationVar(&c.ClusterMetricsInterval, "cluster-metrics-interval", c.ClusterMetricsInterval,
"The interval that ClusterMetricsMgr will collect metrics from clusters, default value is 15 seconds.")
// Also register additional multicluster flags from external package
pkgmulticluster.AddFlags(fs)
}

View File

@ -0,0 +1,54 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
"github.com/oam-dev/kubevela/pkg/oam"
)
// OAMConfig contains OAM-specific configuration.
type OAMConfig struct {
SystemDefinitionNamespace string
}
// NewOAMConfig creates a new OAMConfig with defaults.
func NewOAMConfig() *OAMConfig {
return &OAMConfig{
SystemDefinitionNamespace: "vela-system",
}
}
// AddFlags registers OAM configuration flags.
func (c *OAMConfig) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.SystemDefinitionNamespace,
"system-definition-namespace",
c.SystemDefinitionNamespace,
"Define the namespace of the system-level definition")
}
// SyncToOAMGlobals syncs the parsed configuration values to OAM package global variables.
// This should be called after flag parsing to ensure the OAM runtime uses the configured values.
//
// NOTE: This method exists for backward compatibility with legacy code that depends on global
// variables in the oam package. Ideally, configuration should be injected rather than using globals.
//
// The flow is: CLI flags -> OAMConfig struct fields -> oam globals (via this method)
func (c *OAMConfig) SyncToOAMGlobals() {
oam.SystemDefinitionNamespace = c.SystemDefinitionNamespace
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
)
// ObservabilityConfig contains metrics and logging configuration.
type ObservabilityConfig struct {
MetricsAddr string
LogFilePath string
LogFileMaxSize uint64
LogDebug bool
DevLogs bool
}
// NewObservabilityConfig creates a new ObservabilityConfig with defaults.
func NewObservabilityConfig() *ObservabilityConfig {
return &ObservabilityConfig{
MetricsAddr: ":8080",
LogFilePath: "",
LogFileMaxSize: 1024,
LogDebug: false,
DevLogs: false,
}
}
// AddFlags registers observability configuration flags.
func (c *ObservabilityConfig) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.MetricsAddr, "metrics-addr", c.MetricsAddr,
"The address the metric endpoint binds to.")
fs.StringVar(&c.LogFilePath, "log-file-path", c.LogFilePath,
"The file to write logs to.")
fs.Uint64Var(&c.LogFileMaxSize, "log-file-max-size", c.LogFileMaxSize,
"Defines the maximum size a log file can grow to, Unit is megabytes.")
fs.BoolVar(&c.LogDebug, "log-debug", c.LogDebug,
"Enable debug logs for development purpose")
fs.BoolVar(&c.DevLogs, "dev-logs", c.DevLogs,
"Enable ANSI color formatting for console logs (ignored when log-file-path is set)")
}

View File

@ -0,0 +1,58 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
standardcontroller "github.com/oam-dev/kubevela/pkg/controller"
commonconfig "github.com/oam-dev/kubevela/pkg/controller/common"
)
// PerformanceConfig contains performance and optimization configuration.
type PerformanceConfig struct {
PerfEnabled bool
}
// NewPerformanceConfig creates a new PerformanceConfig with defaults.
func NewPerformanceConfig() *PerformanceConfig {
return &PerformanceConfig{
PerfEnabled: commonconfig.PerfEnabled,
}
}
// AddFlags registers performance configuration flags.
func (c *PerformanceConfig) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&c.PerfEnabled,
"perf-enabled",
c.PerfEnabled,
"Enable performance logging for controllers, disabled by default.")
// Add optimization flags from the standard controller
standardcontroller.AddOptimizeFlags(fs)
}
// SyncToPerformanceGlobals syncs the parsed configuration values to performance package global variables.
// This should be called after flag parsing to ensure the performance monitoring uses the configured values.
//
// NOTE: This method exists for backward compatibility with legacy code that depends on global
// variables in the commonconfig package. Ideally, configuration should be injected rather than using globals.
//
// The flow is: CLI flags -> PerformanceConfig struct fields -> commonconfig globals (via this method)
func (c *PerformanceConfig) SyncToPerformanceGlobals() {
commonconfig.PerfEnabled = c.PerfEnabled
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/kubevela/pkg/util/profiling"
"github.com/spf13/pflag"
)
// ProfilingConfig contains profiling configuration.
// This wraps the external package's profiling configuration flags.
type ProfilingConfig struct {
// Note: The actual configuration is managed by the profiling package
// This is a wrapper to maintain consistency with our config pattern
}
// NewProfilingConfig creates a new ProfilingConfig with defaults.
func NewProfilingConfig() *ProfilingConfig {
return &ProfilingConfig{}
}
// AddFlags registers profiling configuration flags.
// Delegates to the external package's flag registration.
func (c *ProfilingConfig) AddFlags(fs *pflag.FlagSet) {
profiling.AddFlags(fs)
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
ctrlrec "github.com/kubevela/pkg/controller/reconciler"
"github.com/spf13/pflag"
)
// ReconcileConfig contains controller reconciliation configuration.
// This wraps the external package's reconciler configuration flags.
type ReconcileConfig struct {
// Note: The actual configuration is managed by the ctrlrec package
// This is a wrapper to maintain consistency with our config pattern
}
// NewReconcileConfig creates a new ReconcileConfig with defaults.
func NewReconcileConfig() *ReconcileConfig {
return &ReconcileConfig{}
}
// AddFlags registers reconcile configuration flags.
// Delegates to the external package's flag registration.
func (c *ReconcileConfig) AddFlags(fs *pflag.FlagSet) {
ctrlrec.AddFlags(fs)
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
)
// ResourceConfig contains resource management configuration.
type ResourceConfig struct {
MaxDispatchConcurrent int
}
// NewResourceConfig creates a new ResourceConfig with defaults.
func NewResourceConfig() *ResourceConfig {
return &ResourceConfig{
MaxDispatchConcurrent: 10,
}
}
// AddFlags registers resource configuration flags.
func (c *ResourceConfig) AddFlags(fs *pflag.FlagSet) {
fs.IntVar(&c.MaxDispatchConcurrent,
"max-dispatch-concurrent",
c.MaxDispatchConcurrent,
"Set the max dispatch concurrent number, default is 10")
}
// SyncToResourceGlobals syncs the parsed configuration values to resource package global variables.
// This should be called after flag parsing to ensure the resource keeper uses the configured values.
//
// NOTE: This method exists for backward compatibility with legacy code that depends on global
// variables in the resourcekeeper package. The long-term goal should be to refactor to use
// dependency injection rather than globals.
//
// The flow is: CLI flags -> ResourceConfig struct fields -> resourcekeeper globals (via this method)
func (c *ResourceConfig) SyncToResourceGlobals() {
resourcekeeper.MaxDispatchConcurrent = c.MaxDispatchConcurrent
}

View File

@ -0,0 +1,65 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"time"
"github.com/spf13/pflag"
)
// ServerConfig contains server-level configuration.
type ServerConfig struct {
HealthAddr string
StorageDriver string
EnableLeaderElection bool
LeaderElectionNamespace string
LeaseDuration time.Duration
RenewDeadline time.Duration
RetryPeriod time.Duration
}
// NewServerConfig creates a new ServerConfig with defaults.
func NewServerConfig() *ServerConfig {
return &ServerConfig{
HealthAddr: ":9440",
StorageDriver: "Local",
EnableLeaderElection: false,
LeaderElectionNamespace: "",
LeaseDuration: 15 * time.Second,
RenewDeadline: 10 * time.Second,
RetryPeriod: 2 * time.Second,
}
}
// AddFlags registers server configuration flags.
func (c *ServerConfig) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.HealthAddr, "health-addr", c.HealthAddr,
"The address the health endpoint binds to.")
fs.StringVar(&c.StorageDriver, "storage-driver", c.StorageDriver,
"Application storage driver.")
fs.BoolVar(&c.EnableLeaderElection, "enable-leader-election", c.EnableLeaderElection,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
fs.StringVar(&c.LeaderElectionNamespace, "leader-election-namespace", c.LeaderElectionNamespace,
"Determines the namespace in which the leader election configmap will be created.")
fs.DurationVar(&c.LeaseDuration, "leader-election-lease-duration", c.LeaseDuration,
"The duration that non-leader candidates will wait to force acquire leadership")
fs.DurationVar(&c.RenewDeadline, "leader-election-renew-deadline", c.RenewDeadline,
"The duration that the acting controlplane will retry refreshing leadership before giving up")
fs.DurationVar(&c.RetryPeriod, "leader-election-retry-period", c.RetryPeriod,
"The duration the LeaderElector clients should wait between tries of actions")
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/kubevela/pkg/controller/sharding"
"github.com/spf13/pflag"
)
// ShardingConfig contains controller sharding configuration.
// This wraps the external package's sharding configuration flags.
type ShardingConfig struct {
// Note: The actual configuration is managed by the sharding package
// This is a wrapper to maintain consistency with our config pattern
}
// NewShardingConfig creates a new ShardingConfig with defaults.
func NewShardingConfig() *ShardingConfig {
return &ShardingConfig{}
}
// AddFlags registers sharding configuration flags.
// Delegates to the external package's flag registration.
func (c *ShardingConfig) AddFlags(fs *pflag.FlagSet) {
sharding.AddFlags(fs)
}

View File

@ -0,0 +1,47 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
)
// WebhookConfig contains webhook configuration.
type WebhookConfig struct {
UseWebhook bool
CertDir string
WebhookPort int
}
// NewWebhookConfig creates a new WebhookConfig with defaults.
func NewWebhookConfig() *WebhookConfig {
return &WebhookConfig{
UseWebhook: false,
CertDir: "/k8s-webhook-server/serving-certs",
WebhookPort: 9443,
}
}
// AddFlags registers webhook configuration flags.
func (c *WebhookConfig) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&c.UseWebhook, "use-webhook", c.UseWebhook,
"Enable Admission Webhook")
fs.StringVar(&c.CertDir, "webhook-cert-dir", c.CertDir,
"Admission webhook cert/key dir.")
fs.IntVar(&c.WebhookPort, "webhook-port", c.WebhookPort,
"admission webhook listen address")
}

View File

@ -0,0 +1,69 @@
/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/pflag"
wfTypes "github.com/kubevela/workflow/pkg/types"
)
// WorkflowConfig contains workflow engine configuration.
type WorkflowConfig struct {
MaxWaitBackoffTime int
MaxFailedBackoffTime int
MaxStepErrorRetryTimes int
}
// NewWorkflowConfig creates a new WorkflowConfig with defaults.
func NewWorkflowConfig() *WorkflowConfig {
return &WorkflowConfig{
MaxWaitBackoffTime: 60,
MaxFailedBackoffTime: 300,
MaxStepErrorRetryTimes: 10,
}
}
// AddFlags registers workflow configuration flags.
func (c *WorkflowConfig) AddFlags(fs *pflag.FlagSet) {
fs.IntVar(&c.MaxWaitBackoffTime,
"max-workflow-wait-backoff-time",
c.MaxWaitBackoffTime,
"Set the max workflow wait backoff time, default is 60")
fs.IntVar(&c.MaxFailedBackoffTime,
"max-workflow-failed-backoff-time",
c.MaxFailedBackoffTime,
"Set the max workflow failed backoff time, default is 300")
fs.IntVar(&c.MaxStepErrorRetryTimes,
"max-workflow-step-error-retry-times",
c.MaxStepErrorRetryTimes,
"Set the max workflow step error retry times, default is 10")
}
// SyncToWorkflowGlobals syncs the parsed configuration values to workflow package global variables.
// This should be called after flag parsing to ensure the workflow engine uses the configured values.
//
// NOTE: This method exists for backward compatibility with legacy code that depends on global
// variables in the wfTypes package. The long-term goal should be to refactor the workflow
// package to accept configuration via dependency injection rather than globals.
//
// The flow is: CLI flags -> WorkflowConfig struct fields -> wfTypes globals (via this method)
func (c *WorkflowConfig) SyncToWorkflowGlobals() {
wfTypes.MaxWorkflowWaitBackoffTime = c.MaxWaitBackoffTime
wfTypes.MaxWorkflowFailedBackoffTime = c.MaxFailedBackoffTime
wfTypes.MaxWorkflowStepErrorRetryTimes = c.MaxStepErrorRetryTimes
}

View File

@ -17,88 +17,81 @@ limitations under the License.
package options
import (
"strconv"
"time"
"github.com/kubevela/pkg/cue/cuex"
pkgclient "github.com/kubevela/pkg/controller/client"
ctrlrec "github.com/kubevela/pkg/controller/reconciler"
"github.com/kubevela/pkg/controller/sharding"
pkgmulticluster "github.com/kubevela/pkg/multicluster"
utillog "github.com/kubevela/pkg/util/log"
"github.com/kubevela/pkg/util/profiling"
wfTypes "github.com/kubevela/workflow/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cliflag "k8s.io/component-base/cli/flag"
standardcontroller "github.com/oam-dev/kubevela/pkg/controller"
commonconfig "github.com/oam-dev/kubevela/pkg/controller/common"
oamcontroller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
"github.com/oam-dev/kubevela/cmd/core/app/config"
)
// CoreOptions contains everything necessary to create and run vela-core
type CoreOptions struct {
UseWebhook bool
CertDir string
WebhookPort int
MetricsAddr string
EnableLeaderElection bool
LeaderElectionNamespace string
LogFilePath string
LogFileMaxSize uint64
LogDebug bool
DevLogs bool
ControllerArgs *oamcontroller.Args
HealthAddr string
StorageDriver string
InformerSyncPeriod time.Duration
QPS float64
Burst int
LeaseDuration time.Duration
RenewDeadLine time.Duration
RetryPeriod time.Duration
EnableClusterGateway bool
EnableClusterMetrics bool
ClusterMetricsInterval time.Duration
// Config modules - clean, well-organized configuration
Server *config.ServerConfig
Webhook *config.WebhookConfig
Observability *config.ObservabilityConfig
Kubernetes *config.KubernetesConfig
MultiCluster *config.MultiClusterConfig
CUE *config.CUEConfig
Application *config.ApplicationConfig
OAM *config.OAMConfig
Performance *config.PerformanceConfig
Workflow *config.WorkflowConfig
Admission *config.AdmissionConfig
Resource *config.ResourceConfig
Client *config.ClientConfig
Reconcile *config.ReconcileConfig
Sharding *config.ShardingConfig
Feature *config.FeatureConfig
Profiling *config.ProfilingConfig
KLog *config.KLogConfig
Controller *config.ControllerConfig
}
// NewCoreOptions creates a new NewVelaCoreOptions object with default parameters
func NewCoreOptions() *CoreOptions {
// Initialize config modules
server := config.NewServerConfig()
webhook := config.NewWebhookConfig()
observability := config.NewObservabilityConfig()
kubernetes := config.NewKubernetesConfig()
multiCluster := config.NewMultiClusterConfig()
cue := config.NewCUEConfig()
application := config.NewApplicationConfig()
oam := config.NewOAMConfig()
performance := config.NewPerformanceConfig()
workflow := config.NewWorkflowConfig()
admission := config.NewAdmissionConfig()
resource := config.NewResourceConfig()
client := config.NewClientConfig()
reconcile := config.NewReconcileConfig()
sharding := config.NewShardingConfig()
feature := config.NewFeatureConfig()
profiling := config.NewProfilingConfig()
klog := config.NewKLogConfig(observability)
controller := config.NewControllerConfig()
s := &CoreOptions{
UseWebhook: false,
CertDir: "/k8s-webhook-server/serving-certs",
WebhookPort: 9443,
MetricsAddr: ":8080",
EnableLeaderElection: false,
LeaderElectionNamespace: "",
LogFilePath: "",
LogFileMaxSize: 1024,
LogDebug: false,
DevLogs: false,
ControllerArgs: &oamcontroller.Args{
RevisionLimit: 50,
AppRevisionLimit: 10,
DefRevisionLimit: 20,
AutoGenWorkloadDefinition: true,
ConcurrentReconciles: 4,
IgnoreAppWithoutControllerRequirement: false,
IgnoreDefinitionWithoutControllerRequirement: false,
},
HealthAddr: ":9440",
StorageDriver: "Local",
InformerSyncPeriod: 10 * time.Hour,
QPS: 50,
Burst: 100,
LeaseDuration: 15 * time.Second,
RenewDeadLine: 10 * time.Second,
RetryPeriod: 2 * time.Second,
EnableClusterGateway: false,
EnableClusterMetrics: false,
ClusterMetricsInterval: 15 * time.Second,
// Config modules
Server: server,
Webhook: webhook,
Observability: observability,
Kubernetes: kubernetes,
MultiCluster: multiCluster,
CUE: cue,
Application: application,
OAM: oam,
Performance: performance,
Workflow: workflow,
Admission: admission,
Resource: resource,
Client: client,
Reconcile: reconcile,
Sharding: sharding,
Feature: feature,
Profiling: profiling,
KLog: klog,
Controller: controller,
}
return s
}
@ -106,75 +99,28 @@ func NewCoreOptions() *CoreOptions {
func (s *CoreOptions) Flags() cliflag.NamedFlagSets {
fss := cliflag.NamedFlagSets{}
gfs := fss.FlagSet("generic")
gfs.BoolVar(&s.UseWebhook, "use-webhook", s.UseWebhook, "Enable Admission Webhook")
gfs.StringVar(&s.CertDir, "webhook-cert-dir", s.CertDir, "Admission webhook cert/key dir.")
gfs.IntVar(&s.WebhookPort, "webhook-port", s.WebhookPort, "admission webhook listen address")
gfs.StringVar(&s.MetricsAddr, "metrics-addr", s.MetricsAddr, "The address the metric endpoint binds to.")
gfs.BoolVar(&s.EnableLeaderElection, "enable-leader-election", s.EnableLeaderElection,
"Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
gfs.StringVar(&s.LeaderElectionNamespace, "leader-election-namespace", s.LeaderElectionNamespace,
"Determines the namespace in which the leader election configmap will be created.")
gfs.StringVar(&s.LogFilePath, "log-file-path", s.LogFilePath, "The file to write logs to.")
gfs.Uint64Var(&s.LogFileMaxSize, "log-file-max-size", s.LogFileMaxSize, "Defines the maximum size a log file can grow to, Unit is megabytes.")
gfs.BoolVar(&s.LogDebug, "log-debug", s.LogDebug, "Enable debug logs for development purpose")
gfs.BoolVar(&s.DevLogs, "dev-logs", s.DevLogs, "Enable ANSI color formatting for console logs (ignored when log-file-path is set)")
gfs.StringVar(&s.HealthAddr, "health-addr", s.HealthAddr, "The address the health endpoint binds to.")
gfs.DurationVar(&s.InformerSyncPeriod, "informer-sync-period", s.InformerSyncPeriod,
"The re-sync period for informer in controller-runtime. This is a system-level configuration.")
gfs.Float64Var(&s.QPS, "kube-api-qps", s.QPS, "the qps for reconcile clients. Low qps may lead to low throughput. High qps may give stress to api-server. Raise this value if concurrent-reconciles is set to be high.")
gfs.IntVar(&s.Burst, "kube-api-burst", s.Burst, "the burst for reconcile clients. Recommend setting it qps*2.")
gfs.DurationVar(&s.LeaseDuration, "leader-election-lease-duration", s.LeaseDuration,
"The duration that non-leader candidates will wait to force acquire leadership")
gfs.DurationVar(&s.RenewDeadLine, "leader-election-renew-deadline", s.RenewDeadLine,
"The duration that the acting controlplane will retry refreshing leadership before giving up")
gfs.DurationVar(&s.RetryPeriod, "leader-election-retry-period", s.RetryPeriod,
"The duration the LeaderElector clients should wait between tries of actions")
gfs.BoolVar(&s.EnableClusterGateway, "enable-cluster-gateway", s.EnableClusterGateway, "Enable cluster-gateway to use multicluster, disabled by default.")
gfs.BoolVar(&s.EnableClusterMetrics, "enable-cluster-metrics", s.EnableClusterMetrics, "Enable cluster-metrics-management to collect metrics from clusters with cluster-gateway, disabled by default. When this param is enabled, enable-cluster-gateway should be enabled")
gfs.DurationVar(&s.ClusterMetricsInterval, "cluster-metrics-interval", s.ClusterMetricsInterval, "The interval that ClusterMetricsMgr will collect metrics from clusters, default value is 15 seconds.")
gfs.BoolVar(&cuex.EnableExternalPackageForDefaultCompiler, "enable-external-package-for-default-compiler", cuex.EnableExternalPackageForDefaultCompiler, "Enable external package for default compiler")
gfs.BoolVar(&cuex.EnableExternalPackageWatchForDefaultCompiler, "enable-external-package-watch-for-default-compiler", cuex.EnableExternalPackageWatchForDefaultCompiler, "Enable external package watch for default compiler")
// Use config modules to register flags - clean delegation pattern
s.Server.AddFlags(fss.FlagSet("server"))
s.Webhook.AddFlags(fss.FlagSet("webhook"))
s.Observability.AddFlags(fss.FlagSet("observability"))
s.Kubernetes.AddFlags(fss.FlagSet("kubernetes"))
s.MultiCluster.AddFlags(fss.FlagSet("multicluster"))
s.CUE.AddFlags(fss.FlagSet("cue"))
s.Application.AddFlags(fss.FlagSet("application"))
s.OAM.AddFlags(fss.FlagSet("oam"))
s.Performance.AddFlags(fss.FlagSet("performance"))
s.Admission.AddFlags(fss.FlagSet("admission"))
s.Resource.AddFlags(fss.FlagSet("resource"))
s.Workflow.AddFlags(fss.FlagSet("workflow"))
s.Controller.AddFlags(fss.FlagSet("controller"))
s.ControllerArgs.AddFlags(fss.FlagSet("controllerArgs"), s.ControllerArgs)
cfs := fss.FlagSet("commonconfig")
cfs.DurationVar(&commonconfig.ApplicationReSyncPeriod, "application-re-sync-period", commonconfig.ApplicationReSyncPeriod,
"Re-sync period for application to re-sync, also known as the state-keep interval.")
cfs.BoolVar(&commonconfig.PerfEnabled, "perf-enabled", commonconfig.PerfEnabled, "Enable performance logging for controllers, disabled by default.")
ofs := fss.FlagSet("oam")
ofs.StringVar(&oam.SystemDefinitionNamespace, "system-definition-namespace", "vela-system", "define the namespace of the system-level definition")
standardcontroller.AddOptimizeFlags(fss.FlagSet("optimize"))
standardcontroller.AddAdmissionFlags(fss.FlagSet("admission"))
rfs := fss.FlagSet("resourcekeeper")
rfs.IntVar(&resourcekeeper.MaxDispatchConcurrent, "max-dispatch-concurrent", 10, "Set the max dispatch concurrent number, default is 10")
wfs := fss.FlagSet("wfTypes")
wfs.IntVar(&wfTypes.MaxWorkflowWaitBackoffTime, "max-workflow-wait-backoff-time", 60, "Set the max workflow wait backoff time, default is 60")
wfs.IntVar(&wfTypes.MaxWorkflowFailedBackoffTime, "max-workflow-failed-backoff-time", 300, "Set the max workflow failed backoff time, default is 300")
wfs.IntVar(&wfTypes.MaxWorkflowStepErrorRetryTimes, "max-workflow-step-error-retry-times", 10, "Set the max workflow step error retry times, default is 10")
pkgmulticluster.AddFlags(fss.FlagSet("multicluster"))
ctrlrec.AddFlags(fss.FlagSet("controllerreconciles"))
utilfeature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("featuregate"))
sharding.AddFlags(fss.FlagSet("sharding"))
kfs := fss.FlagSet("klog")
pkgclient.AddTimeoutControllerClientFlags(fss.FlagSet("controllerclient"))
utillog.AddFlags(kfs)
profiling.AddFlags(fss.FlagSet("profiling"))
if s.LogDebug {
_ = kfs.Set("v", strconv.Itoa(int(commonconfig.LogDebug)))
}
if s.LogFilePath != "" {
_ = kfs.Set("logtostderr", "false")
_ = kfs.Set("log_file", s.LogFilePath)
_ = kfs.Set("log_file_max_size", strconv.FormatUint(s.LogFileMaxSize, 10))
}
// External package configurations (now wrapped in config modules)
s.Client.AddFlags(fss.FlagSet("client"))
s.Reconcile.AddFlags(fss.FlagSet("reconcile"))
s.Sharding.AddFlags(fss.FlagSet("sharding"))
s.Feature.AddFlags(fss.FlagSet("feature"))
s.Profiling.AddFlags(fss.FlagSet("profiling"))
s.KLog.AddFlags(fss.FlagSet("klog"))
return fss
}

View File

@ -20,104 +20,915 @@ import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/kubevela/pkg/cue/cuex"
wfTypes "github.com/kubevela/workflow/pkg/types"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
oamcontroller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
commonconfig "github.com/oam-dev/kubevela/pkg/controller/common"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
)
func TestCoreOptions_Flags(t *testing.T) {
func TestNewCoreOptions_DefaultValues(t *testing.T) {
opt := NewCoreOptions()
// Test Server defaults
assert.Equal(t, ":9440", opt.Server.HealthAddr)
assert.Equal(t, "Local", opt.Server.StorageDriver)
assert.Equal(t, false, opt.Server.EnableLeaderElection)
assert.Equal(t, "", opt.Server.LeaderElectionNamespace)
assert.Equal(t, 15*time.Second, opt.Server.LeaseDuration)
assert.Equal(t, 10*time.Second, opt.Server.RenewDeadline)
assert.Equal(t, 2*time.Second, opt.Server.RetryPeriod)
// Test Webhook defaults
assert.Equal(t, false, opt.Webhook.UseWebhook)
assert.Equal(t, "/k8s-webhook-server/serving-certs", opt.Webhook.CertDir)
assert.Equal(t, 9443, opt.Webhook.WebhookPort)
// Test Observability defaults
assert.Equal(t, ":8080", opt.Observability.MetricsAddr)
assert.Equal(t, false, opt.Observability.LogDebug)
assert.Equal(t, "", opt.Observability.LogFilePath)
assert.Equal(t, uint64(1024), opt.Observability.LogFileMaxSize)
// Test Kubernetes defaults
assert.Equal(t, 10*time.Hour, opt.Kubernetes.InformerSyncPeriod)
assert.Equal(t, float64(50), opt.Kubernetes.QPS)
assert.Equal(t, 100, opt.Kubernetes.Burst)
// Test MultiCluster defaults
assert.Equal(t, false, opt.MultiCluster.EnableClusterGateway)
assert.Equal(t, false, opt.MultiCluster.EnableClusterMetrics)
assert.Equal(t, 15*time.Second, opt.MultiCluster.ClusterMetricsInterval)
// Test CUE defaults
assert.NotNil(t, opt.CUE)
// Test Application defaults
assert.Equal(t, 5*time.Minute, opt.Application.ReSyncPeriod)
// Test OAM defaults
assert.Equal(t, "vela-system", opt.OAM.SystemDefinitionNamespace)
// Test Performance defaults
assert.Equal(t, false, opt.Performance.PerfEnabled)
// Test Controller defaults
assert.Equal(t, 50, opt.Controller.RevisionLimit)
assert.Equal(t, 10, opt.Controller.AppRevisionLimit)
assert.Equal(t, 20, opt.Controller.DefRevisionLimit)
assert.Equal(t, true, opt.Controller.AutoGenWorkloadDefinition)
assert.Equal(t, 4, opt.Controller.ConcurrentReconciles)
assert.Equal(t, false, opt.Controller.IgnoreAppWithoutControllerRequirement)
assert.Equal(t, false, opt.Controller.IgnoreDefinitionWithoutControllerRequirement)
// Test Workflow defaults
assert.Equal(t, 60, opt.Workflow.MaxWaitBackoffTime)
assert.Equal(t, 300, opt.Workflow.MaxFailedBackoffTime)
assert.Equal(t, 10, opt.Workflow.MaxStepErrorRetryTimes)
// Test Resource defaults
assert.Equal(t, 10, opt.Resource.MaxDispatchConcurrent)
// Ensure all config modules are initialized
assert.NotNil(t, opt.Admission)
assert.NotNil(t, opt.Client)
assert.NotNil(t, opt.Reconcile)
assert.NotNil(t, opt.Sharding)
assert.NotNil(t, opt.Feature)
assert.NotNil(t, opt.Profiling)
assert.NotNil(t, opt.KLog)
assert.NotNil(t, opt.Controller)
}
func TestCoreOptions_FlagsCompleteSet(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opt := &CoreOptions{
ControllerArgs: &oamcontroller.Args{},
}
opt := NewCoreOptions()
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
args := []string{
"--application-re-sync-period=5s",
"--cluster-metrics-interval=5s",
"--enable-cluster-gateway=true",
"--enable-cluster-metrics=true",
"--enable-leader-election=true",
// Server flags
"--health-addr=/healthz",
"--informer-sync-period=3s",
"--kube-api-burst=500",
"--kube-api-qps=200",
"--leader-election-lease-duration=3s",
"--storage-driver=MongoDB",
"--enable-leader-election=true",
"--leader-election-namespace=test-namespace",
"--leader-election-lease-duration=3s",
"--leader-election-renew-deadline=5s",
"--leader-election-retry-period=3s",
"--log-debug=true",
"--log-file-max-size=50",
"--log-file-path=/path/to/log",
"--max-dispatch-concurrent=5",
"--max-workflow-failed-backoff-time=30",
"--max-workflow-step-error-retry-times=5",
"--max-workflow-wait-backoff-time=5",
"--metrics-addr=/metrics",
"--perf-enabled=true",
// Webhook flags
"--use-webhook=true",
"--webhook-cert-dir=/path/to/cert",
"--webhook-port=8080",
// Observability flags
"--metrics-addr=/metrics",
"--log-debug=true",
"--log-file-path=/path/to/log",
"--log-file-max-size=50",
// Kubernetes flags
"--informer-sync-period=3s",
"--kube-api-qps=200",
"--kube-api-burst=500",
// MultiCluster flags
"--enable-cluster-gateway=true",
"--enable-cluster-metrics=true",
"--cluster-metrics-interval=5s",
// CUE flags
"--enable-external-package-for-default-compiler=true",
"--enable-external-package-watch-for-default-compiler=true",
// Application flags
"--application-re-sync-period=5s",
// OAM flags
"--system-definition-namespace=custom-namespace",
// Performance flags
"--perf-enabled=true",
// Controller flags
"--revision-limit=100",
"--application-revision-limit=20",
"--definition-revision-limit=30",
"--autogen-workload-definition=false",
"--concurrent-reconciles=8",
"--ignore-app-without-controller-version=true",
"--ignore-definition-without-controller-version=true",
// Workflow flags
"--max-workflow-wait-backoff-time=30",
"--max-workflow-failed-backoff-time=150",
"--max-workflow-step-error-retry-times=5",
// Resource flags
"--max-dispatch-concurrent=5",
}
if err := fs.Parse(args); err != nil {
t.Errorf("Failed to parse args: %v", err)
}
err := fs.Parse(args)
require.NoError(t, err)
expected := &CoreOptions{
UseWebhook: true,
CertDir: "/path/to/cert",
WebhookPort: 8080,
MetricsAddr: "/metrics",
EnableLeaderElection: true,
LeaderElectionNamespace: "test-namespace",
LogFilePath: "/path/to/log",
LogFileMaxSize: 50,
LogDebug: true,
ControllerArgs: &oamcontroller.Args{},
HealthAddr: "/healthz",
StorageDriver: "",
InformerSyncPeriod: 3 * time.Second,
QPS: 200,
Burst: 500,
LeaseDuration: 3 * time.Second,
RenewDeadLine: 5 * time.Second,
RetryPeriod: 3 * time.Second,
EnableClusterGateway: true,
EnableClusterMetrics: true,
ClusterMetricsInterval: 5 * time.Second,
}
// Verify Server flags
assert.Equal(t, "/healthz", opt.Server.HealthAddr)
assert.Equal(t, "MongoDB", opt.Server.StorageDriver)
assert.Equal(t, true, opt.Server.EnableLeaderElection)
assert.Equal(t, "test-namespace", opt.Server.LeaderElectionNamespace)
assert.Equal(t, 3*time.Second, opt.Server.LeaseDuration)
assert.Equal(t, 5*time.Second, opt.Server.RenewDeadline)
assert.Equal(t, 3*time.Second, opt.Server.RetryPeriod)
if !cmp.Equal(opt, expected, cmp.AllowUnexported(CoreOptions{})) {
t.Errorf("Flags() diff: %v", cmp.Diff(opt, expected, cmp.AllowUnexported(CoreOptions{})))
}
// Verify Webhook flags
assert.Equal(t, true, opt.Webhook.UseWebhook)
assert.Equal(t, "/path/to/cert", opt.Webhook.CertDir)
assert.Equal(t, 8080, opt.Webhook.WebhookPort)
// Verify Observability flags
assert.Equal(t, "/metrics", opt.Observability.MetricsAddr)
assert.Equal(t, true, opt.Observability.LogDebug)
assert.Equal(t, "/path/to/log", opt.Observability.LogFilePath)
assert.Equal(t, uint64(50), opt.Observability.LogFileMaxSize)
// Verify Kubernetes flags
assert.Equal(t, 3*time.Second, opt.Kubernetes.InformerSyncPeriod)
assert.Equal(t, float64(200), opt.Kubernetes.QPS)
assert.Equal(t, 500, opt.Kubernetes.Burst)
// Verify MultiCluster flags
assert.Equal(t, true, opt.MultiCluster.EnableClusterGateway)
assert.Equal(t, true, opt.MultiCluster.EnableClusterMetrics)
assert.Equal(t, 5*time.Second, opt.MultiCluster.ClusterMetricsInterval)
// Verify CUE flags
assert.True(t, opt.CUE.EnableExternalPackage)
assert.True(t, opt.CUE.EnableExternalPackageWatch)
// Verify Application flags
assert.Equal(t, 5*time.Second, opt.Application.ReSyncPeriod)
// Verify OAM flags
assert.Equal(t, "custom-namespace", opt.OAM.SystemDefinitionNamespace)
// Verify Performance flags
assert.Equal(t, true, opt.Performance.PerfEnabled)
// Verify Controller flags
assert.Equal(t, 100, opt.Controller.RevisionLimit)
assert.Equal(t, 20, opt.Controller.AppRevisionLimit)
assert.Equal(t, 30, opt.Controller.DefRevisionLimit)
assert.Equal(t, false, opt.Controller.AutoGenWorkloadDefinition)
assert.Equal(t, 8, opt.Controller.ConcurrentReconciles)
assert.Equal(t, true, opt.Controller.IgnoreAppWithoutControllerRequirement)
assert.Equal(t, true, opt.Controller.IgnoreDefinitionWithoutControllerRequirement)
// Verify Workflow flags
assert.Equal(t, 30, opt.Workflow.MaxWaitBackoffTime)
assert.Equal(t, 150, opt.Workflow.MaxFailedBackoffTime)
assert.Equal(t, 5, opt.Workflow.MaxStepErrorRetryTimes)
// Verify Resource flags
assert.Equal(t, 5, opt.Resource.MaxDispatchConcurrent)
}
func TestCuexOptions_Flags(t *testing.T) {
pflag.NewFlagSet("test", pflag.ContinueOnError)
func TestCuexOptions_SyncToGlobals(t *testing.T) {
// Reset globals
cuex.EnableExternalPackageForDefaultCompiler = false
cuex.EnableExternalPackageWatchForDefaultCompiler = false
opts := &CoreOptions{
ControllerArgs: &oamcontroller.Args{},
}
opts := NewCoreOptions()
fss := opts.Flags()
args := []string{
"--enable-external-package-for-default-compiler=true",
"--enable-external-package-watch-for-default-compiler=true",
}
err := fss.FlagSet("generic").Parse(args)
if err != nil {
return
err := fss.FlagSet("cue").Parse(args)
require.NoError(t, err)
// Before sync, globals should still be false
assert.False(t, cuex.EnableExternalPackageForDefaultCompiler)
assert.False(t, cuex.EnableExternalPackageWatchForDefaultCompiler)
// After sync, globals should be updated
opts.CUE.SyncToCUEGlobals()
assert.True(t, cuex.EnableExternalPackageForDefaultCompiler)
assert.True(t, cuex.EnableExternalPackageWatchForDefaultCompiler)
}
func TestWorkflowOptions_SyncToGlobals(t *testing.T) {
// Store original values
origWait := wfTypes.MaxWorkflowWaitBackoffTime
origFailed := wfTypes.MaxWorkflowFailedBackoffTime
origRetry := wfTypes.MaxWorkflowStepErrorRetryTimes
// Restore after test
defer func() {
wfTypes.MaxWorkflowWaitBackoffTime = origWait
wfTypes.MaxWorkflowFailedBackoffTime = origFailed
wfTypes.MaxWorkflowStepErrorRetryTimes = origRetry
}()
opts := NewCoreOptions()
fss := opts.Flags()
args := []string{
"--max-workflow-wait-backoff-time=120",
"--max-workflow-failed-backoff-time=600",
"--max-workflow-step-error-retry-times=20",
}
assert.True(t, cuex.EnableExternalPackageForDefaultCompiler, "The --enable-external-package-for-default-compiler flag should be enabled")
assert.True(t, cuex.EnableExternalPackageWatchForDefaultCompiler, "The --enable-external-package-watch-for-default-compiler flag should be enabled")
err := fss.FlagSet("workflow").Parse(args)
require.NoError(t, err)
// Verify struct fields are updated
assert.Equal(t, 120, opts.Workflow.MaxWaitBackoffTime)
assert.Equal(t, 600, opts.Workflow.MaxFailedBackoffTime)
assert.Equal(t, 20, opts.Workflow.MaxStepErrorRetryTimes)
// After sync, globals should be updated
opts.Workflow.SyncToWorkflowGlobals()
assert.Equal(t, 120, wfTypes.MaxWorkflowWaitBackoffTime)
assert.Equal(t, 600, wfTypes.MaxWorkflowFailedBackoffTime)
assert.Equal(t, 20, wfTypes.MaxWorkflowStepErrorRetryTimes)
}
func TestOAMOptions_SyncToGlobals(t *testing.T) {
// Store original value
origNamespace := oam.SystemDefinitionNamespace
// Restore after test
defer func() {
oam.SystemDefinitionNamespace = origNamespace
}()
opts := NewCoreOptions()
fss := opts.Flags()
args := []string{
"--system-definition-namespace=custom-system",
}
err := fss.FlagSet("oam").Parse(args)
require.NoError(t, err)
// Verify struct field is updated
assert.Equal(t, "custom-system", opts.OAM.SystemDefinitionNamespace)
// After sync, global should be updated
opts.OAM.SyncToOAMGlobals()
assert.Equal(t, "custom-system", oam.SystemDefinitionNamespace)
}
func TestPerformanceOptions_SyncToGlobals(t *testing.T) {
// Store original value
origPerf := commonconfig.PerfEnabled
// Restore after test
defer func() {
commonconfig.PerfEnabled = origPerf
}()
opts := NewCoreOptions()
fss := opts.Flags()
args := []string{
"--perf-enabled=true",
}
err := fss.FlagSet("performance").Parse(args)
require.NoError(t, err)
// Verify struct field is updated
assert.Equal(t, true, opts.Performance.PerfEnabled)
// After sync, global should be updated
opts.Performance.SyncToPerformanceGlobals()
assert.True(t, commonconfig.PerfEnabled)
}
func TestApplicationOptions_SyncToGlobals(t *testing.T) {
// Store original value
origPeriod := commonconfig.ApplicationReSyncPeriod
// Restore after test
defer func() {
commonconfig.ApplicationReSyncPeriod = origPeriod
}()
opts := NewCoreOptions()
fss := opts.Flags()
args := []string{
"--application-re-sync-period=10m",
}
err := fss.FlagSet("application").Parse(args)
require.NoError(t, err)
// Verify struct field is updated
assert.Equal(t, 10*time.Minute, opts.Application.ReSyncPeriod)
// After sync, global should be updated
opts.Application.SyncToApplicationGlobals()
assert.Equal(t, 10*time.Minute, commonconfig.ApplicationReSyncPeriod)
}
func TestResourceOptions_SyncToGlobals(t *testing.T) {
// Store original value
origDispatch := resourcekeeper.MaxDispatchConcurrent
// Restore after test
defer func() {
resourcekeeper.MaxDispatchConcurrent = origDispatch
}()
opts := NewCoreOptions()
fss := opts.Flags()
args := []string{
"--max-dispatch-concurrent=25",
}
err := fss.FlagSet("resource").Parse(args)
require.NoError(t, err)
// Verify struct field is updated
assert.Equal(t, 25, opts.Resource.MaxDispatchConcurrent)
// After sync, global should be updated
opts.Resource.SyncToResourceGlobals()
assert.Equal(t, 25, resourcekeeper.MaxDispatchConcurrent)
}
func TestCoreOptions_InvalidValues(t *testing.T) {
tests := []struct {
name string
args []string
expectError bool
errorMsg string
}{
{
name: "invalid boolean value",
args: []string{
"--enable-leader-election=notabool",
},
expectError: true,
errorMsg: "invalid argument",
},
{
name: "invalid duration value",
args: []string{
"--leader-election-lease-duration=notaduration",
},
expectError: true,
errorMsg: "invalid argument",
},
{
name: "invalid int value",
args: []string{
"--webhook-port=notanint",
},
expectError: true,
errorMsg: "invalid argument",
},
{
name: "invalid float value",
args: []string{
"--kube-api-qps=notafloat",
},
expectError: true,
errorMsg: "invalid argument",
},
{
name: "invalid uint64 value",
args: []string{
"--log-file-max-size=-100",
},
expectError: true,
errorMsg: "invalid argument",
},
{
name: "unknown flag",
args: []string{
"--unknown-flag=value",
},
expectError: true,
errorMsg: "unknown flag",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opt := NewCoreOptions()
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
err := fs.Parse(tt.args)
if tt.expectError {
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.errorMsg)
} else {
assert.NoError(t, err)
}
})
}
}
func TestCoreOptions_PartialConfiguration(t *testing.T) {
// Test that partial configuration works correctly
// and doesn't override other defaults
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opt := NewCoreOptions()
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
// Only set a few flags
args := []string{
"--enable-leader-election=true",
"--log-debug=true",
"--perf-enabled=true",
}
err := fs.Parse(args)
require.NoError(t, err)
// Check that specified flags are updated
assert.Equal(t, true, opt.Server.EnableLeaderElection)
assert.Equal(t, true, opt.Observability.LogDebug)
assert.Equal(t, true, opt.Performance.PerfEnabled)
// Check that unspecified flags retain defaults
assert.Equal(t, ":9440", opt.Server.HealthAddr)
assert.Equal(t, "Local", opt.Server.StorageDriver)
assert.Equal(t, false, opt.Webhook.UseWebhook)
assert.Equal(t, ":8080", opt.Observability.MetricsAddr)
assert.Equal(t, 10*time.Hour, opt.Kubernetes.InformerSyncPeriod)
assert.Equal(t, 10, opt.Resource.MaxDispatchConcurrent)
}
func TestCoreOptions_FlagSetsOrganization(t *testing.T) {
opt := NewCoreOptions()
fss := opt.Flags()
// Verify that all expected flag sets are created
expectedFlagSets := []string{
"server",
"webhook",
"observability",
"kubernetes",
"multicluster",
"cue",
"application",
"oam",
"performance",
"admission",
"resource",
"workflow",
"controller",
"client",
"reconcile",
"sharding",
"feature",
"profiling",
"klog",
}
for _, name := range expectedFlagSets {
fs := fss.FlagSet(name)
assert.NotNil(t, fs, "FlagSet %s should exist", name)
}
}
func TestCoreOptions_FlagHelp(t *testing.T) {
opt := NewCoreOptions()
fss := opt.Flags()
// Test that flags have proper help messages
serverFS := fss.FlagSet("server")
flag := serverFS.Lookup("enable-leader-election")
assert.NotNil(t, flag)
assert.Contains(t, flag.Usage, "Enable leader election")
webhookFS := fss.FlagSet("webhook")
flag = webhookFS.Lookup("use-webhook")
assert.NotNil(t, flag)
assert.Contains(t, flag.Usage, "Enable Admission Webhook")
obsFS := fss.FlagSet("observability")
flag = obsFS.Lookup("log-debug")
assert.NotNil(t, flag)
assert.Contains(t, flag.Usage, "Enable debug logs")
}
func TestCoreOptions_MultipleSyncCalls(t *testing.T) {
// Store original values
origCUEExternal := cuex.EnableExternalPackageForDefaultCompiler
origCUEWatch := cuex.EnableExternalPackageWatchForDefaultCompiler
origWait := wfTypes.MaxWorkflowWaitBackoffTime
origDispatch := resourcekeeper.MaxDispatchConcurrent
origOAMNamespace := oam.SystemDefinitionNamespace
origAppPeriod := commonconfig.ApplicationReSyncPeriod
origPerf := commonconfig.PerfEnabled
// Restore after test
defer func() {
cuex.EnableExternalPackageForDefaultCompiler = origCUEExternal
cuex.EnableExternalPackageWatchForDefaultCompiler = origCUEWatch
wfTypes.MaxWorkflowWaitBackoffTime = origWait
resourcekeeper.MaxDispatchConcurrent = origDispatch
oam.SystemDefinitionNamespace = origOAMNamespace
commonconfig.ApplicationReSyncPeriod = origAppPeriod
commonconfig.PerfEnabled = origPerf
}()
// Test that calling sync multiple times doesn't cause issues
opts := NewCoreOptions()
// Set some values
opts.CUE.EnableExternalPackage = true
opts.CUE.EnableExternalPackageWatch = false
opts.Workflow.MaxWaitBackoffTime = 100
opts.Resource.MaxDispatchConcurrent = 20
opts.OAM.SystemDefinitionNamespace = "test-system"
opts.Application.ReSyncPeriod = 15 * time.Minute
opts.Performance.PerfEnabled = true
// Call sync multiple times
opts.CUE.SyncToCUEGlobals()
opts.CUE.SyncToCUEGlobals()
opts.Workflow.SyncToWorkflowGlobals()
opts.Workflow.SyncToWorkflowGlobals()
opts.Resource.SyncToResourceGlobals()
opts.Resource.SyncToResourceGlobals()
opts.OAM.SyncToOAMGlobals()
opts.OAM.SyncToOAMGlobals()
opts.Application.SyncToApplicationGlobals()
opts.Application.SyncToApplicationGlobals()
opts.Performance.SyncToPerformanceGlobals()
opts.Performance.SyncToPerformanceGlobals()
// Verify values are still correct
assert.True(t, cuex.EnableExternalPackageForDefaultCompiler)
assert.False(t, cuex.EnableExternalPackageWatchForDefaultCompiler)
assert.Equal(t, 100, wfTypes.MaxWorkflowWaitBackoffTime)
assert.Equal(t, 20, resourcekeeper.MaxDispatchConcurrent)
assert.Equal(t, "test-system", oam.SystemDefinitionNamespace)
assert.Equal(t, 15*time.Minute, commonconfig.ApplicationReSyncPeriod)
assert.True(t, commonconfig.PerfEnabled)
}
func TestCoreOptions_SpecialCharactersInStrings(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opt := NewCoreOptions()
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
// Test with special characters and spaces in paths
args := []string{
`--webhook-cert-dir=/path/with spaces/and-special!@#$%chars`,
`--log-file-path=/var/log/kubevela/日本語/логи.log`,
`--health-addr=[::1]:8080`,
`--metrics-addr=0.0.0.0:9090`,
}
err := fs.Parse(args)
require.NoError(t, err)
assert.Equal(t, `/path/with spaces/and-special!@#$%chars`, opt.Webhook.CertDir)
assert.Equal(t, `/var/log/kubevela/日本語/логи.log`, opt.Observability.LogFilePath)
assert.Equal(t, `[::1]:8080`, opt.Server.HealthAddr)
assert.Equal(t, `0.0.0.0:9090`, opt.Observability.MetricsAddr)
}
func TestCoreOptions_ConcurrentAccess(t *testing.T) {
// Test that the options can be accessed concurrently safely
opt := NewCoreOptions()
// Set some values
opt.Server.EnableLeaderElection = true
opt.Workflow.MaxWaitBackoffTime = 100
opt.Resource.MaxDispatchConcurrent = 20
// Simulate concurrent access
done := make(chan bool, 3)
go func() {
for i := 0; i < 100; i++ {
_ = opt.Server.EnableLeaderElection
}
done <- true
}()
go func() {
for i := 0; i < 100; i++ {
_ = opt.Workflow.MaxWaitBackoffTime
}
done <- true
}()
go func() {
for i := 0; i < 100; i++ {
_ = opt.Resource.MaxDispatchConcurrent
}
done <- true
}()
// Wait for all goroutines
for i := 0; i < 3; i++ {
<-done
}
}
func TestCoreOptions_NilPointerSafety(t *testing.T) {
// Ensure NewCoreOptions never returns nil pointers
opt := NewCoreOptions()
// All config modules should be non-nil
assert.NotNil(t, opt.Server)
assert.NotNil(t, opt.Webhook)
assert.NotNil(t, opt.Observability)
assert.NotNil(t, opt.Kubernetes)
assert.NotNil(t, opt.MultiCluster)
assert.NotNil(t, opt.CUE)
assert.NotNil(t, opt.Application)
assert.NotNil(t, opt.OAM)
assert.NotNil(t, opt.Performance)
assert.NotNil(t, opt.Workflow)
assert.NotNil(t, opt.Admission)
assert.NotNil(t, opt.Resource)
assert.NotNil(t, opt.Client)
assert.NotNil(t, opt.Reconcile)
assert.NotNil(t, opt.Sharding)
assert.NotNil(t, opt.Feature)
assert.NotNil(t, opt.Profiling)
assert.NotNil(t, opt.KLog)
assert.NotNil(t, opt.Controller)
}
func TestCoreOptions_FlagPrecedence(t *testing.T) {
// Test that later flags override earlier ones
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opt := NewCoreOptions()
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
// Parse with one value, then parse again with different value
args1 := []string{"--webhook-port=8080"}
err := fs.Parse(args1)
require.NoError(t, err)
assert.Equal(t, 8080, opt.Webhook.WebhookPort)
// Reset and parse with different value
fs = pflag.NewFlagSet("test", pflag.ContinueOnError)
opt = NewCoreOptions()
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
args2 := []string{"--webhook-port=9090"}
err = fs.Parse(args2)
require.NoError(t, err)
assert.Equal(t, 9090, opt.Webhook.WebhookPort)
}
func TestCoreOptions_AllConfigModulesHaveFlags(t *testing.T) {
// Ensure every config module registers at least one flag
opt := NewCoreOptions()
fss := opt.Flags()
configsWithExpectedFlags := map[string][]string{
"server": {"health-addr", "storage-driver", "enable-leader-election"},
"webhook": {"use-webhook", "webhook-cert-dir", "webhook-port"},
"observability": {"metrics-addr", "log-debug", "log-file-path"},
"kubernetes": {"informer-sync-period", "kube-api-qps", "kube-api-burst"},
"multicluster": {"enable-cluster-gateway", "enable-cluster-metrics"},
"cue": {"enable-external-package-for-default-compiler"},
"application": {"application-re-sync-period"},
"oam": {"system-definition-namespace"},
"controller": {"revision-limit", "application-revision-limit", "definition-revision-limit"},
"performance": {"perf-enabled"},
"workflow": {"max-workflow-wait-backoff-time"},
"resource": {"max-dispatch-concurrent"},
}
for setName, expectedFlags := range configsWithExpectedFlags {
fs := fss.FlagSet(setName)
assert.NotNil(t, fs, "FlagSet %s should exist", setName)
for _, flagName := range expectedFlags {
flag := fs.Lookup(flagName)
assert.NotNil(t, flag, "Flag %s should exist in flagset %s", flagName, setName)
}
}
}
func TestCoreOptions_CLIOverridesWork(t *testing.T) {
// This test verifies that CLI flags correctly override default values
// and that the sync methods properly propagate these values to globals
// Store original globals to restore after test
origWait := wfTypes.MaxWorkflowWaitBackoffTime
origFailed := wfTypes.MaxWorkflowFailedBackoffTime
origRetry := wfTypes.MaxWorkflowStepErrorRetryTimes
origDispatch := resourcekeeper.MaxDispatchConcurrent
origOAMNamespace := oam.SystemDefinitionNamespace
origAppPeriod := commonconfig.ApplicationReSyncPeriod
origPerf := commonconfig.PerfEnabled
origCUEExternal := cuex.EnableExternalPackageForDefaultCompiler
origCUEWatch := cuex.EnableExternalPackageWatchForDefaultCompiler
defer func() {
wfTypes.MaxWorkflowWaitBackoffTime = origWait
wfTypes.MaxWorkflowFailedBackoffTime = origFailed
wfTypes.MaxWorkflowStepErrorRetryTimes = origRetry
resourcekeeper.MaxDispatchConcurrent = origDispatch
oam.SystemDefinitionNamespace = origOAMNamespace
commonconfig.ApplicationReSyncPeriod = origAppPeriod
commonconfig.PerfEnabled = origPerf
cuex.EnableExternalPackageForDefaultCompiler = origCUEExternal
cuex.EnableExternalPackageWatchForDefaultCompiler = origCUEWatch
}()
opt := NewCoreOptions()
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
// Verify defaults first
assert.Equal(t, 60, opt.Workflow.MaxWaitBackoffTime, "Default should be 60")
assert.Equal(t, 300, opt.Workflow.MaxFailedBackoffTime, "Default should be 300")
assert.Equal(t, 10, opt.Workflow.MaxStepErrorRetryTimes, "Default should be 10")
assert.Equal(t, 10, opt.Resource.MaxDispatchConcurrent, "Default should be 10")
assert.Equal(t, "vela-system", opt.OAM.SystemDefinitionNamespace, "Default should be vela-system")
assert.Equal(t, false, opt.Performance.PerfEnabled, "Default should be false")
// Parse CLI args with overrides
args := []string{
"--max-workflow-wait-backoff-time=999",
"--max-workflow-failed-backoff-time=888",
"--max-workflow-step-error-retry-times=77",
"--max-dispatch-concurrent=66",
"--system-definition-namespace=custom-ns",
"--application-re-sync-period=20m",
"--perf-enabled=true",
"--enable-external-package-for-default-compiler=true",
"--enable-external-package-watch-for-default-compiler=true",
}
err := fs.Parse(args)
require.NoError(t, err)
// Verify struct fields got CLI values (not defaults)
assert.Equal(t, 999, opt.Workflow.MaxWaitBackoffTime, "CLI override should be 999")
assert.Equal(t, 888, opt.Workflow.MaxFailedBackoffTime, "CLI override should be 888")
assert.Equal(t, 77, opt.Workflow.MaxStepErrorRetryTimes, "CLI override should be 77")
assert.Equal(t, 66, opt.Resource.MaxDispatchConcurrent, "CLI override should be 66")
assert.Equal(t, "custom-ns", opt.OAM.SystemDefinitionNamespace, "CLI override should be custom-ns")
assert.Equal(t, 20*time.Minute, opt.Application.ReSyncPeriod, "CLI override should be 20m")
assert.Equal(t, true, opt.Performance.PerfEnabled, "CLI override should be true")
assert.Equal(t, true, opt.CUE.EnableExternalPackage, "CLI override should be true")
assert.Equal(t, true, opt.CUE.EnableExternalPackageWatch, "CLI override should be true")
// Now sync to globals
opt.Workflow.SyncToWorkflowGlobals()
opt.Resource.SyncToResourceGlobals()
opt.OAM.SyncToOAMGlobals()
opt.Application.SyncToApplicationGlobals()
opt.Performance.SyncToPerformanceGlobals()
opt.CUE.SyncToCUEGlobals()
// Verify globals got the CLI values
assert.Equal(t, 999, wfTypes.MaxWorkflowWaitBackoffTime, "Global should have CLI value")
assert.Equal(t, 888, wfTypes.MaxWorkflowFailedBackoffTime, "Global should have CLI value")
assert.Equal(t, 77, wfTypes.MaxWorkflowStepErrorRetryTimes, "Global should have CLI value")
assert.Equal(t, 66, resourcekeeper.MaxDispatchConcurrent, "Global should have CLI value")
assert.Equal(t, "custom-ns", oam.SystemDefinitionNamespace, "Global should have CLI value")
assert.Equal(t, 20*time.Minute, commonconfig.ApplicationReSyncPeriod, "Global should have CLI value")
assert.Equal(t, true, commonconfig.PerfEnabled, "Global should have CLI value")
assert.Equal(t, true, cuex.EnableExternalPackageForDefaultCompiler, "Global should have CLI value")
assert.Equal(t, true, cuex.EnableExternalPackageWatchForDefaultCompiler, "Global should have CLI value")
}
func TestCoreOptions_CompleteIntegration(t *testing.T) {
// A comprehensive integration test
opt := NewCoreOptions()
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
for _, f := range opt.Flags().FlagSets {
fs.AddFlagSet(f)
}
// Simulate a real-world configuration
args := []string{
// Production-like settings
"--enable-leader-election=true",
"--leader-election-namespace=vela-system",
"--use-webhook=true",
"--webhook-port=9443",
"--metrics-addr=:8080",
"--health-addr=:9440",
"--log-debug=false",
"--log-file-path=/var/log/vela/core.log",
"--log-file-max-size=100",
"--kube-api-qps=100",
"--kube-api-burst=200",
"--enable-cluster-gateway=true",
"--enable-cluster-metrics=true",
"--cluster-metrics-interval=30s",
"--application-re-sync-period=10m",
"--perf-enabled=true",
"--max-dispatch-concurrent=20",
"--max-workflow-wait-backoff-time=120",
"--max-workflow-failed-backoff-time=600",
}
err := fs.Parse(args)
require.NoError(t, err)
// Verify the configuration is production-ready
assert.True(t, opt.Server.EnableLeaderElection, "Leader election should be enabled in production")
assert.Equal(t, "vela-system", opt.Server.LeaderElectionNamespace)
assert.True(t, opt.Webhook.UseWebhook, "Webhook should be enabled in production")
assert.Equal(t, 9443, opt.Webhook.WebhookPort)
assert.False(t, opt.Observability.LogDebug, "Debug logging should be disabled in production")
assert.NotEmpty(t, opt.Observability.LogFilePath, "Log file path should be set in production")
// Verify performance settings
assert.True(t, opt.Performance.PerfEnabled)
assert.Equal(t, 20, opt.Resource.MaxDispatchConcurrent)
// Verify cluster settings
assert.True(t, opt.MultiCluster.EnableClusterGateway)
assert.True(t, opt.MultiCluster.EnableClusterMetrics)
assert.Equal(t, 30*time.Second, opt.MultiCluster.ClusterMetricsInterval)
// Sync all configurations that need it
opt.CUE.SyncToCUEGlobals()
opt.Workflow.SyncToWorkflowGlobals()
opt.Resource.SyncToResourceGlobals()
opt.OAM.SyncToOAMGlobals()
opt.Application.SyncToApplicationGlobals()
opt.Performance.SyncToPerformanceGlobals()
// Verify sync worked
assert.Equal(t, 20, resourcekeeper.MaxDispatchConcurrent)
assert.Equal(t, 120, wfTypes.MaxWorkflowWaitBackoffTime)
assert.Equal(t, 600, wfTypes.MaxWorkflowFailedBackoffTime)
assert.Equal(t, "vela-system", oam.SystemDefinitionNamespace)
assert.Equal(t, 10*time.Minute, commonconfig.ApplicationReSyncPeriod)
assert.True(t, commonconfig.PerfEnabled)
}

View File

@ -18,6 +18,7 @@ package app
import (
"context"
"flag"
"fmt"
"io"
"os"
@ -98,14 +99,32 @@ func NewCoreCommand() *cobra.Command {
}
func run(ctx context.Context, s *options.CoreOptions) error {
// Sync parsed config values to external package global variables
s.Workflow.SyncToWorkflowGlobals()
s.CUE.SyncToCUEGlobals()
s.Application.SyncToApplicationGlobals()
s.Performance.SyncToPerformanceGlobals()
s.Resource.SyncToResourceGlobals()
s.OAM.SyncToOAMGlobals()
restConfig := ctrl.GetConfigOrDie()
restConfig.UserAgent = types.KubeVelaName + "/" + version.GitRevision
restConfig.QPS = float32(s.QPS)
restConfig.Burst = s.Burst
restConfig.QPS = float32(s.Kubernetes.QPS)
restConfig.Burst = s.Kubernetes.Burst
restConfig.Wrap(auth.NewImpersonatingRoundTripper)
// Configure klog based on parsed observability settings
if s.Observability.LogDebug {
_ = flag.Set("v", strconv.Itoa(int(commonconfig.LogDebug)))
}
if s.Observability.LogFilePath != "" {
_ = flag.Set("logtostderr", "false")
_ = flag.Set("log_file", s.Observability.LogFilePath)
_ = flag.Set("log_file_max_size", strconv.FormatUint(s.Observability.LogFileMaxSize, 10))
}
// Set logger (use --dev-logs=true for local development)
if s.DevLogs {
if s.Observability.DevLogs {
logOutput := newColorWriter(os.Stdout)
klog.LogToStderr(false)
klog.SetOutput(logOutput)
@ -122,15 +141,15 @@ func run(ctx context.Context, s *options.CoreOptions) error {
go profiling.StartProfilingServer(nil)
// wrapper the round tripper by multi cluster rewriter
if s.EnableClusterGateway {
if s.MultiCluster.EnableClusterGateway {
client, err := multicluster.Initialize(restConfig, true)
if err != nil {
klog.ErrorS(err, "failed to enable multi-cluster capability")
return err
}
if s.EnableClusterMetrics {
_, err := multicluster.NewClusterMetricsMgr(context.Background(), client, s.ClusterMetricsInterval)
if s.MultiCluster.EnableClusterMetrics {
_, err := multicluster.NewClusterMetricsMgr(context.Background(), client, s.MultiCluster.ClusterMetricsInterval)
if err != nil {
klog.ErrorS(err, "failed to enable multi-cluster-metrics capability")
return err
@ -139,32 +158,32 @@ func run(ctx context.Context, s *options.CoreOptions) error {
}
if utilfeature.DefaultMutableFeatureGate.Enabled(features.ApplyOnce) {
commonconfig.ApplicationReSyncPeriod = s.InformerSyncPeriod
commonconfig.ApplicationReSyncPeriod = s.Kubernetes.InformerSyncPeriod
}
leaderElectionID := util.GenerateLeaderElectionID(types.KubeVelaName, s.ControllerArgs.IgnoreAppWithoutControllerRequirement)
leaderElectionID := util.GenerateLeaderElectionID(types.KubeVelaName, s.Controller.IgnoreAppWithoutControllerRequirement)
leaderElectionID += sharding.GetShardIDSuffix()
mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
Scheme: scheme,
Metrics: metricsserver.Options{
BindAddress: s.MetricsAddr,
BindAddress: s.Observability.MetricsAddr,
},
LeaderElection: s.EnableLeaderElection,
LeaderElectionNamespace: s.LeaderElectionNamespace,
LeaderElection: s.Server.EnableLeaderElection,
LeaderElectionNamespace: s.Server.LeaderElectionNamespace,
LeaderElectionID: leaderElectionID,
WebhookServer: ctrlwebhook.NewServer(ctrlwebhook.Options{
Port: s.WebhookPort,
CertDir: s.CertDir,
Port: s.Webhook.WebhookPort,
CertDir: s.Webhook.CertDir,
}),
HealthProbeBindAddress: s.HealthAddr,
LeaseDuration: &s.LeaseDuration,
RenewDeadline: &s.RenewDeadLine,
RetryPeriod: &s.RetryPeriod,
HealthProbeBindAddress: s.Server.HealthAddr,
LeaseDuration: &s.Server.LeaseDuration,
RenewDeadline: &s.Server.RenewDeadline,
RetryPeriod: &s.Server.RetryPeriod,
NewClient: velaclient.DefaultNewControllerClient,
NewCache: cache.BuildCache(ctx,
ctrlcache.Options{
Scheme: scheme,
SyncPeriod: &s.InformerSyncPeriod,
SyncPeriod: &s.Kubernetes.InformerSyncPeriod,
// SyncPeriod is configured with default value, aka. 10h. First, controller-runtime does not
// recommend use it as a time trigger, instead, it is expected to work for failure tolerance
// of controller-runtime. Additionally, set this value will affect not only application
@ -210,7 +229,7 @@ func run(ctx context.Context, s *options.CoreOptions) error {
klog.ErrorS(err, "Failed to run manager")
return err
}
if s.LogFilePath != "" {
if s.Observability.LogFilePath != "" {
klog.Flush()
}
klog.Info("Safely stops Program...")
@ -228,7 +247,7 @@ func prepareRunInShardingMode(ctx context.Context, mgr manager.Manager, s *optio
}
} else {
klog.Infof("controller running in sharding mode, current shard id: %s", sharding.ShardID)
if err := application.Setup(mgr, *s.ControllerArgs); err != nil {
if err := application.Setup(mgr, s.Controller.Args); err != nil {
return err
}
}
@ -237,16 +256,16 @@ func prepareRunInShardingMode(ctx context.Context, mgr manager.Manager, s *optio
}
func prepareRun(ctx context.Context, mgr manager.Manager, s *options.CoreOptions) error {
if s.UseWebhook {
klog.InfoS("Enable webhook", "server port", strconv.Itoa(s.WebhookPort))
oamwebhook.Register(mgr, *s.ControllerArgs)
if err := waitWebhookSecretVolume(s.CertDir, waitSecretTimeout, waitSecretInterval); err != nil {
if s.Webhook.UseWebhook {
klog.InfoS("Enable webhook", "server port", strconv.Itoa(s.Webhook.WebhookPort))
oamwebhook.Register(mgr, s.Controller.Args)
if err := waitWebhookSecretVolume(s.Webhook.CertDir, waitSecretTimeout, waitSecretInterval); err != nil {
klog.ErrorS(err, "Unable to get webhook secret")
return err
}
}
if err := oamv1beta1.Setup(mgr, *s.ControllerArgs); err != nil {
if err := oamv1beta1.Setup(mgr, s.Controller.Args); err != nil {
klog.ErrorS(err, "Unable to setup the oam controller")
return err
}

View File

@ -16,10 +16,6 @@ limitations under the License.
package core_oam_dev
import (
"github.com/spf13/pflag"
)
// Args args used by controller
type Args struct {
@ -47,17 +43,3 @@ type Args struct {
// IgnoreDefinitionWithoutControllerRequirement indicates that trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation.
IgnoreDefinitionWithoutControllerRequirement bool
}
// AddFlags adds flags to the specified FlagSet
func (a *Args) AddFlags(fs *pflag.FlagSet, c *Args) {
fs.IntVar(&a.RevisionLimit, "revision-limit", c.RevisionLimit,
"RevisionLimit is the maximum number of revisions that will be maintained. The default value is 50.")
fs.IntVar(&a.AppRevisionLimit, "application-revision-limit", c.AppRevisionLimit,
"application-revision-limit is the maximum number of application useless revisions that will be maintained, if the useless revisions exceed this number, older ones will be GCed first.The default value is 10.")
fs.IntVar(&a.DefRevisionLimit, "definition-revision-limit", c.DefRevisionLimit,
"definition-revision-limit is the maximum number of component/trait definition useless revisions that will be maintained, if the useless revisions exceed this number, older ones will be GCed first.The default value is 20.")
fs.BoolVar(&a.AutoGenWorkloadDefinition, "autogen-workload-definition", c.AutoGenWorkloadDefinition, "Automatic generated workloadDefinition which componentDefinition refers to.")
fs.IntVar(&a.ConcurrentReconciles, "concurrent-reconciles", c.ConcurrentReconciles, "concurrent-reconciles is the concurrent reconcile number of the controller. The default value is 4")
fs.BoolVar(&a.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", c.IgnoreAppWithoutControllerRequirement, "If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation")
fs.BoolVar(&a.IgnoreDefinitionWithoutControllerRequirement, "ignore-definition-without-controller-version", c.IgnoreDefinitionWithoutControllerRequirement, "If true, trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation")
}