def test_rapid_additions_and_deletions(self): namespace = 'watt-rapid' # Install Ambassador install_ambassador(namespace=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 ]) # Let's port-forward ambassador service to talk to QOTM port_forward_port = 6000 port_forward_command = [ 'kubectl', 'port-forward', '--namespace', namespace, 'service/ambassador', f'{port_forward_port}:80' ] run_and_assert(port_forward_command, communicate=False) qotm_url = f'http://localhost:{port_forward_port}/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}"
def test_rapid_additions_and_deletions(self): namespace = 'watt-rapid' # Make sure telepresence is connected. Do this early on in the test to give the TP daemon plenty of # time to do its thing while we wait for other k8 resources to reconcile. run_with_retry(['telepresence', 'connect']) # Install Ambassador install_ambassador(namespace=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}"
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)
def create_headerecho_mapping(namespace): headerecho_mapping = f""" --- apiVersion: getambassador.io/v2 kind: Mapping metadata: name: headerecho-mapping namespace: {namespace} spec: prefix: /headerecho/ rewrite: / service: headerecho """ apply_kube_artifacts(namespace=namespace, artifacts=headerecho_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)
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)
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 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]) # Let's port-forward ambassador service to talk to Ambassador. # IMPORTANT: We _must_ choose a unique port_forward_port so long as test_watt.py, # test_knative.py, and others like it, run in the same environment as this test. # Otherwise we get port collisions and it's madness. port_forward_port = 6123 port_forward_command = ['kubectl', 'port-forward', '--namespace', namespace, 'service/ambassador', f'{port_forward_port}:80'] run_and_assert(port_forward_command, communicate=False) print("Waiting 5 seconds, just because...") time.sleep(2) # Assert 200 OK at httpbin/status/200 endpoint ready = False httpbin_url = f'http://localhost:{port_forward_port}/httpbin/status/200' headerecho_url = f'http://localhost:{port_forward_port}/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://localhost:{port_forward_port}/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
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) # Let's port-forward ambassador service to talk to QOTM port_forward_port = 7000 port_forward_command = [ 'kubectl', 'port-forward', '--namespace', namespace, 'service/ambassador', f'{port_forward_port}:80' ] run_and_assert(port_forward_command, communicate=False) # Assert 200 OK at /qotm/ endpoint qotm_url = f'http://localhost:{port_forward_port}/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://localhost:{port_forward_port}/' 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" )
def test_knative(self): namespace = 'knative-testing' # Make sure telepresence is connected. Do this early on in the test to give the TP daemon plenty of # time to do its thing while we wait for other k8 resources to reconcile. run_with_retry(['telepresence', 'connect']) # 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" )
def test_header_case_overrides(self): # Is there any reason not to use the default namespace? namespace = 'header-case-overrides' # Make sure telepresence is connected. Do this early on in the test to give the TP daemon plenty of # time to do its thing while we wait for other k8 resources to reconcile. run_with_retry(['telepresence', 'connect']) # 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 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