diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f10f5b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +__pycache__/ + +### Visual Studio Code ### +.vscode +*.code-workspace diff --git a/iam_mapping.py b/iam_mapping.py index 4f32cd8..c51aeff 100644 --- a/iam_mapping.py +++ b/iam_mapping.py @@ -13,30 +13,23 @@ logger.info("Could not load kubeconfig. Error is : {}".format(e)) API = client.CoreV1Api() +custom_objects_API = client.CustomObjectsApi() - +@kopf.on.update('iamauthenticator.k8s.aws', 'v1alpha1', 'iamidentitymappings') @kopf.on.create('iamauthenticator.k8s.aws', 'v1alpha1', 'iamidentitymappings') -def create_mapping(body: dict, meta: dict, spec: dict, **kwargs): - logger.info('Adding mapping for user {} as {} to {}'.format( - spec['userarn'], spec['username'], spec['groups'])) - - cm = API.read_namespaced_config_map('aws-auth', 'kube-system') - users = get_user_mapping(cm) - updated_mapping = ensure_user(spec, users) - apply_mapping(cm, updated_mapping) - +async def create_mapping(body: dict, meta: dict, spec: dict, event: str, diff: set, **kwargs): + # Do nothing when we have no diff + if len(diff) < 1: + return dict() -@kopf.on.update('iamauthenticator.k8s.aws', 'v1alpha1', 'iamidentitymappings') -def update_mapping(body: dict, meta: dict, spec: dict, **kwargs): - logger.info('Update mapping for user {} as {} to {}'.format( - spec['userarn'], spec['username'], spec['groups'])) + logger.info('{} mapping for user {} as {} to {}'.format( + event.title(), spec['userarn'], spec['username'], spec['groups'])) cm = API.read_namespaced_config_map('aws-auth', 'kube-system') users = get_user_mapping(cm) updated_mapping = ensure_user(spec, users) apply_mapping(cm, updated_mapping) - @kopf.on.delete('iamauthenticator.k8s.aws', 'v1alpha1', 'iamidentitymappings') def delete_mapping(body: dict, meta: dict, spec: dict, **kwargs): logger.info('Delete mapping for user {} as {} to {}'.format( @@ -47,19 +40,28 @@ def delete_mapping(body: dict, meta: dict, spec: dict, **kwargs): updated_mapping = delete_user(spec, users) apply_mapping(cm, updated_mapping) +def full_synchronize(): + # Get Kubernetes' objects + cm = API.read_namespaced_config_map('aws-auth', 'kube-system') + identity_mappings = custom_objects_API.list_cluster_custom_object('iamauthenticator.k8s.aws', 'v1alpha1', 'iamidentitymappings') + + users = get_user_mapping(cm) + users = users if type(users) == "list" else list() + for im in identity_mappings["items"]: + users = ensure_user(im["spec"], users) + apply_mapping(cm, users) def get_user_mapping(cm): - if 'mapUsers' in cm.data: + try: return yaml.safe_load(cm.data['mapUsers']) - else: + except: return [] def apply_mapping(existing_cm, user_mapping): - existing_cm.data["mapUsers"] = yaml.dump(user_mapping) + existing_cm.data["mapUsers"] = yaml.safe_dump(user_mapping) API.patch_namespaced_config_map('aws-auth', 'kube-system', existing_cm) - def ensure_user(user, user_list): for i, existing_user in enumerate(user_list): # Handle existing user @@ -81,3 +83,6 @@ def delete_user(user, user_list): raise Exception( "Want to delete {}, but not found".format(user['username'])) return user_list + +# Do a full synchronization at the start +full_synchronize() diff --git a/kubernetes/test/test-iam.yaml b/kubernetes/test/test-iam.yaml index 4dd07ba..c46ba39 100644 --- a/kubernetes/test/test-iam.yaml +++ b/kubernetes/test/test-iam.yaml @@ -3,7 +3,7 @@ kind: IAMIdentityMapping metadata: name: johndoe spec: - arn: arn:aws:iam::123456789:user/johndoe + userarn: arn:aws:iam::123456789:user/johndoe username: johndoe groups: - system:masters