mirror of https://github.com/docker/cli.git
105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
/*
|
|
Copyright The containerd Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package continuity
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
// Digester produces a digest for a given read stream
|
|
type Digester interface {
|
|
Digest(io.Reader) (digest.Digest, error)
|
|
}
|
|
|
|
// ContentProvider produces a read stream for a given digest
|
|
type ContentProvider interface {
|
|
Reader(digest.Digest) (io.ReadCloser, error)
|
|
}
|
|
|
|
type simpleDigester struct {
|
|
algorithm digest.Algorithm
|
|
}
|
|
|
|
func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) {
|
|
digester := sd.algorithm.Digester()
|
|
|
|
if _, err := io.Copy(digester.Hash(), r); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return digester.Digest(), nil
|
|
}
|
|
|
|
// uniqifyDigests sorts and uniqifies the provided digest, ensuring that the
|
|
// digests are not repeated and no two digests with the same algorithm have
|
|
// different values. Because a stable sort is used, this has the effect of
|
|
// "zipping" digest collections from multiple resources.
|
|
func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) {
|
|
sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here.
|
|
seen := map[digest.Digest]struct{}{}
|
|
algs := map[digest.Algorithm][]digest.Digest{} // detect different digests.
|
|
|
|
var out []digest.Digest
|
|
// uniqify the digests
|
|
for _, d := range digests {
|
|
if _, ok := seen[d]; ok {
|
|
continue
|
|
}
|
|
|
|
seen[d] = struct{}{}
|
|
algs[d.Algorithm()] = append(algs[d.Algorithm()], d)
|
|
|
|
if len(algs[d.Algorithm()]) > 1 {
|
|
return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm())
|
|
}
|
|
|
|
out = append(out, d)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// digestsMatch compares the two sets of digests to see if they match.
|
|
func digestsMatch(as, bs []digest.Digest) bool {
|
|
all := append(as, bs...)
|
|
|
|
uniqified, err := uniqifyDigests(all...)
|
|
if err != nil {
|
|
// the only error uniqifyDigests returns is when the digests disagree.
|
|
return false
|
|
}
|
|
|
|
disjoint := len(as) + len(bs)
|
|
if len(uniqified) == disjoint {
|
|
// if these two sets have the same cardinality, we know both sides
|
|
// didn't share any digests.
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type digestSlice []digest.Digest
|
|
|
|
func (p digestSlice) Len() int { return len(p) }
|
|
func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] }
|
|
func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|