...

Text file src/github.com/emissary-ingress/emissary/v3/python/tests/kat/t_loadbalancer.py

Documentation: github.com/emissary-ingress/emissary/v3/python/tests/kat

     1from typing import Dict, Generator, Tuple, Union
     2
     3import tests.integration.manifests as integration_manifests
     4from abstract_tests import HTTP, AmbassadorTest, Node, ServiceType
     5from kat.harness import Query
     6
     7LOADBALANCER_POD = """
     8---
     9apiVersion: v1
    10kind: Pod
    11metadata:
    12  name: {name}
    13  labels:
    14    backend: {backend}
    15    scope: AmbassadorTest
    16spec:
    17  containers:
    18  - name: backend
    19    image: {images[kat-server]}
    20    ports:
    21    - containerPort: 8080
    22    env:
    23    - name: BACKEND_8080
    24      value: {backend_env}
    25    - name: BACKEND_8443
    26      value: {backend_env}
    27"""
    28
    29
    30class LoadBalancerTest(AmbassadorTest):
    31    target: ServiceType
    32
    33    def init(self):
    34        self.target = HTTP()
    35
    36    def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
    37        yield self, self.format(
    38            """
    39---
    40apiVersion: getambassador.io/v3alpha1
    41kind: Mapping
    42name:  {self.name}-0
    43hostname: "*"
    44prefix: /{self.name}-0/
    45service: {self.target.path.fqdn}
    46---
    47apiVersion: getambassador.io/v3alpha1
    48kind: Mapping
    49name:  {self.name}-1
    50hostname: "*"
    51prefix: /{self.name}-1/
    52service: {self.target.path.fqdn}
    53resolver:  endpoint
    54load_balancer:
    55  policy: round_robin
    56---
    57apiVersion: getambassador.io/v3alpha1
    58kind: Mapping
    59name:  {self.name}-2
    60hostname: "*"
    61prefix: /{self.name}-2/
    62service: {self.target.path.fqdn}
    63resolver: endpoint
    64load_balancer:
    65  policy: ring_hash
    66  header: test-header
    67---
    68apiVersion: getambassador.io/v3alpha1
    69kind: Mapping
    70name:  {self.name}-3
    71hostname: "*"
    72prefix: /{self.name}-3/
    73service: {self.target.path.fqdn}
    74resolver: endpoint
    75load_balancer:
    76  policy: ring_hash
    77  source_ip: True
    78---
    79apiVersion: getambassador.io/v3alpha1
    80kind: Mapping
    81name:  {self.name}-4
    82hostname: "*"
    83prefix: /{self.name}-4/
    84service: {self.target.path.fqdn}
    85resolver: endpoint
    86load_balancer:
    87  policy: ring_hash
    88  cookie:
    89    name: test-cookie
    90---
    91apiVersion: getambassador.io/v3alpha1
    92kind: Mapping
    93name:  {self.name}-5
    94hostname: "*"
    95prefix: /{self.name}-5/
    96service: {self.target.path.fqdn}
    97resolver: endpoint
    98load_balancer:
    99  policy: ring_hash
   100  cookie:
   101    name: test-cookie
   102  header: test-header
   103  source_ip: True
   104---
   105apiVersion: getambassador.io/v3alpha1
   106kind: Mapping
   107name:  {self.name}-6
   108hostname: "*"
   109prefix: /{self.name}-6/
   110service: {self.target.path.fqdn}
   111resolver: endpoint
   112load_balancer:
   113  policy: round_robin
   114  cookie:
   115    name: test-cookie
   116---
   117apiVersion: getambassador.io/v3alpha1
   118kind: Mapping
   119name:  {self.name}-7
   120hostname: "*"
   121prefix: /{self.name}-7/
   122service: {self.target.path.fqdn}
   123resolver: endpoint
   124load_balancer:
   125  policy: rr
   126---
   127apiVersion: getambassador.io/v3alpha1
   128kind: Mapping
   129name:  {self.name}-8
   130hostname: "*"
   131prefix: /{self.name}-8/
   132service: {self.target.path.fqdn}
   133resolver: endpoint
   134load_balancer:
   135  policy: least_request
   136---
   137apiVersion: getambassador.io/v3alpha1
   138kind: Mapping
   139name:  {self.name}-9
   140hostname: "*"
   141prefix: /{self.name}-9/
   142service: {self.target.path.fqdn}
   143resolver: endpoint
   144load_balancer:
   145  policy: least_request
   146  cookie:
   147    name: test-cookie
   148"""
   149        )
   150
   151    def queries(self):
   152        yield Query(self.url(self.name + "-0/"))
   153        yield Query(self.url(self.name + "-1/"))
   154        yield Query(self.url(self.name + "-2/"))
   155        yield Query(self.url(self.name + "-3/"))
   156        yield Query(self.url(self.name + "-4/"))
   157        yield Query(self.url(self.name + "-5/"), expected=404)
   158        yield Query(self.url(self.name + "-6/"), expected=404)
   159        yield Query(self.url(self.name + "-7/"), expected=404)
   160        yield Query(self.url(self.name + "-8/"))
   161        yield Query(self.url(self.name + "-9/"), expected=404)
   162
   163
   164class GlobalLoadBalancing(AmbassadorTest):
   165    target: ServiceType
   166
   167    def init(self):
   168        self.target = HTTP()
   169
   170    def manifests(self) -> str:
   171        backend = self.name.lower() + "-backend"
   172        return (
   173            integration_manifests.format(
   174                LOADBALANCER_POD,
   175                name="{}-1".format(self.path.k8s),
   176                backend=backend,
   177                backend_env="{}-1".format(self.path.k8s),
   178            )
   179            + integration_manifests.format(
   180                LOADBALANCER_POD,
   181                name="{}-2".format(self.path.k8s),
   182                backend=backend,
   183                backend_env="{}-2".format(self.path.k8s),
   184            )
   185            + integration_manifests.format(
   186                LOADBALANCER_POD,
   187                name="{}-3".format(self.path.k8s),
   188                backend=backend,
   189                backend_env="{}-3".format(self.path.k8s),
   190            )
   191            + """
   192---
   193apiVersion: v1
   194kind: Service
   195metadata:
   196  labels:
   197    scope: AmbassadorTest
   198  name: globalloadbalancing-service
   199spec:
   200  ports:
   201  - name: http
   202    port: 80
   203    targetPort: 8080
   204  selector:
   205    backend: {backend}
   206""".format(
   207                backend=backend
   208            )
   209            + super().manifests()
   210        )
   211
   212    def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
   213        yield self, self.format(
   214            """
   215apiVersion: getambassador.io/v3alpha1
   216kind:  Module
   217name:  ambassador
   218config:
   219  resolver: endpoint
   220  load_balancer:
   221    policy: ring_hash
   222    header: LB-HEADER
   223---
   224apiVersion: getambassador.io/v3alpha1
   225kind: Mapping
   226name:  {self.name}-header
   227hostname: "*"
   228prefix: /{self.name}-header/
   229service: globalloadbalancing-service
   230load_balancer:
   231  policy: ring_hash
   232  cookie:
   233    name: lb-cookie
   234---
   235apiVersion: getambassador.io/v3alpha1
   236kind: Mapping
   237name:  {self.name}-generic
   238hostname: "*"
   239prefix: /{self.name}-generic/
   240service: globalloadbalancing-service
   241"""
   242        )
   243
   244    def queries(self):
   245        # generic header queries
   246        for i in range(50):
   247            yield Query(self.url(self.name) + "-header/")
   248
   249        # header queries
   250        for i in range(50):
   251            yield Query(self.url(self.name) + "-header/", headers={"LB-HEADER": "yes"})
   252
   253        # cookie queries
   254        for i in range(50):
   255            yield Query(
   256                self.url(self.name) + "-header/", cookies=[{"name": "lb-cookie", "value": "yes"}]
   257            )
   258
   259        # generic - generic queries
   260        for i in range(50):
   261            yield Query(self.url(self.name) + "-generic/")
   262
   263        # generic - header queries
   264        for i in range(50):
   265            yield Query(self.url(self.name) + "-generic/", headers={"LB-HEADER": "yes"})
   266
   267        # generic - cookie queries
   268        for i in range(50):
   269            yield Query(
   270                self.url(self.name) + "-generic/", cookies=[{"name": "lb-cookie", "value": "yes"}]
   271            )
   272
   273    def check(self):
   274        assert len(self.results) == 300
   275
   276        generic_queries = self.results[:50]
   277        header_queries = self.results[50:100]
   278        cookie_queries = self.results[100:150]
   279
   280        generic_generic_queries = self.results[150:200]
   281        generic_header_queries = self.results[200:250]
   282        generic_cookie_queries = self.results[250:300]
   283
   284        # generic header queries - no cookie, no header
   285        generic_dict: Dict[str, int] = {}
   286        for result in generic_queries:
   287            assert result.backend
   288            generic_dict[result.backend.name] = (
   289                generic_dict[result.backend.name] + 1 if result.backend.name in generic_dict else 1
   290            )
   291        assert len(generic_dict) == 3
   292
   293        # header queries - no cookie - no sticky expected
   294        header_dict: Dict[str, int] = {}
   295        for result in header_queries:
   296            assert result.backend
   297            header_dict[result.backend.name] = (
   298                header_dict[result.backend.name] + 1 if result.backend.name in header_dict else 1
   299            )
   300        assert len(header_dict) == 3
   301
   302        # cookie queries - no headers - sticky expected
   303        cookie_dict: Dict[str, int] = {}
   304        for result in cookie_queries:
   305            assert result.backend
   306            cookie_dict[result.backend.name] = (
   307                cookie_dict[result.backend.name] + 1 if result.backend.name in cookie_dict else 1
   308            )
   309        assert len(cookie_dict) == 1
   310
   311        # generic header queries - no cookie, no header
   312        generic_generic_dict: Dict[str, int] = {}
   313        for result in generic_generic_queries:
   314            assert result.backend
   315            generic_generic_dict[result.backend.name] = (
   316                generic_generic_dict[result.backend.name] + 1
   317                if result.backend.name in generic_generic_dict
   318                else 1
   319            )
   320        assert len(generic_generic_dict) == 3
   321
   322        # header queries - no cookie - sticky expected
   323        generic_header_dict: Dict[str, int] = {}
   324        for result in generic_header_queries:
   325            assert result.backend
   326            generic_header_dict[result.backend.name] = (
   327                generic_header_dict[result.backend.name] + 1
   328                if result.backend.name in generic_header_dict
   329                else 1
   330            )
   331        assert len(generic_header_dict) == 1
   332
   333        # cookie queries - no headers - no sticky expected
   334        generic_cookie_dict: Dict[str, int] = {}
   335        for result in generic_cookie_queries:
   336            assert result.backend
   337            generic_cookie_dict[result.backend.name] = (
   338                generic_cookie_dict[result.backend.name] + 1
   339                if result.backend.name in generic_cookie_dict
   340                else 1
   341            )
   342        assert len(generic_cookie_dict) == 3
   343
   344
   345class PerMappingLoadBalancing(AmbassadorTest):
   346    target: ServiceType
   347    policy: str
   348
   349    def init(self):
   350        self.target = HTTP()
   351
   352    def manifests(self) -> str:
   353        backend = self.name.lower() + "-backend"
   354        return (
   355            integration_manifests.format(
   356                LOADBALANCER_POD,
   357                name="{}-1".format(self.path.k8s),
   358                backend=backend,
   359                backend_env="{}-1".format(self.path.k8s),
   360            )
   361            + integration_manifests.format(
   362                LOADBALANCER_POD,
   363                name="{}-2".format(self.path.k8s),
   364                backend=backend,
   365                backend_env="{}-2".format(self.path.k8s),
   366            )
   367            + integration_manifests.format(
   368                LOADBALANCER_POD,
   369                name="{}-3".format(self.path.k8s),
   370                backend=backend,
   371                backend_env="{}-3".format(self.path.k8s),
   372            )
   373            + """
   374---
   375apiVersion: v1
   376kind: Service
   377metadata:
   378  labels:
   379    scope: AmbassadorTest
   380  name: permappingloadbalancing-service
   381spec:
   382  ports:
   383  - name: http
   384    port: 80
   385    targetPort: 8080
   386  selector:
   387    backend: {backend}
   388""".format(
   389                backend=backend
   390            )
   391            + super().manifests()
   392        )
   393
   394    def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
   395        for policy in ["ring_hash", "maglev"]:
   396            self.policy = policy
   397            yield self, self.format(
   398                """
   399---
   400apiVersion: getambassador.io/v3alpha1
   401kind: Mapping
   402name:  {self.name}-header-{self.policy}
   403hostname: "*"
   404prefix: /{self.name}-header-{self.policy}/
   405service: permappingloadbalancing-service
   406resolver: endpoint
   407load_balancer:
   408  policy: {self.policy}
   409  header: LB-HEADER
   410---
   411apiVersion: getambassador.io/v3alpha1
   412kind: Mapping
   413name:  {self.name}-sourceip-{self.policy}
   414hostname: "*"
   415prefix: /{self.name}-sourceip-{self.policy}/
   416service: permappingloadbalancing-service
   417resolver: endpoint
   418load_balancer:
   419  policy: {self.policy}
   420  source_ip: true
   421---
   422apiVersion: getambassador.io/v3alpha1
   423kind: Mapping
   424name:  {self.name}-cookie-{self.policy}
   425hostname: "*"
   426prefix: /{self.name}-cookie-{self.policy}/
   427service: permappingloadbalancing-service
   428resolver: endpoint
   429load_balancer:
   430  policy: {self.policy}
   431  cookie:
   432    name: lb-cookie
   433    ttl: 125s
   434    path: /foo
   435---
   436apiVersion: getambassador.io/v3alpha1
   437kind: Mapping
   438name:  {self.name}-cookie-no-ttl-{self.policy}
   439hostname: "*"
   440prefix: /{self.name}-cookie-no-ttl-{self.policy}/
   441service: permappingloadbalancing-service
   442resolver: endpoint
   443load_balancer:
   444  policy: {self.policy}
   445  cookie:
   446    name: lb-cookie
   447"""
   448            )
   449
   450    def queries(self):
   451        for policy in ["ring_hash", "maglev"]:
   452            # generic header queries
   453            for i in range(50):
   454                yield Query(self.url(self.name) + "-header-{}/".format(policy))
   455
   456            # header queries
   457            for i in range(50):
   458                yield Query(
   459                    self.url(self.name) + "-header-{}/".format(policy), headers={"LB-HEADER": "yes"}
   460                )
   461
   462            # source IP queries
   463            for i in range(50):
   464                yield Query(self.url(self.name) + "-sourceip-{}/".format(policy))
   465
   466            # generic cookie queries
   467            for i in range(50):
   468                yield Query(self.url(self.name) + "-cookie-{}/".format(policy))
   469
   470            # cookie queries
   471            for i in range(50):
   472                yield Query(
   473                    self.url(self.name) + "-cookie-{}/".format(policy),
   474                    cookies=[{"name": "lb-cookie", "value": "yes"}],
   475                )
   476
   477            # cookie no TTL queries
   478            for i in range(50):
   479                yield Query(
   480                    self.url(self.name) + "-cookie-no-ttl-{}/".format(policy),
   481                    cookies=[{"name": "lb-cookie", "value": "yes"}],
   482                )
   483
   484    def check(self):
   485        assert len(self.results) == 600
   486
   487        for i in [0, 300]:
   488            generic_header_queries = self.results[0 + i : 50 + i]
   489            header_queries = self.results[50 + i : 100 + i]
   490            source_ip_queries = self.results[100 + i : 150 + i]
   491            generic_cookie_queries = self.results[150 + i : 200 + i]
   492            cookie_queries = self.results[200 + i : 250 + i]
   493            cookie_no_ttl_queries = self.results[250 + i : 300 + i]
   494
   495            # generic header queries
   496            generic_header_dict: Dict[str, int] = {}
   497            for result in generic_header_queries:
   498                assert result.backend
   499                generic_header_dict[result.backend.name] = (
   500                    generic_header_dict[result.backend.name] + 1
   501                    if result.backend.name in generic_header_dict
   502                    else 1
   503                )
   504            assert len(generic_header_dict) == 3
   505
   506            # header queries
   507            header_dict: Dict[str, int] = {}
   508            for result in header_queries:
   509                assert result.backend
   510                header_dict[result.backend.name] = (
   511                    header_dict[result.backend.name] + 1
   512                    if result.backend.name in header_dict
   513                    else 1
   514                )
   515            assert len(header_dict) == 1
   516
   517            # source IP queries
   518            source_ip_dict: Dict[str, int] = {}
   519            for result in source_ip_queries:
   520                assert result.backend
   521                source_ip_dict[result.backend.name] = (
   522                    source_ip_dict[result.backend.name] + 1
   523                    if result.backend.name in source_ip_dict
   524                    else 1
   525                )
   526            assert len(source_ip_dict) == 1
   527            assert list(source_ip_dict.values())[0] == 50
   528
   529            # generic cookie queries - results must include Set-Cookie header
   530            generic_cookie_dict: Dict[str, int] = {}
   531            for result in generic_cookie_queries:
   532                assert "Set-Cookie" in result.headers
   533                assert len(result.headers["Set-Cookie"]) == 1
   534                assert "lb-cookie=" in result.headers["Set-Cookie"][0]
   535                assert "Max-Age=125" in result.headers["Set-Cookie"][0]
   536                assert "Path=/foo" in result.headers["Set-Cookie"][0]
   537
   538                assert result.backend
   539                generic_cookie_dict[result.backend.name] = (
   540                    generic_cookie_dict[result.backend.name] + 1
   541                    if result.backend.name in generic_cookie_dict
   542                    else 1
   543                )
   544            assert len(generic_cookie_dict) == 3
   545
   546            # cookie queries
   547            cookie_dict: Dict[str, int] = {}
   548            for result in cookie_queries:
   549                assert "Set-Cookie" not in result.headers
   550
   551                assert result.backend
   552                cookie_dict[result.backend.name] = (
   553                    cookie_dict[result.backend.name] + 1
   554                    if result.backend.name in cookie_dict
   555                    else 1
   556                )
   557            assert len(cookie_dict) == 1
   558
   559            # cookie no TTL queries
   560            cookie_no_ttl_dict: Dict[str, int] = {}
   561            for result in cookie_no_ttl_queries:
   562                assert "Set-Cookie" not in result.headers
   563
   564                assert result.backend
   565                cookie_no_ttl_dict[result.backend.name] = (
   566                    cookie_no_ttl_dict[result.backend.name] + 1
   567                    if result.backend.name in cookie_no_ttl_dict
   568                    else 1
   569                )
   570            assert len(cookie_no_ttl_dict) == 1

View as plain text