Ejemplo n.º 1
0
    def test_rapid_additions_and_deletions(self):
        namespace = 'watt-rapid'

        # Install Ambassador
        install_ambassador(namespace=namespace)

        # Set up our listener.
        self.create_listeners(namespace)

        # Install QOTM
        apply_kube_artifacts(namespace=namespace, artifacts=qotm_manifests)

        # Install QOTM Ambassador manifests
        self.apply_qotm_endpoint_manifests(namespace=namespace)

        # Now let's wait for ambassador and QOTM pods to become ready
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'service=ambassador', '-n', namespace
        ])
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'service=qotm', '-n', namespace
        ])

        # Assume we can reach Ambassador through telepresence
        qotm_host = "ambassador." + namespace
        qotm_url = f"http://{qotm_host}/qotm/"

        # Assert 200 OK at /qotm/ endpoint
        qotm_ready = False

        loop_limit = 60
        while not qotm_ready:
            assert loop_limit > 0, "QOTM is not ready yet, aborting..."
            try:
                qotm_http_code = get_code_with_retry(qotm_url)
                assert qotm_http_code == 200, f"Expected 200 OK, got {qotm_http_code}"
                print(f"{qotm_url} is ready")
                qotm_ready = True

            except Exception as e:
                print(f"Error: {e}")
                print(f"{qotm_url} not ready yet, trying again...")
                time.sleep(1)
                loop_limit -= 1

        # Try to mess up Ambassador by applying and deleting QOTM mapping over and over
        for i in range(10):
            self.delete_qotm_mapping(namespace=namespace)
            self.create_qotm_mapping(namespace=namespace)

        # Let's give Ambassador a few seconds to register the changes...
        time.sleep(5)

        # Assert 200 OK at /qotm/ endpoint
        qotm_http_code = get_code_with_retry(qotm_url)
        assert qotm_http_code == 200, f"Expected 200 OK, got {qotm_http_code}"
Ejemplo n.º 2
0
    def apply_qotm_endpoint_manifests(self, namespace):
        qotm_resolver = f"""
apiVersion: getambassador.io/v2
kind: KubernetesEndpointResolver
metadata:
  name: qotm-resolver
  namespace: {namespace}
"""

        apply_kube_artifacts(namespace=namespace, artifacts=qotm_resolver)
        self.create_qotm_mapping(namespace=namespace)
Ejemplo n.º 3
0
def create_qotm_mapping(namespace):
    qotm_mapping = f"""
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name:  qotm-mapping
  namespace: {namespace}
spec:
  prefix: /qotm/
  service: qotm
"""

    apply_kube_artifacts(namespace=namespace, artifacts=qotm_mapping)
Ejemplo n.º 4
0
def create_httpbin_mapping(namespace):
    httpbin_mapping = f"""
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name:  httpbin-mapping
  namespace: {namespace}
spec:
  prefix: /httpbin/
  rewrite: /
  service: httpbin
"""

    apply_kube_artifacts(namespace=namespace, artifacts=httpbin_mapping)
    def create_module(self, namespace):
        manifest = f"""
---
apiVersion: getambassador.io/v2
kind: Module
metadata:
  name: ambassador
spec:
  config:
    header_case_overrides:
    - X-HELLO
    - X-FOO-Bar
        """

        apply_kube_artifacts(namespace=namespace, artifacts=manifest)
Ejemplo n.º 6
0
def create_qotm_mapping(namespace):
    qotm_mapping = f"""
---
apiVersion: x.getambassador.io/v3alpha1
kind: AmbassadorMapping
metadata:
  name:  qotm-mapping
  namespace: {namespace}
spec:
  hostname: "*"
  prefix: /qotm/
  service: qotm
"""

    apply_kube_artifacts(namespace=namespace, artifacts=qotm_mapping)
def create_headerecho_mapping(namespace):
    headerecho_mapping = f"""
---
apiVersion: x.getambassador.io/v3alpha1
kind: AmbassadorMapping
metadata:
  name:  headerecho-mapping
  namespace: {namespace}
spec:
  hostname: "*"
  prefix: /headerecho/
  rewrite: /
  service: headerecho
"""

    apply_kube_artifacts(namespace=namespace, artifacts=headerecho_mapping)
    def create_listeners(self, namespace):
        manifest = f"""
---
apiVersion: x.getambassador.io/v3alpha1
kind: AmbassadorListener
metadata:
  name: listener-8080
spec:
  port: 8080
  protocol: HTTP
  securityModel: INSECURE
  hostBinding:
    namespace:
      from: SELF
"""

        apply_kube_artifacts(namespace=namespace, artifacts=manifest)
Ejemplo n.º 9
0
    def create_qotm_mapping(self, namespace):
        qotm_mapping = f"""
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name:  qotm-mapping
  namespace: {namespace}
spec:
  prefix: /qotm/
  service: qotm.{namespace}
  resolver: qotm-resolver
  load_balancer:
    policy: round_robin
        """

        apply_kube_artifacts(namespace=namespace, artifacts=qotm_mapping)
Ejemplo n.º 10
0
def install_ambassador(namespace, single_namespace=True, envs=None):
    """
    Install Ambassador into a given namespace. NOTE WELL that although there
    is a 'single_namespace' parameter, this function probably needs work to do
    the fully-correct thing with single_namespace False.

    :param namespace: namespace to install Ambassador in
    :param single_namespace: should we set AMBASSADOR_SINGLE_NAMESPACE? SEE NOTE ABOVE!
    :param envs: [
      {
        'name': 'ENV_NAME',
        'value': 'ENV_VALUE'
      },
      ...
      ...
    ]
    """

    if envs is None:
        envs = []

    found_single_namespace = False

    if single_namespace:
        for e in envs:
            if e['name'] == 'AMBASSADOR_SINGLE_NAMESPACE':
                e['value'] = 'true'
                found_single_namespace = True
                break

        if not found_single_namespace:
            envs.append({
                'name': 'AMBASSADOR_SINGLE_NAMESPACE',
                'value': 'true'
            })

    # Create namespace to install Ambassador
    create_namespace(namespace)

    # Create Ambassador CRDs
    apply_kube_artifacts(namespace=namespace, artifacts=load_manifest('crds'))

    # Proceed to install Ambassador now
    final_yaml = []

    serviceAccountExtra = ''
    if os.environ.get("DEV_USE_IMAGEPULLSECRET", False):
        serviceAccountExtra = """
imagePullSecrets:
- name: dev-image-pull-secret
"""

    rbac_manifest_name = 'rbac_namespace_scope' if single_namespace else 'rbac_cluster_scope'

    # Hackish fakes of actual KAT structures -- it's _far_ too much work to synthesize
    # actual KAT Nodes and Paths.
    fakeNode = namedtuple('fakeNode', ['namespace', 'path', 'ambassador_id'])
    fakePath = namedtuple('fakePath', ['k8s'])

    ambassador_yaml = list(
        yaml.safe_load_all(
            (load_manifest(rbac_manifest_name) + load_manifest('ambassador') +
             (CLEARTEXT_HOST_YAML % namespace)).format(
                 capabilities_block="",
                 envs="",
                 extra_ports="",
                 serviceAccountExtra=serviceAccountExtra,
                 image=os.environ["AMBASSADOR_DOCKER_IMAGE"],
                 self=fakeNode(namespace=namespace,
                               ambassador_id='default',
                               path=fakePath(k8s='ambassador')))))

    for manifest in ambassador_yaml:
        kind = manifest.get('kind', None)
        metadata = manifest.get('metadata', {})
        name = metadata.get('name', None)

        if (kind == "Pod") and (name == "ambassador"):
            # Force AMBASSADOR_ID to match ours.
            #
            # XXX This is not likely to work without single_namespace=True.
            for envvar in manifest['spec']['containers'][0]['env']:
                if envvar.get('name', '') == 'AMBASSADOR_ID':
                    envvar['value'] = 'default'

            # add new envs, if any
            manifest['spec']['containers'][0]['env'].extend(envs)

    apply_kube_artifacts(namespace=namespace,
                         artifacts=yaml.safe_dump_all(ambassador_yaml))
Ejemplo n.º 11
0
def create_namespace(namespace):
    apply_kube_artifacts(namespace=namespace,
                         artifacts=namespace_manifest(namespace))
    def test_header_case_overrides(self):
        # Is there any reason not to use the default namespace?
        namespace = 'header-case-overrides'

        # Install Ambassador
        install_ambassador(namespace=namespace)

        # Install httpbin
        apply_kube_artifacts(namespace=namespace, artifacts=httpbin_manifests)

        # Install headerecho
        apply_kube_artifacts(namespace=namespace,
                             artifacts=headerecho_manifests)

        # Install listeners.
        self.create_listeners(namespace)

        # Install module
        self.create_module(namespace)

        # Install httpbin mapping
        create_httpbin_mapping(namespace=namespace)

        # Install headerecho mapping
        create_headerecho_mapping(namespace=namespace)

        # Now let's wait for ambassador and httpbin pods to become ready
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'service=ambassador', '-n', namespace
        ])
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'service=httpbin', '-n', namespace
        ])

        # Assume we can reach Ambassador through telepresence
        ambassador_host = "ambassador." + namespace

        # Assert 200 OK at httpbin/status/200 endpoint
        ready = False
        httpbin_url = f'http://{ambassador_host}/httpbin/status/200'
        headerecho_url = f'http://{ambassador_host}/headerecho/'

        loop_limit = 10
        while not ready:
            assert loop_limit > 0, "httpbin is not ready yet, aborting..."
            try:
                print(f"trying {httpbin_url}...")
                resp = requests.get(httpbin_url, timeout=5)
                code = resp.status_code
                assert code == 200, f"Expected 200 OK, got {code}"
                resp.close()
                print(f"{httpbin_url} is ready")

                print(f"trying {headerecho_url}...")
                resp = requests.get(headerecho_url, timeout=5)
                code = resp.status_code
                assert code == 200, f"Expected 200 OK, got {code}"
                resp.close()
                print(f"{headerecho_url} is ready")

                ready = True

            except Exception as e:
                print(f"Error: {e}")
                print(f"{httpbin_url} not ready yet, trying again...")
                time.sleep(1)
                loop_limit -= 1

        assert ready

        httpbin_url = f'http://{ambassador_host}/httpbin/response-headers?x-Hello=1&X-foo-Bar=1&x-Lowercase1=1&x-lowercase2=1'
        resp = requests.get(httpbin_url, timeout=5)
        code = resp.status_code
        assert code == 200, f"Expected 200 OK, got {code}"

        # First, test that the response headers have the correct case.

        # Very important: this test relies on matching case sensitive header keys.
        # Fortunately it appears that we can convert resp.headers, a case insensitive
        # dictionary, into a list of case sensitive keys.
        keys = [h for h in resp.headers.keys()]
        for k in keys:
            print(f"header key: {k}")

        assert 'x-hello' not in keys
        assert 'X-HELLO' in keys
        assert 'x-foo-bar' not in keys
        assert 'X-FOO-Bar' in keys
        assert 'x-lowercase1' in keys
        assert 'x-Lowercase1' not in keys
        assert 'x-lowercase2' in keys
        resp.close()

        # Second, test that the request headers sent to the headerecho server
        # have the correct case.

        headers = {
            'x-Hello': '1',
            'X-foo-Bar': '1',
            'x-Lowercase1': '1',
            'x-lowercase2': '1'
        }
        resp = requests.get(headerecho_url, headers=headers, timeout=5)
        code = resp.status_code
        assert code == 200, f"Expected 200 OK, got {code}"

        response_obj = json.loads(resp.text)
        print(f"response_obj = {response_obj}")
        assert response_obj
        assert 'headers' in response_obj

        hdrs = response_obj['headers']
        assert 'x-hello' not in hdrs
        assert 'X-HELLO' in hdrs
        assert 'x-foo-bar' not in hdrs
        assert 'X-FOO-Bar' in hdrs
        assert 'x-lowercase1' in hdrs
        assert 'x-Lowercase1' not in hdrs
        assert 'x-lowercase2' in hdrs
Ejemplo n.º 13
0
    def test_knative(self):
        namespace = 'knative-testing'

        # Install Knative
        apply_kube_artifacts(namespace=None,
                             artifacts=load_manifest("knative_serving_crds"))
        apply_kube_artifacts(namespace='knative-serving',
                             artifacts=load_manifest("knative_serving_0.18.0"))
        run_and_assert([
            'kubectl', 'patch', 'configmap/config-network', '--type', 'merge',
            '--patch',
            r'{"data": {"ingress.class": "ambassador.ingress.networking.knative.dev"}}',
            '-n', 'knative-serving'
        ])

        # Wait for Knative to become ready
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'app=activator', '-n', 'knative-serving'
        ])
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'app=controller', '-n', 'knative-serving'
        ])
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'app=webhook', '-n', 'knative-serving'
        ])
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'app=autoscaler', '-n', 'knative-serving'
        ])

        # Install Ambassador
        install_ambassador(namespace=namespace,
                           envs=[{
                               'name': 'AMBASSADOR_KNATIVE_SUPPORT',
                               'value': 'true'
                           }])

        # Install QOTM
        apply_kube_artifacts(namespace=namespace, artifacts=qotm_manifests)
        create_qotm_mapping(namespace=namespace)

        # Now let's wait for ambassador and QOTM pods to become ready
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'service=ambassador', '-n', namespace
        ])
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready', 'pod',
            '-l', 'service=qotm', '-n', namespace
        ])

        # Create kservice
        apply_kube_artifacts(namespace=namespace,
                             artifacts=knative_service_example)

        # Assume we can reach Ambassador through telepresence
        qotm_host = "ambassador." + namespace

        # Assert 200 OK at /qotm/ endpoint
        qotm_url = f'http://{qotm_host}/qotm/'
        code = get_code_with_retry(qotm_url)
        assert code == 200, f"Expected 200 OK, got {code}"
        print(f"{qotm_url} is ready")

        # Assert 200 OK at / with Knative Host header and 404 with other/no header
        kservice_url = f'http://{qotm_host}/'

        code = get_code_with_retry(kservice_url)
        assert code == 404, f"Expected 404, got {code}"
        print(f"{kservice_url} returns 404 with no host")

        code = get_code_with_retry(kservice_url,
                                   headers={'Host': 'random.host.whatever'})
        assert code == 404, f"Expected 404, got {code}"
        print(f"{kservice_url} returns 404 with a random host")

        # Wait for kservice
        run_and_assert([
            'kubectl', 'wait', '--timeout=90s', '--for=condition=Ready',
            'ksvc', 'helloworld-go', '-n', namespace
        ])

        # kservice pod takes some time to spin up, so let's try a few times
        code = 000
        host = f'helloworld-go.{namespace}.example.com'
        for _ in range(5):
            code = get_code_with_retry(kservice_url, headers={'Host': host})
            if code == 200:
                break

        assert code == 200, f"Expected 200, got {code}"
        print(
            f"{kservice_url} returns 200 OK with host helloworld-go.{namespace}.example.com"
        )