From 7c74f8a9050fece2e6c111f1b6aca6a9b89be5eb Mon Sep 17 00:00:00 2001 From: Chico Venancio Date: Thu, 8 Aug 2019 11:18:46 -0300 Subject: [PATCH 1/6] add registry_class config value --- binderhub/app.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/binderhub/app.py b/binderhub/app.py index 5f41f4bc2..ab17e0376 100644 --- a/binderhub/app.py +++ b/binderhub/app.py @@ -20,7 +20,16 @@ import tornado.log from tornado.log import app_log import tornado.web -from traitlets import Unicode, Integer, Bool, Dict, validate, TraitError, default +from traitlets import ( + Unicode, + Integer, + Bool, + Dict, + validate, + TraitError, + default, + Type, +) from traitlets.config import Application from jupyterhub.services.auth import HubOAuthCallbackHandler @@ -195,6 +204,14 @@ def _valid_badge_base_url(self, proposal): config=True, ) + registry_class = Type( + DockerRegistry, + help=""" + Registry class implementation, change to define your own + """, + config=True + ) + use_registry = Bool( True, help=""" @@ -511,7 +528,7 @@ def initialize(self, *args, **kwargs): ]) jinja_env = Environment(loader=loader, **jinja_options) if self.use_registry and self.builder_required: - registry = DockerRegistry(parent=self) + registry = self.registry_class(parent=self) else: registry = None From 81f92ca6695c9cc23b6e8212bc0319a2a0d5962d Mon Sep 17 00:00:00 2001 From: Chico Venancio Date: Sat, 10 Aug 2019 11:02:22 -0300 Subject: [PATCH 2/6] add boto3 requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index f34cb78aa..9370884c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ python-json-logger jupyterhub jsonschema pycurl +boto3 From 7d6f7756438692b6cec753d8bc7c8cba5c8bf941 Mon Sep 17 00:00:00 2001 From: Chico Venancio Date: Tue, 27 Aug 2019 13:04:03 -0300 Subject: [PATCH 3/6] add AWSElasticContainerRegistry class --- binderhub/registry.py | 63 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/binderhub/registry.py b/binderhub/registry.py index 8891041b2..fb5439bfc 100644 --- a/binderhub/registry.py +++ b/binderhub/registry.py @@ -6,10 +6,13 @@ import os from urllib.parse import urlparse +import boto3 +import kubernetes.client +import kubernetes.config from tornado import gen, httpclient from tornado.httputil import url_concat +from traitlets import default, Dict, Unicode, Any from traitlets.config import LoggingConfigurable -from traitlets import Dict, Unicode, default DEFAULT_DOCKER_REGISTRY_URL = "https://registry.hub.docker.com" DEFAULT_DOCKER_AUTH_URL = "https://index.docker.io/v1" @@ -224,3 +227,61 @@ def get_image_manifest(self, image, tag): raise else: return json.loads(resp.body.decode("utf-8")) + + +class AWSElasticContainerRegistry(DockerRegistry): + aws_region = Unicode( + config=True, + help=""" + AWS region for ECR service + """, + ) + + ecr_client = Any() + + @default("ecr_client") + def _get_ecr_client(self): + return boto3.client("ecr", region_name=self.aws_region) + + username = "AWS" + + kubernetes.config.load_incluster_config() + kube_client = kubernetes.client.CoreV1Api() + + def _get_ecr_auth(self): + return self.ecr_client.get_authorization_token()["authorizationData"][0] + + @default("url") + def _default_url(self): + return self._get_ecr_auth()["proxyEndpoint"] + + def _patch_docker_config_secret(self, auth): + """Patch binder-push-secret""" + secret_data = {"auths": {self.url: {"auth": auth["authorizationToken"]}}} + secret_data = base64.b64encode(json.dumps(secret_data).encode("utf8")).decode( + "utf8" + ) + with open("/var/run/secrets/kubernetes.io/serviceaccount/namespace") as f: + namespace = f.read() + self.kube_client.patch_namespaced_secret( + "binder-push-secret", namespace, {"data": {"config.json": secret_data}} + ) + + @default("password") + def _get_ecr_pawssord(self): + """Get ecr password""" + auth = self._get_ecr_auth() + self.password_expires = auth["expiresAt"] + self._patch_docker_config_secret(auth) + return base64.b64decode(auth['authorizationToken']).decode("utf-8").split(':')[1] + + async def get_image_manifest(self, image, tag): + try: + repo_name = image.split("/", 1)[1] + self.ecr_client.create_repository(repositoryName=repo_name) + self.log.info("Creating ECR repo {}".format(repo_name)) + except self.ecr_client.exceptions.RepositoryAlreadyExistsException: + self.log.info("ECR repo {} already exists".format(repo_name)) + # TODO: check for expiration before reseting password + self.password = self._get_ecr_pawssord() + return await super().get_image_manifest(repo_name, tag) From b0b438c96d1d67751e1afb8fc4570a39d6d2560f Mon Sep 17 00:00:00 2001 From: Chico Venancio Date: Tue, 27 Aug 2019 15:52:31 -0300 Subject: [PATCH 4/6] rbac.yaml: allow manipulation of secrets --- helm-chart/binderhub/templates/rbac.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helm-chart/binderhub/templates/rbac.yaml b/helm-chart/binderhub/templates/rbac.yaml index 210e62df0..d249d4e09 100644 --- a/helm-chart/binderhub/templates/rbac.yaml +++ b/helm-chart/binderhub/templates/rbac.yaml @@ -14,6 +14,10 @@ rules: - apiGroups: [""] resources: ["pods/log"] verbs: ["get"] +- apiGroups: + - "" + resources: ["secrets"] + verbs: ["get", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 From 6cb5b6a54706de21abd48c70e428e2606b66f76d Mon Sep 17 00:00:00 2001 From: Chico Venancio Date: Tue, 27 Aug 2019 18:03:45 -0300 Subject: [PATCH 5/6] doc-requirements: add boto3 --- doc/doc-requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/doc-requirements.txt b/doc/doc-requirements.txt index 96b78c0a8..0763e3dd1 100644 --- a/doc/doc-requirements.txt +++ b/doc/doc-requirements.txt @@ -20,3 +20,4 @@ python-json-logger jupyterhub jsonschema #pycurl Do not install for docs as it breaks the RTD build. Its primary use is for mocks in testing . +boto3 From e25e59763ef2e33fb829e2feb36329721b28e015 Mon Sep 17 00:00:00 2001 From: Chico Venancio Date: Fri, 8 Nov 2019 18:52:21 -0300 Subject: [PATCH 6/6] Fix syntax --- binderhub/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/binderhub/app.py b/binderhub/app.py index 81a7a89ac..4a9cd4640 100644 --- a/binderhub/app.py +++ b/binderhub/app.py @@ -211,6 +211,7 @@ def _valid_badge_base_url(self, proposal): Registry class implementation, change to define your own """, config=True + ) sticky_builds = Bool( False,