blob: 61fe17b49d22068783c5457ee7a3fff804324696 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Apply the messaging network boundary before creating or updating workloads."""
import openserverless.kube as kube
import openserverless.config as cfg
def reconcile(owner):
"""Install the boundary on existing clusters during operator resume/update."""
# Import lazily: component create functions use this module's apply helper.
from openserverless import kafka, zookeeper
enabled = [(name, module) for name, module in
[('zookeeper', zookeeper), ('kafka', kafka)]
if cfg.get('components.' + name)]
if not enabled:
return
# Never turn a failed owner lookup into an ownerless component deployment.
if not owner or not owner.get('metadata', {}).get('uid'):
raise RuntimeError('Cannot reconcile messaging isolation without the Whisk owner')
for name, module in enabled:
module.create(owner)
def apply(spec, component):
policy_name = 'openserverless-' + component + '-isolation'
policies = [item for item in spec['items']
if item['kind'] == 'NetworkPolicy']
if not any(item['metadata']['name'] == policy_name for item in policies):
raise RuntimeError('Missing ' + component + ' isolation policy')
# Do not create/reconcile an exposed service if policy application fails.
# The complete spec still owns the policy for reconciliation and deletion.
kube.apply({'apiVersion': 'v1', 'kind': 'List', 'items': policies})
result = kube.apply(spec)
# The template boundary revision rolls legacy pods to close existing
# anonymous sessions as well as blocking new connections.
kube.kubectl('rollout', 'status', 'statefulset/' + component, '--timeout=300s')
return result
def delete(spec, component):
policies = [item for item in spec['items'] if item['kind'] == 'NetworkPolicy']
workloads = [item for item in spec['items'] if item['kind'] != 'NetworkPolicy']
result = kube.delete({'apiVersion': 'v1', 'kind': 'List', 'items': workloads})
# StatefulSet deletion can return while its dependent pods are terminating.
# Keep the boundary until those pods are actually gone; fail closed on error.
pods = kube.kubectl('get', 'pods', '-l', 'name=' + component, '-o', 'name').split()
if pods:
kube.kubectl('wait', '--for=delete', *pods, '--timeout=300s')
if policies:
result += kube.delete({'apiVersion': 'v1', 'kind': 'List', 'items': policies})
return result