Skip to content

Commit ee15539

Browse files
committed
chore(network): exempt gateway data-plane pods from NetworkPolicy
1 parent 4451bf5 commit ee15539

9 files changed

Lines changed: 128 additions & 43 deletions

File tree

charts/controller/files/network-policy-template.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
{
22
"spec": {
3-
"podSelector": {},
3+
"podSelector": {
4+
"matchExpressions": [
5+
{
6+
"key": "drycc.cc/workspace",
7+
"operator": "Exists"
8+
}
9+
]
10+
},
411
"policyTypes": [
512
"Ingress"
613
],
@@ -10,7 +17,7 @@
1017
{
1118
"namespaceSelector": {
1219
"matchLabels": {
13-
"drycc.cc/workspace_id": "{{workspace_id}}"
20+
"drycc.cc/workspace": "{{workspace}}"
1421
}
1522
}
1623
}

rootfs/api/models/app.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,6 @@ def _default_actor(self):
122122
def ptypes(self):
123123
return list(self.structure.keys())
124124

125-
@property
126-
def scheduler(self):
127-
"""
128-
Override @Base.AuditedModel.scheduler;
129-
since the app itself doesn't have an app object context,
130-
directly reference using ID instead.
131-
"""
132-
scheduler = super(App, self).scheduler
133-
scheduler.metadata["annotations"]["drycc.cc/app_id"] = str(self.id)
134-
scheduler.metadata["annotations"]["drycc.cc/workspace_id"] = str(self.workspace_id)
135-
return scheduler
136-
137125
def check_ptypes(self, ptypes: set):
138126
"""
139127
check available procfile types
@@ -955,7 +943,7 @@ def _create_network_policy(self):
955943
return
956944
name = namespace = self.id
957945
json_str = Template(settings.DRYCC_NETWORK_POLICY_TEMPLATE).render(
958-
Context({"workspace_id": str(self.workspace_id)})).strip()
946+
Context({"workspace": str(self.workspace.id)})).strip()
959947
kwargs = json.loads(json_str) if json_str else {}
960948
try:
961949
self.scheduler.networkpolicy.get(namespace, name)

rootfs/api/models/base.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,13 @@ def app_log(self, app_id, msg):
6060

6161
@property
6262
def scheduler(self):
63-
labels = annotations = {}
64-
if hasattr(self, 'app'):
65-
labels["drycc.cc/app_id"] = str(self.app.id)
66-
labels["drycc.cc/workspace_id"] = str(self.app.workspace.id)
67-
return get_scheduler(metadata={"labels": labels, "annotations": annotations})
63+
from api.models.app import App
64+
labels = {}
65+
if hasattr(self, 'app') or isinstance(self, App):
66+
app = getattr(self, 'app', self)
67+
labels["drycc.cc/app"] = app.id
68+
labels["drycc.cc/workspace"] = app.workspace.id
69+
return get_scheduler(metadata={"labels": labels, "annotations": dict(labels)})
6870

6971

7072
class UuidAuditedModel(AuditedModel):

rootfs/api/tests/test_app.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,20 @@ def test_get_private_registry_config_off_cluster(self, mock_requests):
658658
auth = bytes('{}:{}'.format("test", "test"), 'UTF-8')
659659
encAuth = base64.b64encode(auth).decode(encoding='UTF-8')
660660
image = "test.com/test/test"
661-
docker_config, name, create = App()._get_private_registry_config("web", image, registry.get("web", {})) # noqa
661+
scheduler = mock.Mock()
662+
scheduler.secret.get.return_value.json.return_value = {
663+
'data': {
664+
'registry-host': '',
665+
'registry-username': 'test',
666+
'registry-password': 'test',
667+
}
668+
}
669+
with mock.patch.object(
670+
App, 'scheduler', new_callable=mock.PropertyMock
671+
) as scheduler_property:
672+
scheduler_property.return_value = scheduler
673+
docker_config, name, create = App()._get_private_registry_config(
674+
"web", image, registry.get("web", {})) # noqa
662675
dockerConfig = json.loads(docker_config)
663676
expected = {"https://index.docker.io/v1/": {
664677
"auth": encAuth

rootfs/scheduler/__init__.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ def __init__(self, url, k8s_api_verify_tls=True, metadata=None):
4545
self.url = url
4646
self.k8s_api_verify_tls = k8s_api_verify_tls
4747
self.session = get_k8s_session(self.k8s_api_verify_tls)
48-
self.metadata = metadata
48+
self.metadata = {} if metadata is None else metadata
4949

5050
# map the various k8s Resources to an internal property
5151
from scheduler.resources import Resource # lazy load
52+
if not isinstance(self, Resource):
53+
KubeHTTPClient.resource_mapping = OrderedDict()
5254
for res in Resource:
5355
name = str(res.__name__).lower() # singular
5456
component = name + 's' # make plural
@@ -58,7 +60,8 @@ def __init__(self, url, k8s_api_verify_tls=True, metadata=None):
5860

5961
# get past recursion problems in case of self reference
6062
self.resource_mapping[component] = ''
61-
self.resource_mapping[component] = res(self.url, self.k8s_api_verify_tls)
63+
self.resource_mapping[component] = res(
64+
self.url, self.k8s_api_verify_tls, metadata=self.metadata)
6265
# map singular Resource name to the plural one
6366
self.resource_mapping[name] = component
6467
if res.short_name is not None:
@@ -185,45 +188,45 @@ def http_get(self, path, params=None, **kwargs):
185188

186189
return response
187190

188-
def http_post(self, path, data=None, json=None, **kwargs):
191+
def http_post(self, path, json=None, **kwargs):
189192
"""
190193
Make a POST request to the k8s server.
191194
"""
192195
try:
193196
url = urljoin(self.url, path)
194-
if self.metadata is not None and "metadata" in data:
195-
data["metadata"] = utils.dict_merge(data["metadata"], self.metadata)
196-
response = self.session.post(url, data=data, json=json, **kwargs)
197+
if json is not None and "metadata" in json:
198+
json["metadata"] = utils.dict_merge(json["metadata"], self.metadata)
199+
response = self.session.post(url, json=json, **kwargs)
197200
except requests.exceptions.ConnectionError as err:
198201
# reraise as KubeException, but log stacktrace.
199202
message = "There was a problem posting data to " \
200203
"the Kubernetes API server. URL: {}, " \
201-
"data: {}, json: {}".format(url, data, json)
204+
"json: {}".format(url, json)
202205
logger.error(message)
203206
raise KubeException(message) from err
204207

205208
return response
206209

207-
def http_put(self, path, data=None, **kwargs):
210+
def http_put(self, path, json=None, **kwargs):
208211
"""
209212
Make a PUT request to the k8s server.
210213
"""
211214
try:
212215
url = urljoin(self.url, path)
213-
if self.metadata is not None and "metadata" in data:
214-
data["metadata"] = utils.dict_merge(data["metadata"], self.metadata)
215-
response = self.session.put(url, data=data, **kwargs)
216+
if json is not None and "metadata" in json:
217+
json["metadata"] = utils.dict_merge(json["metadata"], self.metadata)
218+
response = self.session.put(url, json=json, **kwargs)
216219
except requests.exceptions.ConnectionError as err:
217220
# reraise as KubeException, but log stacktrace.
218221
message = "There was a problem putting data to " \
219222
"the Kubernetes API server. URL: {}, " \
220-
"data: {}".format(url, data)
223+
"json: {}".format(url, json)
221224
logger.error(message)
222225
raise KubeException(message) from err
223226

224227
return response
225228

226-
def http_patch(self, path, data=None, **kwargs):
229+
def http_patch(self, path, json=None, **kwargs):
227230
"""
228231
Make a PATCH request to the k8s server.
229232
"""
@@ -234,14 +237,14 @@ def http_patch(self, path, data=None, **kwargs):
234237
# application/merge-patch+json,
235238
# application/apply-patch+yaml
236239
# self.session.headers["Content-Type"] = "application/json-patch+json"
237-
if self.metadata is not None and "metadata" in data:
238-
data["metadata"] = utils.dict_merge(data["metadata"], self.metadata)
239-
response = self.session.patch(url, data=data, **kwargs)
240+
if json is not None and "metadata" in json:
241+
json["metadata"] = utils.dict_merge(json["metadata"], self.metadata)
242+
response = self.session.patch(url, json=json, **kwargs)
240243
except requests.exceptions.ConnectionError as err:
241244
# reraise as KubeException, but log stacktrace.
242245
message = "There was a problem patching data to " \
243246
"the Kubernetes API server. URL: {}, " \
244-
"data: {}".format(url, data)
247+
"json: {}".format(url, json)
245248
logger.error(message)
246249
raise KubeException(message) from err
247250

rootfs/scheduler/resources/namespace.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from scheduler.exceptions import KubeHTTPException
22
from scheduler.resources import Resource
3+
import copy
34

45

56
class Namespace(Resource):
@@ -26,16 +27,18 @@ def get(self, name=None, **kwargs):
2627

2728
return response
2829

29-
def create(self, namespace):
30+
def create(self, namespace, **kwargs):
31+
# labels that represent the namespace
32+
labels = copy.deepcopy(kwargs.get('labels', {}))
33+
labels.update({'heritage': 'drycc'})
34+
3035
url = self.api("/namespaces")
3136
data = {
3237
"kind": "Namespace",
3338
"apiVersion": self.api_version,
3439
"metadata": {
3540
"name": namespace,
36-
"labels": {
37-
'heritage': 'drycc'
38-
}
41+
"labels": labels,
3942
}
4043
}
4144

rootfs/scheduler/resources/pod.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from scheduler.resources import Resource
1010
from scheduler.states import PodState
1111

12+
from api import utils
13+
1214
DEFAULT_CONTAINER_PORT = 5000
1315

1416

@@ -192,7 +194,7 @@ def manifest(self, namespace, name, image, **kwargs):
192194
spec['imagePullSecrets'] = [{'name': kwargs.get('image_pull_secret_name')}]
193195

194196
spec['containers'] = [container]
195-
197+
manifest["metadata"] = utils.dict_merge(manifest["metadata"], self.metadata)
196198
return manifest
197199

198200
def _set_container(self, namespace, container_name, data, **kwargs):

rootfs/scheduler/tests/test_namespaces.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,44 @@
33
44
Run the tests with './manage.py test scheduler'
55
"""
6+
from django.conf import settings
7+
8+
from scheduler import mock as scheduler_mock
69
from scheduler import KubeHTTPException
710
from scheduler.tests import TestCase
11+
from scheduler.tests.utils import generate_random_name
812

913

1014
class NamespacesTest(TestCase):
1115
"""Tests scheduler namespace calls"""
1216

17+
def test_create_namespace_with_metadata_injection(self):
18+
namespace = generate_random_name()
19+
scheduler = scheduler_mock.MockSchedulerClient(
20+
settings.SCHEDULER_URL,
21+
metadata={
22+
'labels': {
23+
'drycc.cc/app': 'test-app',
24+
'drycc.cc/workspace': 'test-workspace',
25+
},
26+
'annotations': {
27+
'app.kubernetes.io/managed-by': 'drycc',
28+
'drycc.cc/app': 'test-app',
29+
}
30+
}
31+
)
32+
33+
response = scheduler.ns.create(namespace)
34+
data = response.json()
35+
36+
self.assertEqual(response.status_code, 201, data)
37+
self.assertEqual(data['metadata']['labels']['heritage'], 'drycc')
38+
self.assertEqual(data['metadata']['labels']['drycc.cc/app'], 'test-app')
39+
self.assertEqual(data['metadata']['labels']['drycc.cc/workspace'], 'test-workspace')
40+
self.assertEqual(
41+
data['metadata']['annotations']['app.kubernetes.io/managed-by'], 'drycc')
42+
self.assertEqual(data['metadata']['annotations']['drycc.cc/app'], 'test-app')
43+
1344
def test_create_namespace(self):
1445
# subclassed function does all the checking
1546
self.create_namespace()

rootfs/scheduler/tests/test_services.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
44
Run the tests with './manage.py test scheduler'
55
"""
6+
from django.conf import settings
7+
8+
from scheduler import mock as scheduler_mock
69
from scheduler import KubeHTTPException
710
from scheduler.tests import TestCase
811
from scheduler.tests.utils import generate_random_name
@@ -11,6 +14,39 @@
1114
class ServicesTest(TestCase):
1215
"""Tests scheduler service calls"""
1316

17+
def test_create_service_with_metadata_injection(self):
18+
name = generate_random_name()
19+
scheduler = scheduler_mock.MockSchedulerClient(
20+
settings.SCHEDULER_URL,
21+
metadata={
22+
'labels': {
23+
'drycc.cc/app': 'test-app',
24+
'drycc.cc/workspace': 'test-workspace',
25+
},
26+
'annotations': {
27+
'app.kubernetes.io/managed-by': 'drycc',
28+
'drycc.cc/workspace': 'test-workspace',
29+
}
30+
}
31+
)
32+
33+
response = scheduler.svc.create(self.namespace, name, ports=[{
34+
'port': 5000,
35+
'protocol': 'TCP',
36+
'targetPort': 5000,
37+
}])
38+
data = response.json()
39+
40+
self.assertEqual(response.status_code, 201, data)
41+
self.assertEqual(data['metadata']['labels']['app'], self.namespace)
42+
self.assertEqual(data['metadata']['labels']['heritage'], 'drycc')
43+
self.assertEqual(data['metadata']['labels']['drycc.cc/app'], 'test-app')
44+
self.assertEqual(data['metadata']['labels']['drycc.cc/workspace'], 'test-workspace')
45+
self.assertEqual(
46+
data['metadata']['annotations']['app.kubernetes.io/managed-by'], 'drycc')
47+
self.assertEqual(
48+
data['metadata']['annotations']['drycc.cc/workspace'], 'test-workspace')
49+
1450
def create(self, port=5000, protocol="TCP", target_port=5000):
1551
"""
1652
Helper function to create and verify a service on the namespace

0 commit comments

Comments
 (0)