1import logging
2import os
3import sys
4
5import pytest
6
7import tests.integration.manifests as integration_manifests
8from ambassador import IR, Config
9from ambassador.fetch import ResourceFetcher
10from ambassador.utils import NullSecretHandler, parse_bool
11from kat.harness import is_knative_compatible
12from tests.integration.utils import create_qotm_mapping, get_code_with_retry, install_ambassador
13from tests.kubeutils import apply_kube_artifacts
14from tests.manifests import qotm_manifests
15from tests.runutils import run_and_assert
16
17logger = logging.getLogger("ambassador")
18
19# knative_service_example gets applied to the cluster with `kubectl --namespace=knative-testing
20# apply`; we therefore DO NOT explicitly set the 'namespace:' because --namespace will imply it, and
21# explicitly setting anything only adds room for something else to go wrong.
22knative_service_example = """
23---
24apiVersion: serving.knative.dev/v1alpha1
25kind: Service
26metadata:
27 name: helloworld-go
28spec:
29 template:
30 spec:
31 containers:
32 - image: gcr.io/knative-samples/helloworld-go
33 env:
34 - name: TARGET
35 value: "Ambassador is Awesome"
36"""
37
38# knative_ingress_example is not applied to the cluster, but is instead fed directly to the
39# ResourceFetcher; so we MUST explicitly set the namespace, because we can't rely on kubectl and/or
40# the apiserver to auto-populate it for us.
41knative_ingress_example = """
42apiVersion: networking.internal.knative.dev/v1alpha1
43kind: Ingress
44metadata:
45 name: helloworld-go
46 namespace: default
47spec:
48 rules:
49 - hosts:
50 - helloworld-go.default.svc.cluster.local
51 http:
52 paths:
53 - retries:
54 attempts: 3
55 perTryTimeout: 10m0s
56 splits:
57 - percent: 100
58 serviceName: helloworld-go-qf94m
59 servicePort: 80
60 timeout: 10m0s
61 visibility: ClusterLocal
62 visibility: ExternalIP
63"""
64
65
66class KnativeTesting:
67 def test_knative(self):
68 namespace = "knative-testing"
69
70 # Install Knative
71 apply_kube_artifacts(
72 namespace=None, artifacts=integration_manifests.load("knative_serving_crds")
73 )
74 apply_kube_artifacts(
75 namespace="knative-serving",
76 artifacts=integration_manifests.load("knative_serving_0.18.0"),
77 )
78 run_and_assert(
79 [
80 "tools/bin/kubectl",
81 "patch",
82 "configmap/config-network",
83 "--type",
84 "merge",
85 "--patch",
86 r'{"data": {"ingress.class": "ambassador.ingress.networking.knative.dev"}}',
87 "-n",
88 "knative-serving",
89 ]
90 )
91
92 # Wait for Knative to become ready
93 run_and_assert(
94 [
95 "tools/bin/kubectl",
96 "wait",
97 "--timeout=90s",
98 "--for=condition=Ready",
99 "pod",
100 "-l",
101 "app=activator",
102 "-n",
103 "knative-serving",
104 ]
105 )
106 run_and_assert(
107 [
108 "tools/bin/kubectl",
109 "wait",
110 "--timeout=90s",
111 "--for=condition=Ready",
112 "pod",
113 "-l",
114 "app=controller",
115 "-n",
116 "knative-serving",
117 ]
118 )
119 run_and_assert(
120 [
121 "tools/bin/kubectl",
122 "wait",
123 "--timeout=90s",
124 "--for=condition=Ready",
125 "pod",
126 "-l",
127 "app=webhook",
128 "-n",
129 "knative-serving",
130 ]
131 )
132 run_and_assert(
133 [
134 "tools/bin/kubectl",
135 "wait",
136 "--timeout=90s",
137 "--for=condition=Ready",
138 "pod",
139 "-l",
140 "app=autoscaler",
141 "-n",
142 "knative-serving",
143 ]
144 )
145
146 # Install Ambassador
147 install_ambassador(
148 namespace=namespace, envs=[{"name": "AMBASSADOR_KNATIVE_SUPPORT", "value": "true"}]
149 )
150
151 # Install QOTM
152 apply_kube_artifacts(namespace=namespace, artifacts=qotm_manifests)
153 create_qotm_mapping(namespace=namespace)
154
155 # Now let's wait for ambassador and QOTM pods to become ready
156 run_and_assert(
157 [
158 "tools/bin/kubectl",
159 "wait",
160 "--timeout=90s",
161 "--for=condition=Ready",
162 "pod",
163 "-l",
164 "service=ambassador",
165 "-n",
166 namespace,
167 ]
168 )
169 run_and_assert(
170 [
171 "tools/bin/kubectl",
172 "wait",
173 "--timeout=90s",
174 "--for=condition=Ready",
175 "pod",
176 "-l",
177 "service=qotm",
178 "-n",
179 namespace,
180 ]
181 )
182
183 # Create kservice
184 apply_kube_artifacts(namespace=namespace, artifacts=knative_service_example)
185
186 # Assume we can reach Ambassador through telepresence
187 qotm_host = "ambassador." + namespace
188
189 # Assert 200 OK at /qotm/ endpoint
190 qotm_url = f"http://{qotm_host}/qotm/"
191 code = get_code_with_retry(qotm_url)
192 assert code == 200, f"Expected 200 OK, got {code}"
193 print(f"{qotm_url} is ready")
194
195 # Assert 200 OK at / with Knative Host header and 404 with other/no header
196 kservice_url = f"http://{qotm_host}/"
197
198 code = get_code_with_retry(kservice_url)
199 assert code == 404, f"Expected 404, got {code}"
200 print(f"{kservice_url} returns 404 with no host")
201
202 code = get_code_with_retry(kservice_url, headers={"Host": "random.host.whatever"})
203 assert code == 404, f"Expected 404, got {code}"
204 print(f"{kservice_url} returns 404 with a random host")
205
206 # Wait for kservice
207 run_and_assert(
208 [
209 "tools/bin/kubectl",
210 "wait",
211 "--timeout=90s",
212 "--for=condition=Ready",
213 "ksvc",
214 "helloworld-go",
215 "-n",
216 namespace,
217 ]
218 )
219
220 # kservice pod takes some time to spin up, so let's try a few times
221 code = 000
222 host = f"helloworld-go.{namespace}.example.com"
223 for _ in range(5):
224 code = get_code_with_retry(kservice_url, headers={"Host": host})
225 if code == 200:
226 break
227
228 assert code == 200, f"Expected 200, got {code}"
229 print(f"{kservice_url} returns 200 OK with host helloworld-go.{namespace}.example.com")
230
231
232def test_knative_counters():
233 aconf = Config()
234 fetcher = ResourceFetcher(logger, aconf)
235 fetcher.parse_yaml(knative_ingress_example, k8s=True)
236 aconf.load_all(fetcher.sorted())
237
238 secret_handler = NullSecretHandler(logger, None, None, "0")
239 ir = IR(aconf, secret_handler=secret_handler)
240 feats = ir.features()
241
242 assert feats["knative_ingress_count"] == 1, f"Expected a Knative ingress, did not find one"
243 assert (
244 feats["cluster_ingress_count"] == 0
245 ), f"Expected no Knative cluster ingresses, found at least one"
246
247
248@pytest.mark.flaky(reruns=1, reruns_delay=10)
249def test_knative():
250 if not parse_bool(os.environ.get("AMBASSADOR_PYTEST_KNATIVE_TEST", "false")):
251 pytest.xfail("AMBASSADOR_PYTEST_KNATIVE_TEST is not set, xfailing...")
252
253 if is_knative_compatible():
254 knative_test = KnativeTesting()
255 knative_test.test_knative()
256 else:
257 pytest.xfail("Knative is not supported")
258
259
260if __name__ == "__main__":
261 pytest.main(sys.argv)
View as plain text