def validate(state, config, *vargs): allowed = True reason = "Namespace accepted" ignore_namespaces = config.get('ignore_namespaces', ['default', 'kube-system']) metadata = state.get('object', {}).get('metadata', {}) annotations = metadata.get('annotations', {}) name = metadata.get('name') if ignore_namespaces and name in ignore_namespaces: log("Namespace ignored:", name) return make_response(True) owner = annotations.get('getup.io/owner') or annotations.get( 'openshift.io/requester') if owner is None: allowed = False reason = "Missing ownership annotation: either \"getup.io/owner\" or \"openshift.io/requester\" must be supplied." elif config.get('username_type') == 'email' and '@' not in owner: allowed = False reason = "Invalid annotation: owner must be an email address: %s" % owner log("{}: {}".format(reason, name)) return make_response(allowed, reason)
def reconcile(state, config, *args): metadata = state.get('object', {}).get('metadata', {}) finalizers = metadata.get('finalizers', []) if not finalizers: return if 'orphan' in finalizers: log("Removing finalizer from Job {}/{}: 'orphan'".format( metadata['namespace'], metadata['name'])) finalizers = [f for f in finalizers if f != 'orphan'] state['object']['metadata']['finalizers'] = finalizers return state
def load_config(controller_name, controller_type): config = {} config_file = os.environ.get( "CONTROLLER_CONFIG_{}".format(controller_name.upper()), "/config/{}-{}.yaml".format(controller_name, controller_type)) try: with open(config_file, 'r') as cf: config = yaml.safe_load(cf) except FileNotFoundError as ex: pass except Exception as ex: log("Error loading config[%s:%s]: %s: %s" % (controller_name, controller_type, config_file, str(ex))) sys.exit(1) return config
def reconcile(state, config, *args): ignore_namespaces = config.get('ignore_namespaces', ['default', 'kube-system']) metadata = state.get('object', {}).get('metadata', {}) annotations = metadata.get('annotations', {}) name = metadata.get('name') uid = metadata.get('uid') if ignore_namespaces and name in ignore_namespaces: log("Namespace ignored:", name) return owner = annotations.get('getup.io/owner') or annotations.get( 'openshift.io/requester') if owner is None: log("Namespace %s is missing ownership annotation: either \"getup.io/owner\" or \"openshift.io/requester\" must be supplied." % name) return if config.get('username_type') == 'email' and '@' not in owner: log("Invalid annotation: owner must be an email address: %s" % owner) return log('Project "%s" owner should be "%s"' % (name, owner)) project_list_url = os.environ["GETUP_API_URL"].strip( '/') + '/api/v1/project/' project_url = project_list_url + name + '/' auth = (os.environ["GETUP_API_USERNAME"], os.environ["GETUP_API_PASSWORD"]) res = requests.get(project_url, auth=auth, params={"status": "active"}, allow_redirects=True) project = res.json() if res.status_code == 200: # project already exists on DB. if project['owner'] == owner: log('Project "%s" already owned by "%s".' % (name, owner)) else: log('WARNING: Conflict: Project "%s" owned by "%s", but namespace says it should be owned by "%s". Please fix ambiguity.' % (name, project['owner'], owner)) if project['uid'] != uid: # namespace was recreated with same name, must finish old one and create new Billing Project res = requests.patch(project_url, auth=auth, allow_redirects=True, params={ 'sync': True, 'status': 'active' }, data=json.dumps({'status': 'finished'}), headers={'content-type': 'application/json'}) if not res.ok: log('Project "%s" status change failed: %s %s' % (name, res.status_code, res.text)) else: log('Project "%s" status changed to "finished"' % name) elif res.status_code == 404: # project do not exists on DB, lets create one data = {'owner': owner, 'name': name, 'family': 'default'} log('Will create project with "%s"' % data) res = requests.post(project_list_url, auth=auth, data=data, params={"sync": True}) if not res.ok: log('Error creating Project:', res.text) else: log('Project "%s" created successfully' % name)
def reconcile(state, config, *args): metadata = state.get('object', {}).get('metadata', {}) annotations = metadata.get('annotations', {}) labels = metadata.get('labels', {}) name = metadata.get('name') uid = metadata.get('uid') taints = state.get('object', {}).get('spec', {}).get('taints', []) # copy label to annotation for prefix_name, value in labels.items(): if not prefix_name.startswith('annotation.getup.io/'): continue _, name = prefix_name.split('/') if not name: continue log('Added annotation: {}={}'.format(name, value)) annotations[name] = value # copy annotation to label for prefix_name, value in annotations.items(): if not prefix_name.startswith('label.getup.io/'): continue _, name = prefix_name.split('/') if not name: continue log('Added label: {}={}'.format(name, value)) labels[name] = value # taint from labels or annotations for prefix_name, value in itertools.chain(labels.items(), annotations.items()): if not prefix_name.startswith('taint.getup.io/'): continue _, name = prefix_name.split('/') if not name: continue try: taint = json.loads(value) except json.decoder.JSONDecodeError: tmp = value.split(':') taint = { 'key': name, 'value': tmp[0], 'effect': tmp[1] if len(tmp) > 1 else os.environ.get( 'DEFAULT_NODE_TAINT_EFFECT', 'NoSchedule'), } found = False for t in taints: if t['effect'] == taint['effect'] and t['key'] == taint[ 'key'] and t['value'] == taint['value']: found = True break if found: continue log('Added taint: {}'.format(taint)) taints.append(taint) state['object']['spec']['taints'] = taints state['object']['metadata']['annotations'] = annotations state['object']['metadata']['labels'] = labels return state
config = yaml.safe_load(cf) except FileNotFoundError as ex: pass except Exception as ex: log("Error loading config[%s:%s]: %s: %s" % (controller_name, controller_type, config_file, str(ex))) sys.exit(1) return config if __name__ == "__main__": try: controller_name, controller_type, *args = *sys.argv[1:3], *sys.argv[3:] except ValueError as ex: log("Invalid parameters: Usage: %s [controller-name] [controller-type]" % sys.argv[0]) log("Invalid parameters: Got %s" % sys.argv) sys.exit(1) try: controller = importlib.import_module( 'controllers.{}'.format(controller_name)) func = getattr(controller, controller_type) config = load_config(controller_name, controller_type) state = json.load(sys.stdin) result = func(state, config, *args) if result is not None: print(json.dumps(result))