diff --git a/cmd/webhook/app/BUILD.bazel b/cmd/webhook/app/BUILD.bazel index d031fdbc8..bb78b9ec1 100644 --- a/cmd/webhook/app/BUILD.bazel +++ b/cmd/webhook/app/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "//internal/apis/config/webhook:go_default_library", "//pkg/logs:go_default_library", "//pkg/util:go_default_library", + "//pkg/util/feature:go_default_library", "//pkg/webhook:go_default_library", "//pkg/webhook/authority:go_default_library", "//pkg/webhook/configfile:go_default_library", diff --git a/cmd/webhook/app/options/BUILD.bazel b/cmd/webhook/app/options/BUILD.bazel index 10f13e741..aa32225d9 100644 --- a/cmd/webhook/app/options/BUILD.bazel +++ b/cmd/webhook/app/options/BUILD.bazel @@ -13,6 +13,7 @@ go_library( "//internal/apis/config/webhook/scheme:go_default_library", "//pkg/apis/config/webhook/v1alpha1:go_default_library", "//pkg/logs:go_default_library", + "//pkg/util/feature:go_default_library", "@com_github_spf13_pflag//:go_default_library", "@io_k8s_component_base//cli/flag:go_default_library", ], diff --git a/cmd/webhook/app/options/options.go b/cmd/webhook/app/options/options.go index cbbedebf5..9cdbd44f7 100644 --- a/cmd/webhook/app/options/options.go +++ b/cmd/webhook/app/options/options.go @@ -25,6 +25,7 @@ import ( config "github.com/jetstack/cert-manager/internal/apis/config/webhook" configscheme "github.com/jetstack/cert-manager/internal/apis/config/webhook/scheme" configv1alpha1 "github.com/jetstack/cert-manager/pkg/apis/config/webhook/v1alpha1" + utilfeature "github.com/jetstack/cert-manager/pkg/util/feature" ) // WebhookFlags defines options that can only be configured via flags. @@ -88,5 +89,6 @@ func AddConfigFlags(fs *pflag.FlagSet, c *config.WebhookConfiguration) { fs.StringVar(&c.TLSConfig.MinTLSVersion, "tls-min-version", c.TLSConfig.MinTLSVersion, "Minimum TLS version supported. "+ "Possible values: "+strings.Join(tlsPossibleVersions, ", ")) - + fs.Var(cliflag.NewMapStringBool(&c.FeatureGates), "feature-gates", "A set of key=value pairs that describe feature gates for alpha/experimental features. "+ + "Options are:\n"+strings.Join(utilfeature.DefaultFeatureGate.KnownFeatures(), "\n")) } diff --git a/cmd/webhook/app/webhook.go b/cmd/webhook/app/webhook.go index 7690156cd..b62bdeddf 100644 --- a/cmd/webhook/app/webhook.go +++ b/cmd/webhook/app/webhook.go @@ -34,6 +34,7 @@ import ( config "github.com/jetstack/cert-manager/internal/apis/config/webhook" logf "github.com/jetstack/cert-manager/pkg/logs" "github.com/jetstack/cert-manager/pkg/util" + utilfeature "github.com/jetstack/cert-manager/pkg/util/feature" "github.com/jetstack/cert-manager/pkg/webhook" "github.com/jetstack/cert-manager/pkg/webhook/authority" "github.com/jetstack/cert-manager/pkg/webhook/configfile" @@ -165,6 +166,12 @@ func NewServerCommand(stopCh <-chan struct{}) *cobra.Command { return } + // set feature gates from initial flags-based config + if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(webhookConfig.FeatureGates); err != nil { + log.Error(err, "Failed to set feature gates from initial flags-based config") + os.Exit(1) + } + if err := options.ValidateWebhookFlags(webhookFlags); err != nil { log.Error(err, "Failed to validate webhook flags") os.Exit(1) @@ -181,6 +188,11 @@ func NewServerCommand(stopCh <-chan struct{}) *cobra.Command { log.Error(err, "Failed to merge flags with config file values") os.Exit(1) } + // update feature gates based on new config + if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(webhookConfig.FeatureGates); err != nil { + log.Error(err, "Failed to set feature gates from config file") + os.Exit(1) + } } srv, err := NewServerWithOptions(log, *webhookFlags, *webhookConfig) diff --git a/devel/addon/certmanager/install.sh b/devel/addon/certmanager/install.sh index 56c4834d0..201824e2d 100755 --- a/devel/addon/certmanager/install.sh +++ b/devel/addon/certmanager/install.sh @@ -72,6 +72,7 @@ helm upgrade \ --set startupapicheck.image.tag="${APP_VERSION}" \ --set installCRDs=true \ --set featureGates="${FEATURE_GATES//,/\\,}" `# escape commas in --set by replacing , with \, (see https://github.com/helm/helm/issues/2952)` \ + --set "webhook.extraArgs={--feature-gates=AllAlpha=true}" \ --set "extraArgs={--dns01-recursive-nameservers=${SERVICE_IP_PREFIX}.16:53,--dns01-recursive-nameservers-only=true}" \ "$RELEASE_NAME" \ "$REPO_ROOT/bazel-bin/deploy/charts/cert-manager/cert-manager.tgz" diff --git a/internal/BUILD.bazel b/internal/BUILD.bazel index cbb19b1f6..4c5f9e374 100644 --- a/internal/BUILD.bazel +++ b/internal/BUILD.bazel @@ -19,6 +19,7 @@ filegroup( "//internal/ingress:all-srcs", "//internal/test/paths:all-srcs", "//internal/vault:all-srcs", + "//internal/webhook/feature:all-srcs", ], tags = ["automanaged"], visibility = ["//visibility:public"], diff --git a/internal/apis/config/webhook/types.go b/internal/apis/config/webhook/types.go index c08f43b6e..0402eb4da 100644 --- a/internal/apis/config/webhook/types.go +++ b/internal/apis/config/webhook/types.go @@ -50,6 +50,12 @@ type WebhookConfiguration struct { // pprofAddress configures the address on which /debug/pprof endpoint will be served if enabled. // Defaults to 'localhost:6060'. PprofAddress string + + // featureGates is a map of feature names to bools that enable or disable experimental + // features. + // Default: nil + // +optional + FeatureGates map[string]bool } // TLSConfig configures how TLS certificates are sourced for serving. diff --git a/internal/apis/config/webhook/v1alpha1/zz_generated.conversion.go b/internal/apis/config/webhook/v1alpha1/zz_generated.conversion.go index bcbba6ce8..cc7af8172 100644 --- a/internal/apis/config/webhook/v1alpha1/zz_generated.conversion.go +++ b/internal/apis/config/webhook/v1alpha1/zz_generated.conversion.go @@ -170,6 +170,7 @@ func autoConvert_v1alpha1_WebhookConfiguration_To_webhook_WebhookConfiguration(i out.APIServerHost = in.APIServerHost out.EnablePprof = in.EnablePprof out.PprofAddress = in.PprofAddress + out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) return nil } @@ -188,6 +189,7 @@ func autoConvert_webhook_WebhookConfiguration_To_v1alpha1_WebhookConfiguration(i out.APIServerHost = in.APIServerHost out.EnablePprof = in.EnablePprof out.PprofAddress = in.PprofAddress + out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) return nil } diff --git a/internal/apis/config/webhook/zz_generated.deepcopy.go b/internal/apis/config/webhook/zz_generated.deepcopy.go index 862f4692d..aeeba9d7f 100644 --- a/internal/apis/config/webhook/zz_generated.deepcopy.go +++ b/internal/apis/config/webhook/zz_generated.deepcopy.go @@ -100,6 +100,13 @@ func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) { **out = **in } in.TLSConfig.DeepCopyInto(&out.TLSConfig) + if in.FeatureGates != nil { + in, out := &in.FeatureGates, &out.FeatureGates + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } diff --git a/internal/webhook/feature/BUILD.bazel b/internal/webhook/feature/BUILD.bazel new file mode 100644 index 000000000..f28d9d01a --- /dev/null +++ b/internal/webhook/feature/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["features.go"], + importpath = "github.com/jetstack/cert-manager/internal/webhook/feature", + visibility = ["//:__subpackages__"], + deps = [ + "//pkg/util/feature:go_default_library", + "@io_k8s_component_base//featuregate:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/internal/webhook/feature/features.go b/internal/webhook/feature/features.go new file mode 100644 index 000000000..ac05dd7b3 --- /dev/null +++ b/internal/webhook/feature/features.go @@ -0,0 +1,31 @@ +package feature + +import ( + utilfeature "github.com/jetstack/cert-manager/pkg/util/feature" + "k8s.io/component-base/featuregate" +) + +const ( +// FeatureName will enable XYZ feature. +// Fill this section out with additional details about the feature. +// +// Owner (responsible for graduating feature through to GA): @username +// Alpha: vX.Y +// Beta: ... +//FeatureName featuregate.Feature = "FeatureName" + +// Insert features below this line to maintain the template above. +) + +func init() { + utilfeature.DefaultMutableFeatureGate.Add(webhookFeatureGates) +} + +// webhookFeatureGates defines all feature gates for the webhook component. +// To add a new feature, define a key for it above and add it here. +// To check whether a feature is enabled, use: +// utilfeature.DefaultFeatureGate.Enabled(feature.FeatureName) +// Where utilfeature is github.com/jetstack/cert-manager/pkg/util/feature. +var webhookFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ + //FeatureName: {Default: false, PreRelease: featuregate.Alpha}, +} diff --git a/pkg/apis/config/webhook/v1alpha1/types.go b/pkg/apis/config/webhook/v1alpha1/types.go index 441bcbd9d..13b01a847 100644 --- a/pkg/apis/config/webhook/v1alpha1/types.go +++ b/pkg/apis/config/webhook/v1alpha1/types.go @@ -48,6 +48,12 @@ type WebhookConfiguration struct { // pprofAddress configures the address on which /debug/pprof endpoint will be served if enabled. // Defaults to 'localhost:6060'. PprofAddress string `json:"pprofAddress,omitempty"` + + // featureGates is a map of feature names to bools that enable or disable experimental + // features. + // Default: nil + // +optional + FeatureGates map[string]bool `json:"featureGates,omitempty"` } // TLSConfig configures how TLS certificates are sourced for serving. diff --git a/pkg/apis/config/webhook/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/config/webhook/v1alpha1/zz_generated.deepcopy.go index 3faedb8d1..d5674181e 100644 --- a/pkg/apis/config/webhook/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/config/webhook/v1alpha1/zz_generated.deepcopy.go @@ -100,6 +100,13 @@ func (in *WebhookConfiguration) DeepCopyInto(out *WebhookConfiguration) { **out = **in } in.TLSConfig.DeepCopyInto(&out.TLSConfig) + if in.FeatureGates != nil { + in, out := &in.FeatureGates, &out.FeatureGates + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return }