11import backoff
2+ import base64
23from collections import OrderedDict
34from datetime import datetime
5+ from docker .auth import auth as docker_auth
46import functools
7+ import json
58import logging
69import random
710import re
@@ -398,6 +401,7 @@ def _scale_pods(self, scale_types):
398401 version = "v{}" .format (release .version )
399402 image = release .image
400403 envs = self ._build_env_vars (release .build .type , version , image , release .config .values )
404+ registry = release .config .registry
401405
402406 # see if the app config has deploy batch preference, otherwise use global
403407 batches = release .config .values .get ('DEIS_DEPLOY_BATCHES' , settings .DEIS_DEPLOY_BATCHES )
@@ -408,6 +412,9 @@ def _scale_pods(self, scale_types):
408412 # get application level pod termination grace period
409413 pod_termination_grace_period_seconds = release .config .values .get ('KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS' , settings .KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS ) # noqa
410414
415+ # create image pull secret if needed
416+ image_pull_secret_name = self .image_pull_secret (self .id , registry , image )
417+
411418 tasks = []
412419 for scale_type , replicas in scale_types .items ():
413420 # only web / cmd are routable
@@ -427,7 +434,7 @@ def _scale_pods(self, scale_types):
427434 'cpu' : release .config .cpu ,
428435 'tags' : release .config .tags ,
429436 'envs' : envs ,
430- 'registry' : release . config . registry ,
437+ 'registry' : registry ,
431438 'version' : version ,
432439 'replicas' : replicas ,
433440 'app_type' : scale_type ,
@@ -437,6 +444,7 @@ def _scale_pods(self, scale_types):
437444 'deploy_batches' : batches ,
438445 'deploy_timeout' : deploy_timeout ,
439446 'pod_termination_grace_period_seconds' : pod_termination_grace_period_seconds ,
447+ 'image_pull_secret_name' : image_pull_secret_name ,
440448 }
441449
442450 # gather all proc types to be deployed
@@ -482,6 +490,12 @@ def deploy(self, release, force_deploy=False):
482490 self .structure = self ._default_structure (release )
483491 self .save ()
484492
493+ image = release .image
494+ registry = release .config .registry
495+ version = "v{}" .format (release .version )
496+ envs = self ._build_env_vars (release .build .type , version , image , release .config .values )
497+ tags = release .config .tags
498+
485499 # see if the app config has deploy batch preference, otherwise use global
486500 batches = release .config .values .get ('DEIS_DEPLOY_BATCHES' , settings .DEIS_DEPLOY_BATCHES )
487501
@@ -493,12 +507,11 @@ def deploy(self, release, force_deploy=False):
493507 # get application level pod termination grace period
494508 pod_termination_grace_period_seconds = release .config .values .get ('KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS' , settings .KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS ) # noqa
495509
510+ # create image pull secret if needed
511+ image_pull_secret_name = self .image_pull_secret (self .id , registry , image )
512+
496513 # deploy application to k8s. Also handles initial scaling
497514 deploys = {}
498- image = release .image
499- version = "v{}" .format (release .version )
500- envs = self ._build_env_vars (release .build .type , version , image , release .config .values )
501- tags = release .config .tags
502515
503516 for scale_type , replicas in self .structure .items ():
504517 # only web / cmd are routable
@@ -518,7 +531,7 @@ def deploy(self, release, force_deploy=False):
518531 'cpu' : release .config .cpu ,
519532 'tags' : tags ,
520533 'envs' : envs ,
521- 'registry' : release . config . registry ,
534+ 'registry' : registry ,
522535 'replicas' : replicas ,
523536 'version' : version ,
524537 'app_type' : scale_type ,
@@ -530,6 +543,7 @@ def deploy(self, release, force_deploy=False):
530543 'deployment_history_limit' : deployment_history ,
531544 'release_summary' : release .summary ,
532545 'pod_termination_grace_period_seconds' : pod_termination_grace_period_seconds ,
546+ 'image_pull_secret_name' : image_pull_secret_name ,
533547 }
534548
535549 # Sort deploys so routable comes first
@@ -733,28 +747,34 @@ def pod_name(size=5, chars=string.ascii_lowercase + string.digits):
733747 if release .build is None :
734748 raise DeisException ('No build associated with this release to run this command' )
735749
750+ image = release .image
751+ registry = release .config .registry
752+ version = "v{}" .format (release .version )
753+ envs = self ._build_env_vars (release .build .type , version , image , release .config .values )
754+
736755 # see if the app config has deploy timeout preference, otherwise use global
737756 deploy_timeout = release .config .values .get ('DEIS_DEPLOY_TIMEOUT' , settings .DEIS_DEPLOY_TIMEOUT ) # noqa
738757
739758 # get application level pod termination grace period
740759 pod_termination_grace_period_seconds = release .config .values .get ('KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS' , settings .KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS ) # noqa
741760
761+ # create image pull secret if needed
762+ image_pull_secret_name = self .image_pull_secret (self .id , registry , image )
763+
742764 name = self ._get_job_id (scale_type ) + '-' + pod_name ()
743765 self .log ("{} on {} runs '{}'" .format (user .username , name , command ))
744766
745- image = release .image
746- version = "v{}" .format (release .version )
747- envs = self ._build_env_vars (release .build .type , version , image , release .config .values )
748767 kwargs = {
749768 'memory' : release .config .memory ,
750769 'cpu' : release .config .cpu ,
751770 'tags' : release .config .tags ,
752771 'envs' : envs ,
753- 'registry' : release . config . registry ,
772+ 'registry' : registry ,
754773 'version' : version ,
755774 'build_type' : release .build .type ,
756775 'deploy_timeout' : deploy_timeout ,
757776 'pod_termination_grace_period_seconds' : pod_termination_grace_period_seconds ,
777+ 'image_pull_secret_name' : image_pull_secret_name ,
758778 }
759779
760780 try :
@@ -962,3 +982,70 @@ def autoscale(self, proc_type, autoscale):
962982 else :
963983 # let the user know about any other errors
964984 raise ServiceUnavailable (str (e )) from e
985+
986+ def image_pull_secret (self , namespace , registry , image ):
987+ """
988+ Take registry information and set as an imagePullSecret for an RC / Deployment
989+ http://kubernetes.io/docs/user-guide/images/#specifying-imagepullsecrets-on-a-pod
990+ """
991+ docker_config , name , create = self ._get_private_registry_config (image , registry )
992+ if create is None :
993+ return
994+ elif create :
995+ data = {'.dockerconfigjson' : docker_config }
996+ try :
997+ self ._scheduler .secret .get (namespace , name )
998+ except KubeHTTPException :
999+ self ._scheduler .secret .create (
1000+ namespace ,
1001+ name ,
1002+ data ,
1003+ secret_type = 'kubernetes.io/dockerconfigjson'
1004+ )
1005+ else :
1006+ self ._scheduler .secret .update (
1007+ namespace ,
1008+ name ,
1009+ data ,
1010+ secret_type = 'kubernetes.io/dockerconfigjson'
1011+ )
1012+
1013+ return name
1014+
1015+ def _get_private_registry_config (self , image , registry = None ):
1016+ name = settings .REGISTRY_SECRET_PREFIX
1017+ if registry :
1018+ # try to get the hostname information
1019+ hostname = registry .get ('hostname' , None )
1020+ if not hostname :
1021+ hostname , _ = docker_auth .split_repo_name (image )
1022+
1023+ if hostname == docker_auth .INDEX_NAME :
1024+ hostname = 'https://index.docker.io/v1/'
1025+
1026+ username = registry .get ('username' )
1027+ password = registry .get ('password' )
1028+ elif settings .REGISTRY_LOCATION == 'off-cluster' :
1029+ secret = self ._scheduler .secret .get ('deis' , 'registry-secret' ).json ()
1030+ username = secret ['data' ]['username' ]
1031+ password = secret ['data' ]['password' ]
1032+ hostname = secret ['data' ]['hostname' ]
1033+ if hostname == '' :
1034+ hostname = 'https://index.docker.io/v1/'
1035+ name = name + '-' + settings .REGISTRY_LOCATION
1036+ elif settings .REGISTRY_LOCATION in ['ecr' , 'gcr' ]:
1037+ return None , name + '-' + settings .REGISTRY_LOCATION , False
1038+ else :
1039+ return None , None , None
1040+
1041+ # create / update private registry secret
1042+ auth = bytes ('{}:{}' .format (username , password ), 'UTF-8' )
1043+ # value has to be a base64 encoded JSON
1044+ docker_config = json .dumps ({
1045+ 'auths' : {
1046+ hostname : {
1047+ 'auth' : base64 .b64encode (auth ).decode (encoding = 'UTF-8' )
1048+ }
1049+ }
1050+ })
1051+ return docker_config , name , True
0 commit comments