Ejemplo n.º 1
0
    def push(self,
             session,
             namespace,
             repo_name,
             tag_names,
             images,
             credentials=None,
             expected_failure=None,
             options=None):
        auth = self._auth_for_credentials(credentials)
        tag_names = [tag_names] if isinstance(tag_names, str) else tag_names

        # Ping!
        self.ping(session)

        # PUT /v1/repositories/{namespace}/{repository}/
        result = self.conduct(session,
                              'PUT',
                              '/v1/repositories/%s/' %
                              self.repo_name(namespace, repo_name),
                              expected_status=(201, expected_failure,
                                               V1ProtocolSteps.PUT_IMAGES),
                              json_data={},
                              auth=auth)
        if result.status_code != 201:
            return

        headers = {}
        headers[
            'Authorization'] = 'token ' + result.headers['www-authenticate']

        for image in images:
            assert image.urls is None

            # PUT /v1/images/{imageID}/json
            image_json_data = {'id': image.id}
            if image.size is not None:
                image_json_data['Size'] = image.size

            if image.parent_id is not None:
                image_json_data['parent'] = image.parent_id

            if image.config is not None:
                image_json_data['config'] = image.config

            if image.created is not None:
                image_json_data['created'] = image.created

            image_json = json.dumps(image_json_data)
            response = self.conduct(
                session,
                'PUT',
                '/v1/images/%s/json' % image.id,
                data=image_json,
                headers=headers,
                expected_status=(200, expected_failure,
                                 V1ProtocolSteps.PUT_IMAGE_JSON))
            if response.status_code != 200:
                return

            # PUT /v1/images/{imageID}/checksum (old style)
            old_checksum = compute_tarsum(StringIO(image.bytes), image_json)
            checksum_headers = {'X-Docker-Checksum': old_checksum}
            checksum_headers.update(headers)

            self.conduct(session,
                         'PUT',
                         '/v1/images/%s/checksum' % image.id,
                         headers=checksum_headers)

            # PUT /v1/images/{imageID}/layer
            self.conduct(session,
                         'PUT',
                         '/v1/images/%s/layer' % image.id,
                         data=StringIO(image.bytes),
                         headers=headers)

            # PUT /v1/images/{imageID}/checksum (new style)
            checksum = compute_simple(StringIO(image.bytes), image_json)
            checksum_headers = {'X-Docker-Checksum-Payload': checksum}
            checksum_headers.update(headers)

            self.conduct(session,
                         'PUT',
                         '/v1/images/%s/checksum' % image.id,
                         headers=checksum_headers)

        # PUT /v1/repositories/{namespace}/{repository}/tags/latest
        for tag_name in tag_names:
            self.conduct(session,
                         'PUT',
                         '/v1/repositories/%s/tags/%s' %
                         (self.repo_name(namespace, repo_name), tag_name),
                         data='"%s"' % images[-1].id,
                         headers=headers,
                         expected_status=(200, expected_failure,
                                          V1ProtocolSteps.PUT_TAG))

        # PUT /v1/repositories/{namespace}/{repository}/images
        self.conduct(session,
                     'PUT',
                     '/v1/repositories/%s/images' %
                     self.repo_name(namespace, repo_name),
                     expected_status=204,
                     headers=headers)

        return PushResult(manifests=None, headers=headers)
Ejemplo n.º 2
0
    def push(
        self,
        session,
        namespace,
        repo_name,
        tag_names,
        images,
        credentials=None,
        expected_failure=None,
        options=None,
    ):
        auth = self._auth_for_credentials(credentials)
        tag_names = [tag_names] if isinstance(tag_names, str) else tag_names

        # Ping!
        self.ping(session)

        # PUT /v1/repositories/{namespace}/{repository}/
        result = self.conduct(
            session,
            "PUT",
            "/v1/repositories/%s/" % self.repo_name(namespace, repo_name),
            expected_status=(201, expected_failure,
                             V1ProtocolSteps.PUT_IMAGES),
            json_data={},
            auth=auth,
        )
        if result.status_code != 201:
            return

        headers = {}
        headers[
            "Authorization"] = "token " + result.headers["www-authenticate"]

        for image in images:
            assert image.urls is None

            # PUT /v1/images/{imageID}/json
            image_json_data = {"id": image.id}
            if image.size is not None:
                image_json_data["Size"] = image.size

            if image.parent_id is not None:
                image_json_data["parent"] = image.parent_id

            if image.config is not None:
                image_json_data["config"] = image.config

            if image.created is not None:
                image_json_data["created"] = image.created

            image_json = json.dumps(image_json_data)

            response = self.conduct(
                session,
                "PUT",
                "/v1/images/%s/json" % image.id,
                data=image_json,
                headers=headers,
                expected_status=(200, expected_failure,
                                 V1ProtocolSteps.PUT_IMAGE_JSON),
            )
            if response.status_code != 200:
                return

            # PUT /v1/images/{imageID}/checksum (old style)
            old_checksum = compute_tarsum(BytesIO(image.bytes), image_json)
            checksum_headers = {"X-Docker-Checksum": old_checksum}
            checksum_headers.update(headers)

            self.conduct(session,
                         "PUT",
                         "/v1/images/%s/checksum" % image.id,
                         headers=checksum_headers)

            # PUT /v1/images/{imageID}/layer
            self.conduct(
                session,
                "PUT",
                "/v1/images/%s/layer" % image.id,
                data=BytesIO(image.bytes),
                headers=headers,
            )

            # PUT /v1/images/{imageID}/checksum (new style)
            checksum = compute_simple(BytesIO(image.bytes), image_json)
            checksum_headers = {"X-Docker-Checksum-Payload": checksum}
            checksum_headers.update(headers)

            self.conduct(session,
                         "PUT",
                         "/v1/images/%s/checksum" % image.id,
                         headers=checksum_headers)

        # PUT /v1/repositories/{namespace}/{repository}/tags/latest
        for tag_name in tag_names:
            self.conduct(
                session,
                "PUT",
                "/v1/repositories/%s/tags/%s" %
                (self.repo_name(namespace, repo_name), tag_name),
                data='"%s"' % images[-1].id,
                headers=headers,
                expected_status=(200, expected_failure,
                                 V1ProtocolSteps.PUT_TAG),
            )

        # PUT /v1/repositories/{namespace}/{repository}/images
        self.conduct(
            session,
            "PUT",
            "/v1/repositories/%s/images" %
            self.repo_name(namespace, repo_name),
            expected_status=204,
            headers=headers,
        )

        return PushResult(manifests=None, headers=headers)
Ejemplo n.º 3
0
def put_image_layer(namespace, repository, image_id):
    logger.debug("Checking repo permissions")
    permission = ModifyRepositoryPermission(namespace, repository)
    if not permission.can():
        abort(403)

    repository_ref = registry_model.lookup_repository(namespace,
                                                      repository,
                                                      kind_filter="image")
    if repository_ref is None:
        abort(403)

    logger.debug("Checking for image in manifest builder")
    builder = lookup_manifest_builder(repository_ref,
                                      session.get("manifest_builder"), store,
                                      docker_v2_signing_key)
    if builder is None:
        abort(400)

    layer = builder.lookup_layer(image_id)
    if layer is None:
        abort(404)

    logger.debug("Storing layer data")
    input_stream = request.stream
    if request.headers.get("transfer-encoding") == "chunked":
        # Careful, might work only with WSGI servers supporting chunked
        # encoding (Gunicorn)
        input_stream = request.environ["wsgi.input"]

    expiration_sec = app.config["PUSH_TEMP_TAG_EXPIRATION_SEC"]
    settings = BlobUploadSettings(
        maximum_blob_size=app.config["MAXIMUM_LAYER_SIZE"],
        committed_blob_expiration=expiration_sec,
    )

    extra_handlers = []

    # Add a handler that copies the data into a temp file. This is used to calculate the tarsum,
    # which is only needed for older versions of Docker.
    requires_tarsum = bool(builder.get_layer_checksums(layer))
    if requires_tarsum:
        tmp, tmp_hndlr = store.temp_store_handler()
        extra_handlers.append(tmp_hndlr)

    # Add a handler which computes the simple Docker V1 checksum.
    h, sum_hndlr = checksums.simple_checksum_handler(layer.v1_metadata_string)
    extra_handlers.append(sum_hndlr)

    uploaded_blob = None
    try:
        with upload_blob(repository_ref,
                         store,
                         settings,
                         extra_blob_stream_handlers=extra_handlers) as manager:
            manager.upload_chunk(app.config, input_stream)
            uploaded_blob = manager.commit_to_blob(app.config)
    except BlobUploadException:
        logger.exception("Exception when writing image data")
        abort(520,
              "Image %(image_id)s could not be written. Please try again.",
              image_id=image_id)

    # Compute the final checksum
    csums = []
    csums.append("sha256:{0}".format(h.hexdigest()))

    try:
        if requires_tarsum:
            tmp.seek(0)
            csums.append(
                checksums.compute_tarsum(tmp, layer.v1_metadata_string))
            tmp.close()
    except (IOError, checksums.TarError) as exc:
        logger.debug("put_image_layer: Error when computing tarsum %s", exc)

    # If there was already a precomputed checksum, validate against it now.
    if builder.get_layer_checksums(layer):
        checksum = builder.get_layer_checksums(layer)[0]
        if not builder.validate_layer_checksum(layer, checksum):
            logger.debug(
                "put_image_checksum: Wrong checksum. Given: %s and expected: %s",
                checksum,
                builder.get_layer_checksums(layer),
            )
            abort(
                400,
                "Checksum mismatch for image: %(image_id)s",
                issue="checksum-mismatch",
                image_id=image_id,
            )

    # Assign the blob to the layer in the manifest.
    if not builder.assign_layer_blob(layer, uploaded_blob, csums):
        abort(500, "Something went wrong")

    # Send a job to the work queue to replicate the image layer.
    # TODO: move this into a better place.
    queue_storage_replication(namespace, uploaded_blob)

    return make_response("true", 200)