Compare commits

...
Author SHA1 Message Date
Mikkel Oscar Lyderik Larsen 24b7276282 Update dependencies
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2023-07-13 17:05:57 +02:00
Mikkel Oscar Lyderik LarsenandGitHub 542d90d9fe Merge pull request #577 from zalando-incubator/update-golangci-lint-cfg
Update golangci-lint config
2023-06-30 09:24:57 +02:00
Mikkel Oscar Lyderik Larsen 0f359920af Update golangci-lint config
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2023-06-30 09:08:14 +02:00
Mikkel Oscar Lyderik LarsenandGitHub 54e2d2d564 Merge pull request #576 from AlexanderYastrebov/fix-codespell-typos
all: fix typos discovered by codespell
2023-06-12 13:48:47 +02:00
Alexander Yastrebov 16ec43c361 all: fix typos discovered by codespell
```
codespell --skip .git | less
```

See https://github.com/codespell-project/codespell

Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
2023-06-12 11:48:39 +02:00
Mikkel Oscar Lyderik LarsenandGitHub ffcbfcee48 Merge pull request #574 from zalando-incubator/update-deps-2023-06-01
Update dependencies
2023-06-01 14:00:05 +02:00
Mikkel Oscar Lyderik Larsen 69f95534e8 Update dependencies
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2023-06-01 13:48:06 +02:00
Lucas ThiesenandGitHub c2179a35ba Merge pull request #552 from zalando-incubator/hostname-rps-collector
Add hostname RPS metric collector
2023-05-26 11:13:42 +02:00
Mikkel Oscar Lyderik LarsenandMikkel Oscar Lyderik Larsen 35e3fe83e8 Update README.md
Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
2023-05-25 10:14:53 +02:00
Lucas Thiesen 69df60e724 Update flags with new metric collector name
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:58:29 +02:00
Lucas Thiesen d171e049bf Update documentation with new metric name
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:55:20 +02:00
Lucas Thiesen b89ca19e6a Rename collector to external RPS
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:38 +02:00
Lucas Thiesen a276b64576 Fix error string warnings
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:38 +02:00
Lucas Thiesen ff6d479f1a Compile regex at plugin level for time optimization
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:38 +02:00
Lucas Thiesen cd986058e4 Delete unwanted comments
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:38 +02:00
Lucas Thiesen d6a33fed63 Update documentation for multi hostnames per metric and weight param
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:38 +02:00
Lucas Thiesen b8532b756b Update HPA to autoscaling/v2 in new collectors
Reference links: https://github.com/zalando-incubator/kube-metrics-adapter/pull/551

Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:37 +02:00
Lucas Thiesen f28653de74 Use regexp.Compile to avoid performance problems
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:37 +02:00
Lucas Thiesen 2f5d3f5a42 Change hostname collector type to requests-per-second
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:37 +02:00
Lucas Thiesen 153d754353 Add support for traffic weight specification
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:37 +02:00
Lucas Thiesen 02ec2282ab Add multi hostname per metric support
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:37 +02:00
Lucas Thiesen 65dd585813 Add documentation regarding hostname-rps collector to readme
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:36 +02:00
Lucas Thiesen aa7b64e637 Add hostname RPS metric collector
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:36 +02:00
Lucas Thiesen 7633ac551e Extract fake controller to use it in other tests
Signed-off-by: Lucas Thiesen <lucas.thiesen@zalando.de>
2023-05-23 15:46:36 +02:00
Mikkel Oscar Lyderik LarsenandGitHub a103a3295e Merge pull request #563 from antoinedeschenes/helm-use-image-0.2.0 2023-05-12 23:23:44 +02:00
Antoine Deschênes f63915f7ff helm: bump app version to v0.2.0
Signed-off-by: Antoine Deschênes <antoine.deschenes@linux.com>
2023-05-12 10:26:06 -04:00
22 changed files with 831 additions and 242 deletions
-2
View File
@@ -4,7 +4,6 @@ run:
linters:
disable-all: true
enable:
- deadcode
- errcheck
- gosimple
- govet
@@ -12,4 +11,3 @@ linters:
- staticcheck
- typecheck
- unused
- varcheck
+1 -1
View File
@@ -50,7 +50,7 @@ contribution is in line with our goals.
- Make sure you sign-off on your commits `git commit -s -m "adding X to change Y"`
- Write good commit messages (see below).
- Push your changes to a topic branch in your fork of the repository.
- As you push your changes, update the pull request with new infomation and tasks as you complete them
- As you push your changes, update the pull request with new information and tasks as you complete them
- Project maintainers might comment on your work as you progress
- When you are done, remove the `work in progess` label and ping the maintainers for a review
- Your pull request must receive a :thumbsup: from two [maintainers](MAINTAINERS)
+58
View File
@@ -402,6 +402,64 @@ the `backend` label under `matchLabels` for the metric. The ingress annotation
where the backend weights can be obtained can be specified through the flag
`--skipper-backends-annotation`.
## External RPS collector
The External RPS collector, like Skipper collector, is a simple wrapper around the Prometheus collector to
make it easy to define an HPA for scaling based on the RPS measured for a given hostname. When
[skipper](https://github.com/zalando/skipper) is used as the ingress
implementation in your cluster everything should work automatically, in case another reverse proxy is used as ingress, like [Nginx](https://github.com/kubernetes/ingress-nginx) for example, its necessary to configure which prometheus metric should be used through `--external-rps-metric-name <metric-name>` flag. Assuming `skipper-ingress` is being used or the appropriate metric name is passed using the flag mentioned previously this collector provides the correct Prometheus queries out of the
box so users don't have to define those manually.
### Supported metrics
| Metric | Description | Type | Kind | K8s Versions |
| ------------ | -------------- | ------- | -- | -- |
| `requests-per-second` | Scale based on requests per second for a certain hostname. | External | | `>=1.12` |
### Example: External Metric
This is an example of an HPA that will scale based on `requests-per-second` for the RPS measured in the hostnames called: `www.example1.com` and `www.example2.com`; and weighted by 42%.
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
annotations:
metric-config.external.example-rps.requests-per-second/hostname: www.example1.com,www.example2.com
metric-config.external.example-rps.requests-per-second/weight: "42"
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: custom-metrics-consumer
minReplicas: 1
maxReplicas: 10
metrics:
- type: External
external:
metric:
name: example-rps
selector:
matchLabels:
type: requests-per-second
target:
type: AverageValue
averageValue: "42"
```
### Multiple hostnames per metric
This metric supports a relation of n:1 between hostnames and metrics. The way it works is the measured RPS is the sum of the RPS rate of each of the specified hostnames. This value is further modified by the weight parameter explained below.
### Metric weighting based on backend
There are ingress-controllers, like skipper-ingress, that supports sending traffic to different backends based on some kind of configuration, in case of skipper annotations
present on the `Ingress` object, or weights on the RouteGroup backends. By
default the number of replicas will be calculated based on the full traffic
served by these components. If however only the traffic being routed to
a specific hostname should be used then the weight for the configured hostname(s) might be specified via the `weight` annotation `metric-config.external.<metric-name>.request-per-second/weight` for the metric being configured.
## InfluxDB collector
The InfluxDB collector maps [Flux](https://github.com/influxdata/flux) queries to metrics that can be used for scaling.
+2 -2
View File
@@ -56,7 +56,7 @@ spec:
properties:
date:
description: Defines the starting date of a OneTime schedule.
It has to be a RFC3339 formated date.
It has to be a RFC3339 formatted date.
format: date-time
type: string
durationMinutes:
@@ -65,7 +65,7 @@ spec:
type: integer
endDate:
description: Defines the ending date of a OneTime schedule.
It must be a RFC3339 formated date.
It must be a RFC3339 formatted date.
format: date-time
type: string
period:
+1 -1
View File
@@ -1,6 +1,6 @@
apiVersion: v2
name: kube-metrics-adapter
version: 0.1.18
version: 0.2.0
description: kube-metrics-adapter helm chart
home: https://github.com/zalando-incubator/kube-metrics-adapter
maintainers:
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.5.0
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: clusterscalingschedules.zalando.org
spec:
@@ -15,7 +15,12 @@ spec:
singular: clusterscalingschedule
scope: Cluster
versions:
- name: v1
- additionalPrinterColumns:
- description: Whether one or more schedules are currently active.
jsonPath: .status.active
name: Active
type: boolean
name: v1
schema:
openAPIV3Schema:
description: ClusterScalingSchedule describes a cluster scoped time based
@@ -36,6 +41,11 @@ spec:
spec:
description: ScalingScheduleSpec is the spec part of the ScalingSchedule.
properties:
scalingWindowDurationMinutes:
description: Fade the scheduled values in and out over this many minutes.
If unset, the default per-cluster value will be used.
format: int64
type: integer
schedules:
description: Schedules is the list of schedules for this ScalingSchedule
resource. All the schedules defined here will result on the value
@@ -46,13 +56,18 @@ spec:
properties:
date:
description: Defines the starting date of a OneTime schedule.
It has to be a RFC3339 formated date.
It has to be a RFC3339 formatted date.
format: date-time
type: string
durationMinutes:
description: The duration in minutes that the configured value
will be returned for the defined schedule.
description: The duration in minutes (default 0) that the configured
value will be returned for the defined schedule.
type: integer
endDate:
description: Defines the ending date of a OneTime schedule.
It must be a RFC3339 formatted date.
format: date-time
type: string
period:
description: Defines the details of a Repeating schedule.
properties:
@@ -71,6 +86,10 @@ spec:
- Sat
type: string
type: array
endTime:
description: The endTime has the format HH:MM
pattern: (([0-1][0-9])|([2][0-3])):([0-5][0-9])
type: string
startTime:
description: The startTime has the format HH:MM
pattern: (([0-1][0-9])|([2][0-3])):([0-5][0-9])
@@ -95,9 +114,9 @@ spec:
value:
description: The metric value that will be returned for the
defined schedule.
format: int64
type: integer
required:
- durationMinutes
- type
- value
type: object
@@ -105,11 +124,22 @@ spec:
required:
- schedules
type: object
status:
description: ScalingScheduleStatus is the status section of the ScalingSchedule.
properties:
active:
default: false
description: Active is true if at least one of the schedules defined
in the scaling schedule is currently active.
type: boolean
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
+3
View File
@@ -164,6 +164,9 @@ spec:
{{- if .Values.skipperIngressMetrics }}
- --skipper-ingress-metrics={{ .Values.skipperIngressMetrics }}
{{- end}}
{{- if .Values.skipperRouteGroupMetrics }}
- --skipper-routegroup-metrics={{ .Values.skipperRouteGroupMetrics }}
{{- end}}
{{- if .Values.log.stderrThreshold }}
- --stderrthreshold={{ .Values.log.stderrThreshold }}
{{- end}}
+17
View File
@@ -59,12 +59,22 @@ rules:
- statefulsets
verbs:
- get
{{- if .Values.skipperRouteGroupMetrics }}
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs:
- get
{{- end }}
{{- if .Values.skipperRouteGroupMetrics }}
- apiGroups:
- zalando.org
resources:
- routegroups
verbs:
- get
{{- end }}
- apiGroups:
- autoscaling
resources:
@@ -83,6 +93,13 @@ rules:
- get
- list
- watch
- apiGroups:
- zalando.org
resources:
- clusterscalingschedules/status
- scalingschedules/status
verbs:
- update
{{- end}}
---
apiVersion: rbac.authorization.k8s.io/v1
+38 -6
View File
@@ -3,19 +3,26 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.5.0
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: scalingschedules.zalando.org
spec:
group: zalando.org
names:
categories:
- all
kind: ScalingSchedule
listKind: ScalingScheduleList
plural: scalingschedules
singular: scalingschedule
scope: Namespaced
versions:
- name: v1
- additionalPrinterColumns:
- description: Whether one or more schedules are currently active.
jsonPath: .status.active
name: Active
type: boolean
name: v1
schema:
openAPIV3Schema:
description: ScalingSchedule describes a namespaced time based metric to be
@@ -36,6 +43,11 @@ spec:
spec:
description: ScalingScheduleSpec is the spec part of the ScalingSchedule.
properties:
scalingWindowDurationMinutes:
description: Fade the scheduled values in and out over this many minutes.
If unset, the default per-cluster value will be used.
format: int64
type: integer
schedules:
description: Schedules is the list of schedules for this ScalingSchedule
resource. All the schedules defined here will result on the value
@@ -46,13 +58,18 @@ spec:
properties:
date:
description: Defines the starting date of a OneTime schedule.
It has to be a RFC3339 formated date.
It has to be a RFC3339 formatted date.
format: date-time
type: string
durationMinutes:
description: The duration in minutes that the configured value
will be returned for the defined schedule.
description: The duration in minutes (default 0) that the configured
value will be returned for the defined schedule.
type: integer
endDate:
description: Defines the ending date of a OneTime schedule.
It must be a RFC3339 formatted date.
format: date-time
type: string
period:
description: Defines the details of a Repeating schedule.
properties:
@@ -71,6 +88,10 @@ spec:
- Sat
type: string
type: array
endTime:
description: The endTime has the format HH:MM
pattern: (([0-1][0-9])|([2][0-3])):([0-5][0-9])
type: string
startTime:
description: The startTime has the format HH:MM
pattern: (([0-1][0-9])|([2][0-3])):([0-5][0-9])
@@ -95,9 +116,9 @@ spec:
value:
description: The metric value that will be returned for the
defined schedule.
format: int64
type: integer
required:
- durationMinutes
- type
- value
type: object
@@ -105,11 +126,22 @@ spec:
required:
- schedules
type: object
status:
description: ScalingScheduleStatus is the status section of the ScalingSchedule.
properties:
active:
default: false
description: Active is true if at least one of the schedules defined
in the scaling schedule is currently active.
type: boolean
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
+2 -1
View File
@@ -4,7 +4,7 @@ replicas: 1
registry:
image: registry.opensource.zalan.do/teapot/kube-metrics-adapter
imageTag: v0.1.10
imageTag: v0.2.0
imagePullPolicy: IfNotPresent
service:
@@ -22,6 +22,7 @@ http2MaxStreamsPerConnection:
listerKubeConfig:
skipperBackendsAnnotation:
skipperIngressMetrics:
skipperRouteGroupMetrics:
token:
vmodule:
+2 -2
View File
@@ -58,7 +58,7 @@ spec:
properties:
date:
description: Defines the starting date of a OneTime schedule.
It has to be a RFC3339 formated date.
It has to be a RFC3339 formatted date.
format: date-time
type: string
durationMinutes:
@@ -67,7 +67,7 @@ spec:
type: integer
endDate:
description: Defines the ending date of a OneTime schedule.
It must be a RFC3339 formated date.
It must be a RFC3339 formatted date.
format: date-time
type: string
period:
+44 -43
View File
@@ -1,34 +1,33 @@
module github.com/zalando-incubator/kube-metrics-adapter
require (
github.com/aws/aws-sdk-go v1.44.254
github.com/aws/aws-sdk-go v1.44.299
github.com/influxdata/influxdb-client-go v0.2.0
github.com/prometheus/client_golang v1.15.0
github.com/prometheus/common v0.42.0
github.com/sirupsen/logrus v1.9.0
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/common v0.44.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/spyzhov/ajson v0.7.2
github.com/stretchr/testify v1.8.2
github.com/spyzhov/ajson v0.8.0
github.com/stretchr/testify v1.8.4
github.com/szuecs/routegroup-client v0.21.1
github.com/zalando-incubator/cluster-lifecycle-manager v0.0.0-20230223125308-aff25efae501
golang.org/x/net v0.9.0
golang.org/x/oauth2 v0.7.0
golang.org/x/sync v0.1.0
k8s.io/api v0.23.0
k8s.io/apimachinery v0.23.0
k8s.io/apiserver v0.23.0
k8s.io/client-go v0.23.0
k8s.io/code-generator v0.23.0
k8s.io/component-base v0.23.0
github.com/zalando-incubator/cluster-lifecycle-manager v0.0.0-20230601114834-6ed1bba3c85d
golang.org/x/net v0.12.0
golang.org/x/oauth2 v0.10.0
golang.org/x/sync v0.3.0
k8s.io/api v0.23.17
k8s.io/apimachinery v0.23.17
k8s.io/apiserver v0.23.17
k8s.io/client-go v0.23.17
k8s.io/code-generator v0.23.17
k8s.io/component-base v0.23.17
k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf
k8s.io/metrics v0.22.17
k8s.io/kube-openapi v0.0.0-20230614213217-ba0abe644833
k8s.io/metrics v0.23.17
sigs.k8s.io/controller-tools v0.8.0
sigs.k8s.io/custom-metrics-apiserver v1.22.0
)
require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
@@ -41,10 +40,10 @@ require (
github.com/fatih/color v1.13.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gobuffalo/flect v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
@@ -56,7 +55,7 @@ require (
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -71,47 +70,48 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.etcd.io/etcd/api/v3 v3.5.6 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.6 // indirect
go.etcd.io/etcd/client/v2 v2.305.6 // indirect
go.etcd.io/etcd/client/v3 v3.5.6 // indirect
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/v2 v2.305.7 // indirect
go.etcd.io/etcd/client/v3 v3.5.9 // indirect
go.opentelemetry.io/contrib v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
go.opentelemetry.io/otel/sdk v1.14.0 // indirect
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/term v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.11.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.55.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.23.0 // indirect
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c // indirect
k8s.io/klog/v2 v2.90.0 // indirect
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
@@ -121,6 +121,7 @@ replace (
go.opentelemetry.io/otel => go.opentelemetry.io/otel v0.20.0
go.opentelemetry.io/otel/sdk => go.opentelemetry.io/otel/sdk v0.20.0
go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v0.20.0
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65
)
go 1.20
+82 -111
View File
File diff suppressed because it is too large Load Diff
@@ -2366,7 +2366,7 @@ func schema_k8sio_api_core_v1_EndpointSubset(ref common.ReferenceCallback) commo
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n }\nThe resulting set of endpoints can be viewed as:\n a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n b: [ 10.10.1.1:309, 10.10.2.2:309 ]",
Description: "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n\n\t{\n\t Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n\t Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n\t}\n\nThe resulting set of endpoints can be viewed as:\n\n\ta: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n\tb: [ 10.10.1.1:309, 10.10.2.2:309 ]",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"addresses": {
@@ -2423,7 +2423,7 @@ func schema_k8sio_api_core_v1_Endpoints(ref common.ReferenceCallback) common.Ope
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Endpoints is a collection of endpoints that implement the actual service. Example:\n Name: \"mysvc\",\n Subsets: [\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n },\n {\n Addresses: [{\"ip\": \"10.10.3.3\"}],\n Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n },\n ]",
Description: "Endpoints is a collection of endpoints that implement the actual service. Example:\n\n\t Name: \"mysvc\",\n\t Subsets: [\n\t {\n\t Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n\t Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n\t },\n\t {\n\t Addresses: [{\"ip\": \"10.10.3.3\"}],\n\t Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n\t },\n\t]",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
@@ -4327,11 +4327,11 @@ func schema_k8sio_api_core_v1_LimitRangeItem(ref common.ReferenceCallback) commo
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Type of resource that this limit applies to.\n\nPossible enum values:\n - `\"Container\"` Limit that applies to all containers in a namespace\n - `\"PersistentVolumeClaim\"` Limit that applies to all persistent volume claims in a namespace\n - `\"Pod\"` Limit that applies to all pods in a namespace",
Description: "Type of resource that this limit applies to.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"Container", "PersistentVolumeClaim", "Pod"}},
},
},
"max": {
SchemaProps: spec.SchemaProps{
@@ -4776,11 +4776,11 @@ func schema_k8sio_api_core_v1_NamespaceCondition(ref common.ReferenceCallback) c
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Type of namespace controller condition.\n\nPossible enum values:\n - `\"NamespaceContentRemaining\"` contains information about resources remaining in a namespace.\n - `\"NamespaceDeletionContentFailure\"` contains information about namespace deleter errors during deletion of resources.\n - `\"NamespaceDeletionDiscoveryFailure\"` contains information about namespace deleter errors during resource discovery.\n - `\"NamespaceDeletionGroupVersionParsingFailure\"` contains information about namespace deleter errors parsing GV for legacy types.\n - `\"NamespaceFinalizersRemaining\"` contains information about which finalizers are on resources remaining in a namespace.",
Description: "Type of namespace controller condition.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NamespaceContentRemaining", "NamespaceDeletionContentFailure", "NamespaceDeletionDiscoveryFailure", "NamespaceDeletionGroupVersionParsingFailure", "NamespaceFinalizersRemaining"}},
},
},
"status": {
SchemaProps: spec.SchemaProps{
@@ -4997,11 +4997,11 @@ func schema_k8sio_api_core_v1_NodeAddress(ref common.ReferenceCallback) common.O
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Node address type, one of Hostname, ExternalIP or InternalIP.\n\nPossible enum values:\n - `\"ExternalDNS\"` identifies a DNS name which resolves to an IP address which has the characteristics of a NodeExternalIP. The IP it resolves to may or may not be a listed NodeExternalIP address.\n - `\"ExternalIP\"` identifies an IP address which is, in some way, intended to be more usable from outside the cluster then an internal IP, though no specific semantics are defined. It may be a globally routable IP, though it is not required to be. External IPs may be assigned directly to an interface on the node, like a NodeInternalIP, or alternatively, packets sent to the external IP may be NAT'ed to an internal node IP rather than being delivered directly (making the IP less efficient for node-to-node traffic than a NodeInternalIP).\n - `\"Hostname\"` identifies a name of the node. Although every node can be assumed to have a NodeAddress of this type, its exact syntax and semantics are not defined, and are not consistent between different clusters.\n - `\"InternalDNS\"` identifies a DNS name which resolves to an IP address which has the characteristics of a NodeInternalIP. The IP it resolves to may or may not be a listed NodeInternalIP address.\n - `\"InternalIP\"` identifies an IP address which is assigned to one of the node's network interfaces. Every node should have at least one address of this type. An internal IP is normally expected to be reachable from every other node, but may not be visible to hosts outside the cluster. By default it is assumed that kube-apiserver can reach node internal IPs, though it is possible to configure clusters where this is not the case. NodeInternalIP is the default type of node IP, and does not necessarily imply that the IP is ONLY reachable internally. If a node has multiple internal IPs, no specific semantics are assigned to the additional IPs.",
Description: "Node address type, one of Hostname, ExternalIP or InternalIP.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"ExternalDNS", "ExternalIP", "Hostname", "InternalDNS", "InternalIP"}},
},
},
"address": {
SchemaProps: spec.SchemaProps{
@@ -5062,11 +5062,11 @@ func schema_k8sio_api_core_v1_NodeCondition(ref common.ReferenceCallback) common
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Type of node condition.\n\nPossible enum values:\n - `\"DiskPressure\"` means the kubelet is under pressure due to insufficient available disk.\n - `\"MemoryPressure\"` means the kubelet is under pressure due to insufficient available memory.\n - `\"NetworkUnavailable\"` means that network for the node is not correctly configured.\n - `\"PIDPressure\"` means the kubelet is under pressure due to insufficient available PID.\n - `\"Ready\"` means kubelet is healthy and ready to accept pods.",
Description: "Type of node condition.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"DiskPressure", "MemoryPressure", "NetworkUnavailable", "PIDPressure", "Ready"}},
},
},
"status": {
SchemaProps: spec.SchemaProps{
@@ -5982,11 +5982,10 @@ func schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref common.Referenc
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "\n\n\nPossible enum values:\n - `\"FileSystemResizePending\"` - controller resize is finished and a file system resize is pending on node\n - `\"Resizing\"` - a user trigger resize of pvc has been started",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"FileSystemResizePending", "Resizing"}},
Default: "",
Type: []string{"string"},
Format: "",
},
},
"status": {
SchemaProps: spec.SchemaProps{
@@ -7056,11 +7055,11 @@ func schema_k8sio_api_core_v1_PodCondition(ref common.ReferenceCallback) common.
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Description: "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions\n\nPossible enum values:\n - `\"ContainersReady\"` indicates whether all containers in the pod are ready.\n - `\"Initialized\"` means that all init containers in the pod have started successfully.\n - `\"PodScheduled\"` represents status of the scheduling process for this pod.\n - `\"Ready\"` means the pod is able to service requests and should be added to the load balancing pools of all matching services.",
Description: "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"ContainersReady", "Initialized", "PodScheduled", "Ready"}},
},
},
"status": {
SchemaProps: spec.SchemaProps{
@@ -7274,7 +7273,7 @@ func schema_k8sio_api_core_v1_PodIP(ref common.ReferenceCallback) common.OpenAPI
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "IP address information for entries in the (plural) PodIPs field. Each entry includes:\n IP: An IP address allocated to the pod. Routable at least within the cluster.",
Description: "IP address information for entries in the (plural) PodIPs field. Each entry includes:\n\n\tIP: An IP address allocated to the pod. Routable at least within the cluster.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"ip": {
@@ -7539,11 +7538,11 @@ func schema_k8sio_api_core_v1_PodReadinessGate(ref common.ReferenceCallback) com
Properties: map[string]spec.Schema{
"conditionType": {
SchemaProps: spec.SchemaProps{
Description: "ConditionType refers to a condition in the pod's condition list with matching type.\n\nPossible enum values:\n - `\"ContainersReady\"` indicates whether all containers in the pod are ready.\n - `\"Initialized\"` means that all init containers in the pod have started successfully.\n - `\"PodScheduled\"` represents status of the scheduling process for this pod.\n - `\"Ready\"` means the pod is able to service requests and should be added to the load balancing pools of all matching services.",
Description: "ConditionType refers to a condition in the pod's condition list with matching type.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"ContainersReady", "Initialized", "PodScheduled", "Ready"}},
},
},
},
Required: []string{"conditionType"},
@@ -12019,7 +12018,7 @@ func schema_apimachinery_pkg_api_resource_Quantity(ref common.ReferenceCallback)
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity> ::= <signedNumber><suffix>\n (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit> ::= 0 | 1 | ... | 9 <digits> ::= <digit> | <digit><digits> <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign> ::= \"+\" | \"-\" <signedNumber> ::= <number> | <sign><number> <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei\n (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI> ::= m | \"\" | k | M | G | T | P | E\n (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n a. No precision is lost\n b. No fractional digits will be emitted\n c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n 1.5 will be serialized as \"1500m\"\n 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.",
Description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity> ::= <signedNumber><suffix>\n\n\t(Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n\n<digit> ::= 0 | 1 | ... | 9 <digits> ::= <digit> | <digit><digits> <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign> ::= \"+\" | \"-\" <signedNumber> ::= <number> | <sign><number> <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n<decimalSI> ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n\ta. No precision is lost\n\tb. No fractional digits will be emitted\n\tc. The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n\t1.5 will be serialized as \"1500m\"\n\t1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.",
Type: resource.Quantity{}.OpenAPISchemaType(),
Format: resource.Quantity{}.OpenAPISchemaFormat(),
},
@@ -14942,8 +14941,9 @@ func schema_pkg_apis_metrics_v1beta1_NodeMetrics(ref common.ReferenceCallback) c
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"timestamp": {
@@ -15057,8 +15057,9 @@ func schema_pkg_apis_metrics_v1beta1_PodMetrics(ref common.ReferenceCallback) co
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"timestamp": {
+2 -2
View File
@@ -68,11 +68,11 @@ type Schedule struct {
// +optional
Period *SchedulePeriod `json:"period,omitempty"`
// Defines the starting date of a OneTime schedule. It has to
// be a RFC3339 formated date.
// be a RFC3339 formatted date.
// +optional
Date *ScheduleDate `json:"date,omitempty"`
// Defines the ending date of a OneTime schedule. It must be
// a RFC3339 formated date.
// a RFC3339 formatted date.
// +optional
EndDate *ScheduleDate `json:"endDate,omitempty"`
// The duration in minutes (default 0) that the configured value will be
@@ -61,6 +61,10 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface {
func NewForConfig(c *rest.Config) (*Clientset, error) {
configShallowCopy := *c
if configShallowCopy.UserAgent == "" {
configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
}
// share the transport between all clients
httpClient, err := rest.HTTPClientFor(&configShallowCopy)
if err != nil {
+125
View File
@@ -0,0 +1,125 @@
package collector
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
autoscalingv2 "k8s.io/api/autoscaling/v2"
)
const (
ExternalRPSMetricType = "requests-per-second"
ExternalRPSQuery = `scalar(sum(rate(%s{host=~"%s"}[1m])) * %.4f)`
)
type ExternalRPSCollectorPlugin struct {
metricName string
promPlugin CollectorPlugin
pattern *regexp.Regexp
}
type ExternalRPSCollector struct {
interval time.Duration
promCollector Collector
}
func NewExternalRPSCollectorPlugin(
promPlugin CollectorPlugin,
metricName string,
) (*ExternalRPSCollectorPlugin, error) {
if metricName == "" {
return nil, fmt.Errorf("failed to initialize hostname collector plugin, metric name was not defined")
}
p, err := regexp.Compile("^[a-zA-Z0-9.-]+$")
if err != nil {
return nil, fmt.Errorf("failed to create regular expression to match hostname format")
}
return &ExternalRPSCollectorPlugin{
metricName: metricName,
promPlugin: promPlugin,
pattern: p,
}, nil
}
// NewCollector initializes a new skipper collector from the specified HPA.
func (p *ExternalRPSCollectorPlugin) NewCollector(
hpa *autoscalingv2.HorizontalPodAutoscaler,
config *MetricConfig,
interval time.Duration,
) (Collector, error) {
if config == nil {
return nil, fmt.Errorf("metric config not present, it is not possible to initialize the collector")
}
// Need to copy config and add a promQL query in order to get
// RPS data from a specific hostname from prometheus. The idea
// of the copy is to not modify the original config struct.
confCopy := *config
if _, ok := config.Config["hostnames"]; !ok {
return nil, fmt.Errorf("Hostname is not specified, unable to create collector")
}
hostnames := strings.Split(config.Config["hostnames"], ",")
if p.pattern == nil {
return nil, fmt.Errorf("plugin did not specify hostname regex pattern, unable to create collector")
}
for _, h := range hostnames {
if ok := p.pattern.MatchString(h); !ok {
return nil, fmt.Errorf(
"invalid hostname format, unable to create collector: %s",
h,
)
}
}
weight := 1.0
if w, ok := config.Config["weight"]; ok {
num, err := strconv.ParseFloat(w, 64)
if err != nil {
return nil, fmt.Errorf("could not parse weight annotation, unable to create collector: %s", w)
}
weight = num / 100.0
}
confCopy.Config = map[string]string{
"query": fmt.Sprintf(
ExternalRPSQuery,
p.metricName,
strings.ReplaceAll(strings.Join(hostnames, "|"), ".", "_"),
weight,
),
}
c, err := p.promPlugin.NewCollector(hpa, &confCopy, interval)
if err != nil {
return nil, err
}
return &ExternalRPSCollector{
interval: interval,
promCollector: c,
}, nil
}
// GetMetrics gets hostname metrics from Prometheus
func (c *ExternalRPSCollector) GetMetrics() ([]CollectedMetric, error) {
v, err := c.promCollector.GetMetrics()
if err != nil {
return nil, err
}
if len(v) != 1 {
return nil, fmt.Errorf("expected to only get one metric value, got %d", len(v))
}
return v, nil
}
// Interval returns the interval at which the collector should run.
func (c *ExternalRPSCollector) Interval() time.Duration {
return c.interval
}
File diff suppressed because it is too large Load Diff
+57
View File
@@ -0,0 +1,57 @@
package collector
import (
"time"
autoscalingv2 "k8s.io/api/autoscaling/v2"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/metrics/pkg/apis/custom_metrics"
)
type FakeCollectorPlugin struct {
metrics []CollectedMetric
config map[string]string
}
type FakeCollector struct {
metrics []CollectedMetric
interval time.Duration
stub func() ([]CollectedMetric, error)
}
func (c *FakeCollector) GetMetrics() ([]CollectedMetric, error) {
if c.stub != nil {
v, err := c.stub()
return v, err
}
return c.metrics, nil
}
func (FakeCollector) Interval() time.Duration {
return time.Minute
}
func (p *FakeCollectorPlugin) NewCollector(
hpa *autoscalingv2.HorizontalPodAutoscaler,
config *MetricConfig,
interval time.Duration,
) (Collector, error) {
p.config = config.Config
return &FakeCollector{metrics: p.metrics, interval: interval}, nil
}
func makePlugin(metric int) *FakeCollectorPlugin {
return &FakeCollectorPlugin{
metrics: []CollectedMetric{
{
Custom: custom_metrics.MetricValue{Value: *resource.NewQuantity(int64(metric), resource.DecimalSI)},
},
},
}
}
func makeCollectorWithStub(f func() ([]CollectedMetric, error)) *FakeCollector {
return &FakeCollector{stub: f}
}
+2 -37
View File
@@ -61,10 +61,10 @@ func TestTargetRefReplicasStatefulSets(t *testing.T) {
require.Equal(t, statefulSet.Status.Replicas, replicas)
}
func newHPA(namesapce string, refName string, refKind string) *autoscalingv2.HorizontalPodAutoscaler {
func newHPA(namespace string, refName string, refKind string) *autoscalingv2.HorizontalPodAutoscaler {
return &autoscalingv2.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: namesapce,
Name: namespace,
},
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
@@ -658,38 +658,3 @@ func makeConfig(resourceName, namespace, kind, backend string, fakedAverage bool
}
return config
}
type FakeCollectorPlugin struct {
metrics []CollectedMetric
config map[string]string
}
type FakeCollector struct {
metrics []CollectedMetric
}
func (c *FakeCollector) GetMetrics() ([]CollectedMetric, error) {
return c.metrics, nil
}
func (FakeCollector) Interval() time.Duration {
return time.Minute
}
func (p *FakeCollectorPlugin) NewCollector(hpa *autoscalingv2.HorizontalPodAutoscaler, config *MetricConfig, interval time.Duration) (Collector, error) {
if p.config != nil {
return nil, fmt.Errorf("config already assigned once: %v", p.config)
}
p.config = config.Config
return &FakeCollector{metrics: p.metrics}, nil
}
func makePlugin(metric int) *FakeCollectorPlugin {
return &FakeCollectorPlugin{
metrics: []CollectedMetric{
{
Custom: custom_metrics.MetricValue{Value: *resource.NewQuantity(int64(metric), resource.DecimalSI)},
},
},
}
}
+1 -1
View File
@@ -363,7 +363,7 @@ func collectorRunner(ctx context.Context, collector collector.Collector, metrics
}
}
// Remove removes a collector from the Collector schduler. The collector is
// Remove removes a collector from the Collector scheduler. The collector is
// stopped before it's removed.
func (t *CollectorScheduler) Remove(resourceRef resourceReference) {
t.Lock()
+23 -2
View File
@@ -65,6 +65,7 @@ func NewCommandStartAdapterServer(stopCh <-chan struct{}) *cobra.Command {
MetricsAddress: ":7979",
ZMONTokenName: "zmon",
CredentialsDir: "/meta/credentials",
ExternalRPSMetricName: "skipper_serve_host_duration_seconds_count",
}
cmd := &cobra.Command{
@@ -132,6 +133,10 @@ func NewCommandStartAdapterServer(stopCh <-chan struct{}) *cobra.Command {
flags.DurationVar(&o.DefaultScheduledScalingWindow, "scaling-schedule-default-scaling-window", 10*time.Minute, "Default rampup and rampdown window duration for ScalingSchedules")
flags.IntVar(&o.RampSteps, "scaling-schedule-ramp-steps", 10, "Number of steps used to rampup and rampdown ScalingSchedules. It's used to guarantee won't avoid reaching the max scaling due to the 10% minimum change rule.")
flags.StringVar(&o.DefaultTimeZone, "scaling-schedule-default-time-zone", "Europe/Berlin", "Default time zone to use for ScalingSchedules.")
flags.StringVar(&o.ExternalRPSMetricName, "external-rps-metric-name", o.ExternalRPSMetricName, ""+
"The name of the metric that should be used to query prometheus for RPS per hostname.")
flags.BoolVar(&o.ExternalRPSMetrics, "external-rps-metrics", o.ExternalRPSMetrics, ""+
"whether to enable external RPS metric collector or not")
return cmd
}
@@ -218,6 +223,18 @@ func (o AdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-chan struct
}
}
}
// External RPS collector, like skipper's, depends on prometheus being enabled.
// Also, to enable hostname metric its necessary to pass the metric name that
// will be used. This was built this way so we can support hostname metrics to
// any ingress provider, e.g. Skipper, Nginx, envoy etc, in a simple way.
if o.ExternalRPSMetrics && o.ExternalRPSMetricName != "" {
externalRPSPlugin, err := collector.NewExternalRPSCollectorPlugin(promPlugin, o.ExternalRPSMetricName)
collectorFactory.RegisterExternalCollector([]string{collector.ExternalRPSMetricType}, externalRPSPlugin)
if err != nil {
return fmt.Errorf("failed to register hostname collector plugin: %v", err)
}
}
}
if o.InfluxDBAddress != "" {
@@ -315,7 +332,7 @@ func (o AdapterServerOptions) RunCustomMetricsAdapterServer(stopCh <-chan struct
return fmt.Errorf("failed to register ScalingSchedule object collector plugin: %v", err)
}
// setup ScheduledScaling controller to continously update
// setup ScheduledScaling controller to continuously update
// status of ScalingSchedule and ClusterScalingSchedule
// resources.
scheduledScalingController := scheduledscaling.NewController(scalingScheduleClient.ZalandoV1(), scalingSchedulesStore, clusterScalingSchedulesStore, time.Now, o.DefaultScheduledScalingWindow, o.DefaultTimeZone)
@@ -383,7 +400,7 @@ func newOauth2HTTPClient(ctx context.Context, tokenSource oauth2.TokenSource) *h
// add HTTP client to context (this is how the oauth2 lib gets it).
ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
// instantiate an http.Client containg the token source.
// instantiate an http.Client containing the token source.
return oauth2.NewClient(ctx, tokenSource)
}
@@ -445,4 +462,8 @@ type AdapterServerOptions struct {
RampSteps int
// Default time zone to use for ScalingSchedules.
DefaultTimeZone string
// Feature flag to enable external rps metric collector
ExternalRPSMetrics bool
// Name of the Prometheus metric that stores RPS by hostname for external RPS metrics.
ExternalRPSMetricName string
}