func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, inputInfo types.BlobInfo, cache types.BlobInfoCache, isConfig bool) (types.BlobInfo, error) {
	if inputInfo.Digest.String() != "" {
		// This should not really be necessary, at least the copy code calls TryReusingBlob automatically.
		// Still, we need to check, if only because the "initiate upload" endpoint does not have a documented "blob already exists" return value.
		// But we do that with NoCache, so that it _only_ checks the primary destination, instead of trying all mount candidates _again_.
		haveBlob, reusedInfo, err := d.TryReusingBlob(ctx, inputInfo, none.NoCache, false)
		if err != nil {
			return types.BlobInfo{}, err
		}
		if haveBlob {
			return reusedInfo, nil
		}
	}

	// FIXME? Chunked upload, progress reporting, etc.
	uploadPath := fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref))
	logrus.Debugf("Uploading %s", uploadPath)
	res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth, nil)
	if err != nil {
		return types.BlobInfo{}, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusAccepted {
		logrus.Debugf("Error initiating layer upload, response %#v", *res)
		return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error initiating layer upload to %s in %s", uploadPath, d.c.registry)
	}
	uploadLocation, err := res.Location()
	if err != nil {
		return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL")
	}

	digester := digest.Canonical.Digester()
	sizeCounter := &sizeCounter{}
	tee := io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter))
	res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth, nil)
	if err != nil {
		logrus.Debugf("Error uploading layer chunked, response %#v", res)
		return types.BlobInfo{}, err
	}
	defer res.Body.Close()
	computedDigest := digester.Digest()

	uploadLocation, err = res.Location()
	if err != nil {
		return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL")
	}

	// FIXME: DELETE uploadLocation on failure (does not really work in docker/distribution servers, which incorrectly require the "delete" action in the token's scope)

	locationQuery := uploadLocation.Query()
	// TODO: check inputInfo.Digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717
	locationQuery.Set("digest", computedDigest.String())
	uploadLocation.RawQuery = locationQuery.Encode()
	res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth, nil)
	if err != nil {
		return types.BlobInfo{}, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusCreated {
		logrus.Debugf("Error uploading layer, response %#v", *res)
		return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error uploading layer to %s", uploadLocation)
	}

	logrus.Debugf("Upload of layer %s complete", computedDigest)
	cache.RecordKnownLocation(d.ref.Transport(), bicTransportScope(d.ref), computedDigest, newBICLocationReference(d.ref))
	return types.BlobInfo{Digest: computedDigest, Size: sizeCounter.size}, nil
}
func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, inputInfo types.BlobInfo, cache types.BlobInfoCache, isConfig bool) (types.BlobInfo, error) {
	if inputInfo.Digest.String() != "" {
		
		
		
		haveBlob, reusedInfo, err := d.TryReusingBlob(ctx, inputInfo, none.NoCache, false)
		if err != nil {
			return types.BlobInfo{}, err
		}
		if haveBlob {
			return reusedInfo, nil
		}
	}

	
	uploadPath := fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref))
	logrus.Debugf("Uploading %s", uploadPath)
	res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth, nil)
	if err != nil {
		return types.BlobInfo{}, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusAccepted {
		logrus.Debugf("Error initiating layer upload, response %#v", *res)
		return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error initiating layer upload to %s in %s", uploadPath, d.c.registry)
	}
	uploadLocation, err := res.Location()
	if err != nil {
		return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL")
	}

	digester := digest.Canonical.Digester()
	sizeCounter := &sizeCounter{}
	tee := io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter))
	res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth, nil)
	if err != nil {
		logrus.Debugf("Error uploading layer chunked, response %#v", res)
		return types.BlobInfo{}, err
	}
	defer res.Body.Close()
	computedDigest := digester.Digest()

	uploadLocation, err = res.Location()
	if err != nil {
		return types.BlobInfo{}, errors.Wrap(err, "Error determining upload URL")
	}

	

	locationQuery := uploadLocation.Query()
	
	locationQuery.Set("digest", computedDigest.String())
	uploadLocation.RawQuery = locationQuery.Encode()
	res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth, nil)
	if err != nil {
		return types.BlobInfo{}, err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusCreated {
		logrus.Debugf("Error uploading layer, response %#v", *res)
		return types.BlobInfo{}, errors.Wrapf(client.HandleErrorResponse(res), "Error uploading layer to %s", uploadLocation)
	}

	logrus.Debugf("Upload of layer %s complete", computedDigest)
	cache.RecordKnownLocation(d.ref.Transport(), bicTransportScope(d.ref), computedDigest, newBICLocationReference(d.ref))
	return types.BlobInfo{Digest: computedDigest, Size: sizeCounter.size}, nil
}
func ( d * dockerImageDestination ) PutBlob ( ctx context . Context , stream io . Reader , inputInfo types . BlobInfo , cache types . BlobInfoCache , isConfig bool ) ( types . BlobInfo , error ) { if inputInfo . Digest . String ( ) != " " { // This should not really be necessary, at least the copy code calls TryReusingBlob automatically. // Still, we need to check, if only because the "initiate upload" endpoint does not have a documented "blob already exists" return value. // But we do that with NoCache, so that it _only_ checks the primary destination, instead of trying all mount candidates _again_. haveBlob , reusedInfo , err := d . TryReusingBlob ( ctx , inputInfo , none . NoCache , false ) if err != nil { return types . BlobInfo { } , err } if haveBlob { return reusedInfo , nil } } // FIXME? Chunked upload, progress reporting, etc. uploadPath := fmt . Sprintf ( blobUploadPath , reference . Path ( d . ref . ref ) ) logrus . Debugf ( " " , uploadPath ) res , err := d . c . makeRequest ( ctx , " " , uploadPath , nil , nil , v2Auth , nil ) if err != nil { return types . BlobInfo { } , err } defer res . Body . Close ( ) if res . StatusCode != http . StatusAccepted { logrus . Debugf ( " " , * res ) return types . BlobInfo { } , errors . Wrapf ( client . HandleErrorResponse ( res ) , " " , uploadPath , d . c . registry ) } uploadLocation , err := res . Location ( ) if err != nil { return types . BlobInfo { } , errors . Wrap ( err , " " ) } digester := digest . Canonical . Digester ( ) sizeCounter := & sizeCounter { } tee := io . TeeReader ( stream , io . MultiWriter ( digester . Hash ( ) , sizeCounter ) ) res , err = d . c . makeRequestToResolvedURL ( ctx , " " , uploadLocation . String ( ) , map [ string ] [ ] string { " " : { " " } } , tee , inputInfo . Size , v2Auth , nil ) if err != nil { logrus . Debugf ( " " , res ) return types . BlobInfo { } , err } defer res . Body . Close ( ) computedDigest := digester . Digest ( ) uploadLocation , err = res . Location ( ) if err != nil { return types . BlobInfo { } , errors . Wrap ( err , " " ) } // FIXME: DELETE uploadLocation on failure (does not really work in docker/distribution servers, which incorrectly require the "delete" action in the token's scope) locationQuery := uploadLocation . Query ( ) // TODO: check inputInfo.Digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717 locationQuery . Set ( " " , computedDigest . String ( ) ) uploadLocation . RawQuery = locationQuery . Encode ( ) res , err = d . c . makeRequestToResolvedURL ( ctx , " " , uploadLocation . String ( ) , map [ string ] [ ] string { " " : { " " } } , nil , - 1 , v2Auth , nil ) if err != nil { return types . BlobInfo { } , err } defer res . Body . Close ( ) if res . StatusCode != http . StatusCreated { logrus . Debugf ( " " , * res ) return types . BlobInfo { } , errors . Wrapf ( client . HandleErrorResponse ( res ) , " " , uploadLocation ) } logrus . Debugf ( " " , computedDigest ) cache . RecordKnownLocation ( d . ref . Transport ( ) , bicTransportScope ( d . ref ) , computedDigest , newBICLocationReference ( d . ref ) ) return types . BlobInfo { Digest : computedDigest , Size : sizeCounter . size } , nil }
// PutBlob writes contents of stream and returns data representing the result (with all data filled in).
// inputInfo.Digest can be optionally provided if known; it is not mandatory for the implementation to verify it.
// inputInfo.Size is the expected length of stream, if known.
// May update cache.
// WARNING: The contents of stream are being verified on the fly.  Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
// to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
PutBlob writes contents of stream and returns data representing the result ( with all data filled in ) . inputInfo . Digest can be optionally provided if known ; it is not mandatory for the implementation to verify it . inputInfo . Size is the expected length of stream if known . May update cache . WARNING : The contents of stream are being verified on the fly . Until stream . Read () returns io . EOF the contents of the data SHOULD NOT be available to any other readers for download using the supplied digest . If stream . Read () at any time ESPECIALLY at end of input returns an error PutBlob MUST 1 ) fail and 2 ) delete any data stored so far .