def handler(self, obj, namespace=None): apiver, kind, _ = validator.validate(obj) if kind == 'Template': return self._process_template(apiver, kind, action, obj, namespace) else: return func(self, obj, namespace)
def create(self, obj, namespace=DEFAULT_NAMESPACE): """Create an object from the Kubernetes cluster.""" apiver, kind, name = validator.validate(obj) namespace = validator.check_namespace(obj, namespace) url = self._generate_url(apiver, kind, namespace) resp = self.request('post', url, data=obj) logger.info('%s `%s` successfully created', kind.capitalize(), name) return resp
def replace(self, obj, namespace=DEFAULT_NAMESPACE): """Replace a resource on the Kubernetes cluster.""" apiver, kind, name = validator.validate(obj) namespace = validator.check_namespace(obj, namespace) url = self._generate_url(apiver, kind, namespace, name) resp = self.request('put', url, data=obj) logger.info('%s `%s` successfully replaced', kind.capitalize(), name) return resp
def _process_template(self, apiver, kind, method, obj, namespace): url = self._generate_url(apiver, kind, namespace) data = self.request('post', url, data=obj) or {} for o in data.get('objects', []): apiver, kind, name = validator.validate(o) object_url = self._generate_url(apiver, kind, namespace) self.request(method, object_url, data=o) logger.debug('%sd template object: %s', method, name) logger.debug('Processed template with %d objects successfully', len(data.get('objects', []))) return data
def test_create_replace_modify(self): try: self.client.create(SIMPLE_POD, NAMESPACE) self.client.modify(PATCH_POD, NAMESPACE) # give the scheduler a chance to complete the scheduling # so that the server side content stablizes otherwise # there will be a 409 or 422 related error. time.sleep(5) apiver, kind, name = validator.validate(SIMPLE_POD) url = self.client._generate_url(apiver, kind, NAMESPACE, name) obj = self.client.request('get', url) print(json.dumps(obj)) obj['metadata']['labels'].update(REPLACE_POD) print(json.dumps(obj)) self.client.replace(obj, NAMESPACE) except Exception as err: self.fail('replace/modify pod failed unexpectedly: {}'.format(err))
def modify(self, partial, namespace=DEFAULT_NAMESPACE): """Modify a resource. The partial object provided will be strategically merged with the existing resource content. The top level meta data is required to enable modifying the correct resource instance. :param dict partial: changes to be applied to existing resource content :param str namespace: object name and auth scope, such as for teams and projects """ apiver, kind, name = validator.validate(partial) namespace = validator.check_namespace(partial, namespace) url = self._generate_url(apiver, kind, namespace, name) headers = {'Content-Type': 'application/strategic-merge-patch+json'} resp = self.request('patch', url, data=partial, headers=headers) logger.info('%s `%s` successfully modified', kind.capitalize(), name) return resp
def scale(self, obj, namespace=DEFAULT_NAMESPACE, replicas=0): """Scale replicas up or down. By default we scale back down to 0. This function takes an object and scales said object down to a specified value on the Kubernetes cluster :param dict obj: Object of the artifact being modified :param str namesapce: Namespace of the kubernetes cluster to be used :param int replicas: Default 0, size of the amount of replicas to scale """ apiver, kind, name = validator.validate(obj) namespace = validator.check_namespace(obj, namespace) url = self._generate_url(apiver, kind, namespace, name) headers = {'Content-Type': 'application/json-patch+json'} patch = [{ 'op': 'replace', 'path': '/spec/replicas', 'value': replicas }] self.request('patch', url, data=patch, headers=headers) logger.info('`%s` successfully scaled to %s', name, replicas)
def delete(self, obj, namespace=DEFAULT_NAMESPACE): """Delete an object from the Kubernetes cluster. .. note:: Replication controllers must scale to 0 in order to delete pods. Kubernetes 1.3 will implement server-side cascading deletion, but until then, it's mandatory to scale to 0 https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/garbage-collection.md :param dict obj: Object of the artifact being modified :param str namesapce: Namespace of the kubernetes cluster to be used """ apiver, kind, name = validator.validate(obj) namespace = validator.check_namespace(obj, namespace) url = self._generate_url(apiver, kind, namespace, name) if kind in ['ReplicationController']: self.scale(obj, namespace) resp = self.request('delete', url) logger.info('%s `%s` successfully deleted', kind.capitalize(), name) return resp
def test_validate_no_object(self): with self.assertRaises(KubeShiftError): validator.validate(None)
def test_validate_success(self): api_version, kind, name = validator.validate( {'apiVersion': 'v1', 'kind': 'Pod', 'metadata': {'name': 'test'}}) self.assertEqual(api_version, 'v1') self.assertEqual(kind, 'Pod') self.assertEqual(name, 'test')
def test_validate_no_metadata_name_attr(self): with self.assertRaises(KubeShiftError): validator.validate({'apiVersion': 'v1', 'kind': 'Pod'})
def test_validate_no_kind_attr(self): with self.assertRaises(KubeShiftError): validator.validate({'apiVersion': 'v1'})
def test_validate_no_version_attr(self): with self.assertRaises(KubeShiftError): validator.validate({'kind': 'Pod'})
def test_validate_not_dict(self): with self.assertRaises(KubeShiftError): validator.validate('')