diff --git a/test/images/agnhost/README.md b/test/images/agnhost/README.md index 05d58a23fc5..d9d8c99c185 100644 --- a/test/images/agnhost/README.md +++ b/test/images/agnhost/README.md @@ -165,6 +165,35 @@ Usage: ``` +### external-metrics + +Starts an HTTPS server that implements the external metrics API for testing Horizontal Pod Autoscaler (HPA) with external metrics. The server provides a mock implementation of the `external.metrics.k8s.io` API group. + +The server comes with two pre-configured metrics: +- `queue_messages_ready` (value: 100) +- `http_requests_total` (value: 500) + +The server exposes the following endpoints: +- `/apis/external.metrics.k8s.io/v1beta1/namespaces/{namespace}/{metric-name}`: Get external metric values (supports label selectors via query parameter) +- `/healthz` and `/readyz`: Health check endpoints +- `/create/{metric-name}?value={value}&labels={key1=value1,key2=value2}&fail={true|false}`: Create a new metric with optional labels +- `/set/{metric-name}?value={value}&labels={key1=value1,key2=value2}`: Update an existing metric's value +- `/fail/{metric-name}?fail={true|false}&labels={key1=value1,key2=value2}`: Configure a metric to return errors + +The subcommand can accept the following flags: +- `port` (default: `6443`): Port number for the HTTPS server. +- `service-name` (default: `external-metrics-server`): Name of the external metrics service. +- `service-namespace` (default: `default`): Namespace of the external metrics service. + +The server generates self-signed TLS certificates automatically on startup. + +Usage: + +```console + kubectl exec test-agnhost -- /agnhost external-metrics [--port ] [--service-name ] [--service-namespace ] +``` + + ### fake-gitserver Fakes a git server. When doing `git clone http://localhost:8000`, you will clone an empty git diff --git a/test/images/agnhost/VERSION b/test/images/agnhost/VERSION index aa97c2f1d7f..e604dbd28b0 100644 --- a/test/images/agnhost/VERSION +++ b/test/images/agnhost/VERSION @@ -1 +1 @@ -2.62.2 +2.63.0 diff --git a/test/images/agnhost/agnhost.go b/test/images/agnhost/agnhost.go index e53d26455ef..088932e4e09 100644 --- a/test/images/agnhost/agnhost.go +++ b/test/images/agnhost/agnhost.go @@ -27,6 +27,7 @@ import ( crdconvwebhook "k8s.io/kubernetes/test/images/agnhost/crd-conversion-webhook" "k8s.io/kubernetes/test/images/agnhost/dns" "k8s.io/kubernetes/test/images/agnhost/entrypoint-tester" + externalmetrics "k8s.io/kubernetes/test/images/agnhost/external-metrics" "k8s.io/kubernetes/test/images/agnhost/fakegitserver" "k8s.io/kubernetes/test/images/agnhost/fakeregistryserver" grpchealthchecking "k8s.io/kubernetes/test/images/agnhost/grpc-health-checking" @@ -98,6 +99,7 @@ func main() { rootCmd.AddCommand(mtlsclient.CmdMtlsClient) rootCmd.AddCommand(mtlsserver.CmdMtlsServer) rootCmd.AddCommand(nonewprivs.CmdNoNewPrivs) + rootCmd.AddCommand(externalmetrics.CmdExternalMetricsServer) // NOTE(claudiub): Some tests are passing logging related flags, so we need to be able to // accept them. This will also include them in the printed help. code := cli.Run(rootCmd) diff --git a/test/images/sample-external-metrics-server/provider.go b/test/images/agnhost/external-metrics/provider.go similarity index 99% rename from test/images/sample-external-metrics-server/provider.go rename to test/images/agnhost/external-metrics/provider.go index 76f3df5c6ff..023738931c4 100644 --- a/test/images/sample-external-metrics-server/provider.go +++ b/test/images/agnhost/external-metrics/provider.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package externalmetrics import ( "fmt" diff --git a/test/images/sample-external-metrics-server/main.go b/test/images/agnhost/external-metrics/server.go similarity index 91% rename from test/images/sample-external-metrics-server/main.go rename to test/images/agnhost/external-metrics/server.go index f7b70f9f3fb..71d33d6329f 100644 --- a/test/images/sample-external-metrics-server/main.go +++ b/test/images/agnhost/external-metrics/server.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package externalmetrics import ( "context" @@ -22,39 +22,50 @@ import ( "fmt" "net" "net/http" - "os" "strings" "time" - "github.com/spf13/pflag" + "github.com/spf13/cobra" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server/options" "k8s.io/klog/v2" netutils "k8s.io/utils/net" ) +// CmdExternalMetricsServer is used by agnhost Cobra. +var CmdExternalMetricsServer = &cobra.Command{ + Use: "external-metrics", + Short: "Starts an external metrics server for testing", + Long: "Starts an HTTPS server that implements the external metrics API for testing HPA with external metrics", + Args: cobra.MaximumNArgs(0), + Run: main, +} + var provider *metricProvider -func main() { - flags := pflag.NewFlagSet("sample-external-metrics", pflag.ExitOnError) - klog.InitFlags(nil) +var ( + port int + serviceName string + serviceNs string +) + +func init() { + CmdExternalMetricsServer.Flags().IntVar(&port, "port", 6443, "Port number.") + CmdExternalMetricsServer.Flags().StringVar(&serviceName, "service-name", "external-metrics-server", "Name of the external metrics service.") + CmdExternalMetricsServer.Flags().StringVar(&serviceNs, "service-namespace", "default", "Namespace of the external metrics service.") +} + +func main(cmd *cobra.Command, args []string) { // Initialize the metric provider provider = NewConfigurableProvider() // Create some default metrics provider.createMetric("queue_messages_ready", nil, 100, false) provider.createMetric("http_requests_total", nil, 500, false) - secureServing := options.NewSecureServingOptions() - secureServing.BindPort = 6443 + secureServing.BindPort = port secureServing.ServerCert.CertDirectory = "/tmp/cert" secureServing.ServerCert.PairName = "apiserver" - secureServing.AddFlags(flags) - - if err := flags.Parse(os.Args[1:]); err != nil { - klog.Fatalf("Error parsing flags: %v", err) - } - // Generate self-signed TLS certificates if none exist. This allows the server to run with HTTPS // without requiring manually provisioned certificates. The certs are valid for "localhost" and // the loopback IP 127.0.0.1. The second parameter (nil) means no additional alternate names. @@ -96,6 +107,7 @@ func main() { <-listenerStoppedCh <-stoppedCh + } func healthz(w http.ResponseWriter, r *http.Request) { diff --git a/test/images/sample-external-metrics-server/BASEIMAGE b/test/images/sample-external-metrics-server/BASEIMAGE deleted file mode 100644 index 428ae5d6f9e..00000000000 --- a/test/images/sample-external-metrics-server/BASEIMAGE +++ /dev/null @@ -1,5 +0,0 @@ -linux/amd64=registry.k8s.io/build-image/debian-base-amd64:bookworm-v1.0.6 -linux/ppc64le=registry.k8s.io/build-image/debian-base-ppc64le:bookworm-v1.0.6 -linux/s390x=registry.k8s.io/build-image/debian-base-s390x:bookworm-v1.0.6 -windows/amd64/1809=mcr.microsoft.com/windows/nanoserver:1809 -windows/amd64/ltsc2022=mcr.microsoft.com/windows/nanoserver:ltsc2022 diff --git a/test/images/sample-external-metrics-server/Dockerfile b/test/images/sample-external-metrics-server/Dockerfile deleted file mode 100644 index 4a3c1bdc82d..00000000000 --- a/test/images/sample-external-metrics-server/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright The Kubernetes 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. - -ARG BASEIMAGE -FROM $BASEIMAGE - -ADD sample-external-metrics-server /sample-external-metrics-server -EXPOSE 6443 -ENTRYPOINT ["/sample-external-metrics-server"] diff --git a/test/images/sample-external-metrics-server/Makefile b/test/images/sample-external-metrics-server/Makefile deleted file mode 100644 index 2733041d288..00000000000 --- a/test/images/sample-external-metrics-server/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright The Kubernetes 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. - -SRCS = sample-external-metrics-server -OS ?= linux -ARCH ?= amd64 -TARGET ?= $(CURDIR) -GOLANG_VERSION ?= latest -SRC_DIR = $(notdir $(shell pwd)) -export - -bin: - ../image-util.sh bin $(SRCS) - -.PHONY: bin diff --git a/test/images/sample-external-metrics-server/README.md b/test/images/sample-external-metrics-server/README.md deleted file mode 100644 index 79e7b1c9eda..00000000000 --- a/test/images/sample-external-metrics-server/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# Test Metrics Server - -A Kubernetes external metrics API server for testing and development purposes. This server implements the `external.metrics.k8s.io` API and provides endpoints to dynamically create, update, and configure metrics for testing HPA (Horizontal Pod Autoscaler) and other metric-based scenarios. - -## API Endpoints - -### Health Checks - -#### Check Server Health -```bash -curl -k https://localhost:6443/healthz -curl -k https://localhost:6443/readyz -``` - -### Kubernetes External Metrics API - -#### List API Groups -```bash -curl -k https://localhost:6443/apis/external.metrics.k8s.io -``` - -#### List Available Resources -```bash -curl -k https://localhost:6443/apis/external.metrics.k8s.io/v1beta1 -``` - -#### Get Metric Value -```bash -# Query a specific metric -curl -k https://localhost:6443/apis/external.metrics.k8s.io/v1beta1/namespaces/default/queue_messages_ready - -# Query a metric with label selector -curl -k "https://localhost:6443/apis/external.metrics.k8s.io/v1beta1/namespaces/production/error_rate?labelSelector=env=prod,region=us-west" - -# Or via kubectl -kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/queue_messages_ready" -kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/error_rate?labelSelector=env=prod" -``` - -**Error Response (Metric Not Found):** -```bash -curl -k https://localhost:6443/apis/external.metrics.k8s.io/v1beta1/namespaces/default/nonexistent_metric -# HTTP 404: no metric name called nonexistent_metric -``` - -**Error Response (Metric Configured to Fail):** -```bash -# HTTP 500: metric queue_messages_ready is configured to fail -``` - ---- - -### Metric Management - -#### Create a New Metric -```bash -# Create a metric with value 250 -curl -k "https://localhost:6443/create/my_custom_metric?value=250" - -# Create a metric with labels -curl -k "https://localhost:6443/create/error_rate?value=25&labels=env=prod,region=us-west" - -# Create a metric that will fail -curl -k "https://localhost:6443/create/failing_metric?value=100&fail=true" - -# Create a metric with labels that will fail -curl -k "https://localhost:6443/create/error_rate?value=50&labels=env=staging&fail=true" -``` - -#### Update Metric Value -```bash -# Update the value of an existing metric -curl -k "https://localhost:6443/set/queue_messages_ready?value=500" -``` - -```bash -# Update the value of an existing labels metric -curl -k "https://localhost:6443/set/queue_messages_ready?value=500&labels=env=staging" -``` - -**Error Response (Metric Not Found):** -```bash -curl -k "https://localhost:6443/set/nonexistent_metric?value=100" -# HTTP 404: metric nonexistent_metric not found -``` - -#### Configure Metric Failure State -```bash -# Make a metric fail (return HTTP 500) -curl -k "https://localhost:6443/fail/queue_messages_ready?fail=true" - -# Make a metric succeed again -curl -k "https://localhost:6443/fail/queue_messages_ready?fail=false" - -# Make a metric with specific labels fail -curl -k "https://localhost:6443/fail/error_rate?fail=true&labels=env=prod,region=us-west" - -# Make a labeled metric succeed again -curl -k "https://localhost:6443/fail/error_rate?fail=false&labels=env=prod,region=us-west" -``` -### Querying Metrics with Labels -```bash -# Get all response_time metrics (returns all label variants) -curl -k "https://localhost:6443/apis/external.metrics.k8s.io/v1beta1/namespaces/default/response_time" - -# Get only prod environment metrics -curl -k "https://localhost:6443/apis/external.metrics.k8s.io/v1beta1/namespaces/default/response_time?labelSelector=env=prod" - -# Get specific metric with exact label match -curl -k "https://localhost:6443/apis/external.metrics.k8s.io/v1beta1/namespaces/default/response_time?labelSelector=env=prod,service=api" -``` - -## Default Metrics - -The server starts with two pre-configured metrics: - -1. **queue_messages_ready** - Initial value: 100 -2. **http_requests_total** - Initial value: 500 \ No newline at end of file diff --git a/test/images/sample-external-metrics-server/VERSION b/test/images/sample-external-metrics-server/VERSION deleted file mode 100644 index 3eefcb9dd5b..00000000000 --- a/test/images/sample-external-metrics-server/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.0.0