From ba3cc9784cb92bf50ea66114e86887fa3aa26b54 Mon Sep 17 00:00:00 2001 From: Haywood Shannon <5781935+haywoodsh@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:21:41 +0100 Subject: [PATCH 01/24] Attach VSR to VS by labels Signed-off-by: Haywood Shannon <5781935+haywoodsh@users.noreply.github.com> --- .../k8s.nginx.org_virtualserverroutes.yaml | 49 ++++++++++ .../bases/k8s.nginx.org_virtualservers.yaml | 49 ++++++++++ deploy/crds.yaml | 98 +++++++++++++++++++ .../cafe-virtual-server.yaml | 16 ++- .../coffee-virtual-server-route.yaml | 3 + .../tea-virtual-server-route.yaml | 3 + internal/configs/virtualserver.go | 7 ++ internal/k8s/configuration.go | 68 +++++++++---- internal/k8s/handlers.go | 2 +- pkg/apis/configuration/v1/types.go | 19 ++-- .../configuration/validation/virtualserver.go | 7 +- 11 files changed, 286 insertions(+), 35 deletions(-) diff --git a/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml b/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml index c6dffbdf39..ea28078e18 100644 --- a/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml +++ b/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml @@ -438,6 +438,55 @@ spec: type: array route: type: string + routeSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic splits: items: description: Split defines a split. diff --git a/config/crd/bases/k8s.nginx.org_virtualservers.yaml b/config/crd/bases/k8s.nginx.org_virtualservers.yaml index 49adfc3d41..0e57e88948 100644 --- a/config/crd/bases/k8s.nginx.org_virtualservers.yaml +++ b/config/crd/bases/k8s.nginx.org_virtualservers.yaml @@ -500,6 +500,55 @@ spec: type: array route: type: string + routeSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic splits: items: description: Split defines a split. diff --git a/deploy/crds.yaml b/deploy/crds.yaml index 6e7844db4b..986622e935 100644 --- a/deploy/crds.yaml +++ b/deploy/crds.yaml @@ -1338,6 +1338,55 @@ spec: type: array route: type: string + routeSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic splits: items: description: Split defines a split. @@ -2129,6 +2178,55 @@ spec: type: array route: type: string + routeSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic splits: items: description: Split defines a split. diff --git a/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml b/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml index df871b319a..eb05152fbf 100644 --- a/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml +++ b/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml @@ -8,7 +8,15 @@ spec: tls: secret: cafe-secret routes: - - path: /tea - route: tea/tea - - path: /coffee - route: coffee/coffee +# - path: /tea +# route: tea/tea +# - path: /coffee +# route: coffee/coffee +# - path: /coffee +# routeSelector: +# matchLabels: +# route: coffee + - path: / + routeSelector: + matchLabels: + app: cafe diff --git a/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml b/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml index 4f89de0779..1c3be7118d 100644 --- a/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml +++ b/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml @@ -3,6 +3,9 @@ kind: VirtualServerRoute metadata: name: coffee namespace: coffee + labels: + route: coffee + app: cafe spec: host: cafe.example.com upstreams: diff --git a/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml b/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml index 25e7a08b45..6939d6124a 100644 --- a/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml +++ b/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml @@ -3,6 +3,9 @@ kind: VirtualServerRoute metadata: name: tea namespace: tea + labels: +# route: tea + app: cafe spec: host: cafe.example.com upstreams: diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index 9518765e7b..dd2877db2e 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -568,6 +568,12 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( vsrPoliciesFromVs[name] = r.Policies } + continue + } else if r.RouteSelector != nil { + selector := r.RouteSelector + glog.Infof("RouteSelector: %v", selector) + + // get vsr name continue } @@ -681,6 +687,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( } errorPageLocations = append(errorPageLocations, generateErrorPageLocations(errorPages.index, errorPages.pages)...) vsrNamespaceName := fmt.Sprintf("%v/%v", vsr.Namespace, vsr.Name) + glog.Infof("vsrNamespaceName: %v", vsrNamespaceName) // use the VirtualServer error pages if the route does not define any if r.ErrorPages == nil { if vsErrorPages, ok := vsrErrorPagesFromVs[vsrNamespaceName]; ok { diff --git a/internal/k8s/configuration.go b/internal/k8s/configuration.go index 139bf3f7a4..58611224a2 100644 --- a/internal/k8s/configuration.go +++ b/internal/k8s/configuration.go @@ -2,6 +2,8 @@ package k8s import ( "fmt" + "github.com/golang/glog" + "k8s.io/apimachinery/pkg/labels" "reflect" "sort" "strings" @@ -570,6 +572,7 @@ func (c *Configuration) AddOrUpdateVirtualServerRoute(vsr *conf_v1.VirtualServer if !c.hasCorrectIngressClass(vsr) { delete(c.virtualServerRoutes, key) } else { + glog.Infof("labels: %v", vsr.ObjectMeta.Labels) validationError = c.virtualServerValidator.ValidateVirtualServerRoute(vsr) if validationError != nil { delete(c.virtualServerRoutes, key) @@ -1636,32 +1639,57 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* var warnings []string for _, r := range vs.Spec.Routes { - if r.Route == "" { - continue - } + if r.Route != "" { + vsrKey := r.Route - vsrKey := r.Route + // if route is defined without a namespace, use the namespace of VirtualServer. + if !strings.Contains(r.Route, "/") { + vsrKey = fmt.Sprintf("%s/%s", vs.Namespace, r.Route) + } - // if route is defined without a namespace, use the namespace of VirtualServer. - if !strings.Contains(r.Route, "/") { - vsrKey = fmt.Sprintf("%s/%s", vs.Namespace, r.Route) - } + vsr, exists := c.virtualServerRoutes[vsrKey] - vsr, exists := c.virtualServerRoutes[vsrKey] - if !exists { - warning := fmt.Sprintf("VirtualServerRoute %s doesn't exist or invalid", vsrKey) - warnings = append(warnings, warning) - continue - } + // if route is defined + if !exists { + warning := fmt.Sprintf("VirtualServerRoute %s doesn't exist or invalid", vsrKey) + warnings = append(warnings, warning) + continue + } + + err := c.virtualServerValidator.ValidateVirtualServerRouteForVirtualServer(vsr, vs.Spec.Host, r.Path) + if err != nil { + warning := fmt.Sprintf("VirtualServerRoute %s is invalid: %v", vsrKey, err) + warnings = append(warnings, warning) + continue + } + + vsrs = append(vsrs, vsr) + } else if r.RouteSelector != nil { + selector := &metav1.LabelSelector{ + MatchLabels: r.RouteSelector.MatchLabels, + } + sel, err := metav1.LabelSelectorAsSelector(selector) + + if err != nil { + warning := fmt.Sprintf("VirtualServerRoute LabelSelector %s is invalid: %v", selector, err) + warnings = append(warnings, warning) + continue + } + for vsrKey, vsr := range c.virtualServerRoutes { + if sel.Matches(labels.Set(vsr.ObjectMeta.Labels)) { + err := c.virtualServerValidator.ValidateVirtualServerRouteForVirtualServer(vsr, vs.Spec.Host, r.Path) + if err != nil { + warning := fmt.Sprintf("VirtualServerRoute %s is invalid: %v", vsrKey, err) + warnings = append(warnings, warning) + continue + } + glog.Infof("VirtualServerRoute %s found for label selector %v", vsrKey, selector) + vsrs = append(vsrs, vsr) + } + } - err := c.virtualServerValidator.ValidateVirtualServerRouteForVirtualServer(vsr, vs.Spec.Host, r.Path) - if err != nil { - warning := fmt.Sprintf("VirtualServerRoute %s is invalid: %v", vsrKey, err) - warnings = append(warnings, warning) - continue } - vsrs = append(vsrs, vsr) } return vsrs, warnings diff --git a/internal/k8s/handlers.go b/internal/k8s/handlers.go index cc40b0fd8d..3187c913f5 100644 --- a/internal/k8s/handlers.go +++ b/internal/k8s/handlers.go @@ -215,7 +215,7 @@ func createVirtualServerRouteHandlers(lbc *LoadBalancerController) cache.Resourc } - if !reflect.DeepEqual(oldVsr.Spec, curVsr.Spec) { + if !reflect.DeepEqual(oldVsr.Spec, curVsr.Spec) || !reflect.DeepEqual(oldVsr.Labels, curVsr.Labels) { nl.Debugf(lbc.Logger, "VirtualServerRoute %v changed, syncing", curVsr.Name) lbc.AddSyncQueue(curVsr) } diff --git a/pkg/apis/configuration/v1/types.go b/pkg/apis/configuration/v1/types.go index cac87569ab..2a4def2ede 100644 --- a/pkg/apis/configuration/v1/types.go +++ b/pkg/apis/configuration/v1/types.go @@ -182,15 +182,16 @@ type SessionCookie struct { // Route defines a route. type Route struct { - Path string `json:"path"` - Policies []PolicyReference `json:"policies"` - Route string `json:"route"` - Action *Action `json:"action"` - Splits []Split `json:"splits"` - Matches []Match `json:"matches"` - ErrorPages []ErrorPage `json:"errorPages"` - LocationSnippets string `json:"location-snippets"` - Dos string `json:"dos"` + Path string `json:"path"` + Policies []PolicyReference `json:"policies"` + Route string `json:"route"` + RouteSelector *metav1.LabelSelector `json:"routeSelector"` + Action *Action `json:"action"` + Splits []Split `json:"splits"` + Matches []Match `json:"matches"` + ErrorPages []ErrorPage `json:"errorPages"` + LocationSnippets string `json:"location-snippets"` + Dos string `json:"dos"` } // Action defines an action. diff --git a/pkg/apis/configuration/validation/virtualserver.go b/pkg/apis/configuration/validation/virtualserver.go index 77d0a89aa1..1e5e3329bd 100644 --- a/pkg/apis/configuration/validation/virtualserver.go +++ b/pkg/apis/configuration/validation/virtualserver.go @@ -758,6 +758,11 @@ func (vsv *VirtualServerValidator) validateRoute(route v1.Route, fieldPath *fiel } } + if route.RouteSelector != nil { + // TODO: validate RouteSelector + fieldCount++ + } + for i, e := range route.ErrorPages { allErrs = append(allErrs, vsv.validateErrorPage(e, fieldPath.Child("errorPages").Index(i))...) } @@ -772,7 +777,7 @@ func (vsv *VirtualServerValidator) validateRoute(route v1.Route, fieldPath *fiel } if fieldCount != 1 { - msg := "must specify exactly one of `action`, `splits` or `route`" + msg := "must specify exactly one of `action`, `splits`, `route` or `routeSelector`," if isRouteFieldForbidden || len(route.Matches) > 0 { msg = "must specify exactly one of `action` or `splits`" } From d03be9fc78f6a61adb6ecfa79d127767e050f122 Mon Sep 17 00:00:00 2001 From: Haywood Shannon <5781935+haywoodsh@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:02:41 +0000 Subject: [PATCH 02/24] add policy, location snippet, error page from VS to VSR for route selector Signed-off-by: Haywood Shannon <5781935+haywoodsh@users.noreply.github.com> Signed-off-by: Haywood Shannon <5781935+haywoodsh@users.noreply.github.com> --- .../api-key-policy.yaml | 13 +++ .../api-key-secret.yaml | 9 +++ .../cafe-virtual-server.yaml | 22 +++-- .../coffee-virtual-server-route.yaml | 14 +++- .../rate-limit.yaml | 10 +++ .../tea-virtual-server-route.yaml | 5 +- internal/configs/virtualserver.go | 81 ++++++++++++++----- internal/k8s/configuration.go | 72 ++++++++++++----- internal/k8s/configuration_test.go | 81 +++++++++++++------ internal/k8s/controller.go | 20 ++--- internal/k8s/global_configuration.go | 2 +- 11 files changed, 247 insertions(+), 82 deletions(-) create mode 100644 examples/custom-resources/cross-namespace-configuration/api-key-policy.yaml create mode 100644 examples/custom-resources/cross-namespace-configuration/api-key-secret.yaml create mode 100644 examples/custom-resources/cross-namespace-configuration/rate-limit.yaml diff --git a/examples/custom-resources/cross-namespace-configuration/api-key-policy.yaml b/examples/custom-resources/cross-namespace-configuration/api-key-policy.yaml new file mode 100644 index 0000000000..4b517b0a1f --- /dev/null +++ b/examples/custom-resources/cross-namespace-configuration/api-key-policy.yaml @@ -0,0 +1,13 @@ +apiVersion: k8s.nginx.org/v1 +kind: Policy +metadata: + name: api-key-policy + namespace: cafe +spec: + apiKey: + suppliedIn: + header: + - "X-header-name" + query: + - "queryName" + clientSecret: api-key-client-secret diff --git a/examples/custom-resources/cross-namespace-configuration/api-key-secret.yaml b/examples/custom-resources/cross-namespace-configuration/api-key-secret.yaml new file mode 100644 index 0000000000..7f3adb0711 --- /dev/null +++ b/examples/custom-resources/cross-namespace-configuration/api-key-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: api-key-client-secret + namespace: cafe +type: nginx.org/apikey +data: + client1: cGFzc3dvcmQ= # password + client2: YW5vdGhlci1wYXNzd29yZA== # another-password diff --git a/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml b/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml index eb05152fbf..d764ac5b35 100644 --- a/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml +++ b/examples/custom-resources/cross-namespace-configuration/cafe-virtual-server.yaml @@ -7,16 +7,28 @@ spec: host: cafe.example.com tls: secret: cafe-secret + server-snippets: | + # snippet defined in VS server block + proxy_set_header X-VS-Name "Cafe"; routes: # - path: /tea # route: tea/tea +# policies: +# - name: rate-limit-policy # - path: /coffee # route: coffee/coffee -# - path: /coffee -# routeSelector: -# matchLabels: -# route: coffee - path: / routeSelector: matchLabels: - app: cafe + app: cafe +# route: tea + policies: + - name: api-key-policy + location-snippets: | + # snippet defined in VS + proxy_set_header X-VS-Name "Cafe"; + errorPages: + - codes: [ 502, 503 ] + redirect: + code: 301 + url: https://nginx.org diff --git a/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml b/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml index 1c3be7118d..4e8334aea6 100644 --- a/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml +++ b/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml @@ -4,8 +4,8 @@ metadata: name: coffee namespace: coffee labels: - route: coffee app: cafe + route: coffee spec: host: cafe.example.com upstreams: @@ -16,3 +16,15 @@ spec: - path: /coffee action: pass: coffee + policies: + - name: rate-limit-policy + location-snippets: | + # snippet defined in VSR + proxy_set_header X-VSR-Name "Coffee"; + errorPages: + - codes: [404] + return: + code: 200 + body: "Original resource not found, but success!" + + diff --git a/examples/custom-resources/cross-namespace-configuration/rate-limit.yaml b/examples/custom-resources/cross-namespace-configuration/rate-limit.yaml new file mode 100644 index 0000000000..7da2027130 --- /dev/null +++ b/examples/custom-resources/cross-namespace-configuration/rate-limit.yaml @@ -0,0 +1,10 @@ +apiVersion: k8s.nginx.org/v1 +kind: Policy +metadata: + name: rate-limit-policy + namespace: coffee +spec: + rateLimit: + rate: 1r/s + key: ${binary_remote_addr} + zoneSize: 10M diff --git a/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml b/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml index 6939d6124a..b332c71fba 100644 --- a/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml +++ b/examples/custom-resources/cross-namespace-configuration/tea-virtual-server-route.yaml @@ -4,7 +4,7 @@ metadata: name: tea namespace: tea labels: -# route: tea + route: tea app: cafe spec: host: cafe.example.com @@ -16,3 +16,6 @@ spec: - path: /tea action: pass: tea +# location-snippets: | +# # snippet defined in VSR +# proxy_set_header X-VSR-Name "Tea"; diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index dd2877db2e..dc45011b50 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "net/url" "os" "path" @@ -85,23 +86,24 @@ type PodInfo struct { // VirtualServerEx holds a VirtualServer along with the resources that are referenced in this VirtualServer. type VirtualServerEx struct { - VirtualServer *conf_v1.VirtualServer - HTTPPort int - HTTPSPort int - HTTPIPv4 string - HTTPIPv6 string - HTTPSIPv4 string - HTTPSIPv6 string - Endpoints map[string][]string - VirtualServerRoutes []*conf_v1.VirtualServerRoute - ExternalNameSvcs map[string]bool - Policies map[string]*conf_v1.Policy - PodsByIP map[string]PodInfo - SecretRefs map[string]*secrets.SecretReference - ApPolRefs map[string]*unstructured.Unstructured - LogConfRefs map[string]*unstructured.Unstructured - DosProtectedRefs map[string]*unstructured.Unstructured - DosProtectedEx map[string]*DosEx + VirtualServer *conf_v1.VirtualServer + HTTPPort int + HTTPSPort int + HTTPIPv4 string + HTTPIPv6 string + HTTPSIPv4 string + HTTPSIPv6 string + Endpoints map[string][]string + VirtualServerRoutes []*conf_v1.VirtualServerRoute + VirtualServerSelectorRoutes map[string][]string + ExternalNameSvcs map[string]bool + Policies map[string]*conf_v1.Policy + PodsByIP map[string]PodInfo + SecretRefs map[string]*secrets.SecretReference + ApPolRefs map[string]*unstructured.Unstructured + LogConfRefs map[string]*unstructured.Unstructured + DosProtectedRefs map[string]*unstructured.Unstructured + DosProtectedEx map[string]*DosEx } func (vsx *VirtualServerEx) String() string { @@ -398,6 +400,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( apResources *appProtectResourcesForVS, dosResources map[string]*appProtectDosResource, ) (version2.VirtualServerConfig, Warnings) { + //l := nl.LoggerFromContext(vsc.cfgParams.Context) vsc.clearWarnings() useCustomListeners := false @@ -570,10 +573,48 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( continue } else if r.RouteSelector != nil { - selector := r.RouteSelector - glog.Infof("RouteSelector: %v", selector) // get vsr name + + selector := &metav1.LabelSelector{ + MatchLabels: r.RouteSelector.MatchLabels, + } + sel, _ := metav1.LabelSelectorAsSelector(selector) + + selectorKey := sel.String() + vsrKeys := vsEx.VirtualServerSelectorRoutes[selectorKey] + //nl.Infof(l, "VirtualServerRoutes: %v", vsEx.VirtualServerRoutes) + // + //nl.Infof(l, "VirtualServerSelectorRoutes: %v", vsEx.VirtualServerSelectorRoutes) + // + //nl.Infof(l, "vsrKeys: %v", vsrKeys) + // + //nl.Infof(l, "RouteSelector: %v", selector) + + // store route location snippet for the referenced VirtualServerRoute in case they don't define their own + if r.LocationSnippets != "" { + for _, name := range vsrKeys { + vsrLocationSnippetsFromVs[name] = r.LocationSnippets + } + } + + // store route error pages and route index for the referenced VirtualServerRoute in case they don't define their own + if len(r.ErrorPages) > 0 { + for _, name := range vsrKeys { + vsrErrorPagesFromVs[name] = errorPages.pages + vsrErrorPagesRouteIndex[name] = errorPages.index + } + } + + // store route policies for the referenced VirtualServerRoute in case they don't define their own + if len(r.Policies) > 0 { + //nl.Infof(l, "Route Policies: %v", r.Policies) + for _, name := range vsrKeys { + //nl.Infof(l, "Adding policy to VSR $v: %v", name, r.Policies) + vsrPoliciesFromVs[name] = r.Policies + } + } + continue } @@ -687,7 +728,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( } errorPageLocations = append(errorPageLocations, generateErrorPageLocations(errorPages.index, errorPages.pages)...) vsrNamespaceName := fmt.Sprintf("%v/%v", vsr.Namespace, vsr.Name) - glog.Infof("vsrNamespaceName: %v", vsrNamespaceName) + //glog.Infof("vsrNamespaceName: %v", vsrNamespaceName) // use the VirtualServer error pages if the route does not define any if r.ErrorPages == nil { if vsErrorPages, ok := vsrErrorPagesFromVs[vsrNamespaceName]; ok { diff --git a/internal/k8s/configuration.go b/internal/k8s/configuration.go index 58611224a2..12cd4c6a6d 100644 --- a/internal/k8s/configuration.go +++ b/internal/k8s/configuration.go @@ -2,8 +2,9 @@ package k8s import ( "fmt" - "github.com/golang/glog" + nl "github.com/nginxinc/kubernetes-ingress/internal/logger" "k8s.io/apimachinery/pkg/labels" + "log/slog" "reflect" "sort" "strings" @@ -207,23 +208,25 @@ func NewMinionConfiguration(ing *networking.Ingress) *MinionConfiguration { // VirtualServerConfiguration holds a VirtualServer along with its VirtualServerRoutes. type VirtualServerConfiguration struct { - VirtualServer *conf_v1.VirtualServer - VirtualServerRoutes []*conf_v1.VirtualServerRoute - Warnings []string - HTTPPort int - HTTPSPort int - HTTPIPv4 string - HTTPIPv6 string - HTTPSIPv4 string - HTTPSIPv6 string + VirtualServer *conf_v1.VirtualServer + VirtualServerRoutes []*conf_v1.VirtualServerRoute + VirtualServerRouteSelectors map[string][]string + Warnings []string + HTTPPort int + HTTPSPort int + HTTPIPv4 string + HTTPIPv6 string + HTTPSIPv4 string + HTTPSIPv6 string } // NewVirtualServerConfiguration creates a VirtualServerConfiguration. -func NewVirtualServerConfiguration(vs *conf_v1.VirtualServer, vsrs []*conf_v1.VirtualServerRoute, warnings []string) *VirtualServerConfiguration { +func NewVirtualServerConfiguration(vs *conf_v1.VirtualServer, vsrs []*conf_v1.VirtualServerRoute, routeSelectors map[string][]string, warnings []string) *VirtualServerConfiguration { return &VirtualServerConfiguration{ - VirtualServer: vs, - VirtualServerRoutes: vsrs, - Warnings: warnings, + VirtualServer: vs, + VirtualServerRoutes: vsrs, + VirtualServerRouteSelectors: routeSelectors, + Warnings: warnings, } } @@ -270,6 +273,11 @@ func (vsc *VirtualServerConfiguration) IsEqual(resource Resource) bool { } } + //TODO: check all values of the map + if len(vsc.VirtualServerRouteSelectors) != len(vsConfig.VirtualServerRouteSelectors) { + return false + } + return true } @@ -344,6 +352,10 @@ type TransportServerMetrics struct { // The IC needs to ensure that at any point in time the NGINX config on the filesystem reflects the state // of the objects in the Configuration. type Configuration struct { + //Context context.Context + + logger *slog.Logger + hosts map[string]Resource listenerHosts map[listenerHostKey]*TransportServerConfiguration listenerMap map[string]conf_v1.Listener @@ -386,6 +398,8 @@ type Configuration struct { // NewConfiguration creates a new Configuration. func NewConfiguration( + //ctx context.Context, + logger *slog.Logger, hasCorrectIngressClass func(interface{}) bool, isPlus bool, appProtectEnabled bool, @@ -399,7 +413,11 @@ func NewConfiguration( isCertManagerEnabled bool, isIPV6Disabled bool, ) *Configuration { + //l := nl.LoggerFromContext(ctx) + return &Configuration{ + //Context: ctx, + logger: logger, hosts: make(map[string]Resource), listenerHosts: make(map[listenerHostKey]*TransportServerConfiguration), ingresses: make(map[string]*networking.Ingress), @@ -426,6 +444,7 @@ func NewConfiguration( snippetsEnabled: snippetsEnabled, isCertManagerEnabled: isCertManagerEnabled, isIPV6Disabled: isIPV6Disabled, + //logger: l, } } @@ -572,7 +591,7 @@ func (c *Configuration) AddOrUpdateVirtualServerRoute(vsr *conf_v1.VirtualServer if !c.hasCorrectIngressClass(vsr) { delete(c.virtualServerRoutes, key) } else { - glog.Infof("labels: %v", vsr.ObjectMeta.Labels) + nl.Debugf(c.logger, "labels: %v", vsr.ObjectMeta.Labels) validationError = c.virtualServerValidator.ValidateVirtualServerRoute(vsr) if validationError != nil { delete(c.virtualServerRoutes, key) @@ -1419,6 +1438,7 @@ func squashResourceChanges(changes []ResourceChange) []ResourceChange { } func (c *Configuration) buildHostsAndResources() (newHosts map[string]Resource, newResources map[string]Resource) { + //l := nl.LoggerFromContext(c.Context) newHosts = make(map[string]Resource) newResources = make(map[string]Resource) var challengesVSR []*conf_v1.VirtualServerRoute @@ -1474,13 +1494,14 @@ func (c *Configuration) buildHostsAndResources() (newHosts map[string]Resource, for _, key := range getSortedVirtualServerKeys(c.virtualServers) { vs := c.virtualServers[key] - vsrs, warnings := c.buildVirtualServerRoutes(vs) + vsrs, routeSelectorMap, warnings := c.buildVirtualServerRoutes(vs) for _, vsr := range challengesVSR { if vs.Spec.Host == vsr.Spec.Host { vsrs = append(vsrs, vsr) } } - resource := NewVirtualServerConfiguration(vs, vsrs, warnings) + resource := NewVirtualServerConfiguration(vs, vsrs, routeSelectorMap, warnings) + nl.Infof(c.logger, "resource: %v", resource) c.buildListenersForVSConfiguration(resource) @@ -1634,8 +1655,10 @@ func (c *Configuration) buildMinionConfigs(masterHost string) ([]*MinionConfigur return minionConfigs, childWarnings } -func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]*conf_v1.VirtualServerRoute, []string) { +func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]*conf_v1.VirtualServerRoute, map[string][]string, []string) { + //l := nl.LoggerFromContext(c.Context) var vsrs []*conf_v1.VirtualServerRoute + var routeSelectorMap map[string][]string var warnings []string for _, r := range vs.Spec.Routes { @@ -1670,6 +1693,10 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* } sel, err := metav1.LabelSelectorAsSelector(selector) + selectorKey := sel.String() + + //routeSelectorMap[selectorKey] := []*conf_v1.VirtualServerRoute{} + if err != nil { warning := fmt.Sprintf("VirtualServerRoute LabelSelector %s is invalid: %v", selector, err) warnings = append(warnings, warning) @@ -1683,7 +1710,12 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* warnings = append(warnings, warning) continue } - glog.Infof("VirtualServerRoute %s found for label selector %v", vsrKey, selector) + nl.Infof(c.logger, "VirtualServerRoute %s found for label selector %v", vsrKey, selector) + if routeSelectorMap == nil { + routeSelectorMap = make(map[string][]string) + } + routeSelectorMap[selectorKey] = append(routeSelectorMap[selectorKey], vsrKey) + vsrs = append(vsrs, vsr) } } @@ -1692,7 +1724,7 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* } - return vsrs, warnings + return vsrs, routeSelectorMap, warnings } // GetTransportServerMetrics returns metrics about TransportServers diff --git a/internal/k8s/configuration_test.go b/internal/k8s/configuration_test.go index b8d0289f09..50ec07e63c 100644 --- a/internal/k8s/configuration_test.go +++ b/internal/k8s/configuration_test.go @@ -27,6 +27,7 @@ func createTestConfiguration() *Configuration { snippetsEnabled := true isIPV6Disabled := false return NewConfiguration( + lbc.Logger, lbc.HasCorrectIngressClass, isPlus, appProtectEnabled, @@ -1182,12 +1183,13 @@ func TestDeleteNonExistingVirtualServer(t *testing.T) { } } +// TODO: vsr route selector test func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { configuration := createTestConfiguration() // Add VirtualServerRoute-1 - vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first") + vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first", nil) var expectedChanges []ResourceChange expectedProblems := []ConfigurationProblem{ { @@ -1219,6 +1221,10 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Path: "/second", Route: "virtualserverroute-2", }, + { + Path: "/", + RouteSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "route"}}, + }, }) expectedChanges = []ResourceChange{ { @@ -1240,10 +1246,10 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) } - vsr2 := createTestVirtualServerRoute("virtualserverroute-2", "foo.example.com", "/second") - // Add VirtualServerRoute-2 + vsr2 := createTestVirtualServerRoute("virtualserverroute-2", "foo.example.com", "/second", nil) + expectedChanges = []ResourceChange{ { Op: AddOrUpdate, @@ -1287,6 +1293,29 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) } + // Add VirtualServerRoute-3 and VirtualServerRoute-4 with selectors + + vsr3 := createTestVirtualServerRoute("virtualserverroute-3", "foo.example.com", "/third", map[string]string{"app": "route"}) + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{updatedVSR1, vsr2, vsr3}, + VirtualServerRouteSelectors: map[string][]string{"app=route": {"default/virtualserverroute-3"}}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr3) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + // Make VirtualServerRoute-1 invalid invalidVSR1 := updatedVSR1.DeepCopy() @@ -1297,7 +1326,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2, vsr3}, Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"}, }, }, @@ -1326,7 +1355,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2, vsr3}, }, }, } @@ -1350,7 +1379,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2, vsr3}, Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 is invalid: spec.subroutes[0]: Invalid value: \"/\": must start with '/first'"}, }, }, @@ -1378,7 +1407,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2, vsr3}, }, }, } @@ -1402,7 +1431,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr3}, Warnings: []string{"VirtualServerRoute default/virtualserverroute-2 is invalid: spec.host: Invalid value: \"bar.example.com\": must be equal to 'foo.example.com'"}, }, }, @@ -1461,7 +1490,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr3}, Warnings: []string{"VirtualServerRoute default/virtualserverroute-2 is invalid: spec.host: Invalid value: \"bar.example.com\": must be equal to 'foo.example.com'"}, }, }, @@ -1489,7 +1518,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2, vsr3}, }, }, } @@ -1510,7 +1539,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { Op: AddOrUpdate, Resource: &VirtualServerConfiguration{ VirtualServer: vs, - VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2}, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2, vsr3}, Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"}, }, }, @@ -1570,7 +1599,7 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { func TestAddInvalidVirtualServerRoute(t *testing.T) { configuration := createTestConfiguration() - vsr := createTestVirtualServerRoute("virtualserverroute", "", "/") + vsr := createTestVirtualServerRoute("virtualserverroute", "", "/", nil) var expectedChanges []ResourceChange expectedProblems := []ConfigurationProblem{ @@ -1594,7 +1623,7 @@ func TestAddInvalidVirtualServerRoute(t *testing.T) { func TestAddVirtualServerWithIncorrectClass(t *testing.T) { configuration := createTestConfiguration() - vsr := createTestVirtualServerRoute("virtualserver", "foo.example.com", "/") + vsr := createTestVirtualServerRoute("virtualserver", "foo.example.com", "/", nil) vsr.Spec.IngressClass = "someproxy" var expectedChanges []ResourceChange @@ -3763,11 +3792,12 @@ func createTestVirtualServerWithRoutes(name string, host string, routes []conf_v return vs } -func createTestVirtualServerRoute(name string, host string, path string) *conf_v1.VirtualServerRoute { +func createTestVirtualServerRoute(name string, host string, path string, labels map[string]string) *conf_v1.VirtualServerRoute { return &conf_v1.VirtualServerRoute{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: name, + Labels: labels, }, Spec: conf_v1.VirtualServerRouteSpec{ IngressClass: "nginx", @@ -4123,7 +4153,7 @@ func TestFindResourcesForResourceReference(t *testing.T) { Route: "virtualserverroute", }, }) - vsr := createTestVirtualServerRoute("virtualserverroute", "asd.example.com", "/") + vsr := createTestVirtualServerRoute("virtualserverroute", "asd.example.com", "/", nil) tsPassthrough := createTestTLSPassthroughTransportServer("transportserver-passthrough", "ts.example.com") listeners := []conf_v1.Listener{ { @@ -4494,6 +4524,7 @@ func TestIsEqualForIngressConfigurations(t *testing.T) { } } +// TODO: vsr route selector test func TestIsEqualForVirtualServers(t *testing.T) { t.Parallel() vs := createTestVirtualServerWithRoutes( @@ -4505,7 +4536,7 @@ func TestIsEqualForVirtualServers(t *testing.T) { Route: "virtualserverroute", }, }) - vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/") + vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/", nil) vsWithUpdatedGen := vs.DeepCopy() vsWithUpdatedGen.Generation++ @@ -4520,26 +4551,26 @@ func TestIsEqualForVirtualServers(t *testing.T) { msg string }{ { - vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}), - vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}), + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), expected: true, msg: "equal virtual servers", }, { - vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}), - vsConfig2: NewVirtualServerConfiguration(vsWithUpdatedGen, []*conf_v1.VirtualServerRoute{vsr}, []string{}), + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vsWithUpdatedGen, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), expected: false, msg: "virtual servers with different generation", }, { - vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}), - vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{}, []string{}), + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{}, nil, []string{}), expected: false, msg: "virtual servers with different number of virtual server routes", }, { - vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}), - vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsrWithUpdatedGen}, []string{}), + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsrWithUpdatedGen}, nil, []string{}), expected: false, msg: "virtual servers with virtual server routes with different generation", }, @@ -4556,7 +4587,7 @@ func TestIsEqualForVirtualServers(t *testing.T) { func TestIsEqualForDifferentResources(t *testing.T) { t.Parallel() ingConfig := NewRegularIngressConfiguration(createTestIngress("ingress", "foo.example.com")) - vsConfig := NewVirtualServerConfiguration(createTestVirtualServer("virtualserver", "bar.example.com"), []*conf_v1.VirtualServerRoute{}, []string{}) + vsConfig := NewVirtualServerConfiguration(createTestVirtualServer("virtualserver", "bar.example.com"), []*conf_v1.VirtualServerRoute{}, nil, []string{}) result := ingConfig.IsEqual(vsConfig) if result != false { diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 3276b632bb..09719cef17 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -340,6 +340,7 @@ func NewLoadBalancerController(input NewLoadBalancerControllerInput) *LoadBalanc } lbc.configuration = NewConfiguration( + lbc.Logger, lbc.HasCorrectIngressClass, input.IsNginxPlus, input.AppProtectEnabled, @@ -820,7 +821,7 @@ func (lbc *LoadBalancerController) createExtendedResources(resources []Resource) switch impl := r.(type) { case *VirtualServerConfiguration: vs := impl.VirtualServer - vsEx := lbc.createVirtualServerEx(vs, impl.VirtualServerRoutes) + vsEx := lbc.createVirtualServerEx(vs, impl.VirtualServerRoutes, impl.VirtualServerRouteSelectors) result.VirtualServerExes = append(result.VirtualServerExes, vsEx) case *IngressConfiguration: @@ -1173,7 +1174,7 @@ func (lbc *LoadBalancerController) processChanges(changes []ResourceChange) { if c.Op == AddOrUpdate { switch impl := c.Resource.(type) { case *VirtualServerConfiguration: - vsEx := lbc.createVirtualServerEx(impl.VirtualServer, impl.VirtualServerRoutes) + vsEx := lbc.createVirtualServerEx(impl.VirtualServer, impl.VirtualServerRoutes, impl.VirtualServerRouteSelectors) warnings, addOrUpdateErr := lbc.configurator.AddOrUpdateVirtualServer(vsEx) lbc.updateVirtualServerStatusAndEvents(impl, warnings, addOrUpdateErr) @@ -2115,13 +2116,14 @@ func (lbc *LoadBalancerController) createIngressEx(ing *networking.Ingress, vali return ingEx } -func (lbc *LoadBalancerController) createVirtualServerEx(virtualServer *conf_v1.VirtualServer, virtualServerRoutes []*conf_v1.VirtualServerRoute) *configs.VirtualServerEx { +func (lbc *LoadBalancerController) createVirtualServerEx(virtualServer *conf_v1.VirtualServer, virtualServerRoutes []*conf_v1.VirtualServerRoute, selectorMap map[string][]string) *configs.VirtualServerEx { virtualServerEx := configs.VirtualServerEx{ - VirtualServer: virtualServer, - SecretRefs: make(map[string]*secrets.SecretReference), - ApPolRefs: make(map[string]*unstructured.Unstructured), - LogConfRefs: make(map[string]*unstructured.Unstructured), - DosProtectedEx: make(map[string]*configs.DosEx), + VirtualServer: virtualServer, + VirtualServerSelectorRoutes: selectorMap, + SecretRefs: make(map[string]*secrets.SecretReference), + ApPolRefs: make(map[string]*unstructured.Unstructured), + LogConfRefs: make(map[string]*unstructured.Unstructured), + DosProtectedEx: make(map[string]*configs.DosEx), } resource := lbc.configuration.hosts[virtualServer.Spec.Host] @@ -3363,7 +3365,7 @@ func (lbc *LoadBalancerController) haltIfVSRConfigInvalid(vsrNew *conf_v1.Virtua if c.Op == AddOrUpdate { switch impl := c.Resource.(type) { case *VirtualServerConfiguration: - vsEx = lbc.createVirtualServerEx(impl.VirtualServer, impl.VirtualServerRoutes) + vsEx = lbc.createVirtualServerEx(impl.VirtualServer, impl.VirtualServerRoutes, impl.VirtualServerRouteSelectors) lbc.updateVirtualServerStatusAndEvents(impl, configs.Warnings{}, nil) } } diff --git a/internal/k8s/global_configuration.go b/internal/k8s/global_configuration.go index fb71023b04..3108ecc532 100644 --- a/internal/k8s/global_configuration.go +++ b/internal/k8s/global_configuration.go @@ -124,7 +124,7 @@ func (lbc *LoadBalancerController) processChangesFromGlobalConfiguration(changes switch impl := c.Resource.(type) { case *VirtualServerConfiguration: if c.Op == AddOrUpdate { - vsEx := lbc.createVirtualServerEx(impl.VirtualServer, impl.VirtualServerRoutes) + vsEx := lbc.createVirtualServerEx(impl.VirtualServer, impl.VirtualServerRoutes, impl.VirtualServerRouteSelectors) updatedVSExes = append(updatedVSExes, vsEx) updatedResources = append(updatedResources, impl) From aaaa4dfdcefe419b084b371d4b8c12d25ed3385a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:07:03 +0000 Subject: [PATCH 03/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../coffee-virtual-server-route.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml b/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml index 4e8334aea6..7d1ac9bffb 100644 --- a/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml +++ b/examples/custom-resources/cross-namespace-configuration/coffee-virtual-server-route.yaml @@ -26,5 +26,3 @@ spec: return: code: 200 body: "Original resource not found, but success!" - - From ef5dd51b608ec2d1228e3d408d93bf7034b71ae6 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Wed, 13 Nov 2024 17:19:03 +0000 Subject: [PATCH 04/24] fix linting issues --- internal/configs/virtualserver.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index dc45011b50..5a6091da77 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -5,7 +5,6 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "net/url" "os" "path" @@ -13,6 +12,8 @@ import ( "strconv" "strings" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/nginxinc/kubernetes-ingress/internal/configs/version2" "github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets" nl "github.com/nginxinc/kubernetes-ingress/internal/logger" @@ -400,7 +401,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( apResources *appProtectResourcesForVS, dosResources map[string]*appProtectDosResource, ) (version2.VirtualServerConfig, Warnings) { - //l := nl.LoggerFromContext(vsc.cfgParams.Context) + // l := nl.LoggerFromContext(vsc.cfgParams.Context) vsc.clearWarnings() useCustomListeners := false @@ -608,9 +609,9 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( // store route policies for the referenced VirtualServerRoute in case they don't define their own if len(r.Policies) > 0 { - //nl.Infof(l, "Route Policies: %v", r.Policies) + // nl.Infof(l, "Route Policies: %v", r.Policies) for _, name := range vsrKeys { - //nl.Infof(l, "Adding policy to VSR $v: %v", name, r.Policies) + // nl.Infof(l, "Adding policy to VSR $v: %v", name, r.Policies) vsrPoliciesFromVs[name] = r.Policies } } @@ -728,7 +729,7 @@ func (vsc *virtualServerConfigurator) GenerateVirtualServerConfig( } errorPageLocations = append(errorPageLocations, generateErrorPageLocations(errorPages.index, errorPages.pages)...) vsrNamespaceName := fmt.Sprintf("%v/%v", vsr.Namespace, vsr.Name) - //glog.Infof("vsrNamespaceName: %v", vsrNamespaceName) + // glog.Infof("vsrNamespaceName: %v", vsrNamespaceName) // use the VirtualServer error pages if the route does not define any if r.ErrorPages == nil { if vsErrorPages, ok := vsrErrorPagesFromVs[vsrNamespaceName]; ok { From 6440451d38a184f7b770167e40541a8daf6c50b4 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 14:15:15 +0000 Subject: [PATCH 05/24] Use a separate example for testing VSR functionality --- .../vsr-route-selector/README.md | 146 ++++++++++++++++++ .../vsr-route-selector/api-key-policy.yaml | 13 ++ .../vsr-route-selector/api-key-secret.yaml | 9 ++ .../vsr-route-selector/cafe-secret.yaml | 9 ++ .../cafe-virtual-server.yaml | 34 ++++ .../coffee-virtual-server-route.yaml | 28 ++++ .../vsr-route-selector/coffee.yaml | 34 ++++ .../vsr-route-selector/namespaces.yaml | 14 ++ .../vsr-route-selector/rate-limit.yaml | 10 ++ .../tea-virtual-server-route.yaml | 21 +++ .../vsr-route-selector/tea.yaml | 34 ++++ 11 files changed, 352 insertions(+) create mode 100644 examples/custom-resources/vsr-route-selector/README.md create mode 100644 examples/custom-resources/vsr-route-selector/api-key-policy.yaml create mode 100644 examples/custom-resources/vsr-route-selector/api-key-secret.yaml create mode 100644 examples/custom-resources/vsr-route-selector/cafe-secret.yaml create mode 100644 examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml create mode 100644 examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml create mode 100644 examples/custom-resources/vsr-route-selector/coffee.yaml create mode 100644 examples/custom-resources/vsr-route-selector/namespaces.yaml create mode 100644 examples/custom-resources/vsr-route-selector/rate-limit.yaml create mode 100644 examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml create mode 100644 examples/custom-resources/vsr-route-selector/tea.yaml diff --git a/examples/custom-resources/vsr-route-selector/README.md b/examples/custom-resources/vsr-route-selector/README.md new file mode 100644 index 0000000000..6e415205fc --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/README.md @@ -0,0 +1,146 @@ +# Cross-Namespace Configuration + +In this example we use the [VirtualServer and +VirtualServerRoute](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) +resources to configure load balancing for the modified cafe application from the [Basic +Configuration](../basic-configuration/) example. We have put the load balancing configuration as well as the deployments +and services into multiple namespaces. Instead of one namespace, we now use three: `tea`, `coffee`, and `cafe`. + +- In the tea namespace, we create the tea deployment, service, and the corresponding load-balancing configuration. +- In the coffee namespace, we create the coffee deployment, service, and the corresponding load-balancing configuration. +- In the cafe namespace, we create the cafe secret with the TLS certificate and key and the load-balancing configuration + for the cafe application. That configuration references the coffee and tea configurations. + +## Prerequisites + +1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/) + instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + + ```console + IC_IP=XXX.YYY.ZZZ.III + ``` + +1. Save the HTTPS port of the Ingress Controller into a shell variable: + + ```console + IC_HTTPS_PORT= + ``` + +## Step 1 - Create Namespaces + +Create the required tea, coffee, and cafe namespaces: + +```console +kubectl create -f namespaces.yaml +``` + +## Step 2 - Deploy the Cafe Application + +1. Create the tea deployment and service in the tea namespace: + + ```console + kubectl create -f tea.yaml + ``` + +1. Create the coffee deployment and service in the coffee namespace: + + ```console + kubectl create -f coffee.yaml + ``` + +## Step 3 - Configure Load Balancing and TLS Termination + +1. Create the VirtualServerRoute resource for tea in the tea namespace: + + ```console + kubectl create -f tea-virtual-server-route.yaml + ``` + +1. Create the VirtualServerRoute resource for coffee in the coffee namespace: + + ```console + kubectl create -f coffee-virtual-server-route.yaml + ``` + +1. Create the secret with the TLS certificate and key in the cafe namespace: + + ```console + kubectl create -f cafe-secret.yaml + ``` + +1. Create the VirtualServer resource for the cafe app in the cafe namespace: + + ```console + kubectl create -f cafe-virtual-server.yaml + ``` + +## Step 4 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServerRoutes and + VirtualServer: + + ```console + kubectl describe virtualserverroute tea -n tea + ``` + + ```text + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Warning NoVirtualServersFound 2m nginx-ingress-controller No VirtualServer references VirtualServerRoute tea/tea + Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for tea/tea was added or updated + ``` + + ```console + kubectl describe virtualserverroute coffee -n coffee + ``` + + ```text + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Warning NoVirtualServersFound 2m nginx-ingress-controller No VirtualServer references VirtualServerRoute coffee/coffee + Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for coffee/coffee was added or updated + ``` + + ```console + kubectl describe virtualserver cafe -n cafe + ``` + + ```text + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for cafe/cafe was added or updated + ``` + +1. Access the application using curl. We'll use curl's `--insecure` option to turn off certificate verification of our + self-signed certificate and `--resolve` option to set the IP address and HTTPS port of the Ingress Controller to the + domain name of the cafe application: + + To get coffee: + + ```console + curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure + ``` + + ```text + Server address: 10.16.1.193:80 + Server name: coffee-7dbb5795f6-mltpf + ... + ``` + + If your prefer tea: + + ```console + curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/tea --insecure + ``` + + ```text + Server address: 10.16.0.157:80 + Server name: tea-7d57856c44-674b8 + ... diff --git a/examples/custom-resources/vsr-route-selector/api-key-policy.yaml b/examples/custom-resources/vsr-route-selector/api-key-policy.yaml new file mode 100644 index 0000000000..4b517b0a1f --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/api-key-policy.yaml @@ -0,0 +1,13 @@ +apiVersion: k8s.nginx.org/v1 +kind: Policy +metadata: + name: api-key-policy + namespace: cafe +spec: + apiKey: + suppliedIn: + header: + - "X-header-name" + query: + - "queryName" + clientSecret: api-key-client-secret diff --git a/examples/custom-resources/vsr-route-selector/api-key-secret.yaml b/examples/custom-resources/vsr-route-selector/api-key-secret.yaml new file mode 100644 index 0000000000..7f3adb0711 --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/api-key-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: api-key-client-secret + namespace: cafe +type: nginx.org/apikey +data: + client1: cGFzc3dvcmQ= # password + client2: YW5vdGhlci1wYXNzd29yZA== # another-password diff --git a/examples/custom-resources/vsr-route-selector/cafe-secret.yaml b/examples/custom-resources/vsr-route-selector/cafe-secret.yaml new file mode 100644 index 0000000000..81da195ae4 --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/cafe-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cafe-secret + namespace: cafe +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== diff --git a/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml b/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml new file mode 100644 index 0000000000..d764ac5b35 --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml @@ -0,0 +1,34 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe + namespace: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + server-snippets: | + # snippet defined in VS server block + proxy_set_header X-VS-Name "Cafe"; + routes: +# - path: /tea +# route: tea/tea +# policies: +# - name: rate-limit-policy +# - path: /coffee +# route: coffee/coffee + - path: / + routeSelector: + matchLabels: + app: cafe +# route: tea + policies: + - name: api-key-policy + location-snippets: | + # snippet defined in VS + proxy_set_header X-VS-Name "Cafe"; + errorPages: + - codes: [ 502, 503 ] + redirect: + code: 301 + url: https://nginx.org diff --git a/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml b/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml new file mode 100644 index 0000000000..7d1ac9bffb --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml @@ -0,0 +1,28 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServerRoute +metadata: + name: coffee + namespace: coffee + labels: + app: cafe + route: coffee +spec: + host: cafe.example.com + upstreams: + - name: coffee + service: coffee-svc + port: 80 + subroutes: + - path: /coffee + action: + pass: coffee + policies: + - name: rate-limit-policy + location-snippets: | + # snippet defined in VSR + proxy_set_header X-VSR-Name "Coffee"; + errorPages: + - codes: [404] + return: + code: 200 + body: "Original resource not found, but success!" diff --git a/examples/custom-resources/vsr-route-selector/coffee.yaml b/examples/custom-resources/vsr-route-selector/coffee.yaml new file mode 100644 index 0000000000..1349f927ce --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/coffee.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee + namespace: coffee +spec: + replicas: 1 + selector: + matchLabels: + app: coffee + template: + metadata: + labels: + app: coffee + spec: + containers: + - name: coffee + image: nginxdemos/nginx-hello:plain-text + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-svc + namespace: coffee +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: coffee diff --git a/examples/custom-resources/vsr-route-selector/namespaces.yaml b/examples/custom-resources/vsr-route-selector/namespaces.yaml new file mode 100644 index 0000000000..6c47c83f34 --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/namespaces.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cafe +--- +apiVersion: v1 +kind: Namespace +metadata: + name: tea +--- +apiVersion: v1 +kind: Namespace +metadata: + name: coffee diff --git a/examples/custom-resources/vsr-route-selector/rate-limit.yaml b/examples/custom-resources/vsr-route-selector/rate-limit.yaml new file mode 100644 index 0000000000..7da2027130 --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/rate-limit.yaml @@ -0,0 +1,10 @@ +apiVersion: k8s.nginx.org/v1 +kind: Policy +metadata: + name: rate-limit-policy + namespace: coffee +spec: + rateLimit: + rate: 1r/s + key: ${binary_remote_addr} + zoneSize: 10M diff --git a/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml b/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml new file mode 100644 index 0000000000..b332c71fba --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml @@ -0,0 +1,21 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServerRoute +metadata: + name: tea + namespace: tea + labels: + route: tea + app: cafe +spec: + host: cafe.example.com + upstreams: + - name: tea + service: tea-svc + port: 80 + subroutes: + - path: /tea + action: + pass: tea +# location-snippets: | +# # snippet defined in VSR +# proxy_set_header X-VSR-Name "Tea"; diff --git a/examples/custom-resources/vsr-route-selector/tea.yaml b/examples/custom-resources/vsr-route-selector/tea.yaml new file mode 100644 index 0000000000..615e2fd436 --- /dev/null +++ b/examples/custom-resources/vsr-route-selector/tea.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea + namespace: tea +spec: + replicas: 1 + selector: + matchLabels: + app: tea + template: + metadata: + labels: + app: tea + spec: + containers: + - name: tea + image: nginxdemos/nginx-hello:plain-text + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-svc + namespace: tea +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: tea From 211dc33e231f57aae9ddb074cd008732889a6c4f Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 14:57:43 +0000 Subject: [PATCH 06/24] Keep vsr tests separate for now --- internal/k8s/configuration.go | 14 +- internal/k8s/configuration_vsr_test.go | 619 +++++++++++++++++++++++++ 2 files changed, 629 insertions(+), 4 deletions(-) create mode 100644 internal/k8s/configuration_vsr_test.go diff --git a/internal/k8s/configuration.go b/internal/k8s/configuration.go index 12cd4c6a6d..6a8b44c9ed 100644 --- a/internal/k8s/configuration.go +++ b/internal/k8s/configuration.go @@ -2,14 +2,15 @@ package k8s import ( "fmt" - nl "github.com/nginxinc/kubernetes-ingress/internal/logger" - "k8s.io/apimachinery/pkg/labels" "log/slog" "reflect" "sort" "strings" "sync" + nl "github.com/nginxinc/kubernetes-ingress/internal/logger" + "k8s.io/apimachinery/pkg/labels" + "github.com/nginxinc/kubernetes-ingress/internal/configs" conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/validation" @@ -1501,7 +1502,9 @@ func (c *Configuration) buildHostsAndResources() (newHosts map[string]Resource, } } resource := NewVirtualServerConfiguration(vs, vsrs, routeSelectorMap, warnings) - nl.Infof(c.logger, "resource: %v", resource) + + // todo - uncomment (Jakub) + //nl.Infof(c.logger, "resource: %v", resource) c.buildListenersForVSConfiguration(resource) @@ -1710,7 +1713,10 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* warnings = append(warnings, warning) continue } - nl.Infof(c.logger, "VirtualServerRoute %s found for label selector %v", vsrKey, selector) + + // todo (Jakub) + // nl.Infof(c.logger, "VirtualServerRoute %s found for label selector %v", vsrKey, selector) + if routeSelectorMap == nil { routeSelectorMap = make(map[string][]string) } diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go new file mode 100644 index 0000000000..d3d8132e16 --- /dev/null +++ b/internal/k8s/configuration_vsr_test.go @@ -0,0 +1,619 @@ +package k8s + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// WIP - Jakub +func TestAddVirtualServerVSR(t *testing.T) { + configuration := createTestConfiguration() + + // no problems are expected for all cases + var expectedProblems []ConfigurationProblem + + // Add a VirtualServer + + vs := createTestVirtualServer("virtualserver", "foo.example.com") + expectedChanges := []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + }, + }, + } + + changes, problems := configuration.AddOrUpdateVirtualServer(vs) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Update VirtualServer + + updatedVS := vs.DeepCopy() + updatedVS.Generation++ + updatedVS.Spec.ServerSnippets = "# snippet" + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: updatedVS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Make VirtualServer invalid + + invalidVS := updatedVS.DeepCopy() + invalidVS.Generation++ + invalidVS.Spec.Host = "" + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &VirtualServerConfiguration{ + VirtualServer: updatedVS, + }, + Error: "spec.host: Required value", + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServer(invalidVS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore VirtualServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: updatedVS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Update VirtualServer host + + updatedHostVS := updatedVS.DeepCopy() + updatedHostVS.Generation++ + updatedHostVS.Spec.Host = "bar.example.com" + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: updatedHostVS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServer(updatedHostVS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Delete VirtualServer + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &VirtualServerConfiguration{ + VirtualServer: updatedHostVS, + }, + }, + } + + changes, problems = configuration.DeleteVirtualServer("default/virtualserver") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +// WIP - Jakub +// TODO: vsr route selector test +func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { + configuration := createTestConfiguration() + + // Add VirtualServerRoute-1 + + vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first", nil) + var expectedChanges []ResourceChange + expectedProblems := []ConfigurationProblem{ + { + Object: vsr1, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Add VirtualServer + + vs := createTestVirtualServerWithRoutes( + "virtualserver", + "foo.example.com", + []conf_v1.Route{ + { + Path: "/first", + Route: "virtualserverroute-1", + }, + { + Path: "/second", + Route: "virtualserverroute-2", + }, + { + Path: "/", + RouteSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "route"}}, + }, + }) + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-2 doesn't exist or invalid"}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServer(vs) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Add VirtualServerRoute-2 + + vsr2 := createTestVirtualServerRoute("virtualserverroute-2", "foo.example.com", "/second", nil) + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr2) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Update VirtualServerRoute-1 + + updatedVSR1 := vsr1.DeepCopy() + updatedVSR1.Generation++ + updatedVSR1.Spec.Subroutes[0].LocationSnippets = "# snippet" + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{updatedVSR1, vsr2}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(updatedVSR1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Add VirtualServerRoute-3 and VirtualServerRoute-4 with selectors + + vsr3 := createTestVirtualServerRoute("virtualserverroute-3", "foo.example.com", "/third", map[string]string{"app": "route"}) + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{updatedVSR1, vsr2, vsr3}, + VirtualServerRouteSelectors: map[string][]string{"app=route": {"default/virtualserverroute-3"}}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr3) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Make VirtualServerRoute-1 invalid + + invalidVSR1 := updatedVSR1.DeepCopy() + invalidVSR1.Generation++ + invalidVSR1.Spec.Host = "" + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2, vsr3}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"}, + }, + }, + } + expectedProblems = []ConfigurationProblem{ + { + Object: invalidVSR1, + IsError: true, + Reason: "Rejected", + Message: "VirtualServerRoute default/virtualserverroute-1 was rejected with error: spec.host: Required value", + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(invalidVSR1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore VirtualServerRoute-1 + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2, vsr3}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Make VirtualServerRoute-1 invalid for VirtualServer + + invalidForVSVSR1 := vsr1.DeepCopy() + invalidForVSVSR1.Generation++ + invalidForVSVSR1.Spec.Subroutes[0].Path = "/" + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2, vsr3}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 is invalid: spec.subroutes[0]: Invalid value: \"/\": must start with '/first'"}, + }, + }, + } + expectedProblems = []ConfigurationProblem{ + { + Object: invalidForVSVSR1, + Reason: "Ignored", + Message: "VirtualServer default/virtualserver ignores VirtualServerRoute", + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(invalidForVSVSR1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore VirtualServerRoute-1 + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2, vsr3}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Update host of VirtualServerRoute-2 + + updatedVSR2 := vsr2.DeepCopy() + updatedVSR2.Generation++ + updatedVSR2.Spec.Host = "bar.example.com" + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr3}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-2 is invalid: spec.host: Invalid value: \"bar.example.com\": must be equal to 'foo.example.com'"}, + }, + }, + } + expectedProblems = []ConfigurationProblem{ + { + Object: updatedVSR2, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(updatedVSR2) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Update host of VirtualServer + + updatedVS := vs.DeepCopy() + updatedVS.Generation++ + updatedVS.Spec.Host = "bar.example.com" + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: updatedVS, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{updatedVSR2}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 is invalid: spec.host: Invalid value: \"foo.example.com\": must be equal to 'bar.example.com'"}, + }, + }, + } + expectedProblems = []ConfigurationProblem{ + { + Object: vsr1, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore host of VirtualServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr3}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-2 is invalid: spec.host: Invalid value: \"bar.example.com\": must be equal to 'foo.example.com'"}, + }, + }, + } + expectedProblems = []ConfigurationProblem{ + { + Object: updatedVSR2, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + changes, problems = configuration.AddOrUpdateVirtualServer(vs) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore host of VirtualServerRoute-2 + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2, vsr3}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr2) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Remove VirtualServerRoute-1 + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2, vsr3}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.DeleteVirtualServerRoute("default/virtualserverroute-1") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + + // Remove VirtualServer + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2}, + Warnings: []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"}, + }, + }, + } + expectedProblems = []ConfigurationProblem{ + { + Object: vsr2, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + changes, problems = configuration.DeleteVirtualServer("default/virtualserver") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Remove VirtualServerRoute-2 + + expectedChanges = nil + expectedProblems = nil + + changes, problems = configuration.DeleteVirtualServerRoute("default/virtualserverroute-2") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff) + } +} + +// WIP (Jakub) +// TODO: vsr route selector test +func TestIsEqualForVirtualServers(t *testing.T) { + t.Parallel() + vs := createTestVirtualServerWithRoutes( + "virtualserver", + "foo.example.com", + []conf_v1.Route{ + { + Path: "/", + Route: "virtualserverroute", + }, + }) + vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/", nil) + + vsWithUpdatedGen := vs.DeepCopy() + vsWithUpdatedGen.Generation++ + + vsrWithUpdatedGen := vsr.DeepCopy() + vsrWithUpdatedGen.Generation++ + + tests := []struct { + vsConfig1 *VirtualServerConfiguration + vsConfig2 *VirtualServerConfiguration + expected bool + msg string + }{ + { + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + expected: true, + msg: "equal virtual servers", + }, + { + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vsWithUpdatedGen, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + expected: false, + msg: "virtual servers with different generation", + }, + { + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{}, nil, []string{}), + expected: false, + msg: "virtual servers with different number of virtual server routes", + }, + { + vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, nil, []string{}), + vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsrWithUpdatedGen}, nil, []string{}), + expected: false, + msg: "virtual servers with virtual server routes with different generation", + }, + } + + for _, test := range tests { + result := test.vsConfig1.IsEqual(test.vsConfig2) + if result != test.expected { + t.Errorf("IsEqual() returned %v but expected %v for the case of %s", result, test.expected, test.msg) + } + } +} From 3f41c4f784cf2c8c55af37cd9ecececa4b053b02 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 14:58:53 +0000 Subject: [PATCH 07/24] WIP - create minimum example for VSR --- examples/custom-resources/basic-vsr/README.md | 91 +++++++++++++++++++ .../basic-vsr/cafe-secret.yaml | 8 ++ .../basic-vsr/cafe-virtual-server-2.yaml | 22 +++++ .../basic-vsr/cafe-virtual-server-orig.yaml | 22 +++++ .../basic-vsr/cafe-virtual-server.yaml | 22 +++++ examples/custom-resources/basic-vsr/cafe.yaml | 65 +++++++++++++ .../coffee-virtual-server-route.yaml | 15 +++ 7 files changed, 245 insertions(+) create mode 100644 examples/custom-resources/basic-vsr/README.md create mode 100644 examples/custom-resources/basic-vsr/cafe-secret.yaml create mode 100644 examples/custom-resources/basic-vsr/cafe-virtual-server-2.yaml create mode 100644 examples/custom-resources/basic-vsr/cafe-virtual-server-orig.yaml create mode 100644 examples/custom-resources/basic-vsr/cafe-virtual-server.yaml create mode 100644 examples/custom-resources/basic-vsr/cafe.yaml create mode 100644 examples/custom-resources/basic-vsr/coffee-virtual-server-route.yaml diff --git a/examples/custom-resources/basic-vsr/README.md b/examples/custom-resources/basic-vsr/README.md new file mode 100644 index 0000000000..19311afd44 --- /dev/null +++ b/examples/custom-resources/basic-vsr/README.md @@ -0,0 +1,91 @@ +# Basic Configuration + +In this example we configure load balancing with TLS termination for a simple web application using the +[VirtualServer](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) +resource. The application, called cafe, lets you get either tea via the tea service or coffee via the coffee service. +You indicate your drink preference with the URI of your HTTP request: URIs ending with `/tea` get you tea and URIs +ending with `/coffee` get you coffee. + +The example is similar to the [complete example](../../ingress-resources/complete-example/README.md). +However, instead of the Ingress resource, we use the VirtualServer. + +## Prerequisites + +1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/) + instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + + ```console + IC_IP=XXX.YYY.ZZZ.III + ``` + +1. Save the HTTPS port of the Ingress Controller into a shell variable: + + ```console + IC_HTTPS_PORT= + ``` + +## Step 1 - Deploy the Cafe Application + +Create the coffee and the tea deployments and services: + +```console +kubectl create -f cafe.yaml +``` + +## Step 2 - Configure Load Balancing and TLS Termination + +1. Create the secret with the TLS certificate and key: + + ```console + kubectl create -f cafe-secret.yaml + ``` + +2. Create the VirtualServer resource: + + ```console + kubectl create -f cafe-virtual-server.yaml + ``` + +## Step 3 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServer: + + ```console + kubectl describe virtualserver cafe + ``` + + ```text + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 7s nginx-ingress-controller Configuration for default/cafe was added or updated + ``` + +1. Access the application using curl. We'll use curl's `--insecure` option to turn off certificate verification of our + self-signed certificate and `--resolve` option to set the IP address and HTTPS port of the Ingress Controller to the + domain name of the cafe application: + + To get coffee: + + ```console + curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure + ``` + + ```text + Server address: 10.16.1.182:80 + Server name: coffee-7dbb5795f6-tnbtq + ... + ``` + + If your prefer tea: + + ```console + curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/tea --insecure + ``` + + ```text + Server address: 10.16.0.149:80 + Server name: tea-7d57856c44-zlftd + ... diff --git a/examples/custom-resources/basic-vsr/cafe-secret.yaml b/examples/custom-resources/basic-vsr/cafe-secret.yaml new file mode 100644 index 0000000000..8f9fd84855 --- /dev/null +++ b/examples/custom-resources/basic-vsr/cafe-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cafe-secret +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== diff --git a/examples/custom-resources/basic-vsr/cafe-virtual-server-2.yaml b/examples/custom-resources/basic-vsr/cafe-virtual-server-2.yaml new file mode 100644 index 0000000000..cd006a8d93 --- /dev/null +++ b/examples/custom-resources/basic-vsr/cafe-virtual-server-2.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + action: + pass: coffee diff --git a/examples/custom-resources/basic-vsr/cafe-virtual-server-orig.yaml b/examples/custom-resources/basic-vsr/cafe-virtual-server-orig.yaml new file mode 100644 index 0000000000..cd006a8d93 --- /dev/null +++ b/examples/custom-resources/basic-vsr/cafe-virtual-server-orig.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + action: + pass: coffee diff --git a/examples/custom-resources/basic-vsr/cafe-virtual-server.yaml b/examples/custom-resources/basic-vsr/cafe-virtual-server.yaml new file mode 100644 index 0000000000..cd006a8d93 --- /dev/null +++ b/examples/custom-resources/basic-vsr/cafe-virtual-server.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + action: + pass: coffee diff --git a/examples/custom-resources/basic-vsr/cafe.yaml b/examples/custom-resources/basic-vsr/cafe.yaml new file mode 100644 index 0000000000..f049e8bf29 --- /dev/null +++ b/examples/custom-resources/basic-vsr/cafe.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee +spec: + replicas: 2 + selector: + matchLabels: + app: coffee + template: + metadata: + labels: + app: coffee + spec: + containers: + - name: coffee + image: nginxdemos/nginx-hello:plain-text + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-svc +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: coffee +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea +spec: + replicas: 1 + selector: + matchLabels: + app: tea + template: + metadata: + labels: + app: tea + spec: + containers: + - name: tea + image: nginxdemos/nginx-hello:plain-text + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-svc +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: tea diff --git a/examples/custom-resources/basic-vsr/coffee-virtual-server-route.yaml b/examples/custom-resources/basic-vsr/coffee-virtual-server-route.yaml new file mode 100644 index 0000000000..4f89de0779 --- /dev/null +++ b/examples/custom-resources/basic-vsr/coffee-virtual-server-route.yaml @@ -0,0 +1,15 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServerRoute +metadata: + name: coffee + namespace: coffee +spec: + host: cafe.example.com + upstreams: + - name: coffee + service: coffee-svc + port: 80 + subroutes: + - path: /coffee + action: + pass: coffee From 5306defdb917fb3685b5b866322f3914ae5d1bc6 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 15:21:15 +0000 Subject: [PATCH 08/24] WIP - keep VRS tests separate for now --- internal/k8s/configuration_vsr_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index d3d8132e16..da2032bf16 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -559,7 +559,7 @@ func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { // WIP (Jakub) // TODO: vsr route selector test -func TestIsEqualForVirtualServers(t *testing.T) { +func TestIsEqualForVirtualServersVSR(t *testing.T) { t.Parallel() vs := createTestVirtualServerWithRoutes( "virtualserver", From 8b2981d4b7e51b78d977c49c5e2ab5ff09ca9cfd Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 15:28:57 +0000 Subject: [PATCH 09/24] WIP - update codegen --- pkg/apis/configuration/v1/zz_generated.deepcopy.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/apis/configuration/v1/zz_generated.deepcopy.go b/pkg/apis/configuration/v1/zz_generated.deepcopy.go index b617f3cb93..a807d2b113 100644 --- a/pkg/apis/configuration/v1/zz_generated.deepcopy.go +++ b/pkg/apis/configuration/v1/zz_generated.deepcopy.go @@ -6,6 +6,7 @@ package v1 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -891,6 +892,11 @@ func (in *Route) DeepCopyInto(out *Route) { *out = make([]PolicyReference, len(*in)) copy(*out, *in) } + if in.RouteSelector != nil { + in, out := &in.RouteSelector, &out.RouteSelector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } if in.Action != nil { in, out := &in.Action, &out.Action *out = new(Action) From aa7e4170fffbceda525fbf648d13396c4ecb5dc0 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 15:33:57 +0000 Subject: [PATCH 10/24] WIP - fix linting issues --- internal/k8s/configuration.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/internal/k8s/configuration.go b/internal/k8s/configuration.go index 6a8b44c9ed..c3cdcb8632 100644 --- a/internal/k8s/configuration.go +++ b/internal/k8s/configuration.go @@ -274,7 +274,7 @@ func (vsc *VirtualServerConfiguration) IsEqual(resource Resource) bool { } } - //TODO: check all values of the map + // TODO: check all values of the map if len(vsc.VirtualServerRouteSelectors) != len(vsConfig.VirtualServerRouteSelectors) { return false } @@ -353,7 +353,7 @@ type TransportServerMetrics struct { // The IC needs to ensure that at any point in time the NGINX config on the filesystem reflects the state // of the objects in the Configuration. type Configuration struct { - //Context context.Context + // Context context.Context logger *slog.Logger @@ -399,7 +399,7 @@ type Configuration struct { // NewConfiguration creates a new Configuration. func NewConfiguration( - //ctx context.Context, + // ctx context.Context, logger *slog.Logger, hasCorrectIngressClass func(interface{}) bool, isPlus bool, @@ -414,10 +414,10 @@ func NewConfiguration( isCertManagerEnabled bool, isIPV6Disabled bool, ) *Configuration { - //l := nl.LoggerFromContext(ctx) + // l := nl.LoggerFromContext(ctx) return &Configuration{ - //Context: ctx, + // Context: ctx, logger: logger, hosts: make(map[string]Resource), listenerHosts: make(map[listenerHostKey]*TransportServerConfiguration), @@ -445,7 +445,7 @@ func NewConfiguration( snippetsEnabled: snippetsEnabled, isCertManagerEnabled: isCertManagerEnabled, isIPV6Disabled: isIPV6Disabled, - //logger: l, + // logger: l, } } @@ -1439,7 +1439,7 @@ func squashResourceChanges(changes []ResourceChange) []ResourceChange { } func (c *Configuration) buildHostsAndResources() (newHosts map[string]Resource, newResources map[string]Resource) { - //l := nl.LoggerFromContext(c.Context) + // l := nl.LoggerFromContext(c.Context) newHosts = make(map[string]Resource) newResources = make(map[string]Resource) var challengesVSR []*conf_v1.VirtualServerRoute @@ -1504,7 +1504,7 @@ func (c *Configuration) buildHostsAndResources() (newHosts map[string]Resource, resource := NewVirtualServerConfiguration(vs, vsrs, routeSelectorMap, warnings) // todo - uncomment (Jakub) - //nl.Infof(c.logger, "resource: %v", resource) + // nl.Infof(c.logger, "resource: %v", resource) c.buildListenersForVSConfiguration(resource) @@ -1659,7 +1659,7 @@ func (c *Configuration) buildMinionConfigs(masterHost string) ([]*MinionConfigur } func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]*conf_v1.VirtualServerRoute, map[string][]string, []string) { - //l := nl.LoggerFromContext(c.Context) + // l := nl.LoggerFromContext(c.Context) var vsrs []*conf_v1.VirtualServerRoute var routeSelectorMap map[string][]string var warnings []string @@ -1698,7 +1698,7 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* selectorKey := sel.String() - //routeSelectorMap[selectorKey] := []*conf_v1.VirtualServerRoute{} + // routeSelectorMap[selectorKey] := []*conf_v1.VirtualServerRoute{} if err != nil { warning := fmt.Sprintf("VirtualServerRoute LabelSelector %s is invalid: %v", selector, err) @@ -1727,7 +1727,6 @@ func (c *Configuration) buildVirtualServerRoutes(vs *conf_v1.VirtualServer) ([]* } } - } return vsrs, routeSelectorMap, warnings From bb37add9599e7aafe8522fc197c7d4536294d1be Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 17:05:50 +0000 Subject: [PATCH 11/24] WIP - test steps cleanup --- internal/k8s/configuration_test.go | 3 ++- internal/k8s/configuration_vsr_test.go | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/internal/k8s/configuration_test.go b/internal/k8s/configuration_test.go index 50ec07e63c..2f24aa8162 100644 --- a/internal/k8s/configuration_test.go +++ b/internal/k8s/configuration_test.go @@ -1189,7 +1189,8 @@ func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) { // Add VirtualServerRoute-1 - vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first", nil) + labels := make(map[string]string) + vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first", labels) var expectedChanges []ResourceChange expectedProblems := []ConfigurationProblem{ { diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index da2032bf16..fb44ad2e04 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -10,13 +10,8 @@ import ( // WIP - Jakub func TestAddVirtualServerVSR(t *testing.T) { - configuration := createTestConfiguration() - - // no problems are expected for all cases - var expectedProblems []ConfigurationProblem // Add a VirtualServer - vs := createTestVirtualServer("virtualserver", "foo.example.com") expectedChanges := []ResourceChange{ { @@ -27,13 +22,22 @@ func TestAddVirtualServerVSR(t *testing.T) { }, } + // ========= + // Note: call t.Fatal() as there is no point to carry on and update the VS if the VS is not created + // meaning we have errors or `problems` when creating the VS. + configuration := createTestConfiguration() + // no problems are expected for all cases + var expectedProblems []ConfigurationProblem + changes, problems := configuration.AddOrUpdateVirtualServer(vs) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + + if !cmp.Equal(expectedChanges, changes) { + t.Fatal(cmp.Diff(expectedChanges, changes)) } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + if !cmp.Equal(expectedProblems, problems) { + t.Fatal(cmp.Diff(expectedProblems, problems)) } + // ========= // Update VirtualServer From dde02376e5e61d0c95d415ced9c9c5031ac87397 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 17:26:02 +0000 Subject: [PATCH 12/24] WIP - make the test pass - TestAddVirtualServerVSR --- internal/k8s/configuration_vsr_test.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index fb44ad2e04..b4fa255058 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -37,7 +37,7 @@ func TestAddVirtualServerVSR(t *testing.T) { if !cmp.Equal(expectedProblems, problems) { t.Fatal(cmp.Diff(expectedProblems, problems)) } - // ========= + // ========= End Add VS ========= // Update VirtualServer @@ -55,12 +55,13 @@ func TestAddVirtualServerVSR(t *testing.T) { } changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + if !cmp.Equal(expectedChanges, changes) { + t.Fatal(cmp.Diff(expectedChanges, changes)) } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff) + if !cmp.Equal(expectedProblems, problems) { + t.Fatal(cmp.Diff(expectedProblems, problems)) } + // ========= End Update VS ========= // Make VirtualServer invalid @@ -147,6 +148,15 @@ func TestAddVirtualServerVSR(t *testing.T) { } } +// Test if correct changes and problems are reported +// if we try to update VS with invalid configuration. +// +// TODO: add workflow (Jakub) +func TestAddVirtualServer_InvalidVS(t *testing.T) { + t.Parallel() + +} + // WIP - Jakub // TODO: vsr route selector test func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { From 080af699cd2a3473d2ae35ac5b6ccf506e017e35 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 17:28:10 +0000 Subject: [PATCH 13/24] WIP - make the test pass - TestAddVirtualServerVSR --- internal/k8s/configuration_vsr_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index b4fa255058..1a72376dda 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -10,7 +10,6 @@ import ( // WIP - Jakub func TestAddVirtualServerVSR(t *testing.T) { - // Add a VirtualServer vs := createTestVirtualServer("virtualserver", "foo.example.com") expectedChanges := []ResourceChange{ @@ -154,7 +153,6 @@ func TestAddVirtualServerVSR(t *testing.T) { // TODO: add workflow (Jakub) func TestAddVirtualServer_InvalidVS(t *testing.T) { t.Parallel() - } // WIP - Jakub From b5f22b9110c4554c3b74c73802db03f226d895d3 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 17:34:10 +0000 Subject: [PATCH 14/24] WIP - fix return expression --- internal/k8s/configuration.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/k8s/configuration.go b/internal/k8s/configuration.go index c3cdcb8632..ef0122efcc 100644 --- a/internal/k8s/configuration.go +++ b/internal/k8s/configuration.go @@ -275,11 +275,7 @@ func (vsc *VirtualServerConfiguration) IsEqual(resource Resource) bool { } // TODO: check all values of the map - if len(vsc.VirtualServerRouteSelectors) != len(vsConfig.VirtualServerRouteSelectors) { - return false - } - - return true + return len(vsc.VirtualServerRouteSelectors) != len(vsConfig.VirtualServerRouteSelectors) } // TransportServerConfiguration holds a TransportServer resource. From ecd5c6208c093855d8c256c7880e62e05aa1c812 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 18 Nov 2024 17:44:42 +0000 Subject: [PATCH 15/24] WIP - reflect tested functionality in the test name --- internal/k8s/configuration_vsr_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index 1a72376dda..0ae445aed6 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -9,7 +9,7 @@ import ( ) // WIP - Jakub -func TestAddVirtualServerVSR(t *testing.T) { +func TestAddAndUpdateVirtualServer(t *testing.T) { // Add a VirtualServer vs := createTestVirtualServer("virtualserver", "foo.example.com") expectedChanges := []ResourceChange{ From eda974bc40ff2dcc70f6df70f998703cc30f37fa Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Tue, 19 Nov 2024 10:40:01 +0000 Subject: [PATCH 16/24] WIP - invalid case for adding VSR --- internal/k8s/configuration_vsr_test.go | 33 +++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index 0ae445aed6..b9b5c4313e 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -155,6 +155,35 @@ func TestAddVirtualServer_InvalidVS(t *testing.T) { t.Parallel() } +// Negative flow - User attempts to add VSR to not existing VS +func TestAttemptToAddVSRtoNotExistingVS_ReturnsProblems(t *testing.T) { + t.Parallel() + + configuration := createTestConfiguration() + + labels := make(map[string]string) + vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/first", labels) + + // Try to add VirtualServerRoute + + var expectedChanges []ResourceChange + expectedProblems := []ConfigurationProblem{ + { + Object: vsr, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr) + if !cmp.Equal(expectedChanges, changes) { + t.Error(cmp.Diff(expectedChanges, changes)) + } + if !cmp.Equal(expectedProblems, problems) { + t.Error(cmp.Diff(expectedProblems, problems)) + } +} + // WIP - Jakub // TODO: vsr route selector test func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { @@ -162,7 +191,9 @@ func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { // Add VirtualServerRoute-1 - vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first", nil) + labels := make(map[string]string) + vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first", labels) + var expectedChanges []ResourceChange expectedProblems := []ConfigurationProblem{ { From 83df813fb2df9bffffb894ff8832d129eb24fdf1 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Tue, 19 Nov 2024 10:46:46 +0000 Subject: [PATCH 17/24] WIP - compare status update, not the message --- internal/k8s/configuration_vsr_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index b9b5c4313e..2b9630d976 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -176,10 +177,10 @@ func TestAttemptToAddVSRtoNotExistingVS_ReturnsProblems(t *testing.T) { } changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr) - if !cmp.Equal(expectedChanges, changes) { + if !cmp.Equal(expectedChanges, changes, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { t.Error(cmp.Diff(expectedChanges, changes)) } - if !cmp.Equal(expectedProblems, problems) { + if !cmp.Equal(expectedProblems, problems, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { t.Error(cmp.Diff(expectedProblems, problems)) } } From 83f508d3dcc652fc0fe026062734be164ac50eea Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Tue, 19 Nov 2024 12:07:27 +0000 Subject: [PATCH 18/24] WIP - Add VSR to VS --- internal/k8s/configuration_vsr_test.go | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index 2b9630d976..a41b4e1e06 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -185,6 +185,87 @@ func TestAttemptToAddVSRtoNotExistingVS_ReturnsProblems(t *testing.T) { } } +// TestAddVSRtoVS logic: +// +// 1) Create VSR +// 2) Create VS +// 3) Add the VSR to the VS +func TestAddVSRtoVS(t *testing.T) { + t.Parallel() + + configuration := createTestConfiguration() + + // ========= + // Step 1 + // ========= + // Add VirtualServerRoute + // We add VSR that references not existing VS. Chance we expect to see `Problems` + + labels := make(map[string]string) + vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/first", labels) + + var expectedChanges []ResourceChange + expectedProblems := []ConfigurationProblem{ + { + Object: vsr, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + // adding VSR but no VS exist at this stage, chance we get problems + // if we don't get it right now we call t.Fatal as there is no + // point to continue test - preconditions are not setup correctly. + changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr) + if !cmp.Equal(expectedChanges, changes, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { + t.Fatal(cmp.Diff(expectedChanges, changes)) + } + if !cmp.Equal(expectedProblems, problems, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { + t.Fatal(cmp.Diff(expectedProblems, problems)) + } + + // ========= + // Step 2 + // ========= + // Create the missing VS with routes. + // Note that the 1st Route in the routes slice below matches the route from the previous step! + + routes := []conf_v1.Route{ + { + Path: "/first", + Route: "virtualserverroute", + }, + { + Path: "/", + RouteSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "route"}}, + }, + } + + vs := createTestVirtualServerWithRoutes("virtualserver", "foo.example.com", routes) + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr}, + }, + }, + } + expectedProblems = nil + + // We update configuration here - add the missing VS (see Step 1) + // At this point we have both VS and VSR in the configuration. They match. + changes, problems = configuration.AddOrUpdateVirtualServer(vs) + + if !cmp.Equal(expectedChanges, changes) { + t.Error(cmp.Diff(expectedChanges, changes)) + } + if !cmp.Equal(expectedProblems, problems) { + t.Error(cmp.Diff(expectedProblems, problems)) + } +} + // WIP - Jakub // TODO: vsr route selector test func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { From 38653efaed800e1e48e131c93276e2fe0cd14f76 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Tue, 19 Nov 2024 14:01:45 +0000 Subject: [PATCH 19/24] WIP - keep TS config tests tegether --- internal/k8s/configuration_test.go | 926 ++----------------------- internal/k8s/configuration_ts_test.go | 825 ++++++++++++++++++++++ internal/k8s/configuration_vsr_test.go | 5 + 3 files changed, 887 insertions(+), 869 deletions(-) create mode 100644 internal/k8s/configuration_ts_test.go diff --git a/internal/k8s/configuration_test.go b/internal/k8s/configuration_test.go index 2f24aa8162..a7a7ebc05f 100644 --- a/internal/k8s/configuration_test.go +++ b/internal/k8s/configuration_test.go @@ -1624,7 +1624,8 @@ func TestAddInvalidVirtualServerRoute(t *testing.T) { func TestAddVirtualServerWithIncorrectClass(t *testing.T) { configuration := createTestConfiguration() - vsr := createTestVirtualServerRoute("virtualserver", "foo.example.com", "/", nil) + labels := make(map[string]string) + vsr := createTestVirtualServerRoute("virtualserver", "foo.example.com", "/", labels) vsr.Spec.IngressClass = "someproxy" var expectedChanges []ResourceChange @@ -1821,627 +1822,90 @@ func TestHostCollisions(t *testing.T) { t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } - // Delete master Ingress - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &IngressConfiguration{ - Ingress: masterIng, - IsMaster: true, - ValidHosts: map[string]bool{"foo.example.com": true}, - ChildWarnings: map[string][]string{}, - }, - }, - { - Op: AddOrUpdate, - Resource: &IngressConfiguration{ - Ingress: regularIng, - ValidHosts: map[string]bool{"foo.example.com": true, "bar.example.com": true}, - ChildWarnings: map[string][]string{}, - }, - }, - } - expectedProblems = nil - - changes, problems = configuration.DeleteIngress("default/master-ingress") - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) - } - - // Delete regular Ingress - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &IngressConfiguration{ - Ingress: regularIng, - ValidHosts: map[string]bool{"foo.example.com": true, "bar.example.com": true}, - ChildWarnings: map[string][]string{}, - }, - }, - { - Op: AddOrUpdate, - Resource: &VirtualServerConfiguration{ - VirtualServer: vs, - }, - }, - } - expectedProblems = nil - - changes, problems = configuration.DeleteIngress("default/regular-ingress") - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) - } - - // Delete VirtualServer - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &VirtualServerConfiguration{ - VirtualServer: vs, - }, - }, - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 0, - TransportServer: ts, - }, - }, - } - expectedProblems = nil - - changes, problems = configuration.DeleteVirtualServer("default/virtualserver") - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestAddTransportServer(t *testing.T) { - configuration := createTestConfiguration() - - listeners := []conf_v1.Listener{ - { - Name: "tcp-7777", - Port: 7777, - Protocol: "TCP", - }, - } - - addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) - - ts := createTestTransportServer("transportserver", "tcp-7777", "TCP") - - // no problems are expected for all cases - var expectedProblems []ConfigurationProblem - var expectedChanges []ResourceChange - - // Add TransportServer - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: ts, - }, - }, - } - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Update TransportServer - - updatedTS := ts.DeepCopy() - updatedTS.Generation++ - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Make TransportServer invalid - - invalidTS := updatedTS.DeepCopy() - invalidTS.Generation++ - invalidTS.Spec.Upstreams = nil - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - Error: `spec.action.pass: Not found: "myapp"`, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(invalidTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Restore TransportServer - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Delete TransportServer - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - }, - } - - changes, problems = configuration.DeleteTransportServer("default/transportserver") - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestAddTransportServerWithHost(t *testing.T) { - configuration := createTestConfiguration() - - listeners := []conf_v1.Listener{ - { - Name: "tcp-7777", - Port: 7777, - Protocol: "TCP", - }, - } - - addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) - - secretName := "echo-secret" - - ts := createTestTransportServerWithHost("transportserver", "echo.example.com", "tcp-7777", secretName) - - // no problems are expected for all cases - var expectedProblems []ConfigurationProblem - var expectedChanges []ResourceChange - - // Add TransportServer - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: ts, - }, - }, - } - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Update TransportServer - - updatedTS := ts.DeepCopy() - updatedTS.Generation++ - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Make TransportServer invalid - - invalidTS := updatedTS.DeepCopy() - invalidTS.Generation++ - invalidTS.Spec.Upstreams = nil - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - Error: `spec.action.pass: Not found: "myapp"`, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(invalidTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Restore TransportServer - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Delete TransportServer - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: updatedTS, - }, - }, - } - - changes, problems = configuration.DeleteTransportServer("default/transportserver") - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestAddTransportServerForTLSPassthrough(t *testing.T) { - configuration := createTestConfiguration() - - ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com") - - // no problems are expected for all cases - var expectedProblems []ConfigurationProblem - - // Add TransportServer - - expectedChanges := []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 0, - TransportServer: ts, - }, - }, - } - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // DeleteTransportServer - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &TransportServerConfiguration{ - ListenerPort: 0, - TransportServer: ts, - }, - }, - } - - changes, problems = configuration.DeleteTransportServer("default/transportserver") - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestListenerFlip(t *testing.T) { - configuration := createTestConfiguration() - - listeners := []conf_v1.Listener{ - { - Name: "tcp-7777", - Port: 7777, - Protocol: "TCP", - }, - { - Name: "tcp-8888", - Port: 8888, - Protocol: "TCP", - }, - } - addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) - - ts := createTestTransportServer("transportserver", "tcp-7777", "TCP") - - // no problems are expected for all cases - var expectedProblems []ConfigurationProblem - var expectedChanges []ResourceChange - - // Add TransportServer - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: ts, - }, - }, - } - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Update TransportServer listener - - updatedListenerTS := ts.DeepCopy() - updatedListenerTS.Generation++ - updatedListenerTS.Spec.Listener.Name = "tcp-8888" - - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 8888, - TransportServer: updatedListenerTS, - }, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(updatedListenerTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Update TransportSever listener to TLS Passthrough - - updatedWithPassthroughTS := updatedListenerTS.DeepCopy() - updatedWithPassthroughTS.Generation++ - updatedWithPassthroughTS.Spec.Listener.Name = "tls-passthrough" - updatedWithPassthroughTS.Spec.Listener.Protocol = "TLS_PASSTHROUGH" - updatedWithPassthroughTS.Spec.Host = "example.com" - - expectedChanges = []ResourceChange{ - { - Op: Delete, - Resource: &TransportServerConfiguration{ - ListenerPort: 8888, - TransportServer: updatedListenerTS, - }, - }, - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 0, - TransportServer: updatedWithPassthroughTS, - }, - }, - } - - changes, problems = configuration.AddOrUpdateTransportServer(updatedWithPassthroughTS) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestAddInvalidTransportServer(t *testing.T) { - configuration := createTestConfiguration() - - ts := createTestTransportServer("transportserver", "", "TCP") - - expectedProblems := []ConfigurationProblem{ - { - Object: ts, - IsError: true, - Reason: "Rejected", - Message: "TransportServer default/transportserver was rejected with error: spec.listener.name: Required value", - }, - } - var expectedChanges []ResourceChange - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestAddTransportServerWithIncorrectClass(t *testing.T) { - configuration := createTestConfiguration() - - // Add TransportServer with incorrect class - - ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com") - ts.Spec.IngressClass = "someproxy" - - var expectedProblems []ConfigurationProblem - var expectedChanges []ResourceChange - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - - // Make the class correct - - updatedTS := ts.DeepCopy() - updatedTS.Generation++ - updatedTS.Spec.IngressClass = "nginx" + // Delete master Ingress expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &IngressConfiguration{ + Ingress: masterIng, + IsMaster: true, + ValidHosts: map[string]bool{"foo.example.com": true}, + ChildWarnings: map[string][]string{}, + }, + }, { Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - TransportServer: updatedTS, + Resource: &IngressConfiguration{ + Ingress: regularIng, + ValidHosts: map[string]bool{"foo.example.com": true, "bar.example.com": true}, + ChildWarnings: map[string][]string{}, }, }, } expectedProblems = nil - changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) + changes, problems = configuration.DeleteIngress("default/master-ingress") if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } - // Make the class incorrect + // Delete regular Ingress expectedChanges = []ResourceChange{ { Op: Delete, - Resource: &TransportServerConfiguration{ - TransportServer: updatedTS, + Resource: &IngressConfiguration{ + Ingress: regularIng, + ValidHosts: map[string]bool{"foo.example.com": true, "bar.example.com": true}, + ChildWarnings: map[string][]string{}, + }, + }, + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, }, }, } expectedProblems = nil - changes, problems = configuration.AddOrUpdateTransportServer(ts) + changes, problems = configuration.DeleteIngress("default/regular-ingress") if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } -} - -func TestAddTransportServerWithNonExistingListener(t *testing.T) { - configuration := createTestConfiguration() - - addOrUpdateGlobalConfiguration(t, configuration, []conf_v1.Listener{}, noChanges, noProblems) - ts := createTestTransportServer("transportserver", "tcp-7777", "TCP") + // Delete VirtualServer - expectedProblems := []ConfigurationProblem{ + expectedChanges = []ResourceChange{ { - Object: ts, - IsError: false, - Reason: "Rejected", - Message: `Listener tcp-7777 doesn't exist`, + Op: Delete, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + }, + }, + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 0, + TransportServer: ts, + }, }, } - var expectedChanges []ResourceChange - - changes, problems := configuration.AddOrUpdateTransportServer(ts) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) - } -} - -func TestDeleteNonExistingTransportServer(t *testing.T) { - configuration := createTestConfiguration() - - var expectedChanges []ResourceChange - var expectedProblems []ConfigurationProblem + expectedProblems = nil - changes, problems := configuration.DeleteTransportServer("default/transportserver") + changes, problems = configuration.DeleteVirtualServer("default/virtualserver") if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff) } } @@ -3593,7 +3057,9 @@ func TestChallengeIngressNoVSR(t *testing.T) { vs := createTestVirtualServer("virtualserver", "bar.example.com") ing := createTestChallengeIngress("challenge", "foo.example.com", "/.well-known/acme-challenge/test", "cm-acme-http-solver-test") - configuration.AddOrUpdateVirtualServer(vs) + + changes, problems := configuration.AddOrUpdateVirtualServer(vs) + expectedChanges := []ResourceChange{ { Op: AddOrUpdate, @@ -3607,12 +3073,13 @@ func TestChallengeIngressNoVSR(t *testing.T) { }, } - changes, problems := configuration.AddOrUpdateIngress(ing) + changes, problems = configuration.AddOrUpdateIngress(ing) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("AddOrUpdateIngress() returned unexpected changes (-want +got):\n%s", diff) } if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff) + t.Errorf("AddOrUpdateIngress() returned unexpected problems (-want +got):\n%s", diff) } } @@ -3844,48 +3311,6 @@ func createTestChallengeVirtualServerRoute(name string, host string, path string } } -func createTestTransportServer(name string, listenerName string, listenerProtocol string) *conf_v1.TransportServer { - return &conf_v1.TransportServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: "default", - CreationTimestamp: metav1.Now(), - Generation: 1, - }, - Spec: conf_v1.TransportServerSpec{ - Listener: conf_v1.TransportServerListener{ - Name: listenerName, - Protocol: listenerProtocol, - }, - Upstreams: []conf_v1.TransportServerUpstream{ - { - Name: "myapp", - Service: "myapp-svc", - Port: 1234, - }, - }, - Action: &conf_v1.TransportServerAction{ - Pass: "myapp", - }, - }, - } -} - -func createTestTransportServerWithHost(name string, host string, listenerName string, secretName string) *conf_v1.TransportServer { - ts := createTestTransportServer(name, listenerName, "TCP") - ts.Spec.Host = host - ts.Spec.TLS = &conf_v1.TransportServerTLS{Secret: secretName} - - return ts -} - -func createTestTLSPassthroughTransportServer(name string, host string) *conf_v1.TransportServer { - ts := createTestTransportServer(name, conf_v1.TLSPassthroughListenerName, conf_v1.TLSPassthroughListenerProtocol) - ts.Spec.Host = host - - return ts -} - func createTestGlobalConfiguration(listeners []conf_v1.Listener) *conf_v1.GlobalConfiguration { return &conf_v1.GlobalConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -4331,105 +3756,6 @@ func TestGetResources(t *testing.T) { } } -func TestGetTransportServerMetrics(t *testing.T) { - t.Parallel() - tsPass := createTestTLSPassthroughTransportServer("transportserver", "abc.example.com") - tsTCP := createTestTransportServer("transportserver-tcp", "tcp-7777", "TCP") - tsUDP := createTestTransportServer("transportserver-udp", "udp-7777", "UDP") - - tests := []struct { - tses []*conf_v1.TransportServer - expected *TransportServerMetrics - msg string - }{ - { - tses: nil, - expected: &TransportServerMetrics{ - TotalTLSPassthrough: 0, - TotalTCP: 0, - TotalUDP: 0, - }, - msg: "no TransportServers", - }, - { - tses: []*conf_v1.TransportServer{ - tsPass, - }, - expected: &TransportServerMetrics{ - TotalTLSPassthrough: 1, - TotalTCP: 0, - TotalUDP: 0, - }, - msg: "one TLSPassthrough TransportServer", - }, - { - tses: []*conf_v1.TransportServer{ - tsTCP, - }, - expected: &TransportServerMetrics{ - TotalTLSPassthrough: 0, - TotalTCP: 1, - TotalUDP: 0, - }, - msg: "one TCP TransportServer", - }, - { - tses: []*conf_v1.TransportServer{ - tsUDP, - }, - expected: &TransportServerMetrics{ - TotalTLSPassthrough: 0, - TotalTCP: 0, - TotalUDP: 1, - }, - msg: "one UDP TransportServer", - }, - { - tses: []*conf_v1.TransportServer{ - tsPass, tsTCP, tsUDP, - }, - expected: &TransportServerMetrics{ - TotalTLSPassthrough: 1, - TotalTCP: 1, - TotalUDP: 1, - }, - msg: "TLSPassthrough, TCP and UDP TransportServers", - }, - } - - listeners := []conf_v1.Listener{ - { - Name: "tcp-7777", - Port: 7777, - Protocol: "TCP", - }, - { - Name: "udp-7777", - Port: 7777, - Protocol: "UDP", - }, - } - gc := createTestGlobalConfiguration(listeners) - - for _, test := range tests { - configuration := createTestConfiguration() - - _, _, err := configuration.AddOrUpdateGlobalConfiguration(gc) - if err != nil { - t.Fatalf("AddOrUpdateGlobalConfiguration() returned unexpected error %v", err) - } - - for _, ts := range test.tses { - configuration.AddOrUpdateTransportServer(ts) - } - - result := configuration.GetTransportServerMetrics() - if diff := cmp.Diff(test.expected, result); diff != "" { - t.Errorf("GetTransportServerMetrics() returned unexpected result for the case of %s (-want +got):\n%s", test.msg, diff) - } - } -} - func TestIsEqualForIngressConfigurations(t *testing.T) { t.Parallel() regularIng := createTestIngress("regular-ingress", "foo.example.com") @@ -4786,141 +4112,3 @@ var ( }, } ) - -func TestTransportServerListenerHostCollisions(t *testing.T) { - configuration := createTestConfiguration() - - listeners := []conf_v1.Listener{ - { - Name: "tcp-7777", - Port: 7777, - Protocol: "TCP", - }, - { - Name: "tcp-8888", - Port: 8888, - Protocol: "TCP", - }, - } - - addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) - - // Create TransportServers with the same listener and host - ts1 := createTestTransportServerWithHost("ts1", "example.com", "tcp-7777", "secret1") - ts2 := createTestTransportServerWithHost("ts2", "example.com", "tcp-7777", "secret2") // same listener and host - ts3 := createTestTransportServerWithHost("ts3", "example.org", "tcp-7777", "secret3") // different host - ts4 := createTestTransportServer("ts4", "tcp-7777", "TCP") // No host same listener - ts5 := createTestTransportServer("ts5", "tcp-7777", "TCP") // same as ts4 to induce error with empty host twice - ts6 := createTestTransportServerWithHost("ts6", "example.com", "tcp-8888", "secret4") // different listener - - // Add ts1 to the configuration - expectedChanges := []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: ts1, - }, - }, - } - changes, problems := configuration.AddOrUpdateTransportServer(ts1) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts1) returned unexpected result (-want +got):\n%s", diff) - } - if len(problems) != 0 { - t.Errorf("AddOrUpdateTransportServer(ts1) returned problems %v", problems) - } - - // Try to add ts2, should be rejected due to conflict - changes, problems = configuration.AddOrUpdateTransportServer(ts2) - expectedChanges = nil // No changes expected - expectedProblems := []ConfigurationProblem{ - { - Object: ts2, - IsError: false, - Reason: "Rejected", - Message: "Listener tcp-7777 with host example.com is taken by another resource", - }, - } - - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts2) returned unexpected changes (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts2) returned unexpected problems (-want +got):\n%s", diff) - } - - // Add ts3 with a different host, should be accepted - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: ts3, - }, - }, - } - changes, problems = configuration.AddOrUpdateTransportServer(ts3) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts3) returned unexpected result (-want +got):\n%s", diff) - } - if len(problems) != 0 { - t.Errorf("AddOrUpdateTransportServer(ts3) returned problems %v", problems) - } - - // Add ts4 with no host, should be accepted - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 7777, - TransportServer: ts4, - }, - }, - } - changes, problems = configuration.AddOrUpdateTransportServer(ts4) - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts4) returned unexpected result (-want +got):\n%s", diff) - } - if len(problems) != 0 { - t.Errorf("AddOrUpdateTransportServer(ts4) returned problems %v", problems) - } - - // Try to add ts5 with no host, should be rejected due to conflict - changes, problems = configuration.AddOrUpdateTransportServer(ts5) - expectedChanges = nil - expectedProblems = []ConfigurationProblem{ - { - Object: ts5, - IsError: false, - Reason: "Rejected", - Message: "Listener tcp-7777 with host empty host is taken by another resource", - }, - } - - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts5) returned unexpected changes (-want +got):\n%s", diff) - } - if diff := cmp.Diff(expectedProblems, problems); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts5) returned unexpected problems (-want +got):\n%s", diff) - } - - // Try to add ts6 with different listener, but same domain as initial ts, should be fine as different listener - changes, problems = configuration.AddOrUpdateTransportServer(ts6) - expectedChanges = []ResourceChange{ - { - Op: AddOrUpdate, - Resource: &TransportServerConfiguration{ - ListenerPort: 8888, - TransportServer: ts6, - }, - }, - } - if diff := cmp.Diff(expectedChanges, changes); diff != "" { - t.Errorf("AddOrUpdateTransportServer(ts6) returned unexpected changes (-want +got):\n%s", diff) - } - - if len(problems) != 0 { - t.Errorf("AddOrUpdateTransportServer(ts6) returned problems %v", problems) - } -} diff --git a/internal/k8s/configuration_ts_test.go b/internal/k8s/configuration_ts_test.go new file mode 100644 index 0000000000..f168c87543 --- /dev/null +++ b/internal/k8s/configuration_ts_test.go @@ -0,0 +1,825 @@ +package k8s + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestAddTransportServer(t *testing.T) { + configuration := createTestConfiguration() + + listeners := []conf_v1.Listener{ + { + Name: "tcp-7777", + Port: 7777, + Protocol: "TCP", + }, + } + + addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) + + ts := createTestTransportServer("transportserver", "tcp-7777", "TCP") + + // no problems are expected for all cases + var expectedProblems []ConfigurationProblem + var expectedChanges []ResourceChange + + // Add TransportServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: ts, + }, + }, + } + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Update TransportServer + + updatedTS := ts.DeepCopy() + updatedTS.Generation++ + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Make TransportServer invalid + + invalidTS := updatedTS.DeepCopy() + invalidTS.Generation++ + invalidTS.Spec.Upstreams = nil + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + Error: `spec.action.pass: Not found: "myapp"`, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(invalidTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore TransportServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Delete TransportServer + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + }, + } + + changes, problems = configuration.DeleteTransportServer("default/transportserver") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestAddTransportServerWithHost(t *testing.T) { + configuration := createTestConfiguration() + + listeners := []conf_v1.Listener{ + { + Name: "tcp-7777", + Port: 7777, + Protocol: "TCP", + }, + } + + addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) + + secretName := "echo-secret" + + ts := createTestTransportServerWithHost("transportserver", "echo.example.com", "tcp-7777", secretName) + + // no problems are expected for all cases + var expectedProblems []ConfigurationProblem + var expectedChanges []ResourceChange + + // Add TransportServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: ts, + }, + }, + } + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Update TransportServer + + updatedTS := ts.DeepCopy() + updatedTS.Generation++ + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Make TransportServer invalid + + invalidTS := updatedTS.DeepCopy() + invalidTS.Generation++ + invalidTS.Spec.Upstreams = nil + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + Error: `spec.action.pass: Not found: "myapp"`, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(invalidTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Restore TransportServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Delete TransportServer + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: updatedTS, + }, + }, + } + + changes, problems = configuration.DeleteTransportServer("default/transportserver") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestAddTransportServerForTLSPassthrough(t *testing.T) { + configuration := createTestConfiguration() + + ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com") + + // no problems are expected for all cases + var expectedProblems []ConfigurationProblem + + // Add TransportServer + + expectedChanges := []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 0, + TransportServer: ts, + }, + }, + } + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // DeleteTransportServer + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + ListenerPort: 0, + TransportServer: ts, + }, + }, + } + + changes, problems = configuration.DeleteTransportServer("default/transportserver") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestListenerFlip(t *testing.T) { + configuration := createTestConfiguration() + + listeners := []conf_v1.Listener{ + { + Name: "tcp-7777", + Port: 7777, + Protocol: "TCP", + }, + { + Name: "tcp-8888", + Port: 8888, + Protocol: "TCP", + }, + } + addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) + + ts := createTestTransportServer("transportserver", "tcp-7777", "TCP") + + // no problems are expected for all cases + var expectedProblems []ConfigurationProblem + var expectedChanges []ResourceChange + + // Add TransportServer + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: ts, + }, + }, + } + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Update TransportServer listener + + updatedListenerTS := ts.DeepCopy() + updatedListenerTS.Generation++ + updatedListenerTS.Spec.Listener.Name = "tcp-8888" + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 8888, + TransportServer: updatedListenerTS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(updatedListenerTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Update TransportSever listener to TLS Passthrough + + updatedWithPassthroughTS := updatedListenerTS.DeepCopy() + updatedWithPassthroughTS.Generation++ + updatedWithPassthroughTS.Spec.Listener.Name = "tls-passthrough" + updatedWithPassthroughTS.Spec.Listener.Protocol = "TLS_PASSTHROUGH" + updatedWithPassthroughTS.Spec.Host = "example.com" + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + ListenerPort: 8888, + TransportServer: updatedListenerTS, + }, + }, + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 0, + TransportServer: updatedWithPassthroughTS, + }, + }, + } + + changes, problems = configuration.AddOrUpdateTransportServer(updatedWithPassthroughTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestAddInvalidTransportServer(t *testing.T) { + configuration := createTestConfiguration() + + ts := createTestTransportServer("transportserver", "", "TCP") + + expectedProblems := []ConfigurationProblem{ + { + Object: ts, + IsError: true, + Reason: "Rejected", + Message: "TransportServer default/transportserver was rejected with error: spec.listener.name: Required value", + }, + } + var expectedChanges []ResourceChange + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestAddTransportServerWithIncorrectClass(t *testing.T) { + configuration := createTestConfiguration() + + // Add TransportServer with incorrect class + + ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com") + ts.Spec.IngressClass = "someproxy" + + var expectedProblems []ConfigurationProblem + var expectedChanges []ResourceChange + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Make the class correct + + updatedTS := ts.DeepCopy() + updatedTS.Generation++ + updatedTS.Spec.IngressClass = "nginx" + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + TransportServer: updatedTS, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateTransportServer(updatedTS) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + + // Make the class incorrect + + expectedChanges = []ResourceChange{ + { + Op: Delete, + Resource: &TransportServerConfiguration{ + TransportServer: updatedTS, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestAddTransportServerWithNonExistingListener(t *testing.T) { + configuration := createTestConfiguration() + + addOrUpdateGlobalConfiguration(t, configuration, []conf_v1.Listener{}, noChanges, noProblems) + + ts := createTestTransportServer("transportserver", "tcp-7777", "TCP") + + expectedProblems := []ConfigurationProblem{ + { + Object: ts, + IsError: false, + Reason: "Rejected", + Message: `Listener tcp-7777 doesn't exist`, + }, + } + var expectedChanges []ResourceChange + + changes, problems := configuration.AddOrUpdateTransportServer(ts) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func TestDeleteNonExistingTransportServer(t *testing.T) { + configuration := createTestConfiguration() + + var expectedChanges []ResourceChange + var expectedProblems []ConfigurationProblem + + changes, problems := configuration.DeleteTransportServer("default/transportserver") + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff) + } +} + +func createTestTransportServer(name string, listenerName string, listenerProtocol string) *conf_v1.TransportServer { + return &conf_v1.TransportServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + CreationTimestamp: metav1.Now(), + Generation: 1, + }, + Spec: conf_v1.TransportServerSpec{ + Listener: conf_v1.TransportServerListener{ + Name: listenerName, + Protocol: listenerProtocol, + }, + Upstreams: []conf_v1.TransportServerUpstream{ + { + Name: "myapp", + Service: "myapp-svc", + Port: 1234, + }, + }, + Action: &conf_v1.TransportServerAction{ + Pass: "myapp", + }, + }, + } +} + +func createTestTransportServerWithHost(name string, host string, listenerName string, secretName string) *conf_v1.TransportServer { + ts := createTestTransportServer(name, listenerName, "TCP") + ts.Spec.Host = host + ts.Spec.TLS = &conf_v1.TransportServerTLS{Secret: secretName} + + return ts +} + +func createTestTLSPassthroughTransportServer(name string, host string) *conf_v1.TransportServer { + ts := createTestTransportServer(name, conf_v1.TLSPassthroughListenerName, conf_v1.TLSPassthroughListenerProtocol) + ts.Spec.Host = host + + return ts +} + +func TestGetTransportServerMetrics(t *testing.T) { + t.Parallel() + tsPass := createTestTLSPassthroughTransportServer("transportserver", "abc.example.com") + tsTCP := createTestTransportServer("transportserver-tcp", "tcp-7777", "TCP") + tsUDP := createTestTransportServer("transportserver-udp", "udp-7777", "UDP") + + tests := []struct { + tses []*conf_v1.TransportServer + expected *TransportServerMetrics + msg string + }{ + { + tses: nil, + expected: &TransportServerMetrics{ + TotalTLSPassthrough: 0, + TotalTCP: 0, + TotalUDP: 0, + }, + msg: "no TransportServers", + }, + { + tses: []*conf_v1.TransportServer{ + tsPass, + }, + expected: &TransportServerMetrics{ + TotalTLSPassthrough: 1, + TotalTCP: 0, + TotalUDP: 0, + }, + msg: "one TLSPassthrough TransportServer", + }, + { + tses: []*conf_v1.TransportServer{ + tsTCP, + }, + expected: &TransportServerMetrics{ + TotalTLSPassthrough: 0, + TotalTCP: 1, + TotalUDP: 0, + }, + msg: "one TCP TransportServer", + }, + { + tses: []*conf_v1.TransportServer{ + tsUDP, + }, + expected: &TransportServerMetrics{ + TotalTLSPassthrough: 0, + TotalTCP: 0, + TotalUDP: 1, + }, + msg: "one UDP TransportServer", + }, + { + tses: []*conf_v1.TransportServer{ + tsPass, tsTCP, tsUDP, + }, + expected: &TransportServerMetrics{ + TotalTLSPassthrough: 1, + TotalTCP: 1, + TotalUDP: 1, + }, + msg: "TLSPassthrough, TCP and UDP TransportServers", + }, + } + + listeners := []conf_v1.Listener{ + { + Name: "tcp-7777", + Port: 7777, + Protocol: "TCP", + }, + { + Name: "udp-7777", + Port: 7777, + Protocol: "UDP", + }, + } + gc := createTestGlobalConfiguration(listeners) + + for _, test := range tests { + configuration := createTestConfiguration() + + _, _, err := configuration.AddOrUpdateGlobalConfiguration(gc) + if err != nil { + t.Fatalf("AddOrUpdateGlobalConfiguration() returned unexpected error %v", err) + } + + for _, ts := range test.tses { + configuration.AddOrUpdateTransportServer(ts) + } + + result := configuration.GetTransportServerMetrics() + if diff := cmp.Diff(test.expected, result); diff != "" { + t.Errorf("GetTransportServerMetrics() returned unexpected result for the case of %s (-want +got):\n%s", test.msg, diff) + } + } +} + +func TestTransportServerListenerHostCollisions(t *testing.T) { + configuration := createTestConfiguration() + + listeners := []conf_v1.Listener{ + { + Name: "tcp-7777", + Port: 7777, + Protocol: "TCP", + }, + { + Name: "tcp-8888", + Port: 8888, + Protocol: "TCP", + }, + } + + addOrUpdateGlobalConfiguration(t, configuration, listeners, noChanges, noProblems) + + // Create TransportServers with the same listener and host + ts1 := createTestTransportServerWithHost("ts1", "example.com", "tcp-7777", "secret1") + ts2 := createTestTransportServerWithHost("ts2", "example.com", "tcp-7777", "secret2") // same listener and host + ts3 := createTestTransportServerWithHost("ts3", "example.org", "tcp-7777", "secret3") // different host + ts4 := createTestTransportServer("ts4", "tcp-7777", "TCP") // No host same listener + ts5 := createTestTransportServer("ts5", "tcp-7777", "TCP") // same as ts4 to induce error with empty host twice + ts6 := createTestTransportServerWithHost("ts6", "example.com", "tcp-8888", "secret4") // different listener + + // Add ts1 to the configuration + expectedChanges := []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: ts1, + }, + }, + } + changes, problems := configuration.AddOrUpdateTransportServer(ts1) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts1) returned unexpected result (-want +got):\n%s", diff) + } + if len(problems) != 0 { + t.Errorf("AddOrUpdateTransportServer(ts1) returned problems %v", problems) + } + + // Try to add ts2, should be rejected due to conflict + changes, problems = configuration.AddOrUpdateTransportServer(ts2) + expectedChanges = nil // No changes expected + expectedProblems := []ConfigurationProblem{ + { + Object: ts2, + IsError: false, + Reason: "Rejected", + Message: "Listener tcp-7777 with host example.com is taken by another resource", + }, + } + + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts2) returned unexpected changes (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts2) returned unexpected problems (-want +got):\n%s", diff) + } + + // Add ts3 with a different host, should be accepted + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: ts3, + }, + }, + } + changes, problems = configuration.AddOrUpdateTransportServer(ts3) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts3) returned unexpected result (-want +got):\n%s", diff) + } + if len(problems) != 0 { + t.Errorf("AddOrUpdateTransportServer(ts3) returned problems %v", problems) + } + + // Add ts4 with no host, should be accepted + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 7777, + TransportServer: ts4, + }, + }, + } + changes, problems = configuration.AddOrUpdateTransportServer(ts4) + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts4) returned unexpected result (-want +got):\n%s", diff) + } + if len(problems) != 0 { + t.Errorf("AddOrUpdateTransportServer(ts4) returned problems %v", problems) + } + + // Try to add ts5 with no host, should be rejected due to conflict + changes, problems = configuration.AddOrUpdateTransportServer(ts5) + expectedChanges = nil + expectedProblems = []ConfigurationProblem{ + { + Object: ts5, + IsError: false, + Reason: "Rejected", + Message: "Listener tcp-7777 with host empty host is taken by another resource", + }, + } + + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts5) returned unexpected changes (-want +got):\n%s", diff) + } + if diff := cmp.Diff(expectedProblems, problems); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts5) returned unexpected problems (-want +got):\n%s", diff) + } + + // Try to add ts6 with different listener, but same domain as initial ts, should be fine as different listener + changes, problems = configuration.AddOrUpdateTransportServer(ts6) + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &TransportServerConfiguration{ + ListenerPort: 8888, + TransportServer: ts6, + }, + }, + } + if diff := cmp.Diff(expectedChanges, changes); diff != "" { + t.Errorf("AddOrUpdateTransportServer(ts6) returned unexpected changes (-want +got):\n%s", diff) + } + + if len(problems) != 0 { + t.Errorf("AddOrUpdateTransportServer(ts6) returned problems %v", problems) + } +} diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index a41b4e1e06..dcff7c4a8c 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -266,6 +266,11 @@ func TestAddVSRtoVS(t *testing.T) { } } +func TestMatchVSwithVSRusingSelector(t *testing.T) { + t.Parallel() + +} + // WIP - Jakub // TODO: vsr route selector test func TestAddVirtualServerWithVirtualServerRoutesVSR(t *testing.T) { From 6387ed40eef7790dc03c6c9dbd1e8b9420a89253 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Wed, 20 Nov 2024 09:47:21 +0000 Subject: [PATCH 20/24] WIP - update test steps --- internal/k8s/configuration_vsr_test.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index dcff7c4a8c..6abd8a0abe 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -185,11 +185,7 @@ func TestAttemptToAddVSRtoNotExistingVS_ReturnsProblems(t *testing.T) { } } -// TestAddVSRtoVS logic: -// -// 1) Create VSR -// 2) Create VS -// 3) Add the VSR to the VS +// TestAddVSRtoVS validates that we can add VSR to VS func TestAddVSRtoVS(t *testing.T) { t.Parallel() @@ -213,9 +209,10 @@ func TestAddVSRtoVS(t *testing.T) { }, } - // adding VSR but no VS exist at this stage, chance we get problems + // adding VSR to the configuration; no VS exist at this stage, chance we get problems + // // if we don't get it right now we call t.Fatal as there is no - // point to continue test - preconditions are not setup correctly. + // point to continue the test - preconditions are not setup correctly. changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr) if !cmp.Equal(expectedChanges, changes, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { t.Fatal(cmp.Diff(expectedChanges, changes)) From 962781dd99ad9c926a2162679636a15ba95de033 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Wed, 20 Nov 2024 14:45:00 +0000 Subject: [PATCH 21/24] WIP - Add test for maching VSR with VS --- internal/k8s/configuration_vsr_test.go | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/internal/k8s/configuration_vsr_test.go b/internal/k8s/configuration_vsr_test.go index 6abd8a0abe..0ecf2b0948 100644 --- a/internal/k8s/configuration_vsr_test.go +++ b/internal/k8s/configuration_vsr_test.go @@ -186,6 +186,7 @@ func TestAttemptToAddVSRtoNotExistingVS_ReturnsProblems(t *testing.T) { } // TestAddVSRtoVS validates that we can add VSR to VS +// todo: describe conditions and reason why the sest func TestAddVSRtoVS(t *testing.T) { t.Parallel() @@ -266,6 +267,66 @@ func TestAddVSRtoVS(t *testing.T) { func TestMatchVSwithVSRusingSelector(t *testing.T) { t.Parallel() + configuration := createTestConfiguration() + + // Add VirtualServerRoute + + labels := make(map[string]string) + vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/first", labels) + + var expectedChanges []ResourceChange + expectedProblems := []ConfigurationProblem{ + { + Object: vsr, + Reason: "NoVirtualServerFound", + Message: "VirtualServer is invalid or doesn't exist", + }, + } + + // adding VSR to the configuration; no VS exist at this stage, chance we get problems + // + // if we don't get it right now we call t.Fatal as there is no + // point to continue the test - preconditions are not setup correctly. + changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr) + if !cmp.Equal(expectedChanges, changes, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { + t.Fatal(cmp.Diff(expectedChanges, changes)) + } + if !cmp.Equal(expectedProblems, problems, cmpopts.IgnoreFields(ConfigurationProblem{}, "Message")) { + t.Fatal(cmp.Diff(expectedProblems, problems)) + } + + // Add VS with VRS with the RouteSelector (LabelSelector) + routes := []conf_v1.Route{ + { + Path: "/first", + Route: "virtualserverroute", + }, + { + Path: "/", + RouteSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "route"}}, + }, + } + + vs := createTestVirtualServerWithRoutes("virtualserver", "foo.example.com", routes) + + expectedChanges = []ResourceChange{ + { + Op: AddOrUpdate, + Resource: &VirtualServerConfiguration{ + VirtualServer: vs, + VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr}, + }, + }, + } + expectedProblems = nil + + changes, problems = configuration.AddOrUpdateVirtualServer(vs) + if !cmp.Equal(expectedChanges, changes) { + t.Error(cmp.Diff(expectedChanges, changes)) + } + if !cmp.Equal(expectedProblems, problems) { + t.Error(cmp.Diff(expectedProblems, problems)) + } } // WIP - Jakub From d27d9b96b972cbbc748f4ad297bbd17682078b35 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 25 Nov 2024 10:57:56 +0000 Subject: [PATCH 22/24] WIP - simplify VSR/VS examples --- .../vsr-route-selector/README.md | 57 +++++------- .../vsr-route-selector/api-key-secret.yaml | 2 +- .../vsr-route-selector/cafe-secret.yaml | 2 +- .../cafe-virtual-server.yaml | 2 +- .../coffee-virtual-server-route.yaml | 2 +- .../vsr-route-selector/coffee.yaml | 4 +- .../vsr-route-selector/namespaces.yaml | 14 --- .../vsr-route-selector/rate-limit.yaml | 10 --- .../tea-virtual-server-route.yaml | 2 +- .../vsr-route-selector/tea.yaml | 4 +- .../README.md | 14 ++- .../cafe-secret.yaml | 0 .../cafe-virtual-server-2.yaml | 0 .../cafe-virtual-server-orig.yaml | 0 .../cafe-virtual-server.yaml | 0 .../cafe.yaml | 0 .../coffee-virtual-server-route.yaml | 0 .../vsr-selector-multinamespace/README.md | 89 +++++++++++++++++++ .../cafe-secret.yaml | 8 ++ .../cafe-virtual-server-2.yaml | 22 +++++ .../cafe-virtual-server-orig.yaml | 22 +++++ .../cafe-virtual-server.yaml | 22 +++++ .../vsr-selector-multinamespace/cafe.yaml | 65 ++++++++++++++ .../coffee-virtual-server-route.yaml | 15 ++++ 24 files changed, 278 insertions(+), 78 deletions(-) delete mode 100644 examples/custom-resources/vsr-route-selector/namespaces.yaml delete mode 100644 examples/custom-resources/vsr-route-selector/rate-limit.yaml rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/README.md (76%) rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/cafe-secret.yaml (100%) rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/cafe-virtual-server-2.yaml (100%) rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/cafe-virtual-server-orig.yaml (100%) rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/cafe-virtual-server.yaml (100%) rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/cafe.yaml (100%) rename examples/custom-resources/{basic-vsr => vsr-selector-basic}/coffee-virtual-server-route.yaml (100%) create mode 100644 examples/custom-resources/vsr-selector-multinamespace/README.md create mode 100644 examples/custom-resources/vsr-selector-multinamespace/cafe-secret.yaml create mode 100644 examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-2.yaml create mode 100644 examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-orig.yaml create mode 100644 examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server.yaml create mode 100644 examples/custom-resources/vsr-selector-multinamespace/cafe.yaml create mode 100644 examples/custom-resources/vsr-selector-multinamespace/coffee-virtual-server-route.yaml diff --git a/examples/custom-resources/vsr-route-selector/README.md b/examples/custom-resources/vsr-route-selector/README.md index 6e415205fc..53acfeb6af 100644 --- a/examples/custom-resources/vsr-route-selector/README.md +++ b/examples/custom-resources/vsr-route-selector/README.md @@ -1,20 +1,23 @@ -# Cross-Namespace Configuration +# Basic, single-namespace VirtualServerRoute Selector In this example we use the [VirtualServer and VirtualServerRoute](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) resources to configure load balancing for the modified cafe application from the [Basic Configuration](../basic-configuration/) example. We have put the load balancing configuration as well as the deployments -and services into multiple namespaces. Instead of one namespace, we now use three: `tea`, `coffee`, and `cafe`. +and services into one default namespace. -- In the tea namespace, we create the tea deployment, service, and the corresponding load-balancing configuration. -- In the coffee namespace, we create the coffee deployment, service, and the corresponding load-balancing configuration. -- In the cafe namespace, we create the cafe secret with the TLS certificate and key and the load-balancing configuration - for the cafe application. That configuration references the coffee and tea configurations. +- In the default namespace, we create the tea deployment, service, and the corresponding load-balancing configuration. +- In the same namespace, we create the cafe secret with the TLS certificate and key and the load-balancing configuration + for the cafe application. That configuration references the tea configuration. ## Prerequisites + +## Step 1 - Install NGINX Ingress COntroller + 1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/) instructions to deploy the Ingress Controller with custom resources enabled. + 1. Save the public IP address of the Ingress Controller into a shell variable: ```console @@ -27,13 +30,6 @@ and services into multiple namespaces. Instead of one namespace, we now use thre IC_HTTPS_PORT= ``` -## Step 1 - Create Namespaces - -Create the required tea, coffee, and cafe namespaces: - -```console -kubectl create -f namespaces.yaml -``` ## Step 2 - Deploy the Cafe Application @@ -43,7 +39,7 @@ kubectl create -f namespaces.yaml kubectl create -f tea.yaml ``` -1. Create the coffee deployment and service in the coffee namespace: +1. Create the coffee deployment and service in the default namespace: ```console kubectl create -f coffee.yaml @@ -51,25 +47,25 @@ kubectl create -f namespaces.yaml ## Step 3 - Configure Load Balancing and TLS Termination -1. Create the VirtualServerRoute resource for tea in the tea namespace: +1. Create the VirtualServerRoute resource for tea: ```console kubectl create -f tea-virtual-server-route.yaml ``` -1. Create the VirtualServerRoute resource for coffee in the coffee namespace: +1. Create the VirtualServerRoute resource for coffee: ```console kubectl create -f coffee-virtual-server-route.yaml ``` -1. Create the secret with the TLS certificate and key in the cafe namespace: +1. Create the secret with the TLS certificate and key: ```console kubectl create -f cafe-secret.yaml ``` -1. Create the VirtualServer resource for the cafe app in the cafe namespace: +1. Create the VirtualServer resource for the cafe app: ```console kubectl create -f cafe-virtual-server.yaml @@ -85,37 +81,23 @@ kubectl create -f namespaces.yaml ``` ```text - . . . - Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Warning NoVirtualServersFound 2m nginx-ingress-controller No VirtualServer references VirtualServerRoute tea/tea - Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for tea/tea was added or updated + WIP - add an example ``` ```console - kubectl describe virtualserverroute coffee -n coffee + kubectl describe virtualserverroute coffee ``` ```text - . . . - Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Warning NoVirtualServersFound 2m nginx-ingress-controller No VirtualServer references VirtualServerRoute coffee/coffee - Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for coffee/coffee was added or updated + WIP - add an example ``` ```console - kubectl describe virtualserver cafe -n cafe + kubectl describe virtualserver cafe ``` ```text - . . . - Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal AddedOrUpdated 1m nginx-ingress-controller Configuration for cafe/cafe was added or updated + WIP - add example ``` 1. Access the application using curl. We'll use curl's `--insecure` option to turn off certificate verification of our @@ -144,3 +126,4 @@ kubectl create -f namespaces.yaml Server address: 10.16.0.157:80 Server name: tea-7d57856c44-674b8 ... + diff --git a/examples/custom-resources/vsr-route-selector/api-key-secret.yaml b/examples/custom-resources/vsr-route-selector/api-key-secret.yaml index 7f3adb0711..1c7533769c 100644 --- a/examples/custom-resources/vsr-route-selector/api-key-secret.yaml +++ b/examples/custom-resources/vsr-route-selector/api-key-secret.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: name: api-key-client-secret - namespace: cafe + namespace: default type: nginx.org/apikey data: client1: cGFzc3dvcmQ= # password diff --git a/examples/custom-resources/vsr-route-selector/cafe-secret.yaml b/examples/custom-resources/vsr-route-selector/cafe-secret.yaml index 81da195ae4..5e6ba01bf6 100644 --- a/examples/custom-resources/vsr-route-selector/cafe-secret.yaml +++ b/examples/custom-resources/vsr-route-selector/cafe-secret.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: name: cafe-secret - namespace: cafe + namespace: default type: kubernetes.io/tls data: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K diff --git a/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml b/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml index d764ac5b35..69c2303f60 100644 --- a/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml +++ b/examples/custom-resources/vsr-route-selector/cafe-virtual-server.yaml @@ -2,7 +2,7 @@ apiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: cafe - namespace: cafe + namespace: default spec: host: cafe.example.com tls: diff --git a/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml b/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml index 7d1ac9bffb..1805aeeac3 100644 --- a/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml +++ b/examples/custom-resources/vsr-route-selector/coffee-virtual-server-route.yaml @@ -2,7 +2,7 @@ apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute metadata: name: coffee - namespace: coffee + namespace: default labels: app: cafe route: coffee diff --git a/examples/custom-resources/vsr-route-selector/coffee.yaml b/examples/custom-resources/vsr-route-selector/coffee.yaml index 1349f927ce..83ccd6d3c9 100644 --- a/examples/custom-resources/vsr-route-selector/coffee.yaml +++ b/examples/custom-resources/vsr-route-selector/coffee.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: coffee - namespace: coffee + namespace: default spec: replicas: 1 selector: @@ -23,7 +23,7 @@ apiVersion: v1 kind: Service metadata: name: coffee-svc - namespace: coffee + namespace: default spec: ports: - port: 80 diff --git a/examples/custom-resources/vsr-route-selector/namespaces.yaml b/examples/custom-resources/vsr-route-selector/namespaces.yaml deleted file mode 100644 index 6c47c83f34..0000000000 --- a/examples/custom-resources/vsr-route-selector/namespaces.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: cafe ---- -apiVersion: v1 -kind: Namespace -metadata: - name: tea ---- -apiVersion: v1 -kind: Namespace -metadata: - name: coffee diff --git a/examples/custom-resources/vsr-route-selector/rate-limit.yaml b/examples/custom-resources/vsr-route-selector/rate-limit.yaml deleted file mode 100644 index 7da2027130..0000000000 --- a/examples/custom-resources/vsr-route-selector/rate-limit.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: k8s.nginx.org/v1 -kind: Policy -metadata: - name: rate-limit-policy - namespace: coffee -spec: - rateLimit: - rate: 1r/s - key: ${binary_remote_addr} - zoneSize: 10M diff --git a/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml b/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml index b332c71fba..7d1043e868 100644 --- a/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml +++ b/examples/custom-resources/vsr-route-selector/tea-virtual-server-route.yaml @@ -2,7 +2,7 @@ apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute metadata: name: tea - namespace: tea + namespace: default labels: route: tea app: cafe diff --git a/examples/custom-resources/vsr-route-selector/tea.yaml b/examples/custom-resources/vsr-route-selector/tea.yaml index 615e2fd436..a4f98e1244 100644 --- a/examples/custom-resources/vsr-route-selector/tea.yaml +++ b/examples/custom-resources/vsr-route-selector/tea.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: tea - namespace: tea + namespace: default spec: replicas: 1 selector: @@ -23,7 +23,7 @@ apiVersion: v1 kind: Service metadata: name: tea-svc - namespace: tea + namespace: default spec: ports: - port: 80 diff --git a/examples/custom-resources/basic-vsr/README.md b/examples/custom-resources/vsr-selector-basic/README.md similarity index 76% rename from examples/custom-resources/basic-vsr/README.md rename to examples/custom-resources/vsr-selector-basic/README.md index 19311afd44..329e694960 100644 --- a/examples/custom-resources/basic-vsr/README.md +++ b/examples/custom-resources/vsr-selector-basic/README.md @@ -1,13 +1,11 @@ # Basic Configuration -In this example we configure load balancing with TLS termination for a simple web application using the -[VirtualServer](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) -resource. The application, called cafe, lets you get either tea via the tea service or coffee via the coffee service. -You indicate your drink preference with the URI of your HTTP request: URIs ending with `/tea` get you tea and URIs -ending with `/coffee` get you coffee. - -The example is similar to the [complete example](../../ingress-resources/complete-example/README.md). -However, instead of the Ingress resource, we use the VirtualServer. +In this example we use the [VirtualServer and +VirtualServerRoute](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) +resources to configure load balancing for the modified cafe application from the [Basic +Configuration](../basic-configuration/) example. We have put the load balancing configuration as well as the deployments +and services into one namespace. + ## Prerequisites diff --git a/examples/custom-resources/basic-vsr/cafe-secret.yaml b/examples/custom-resources/vsr-selector-basic/cafe-secret.yaml similarity index 100% rename from examples/custom-resources/basic-vsr/cafe-secret.yaml rename to examples/custom-resources/vsr-selector-basic/cafe-secret.yaml diff --git a/examples/custom-resources/basic-vsr/cafe-virtual-server-2.yaml b/examples/custom-resources/vsr-selector-basic/cafe-virtual-server-2.yaml similarity index 100% rename from examples/custom-resources/basic-vsr/cafe-virtual-server-2.yaml rename to examples/custom-resources/vsr-selector-basic/cafe-virtual-server-2.yaml diff --git a/examples/custom-resources/basic-vsr/cafe-virtual-server-orig.yaml b/examples/custom-resources/vsr-selector-basic/cafe-virtual-server-orig.yaml similarity index 100% rename from examples/custom-resources/basic-vsr/cafe-virtual-server-orig.yaml rename to examples/custom-resources/vsr-selector-basic/cafe-virtual-server-orig.yaml diff --git a/examples/custom-resources/basic-vsr/cafe-virtual-server.yaml b/examples/custom-resources/vsr-selector-basic/cafe-virtual-server.yaml similarity index 100% rename from examples/custom-resources/basic-vsr/cafe-virtual-server.yaml rename to examples/custom-resources/vsr-selector-basic/cafe-virtual-server.yaml diff --git a/examples/custom-resources/basic-vsr/cafe.yaml b/examples/custom-resources/vsr-selector-basic/cafe.yaml similarity index 100% rename from examples/custom-resources/basic-vsr/cafe.yaml rename to examples/custom-resources/vsr-selector-basic/cafe.yaml diff --git a/examples/custom-resources/basic-vsr/coffee-virtual-server-route.yaml b/examples/custom-resources/vsr-selector-basic/coffee-virtual-server-route.yaml similarity index 100% rename from examples/custom-resources/basic-vsr/coffee-virtual-server-route.yaml rename to examples/custom-resources/vsr-selector-basic/coffee-virtual-server-route.yaml diff --git a/examples/custom-resources/vsr-selector-multinamespace/README.md b/examples/custom-resources/vsr-selector-multinamespace/README.md new file mode 100644 index 0000000000..329e694960 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/README.md @@ -0,0 +1,89 @@ +# Basic Configuration + +In this example we use the [VirtualServer and +VirtualServerRoute](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) +resources to configure load balancing for the modified cafe application from the [Basic +Configuration](../basic-configuration/) example. We have put the load balancing configuration as well as the deployments +and services into one namespace. + + +## Prerequisites + +1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/) + instructions to deploy the Ingress Controller with custom resources enabled. +1. Save the public IP address of the Ingress Controller into a shell variable: + + ```console + IC_IP=XXX.YYY.ZZZ.III + ``` + +1. Save the HTTPS port of the Ingress Controller into a shell variable: + + ```console + IC_HTTPS_PORT= + ``` + +## Step 1 - Deploy the Cafe Application + +Create the coffee and the tea deployments and services: + +```console +kubectl create -f cafe.yaml +``` + +## Step 2 - Configure Load Balancing and TLS Termination + +1. Create the secret with the TLS certificate and key: + + ```console + kubectl create -f cafe-secret.yaml + ``` + +2. Create the VirtualServer resource: + + ```console + kubectl create -f cafe-virtual-server.yaml + ``` + +## Step 3 - Test the Configuration + +1. Check that the configuration has been successfully applied by inspecting the events of the VirtualServer: + + ```console + kubectl describe virtualserver cafe + ``` + + ```text + . . . + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal AddedOrUpdated 7s nginx-ingress-controller Configuration for default/cafe was added or updated + ``` + +1. Access the application using curl. We'll use curl's `--insecure` option to turn off certificate verification of our + self-signed certificate and `--resolve` option to set the IP address and HTTPS port of the Ingress Controller to the + domain name of the cafe application: + + To get coffee: + + ```console + curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/coffee --insecure + ``` + + ```text + Server address: 10.16.1.182:80 + Server name: coffee-7dbb5795f6-tnbtq + ... + ``` + + If your prefer tea: + + ```console + curl --resolve cafe.example.com:$IC_HTTPS_PORT:$IC_IP https://cafe.example.com:$IC_HTTPS_PORT/tea --insecure + ``` + + ```text + Server address: 10.16.0.149:80 + Server name: tea-7d57856c44-zlftd + ... diff --git a/examples/custom-resources/vsr-selector-multinamespace/cafe-secret.yaml b/examples/custom-resources/vsr-selector-multinamespace/cafe-secret.yaml new file mode 100644 index 0000000000..8f9fd84855 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/cafe-secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cafe-secret +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K + tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== diff --git a/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-2.yaml b/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-2.yaml new file mode 100644 index 0000000000..cd006a8d93 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-2.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + action: + pass: coffee diff --git a/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-orig.yaml b/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-orig.yaml new file mode 100644 index 0000000000..cd006a8d93 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server-orig.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + action: + pass: coffee diff --git a/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server.yaml b/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server.yaml new file mode 100644 index 0000000000..cd006a8d93 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/cafe-virtual-server.yaml @@ -0,0 +1,22 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServer +metadata: + name: cafe +spec: + host: cafe.example.com + tls: + secret: cafe-secret + upstreams: + - name: tea + service: tea-svc + port: 80 + - name: coffee + service: coffee-svc + port: 80 + routes: + - path: /tea + action: + pass: tea + - path: /coffee + action: + pass: coffee diff --git a/examples/custom-resources/vsr-selector-multinamespace/cafe.yaml b/examples/custom-resources/vsr-selector-multinamespace/cafe.yaml new file mode 100644 index 0000000000..f049e8bf29 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/cafe.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coffee +spec: + replicas: 2 + selector: + matchLabels: + app: coffee + template: + metadata: + labels: + app: coffee + spec: + containers: + - name: coffee + image: nginxdemos/nginx-hello:plain-text + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: coffee-svc +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: coffee +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tea +spec: + replicas: 1 + selector: + matchLabels: + app: tea + template: + metadata: + labels: + app: tea + spec: + containers: + - name: tea + image: nginxdemos/nginx-hello:plain-text + ports: + - containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + name: tea-svc +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: tea diff --git a/examples/custom-resources/vsr-selector-multinamespace/coffee-virtual-server-route.yaml b/examples/custom-resources/vsr-selector-multinamespace/coffee-virtual-server-route.yaml new file mode 100644 index 0000000000..4f89de0779 --- /dev/null +++ b/examples/custom-resources/vsr-selector-multinamespace/coffee-virtual-server-route.yaml @@ -0,0 +1,15 @@ +apiVersion: k8s.nginx.org/v1 +kind: VirtualServerRoute +metadata: + name: coffee + namespace: coffee +spec: + host: cafe.example.com + upstreams: + - name: coffee + service: coffee-svc + port: 80 + subroutes: + - path: /coffee + action: + pass: coffee From b6af91dc726797fa53786ef5c6a783c93fd9ffc5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:59:19 +0000 Subject: [PATCH 23/24] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/custom-resources/vsr-route-selector/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/custom-resources/vsr-route-selector/README.md b/examples/custom-resources/vsr-route-selector/README.md index 53acfeb6af..2e3dc59aa8 100644 --- a/examples/custom-resources/vsr-route-selector/README.md +++ b/examples/custom-resources/vsr-route-selector/README.md @@ -126,4 +126,3 @@ and services into one default namespace. Server address: 10.16.0.157:80 Server name: tea-7d57856c44-674b8 ... - From 74106dbcffb784f19905c2b84ad59d113621fe22 Mon Sep 17 00:00:00 2001 From: Jakub Jarosz Date: Mon, 25 Nov 2024 11:08:32 +0000 Subject: [PATCH 24/24] WIP - example VSR selector basic --- examples/custom-resources/vsr-selector-basic/README.md | 5 ++--- .../vsr-selector-basic/coffee-virtual-server-route.yaml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/custom-resources/vsr-selector-basic/README.md b/examples/custom-resources/vsr-selector-basic/README.md index 329e694960..3dc2c1b198 100644 --- a/examples/custom-resources/vsr-selector-basic/README.md +++ b/examples/custom-resources/vsr-selector-basic/README.md @@ -1,11 +1,10 @@ -# Basic Configuration +# Basic, Single-Namespace VirtualServeRoute Configuration In this example we use the [VirtualServer and VirtualServerRoute](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/) resources to configure load balancing for the modified cafe application from the [Basic Configuration](../basic-configuration/) example. We have put the load balancing configuration as well as the deployments -and services into one namespace. - +and services into one default namespace. ## Prerequisites diff --git a/examples/custom-resources/vsr-selector-basic/coffee-virtual-server-route.yaml b/examples/custom-resources/vsr-selector-basic/coffee-virtual-server-route.yaml index 4f89de0779..e5c25895e0 100644 --- a/examples/custom-resources/vsr-selector-basic/coffee-virtual-server-route.yaml +++ b/examples/custom-resources/vsr-selector-basic/coffee-virtual-server-route.yaml @@ -2,7 +2,7 @@ apiVersion: k8s.nginx.org/v1 kind: VirtualServerRoute metadata: name: coffee - namespace: coffee + namespace: default spec: host: cafe.example.com upstreams: