class ApplicationBuilder(object): """ ApplicationBuilder is a builder to create an appliction from an image. You should offer many necessary arguments. Parameters: image_name: the image name for the container. tcp_ports: a dict, the tcp ports of the containers. For example: { "http": 80, "https": 443} udp_ports: a dict, the udp ports of the containers. commands: the commands which the container runs when start. envs: a dict for example: {"MYSQL_HOST": "localhost", "PORT": "3306"} volumes: a dict, for example: [{"volume": id, "mount_path": path}] """ namespace = None application = None image_name = None tcp_ports = None udp_ports = None commands = None args = None envs = None is_public = False volumes = None def __init__(self, application, image_name, tcp_ports=None, udp_ports=None, commands=None, args=None, envs=None, is_public=False, volumes=None, min_replicas=None, max_replicas=None, cpu_target=None): self.kubeclient = KubeClient("http://{}:{}{}".format(settings.MASTER_IP, settings.K8S_PORT, settings.K8S_API_PATH)) self.application = application self.namespace = self.application.image.project.name self.application_name = get_application_instance_name(self.application) self.image_name = image_name self.tcp_ports = tcp_ports self.udp_ports = udp_ports self.commands = commands self.args = args self.envs = envs self.is_public = is_public self.volumes = volumes # Compute the resource limit of the application resource_limit = self.application.resource_limit self.cpu = str(resource_limit.cpu) + resource_limit.cpu_unit self.memory = str(resource_limit.memory) + resource_limit.memory_unit # autoscaler if self.application.is_autoscaler: self.min_replicas = int(min_replicas) self.max_replicas = int(max_replicas) self.cpu_target = int(cpu_target) logger.debug(str(self.min_replicas) + " " + str(self.max_replicas) + " " + str(self.cpu_target)) def create_application(self): """ Create application by multiple threading. """ creating_thread = Thread(target=self._create_application) creating_thread.start() def _create_application(self): """ First create a replicationcontroller, then create a service, update database at last. """ logger.info('Create an application {} in namespace {} by image {}.' .format(self.application_name, self.namespace, self.image_name)) if not self._create_controller(): logger.debug('Create controller {} failed.'.format( self.application_name)) logger.info('Create an application {} in namespace {} failed.' .format(self.application_name, self.namespace, self.image_name)) self._update_application_metadata(status='error') return None if self.tcp_ports or self.udp_ports: if not self._create_service(): logger.debug('Create service {} failed.'.format( self.application_name)) logger.info('Create an application {} in namespace {} failed.' .format(self.application_name, self.namespace, self.image_name)) self._update_application_metadata(status='error') return None internal_ip, ports = self._get_service_ip_and_ports() else: internal_ip = None ports = None # update metadata if self.is_public: external_ip = get_optimal_external_ip(namespace=self.namespace, app_name=self.application_name) self._update_application_metadata(status='active', internal_ip=internal_ip, external_ip=external_ip ) else: self._update_application_metadata(status='active', internal_ip=internal_ip ) # mount volume self._mount_volume_onto_application() # create port metadata self._create_ports_metadata(ports) # create environment metadata self._create_environments_metadata() # create autoscaler if self.application.is_autoscaler: scaler_builder = AutoScalerBuilder( application=self.application, min_replicas=self.min_replicas, max_replicas=self.max_replicas, cpu_target=self.cpu_target ) scaler_builder.create_autoscaler() def _create_controller(self): """ Create a replicationcontroller by provided image. """ return self.kubeclient.create_controller(namespace=self.namespace, name=self.application_name, image_name=self.image_name, cpu=self.cpu, memory=self.memory, replicas=self.application.replicas, tcp_ports=self.tcp_ports, udp_ports=self.udp_ports, commands=self.commands, args=self.args, envs=self.envs, volumes=self._get_volume_names_and_path() ) def _create_service(self): """ Create a service on the replicationcontroller. """ return self.kubeclient.create_service(namespace=self.namespace, name=self.application_name, tcp_ports=self.tcp_ports, udp_ports=self.udp_ports, is_public=self.is_public, session_affinity=self.application.session_affinity ) def _get_service_ip_and_ports(self): """ Get internal_ip and ports of the service. """ response = self.kubeclient.get_service_details(self.namespace, self.application_name) return (response['spec']['clusterIP'], response['spec']['ports']) def _update_application_metadata(self, status=None, internal_ip=None, external_ip=None): """ Update the application metadata. """ if status: self.application.status = status if internal_ip: self.application.internal_ip = internal_ip if external_ip: self.application.external_ip = external_ip self.application.save() def _create_ports_metadata(self, ports): """ Create ports metadata for application. """ if not ports: return None for port_dict in ports: port = Port(app=self.application, name=port_dict['name'], protocol=port_dict['protocol'], external_port=port_dict.get('nodePort', None), internal_port=port_dict['port'] ) port.save() def _create_environments_metadata(self): """ Create environment metadata for application. """ if not self.envs: return None for name, value in self.envs.items(): env = Environment(app=self.application, name=name, value=value) env.save() def _mount_volume_onto_application(self): """ Edit the metadata, Mount volumes onto this application. """ if not self.volumes: return None for volume_item in self.volumes: volume = Volume.objects.get(id=volume_item['volume']) logger.debug("mount volume {} onto application {}.".format( volume.name, self.application_name)) volume.app = self.application volume.mount_path = volume_item['mount_path'] volume.save() def _get_volume_names_and_path(self): """ Return the volume names from volume ids. The true volume instance name is "project_name-volume_name". """ if not self.volumes: return None volume_name_path = {} for volume_item in self.volumes: volume = Volume.objects.get(id=int(volume_item['volume'])) volume_name = "{}-{}".format(self.namespace, volume.name) volume_name_path[volume_name] = volume_item['mount_path'] logger.debug(volume_name_path) return volume_name_path
class KubeClientTestCase(unittest.TestCase): def setUp(self): self.client = KubeClient("http://192.168.0.10:8080/api/v1/") def tearDown(self): self.client = None def test_add_slash(self): url = "http://192.168.0.10:8080" self.assertEqual(KubeClient.add_slash(url), "http://192.168.0.10:8080/") def test_create_instance(self): url = "http://192.168.0.10:8080" client = KubeClient(url) self.assertEqual(client.base_url, "http://192.168.0.10:8080/") def test_send_request(self): res = self.client.send_request("get", "namespaces", labels={ 'a': 1, 'name': 'wangtao' }) self.assertEqual(isinstance(res, dict), True) def test_list_namespaces(self): namespaces = self.client.list_namespces() print(namespaces) self.assertEqual(True, True) def test_list_nodes(self): nodes = self.client.list_nodes() print(nodes) self.assertEqual(True, True) def test_create_namespace(self): self.client.create_namespace('user') def test_delete_namespace(self): self.client.delete_namespace('abcd') def test_list_controllers(self): controllers = self.client.list_controllers('user') print(controllers) def test_create_controller_1(self): image_name = '192.168.0.15:5000/user/nginx:1.9.9' res = self.client.create_controller('user', 'test-nginx', image_name, replicas=2, tcp_ports={"http": 80}) print(res) def test_create_controller_2(self): image_name = '192.168.0.15:5000/admin/ubuntu:14.04' self.client.create_controller('test-space', 'test-nginx', image_name, replicas=1, commands=['sleep', '3600'], envs={"MYSQL": "192.168.0.100"}) def test_create_controller_3(self): image_name = '192.168.0.15:5000/admin/nginx:1.9.9' self.client.create_controller('test-space', 'test-nginx', image_name, replicas=1, tcp_ports={ "http": 80, "https": 443 }) def test_create_controller_volume(self): image_name = '192.168.0.15:5000/user/nginx:1.9.9' self.client.create_controller( 'user', 'test-nginx', image_name, cpu="100m", memory="64Mi", replicas=1, tcp_ports={"http": 80}, volumes={"project0-volume0": "/var/www/html"}) def test_delete_controller(self): self.client.delete_controller('test-space', 'test-nginx') def test_list_services(self): services = self.client.list_services('test-space') print(services) def test_create_service_internal(self): res = self.client.create_service('user', 'test-nginx', tcp_ports={"http": 80}, is_public=False) print(res) def test_create_service_external(self): res = self.client.create_service('test-space', 'test-nginx', tcp_ports={"http": 80}, is_public=True) print(res) def test_create_service_session(self): self.client.create_service('test-space', 'nginx', tcp_ports={"http": 80}, is_public=True, session_affinity=True) def test_delete_service(self): self.client.delete_service('test-space', 'nginx') def test_get_service_details(self): res = self.client.get_service_details('test-space', 'test-nginx') print(res) def test_create_persistentvolume(self): res = self.client.create_persistentvolume( 'default', 'project0-volume0', '10Mi', '/hummer/user/project0/volume0', '192.168.0.15') print(res) def test_delete_persistentvolume(self): res = self.client.delete_persistentvolume('default', 'project0-volume0') print(res) def test_create_persistentvolumeclaim(self): res = self.client.create_persistentvolumeclaim('default', 'project0-volume0', '10Mi') print(res) def test_delete_persistentvolumeclaim(self): res = self.client.delete_persistentvolumeclaim('default', 'project0-volume0') print(res) def test_list_pods(self): res = self.client.list_pods('user', label="app=project0-nginx-test") print(res) def test_get_logs_of_pod(self): res = self.client.get_logs_of_pod('user', 'project0-nginx-test-3uhej', 20) lines = res.split('\n') print(lines) def test_create_autoscaler(self): beta_client = KubeClient( "http://192.168.0.10:8080/apis/extensions/v1beta1/") res = beta_client.create_autoscaler('user', 'project0-nginx-test', 1, 5, 50) print(res) def test_delete_autoscaler(self): beta_client = KubeClient( "http://192.168.0.10:8080/apis/extensions/v1beta1/") res = beta_client.delete_autoscaler('user', 'project0-nginx-test') print(res) def test_list_host_ips(self): hosts = self.client.list_host_ips('user', "app=project0-2048") print(hosts)
class ApplicationBuilder(object): """ ApplicationBuilder is a builder to create an appliction from an image. You should offer many necessary arguments. Parameters: image_name: the image name for the container. tcp_ports: a dict, the tcp ports of the containers. For example: { "http": 80, "https": 443} udp_ports: a dict, the udp ports of the containers. commands: the commands which the container runs when start. envs: a dict for example: {"MYSQL_HOST": "localhost", "PORT": "3306"} volumes: a dict, for example: [{"volume": id, "mount_path": path}] """ namespace = None application = None image_name = None tcp_ports = None udp_ports = None commands = None args = None envs = None is_public = False volumes = None def __init__(self, application, image_name, tcp_ports=None, udp_ports=None, commands=None, args=None, envs=None, is_public=False, volumes=None, min_replicas=None, max_replicas=None, cpu_target=None): self.kubeclient = KubeClient("http://{}:{}{}".format( settings.MASTER_IP, settings.K8S_PORT, settings.K8S_API_PATH)) self.application = application self.namespace = self.application.image.project.name self.application_name = get_application_instance_name(self.application) self.image_name = image_name self.tcp_ports = tcp_ports self.udp_ports = udp_ports self.commands = commands self.args = args self.envs = envs self.is_public = is_public self.volumes = volumes # Compute the resource limit of the application resource_limit = self.application.resource_limit self.cpu = str(resource_limit.cpu) + resource_limit.cpu_unit self.memory = str(resource_limit.memory) + resource_limit.memory_unit # autoscaler if self.application.is_autoscaler: self.min_replicas = int(min_replicas) self.max_replicas = int(max_replicas) self.cpu_target = int(cpu_target) logger.debug( str(self.min_replicas) + " " + str(self.max_replicas) + " " + str(self.cpu_target)) def create_application(self): """ Create application by multiple threading. """ creating_thread = Thread(target=self._create_application) creating_thread.start() def _create_application(self): """ First create a replicationcontroller, then create a service, update database at last. """ logger.info( 'Create an application {} in namespace {} by image {}.'.format( self.application_name, self.namespace, self.image_name)) if not self._create_controller(): logger.debug('Create controller {} failed.'.format( self.application_name)) logger.info( 'Create an application {} in namespace {} failed.'.format( self.application_name, self.namespace, self.image_name)) self._update_application_metadata(status='error') return None if self.tcp_ports or self.udp_ports: if not self._create_service(): logger.debug('Create service {} failed.'.format( self.application_name)) logger.info( 'Create an application {} in namespace {} failed.'.format( self.application_name, self.namespace, self.image_name)) self._update_application_metadata(status='error') return None internal_ip, ports = self._get_service_ip_and_ports() else: internal_ip = None ports = None # update metadata if self.is_public: external_ip = get_optimal_external_ip( namespace=self.namespace, app_name=self.application_name) self._update_application_metadata(status='active', internal_ip=internal_ip, external_ip=external_ip) else: self._update_application_metadata(status='active', internal_ip=internal_ip) # mount volume self._mount_volume_onto_application() # create port metadata self._create_ports_metadata(ports) # create environment metadata self._create_environments_metadata() # create autoscaler if self.application.is_autoscaler: scaler_builder = AutoScalerBuilder(application=self.application, min_replicas=self.min_replicas, max_replicas=self.max_replicas, cpu_target=self.cpu_target) scaler_builder.create_autoscaler() def _create_controller(self): """ Create a replicationcontroller by provided image. """ return self.kubeclient.create_controller( namespace=self.namespace, name=self.application_name, image_name=self.image_name, cpu=self.cpu, memory=self.memory, replicas=self.application.replicas, tcp_ports=self.tcp_ports, udp_ports=self.udp_ports, commands=self.commands, args=self.args, envs=self.envs, volumes=self._get_volume_names_and_path()) def _create_service(self): """ Create a service on the replicationcontroller. """ return self.kubeclient.create_service( namespace=self.namespace, name=self.application_name, tcp_ports=self.tcp_ports, udp_ports=self.udp_ports, is_public=self.is_public, session_affinity=self.application.session_affinity) def _get_service_ip_and_ports(self): """ Get internal_ip and ports of the service. """ response = self.kubeclient.get_service_details(self.namespace, self.application_name) return (response['spec']['clusterIP'], response['spec']['ports']) def _update_application_metadata(self, status=None, internal_ip=None, external_ip=None): """ Update the application metadata. """ if status: self.application.status = status if internal_ip: self.application.internal_ip = internal_ip if external_ip: self.application.external_ip = external_ip self.application.save() def _create_ports_metadata(self, ports): """ Create ports metadata for application. """ if not ports: return None for port_dict in ports: port = Port(app=self.application, name=port_dict['name'], protocol=port_dict['protocol'], external_port=port_dict.get('nodePort', None), internal_port=port_dict['port']) port.save() def _create_environments_metadata(self): """ Create environment metadata for application. """ if not self.envs: return None for name, value in self.envs.items(): env = Environment(app=self.application, name=name, value=value) env.save() def _mount_volume_onto_application(self): """ Edit the metadata, Mount volumes onto this application. """ if not self.volumes: return None for volume_item in self.volumes: volume = Volume.objects.get(id=volume_item['volume']) logger.debug("mount volume {} onto application {}.".format( volume.name, self.application_name)) volume.app = self.application volume.mount_path = volume_item['mount_path'] volume.save() def _get_volume_names_and_path(self): """ Return the volume names from volume ids. The true volume instance name is "project_name-volume_name". """ if not self.volumes: return None volume_name_path = {} for volume_item in self.volumes: volume = Volume.objects.get(id=int(volume_item['volume'])) volume_name = "{}-{}".format(self.namespace, volume.name) volume_name_path[volume_name] = volume_item['mount_path'] logger.debug(volume_name_path) return volume_name_path
class KubeClientTestCase(unittest.TestCase): def setUp(self): self.client = KubeClient("http://192.168.0.10:8080/api/v1/") def tearDown(self): self.client = None def test_add_slash(self): url = "http://192.168.0.10:8080" self.assertEqual(KubeClient.add_slash(url), "http://192.168.0.10:8080/") def test_create_instance(self): url = "http://192.168.0.10:8080" client = KubeClient(url) self.assertEqual(client.base_url, "http://192.168.0.10:8080/") def test_send_request(self): res = self.client.send_request("get", "namespaces", labels={'a': 1, 'name': 'wangtao'}) self.assertEqual(isinstance(res, dict), True) def test_list_namespaces(self): namespaces = self.client.list_namespces() print(namespaces) self.assertEqual(True, True) def test_list_nodes(self): nodes = self.client.list_nodes() print(nodes) self.assertEqual(True, True) def test_create_namespace(self): self.client.create_namespace('user') def test_delete_namespace(self): self.client.delete_namespace('abcd') def test_list_controllers(self): controllers = self.client.list_controllers('user') print(controllers) def test_create_controller_1(self): image_name = '192.168.0.15:5000/user/nginx:1.9.9' res = self.client.create_controller('user', 'test-nginx', image_name, replicas=2, tcp_ports={"http": 80}) print(res) def test_create_controller_2(self): image_name = '192.168.0.15:5000/admin/ubuntu:14.04' self.client.create_controller('test-space', 'test-nginx', image_name, replicas=1, commands=['sleep', '3600'], envs={"MYSQL": "192.168.0.100"} ) def test_create_controller_3(self): image_name = '192.168.0.15:5000/admin/nginx:1.9.9' self.client.create_controller('test-space', 'test-nginx', image_name, replicas=1, tcp_ports={"http": 80, "https": 443}) def test_create_controller_volume(self): image_name = '192.168.0.15:5000/user/nginx:1.9.9' self.client.create_controller('user', 'test-nginx', image_name, cpu="100m", memory="64Mi", replicas=1, tcp_ports={"http": 80}, volumes={"project0-volume0": "/var/www/html"}) def test_delete_controller(self): self.client.delete_controller('test-space', 'test-nginx') def test_list_services(self): services = self.client.list_services('test-space') print(services) def test_create_service_internal(self): res = self.client.create_service('user', 'test-nginx', tcp_ports={"http": 80}, is_public=False ) print(res) def test_create_service_external(self): res = self.client.create_service('test-space', 'test-nginx', tcp_ports={"http": 80}, is_public=True ) print(res) def test_create_service_session(self): self.client.create_service('test-space', 'nginx', tcp_ports={"http": 80}, is_public=True, session_affinity=True ) def test_delete_service(self): self.client.delete_service('test-space', 'nginx') def test_get_service_details(self): res = self.client.get_service_details('test-space', 'test-nginx') print(res) def test_create_persistentvolume(self): res = self.client.create_persistentvolume('default', 'project0-volume0', '10Mi', '/hummer/user/project0/volume0', '192.168.0.15') print(res) def test_delete_persistentvolume(self): res = self.client.delete_persistentvolume('default', 'project0-volume0') print(res) def test_create_persistentvolumeclaim(self): res = self.client.create_persistentvolumeclaim('default', 'project0-volume0', '10Mi') print(res) def test_delete_persistentvolumeclaim(self): res = self.client.delete_persistentvolumeclaim('default', 'project0-volume0') print(res) def test_list_pods(self): res = self.client.list_pods('user', label="app=project0-nginx-test") print(res) def test_get_logs_of_pod(self): res = self.client.get_logs_of_pod('user', 'project0-nginx-test-3uhej', 20) lines = res.split('\n') print(lines) def test_create_autoscaler(self): beta_client = KubeClient("http://192.168.0.10:8080/apis/extensions/v1beta1/") res = beta_client.create_autoscaler('user', 'project0-nginx-test', 1, 5, 50) print(res) def test_delete_autoscaler(self): beta_client = KubeClient("http://192.168.0.10:8080/apis/extensions/v1beta1/") res = beta_client.delete_autoscaler('user', 'project0-nginx-test') print(res) def test_list_host_ips(self): hosts = self.client.list_host_ips('user', "app=project0-2048") print(hosts)