blob: 0980896d71d4ac2a139864af9f10263f72e9e8e5 [file] [log] [blame]
Scott Baker82b2b082018-04-16 16:02:14 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""
17 sync_kubernetesserviceinstance.py
18
19 Synchronize KubernetesServiceInstance. See also the related pull_step.
Scott Baker3fd18e52018-04-17 09:18:21 -070020
21 This sync_step is intended to handle the case where callers are creating pods directly, as opposed to using
22 a controller to manage pods for them. It makes some simplifying assumptions, such as each pod has one
23 container and uses one image.
Scott Baker82b2b082018-04-16 16:02:14 -070024"""
25
26from synchronizers.new_base.syncstep import SyncStep
27from synchronizers.new_base.modelaccessor import KubernetesServiceInstance
28
29from xosconfig import Config
30from multistructlog import create_logger
31
Scott Baker3fd18e52018-04-17 09:18:21 -070032from kubernetes.client.rest import ApiException
Scott Baker82b2b082018-04-16 16:02:14 -070033from kubernetes import client as kubernetes_client, config as kubernetes_config
34
35log = create_logger(Config().get('logging'))
36
37class SyncKubernetesServiceInstance(SyncStep):
38
39 """
40 SyncKubernetesServiceInstance
41
42 Implements sync step for syncing kubernetes service instances.
43 """
44
45 provides = [KubernetesServiceInstance]
46 observes = KubernetesServiceInstance
47 requested_interval = 0
48
49 def __init__(self, *args, **kwargs):
50 super(SyncKubernetesServiceInstance, self).__init__(*args, **kwargs)
Scott Baker82b2b082018-04-16 16:02:14 -070051 kubernetes_config.load_incluster_config()
52 self.v1 = kubernetes_client.CoreV1Api()
53
Scott Baker3fd18e52018-04-17 09:18:21 -070054 def get_pod(self, o):
55 """ Given a KubernetesServiceInstance, read the pod from Kubernetes.
56 Return None if the pod does not exist.
57 """
58 try:
59 pod = self.v1.read_namespaced_pod(o.name, o.slice.trust_domain.name)
60 except ApiException, e:
61 if e.status == 404:
62 return None
63 raise
64 return pod
Scott Baker82b2b082018-04-16 16:02:14 -070065
Scott Bakerac43a742018-05-07 16:54:03 -070066 def generate_pod_spec(self, o):
67 pod = kubernetes_client.V1Pod()
68 pod.metadata = kubernetes_client.V1ObjectMeta(name=o.name)
69
70 if o.slice.trust_domain:
71 pod.metadata.namespace = o.slice.trust_domain.name
72
73 if o.image.tag:
74 imageName = o.image.name + ":" + o.image.tag
75 else:
76 # TODO(smbaker): Is this case possible?
77 imageName = o.image.name
78
79 volumes = []
80 volume_mounts = []
81
82 # Attach and mount the configmaps
83 for xos_vol in o.kubernetes_config_volume_mounts.all():
84 k8s_vol = kubernetes_client.V1Volume(name=xos_vol.config.name)
85 k8s_vol.config_map = kubernetes_client.V1ConfigMapVolumeSource(name=xos_vol.config.name)
86 volumes.append(k8s_vol)
87
88 k8s_vol_m = kubernetes_client.V1VolumeMount(name=xos_vol.config.name,
89 mount_path=xos_vol.mount_path,
90 sub_path=xos_vol.sub_path)
91 volume_mounts.append(k8s_vol_m)
92
93 # Attach and mount the secrets
94 for xos_vol in o.kubernetes_secret_volume_mounts.all():
95 k8s_vol = kubernetes_client.V1Volume(name=xos_vol.secret.name)
96 k8s_vol.secret = kubernetes_client.V1SecretVolumeSource(secret_name=xos_vol.secret.name)
97 volumes.append(k8s_vol)
98
99 k8s_vol_m = kubernetes_client.V1VolumeMount(name=xos_vol.secret.name,
100 mount_path=xos_vol.mount_path,
101 sub_path=xos_vol.sub_path)
102 volume_mounts.append(k8s_vol_m)
103
104 container = kubernetes_client.V1Container(name=o.name,
105 image=imageName,
106 volume_mounts=volume_mounts)
107
108 spec = kubernetes_client.V1PodSpec(containers=[container], volumes=volumes)
109 pod.spec = spec
110
111 if o.slice.principal:
112 pod.spec.service_account = o.slice.principal.name
113
114 return pod
115
Scott Baker3fd18e52018-04-17 09:18:21 -0700116 def sync_record(self, o):
117 if o.xos_managed:
118 if (not o.slice) or (not o.slice.trust_domain):
119 raise Exception("No trust domain for service instance", o=o)
120
121 if (not o.name):
Scott Bakerac43a742018-05-07 16:54:03 -0700122 raise Exception("No name for service instance")
Scott Baker3fd18e52018-04-17 09:18:21 -0700123
124 pod = self.get_pod(o)
125 if not pod:
Scott Bakerac43a742018-05-07 16:54:03 -0700126 pod = self.generate_pod_spec(o)
Scott Baker3fd18e52018-04-17 09:18:21 -0700127
128 log.info("Creating pod", o=o, pod=pod)
129
130 pod = self.v1.create_namespaced_pod(o.slice.trust_domain.name, pod)
Scott Bakerac43a742018-05-07 16:54:03 -0700131 else:
132 log.info("Replacing pod", o=o, pod=pod)
133
134 # TODO: apply changes, perhaps by calling self.generate_pod_spec() and copying in the differences,
135 # to accomodate new volumes that might have been attached, or other changes.
136
137 # If we don't apply any changes to the pod, it's still the case that Kubernetes will pull in new
138 # mounts of existing configmaps during the replace operation, if the configmap contents have changed.
139
140 pod = self.v1.replace_namespaced_pod(o.name, o.slice.trust_domain.name, pod)
Scott Baker3fd18e52018-04-17 09:18:21 -0700141
142 if (not o.backend_handle):
143 o.backend_handle = pod.metadata.self_link
144 o.save(update_fields=["backend_handle"])
Scott Baker82b2b082018-04-16 16:02:14 -0700145
146 def delete_record(self, port):
Scott Baker3fd18e52018-04-17 09:18:21 -0700147 # TODO(smbaker): Implement delete step
Scott Baker82b2b082018-04-16 16:02:14 -0700148 pass
149