Skip to content

Commit

Permalink
Move default rule into CNI plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ozdanborne committed Jun 23, 2016
1 parent 241ec96 commit 063c6b0
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 54 deletions.
35 changes: 11 additions & 24 deletions calico.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,6 @@ def __init__(self, network_config, env):
Environment dictionary used when calling the IPAM plugin.
"""

self.labels = {}
"""
Label data to assign to this endpoint. The origin of the labels is orchestrator
dependent.
"""

self.args = network_config.get(ARGS_KEY, {})

# If there Mesos namespaced data, extract any labels that have been specified.
# Note that Mesos labels are a list of {"key": <key>, "value": <value>}, so pull
# the key and values and populate the labels dictionary.
if MESOS_NS_KEY in self.args:
_log.info("Extracting Mesos namespaced data")
labels_list = self.args[MESOS_NS_KEY]. \
get(MESOS_NETWORK_INFO_KEY, {}). \
get(MESOS_LABELS_OUTER_KEY, {}). \
get(MESOS_LABELS_KEY, [])
for label in labels_list:
self.labels[label["key"]] = label["value"]

self.command = env[CNI_COMMAND_ENV]
assert self.command in [CNI_CMD_DELETE, CNI_CMD_ADD], \
"Invalid CNI command %s" % self.command
Expand Down Expand Up @@ -160,6 +140,17 @@ def __init__(self, network_config, env):
Path in which to search for CNI plugins.
"""

self.running_under_mesos = bool(self.network_config.get("args", {}). \
get(MESOS_NS_KEY))
"""
Flag indicating if this plugin is being executed by mesos. Mesos injects "args"
into the network config before passing it to the CNI plugin. Those args will
contain a mesos namespaced field. If that field exists, this must be mesos.
Note: Mesos does not yet inject this information during network deletion. Fortunately,
we don't perform mesos-specific logic during deletion.
"""

self.running_under_k8s = self.k8s_namespace and self.k8s_pod_name
if self.running_under_k8s:
self.workload_id = "%s.%s" % (self.k8s_namespace, self.k8s_pod_name)
Expand Down Expand Up @@ -588,10 +579,6 @@ def _create_endpoint(self, ip_list):
print_cni_error(ERR_CODE_GENERIC, e.message)
sys.exit(ERR_CODE_GENERIC)

# Assign labels to the new endpoint object
_log.debug("Setting Labels: %s", self.labels)
endpoint.labels = self.labels

_log.info("Created Calico endpoint with IP address(es) %s", ip_list)
return endpoint

Expand Down
16 changes: 1 addition & 15 deletions calico_cni/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,7 @@
LOG_DIR = "/var/log/calico/cni"
LOG_FORMAT = '%(asctime)s %(process)d [%(identity)s] %(levelname)s %(message)s'

# Mesos namespaced data. Mesos CNI inserts the following additional data into
# the args field:
# "args" : {
# "org.apache.mesos" : {
# "network_info" : {
# "name" : "mynet",
# "labels" : {
# "labels" : [
# { "key" : "app", "value" : "myapp" },
# { "key" : "env", "value" : "prod" }
# ]
# }
# }
# }
# }
# Mesos namespaced data.
MESOS_NS_KEY = "org.apache.mesos"
MESOS_NETWORK_INFO_KEY = "network_info"
MESOS_LABELS_OUTER_KEY = "labels"
Expand Down
51 changes: 49 additions & 2 deletions calico_cni/policy_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,55 @@ def generate_tags(self):
"""
return []

class MesosPolicyDriver(DefaultPolicyDriver):
"""
Implements default network policy for a Mesos container manager.
"""
def __init__(self, network_name, network_args):
"""
Extract any labels that have been specified in the mesos-namespaced labels field
of the network_args. Mesos CNI inserts the following additional data into
the args field:
"args" : {
"org.apache.mesos" : {
"network_info" : {
"name" : "mynet",
"labels" : {
"labels" : [
{ "key" : "app", "value" : "myapp" },
{ "key" : "env", "value" : "prod" }
]
}
}
}
}
Note that Mesos labels are a list of {"key": <key>, "value": <value>}, so pull
the key and values and populate the labels dictionary.
"""
self.labels = {}

labels_list = network_args[MESOS_NS_KEY]. \
get(MESOS_NETWORK_INFO_KEY, {}). \
get(MESOS_LABELS_OUTER_KEY, {}). \
get(MESOS_LABELS_KEY, [])

for label in labels_list:
self.labels[label["key"]] = label["value"]

DefaultPolicyDriver.__init__(self, network_name)

def apply_profile(self, endpoint):
endpoint.labels = self.labels
self._client.update_endpoint(endpoint)
DefaultPolicyDriver.apply_profile(self, endpoint)


class KubernetesNoPolicyDriver(DefaultPolicyDriver):
"""
Implements default network policy for a Kubernetes container manager.
The different between this an the DefaultPolicyDriver is that this
engine creates profiles which allow all incoming traffic.
Unlike the DefaultPolicyDriver, this engine creates profiles
which allow _all_ incoming traffic.
"""
def generate_rules(self):
"""Generates default rules for a Kubernetes container manager.
Expand Down Expand Up @@ -399,6 +441,7 @@ def get_policy_driver(cni_plugin):
# Extract policy config and network name.
policy_config = cni_plugin.network_config.get(POLICY_KEY, {})
network_name = cni_plugin.network_config["name"]
network_args = cni_plugin.network_config.get(ARGS_KEY, {})
policy_type = policy_config.get("type")
k8s_config = cni_plugin.network_config.get("kubernetes", {})
supported_policy_types = [None,
Expand Down Expand Up @@ -452,6 +495,10 @@ def get_policy_driver(cni_plugin):
_log.debug("Using Kubernetes Driver - no policy")
driver_cls = KubernetesNoPolicyDriver
driver_args = [network_name]
elif cni_plugin.running_under_mesos:
_log.debug("Using Mesos Policy Driver")
driver_cls = MesosPolicyDriver
driver_args = [network_name, network_args]
else:
_log.debug("Using default policy driver")
driver_cls = DefaultPolicyDriver
Expand Down
13 changes: 0 additions & 13 deletions tests/unit/test_cni_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,6 @@ def setUp(self):
"routes": [{"dst": "0.0.0.0/0"}],
"range-start": "",
"range-end": ""
},
"args" : {
"org.apache.mesos" : {
"network_info" : {
"name" : "mynet",
"labels" : {
"labels" : [
{ "key" : "group", "value" : "production" },
]
},
},
},
}
}
self.env = {
Expand Down Expand Up @@ -555,7 +543,6 @@ def test_create_endpoint_mainline(self):
self.plugin._client.create_endpoint.assert_called_once_with(ANY,
self.expected_orch_id, self.expected_workload_id, ip_list)
assert_equal(ep, endpoint)
self.assertEqual(ep.labels, {"group": "production"})

def test_create_endpoint_error(self):
# Mock.
Expand Down
62 changes: 62 additions & 0 deletions tests/unit/test_policy_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
get_policy_driver,
PolicyException,
DefaultPolicyDriver,
MesosPolicyDriver,
KubernetesNoPolicyDriver,
KubernetesAnnotationDriver,
KubernetesPolicyDriver)
Expand Down Expand Up @@ -99,6 +100,48 @@ def test_invalid_network_name(self, net_name):
assert_raises(ValueError, DefaultPolicyDriver, net_name)


class MesosPolicyDriverTest(unittest.TestCase):
def setUp(self):
self.network_name = "test_net_name"
self.network_args = {
"org.apache.mesos" : {
"network_info" : {
"name" : "test_net_name",
"labels" : {
"labels" : [
{ "key" : "app", "value" : "myapp" },
{ "key" : "env", "value" : "prod" }
]
}
}
}
}

# The init function for Mesos Policy driver will convert these
# to a standard dictionary.
self.expected_labels = {"app":"myapp", "env":"prod"}

self.driver = MesosPolicyDriver(self.network_name, self.network_args)
assert_equal(self.driver.profile_name, self.network_name)
assert_equal(self.driver.labels, self.expected_labels)

# Mock the datastore client
self.client = MagicMock(spec=DatastoreClient)
self.driver._client = self.client

@patch("calico_cni.policy_drivers.DefaultPolicyDriver.apply_profile")
def test_apply_profile(self, m_default_apply_profile):
endpoint = MagicMock(spec=Endpoint)
endpoint.endpoint_id = "12345"

# Mock default profile call
self.driver.apply_profile(endpoint)

m_default_apply_profile.assert_called_once()
self.assertEqual(endpoint.labels, self.expected_labels)



class KubernetesDefaultPolicyDriverTest(unittest.TestCase):
"""
Test class for KubernetesDefaultPolicyDriver class.
Expand All @@ -122,6 +165,7 @@ def test_generate_rules(self):
outbound_rules=[Rule(action="allow")])
assert_equal(rules, expected)


class KubernetesAnnotationDriverTest(unittest.TestCase):
"""
Test class for KubernetesAnnotationDriver class.
Expand Down Expand Up @@ -422,6 +466,7 @@ def test_get_metadata_missing(self):
# Should be None
assert_equal(annotations, None)


class KubernetesPolicyDriverTest(unittest.TestCase):
"""
Test class for DefaultDenyInboundDriver class.
Expand Down Expand Up @@ -467,6 +512,7 @@ def test_get_policy_driver_default_k8s(self):
cni_plugin.k8s_pod_name = "podname"
cni_plugin.k8s_namespace = "namespace"
cni_plugin.running_under_k8s = True
cni_plugin.running_under_mesos = False
driver = get_policy_driver(cni_plugin)
assert_true(isinstance(driver, KubernetesNoPolicyDriver))

Expand All @@ -477,6 +523,7 @@ def test_get_policy_driver_k8s_annotations(self):
cni_plugin.k8s_pod_name = "podname"
cni_plugin.k8s_namespace = "namespace"
cni_plugin.running_under_k8s = True
cni_plugin.running_under_mesos = False
driver = get_policy_driver(cni_plugin)
assert_true(isinstance(driver, KubernetesAnnotationDriver))

Expand All @@ -486,6 +533,7 @@ def test_get_policy_driver_k8s(self):
cni_plugin.k8s_pod_name = "podname"
cni_plugin.k8s_namespace = "namespace"
cni_plugin.running_under_k8s = True
cni_plugin.running_under_mesos = False
driver = get_policy_driver(cni_plugin)
assert_true(isinstance(driver, KubernetesPolicyDriver))

Expand All @@ -503,6 +551,7 @@ def test_missing_cert(self):
cni_plugin = Mock(spec=CniPlugin)
cni_plugin.network_config = config
cni_plugin.running_under_k8s = True
cni_plugin.running_under_mesos = False
cni_plugin.k8s_pod_name = "podname"
cni_plugin.k8s_namespace = "namespace"
with assert_raises(SystemExit) as err:
Expand All @@ -517,10 +566,23 @@ def test_get_policy_driver_value_error(self, m_driver):
cni_plugin = Mock(spec=CniPlugin)
cni_plugin.network_config = {"name": "testnetwork"}
cni_plugin.running_under_k8s = False
cni_plugin.running_under_mesos = False

# Call
with assert_raises(SystemExit) as err:
get_policy_driver(cni_plugin)
e = err.exception
assert_equal(e.code, ERR_CODE_GENERIC)

def test_get_policy_driver_mesos(self):
cni_plugin = Mock(spec=CniPlugin)
cni_plugin.network_config = {"name": "testnetwork",
"policy":{"type": "k8s"},
"args": {
"org.apache.mesos": {}
}
}
cni_plugin.running_under_k8s = False
cni_plugin.running_under_mesos = True
driver = get_policy_driver(cni_plugin)
assert_true(isinstance(driver, MesosPolicyDriver))

0 comments on commit 063c6b0

Please sign in to comment.