Search code examples
pythonapidockertags

Docker Python API - Tagging Containers


I'm using Docker with AWS ECR repo. One of the steps they instruct you to do is to run "docker tag" to tag a built image with a tag that includes a "fully-ish qualified" location of where the image is going to be stored in ECR.

I was working on migrating a script I had to Python API (instead of doing shell calls to the docker client). I'm unable to find the equivalent of "docker tag" in the API docs at https://docker-py.readthedocs.io/en/stable/images.html.

Can somebody point me in the right direction?


Solution

  • For those of you using ECR/ECS in AWS, here is an example of how you go about this.

    Amazon provides instructions like this in ECR to push your images:

    aws ecr get-login --no-include-email --region us-west-2
    docker build -t myproj .
    docker tag calclab:latest XXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/myproj:latest
    docker push XXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/myproj:latest
    

    Here is the rough equivalent using the Docker Python API and Boto (AWS's Python library). It includes tagging the image twice in ECR, so that I can track each image's version number while tracking what the latest is (so my ECS Task can, by default, always grab the most current image)

    import docker
    import boto3
    
    def ecrDemo(version_number):
    
        # The ECR Repository URI
        repo = XXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/myproj
        # The name of the [profile] stored in .aws/credentials
        profile = "sandbox"
        # The region your ECR repo is in
        region = "us-west-2"
        # How you want to tag your project locally
        local_tag = "myproj"
    
        #Set up a session
        session = boto3.Session(profile_name=profile, region_name=region)
        ecr = session.client('ecr')
    
        docker_api = docker.APIClient()
    
        print "Building image " + local_tag
        for line in docker_api.build(path='.', tag=local_tag, stream=True, \
            dockerfile='./Dockerfile.myproj'):
            process_docker_api_line(line)
    
        # Make auth call and parse out results
        auth = ecr.get_authorization_token()
        token = auth["authorizationData"][0]["authorizationToken"]
        username, password = b64decode(token).split(':')
        endpoint = auth["authorizationData"][0]["proxyEndpoint"]
    
        # print "Make authentication call"
        # docker_api.login(username=user, password=password, \
        #             registry=endpoint, reauth=True)
        auth_config_payload = {'username': username, 'password': password}
    
    
    
        version_tag = repo + ':latest'
        latest_tag = repo + ':' + version_number
    
        print "Tagging version " + version_tag
        if docker_api.tag(local_tag, version_tag) is False:
            raise RuntimeError("Tag appeared to fail: " + version_tag)
    
        print "Tagging latest " + latest_tag
        if docker_api.tag(local_tag, latest_tag) is False:
            raise RuntimeError("Tag appeared to fail: " + tag_latest)
    
        print "Pushing to repo " + version_tag
        for line in docker_api.push(version_tag, stream=True, auth_config=auth_config_payload):
            self.process_docker_api_line(line)
    
        print "Pushing to repo " + latest_tag
        for line in docker_api.push(latest_tag, stream=True, auth_config=auth_config_payload):
            self.process_docker_api_line(line)
    
        print "Removing taged deployment images"
        # You will still have the local_tag image if you need to troubleshoot
        docker_api.remove_image(version_tag, force=True)
        docker_api.remove_image(latest_tag, force=True)
    
    def process_docker_api_line(payload):
        """ Process the output from API stream, throw an Exception if there is an error """
        # Sometimes Docker sends to "{}\n" blocks together...
        for segment in payload.split('\n'):
            line = segment.strip()
            if line:
                try:
                    line_payload = json.loads(line)
                except ValueError as ex:
                    print "Could not decipher payload from API: " + ex.message
                if line_payload:
                    if "errorDetail" in line_payload:
                        error = line_payload["errorDetail"]
                        sys.stderr.write(error["message"])
                        raise RuntimeError("Error on build - code " + `error["code"]`)
                    elif "stream" in line_payload:
                        sys.stdout.write(line_payload["stream"])