@@ -11,6 +11,7 @@ import subprocess
1111import sys
1212import time
1313import unittest
14+ import base64
1415
1516REMOTE_BASE_IMAGE_NAME = "public.ecr.aws/w0m4q9l7/github-awslabs-smithy-rs-ci"
1617LOCAL_BASE_IMAGE_NAME = "smithy-rs-base-image"
@@ -41,7 +42,8 @@ class Platform(Enum):
4142
4243# Script context
4344class Context :
44- def __init__ (self , start_path , script_path , tools_path , user_id , image_tag , allow_local_build , github_actions ):
45+ def __init__ (self , start_path , script_path , tools_path , user_id , image_tag , allow_local_build , github_actions ,
46+ encrypted_docker_password , docker_passphrase ):
4547 self .start_path = start_path
4648 self .script_path = script_path
4749 self .tools_path = tools_path
@@ -50,6 +52,8 @@ class Context:
5052 self .image_tag = image_tag
5153 self .allow_local_build = allow_local_build
5254 self .github_actions = github_actions
55+ self .encrypted_docker_password = encrypted_docker_password
56+ self .docker_passphrase = docker_passphrase
5357
5458 @staticmethod
5559 def default ():
@@ -60,14 +64,19 @@ class Context:
6064 image_tag = get_cmd_output ("./docker-image-hash" , cwd = script_path )[1 ]
6165 allow_local_build = os .getenv ("ALLOW_LOCAL_BUILD" ) != "false"
6266 github_actions = os .getenv ("GITHUB_ACTIONS" ) == "true"
67+ encrypted_docker_password = os .getenv ("ENCRYPTED_DOCKER_PASSWORD" )
68+ docker_passphrase = os .getenv ("DOCKER_LOGIN_TOKEN_PASSPHRASE" )
69+
6370 print (f"Start path: { start_path } " )
6471 print (f"Script path: { script_path } " )
6572 print (f"Tools path: { tools_path } " )
6673 print (f"User ID: { user_id } " )
6774 print (f"Required base image tag: { image_tag } " )
6875 print (f"Allow local build: { allow_local_build } " )
6976 print (f"Running in GitHub Actions: { github_actions } " )
70- return Context (start_path , script_path , tools_path , user_id , image_tag , allow_local_build , github_actions )
77+ return Context (start_path = start_path , script_path = script_path , tools_path = tools_path , user_id = user_id ,
78+ image_tag = image_tag , allow_local_build = allow_local_build , github_actions = github_actions ,
79+ encrypted_docker_password = encrypted_docker_password , docker_passphrase = docker_passphrase )
7180
7281
7382def output_contains_any (stdout , stderr , messages ):
@@ -76,7 +85,6 @@ def output_contains_any(stdout, stderr, messages):
7685 return True
7786 return False
7887
79-
8088# Mockable shell commands
8189class Shell :
8290 # Returns the platform that this script is running on
@@ -91,6 +99,9 @@ class Shell:
9199 (status , _ , _ ) = get_cmd_output (f"docker inspect \" { image_name } :{ image_tag } \" " , check = False )
92100 return status == 0
93101
102+ def docker_login (self , password ):
103+ get_cmd_output ("docker login --username AWS --password-stdin public.ecr.aws" , input = password .encode ('utf-8' ))
104+
94105 # Pulls the requested `image_name` with `image_tag`. Returns `DockerPullResult`.
95106 def docker_pull (self , image_name , image_tag ):
96107 (status , stdout , stderr ) = get_cmd_output (f"docker pull \" { image_name } :{ image_tag } \" " , check = False )
@@ -102,7 +113,7 @@ class Shell:
102113 print ("-------------------" )
103114
104115 not_found_messages = ["not found: manifest unknown" ]
105- throttle_messages = ["toomanyrequests: Rate exceeded" , "toomanyrequests: Data limit exceeded " ]
116+ throttle_messages = ["toomanyrequests:" ]
106117 retryable_messages = ["net/http: TLS handshake timeout" ]
107118 if status == 0 :
108119 return DockerPullResult .SUCCESS
@@ -160,17 +171,39 @@ def run(command, cwd=None):
160171
161172
162173# Returns (status, output) from a shell command
163- def get_cmd_output (command , cwd = None , check = True ):
174+ def get_cmd_output (command , cwd = None , check = True , ** kwargs ):
175+ if isinstance (command , str ):
176+ command = shlex .split (command )
177+
164178 result = subprocess .run (
165- shlex . split ( command ) ,
179+ command ,
166180 capture_output = True ,
167- check = check ,
168- cwd = cwd
181+ check = False ,
182+ cwd = cwd ,
183+ ** kwargs
169184 )
170- return (result .returncode , result .stdout .decode ("utf-8" ).strip (), result .stderr .decode ("utf-8" ).strip ())
185+ stdout = result .stdout .decode ("utf-8" ).strip ()
186+ stderr = result .stderr .decode ("utf-8" ).strip ()
187+ if check and result .returncode != 0 :
188+ raise Exception (f"failed to run '{ command } .\n { stdout } \n { stderr } " )
189+
190+ return result .returncode , stdout , stderr
191+
192+
193+ def decrypt_and_login (shell , secret , passphrase ):
194+ decoded = base64 .b64decode (secret , validate = True )
195+ if not passphrase :
196+ raise Exception ("a secret was set but no passphrase was set (or it was empty)" )
197+ (code , password , err ) = get_cmd_output (
198+ ["gpg" , "--decrypt" , "--batch" , "--quiet" , "--passphrase" , passphrase , "--output" , "-" ],
199+ input = decoded )
200+ shell .docker_login (password )
201+ print ("Docker login success!" )
171202
172203
173204def acquire_build_image (context = Context .default (), shell = Shell ()):
205+ if context .encrypted_docker_password is not None :
206+ decrypt_and_login (shell , context .encrypted_docker_password , context .docker_passphrase )
174207 # If the image doesn't already exist locally, then look remotely
175208 if not shell .docker_image_exists_locally (LOCAL_BASE_IMAGE_NAME , context .image_tag ):
176209 announce ("Base image not found locally." )
@@ -211,15 +244,18 @@ def acquire_build_image(context=Context.default(), shell=Shell()):
211244
212245
213246class SelfTest (unittest .TestCase ):
214- def test_context (self , allow_local_build = False , github_actions = False ):
247+ def test_context (self , github_actions = False , allow_local_build = False , encrypted_docker_password = None ,
248+ docker_passphrase = None ):
215249 return Context (
216250 start_path = "/tmp/test/start-path" ,
217251 script_path = "/tmp/test/script-path" ,
218252 tools_path = "/tmp/test/tools-path" ,
219253 user_id = "123" ,
220254 image_tag = "someimagetag" ,
255+ encrypted_docker_password = encrypted_docker_password ,
256+ docker_passphrase = docker_passphrase ,
257+ github_actions = github_actions ,
221258 allow_local_build = allow_local_build ,
222- github_actions = github_actions
223259 )
224260
225261 def mock_shell (self ):
@@ -231,6 +267,7 @@ class SelfTest(unittest.TestCase):
231267 shell .docker_pull = MagicMock ()
232268 shell .docker_save = MagicMock ()
233269 shell .docker_tag = MagicMock ()
270+ shell .docker_login = MagicMock ()
234271 return shell
235272
236273 def test_retry_architecture_mismatch (self ):
@@ -247,6 +284,13 @@ class SelfTest(unittest.TestCase):
247284 )
248285 )
249286
287+ def test_docker_login (self ):
288+ shell = self .mock_shell ()
289+ acquire_build_image (self .test_context (
290+ encrypted_docker_password = "jA0ECQMCvYU/JxsX3g/70j0BxbLLW8QaFWWb/DqY9gPhTuEN/xdYVxaoDnV6Fha+lAWdT7xN0qZr5DHPBalLfVvvM1SEXRBI8qnfXyGI" ,
291+ docker_passphrase = "secret" ), shell )
292+ shell .docker_login .assert_called_with ("payload" )
293+
250294 def test_retry_immediate_success (self ):
251295 shell = self .mock_shell ()
252296 shell .docker_pull .side_effect = [DockerPullResult .SUCCESS ]
@@ -374,7 +418,7 @@ class SelfTest(unittest.TestCase):
374418
375419 shell .docker_image_exists_locally .assert_called_once ()
376420 shell .docker_tag .assert_called_with (LOCAL_BASE_IMAGE_NAME , "someimagetag" , LOCAL_BASE_IMAGE_NAME , LOCAL_TAG )
377- shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path" )
421+ shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path/ci-build " )
378422
379423 # When:
380424 # - the base image doesn't exist locally
@@ -391,10 +435,10 @@ class SelfTest(unittest.TestCase):
391435
392436 self .assertEqual (0 , acquire_build_image (context , shell ))
393437 shell .docker_image_exists_locally .assert_called_once ()
394- shell .docker_build_base_image .assert_called_with ("someimagetag" , "/tmp/test/tools-path" )
438+ shell .docker_build_base_image .assert_called_with ("someimagetag" , "/tmp/test/tools-path/ci-build " )
395439 shell .docker_save .assert_not_called ()
396440 shell .docker_tag .assert_called_with (LOCAL_BASE_IMAGE_NAME , "someimagetag" , LOCAL_BASE_IMAGE_NAME , LOCAL_TAG )
397- shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path" )
441+ shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path/ci-build " )
398442
399443 # When:
400444 # - the base image doesn't exist locally
@@ -411,10 +455,10 @@ class SelfTest(unittest.TestCase):
411455
412456 self .assertEqual (0 , acquire_build_image (context , shell ))
413457 shell .docker_image_exists_locally .assert_called_once ()
414- shell .docker_build_base_image .assert_called_with ("someimagetag" , "/tmp/test/tools-path" )
458+ shell .docker_build_base_image .assert_called_with ("someimagetag" , "/tmp/test/tools-path/ci-build " )
415459 shell .docker_save .assert_not_called ()
416460 shell .docker_tag .assert_called_with (LOCAL_BASE_IMAGE_NAME , "someimagetag" , LOCAL_BASE_IMAGE_NAME , LOCAL_TAG )
417- shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path" )
461+ shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path/ci-build " )
418462
419463 # When:
420464 # - the base image doesn't exist locally
@@ -431,14 +475,14 @@ class SelfTest(unittest.TestCase):
431475
432476 self .assertEqual (0 , acquire_build_image (context , shell ))
433477 shell .docker_image_exists_locally .assert_called_once ()
434- shell .docker_build_base_image .assert_called_with ("someimagetag" , "/tmp/test/tools-path" )
478+ shell .docker_build_base_image .assert_called_with ("someimagetag" , "/tmp/test/tools-path/ci-build " )
435479 shell .docker_save .assert_called_with (
436480 LOCAL_BASE_IMAGE_NAME ,
437481 "someimagetag" ,
438482 "/tmp/test/start-path/smithy-rs-base-image"
439483 )
440484 shell .docker_tag .assert_called_with (LOCAL_BASE_IMAGE_NAME , "someimagetag" , LOCAL_BASE_IMAGE_NAME , LOCAL_TAG )
441- shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path" )
485+ shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path/ci-build " )
442486
443487 # When:
444488 # - the base image doesn't exist locally
@@ -478,7 +522,7 @@ class SelfTest(unittest.TestCase):
478522 call (REMOTE_BASE_IMAGE_NAME , "someimagetag" , LOCAL_BASE_IMAGE_NAME , "someimagetag" ),
479523 call (LOCAL_BASE_IMAGE_NAME , "someimagetag" , LOCAL_BASE_IMAGE_NAME , LOCAL_TAG )
480524 ])
481- shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path" )
525+ shell .docker_build_build_image .assert_called_with ("123" , "/tmp/test/tools-path/ci-build " )
482526
483527
484528def main ():
0 commit comments