...

Text file src/github.com/datawire/ambassador/v2/python/tests/kat/t_loadbalancer.py

Documentation: github.com/datawire/ambassador/v2/python/tests/kat

     1from typing import 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 = {}
   286        for result in generic_queries:
   287            generic_dict[result.backend.name] = (
   288                generic_dict[result.backend.name] + 1 if result.backend.name in generic_dict else 1
   289            )
   290        assert len(generic_dict) == 3
   291
   292        # header queries - no cookie - no sticky expected
   293        header_dict = {}
   294        for result in header_queries:
   295            header_dict[result.backend.name] = (
   296                header_dict[result.backend.name] + 1 if result.backend.name in header_dict else 1
   297            )
   298        assert len(header_dict) == 3
   299
   300        # cookie queries - no headers - sticky expected
   301        cookie_dict = {}
   302        for result in cookie_queries:
   303            cookie_dict[result.backend.name] = (
   304                cookie_dict[result.backend.name] + 1 if result.backend.name in cookie_dict else 1
   305            )
   306        assert len(cookie_dict) == 1
   307
   308        # generic header queries - no cookie, no header
   309        generic_generic_dict = {}
   310        for result in generic_generic_queries:
   311            generic_generic_dict[result.backend.name] = (
   312                generic_generic_dict[result.backend.name] + 1
   313                if result.backend.name in generic_generic_dict
   314                else 1
   315            )
   316        assert len(generic_generic_dict) == 3
   317
   318        # header queries - no cookie - sticky expected
   319        generic_header_dict = {}
   320        for result in generic_header_queries:
   321            generic_header_dict[result.backend.name] = (
   322                generic_header_dict[result.backend.name] + 1
   323                if result.backend.name in generic_header_dict
   324                else 1
   325            )
   326        assert len(generic_header_dict) == 1
   327
   328        # cookie queries - no headers - no sticky expected
   329        generic_cookie_dict = {}
   330        for result in generic_cookie_queries:
   331            generic_cookie_dict[result.backend.name] = (
   332                generic_cookie_dict[result.backend.name] + 1
   333                if result.backend.name in generic_cookie_dict
   334                else 1
   335            )
   336        assert len(generic_cookie_dict) == 3
   337
   338
   339class PerMappingLoadBalancing(AmbassadorTest):
   340    target: ServiceType
   341    policy: str
   342
   343    def init(self):
   344        self.target = HTTP()
   345
   346    def manifests(self) -> str:
   347        backend = self.name.lower() + "-backend"
   348        return (
   349            integration_manifests.format(
   350                LOADBALANCER_POD,
   351                name="{}-1".format(self.path.k8s),
   352                backend=backend,
   353                backend_env="{}-1".format(self.path.k8s),
   354            )
   355            + integration_manifests.format(
   356                LOADBALANCER_POD,
   357                name="{}-2".format(self.path.k8s),
   358                backend=backend,
   359                backend_env="{}-2".format(self.path.k8s),
   360            )
   361            + integration_manifests.format(
   362                LOADBALANCER_POD,
   363                name="{}-3".format(self.path.k8s),
   364                backend=backend,
   365                backend_env="{}-3".format(self.path.k8s),
   366            )
   367            + """
   368---
   369apiVersion: v1
   370kind: Service
   371metadata:
   372  labels:
   373    scope: AmbassadorTest
   374  name: permappingloadbalancing-service
   375spec:
   376  ports:
   377  - name: http
   378    port: 80
   379    targetPort: 8080
   380  selector:
   381    backend: {backend}
   382""".format(
   383                backend=backend
   384            )
   385            + super().manifests()
   386        )
   387
   388    def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
   389        for policy in ["ring_hash", "maglev"]:
   390            self.policy = policy
   391            yield self, self.format(
   392                """
   393---
   394apiVersion: getambassador.io/v3alpha1
   395kind: Mapping
   396name:  {self.name}-header-{self.policy}
   397hostname: "*"
   398prefix: /{self.name}-header-{self.policy}/
   399service: permappingloadbalancing-service
   400resolver: endpoint
   401load_balancer:
   402  policy: {self.policy}
   403  header: LB-HEADER
   404---
   405apiVersion: getambassador.io/v3alpha1
   406kind: Mapping
   407name:  {self.name}-sourceip-{self.policy}
   408hostname: "*"
   409prefix: /{self.name}-sourceip-{self.policy}/
   410service: permappingloadbalancing-service
   411resolver: endpoint
   412load_balancer:
   413  policy: {self.policy}
   414  source_ip: true
   415---
   416apiVersion: getambassador.io/v3alpha1
   417kind: Mapping
   418name:  {self.name}-cookie-{self.policy}
   419hostname: "*"
   420prefix: /{self.name}-cookie-{self.policy}/
   421service: permappingloadbalancing-service
   422resolver: endpoint
   423load_balancer:
   424  policy: {self.policy}
   425  cookie:
   426    name: lb-cookie
   427    ttl: 125s
   428    path: /foo
   429---
   430apiVersion: getambassador.io/v3alpha1
   431kind: Mapping
   432name:  {self.name}-cookie-no-ttl-{self.policy}
   433hostname: "*"
   434prefix: /{self.name}-cookie-no-ttl-{self.policy}/
   435service: permappingloadbalancing-service
   436resolver: endpoint
   437load_balancer:
   438  policy: {self.policy}
   439  cookie:
   440    name: lb-cookie
   441"""
   442            )
   443
   444    def queries(self):
   445        for policy in ["ring_hash", "maglev"]:
   446            # generic header queries
   447            for i in range(50):
   448                yield Query(self.url(self.name) + "-header-{}/".format(policy))
   449
   450            # header queries
   451            for i in range(50):
   452                yield Query(
   453                    self.url(self.name) + "-header-{}/".format(policy), headers={"LB-HEADER": "yes"}
   454                )
   455
   456            # source IP queries
   457            for i in range(50):
   458                yield Query(self.url(self.name) + "-sourceip-{}/".format(policy))
   459
   460            # generic cookie queries
   461            for i in range(50):
   462                yield Query(self.url(self.name) + "-cookie-{}/".format(policy))
   463
   464            # cookie queries
   465            for i in range(50):
   466                yield Query(
   467                    self.url(self.name) + "-cookie-{}/".format(policy),
   468                    cookies=[{"name": "lb-cookie", "value": "yes"}],
   469                )
   470
   471            # cookie no TTL queries
   472            for i in range(50):
   473                yield Query(
   474                    self.url(self.name) + "-cookie-no-ttl-{}/".format(policy),
   475                    cookies=[{"name": "lb-cookie", "value": "yes"}],
   476                )
   477
   478    def check(self):
   479        assert len(self.results) == 600
   480
   481        for i in [0, 300]:
   482            generic_header_queries = self.results[0 + i : 50 + i]
   483            header_queries = self.results[50 + i : 100 + i]
   484            source_ip_queries = self.results[100 + i : 150 + i]
   485            generic_cookie_queries = self.results[150 + i : 200 + i]
   486            cookie_queries = self.results[200 + i : 250 + i]
   487            cookie_no_ttl_queries = self.results[250 + i : 300 + i]
   488
   489            # generic header queries
   490            generic_header_dict = {}
   491            for result in generic_header_queries:
   492                generic_header_dict[result.backend.name] = (
   493                    generic_header_dict[result.backend.name] + 1
   494                    if result.backend.name in generic_header_dict
   495                    else 1
   496                )
   497            assert len(generic_header_dict) == 3
   498
   499            # header queries
   500            header_dict = {}
   501            for result in header_queries:
   502                header_dict[result.backend.name] = (
   503                    header_dict[result.backend.name] + 1
   504                    if result.backend.name in header_dict
   505                    else 1
   506                )
   507            assert len(header_dict) == 1
   508
   509            # source IP queries
   510            source_ip_dict = {}
   511            for result in source_ip_queries:
   512                source_ip_dict[result.backend.name] = (
   513                    source_ip_dict[result.backend.name] + 1
   514                    if result.backend.name in source_ip_dict
   515                    else 1
   516                )
   517            assert len(source_ip_dict) == 1
   518            assert list(source_ip_dict.values())[0] == 50
   519
   520            # generic cookie queries - results must include Set-Cookie header
   521            generic_cookie_dict = {}
   522            for result in generic_cookie_queries:
   523                assert "Set-Cookie" in result.headers
   524                assert len(result.headers["Set-Cookie"]) == 1
   525                assert "lb-cookie=" in result.headers["Set-Cookie"][0]
   526                assert "Max-Age=125" in result.headers["Set-Cookie"][0]
   527                assert "Path=/foo" in result.headers["Set-Cookie"][0]
   528
   529                generic_cookie_dict[result.backend.name] = (
   530                    generic_cookie_dict[result.backend.name] + 1
   531                    if result.backend.name in generic_cookie_dict
   532                    else 1
   533                )
   534            assert len(generic_cookie_dict) == 3
   535
   536            # cookie queries
   537            cookie_dict = {}
   538            for result in cookie_queries:
   539                assert "Set-Cookie" not in result.headers
   540
   541                cookie_dict[result.backend.name] = (
   542                    cookie_dict[result.backend.name] + 1
   543                    if result.backend.name in cookie_dict
   544                    else 1
   545                )
   546            assert len(cookie_dict) == 1
   547
   548            # cookie no TTL queries
   549            cookie_no_ttl_dict = {}
   550            for result in cookie_no_ttl_queries:
   551                assert "Set-Cookie" not in result.headers
   552
   553                cookie_no_ttl_dict[result.backend.name] = (
   554                    cookie_no_ttl_dict[result.backend.name] + 1
   555                    if result.backend.name in cookie_no_ttl_dict
   556                    else 1
   557                )
   558            assert len(cookie_no_ttl_dict) == 1

View as plain text