diff --git a/.changelog/3811.txt b/.changelog/3811.txt new file mode 100644 index 0000000000..e333a9df84 --- /dev/null +++ b/.changelog/3811.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +api-gateway: Expose prometheus scrape metrics on api-gateway pods. +``` diff --git a/charts/consul/templates/crd-gatewayclassconfigs-v1.yaml b/charts/consul/templates/crd-gatewayclassconfigs-v1.yaml index 130db72a22..41023c19dc 100644 --- a/charts/consul/templates/crd-gatewayclassconfigs-v1.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs-v1.yaml @@ -131,6 +131,23 @@ spec: for gateway containers format: int32 type: integer + metrics: + description: Metrics defines how to configure the metrics for a gateway. + properties: + enabled: + description: Enable metrics for this class of gateways. If unspecified, + will inherit behavior from the global Helm configuration. + type: boolean + path: + description: The path used for metrics. + type: string + port: + description: The port used for metrics. + format: int32 + maximum: 65535 + minimum: 1024 + type: integer + type: object nodeSelector: additionalProperties: type: string diff --git a/charts/consul/templates/gateway-resources-job.yaml b/charts/consul/templates/gateway-resources-job.yaml index 0af0e45033..e43efc8a9a 100644 --- a/charts/consul/templates/gateway-resources-job.yaml +++ b/charts/consul/templates/gateway-resources-job.yaml @@ -79,6 +79,15 @@ spec: - -openshift-scc-name={{ .Values.connectInject.apiGateway.managedGatewayClass.openshiftSCCName }} {{- end }} - -map-privileged-container-ports={{ .Values.connectInject.apiGateway.managedGatewayClass.mapPrivilegedContainerPorts }} + {{- if (ne (.Values.connectInject.apiGateway.managedGatewayClass.metrics.enabled | toString) "-") }} + - -enable-metrics={{ .Values.connectInject.apiGateway.managedGatewayClass.metrics.enabled | toString }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.metrics.path }} + - -metrics-path={{ .Values.connectInject.apiGateway.managedGatewayClass.metrics.path }} + {{- end }} + {{- if .Values.connectInject.apiGateway.managedGatewayClass.metrics.port }} + - -metrics-port={{ .Values.connectInject.apiGateway.managedGatewayClass.metrics.port }} + {{- end }} resources: requests: memory: "50Mi" diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 27a6e7706f..7d4f00210c 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -2417,6 +2417,19 @@ connectInject: # @type: string service: null + # Metrics settings for gateways created with this gateway class configuration. + metrics: + # This value enables or disables metrics collection on a gateway, overriding the global gateway metrics collection settings. + # @type: boolean + enabled: "-" + # This value sets the port to use for scraping gateway metrics via prometheus, defaults to 20200 if not set. Must be in the port + # range of 1024-65535. + # @type: int + port: null + # This value sets the path to use for scraping gateway metrics via prometheus, defaults to /metrics if not set. + # @type: string + path: null + # The resource settings for Pods handling traffic for Gateway API. # @recurse: false # @type: map diff --git a/control-plane/api-gateway/binding/binder.go b/control-plane/api-gateway/binding/binder.go index c6557985b5..c704a6b04e 100644 --- a/control-plane/api-gateway/binding/binder.go +++ b/control-plane/api-gateway/binding/binder.go @@ -67,6 +67,9 @@ type BinderConfig struct { // Policies is a list containing all GatewayPolicies that are part of the Gateway Deployment Policies []v1alpha1.GatewayPolicy + + // Configuration from helm. + HelmConfig common.HelmConfig } // Binder is used for generating a Snapshot of all operations that should occur both @@ -199,7 +202,8 @@ func (b *Binder) Snapshot() *Snapshot { OnUpdate: b.handleGatewaySyncStatus(snapshot, &b.config.Gateway, consulStatus), }) - registrations := registrationsForPods(entry.Namespace, b.config.Gateway, registrationPods) + metricsConfig := common.GatewayMetricsConfig(b.config.Gateway, *gatewayClassConfig, b.config.HelmConfig) + registrations := registrationsForPods(metricsConfig, entry.Namespace, b.config.Gateway, registrationPods) snapshot.Consul.Registrations = registrations // deregister any not explicitly registered service diff --git a/control-plane/api-gateway/binding/registration.go b/control-plane/api-gateway/binding/registration.go index ae26ab51f6..489e765c61 100644 --- a/control-plane/api-gateway/binding/registration.go +++ b/control-plane/api-gateway/binding/registration.go @@ -6,6 +6,7 @@ package binding import ( "fmt" + gatewaycommon "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul/api" @@ -22,22 +23,34 @@ const ( // consulKubernetesCheckName is the name of health check in Consul for Kubernetes readiness status. consulKubernetesCheckName = "Kubernetes Readiness Check" + + // metricsConfiguration is the configuration key for binding a prometheus port to the envoy instance. + metricsConfiguration = "envoy_prometheus_bind_addr" ) -func registrationsForPods(namespace string, gateway gwv1beta1.Gateway, pods []corev1.Pod) []api.CatalogRegistration { +func registrationsForPods(metrics gatewaycommon.MetricsConfig, namespace string, gateway gwv1beta1.Gateway, pods []corev1.Pod) []api.CatalogRegistration { registrations := []api.CatalogRegistration{} for _, pod := range pods { - registrations = append(registrations, registrationForPod(namespace, gateway, pod)) + registrations = append(registrations, registrationForPod(metrics, namespace, gateway, pod)) } return registrations } -func registrationForPod(namespace string, gateway gwv1beta1.Gateway, pod corev1.Pod) api.CatalogRegistration { +func registrationForPod(metrics gatewaycommon.MetricsConfig, namespace string, gateway gwv1beta1.Gateway, pod corev1.Pod) api.CatalogRegistration { healthStatus := api.HealthCritical if isPodReady(pod) { healthStatus = api.HealthPassing } + var proxyConfigOverrides *api.AgentServiceConnectProxyConfig + if metrics.Enabled { + proxyConfigOverrides = &api.AgentServiceConnectProxyConfig{ + Config: map[string]interface{}{ + metricsConfiguration: fmt.Sprintf("%s:%d", pod.Status.PodIP, metrics.Port), + }, + } + } + return api.CatalogRegistration{ Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), Address: pod.Status.HostIP, @@ -50,6 +63,7 @@ func registrationForPod(namespace string, gateway gwv1beta1.Gateway, pod corev1. Service: gateway.Name, Address: pod.Status.PodIP, Namespace: namespace, + Proxy: proxyConfigOverrides, Meta: map[string]string{ constants.MetaKeyPodName: pod.Name, constants.MetaKeyKubeNS: pod.Namespace, diff --git a/control-plane/api-gateway/binding/registration_test.go b/control-plane/api-gateway/binding/registration_test.go index 356915f9f7..6f60257112 100644 --- a/control-plane/api-gateway/binding/registration_test.go +++ b/control-plane/api-gateway/binding/registration_test.go @@ -6,6 +6,7 @@ package binding import ( "testing" + "github.com/hashicorp/consul-k8s/control-plane/api-gateway/common" "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -68,7 +69,7 @@ func TestRegistrationsForPods_Health(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - registrations := registrationsForPods(tt.consulNamespace, tt.gateway, tt.pods) + registrations := registrationsForPods(common.MetricsConfig{}, tt.consulNamespace, tt.gateway, tt.pods) require.Len(t, registrations, len(tt.expected)) for i := range registrations { diff --git a/control-plane/api-gateway/common/helm_config.go b/control-plane/api-gateway/common/helm_config.go index 9b0ab1d7e8..ecf245c04c 100644 --- a/control-plane/api-gateway/common/helm_config.go +++ b/control-plane/api-gateway/common/helm_config.go @@ -43,6 +43,16 @@ type HelmConfig struct { // defined on a Gateway. MapPrivilegedServicePorts int + // EnableGatewayMetrics indicates whether or not gateway metrics should be enabled + // by default on a deployed gateway, passed from the helm chart via command-line flags to our controller. + EnableGatewayMetrics bool + + // The default path to use for scraping prometheus metrics, passed from the helm chart via command-line flags to our controller. + DefaultPrometheusScrapePath string + + // The default port to use for scraping prometheus metrics, passed from the helm chart via command-line flags to our controller. + DefaultPrometheusScrapePort string + InitContainerResources *v1.ResourceRequirements } diff --git a/control-plane/api-gateway/common/metrics.go b/control-plane/api-gateway/common/metrics.go new file mode 100644 index 0000000000..8ba582c8a5 --- /dev/null +++ b/control-plane/api-gateway/common/metrics.go @@ -0,0 +1,103 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "strconv" + + "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +const ( + defaultScrapePort = 20200 + defaultScrapePath = "/metrics" +) + +type MetricsConfig struct { + Enabled bool + Path string + Port int +} + +func gatewayMetricsEnabled(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config HelmConfig) bool { + // first check our annotations, if something is there, then it means we've explicitly + // annotated metrics collection + if scrape, isSet := gateway.Annotations[constants.AnnotationEnableMetrics]; isSet { + enabled, err := strconv.ParseBool(scrape) + if err == nil { + return enabled + } + // TODO: log an error + // we fall through to the other metrics enabled checks + } + + // if it's not set on the annotation, then we check to see if it's set on the GatewayClassConfig + if gcc.Spec.Metrics.Enabled != nil { + return *gcc.Spec.Metrics.Enabled + } + + // otherwise, fallback to the global helm setting + return config.EnableGatewayMetrics +} + +func fetchPortString(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config HelmConfig) string { + // first check our annotations, if something is there, then it means we've explicitly + // annotated metrics collection + if portString, isSet := gateway.Annotations[constants.AnnotationPrometheusScrapePort]; isSet { + return portString + } + + // if it's not set on the annotation, then we check to see if it's set on the GatewayClassConfig + if gcc.Spec.Metrics.Port != nil { + return strconv.Itoa(int(*gcc.Spec.Metrics.Port)) + } + + // otherwise, fallback to the global helm setting + return config.DefaultPrometheusScrapePort +} + +func gatewayMetricsPort(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config HelmConfig) int { + portString := fetchPortString(gateway, gcc, config) + + port, err := strconv.Atoi(portString) + if err != nil { + // if we can't parse the port string, just use the default + // TODO: log an error + return defaultScrapePort + } + + if port < 1024 || port > 65535 { + // if we requested a privileged port, use the default + // TODO: log an error + return defaultScrapePort + } + + return port +} + +func gatewayMetricsPath(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config HelmConfig) string { + // first check our annotations, if something is there, then it means we've explicitly + // annotated metrics collection + if path, isSet := gateway.Annotations[constants.AnnotationPrometheusScrapePath]; isSet { + return path + } + + // if it's not set on the annotation, then we check to see if it's set on the GatewayClassConfig + if gcc.Spec.Metrics.Path != nil { + return *gcc.Spec.Metrics.Path + } + + // otherwise, fallback to the global helm setting + return config.DefaultPrometheusScrapePath +} + +func GatewayMetricsConfig(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayClassConfig, config HelmConfig) MetricsConfig { + return MetricsConfig{ + Enabled: gatewayMetricsEnabled(gateway, gcc, config), + Path: gatewayMetricsPath(gateway, gcc, config), + Port: gatewayMetricsPort(gateway, gcc, config), + } +} diff --git a/control-plane/api-gateway/controllers/gateway_controller.go b/control-plane/api-gateway/controllers/gateway_controller.go index 8447e69f64..537100fd70 100644 --- a/control-plane/api-gateway/controllers/gateway_controller.go +++ b/control-plane/api-gateway/controllers/gateway_controller.go @@ -204,6 +204,7 @@ func (r *GatewayController) Reconcile(ctx context.Context, req ctrl.Request) (ct ConsulGateway: consulGateway, ConsulGatewayServices: consulServices, Policies: policies, + HelmConfig: r.HelmConfig, }) updates := binder.Snapshot() diff --git a/control-plane/api-gateway/gatekeeper/dataplane.go b/control-plane/api-gateway/gatekeeper/dataplane.go index 090464e9c8..16839cbb09 100644 --- a/control-plane/api-gateway/gatekeeper/dataplane.go +++ b/control-plane/api-gateway/gatekeeper/dataplane.go @@ -22,12 +22,11 @@ const ( netBindCapability = "NET_BIND_SERVICE" consulDataplaneDNSBindHost = "127.0.0.1" consulDataplaneDNSBindPort = 8600 - defaultPrometheusScrapePath = "/metrics" defaultEnvoyProxyConcurrency = 1 volumeName = "consul-connect-inject-data" ) -func consulDataplaneContainer(config common.HelmConfig, gcc v1alpha1.GatewayClassConfig, name, namespace string) (corev1.Container, error) { +func consulDataplaneContainer(metrics common.MetricsConfig, config common.HelmConfig, gcc v1alpha1.GatewayClassConfig, name, namespace string) (corev1.Container, error) { // Extract the service account token's volume mount. var ( err error @@ -38,7 +37,7 @@ func consulDataplaneContainer(config common.HelmConfig, gcc v1alpha1.GatewayClas bearerTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" } - args, err := getDataplaneArgs(namespace, config, bearerTokenFile, name) + args, err := getDataplaneArgs(metrics, namespace, config, bearerTokenFile, name) if err != nil { return corev1.Container{}, err } @@ -100,6 +99,15 @@ func consulDataplaneContainer(config common.HelmConfig, gcc v1alpha1.GatewayClas Name: "proxy-health", ContainerPort: int32(constants.ProxyDefaultHealthPort), }) + + if metrics.Enabled { + container.Ports = append(container.Ports, corev1.ContainerPort{ + Name: "prometheus", + ContainerPort: int32(metrics.Port), + Protocol: corev1.ProtocolTCP, + }) + } + // Configure the resource requests and limits for the proxy if they are set. if gcc.Spec.DeploymentSpec.Resources != nil { container.Resources = *gcc.Spec.DeploymentSpec.Resources @@ -122,7 +130,7 @@ func consulDataplaneContainer(config common.HelmConfig, gcc v1alpha1.GatewayClas return container, nil } -func getDataplaneArgs(namespace string, config common.HelmConfig, bearerTokenFile string, name string) ([]string, error) { +func getDataplaneArgs(metrics common.MetricsConfig, namespace string, config common.HelmConfig, bearerTokenFile string, name string) ([]string, error) { proxyIDFileName := "/consul/connect-inject/proxyid" envoyConcurrency := defaultEnvoyProxyConcurrency @@ -170,9 +178,10 @@ func getDataplaneArgs(namespace string, config common.HelmConfig, bearerTokenFil args = append(args, fmt.Sprintf("-envoy-admin-bind-port=%d", 19000)) - // Set a default scrape path that can be overwritten by the annotation. - prometheusScrapePath := defaultPrometheusScrapePath - args = append(args, "-telemetry-prom-scrape-path="+prometheusScrapePath) + if metrics.Enabled { + // Set up metrics collection. + args = append(args, "-telemetry-prom-scrape-path="+metrics.Path) + } return args, nil } diff --git a/control-plane/api-gateway/gatekeeper/deployment.go b/control-plane/api-gateway/gatekeeper/deployment.go index d85fbf8ef1..d3bb56a69f 100644 --- a/control-plane/api-gateway/gatekeeper/deployment.go +++ b/control-plane/api-gateway/gatekeeper/deployment.go @@ -5,6 +5,7 @@ package gatekeeper import ( "context" + "strconv" "github.com/google/go-cmp/cmp" appsv1 "k8s.io/api/apps/v1" @@ -92,7 +93,21 @@ func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayC return nil, err } - container, err := consulDataplaneContainer(config, gcc, gateway.Name, gateway.Namespace) + annotations := map[string]string{ + "consul.hashicorp.com/connect-inject": "false", + constants.AnnotationGatewayConsulServiceName: gateway.Name, + constants.AnnotationGatewayKind: "api-gateway", + } + + metrics := common.GatewayMetricsConfig(gateway, gcc, config) + + if metrics.Enabled { + annotations[constants.AnnotationPrometheusScrape] = "true" + annotations[constants.AnnotationPrometheusPath] = metrics.Path + annotations[constants.AnnotationPrometheusPort] = strconv.Itoa(metrics.Port) + } + + container, err := consulDataplaneContainer(metrics, config, gcc, gateway.Name, gateway.Namespace) if err != nil { return nil, err } @@ -110,12 +125,8 @@ func (g *Gatekeeper) deployment(gateway gwv1beta1.Gateway, gcc v1alpha1.GatewayC }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: common.LabelsForGateway(&gateway), - Annotations: map[string]string{ - constants.AnnotationInject: "false", - constants.AnnotationGatewayConsulServiceName: gateway.Name, - constants.AnnotationGatewayKind: "api-gateway", - }, + Labels: common.LabelsForGateway(&gateway), + Annotations: annotations, }, Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ diff --git a/control-plane/api/v1alpha1/api_gateway_types.go b/control-plane/api/v1alpha1/api_gateway_types.go index 90f6376d98..bc7b65fbe4 100644 --- a/control-plane/api/v1alpha1/api_gateway_types.go +++ b/control-plane/api/v1alpha1/api_gateway_types.go @@ -65,6 +65,9 @@ type GatewayClassConfigSpec struct { // The value to add to privileged ports ( ports < 1024) for gateway containers MapPrivilegedContainerPorts int32 `json:"mapPrivilegedContainerPorts,omitempty"` + + // Metrics defines how to configure the metrics for a gateway. + Metrics MetricsSpec `json:"metrics,omitempty"` } // +k8s:deepcopy-gen=true @@ -90,6 +93,22 @@ type DeploymentSpec struct { Resources *corev1.ResourceRequirements `json:"resources,omitempty"` } +// +k8s:deepcopy-gen=true + +type MetricsSpec struct { + // +kubebuilder:validation:Maximum=65535 + // +kubebuilder:validation:Minimum=1024 + // The port used for metrics. + Port *int32 `json:"port,omitempty"` + + // The path used for metrics. + Path *string `json:"path,omitempty"` + + // Enable metrics for this class of gateways. If unspecified, will inherit + // behavior from the global Helm configuration. + Enabled *bool `json:"enabled,omitempty"` +} + //+kubebuilder:object:generate=true // CopyAnnotationsSpec defines the annotations that should be copied to the gateway service. diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index 320f05510f..2a1854d178 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -555,6 +555,7 @@ func (in *GatewayClassConfigSpec) DeepCopyInto(out *GatewayClassConfigSpec) { } in.DeploymentSpec.DeepCopyInto(&out.DeploymentSpec) in.CopyAnnotations.DeepCopyInto(&out.CopyAnnotations) + in.Metrics.DeepCopyInto(&out.Metrics) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayClassConfigSpec. @@ -1977,6 +1978,36 @@ func (in *MeshTLSConfig) DeepCopy() *MeshTLSConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsSpec) DeepCopyInto(out *MetricsSpec) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int32) + **out = **in + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(string) + **out = **in + } + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsSpec. +func (in *MetricsSpec) DeepCopy() *MetricsSpec { + if in == nil { + return nil + } + out := new(MetricsSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { *out = *in diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml index ff3158f2a7..c2a857db34 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -127,6 +127,23 @@ spec: for gateway containers format: int32 type: integer + metrics: + description: Metrics defines how to configure the metrics for a gateway. + properties: + enabled: + description: Enable metrics for this class of gateways. If unspecified, + will inherit behavior from the global Helm configuration. + type: boolean + path: + description: The path used for metrics. + type: string + port: + description: The port used for metrics. + format: int32 + maximum: 65535 + minimum: 1024 + type: integer + type: object nodeSelector: additionalProperties: type: string diff --git a/control-plane/subcommand/gateway-resources/command.go b/control-plane/subcommand/gateway-resources/command.go index 911fb0afd9..946e2d2703 100644 --- a/control-plane/subcommand/gateway-resources/command.go +++ b/control-plane/subcommand/gateway-resources/command.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "os" + "strconv" "sync" "time" @@ -94,6 +95,10 @@ type Command struct { flagMapPrivilegedContainerPorts int + flagEnableMetrics string + flagMetricsPort string + flagMetricsPath string + k8sClient client.Client once sync.Once @@ -156,6 +161,10 @@ func (c *Command) init() { "gateway container.", ) + c.flags.StringVar(&c.flagEnableMetrics, "enable-metrics", "", "specify as 'true' or 'false' to enable or disable metrics collection") + c.flags.StringVar(&c.flagMetricsPath, "metrics-path", "", "specify to set the path used for metrics scraping") + c.flags.StringVar(&c.flagMetricsPort, "metrics-port", "", "specify to set the port used for metrics scraping") + c.flags.StringVar(&c.flagGatewayConfigLocation, "gateway-config-file-location", gatewayConfigFilename, "specify a different location for where the gateway config file is") @@ -262,6 +271,17 @@ func (c *Command) Run(args []string) int { }, } + if metricsEnabled, isSet := getMetricsEnabled(c.flagEnableMetrics); isSet { + classConfig.Spec.Metrics.Enabled = &metricsEnabled + if port, isValid := getScrapePort(c.flagMetricsPort); isValid { + port32 := int32(port) + classConfig.Spec.Metrics.Port = &port32 + } + if path, isSet := getScrapePath(c.flagMetricsPath); isSet { + classConfig.Spec.Metrics.Path = &path + } + } + class := &gwv1beta1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{Name: c.flagGatewayClassName, Labels: labels}, Spec: gwv1beta1.GatewayClassSpec{ @@ -346,6 +366,18 @@ func (c *Command) validateFlags() error { } } + if c.flagEnableMetrics != "" { + if _, valid := getMetricsEnabled(c.flagEnableMetrics); !valid { + return errors.New("-enable-metrics must be either 'true' or 'false'") + } + } + + if c.flagMetricsPort != "" { + if _, valid := getScrapePort(c.flagMetricsPort); !valid { + return errors.New("-metrics-port must be a valid unprivileged port number") + } + } + return nil } @@ -604,6 +636,35 @@ func exponentialBackoffWithMaxIntervalAndTime() *backoff.ExponentialBackOff { return backoff } +func getScrapePort(v string) (int, bool) { + port, err := strconv.Atoi(v) + if err != nil { + // we only use the port if it's actually valid + return 0, false + } + if port < 1024 || port > 65535 { + return 0, false + } + return port, true +} + +func getScrapePath(v string) (string, bool) { + if v == "" { + return "", false + } + return v, true +} + +func getMetricsEnabled(v string) (bool, bool) { + if v == "true" { + return true, true + } + if v == "false" { + return false, true + } + return false, false +} + func nonZeroOrNil(v int) *int32 { if v == 0 { return nil diff --git a/control-plane/subcommand/inject-connect/v1controllers.go b/control-plane/subcommand/inject-connect/v1controllers.go index e923f6da9d..37ed344dba 100644 --- a/control-plane/subcommand/inject-connect/v1controllers.go +++ b/control-plane/subcommand/inject-connect/v1controllers.go @@ -114,22 +114,25 @@ func (c *Command) configureV1Controllers(ctx context.Context, mgr manager.Manage HTTPPort: consulConfig.HTTPPort, APITimeout: consulConfig.APITimeout, }, - ImageDataplane: c.flagConsulDataplaneImage, - ImageConsulK8S: c.flagConsulK8sImage, - ConsulDestinationNamespace: c.flagConsulDestinationNamespace, - NamespaceMirroringPrefix: c.flagK8SNSMirroringPrefix, - EnableNamespaces: c.flagEnableNamespaces, - PeeringEnabled: c.flagEnablePeering, - EnableOpenShift: c.flagEnableOpenShift, - EnableNamespaceMirroring: c.flagEnableK8SNSMirroring, - AuthMethod: c.consul.ConsulLogin.AuthMethod, - LogLevel: c.flagLogLevel, - LogJSON: c.flagLogJSON, - TLSEnabled: c.consul.UseTLS, - ConsulTLSServerName: c.consul.TLSServerName, - ConsulPartition: c.consul.Partition, - ConsulCACert: string(c.caCertPem), - InitContainerResources: &c.initContainerResources, + ImageDataplane: c.flagConsulDataplaneImage, + ImageConsulK8S: c.flagConsulK8sImage, + ConsulDestinationNamespace: c.flagConsulDestinationNamespace, + NamespaceMirroringPrefix: c.flagK8SNSMirroringPrefix, + EnableNamespaces: c.flagEnableNamespaces, + PeeringEnabled: c.flagEnablePeering, + EnableOpenShift: c.flagEnableOpenShift, + EnableNamespaceMirroring: c.flagEnableK8SNSMirroring, + AuthMethod: c.consul.ConsulLogin.AuthMethod, + LogLevel: c.flagLogLevel, + LogJSON: c.flagLogJSON, + TLSEnabled: c.consul.UseTLS, + ConsulTLSServerName: c.consul.TLSServerName, + ConsulPartition: c.consul.Partition, + ConsulCACert: string(c.caCertPem), + EnableGatewayMetrics: c.flagEnableGatewayMetrics, + DefaultPrometheusScrapePath: c.flagDefaultPrometheusScrapePath, + DefaultPrometheusScrapePort: c.flagDefaultPrometheusScrapePort, + InitContainerResources: &c.initContainerResources, }, AllowK8sNamespacesSet: allowK8sNamespaces, DenyK8sNamespacesSet: denyK8sNamespaces,