Skip to content

Commit 6f1ca67

Browse files
Merge pull request #16486 from deads2k/image-09-last-snip
Automatic merge from submit-queue (batch tested with PRs 16480, 16486, 16270, 16128, 16489) remove last docker distribution dependency from api This removes the last of the docker distribution references from the image api by copying the code we care about into internal packages. The internal packages makes sure that they don't leak out of our API. @smarterclayton approved of the judicious copy done for these packages in particular.
2 parents 30080de + c662bfd commit 6f1ca67

File tree

10 files changed

+1115
-6
lines changed

10 files changed

+1115
-6
lines changed

hack/import-restrictions.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,14 @@
120120
"allowedImportPackageRoots": [
121121
"vendor/k8s.io/apimachinery",
122122
"vendor/github.com/gogo/protobuf",
123+
"github.com/openshift/origin/pkg/image/apis/image/internal",
123124
"github.com/openshift/origin/pkg/api/apihelpers"
124125
],
125126
"allowedImportPackages": [
126127
"vendor/k8s.io/kubernetes/pkg/api",
127128
"vendor/k8s.io/kubernetes/pkg/api/v1",
128129
"vendor/github.com/golang/glog",
129-
"vendor/github.com/docker/distribution/digest",
130-
"vendor/github.com/blang/semver",
131-
"github.com/openshift/origin/pkg/image/reference"
130+
"vendor/github.com/blang/semver"
132131
]
133132
},
134133

pkg/image/apis/image/helper.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ import (
1414
"k8s.io/apimachinery/pkg/util/sets"
1515

1616
"github.com/blang/semver"
17-
"github.com/docker/distribution/digest"
1817
"github.com/golang/glog"
1918

20-
"github.com/openshift/origin/pkg/image/reference"
19+
"github.com/openshift/origin/pkg/image/apis/image/internal/digest"
2120
)
2221

2322
const (
@@ -145,7 +144,7 @@ func IsRegistryDockerHub(registry string) bool {
145144
func ParseDockerImageReference(spec string) (DockerImageReference, error) {
146145
var ref DockerImageReference
147146

148-
namedRef, err := reference.ParseNamedDockerImageReference(spec)
147+
namedRef, err := parseNamedDockerImageReference(spec)
149148
if err != nil {
150149
return ref, err
151150
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package digest
2+
3+
import (
4+
"fmt"
5+
"hash"
6+
"io"
7+
"regexp"
8+
"strings"
9+
)
10+
11+
const (
12+
// DigestSha256EmptyTar is the canonical sha256 digest of empty data
13+
DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
14+
)
15+
16+
// Digest allows simple protection of hex formatted digest strings, prefixed
17+
// by their algorithm. Strings of type Digest have some guarantee of being in
18+
// the correct format and it provides quick access to the components of a
19+
// digest string.
20+
//
21+
// The following is an example of the contents of Digest types:
22+
//
23+
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
24+
//
25+
// This allows to abstract the digest behind this type and work only in those
26+
// terms.
27+
type Digest string
28+
29+
// NewDigest returns a Digest from alg and a hash.Hash object.
30+
func NewDigest(alg Algorithm, h hash.Hash) Digest {
31+
return NewDigestFromBytes(alg, h.Sum(nil))
32+
}
33+
34+
// NewDigestFromBytes returns a new digest from the byte contents of p.
35+
// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...)
36+
// functions. This is also useful for rebuilding digests from binary
37+
// serializations.
38+
func NewDigestFromBytes(alg Algorithm, p []byte) Digest {
39+
return Digest(fmt.Sprintf("%s:%x", alg, p))
40+
}
41+
42+
// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
43+
func NewDigestFromHex(alg, hex string) Digest {
44+
return Digest(fmt.Sprintf("%s:%s", alg, hex))
45+
}
46+
47+
// DigestRegexp matches valid digest types.
48+
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)
49+
50+
// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
51+
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
52+
53+
var (
54+
// ErrDigestInvalidFormat returned when digest format invalid.
55+
ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")
56+
57+
// ErrDigestInvalidLength returned when digest has invalid length.
58+
ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")
59+
60+
// ErrDigestUnsupported returned when the digest algorithm is unsupported.
61+
ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
62+
)
63+
64+
// ParseDigest parses s and returns the validated digest object. An error will
65+
// be returned if the format is invalid.
66+
func ParseDigest(s string) (Digest, error) {
67+
d := Digest(s)
68+
69+
return d, d.Validate()
70+
}
71+
72+
// FromReader returns the most valid digest for the underlying content using
73+
// the canonical digest algorithm.
74+
func FromReader(rd io.Reader) (Digest, error) {
75+
return Canonical.FromReader(rd)
76+
}
77+
78+
// FromBytes digests the input and returns a Digest.
79+
func FromBytes(p []byte) Digest {
80+
return Canonical.FromBytes(p)
81+
}
82+
83+
// Validate checks that the contents of d is a valid digest, returning an
84+
// error if not.
85+
func (d Digest) Validate() error {
86+
s := string(d)
87+
88+
if !DigestRegexpAnchored.MatchString(s) {
89+
return ErrDigestInvalidFormat
90+
}
91+
92+
i := strings.Index(s, ":")
93+
if i < 0 {
94+
return ErrDigestInvalidFormat
95+
}
96+
97+
// case: "sha256:" with no hex.
98+
if i+1 == len(s) {
99+
return ErrDigestInvalidFormat
100+
}
101+
102+
switch algorithm := Algorithm(s[:i]); algorithm {
103+
case SHA256, SHA384, SHA512:
104+
if algorithm.Size()*2 != len(s[i+1:]) {
105+
return ErrDigestInvalidLength
106+
}
107+
break
108+
default:
109+
return ErrDigestUnsupported
110+
}
111+
112+
return nil
113+
}
114+
115+
// Algorithm returns the algorithm portion of the digest. This will panic if
116+
// the underlying digest is not in a valid format.
117+
func (d Digest) Algorithm() Algorithm {
118+
return Algorithm(d[:d.sepIndex()])
119+
}
120+
121+
// Hex returns the hex digest portion of the digest. This will panic if the
122+
// underlying digest is not in a valid format.
123+
func (d Digest) Hex() string {
124+
return string(d[d.sepIndex()+1:])
125+
}
126+
127+
func (d Digest) String() string {
128+
return string(d)
129+
}
130+
131+
func (d Digest) sepIndex() int {
132+
i := strings.Index(string(d), ":")
133+
134+
if i < 0 {
135+
panic("could not find ':' in digest: " + d)
136+
}
137+
138+
return i
139+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package digest
2+
3+
import (
4+
"crypto"
5+
"fmt"
6+
"hash"
7+
"io"
8+
)
9+
10+
// Algorithm identifies and implementation of a digester by an identifier.
11+
// Note the that this defines both the hash algorithm used and the string
12+
// encoding.
13+
type Algorithm string
14+
15+
// supported digest types
16+
const (
17+
SHA256 Algorithm = "sha256" // sha256 with hex encoding
18+
SHA384 Algorithm = "sha384" // sha384 with hex encoding
19+
SHA512 Algorithm = "sha512" // sha512 with hex encoding
20+
21+
// Canonical is the primary digest algorithm used with the distribution
22+
// project. Other digests may be used but this one is the primary storage
23+
// digest.
24+
Canonical = SHA256
25+
)
26+
27+
var (
28+
// TODO(stevvooe): Follow the pattern of the standard crypto package for
29+
// registration of digests. Effectively, we are a registerable set and
30+
// common symbol access.
31+
32+
// algorithms maps values to hash.Hash implementations. Other algorithms
33+
// may be available but they cannot be calculated by the digest package.
34+
algorithms = map[Algorithm]crypto.Hash{
35+
SHA256: crypto.SHA256,
36+
SHA384: crypto.SHA384,
37+
SHA512: crypto.SHA512,
38+
}
39+
)
40+
41+
// Available returns true if the digest type is available for use. If this
42+
// returns false, New and Hash will return nil.
43+
func (a Algorithm) Available() bool {
44+
h, ok := algorithms[a]
45+
if !ok {
46+
return false
47+
}
48+
49+
// check availability of the hash, as well
50+
return h.Available()
51+
}
52+
53+
func (a Algorithm) String() string {
54+
return string(a)
55+
}
56+
57+
// Size returns number of bytes returned by the hash.
58+
func (a Algorithm) Size() int {
59+
h, ok := algorithms[a]
60+
if !ok {
61+
return 0
62+
}
63+
return h.Size()
64+
}
65+
66+
// Set implemented to allow use of Algorithm as a command line flag.
67+
func (a *Algorithm) Set(value string) error {
68+
if value == "" {
69+
*a = Canonical
70+
} else {
71+
// just do a type conversion, support is queried with Available.
72+
*a = Algorithm(value)
73+
}
74+
75+
return nil
76+
}
77+
78+
// New returns a new digester for the specified algorithm. If the algorithm
79+
// does not have a digester implementation, nil will be returned. This can be
80+
// checked by calling Available before calling New.
81+
func (a Algorithm) New() Digester {
82+
return &digester{
83+
alg: a,
84+
hash: a.Hash(),
85+
}
86+
}
87+
88+
// Hash returns a new hash as used by the algorithm. If not available, the
89+
// method will panic. Check Algorithm.Available() before calling.
90+
func (a Algorithm) Hash() hash.Hash {
91+
if !a.Available() {
92+
// NOTE(stevvooe): A missing hash is usually a programming error that
93+
// must be resolved at compile time. We don't import in the digest
94+
// package to allow users to choose their hash implementation (such as
95+
// when using stevvooe/resumable or a hardware accelerated package).
96+
//
97+
// Applications that may want to resolve the hash at runtime should
98+
// call Algorithm.Available before call Algorithm.Hash().
99+
panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
100+
}
101+
102+
return algorithms[a].New()
103+
}
104+
105+
// FromReader returns the digest of the reader using the algorithm.
106+
func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
107+
digester := a.New()
108+
109+
if _, err := io.Copy(digester.Hash(), rd); err != nil {
110+
return "", err
111+
}
112+
113+
return digester.Digest(), nil
114+
}
115+
116+
// FromBytes digests the input and returns a Digest.
117+
func (a Algorithm) FromBytes(p []byte) Digest {
118+
digester := a.New()
119+
120+
if _, err := digester.Hash().Write(p); err != nil {
121+
// Writes to a Hash should never fail. None of the existing
122+
// hash implementations in the stdlib or hashes vendored
123+
// here can return errors from Write. Having a panic in this
124+
// condition instead of having FromBytes return an error value
125+
// avoids unnecessary error handling paths in all callers.
126+
panic("write to hash function returned error: " + err.Error())
127+
}
128+
129+
return digester.Digest()
130+
}
131+
132+
// TODO(stevvooe): Allow resolution of verifiers using the digest type and
133+
// this registration system.
134+
135+
// Digester calculates the digest of written data. Writes should go directly
136+
// to the return value of Hash, while calling Digest will return the current
137+
// value of the digest.
138+
type Digester interface {
139+
Hash() hash.Hash // provides direct access to underlying hash instance.
140+
Digest() Digest
141+
}
142+
143+
// digester provides a simple digester definition that embeds a hasher.
144+
type digester struct {
145+
alg Algorithm
146+
hash hash.Hash
147+
}
148+
149+
func (d *digester) Hash() hash.Hash {
150+
return d.hash
151+
}
152+
153+
func (d *digester) Digest() Digest {
154+
return NewDigest(d.alg, d.hash)
155+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// digest is a copy from "github.com/docker/distribution/digest" that is kept because we want to avoid the godep,
2+
// this package has no non-standard dependencies, and if it changes lots of other docker registry stuff breaks.
3+
// Don't try this at home!
4+
// Changes here require sign-off from openshift/api-reviewers and they will be rejected.
5+
package digest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// reference is a copy from "github.com/docker/distribution/reference" that is kept because we want to avoid the godep,
2+
// this package has no non-standard dependencies, and if it changes lots of other docker registry stuff breaks.
3+
// Don't try this at home!
4+
// Changes here require sign-off from openshift/api-reviewers and they will be rejected.
5+
package reference

0 commit comments

Comments
 (0)