1import os
2import socket
3import time
4from collections import namedtuple
5
6import requests
7import yaml
8
9import tests.integration.manifests as integration_manifests
10from tests.kubeutils import apply_kube_artifacts
11from tests.manifests import cleartext_host_manifest
12from tests.runutils import run_and_assert
13
14# Assume that both of these are on the PATH if not explicitly set
15KUBESTATUS_PATH = os.environ.get("KUBESTATUS_PATH", "kubestatus")
16
17
18def install_crds() -> None:
19 apply_kube_artifacts(
20 namespace="emissary-system", artifacts=integration_manifests.crd_manifests()
21 )
22
23
24def install_ambassador(namespace, single_namespace=True, envs=None, debug=None):
25 """
26 Install Ambassador into a given namespace. NOTE WELL that although there
27 is a 'single_namespace' parameter, this function probably needs work to do
28 the fully-correct thing with single_namespace False.
29
30 :param namespace: namespace to install Ambassador in
31 :param single_namespace: should we set AMBASSADOR_SINGLE_NAMESPACE? SEE NOTE ABOVE!
32 :param envs: [
33 {
34 'name': 'ENV_NAME',
35 'value': 'ENV_VALUE'
36 },
37 ...
38 ...
39 ]
40 """
41
42 if envs is None:
43 envs = []
44
45 if single_namespace:
46 update_envs(envs, "AMBASSADOR_SINGLE_NAMESPACE", "true")
47
48 if debug:
49 update_envs(envs, "AMBASSADOR_DEBUG", debug)
50
51 # Create namespace to install Ambassador
52 create_namespace(namespace)
53
54 # Create Ambassador CRDs
55 install_crds()
56
57 print("Wait for apiext to be running...")
58 run_and_assert(
59 [
60 "tools/bin/kubectl",
61 "wait",
62 "--timeout=90s",
63 "--for=condition=available",
64 "deploy",
65 "emissary-apiext",
66 "-n",
67 "emissary-system",
68 ]
69 )
70
71 # Proceed to install Ambassador now
72 final_yaml = []
73
74 rbac_manifest_name = "rbac_namespace_scope" if single_namespace else "rbac_cluster_scope"
75
76 # Hackish fakes of actual KAT structures -- it's _far_ too much work to synthesize
77 # actual KAT Nodes and Paths.
78 fakeNode = namedtuple("fakeNode", ["namespace", "path", "ambassador_id"])
79 fakePath = namedtuple("fakePath", ["k8s"])
80
81 ambassador_yaml = list(
82 yaml.safe_load_all(
83 integration_manifests.format(
84 "\n".join(
85 [
86 integration_manifests.load(rbac_manifest_name),
87 integration_manifests.load("ambassador"),
88 (cleartext_host_manifest % namespace),
89 ]
90 ),
91 capabilities_block="",
92 envs="",
93 extra_ports="",
94 self=fakeNode(
95 namespace=namespace, ambassador_id="default", path=fakePath(k8s="ambassador")
96 ),
97 )
98 )
99 )
100
101 for manifest in ambassador_yaml:
102 kind = manifest.get("kind", None)
103 metadata = manifest.get("metadata", {})
104 name = metadata.get("name", None)
105
106 if (kind == "Pod") and (name == "ambassador"):
107 # Force AMBASSADOR_ID to match ours.
108 #
109 # XXX This is not likely to work without single_namespace=True.
110 for envvar in manifest["spec"]["containers"][0]["env"]:
111 if envvar.get("name", "") == "AMBASSADOR_ID":
112 envvar["value"] = "default"
113
114 # add new envs, if any
115 manifest["spec"]["containers"][0]["env"].extend(envs)
116
117 # print("INSTALLING AMBASSADOR: manifests:")
118 # print(yaml.safe_dump_all(ambassador_yaml))
119
120 apply_kube_artifacts(namespace=namespace, artifacts=yaml.safe_dump_all(ambassador_yaml))
121
122
123def update_envs(envs, name, value):
124 found = False
125
126 for e in envs:
127 if e["name"] == name:
128 e["value"] = value
129 found = True
130 break
131
132 if not found:
133 envs.append({"name": name, "value": value})
134
135
136def create_namespace(namespace):
137 apply_kube_artifacts(
138 namespace=namespace, artifacts=integration_manifests.namespace_manifest(namespace)
139 )
140
141
142def create_qotm_mapping(namespace):
143 qotm_mapping = f"""
144---
145apiVersion: getambassador.io/v3alpha1
146kind: Mapping
147metadata:
148 name: qotm-mapping
149 namespace: {namespace}
150spec:
151 hostname: "*"
152 prefix: /qotm/
153 service: qotm
154"""
155
156 apply_kube_artifacts(namespace=namespace, artifacts=qotm_mapping)
157
158
159def create_httpbin_mapping(namespace):
160 httpbin_mapping = f"""
161---
162apiVersion: getambassador.io/v3alpha1
163kind: Mapping
164metadata:
165 name: httpbin-mapping
166 namespace: {namespace}
167spec:
168 hostname: "*"
169 prefix: /httpbin/
170 rewrite: /
171 service: httpbin
172"""
173
174 apply_kube_artifacts(namespace=namespace, artifacts=httpbin_mapping)
175
176
177def get_code_with_retry(req, headers={}):
178 for attempts in range(10):
179 try:
180 resp = requests.get(req, headers=headers, timeout=10)
181 if resp.status_code < 500:
182 return resp.status_code
183 print(f"get_code_with_retry: 5xx code {resp.status_code}, retrying...")
184 except requests.exceptions.ConnectionError as e:
185 print(f"get_code_with_retry: ConnectionError {e}, attempt {attempts+1}")
186 except socket.timeout as e:
187 print(f"get_code_with_retry: socket.timeout {e}, attempt {attempts+1}")
188 except Exception as e:
189 print(f"get_code_with_retry: generic exception {e}, attempt {attempts+1}")
190 time.sleep(5)
191 return 503
View as plain text