123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- # pylint: skip-file
- # flake8: noqa
- class RegistryException(Exception):
- ''' Registry Exception Class '''
- pass
- class RegistryConfig(OpenShiftCLIConfig):
- ''' RegistryConfig is a DTO for the registry. '''
- def __init__(self, rname, namespace, kubeconfig, registry_options):
- super(RegistryConfig, self).__init__(rname, namespace, kubeconfig, registry_options)
- class Registry(OpenShiftCLI):
- ''' Class to wrap the oc command line tools '''
- volume_mount_path = 'spec.template.spec.containers[0].volumeMounts'
- volume_path = 'spec.template.spec.volumes'
- env_path = 'spec.template.spec.containers[0].env'
- def __init__(self,
- registry_config,
- verbose=False):
- ''' Constructor for Registry
- a registry consists of 3 or more parts
- - dc/docker-registry
- - svc/docker-registry
- Parameters:
- :registry_config:
- :verbose:
- '''
- super(Registry, self).__init__(registry_config.namespace, registry_config.kubeconfig, verbose)
- self.version = OCVersion(registry_config.kubeconfig, verbose)
- self.svc_ip = None
- self.portal_ip = None
- self.config = registry_config
- self.verbose = verbose
- self.registry_parts = [{'kind': 'dc', 'name': self.config.name},
- {'kind': 'svc', 'name': self.config.name},
- ]
- self.__prepared_registry = None
- self.volume_mounts = []
- self.volumes = []
- if self.config.config_options['volume_mounts']['value']:
- for volume in self.config.config_options['volume_mounts']['value']:
- volume_info = {'secret_name': volume.get('secret_name', None),
- 'name': volume.get('name', None),
- 'type': volume.get('type', None),
- 'path': volume.get('path', None),
- 'claimName': volume.get('claim_name', None),
- 'claimSize': volume.get('claim_size', None),
- }
- vol, vol_mount = Volume.create_volume_structure(volume_info)
- self.volumes.append(vol)
- self.volume_mounts.append(vol_mount)
- self.dconfig = None
- self.svc = None
- @property
- def deploymentconfig(self):
- ''' deploymentconfig property '''
- return self.dconfig
- @deploymentconfig.setter
- def deploymentconfig(self, config):
- ''' setter for deploymentconfig property '''
- self.dconfig = config
- @property
- def service(self):
- ''' service property '''
- return self.svc
- @service.setter
- def service(self, config):
- ''' setter for service property '''
- self.svc = config
- @property
- def prepared_registry(self):
- ''' prepared_registry property '''
- if not self.__prepared_registry:
- results = self.prepare_registry()
- if not results:
- raise RegistryException('Could not perform registry preparation.')
- self.__prepared_registry = results
- return self.__prepared_registry
- @prepared_registry.setter
- def prepared_registry(self, data):
- ''' setter method for prepared_registry attribute '''
- self.__prepared_registry = data
- def get(self):
- ''' return the self.registry_parts '''
- self.deploymentconfig = None
- self.service = None
- rval = 0
- for part in self.registry_parts:
- result = self._get(part['kind'], rname=part['name'])
- if result['returncode'] == 0 and part['kind'] == 'dc':
- self.deploymentconfig = DeploymentConfig(result['results'][0])
- elif result['returncode'] == 0 and part['kind'] == 'svc':
- self.service = Yedit(content=result['results'][0])
- if result['returncode'] != 0:
- rval = result['returncode']
- return {'returncode': rval, 'deploymentconfig': self.deploymentconfig, 'service': self.service}
- def exists(self):
- '''does the object exist?'''
- self.get()
- if self.deploymentconfig or self.service:
- return True
- return False
- def delete(self, complete=True):
- '''return all pods '''
- parts = []
- for part in self.registry_parts:
- if not complete and part['kind'] == 'svc':
- continue
- parts.append(self._delete(part['kind'], part['name']))
- # Clean up returned results
- rval = 0
- for part in parts:
- # pylint: disable=invalid-sequence-index
- if 'returncode' in part and part['returncode'] != 0:
- rval = part['returncode']
- return {'returncode': rval, 'results': parts}
- def prepare_registry(self):
- ''' prepare a registry for instantiation '''
- options = self.config.to_option_list()
- cmd = ['registry', '-n', self.config.namespace]
- cmd.extend(options)
- cmd.extend(['--dry-run=True', '-o', 'json'])
- results = self.openshift_cmd(cmd, oadm=True, output=True, output_type='json')
- # probably need to parse this
- # pylint thinks results is a string
- # pylint: disable=no-member
- if results['returncode'] != 0 and results['results'].has_key('items'):
- return results
- service = None
- deploymentconfig = None
- # pylint: disable=invalid-sequence-index
- for res in results['results']['items']:
- if res['kind'] == 'DeploymentConfig':
- deploymentconfig = DeploymentConfig(res)
- elif res['kind'] == 'Service':
- service = Service(res)
- # Verify we got a service and a deploymentconfig
- if not service or not deploymentconfig:
- return results
- # results will need to get parsed here and modifications added
- deploymentconfig = DeploymentConfig(self.add_modifications(deploymentconfig))
- # modify service ip
- if self.svc_ip:
- service.put('spec.clusterIP', self.svc_ip)
- if self.portal_ip:
- service.put('spec.portalIP', self.portal_ip)
- # need to create the service and the deploymentconfig
- service_file = Utils.create_tmp_file_from_contents('service', service.yaml_dict)
- deployment_file = Utils.create_tmp_file_from_contents('deploymentconfig', deploymentconfig.yaml_dict)
- return {"service": service,
- "service_file": service_file,
- "service_update": False,
- "deployment": deploymentconfig,
- "deployment_file": deployment_file,
- "deployment_update": False}
- def create(self):
- '''Create a registry'''
- results = []
- for config_file in ['deployment_file', 'service_file']:
- results.append(self._create(self.prepared_registry[config_file]))
- # Clean up returned results
- rval = 0
- for result in results:
- # pylint: disable=invalid-sequence-index
- if 'returncode' in result and result['returncode'] != 0:
- rval = result['returncode']
- return {'returncode': rval, 'results': results}
- def update(self):
- '''run update for the registry. This performs a delete and then create '''
- # Store the current service IP
- if self.service:
- svcip = self.service.get('spec.clusterIP')
- if svcip:
- self.svc_ip = svcip
- portip = self.service.get('spec.portalIP')
- if portip:
- self.portal_ip = portip
- results = []
- if self.prepared_registry['deployment_update']:
- results.append(self._replace(self.prepared_registry['deployment_file']))
- if self.prepared_registry['service_update']:
- results.append(self._replace(self.prepared_registry['service_file']))
- # Clean up returned results
- rval = 0
- for result in results:
- if result['returncode'] != 0:
- rval = result['returncode']
- return {'returncode': rval, 'results': results}
- def add_modifications(self, deploymentconfig):
- ''' update a deployment config with changes '''
- # Currently we know that our deployment of a registry requires a few extra modifications
- # Modification 1
- # we need specific environment variables to be set
- for key, value in self.config.config_options['env_vars']['value'].items():
- if not deploymentconfig.exists_env_key(key):
- deploymentconfig.add_env_value(key, value)
- else:
- deploymentconfig.update_env_var(key, value)
- # Modification 2
- # we need specific volume variables to be set
- for volume in self.volumes:
- deploymentconfig.update_volume(volume)
- for vol_mount in self.volume_mounts:
- deploymentconfig.update_volume_mount(vol_mount)
- # Modification 3
- # Edits
- edit_results = []
- for edit in self.config.config_options['edits'].get('value', []):
- if edit['action'] == 'put':
- edit_results.append(deploymentconfig.put(edit['key'],
- edit['value']))
- if edit['action'] == 'update':
- edit_results.append(deploymentconfig.update(edit['key'],
- edit['value'],
- edit.get('index', None),
- edit.get('curr_value', None)))
- if edit['action'] == 'append':
- edit_results.append(deploymentconfig.append(edit['key'],
- edit['value']))
- if edit_results and not any([res[0] for res in edit_results]):
- return None
- return deploymentconfig.yaml_dict
- def needs_update(self):
- ''' check to see if we need to update '''
- if not self.service or not self.deploymentconfig:
- return True
- exclude_list = ['clusterIP', 'portalIP', 'type', 'protocol']
- if not Utils.check_def_equal(self.prepared_registry['service'].yaml_dict,
- self.service.yaml_dict,
- exclude_list,
- debug=self.verbose):
- self.prepared_registry['service_update'] = True
- exclude_list = ['dnsPolicy',
- 'terminationGracePeriodSeconds',
- 'restartPolicy', 'timeoutSeconds',
- 'livenessProbe', 'readinessProbe',
- 'terminationMessagePath',
- 'securityContext',
- 'imagePullPolicy',
- 'protocol', # ports.portocol: TCP
- 'type', # strategy: {'type': 'rolling'}
- 'defaultMode', # added on secrets
- 'activeDeadlineSeconds', # added in 1.5 for timeouts
- ]
- if not Utils.check_def_equal(self.prepared_registry['deployment'].yaml_dict,
- self.deploymentconfig.yaml_dict,
- exclude_list,
- debug=self.verbose):
- self.prepared_registry['deployment_update'] = True
- return self.prepared_registry['deployment_update'] or self.prepared_registry['service_update'] or False
- # In the future, we would like to break out each ansible state into a function.
- # pylint: disable=too-many-branches,too-many-return-statements
- @staticmethod
- def run_ansible(params, check_mode):
- '''run idempotent ansible code'''
- rconfig = RegistryConfig(params['name'],
- params['namespace'],
- params['kubeconfig'],
- {'images': {'value': params['images'], 'include': True},
- 'latest_images': {'value': params['latest_images'], 'include': True},
- 'labels': {'value': params['labels'], 'include': True},
- 'ports': {'value': ','.join(params['ports']), 'include': True},
- 'replicas': {'value': params['replicas'], 'include': True},
- 'selector': {'value': params['selector'], 'include': True},
- 'service_account': {'value': params['service_account'], 'include': True},
- 'mount_host': {'value': params['mount_host'], 'include': True},
- 'env_vars': {'value': params['env_vars'], 'include': False},
- 'volume_mounts': {'value': params['volume_mounts'], 'include': False},
- 'edits': {'value': params['edits'], 'include': False},
- 'enforce_quota': {'value': params['enforce_quota'], 'include': True},
- 'daemonset': {'value': params['daemonset'], 'include': True},
- 'tls_key': {'value': params['tls_key'], 'include': True},
- 'tls_certificate': {'value': params['tls_certificate'], 'include': True},
- })
- ocregistry = Registry(rconfig, params['debug'])
- api_rval = ocregistry.get()
- state = params['state']
- ########
- # get
- ########
- if state == 'list':
- if api_rval['returncode'] != 0:
- return {'failed': True, 'msg': api_rval}
- return {'changed': False, 'results': api_rval, 'state': state}
- ########
- # Delete
- ########
- if state == 'absent':
- if not ocregistry.exists():
- return {'changed': False, 'state': state}
- if check_mode:
- return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'}
- # Unsure as to why this is angry with the return type.
- # pylint: disable=redefined-variable-type
- api_rval = ocregistry.delete()
- if api_rval['returncode'] != 0:
- return {'failed': True, 'msg': api_rval}
- return {'changed': True, 'results': api_rval, 'state': state}
- if state == 'present':
- ########
- # Create
- ########
- if not ocregistry.exists():
- if check_mode:
- return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'}
- api_rval = ocregistry.create()
- if api_rval['returncode'] != 0:
- return {'failed': True, 'msg': api_rval}
- return {'changed': True, 'results': api_rval, 'state': state}
- ########
- # Update
- ########
- if not params['force'] and not ocregistry.needs_update():
- return {'changed': False, 'state': state}
- if check_mode:
- return {'changed': True, 'msg': 'CHECK_MODE: Would have performed an update.'}
- api_rval = ocregistry.update()
- if api_rval['returncode'] != 0:
- return {'failed': True, 'msg': api_rval}
- return {'changed': True, 'results': api_rval, 'state': state}
- return {'failed': True, 'msg': 'Unknown state passed. %s' % state}
|