ingress-nginx-helm/internal/ingress/annotations/canary/main.go
Marco Ebert 8d3d4a33bf
Chores: Pick patches from main. (#11103)
* Release version v1.10.0

* set deploy url to v1-10-0 in docs

* quotes around numbers fort ports definitions

* Bump dorny/paths-filter from 3.0.1 to 3.0.2

Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 3.0.1 to 3.0.2.
- [Release notes](https://github.com/dorny/paths-filter/releases)
- [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md)
- [Commits](ebc4d7e9eb...de90cc6fb3)

---
updated-dependencies:
- dependency-name: dorny/paths-filter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

* Bump aquasecurity/trivy-action from 0.17.0 to 0.18.0

Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.17.0 to 0.18.0.
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](84384bd6e7...062f259268)

---
updated-dependencies:
- dependency-name: aquasecurity/trivy-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

* Bump github/codeql-action from 3.24.5 to 3.24.6

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.5 to 3.24.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](47b3d888fe...8a470fddaf)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

* Bump github.com/prometheus/common from 0.48.0 to 0.49.0

Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.48.0 to 0.49.0.
- [Release notes](https://github.com/prometheus/common/releases)
- [Commits](https://github.com/prometheus/common/compare/v0.48.0...v0.49.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

* Bump docker/setup-buildx-action from 3.0.0 to 3.1.0

Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](f95db51fdd...0d103c3126)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

* Bump github.com/stretchr/testify from 1.8.4 to 1.9.0

Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.4 to 1.9.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

* Bump actions/download-artifact from 4.1.2 to 4.1.4

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.2 to 4.1.4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](eaceaf801f...c850b930e6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

* Update README.md

remove older version, left latest for release train.

* docs: update the 404 link to FAQ

* bump golang

* golangci-lint update, ci cleanup, group dependabot updates

* bump golangci-lint to v1.56.x

* cleanup empty lines

* group dependabot updates

* run on job changes as well

* remove deprecated checks

* fix lints and format

* Bump github.com/prometheus/common from 0.49.0 to 0.50.0

Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.49.0 to 0.50.0.
- [Release notes](https://github.com/prometheus/common/releases)
- [Commits](https://github.com/prometheus/common/compare/v0.49.0...v0.50.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

* Bump the all group with 1 update

Bumps the all group with 1 update: [google.golang.org/grpc](https://github.com/grpc/grpc-go).

Updates `google.golang.org/grpc` from 1.62.0 to 1.62.1
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.62.0...v1.62.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

* Bump the all group with 1 update

Bumps the all group with 1 update: [actions/add-to-project](https://github.com/actions/add-to-project).

Updates `actions/add-to-project` from 0.5.0 to 0.6.0
- [Release notes](https://github.com/actions/add-to-project/releases)
- [Commits](31b3f3ccdc...0609a2702e)

---
updated-dependencies:
- dependency-name: actions/add-to-project
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all
...

* Bump github.com/onsi/ginkgo/v2 from 2.15.0 to 2.16.0

Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.15.0 to 2.16.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.15.0...v2.16.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

---------

Co-authored-by: Ricardo Katz <rikatz@users.noreply.github.com>
Co-authored-by: longwuyuan <longwuyuan@gmail.com>
Co-authored-by: Bartosz Fenski <fenio@debian.org>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: James Strong <strong.james.e@gmail.com>
Co-authored-by: Grinish <grinish@gmail.com>
Co-authored-by: Carlos Tadeu Panato Junior <ctadeu@gmail.com>
2024-03-11 14:30:46 -07:00

195 lines
7.9 KiB
Go

/*
Copyright 2018 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.
*/
package canary
import (
networking "k8s.io/api/networking/v1"
"k8s.io/klog/v2"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
const (
canaryAnnotation = "canary"
canaryWeightAnnotation = "canary-weight"
canaryWeightTotalAnnotation = "canary-weight-total"
canaryByHeaderAnnotation = "canary-by-header"
canaryByHeaderValueAnnotation = "canary-by-header-value"
canaryByHeaderPatternAnnotation = "canary-by-header-pattern"
canaryByCookieAnnotation = "canary-by-cookie"
)
var CanaryAnnotations = parser.Annotation{
Group: "canary",
Annotations: parser.AnnotationFields{
canaryAnnotation: {
Validator: parser.ValidateBool,
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation enables the Ingress spec to act as an alternative service for requests to route to depending on the rules applied`,
},
canaryWeightAnnotation: {
Validator: parser.ValidateInt,
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation defines the integer based (0 - ) percent of random requests that should be routed to the service specified in the canary Ingress`,
},
canaryWeightTotalAnnotation: {
Validator: parser.ValidateInt,
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskLow,
Documentation: `This annotation The total weight of traffic. If unspecified, it defaults to 100`,
},
canaryByHeaderAnnotation: {
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the header that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress.
When the request header is set to 'always', it will be routed to the canary. When the header is set to 'never', it will never be routed to the canary.
For any other value, the header will be ignored and the request compared against the other canary rules by precedence`,
},
canaryByHeaderValueAnnotation: {
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the header value to match for notifying the Ingress to route the request to the service specified in the Canary Ingress.
When the request header is set to this value, it will be routed to the canary. For any other header value, the header will be ignored and the request compared against the other canary rules by precedence.
This annotation has to be used together with 'canary-by-header'. The annotation is an extension of the 'canary-by-header' to allow customizing the header value instead of using hardcoded values.
It doesn't have any effect if the 'canary-by-header' annotation is not defined`,
},
canaryByHeaderPatternAnnotation: {
Validator: parser.ValidateRegex(parser.IsValidRegex, false),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation works the same way as canary-by-header-value except it does PCRE Regex matching.
Note that when 'canary-by-header-value' is set this annotation will be ignored.
When the given Regex causes error during request processing, the request will be considered as not matching.`,
},
canaryByCookieAnnotation: {
Validator: parser.ValidateRegex(parser.BasicCharsRegex, true),
Scope: parser.AnnotationScopeIngress,
Risk: parser.AnnotationRiskMedium,
Documentation: `This annotation defines the cookie that should be used for notifying the Ingress to route the request to the service specified in the Canary Ingress.
When the cookie is set to 'always', it will be routed to the canary. When the cookie is set to 'never', it will never be routed to the canary`,
},
},
}
type canary struct {
r resolver.Resolver
annotationConfig parser.Annotation
}
// Config returns the configuration rules for setting up the Canary
type Config struct {
Enabled bool
Weight int
WeightTotal int
Header string
HeaderValue string
HeaderPattern string
Cookie string
}
// NewParser parses the ingress for canary related annotations
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return canary{
r: r,
annotationConfig: CanaryAnnotations,
}
}
// Parse parses the annotations contained in the ingress
// rule used to indicate if the canary should be enabled and with what config
func (c canary) Parse(ing *networking.Ingress) (interface{}, error) {
config := &Config{}
var err error
config.Enabled, err = parser.GetBoolAnnotation(canaryAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to 'false'", canaryAnnotation)
}
config.Enabled = false
}
config.Weight, err = parser.GetIntAnnotation(canaryWeightAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to '0'", canaryWeightAnnotation)
}
config.Weight = 0
}
config.WeightTotal, err = parser.GetIntAnnotation(canaryWeightTotalAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to '100'", canaryWeightTotalAnnotation)
}
config.WeightTotal = 100
}
config.Header, err = parser.GetStringAnnotation(canaryByHeaderAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to ''", canaryByHeaderAnnotation)
}
config.Header = ""
}
config.HeaderValue, err = parser.GetStringAnnotation(canaryByHeaderValueAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to ''", canaryByHeaderValueAnnotation)
}
config.HeaderValue = ""
}
config.HeaderPattern, err = parser.GetStringAnnotation(canaryByHeaderPatternAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to ''", canaryByHeaderPatternAnnotation)
}
config.HeaderPattern = ""
}
config.Cookie, err = parser.GetStringAnnotation(canaryByCookieAnnotation, ing, c.annotationConfig.Annotations)
if err != nil {
if errors.IsValidationError(err) {
klog.Warningf("%s is invalid, defaulting to ''", canaryByCookieAnnotation)
}
config.Cookie = ""
}
if !config.Enabled && (config.Weight > 0 || config.Header != "" || config.HeaderValue != "" || config.Cookie != "" ||
config.HeaderPattern != "") {
return nil, errors.NewInvalidAnnotationConfiguration(canaryAnnotation, "configured but not enabled")
}
return config, nil
}
func (c canary) GetDocumentation() parser.AnnotationFields {
return c.annotationConfig.Annotations
}
func (c canary) Validate(anns map[string]string) error {
maxrisk := parser.StringRiskToRisk(c.r.GetSecurityConfiguration().AnnotationsRiskLevel)
return parser.CheckAnnotationRisk(anns, maxrisk, CanaryAnnotations.Annotations)
}