[mqtt] Implements mqtt publish tooling
This commit is contained in:
		
							
								
								
									
										202
									
								
								vendor/github.com/docker/distribution/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								vendor/github.com/docker/distribution/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "{}"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright {yyyy} {name of copyright owner}
 | 
			
		||||
 | 
			
		||||
   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.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										247
									
								
								vendor/github.com/docker/distribution/digestset/set.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								vendor/github.com/docker/distribution/digestset/set.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,247 @@
 | 
			
		||||
package digestset
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	digest "github.com/opencontainers/go-digest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrDigestNotFound is used when a matching digest
 | 
			
		||||
	// could not be found in a set.
 | 
			
		||||
	ErrDigestNotFound = errors.New("digest not found")
 | 
			
		||||
 | 
			
		||||
	// ErrDigestAmbiguous is used when multiple digests
 | 
			
		||||
	// are found in a set. None of the matching digests
 | 
			
		||||
	// should be considered valid matches.
 | 
			
		||||
	ErrDigestAmbiguous = errors.New("ambiguous digest string")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Set is used to hold a unique set of digests which
 | 
			
		||||
// may be easily referenced by easily  referenced by a string
 | 
			
		||||
// representation of the digest as well as short representation.
 | 
			
		||||
// The uniqueness of the short representation is based on other
 | 
			
		||||
// digests in the set. If digests are omitted from this set,
 | 
			
		||||
// collisions in a larger set may not be detected, therefore it
 | 
			
		||||
// is important to always do short representation lookups on
 | 
			
		||||
// the complete set of digests. To mitigate collisions, an
 | 
			
		||||
// appropriately long short code should be used.
 | 
			
		||||
type Set struct {
 | 
			
		||||
	mutex   sync.RWMutex
 | 
			
		||||
	entries digestEntries
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSet creates an empty set of digests
 | 
			
		||||
// which may have digests added.
 | 
			
		||||
func NewSet() *Set {
 | 
			
		||||
	return &Set{
 | 
			
		||||
		entries: digestEntries{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkShortMatch checks whether two digests match as either whole
 | 
			
		||||
// values or short values. This function does not test equality,
 | 
			
		||||
// rather whether the second value could match against the first
 | 
			
		||||
// value.
 | 
			
		||||
func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
 | 
			
		||||
	if len(hex) == len(shortHex) {
 | 
			
		||||
		if hex != shortHex {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if len(shortAlg) > 0 && string(alg) != shortAlg {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	} else if !strings.HasPrefix(hex, shortHex) {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if len(shortAlg) > 0 && string(alg) != shortAlg {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Lookup looks for a digest matching the given string representation.
 | 
			
		||||
// If no digests could be found ErrDigestNotFound will be returned
 | 
			
		||||
// with an empty digest value. If multiple matches are found
 | 
			
		||||
// ErrDigestAmbiguous will be returned with an empty digest value.
 | 
			
		||||
func (dst *Set) Lookup(d string) (digest.Digest, error) {
 | 
			
		||||
	dst.mutex.RLock()
 | 
			
		||||
	defer dst.mutex.RUnlock()
 | 
			
		||||
	if len(dst.entries) == 0 {
 | 
			
		||||
		return "", ErrDigestNotFound
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		searchFunc func(int) bool
 | 
			
		||||
		alg        digest.Algorithm
 | 
			
		||||
		hex        string
 | 
			
		||||
	)
 | 
			
		||||
	dgst, err := digest.Parse(d)
 | 
			
		||||
	if err == digest.ErrDigestInvalidFormat {
 | 
			
		||||
		hex = d
 | 
			
		||||
		searchFunc = func(i int) bool {
 | 
			
		||||
			return dst.entries[i].val >= d
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		hex = dgst.Hex()
 | 
			
		||||
		alg = dgst.Algorithm()
 | 
			
		||||
		searchFunc = func(i int) bool {
 | 
			
		||||
			if dst.entries[i].val == hex {
 | 
			
		||||
				return dst.entries[i].alg >= alg
 | 
			
		||||
			}
 | 
			
		||||
			return dst.entries[i].val >= hex
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	idx := sort.Search(len(dst.entries), searchFunc)
 | 
			
		||||
	if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) {
 | 
			
		||||
		return "", ErrDigestNotFound
 | 
			
		||||
	}
 | 
			
		||||
	if dst.entries[idx].alg == alg && dst.entries[idx].val == hex {
 | 
			
		||||
		return dst.entries[idx].digest, nil
 | 
			
		||||
	}
 | 
			
		||||
	if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) {
 | 
			
		||||
		return "", ErrDigestAmbiguous
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dst.entries[idx].digest, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add adds the given digest to the set. An error will be returned
 | 
			
		||||
// if the given digest is invalid. If the digest already exists in the
 | 
			
		||||
// set, this operation will be a no-op.
 | 
			
		||||
func (dst *Set) Add(d digest.Digest) error {
 | 
			
		||||
	if err := d.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	dst.mutex.Lock()
 | 
			
		||||
	defer dst.mutex.Unlock()
 | 
			
		||||
	entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
 | 
			
		||||
	searchFunc := func(i int) bool {
 | 
			
		||||
		if dst.entries[i].val == entry.val {
 | 
			
		||||
			return dst.entries[i].alg >= entry.alg
 | 
			
		||||
		}
 | 
			
		||||
		return dst.entries[i].val >= entry.val
 | 
			
		||||
	}
 | 
			
		||||
	idx := sort.Search(len(dst.entries), searchFunc)
 | 
			
		||||
	if idx == len(dst.entries) {
 | 
			
		||||
		dst.entries = append(dst.entries, entry)
 | 
			
		||||
		return nil
 | 
			
		||||
	} else if dst.entries[idx].digest == d {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entries := append(dst.entries, nil)
 | 
			
		||||
	copy(entries[idx+1:], entries[idx:len(entries)-1])
 | 
			
		||||
	entries[idx] = entry
 | 
			
		||||
	dst.entries = entries
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove removes the given digest from the set. An err will be
 | 
			
		||||
// returned if the given digest is invalid. If the digest does
 | 
			
		||||
// not exist in the set, this operation will be a no-op.
 | 
			
		||||
func (dst *Set) Remove(d digest.Digest) error {
 | 
			
		||||
	if err := d.Validate(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	dst.mutex.Lock()
 | 
			
		||||
	defer dst.mutex.Unlock()
 | 
			
		||||
	entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
 | 
			
		||||
	searchFunc := func(i int) bool {
 | 
			
		||||
		if dst.entries[i].val == entry.val {
 | 
			
		||||
			return dst.entries[i].alg >= entry.alg
 | 
			
		||||
		}
 | 
			
		||||
		return dst.entries[i].val >= entry.val
 | 
			
		||||
	}
 | 
			
		||||
	idx := sort.Search(len(dst.entries), searchFunc)
 | 
			
		||||
	// Not found if idx is after or value at idx is not digest
 | 
			
		||||
	if idx == len(dst.entries) || dst.entries[idx].digest != d {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entries := dst.entries
 | 
			
		||||
	copy(entries[idx:], entries[idx+1:])
 | 
			
		||||
	entries = entries[:len(entries)-1]
 | 
			
		||||
	dst.entries = entries
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// All returns all the digests in the set
 | 
			
		||||
func (dst *Set) All() []digest.Digest {
 | 
			
		||||
	dst.mutex.RLock()
 | 
			
		||||
	defer dst.mutex.RUnlock()
 | 
			
		||||
	retValues := make([]digest.Digest, len(dst.entries))
 | 
			
		||||
	for i := range dst.entries {
 | 
			
		||||
		retValues[i] = dst.entries[i].digest
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return retValues
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShortCodeTable returns a map of Digest to unique short codes. The
 | 
			
		||||
// length represents the minimum value, the maximum length may be the
 | 
			
		||||
// entire value of digest if uniqueness cannot be achieved without the
 | 
			
		||||
// full value. This function will attempt to make short codes as short
 | 
			
		||||
// as possible to be unique.
 | 
			
		||||
func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
 | 
			
		||||
	dst.mutex.RLock()
 | 
			
		||||
	defer dst.mutex.RUnlock()
 | 
			
		||||
	m := make(map[digest.Digest]string, len(dst.entries))
 | 
			
		||||
	l := length
 | 
			
		||||
	resetIdx := 0
 | 
			
		||||
	for i := 0; i < len(dst.entries); i++ {
 | 
			
		||||
		var short string
 | 
			
		||||
		extended := true
 | 
			
		||||
		for extended {
 | 
			
		||||
			extended = false
 | 
			
		||||
			if len(dst.entries[i].val) <= l {
 | 
			
		||||
				short = dst.entries[i].digest.String()
 | 
			
		||||
			} else {
 | 
			
		||||
				short = dst.entries[i].val[:l]
 | 
			
		||||
				for j := i + 1; j < len(dst.entries); j++ {
 | 
			
		||||
					if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) {
 | 
			
		||||
						if j > resetIdx {
 | 
			
		||||
							resetIdx = j
 | 
			
		||||
						}
 | 
			
		||||
						extended = true
 | 
			
		||||
					} else {
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if extended {
 | 
			
		||||
					l++
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		m[dst.entries[i].digest] = short
 | 
			
		||||
		if i >= resetIdx {
 | 
			
		||||
			l = length
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type digestEntry struct {
 | 
			
		||||
	alg    digest.Algorithm
 | 
			
		||||
	val    string
 | 
			
		||||
	digest digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type digestEntries []*digestEntry
 | 
			
		||||
 | 
			
		||||
func (d digestEntries) Len() int {
 | 
			
		||||
	return len(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d digestEntries) Less(i, j int) bool {
 | 
			
		||||
	if d[i].val != d[j].val {
 | 
			
		||||
		return d[i].val < d[j].val
 | 
			
		||||
	}
 | 
			
		||||
	return d[i].alg < d[j].alg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d digestEntries) Swap(i, j int) {
 | 
			
		||||
	d[i], d[j] = d[j], d[i]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/docker/distribution/reference/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/docker/distribution/reference/helpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
package reference
 | 
			
		||||
 | 
			
		||||
import "path"
 | 
			
		||||
 | 
			
		||||
// IsNameOnly returns true if reference only contains a repo name.
 | 
			
		||||
func IsNameOnly(ref Named) bool {
 | 
			
		||||
	if _, ok := ref.(NamedTagged); ok {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := ref.(Canonical); ok {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FamiliarName returns the familiar name string
 | 
			
		||||
// for the given named, familiarizing if needed.
 | 
			
		||||
func FamiliarName(ref Named) string {
 | 
			
		||||
	if nn, ok := ref.(normalizedNamed); ok {
 | 
			
		||||
		return nn.Familiar().Name()
 | 
			
		||||
	}
 | 
			
		||||
	return ref.Name()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FamiliarString returns the familiar string representation
 | 
			
		||||
// for the given reference, familiarizing if needed.
 | 
			
		||||
func FamiliarString(ref Reference) string {
 | 
			
		||||
	if nn, ok := ref.(normalizedNamed); ok {
 | 
			
		||||
		return nn.Familiar().String()
 | 
			
		||||
	}
 | 
			
		||||
	return ref.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FamiliarMatch reports whether ref matches the specified pattern.
 | 
			
		||||
// See https://godoc.org/path#Match for supported patterns.
 | 
			
		||||
func FamiliarMatch(pattern string, ref Reference) (bool, error) {
 | 
			
		||||
	matched, err := path.Match(pattern, FamiliarString(ref))
 | 
			
		||||
	if namedRef, isNamed := ref.(Named); isNamed && !matched {
 | 
			
		||||
		matched, _ = path.Match(pattern, FamiliarName(namedRef))
 | 
			
		||||
	}
 | 
			
		||||
	return matched, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										199
									
								
								vendor/github.com/docker/distribution/reference/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								vendor/github.com/docker/distribution/reference/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,199 @@
 | 
			
		||||
package reference
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/distribution/digestset"
 | 
			
		||||
	"github.com/opencontainers/go-digest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	legacyDefaultDomain = "index.docker.io"
 | 
			
		||||
	defaultDomain       = "docker.io"
 | 
			
		||||
	officialRepoName    = "library"
 | 
			
		||||
	defaultTag          = "latest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// normalizedNamed represents a name which has been
 | 
			
		||||
// normalized and has a familiar form. A familiar name
 | 
			
		||||
// is what is used in Docker UI. An example normalized
 | 
			
		||||
// name is "docker.io/library/ubuntu" and corresponding
 | 
			
		||||
// familiar name of "ubuntu".
 | 
			
		||||
type normalizedNamed interface {
 | 
			
		||||
	Named
 | 
			
		||||
	Familiar() Named
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseNormalizedNamed parses a string into a named reference
 | 
			
		||||
// transforming a familiar name from Docker UI to a fully
 | 
			
		||||
// qualified reference. If the value may be an identifier
 | 
			
		||||
// use ParseAnyReference.
 | 
			
		||||
func ParseNormalizedNamed(s string) (Named, error) {
 | 
			
		||||
	if ok := anchoredIdentifierRegexp.MatchString(s); ok {
 | 
			
		||||
		return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
 | 
			
		||||
	}
 | 
			
		||||
	domain, remainder := splitDockerDomain(s)
 | 
			
		||||
	var remoteName string
 | 
			
		||||
	if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
 | 
			
		||||
		remoteName = remainder[:tagSep]
 | 
			
		||||
	} else {
 | 
			
		||||
		remoteName = remainder
 | 
			
		||||
	}
 | 
			
		||||
	if strings.ToLower(remoteName) != remoteName {
 | 
			
		||||
		return nil, errors.New("invalid reference format: repository name must be lowercase")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ref, err := Parse(domain + "/" + remainder)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	named, isNamed := ref.(Named)
 | 
			
		||||
	if !isNamed {
 | 
			
		||||
		return nil, fmt.Errorf("reference %s has no name", ref.String())
 | 
			
		||||
	}
 | 
			
		||||
	return named, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseDockerRef normalizes the image reference following the docker convention. This is added
 | 
			
		||||
// mainly for backward compatibility.
 | 
			
		||||
// The reference returned can only be either tagged or digested. For reference contains both tag
 | 
			
		||||
// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@
 | 
			
		||||
// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as
 | 
			
		||||
// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa.
 | 
			
		||||
func ParseDockerRef(ref string) (Named, error) {
 | 
			
		||||
	named, err := ParseNormalizedNamed(ref)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := named.(NamedTagged); ok {
 | 
			
		||||
		if canonical, ok := named.(Canonical); ok {
 | 
			
		||||
			// The reference is both tagged and digested, only
 | 
			
		||||
			// return digested.
 | 
			
		||||
			newNamed, err := WithName(canonical.Name())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			newCanonical, err := WithDigest(newNamed, canonical.Digest())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			return newCanonical, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return TagNameOnly(named), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// splitDockerDomain splits a repository name to domain and remotename string.
 | 
			
		||||
// If no valid domain is found, the default domain is used. Repository name
 | 
			
		||||
// needs to be already validated before.
 | 
			
		||||
func splitDockerDomain(name string) (domain, remainder string) {
 | 
			
		||||
	i := strings.IndexRune(name, '/')
 | 
			
		||||
	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
 | 
			
		||||
		domain, remainder = defaultDomain, name
 | 
			
		||||
	} else {
 | 
			
		||||
		domain, remainder = name[:i], name[i+1:]
 | 
			
		||||
	}
 | 
			
		||||
	if domain == legacyDefaultDomain {
 | 
			
		||||
		domain = defaultDomain
 | 
			
		||||
	}
 | 
			
		||||
	if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
 | 
			
		||||
		remainder = officialRepoName + "/" + remainder
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// familiarizeName returns a shortened version of the name familiar
 | 
			
		||||
// to to the Docker UI. Familiar names have the default domain
 | 
			
		||||
// "docker.io" and "library/" repository prefix removed.
 | 
			
		||||
// For example, "docker.io/library/redis" will have the familiar
 | 
			
		||||
// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
 | 
			
		||||
// Returns a familiarized named only reference.
 | 
			
		||||
func familiarizeName(named namedRepository) repository {
 | 
			
		||||
	repo := repository{
 | 
			
		||||
		domain: named.Domain(),
 | 
			
		||||
		path:   named.Path(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if repo.domain == defaultDomain {
 | 
			
		||||
		repo.domain = ""
 | 
			
		||||
		// Handle official repositories which have the pattern "library/<official repo name>"
 | 
			
		||||
		if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
 | 
			
		||||
			repo.path = split[1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return repo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r reference) Familiar() Named {
 | 
			
		||||
	return reference{
 | 
			
		||||
		namedRepository: familiarizeName(r.namedRepository),
 | 
			
		||||
		tag:             r.tag,
 | 
			
		||||
		digest:          r.digest,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) Familiar() Named {
 | 
			
		||||
	return familiarizeName(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t taggedReference) Familiar() Named {
 | 
			
		||||
	return taggedReference{
 | 
			
		||||
		namedRepository: familiarizeName(t.namedRepository),
 | 
			
		||||
		tag:             t.tag,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c canonicalReference) Familiar() Named {
 | 
			
		||||
	return canonicalReference{
 | 
			
		||||
		namedRepository: familiarizeName(c.namedRepository),
 | 
			
		||||
		digest:          c.digest,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TagNameOnly adds the default tag "latest" to a reference if it only has
 | 
			
		||||
// a repo name.
 | 
			
		||||
func TagNameOnly(ref Named) Named {
 | 
			
		||||
	if IsNameOnly(ref) {
 | 
			
		||||
		namedTagged, err := WithTag(ref, defaultTag)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Default tag must be valid, to create a NamedTagged
 | 
			
		||||
			// type with non-validated input the WithTag function
 | 
			
		||||
			// should be used instead
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
		return namedTagged
 | 
			
		||||
	}
 | 
			
		||||
	return ref
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseAnyReference parses a reference string as a possible identifier,
 | 
			
		||||
// full digest, or familiar name.
 | 
			
		||||
func ParseAnyReference(ref string) (Reference, error) {
 | 
			
		||||
	if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
 | 
			
		||||
		return digestReference("sha256:" + ref), nil
 | 
			
		||||
	}
 | 
			
		||||
	if dgst, err := digest.Parse(ref); err == nil {
 | 
			
		||||
		return digestReference(dgst), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ParseNormalizedNamed(ref)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseAnyReferenceWithSet parses a reference string as a possible short
 | 
			
		||||
// identifier to be matched in a digest set, a full digest, or familiar name.
 | 
			
		||||
func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) {
 | 
			
		||||
	if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
 | 
			
		||||
		dgst, err := ds.Lookup(ref)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return digestReference(dgst), nil
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if dgst, err := digest.Parse(ref); err == nil {
 | 
			
		||||
			return digestReference(dgst), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ParseNormalizedNamed(ref)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										433
									
								
								vendor/github.com/docker/distribution/reference/reference.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								vendor/github.com/docker/distribution/reference/reference.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,433 @@
 | 
			
		||||
// Package reference provides a general type to represent any way of referencing images within the registry.
 | 
			
		||||
// Its main purpose is to abstract tags and digests (content-addressable hash).
 | 
			
		||||
//
 | 
			
		||||
// Grammar
 | 
			
		||||
//
 | 
			
		||||
// 	reference                       := name [ ":" tag ] [ "@" digest ]
 | 
			
		||||
//	name                            := [domain '/'] path-component ['/' path-component]*
 | 
			
		||||
//	domain                          := domain-component ['.' domain-component]* [':' port-number]
 | 
			
		||||
//	domain-component                := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
 | 
			
		||||
//	port-number                     := /[0-9]+/
 | 
			
		||||
//	path-component                  := alpha-numeric [separator alpha-numeric]*
 | 
			
		||||
// 	alpha-numeric                   := /[a-z0-9]+/
 | 
			
		||||
//	separator                       := /[_.]|__|[-]*/
 | 
			
		||||
//
 | 
			
		||||
//	tag                             := /[\w][\w.-]{0,127}/
 | 
			
		||||
//
 | 
			
		||||
//	digest                          := digest-algorithm ":" digest-hex
 | 
			
		||||
//	digest-algorithm                := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
 | 
			
		||||
//	digest-algorithm-separator      := /[+.-_]/
 | 
			
		||||
//	digest-algorithm-component      := /[A-Za-z][A-Za-z0-9]*/
 | 
			
		||||
//	digest-hex                      := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
 | 
			
		||||
//
 | 
			
		||||
//	identifier                      := /[a-f0-9]{64}/
 | 
			
		||||
//	short-identifier                := /[a-f0-9]{6,64}/
 | 
			
		||||
package reference
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/opencontainers/go-digest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// NameTotalLengthMax is the maximum total number of characters in a repository name.
 | 
			
		||||
	NameTotalLengthMax = 255
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
 | 
			
		||||
	ErrReferenceInvalidFormat = errors.New("invalid reference format")
 | 
			
		||||
 | 
			
		||||
	// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
 | 
			
		||||
	ErrTagInvalidFormat = errors.New("invalid tag format")
 | 
			
		||||
 | 
			
		||||
	// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
 | 
			
		||||
	ErrDigestInvalidFormat = errors.New("invalid digest format")
 | 
			
		||||
 | 
			
		||||
	// ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters.
 | 
			
		||||
	ErrNameContainsUppercase = errors.New("repository name must be lowercase")
 | 
			
		||||
 | 
			
		||||
	// ErrNameEmpty is returned for empty, invalid repository names.
 | 
			
		||||
	ErrNameEmpty = errors.New("repository name must have at least one component")
 | 
			
		||||
 | 
			
		||||
	// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
 | 
			
		||||
	ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
 | 
			
		||||
 | 
			
		||||
	// ErrNameNotCanonical is returned when a name is not canonical.
 | 
			
		||||
	ErrNameNotCanonical = errors.New("repository name must be canonical")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Reference is an opaque object reference identifier that may include
 | 
			
		||||
// modifiers such as a hostname, name, tag, and digest.
 | 
			
		||||
type Reference interface {
 | 
			
		||||
	// String returns the full reference
 | 
			
		||||
	String() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Field provides a wrapper type for resolving correct reference types when
 | 
			
		||||
// working with encoding.
 | 
			
		||||
type Field struct {
 | 
			
		||||
	reference Reference
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AsField wraps a reference in a Field for encoding.
 | 
			
		||||
func AsField(reference Reference) Field {
 | 
			
		||||
	return Field{reference}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reference unwraps the reference type from the field to
 | 
			
		||||
// return the Reference object. This object should be
 | 
			
		||||
// of the appropriate type to further check for different
 | 
			
		||||
// reference types.
 | 
			
		||||
func (f Field) Reference() Reference {
 | 
			
		||||
	return f.reference
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalText serializes the field to byte text which
 | 
			
		||||
// is the string of the reference.
 | 
			
		||||
func (f Field) MarshalText() (p []byte, err error) {
 | 
			
		||||
	return []byte(f.reference.String()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalText parses text bytes by invoking the
 | 
			
		||||
// reference parser to ensure the appropriately
 | 
			
		||||
// typed reference object is wrapped by field.
 | 
			
		||||
func (f *Field) UnmarshalText(p []byte) error {
 | 
			
		||||
	r, err := Parse(string(p))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f.reference = r
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Named is an object with a full name
 | 
			
		||||
type Named interface {
 | 
			
		||||
	Reference
 | 
			
		||||
	Name() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tagged is an object which has a tag
 | 
			
		||||
type Tagged interface {
 | 
			
		||||
	Reference
 | 
			
		||||
	Tag() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NamedTagged is an object including a name and tag.
 | 
			
		||||
type NamedTagged interface {
 | 
			
		||||
	Named
 | 
			
		||||
	Tag() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Digested is an object which has a digest
 | 
			
		||||
// in which it can be referenced by
 | 
			
		||||
type Digested interface {
 | 
			
		||||
	Reference
 | 
			
		||||
	Digest() digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Canonical reference is an object with a fully unique
 | 
			
		||||
// name including a name with domain and digest
 | 
			
		||||
type Canonical interface {
 | 
			
		||||
	Named
 | 
			
		||||
	Digest() digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// namedRepository is a reference to a repository with a name.
 | 
			
		||||
// A namedRepository has both domain and path components.
 | 
			
		||||
type namedRepository interface {
 | 
			
		||||
	Named
 | 
			
		||||
	Domain() string
 | 
			
		||||
	Path() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Domain returns the domain part of the Named reference
 | 
			
		||||
func Domain(named Named) string {
 | 
			
		||||
	if r, ok := named.(namedRepository); ok {
 | 
			
		||||
		return r.Domain()
 | 
			
		||||
	}
 | 
			
		||||
	domain, _ := splitDomain(named.Name())
 | 
			
		||||
	return domain
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Path returns the name without the domain part of the Named reference
 | 
			
		||||
func Path(named Named) (name string) {
 | 
			
		||||
	if r, ok := named.(namedRepository); ok {
 | 
			
		||||
		return r.Path()
 | 
			
		||||
	}
 | 
			
		||||
	_, path := splitDomain(named.Name())
 | 
			
		||||
	return path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func splitDomain(name string) (string, string) {
 | 
			
		||||
	match := anchoredNameRegexp.FindStringSubmatch(name)
 | 
			
		||||
	if len(match) != 3 {
 | 
			
		||||
		return "", name
 | 
			
		||||
	}
 | 
			
		||||
	return match[1], match[2]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SplitHostname splits a named reference into a
 | 
			
		||||
// hostname and name string. If no valid hostname is
 | 
			
		||||
// found, the hostname is empty and the full value
 | 
			
		||||
// is returned as name
 | 
			
		||||
// DEPRECATED: Use Domain or Path
 | 
			
		||||
func SplitHostname(named Named) (string, string) {
 | 
			
		||||
	if r, ok := named.(namedRepository); ok {
 | 
			
		||||
		return r.Domain(), r.Path()
 | 
			
		||||
	}
 | 
			
		||||
	return splitDomain(named.Name())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse parses s and returns a syntactically valid Reference.
 | 
			
		||||
// If an error was encountered it is returned, along with a nil Reference.
 | 
			
		||||
// NOTE: Parse will not handle short digests.
 | 
			
		||||
func Parse(s string) (Reference, error) {
 | 
			
		||||
	matches := ReferenceRegexp.FindStringSubmatch(s)
 | 
			
		||||
	if matches == nil {
 | 
			
		||||
		if s == "" {
 | 
			
		||||
			return nil, ErrNameEmpty
 | 
			
		||||
		}
 | 
			
		||||
		if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil {
 | 
			
		||||
			return nil, ErrNameContainsUppercase
 | 
			
		||||
		}
 | 
			
		||||
		return nil, ErrReferenceInvalidFormat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(matches[1]) > NameTotalLengthMax {
 | 
			
		||||
		return nil, ErrNameTooLong
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var repo repository
 | 
			
		||||
 | 
			
		||||
	nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
 | 
			
		||||
	if len(nameMatch) == 3 {
 | 
			
		||||
		repo.domain = nameMatch[1]
 | 
			
		||||
		repo.path = nameMatch[2]
 | 
			
		||||
	} else {
 | 
			
		||||
		repo.domain = ""
 | 
			
		||||
		repo.path = matches[1]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ref := reference{
 | 
			
		||||
		namedRepository: repo,
 | 
			
		||||
		tag:             matches[2],
 | 
			
		||||
	}
 | 
			
		||||
	if matches[3] != "" {
 | 
			
		||||
		var err error
 | 
			
		||||
		ref.digest, err = digest.Parse(matches[3])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := getBestReferenceType(ref)
 | 
			
		||||
	if r == nil {
 | 
			
		||||
		return nil, ErrNameEmpty
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseNamed parses s and returns a syntactically valid reference implementing
 | 
			
		||||
// the Named interface. The reference must have a name and be in the canonical
 | 
			
		||||
// form, otherwise an error is returned.
 | 
			
		||||
// If an error was encountered it is returned, along with a nil Reference.
 | 
			
		||||
// NOTE: ParseNamed will not handle short digests.
 | 
			
		||||
func ParseNamed(s string) (Named, error) {
 | 
			
		||||
	named, err := ParseNormalizedNamed(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if named.String() != s {
 | 
			
		||||
		return nil, ErrNameNotCanonical
 | 
			
		||||
	}
 | 
			
		||||
	return named, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithName returns a named object representing the given string. If the input
 | 
			
		||||
// is invalid ErrReferenceInvalidFormat will be returned.
 | 
			
		||||
func WithName(name string) (Named, error) {
 | 
			
		||||
	if len(name) > NameTotalLengthMax {
 | 
			
		||||
		return nil, ErrNameTooLong
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	match := anchoredNameRegexp.FindStringSubmatch(name)
 | 
			
		||||
	if match == nil || len(match) != 3 {
 | 
			
		||||
		return nil, ErrReferenceInvalidFormat
 | 
			
		||||
	}
 | 
			
		||||
	return repository{
 | 
			
		||||
		domain: match[1],
 | 
			
		||||
		path:   match[2],
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithTag combines the name from "name" and the tag from "tag" to form a
 | 
			
		||||
// reference incorporating both the name and the tag.
 | 
			
		||||
func WithTag(name Named, tag string) (NamedTagged, error) {
 | 
			
		||||
	if !anchoredTagRegexp.MatchString(tag) {
 | 
			
		||||
		return nil, ErrTagInvalidFormat
 | 
			
		||||
	}
 | 
			
		||||
	var repo repository
 | 
			
		||||
	if r, ok := name.(namedRepository); ok {
 | 
			
		||||
		repo.domain = r.Domain()
 | 
			
		||||
		repo.path = r.Path()
 | 
			
		||||
	} else {
 | 
			
		||||
		repo.path = name.Name()
 | 
			
		||||
	}
 | 
			
		||||
	if canonical, ok := name.(Canonical); ok {
 | 
			
		||||
		return reference{
 | 
			
		||||
			namedRepository: repo,
 | 
			
		||||
			tag:             tag,
 | 
			
		||||
			digest:          canonical.Digest(),
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return taggedReference{
 | 
			
		||||
		namedRepository: repo,
 | 
			
		||||
		tag:             tag,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDigest combines the name from "name" and the digest from "digest" to form
 | 
			
		||||
// a reference incorporating both the name and the digest.
 | 
			
		||||
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
 | 
			
		||||
	if !anchoredDigestRegexp.MatchString(digest.String()) {
 | 
			
		||||
		return nil, ErrDigestInvalidFormat
 | 
			
		||||
	}
 | 
			
		||||
	var repo repository
 | 
			
		||||
	if r, ok := name.(namedRepository); ok {
 | 
			
		||||
		repo.domain = r.Domain()
 | 
			
		||||
		repo.path = r.Path()
 | 
			
		||||
	} else {
 | 
			
		||||
		repo.path = name.Name()
 | 
			
		||||
	}
 | 
			
		||||
	if tagged, ok := name.(Tagged); ok {
 | 
			
		||||
		return reference{
 | 
			
		||||
			namedRepository: repo,
 | 
			
		||||
			tag:             tagged.Tag(),
 | 
			
		||||
			digest:          digest,
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return canonicalReference{
 | 
			
		||||
		namedRepository: repo,
 | 
			
		||||
		digest:          digest,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TrimNamed removes any tag or digest from the named reference.
 | 
			
		||||
func TrimNamed(ref Named) Named {
 | 
			
		||||
	domain, path := SplitHostname(ref)
 | 
			
		||||
	return repository{
 | 
			
		||||
		domain: domain,
 | 
			
		||||
		path:   path,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getBestReferenceType(ref reference) Reference {
 | 
			
		||||
	if ref.Name() == "" {
 | 
			
		||||
		// Allow digest only references
 | 
			
		||||
		if ref.digest != "" {
 | 
			
		||||
			return digestReference(ref.digest)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if ref.tag == "" {
 | 
			
		||||
		if ref.digest != "" {
 | 
			
		||||
			return canonicalReference{
 | 
			
		||||
				namedRepository: ref.namedRepository,
 | 
			
		||||
				digest:          ref.digest,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return ref.namedRepository
 | 
			
		||||
	}
 | 
			
		||||
	if ref.digest == "" {
 | 
			
		||||
		return taggedReference{
 | 
			
		||||
			namedRepository: ref.namedRepository,
 | 
			
		||||
			tag:             ref.tag,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ref
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type reference struct {
 | 
			
		||||
	namedRepository
 | 
			
		||||
	tag    string
 | 
			
		||||
	digest digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r reference) String() string {
 | 
			
		||||
	return r.Name() + ":" + r.tag + "@" + r.digest.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r reference) Tag() string {
 | 
			
		||||
	return r.tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r reference) Digest() digest.Digest {
 | 
			
		||||
	return r.digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type repository struct {
 | 
			
		||||
	domain string
 | 
			
		||||
	path   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) String() string {
 | 
			
		||||
	return r.Name()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) Name() string {
 | 
			
		||||
	if r.domain == "" {
 | 
			
		||||
		return r.path
 | 
			
		||||
	}
 | 
			
		||||
	return r.domain + "/" + r.path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) Domain() string {
 | 
			
		||||
	return r.domain
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r repository) Path() string {
 | 
			
		||||
	return r.path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type digestReference digest.Digest
 | 
			
		||||
 | 
			
		||||
func (d digestReference) String() string {
 | 
			
		||||
	return digest.Digest(d).String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d digestReference) Digest() digest.Digest {
 | 
			
		||||
	return digest.Digest(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type taggedReference struct {
 | 
			
		||||
	namedRepository
 | 
			
		||||
	tag string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t taggedReference) String() string {
 | 
			
		||||
	return t.Name() + ":" + t.tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t taggedReference) Tag() string {
 | 
			
		||||
	return t.tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type canonicalReference struct {
 | 
			
		||||
	namedRepository
 | 
			
		||||
	digest digest.Digest
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c canonicalReference) String() string {
 | 
			
		||||
	return c.Name() + "@" + c.digest.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c canonicalReference) Digest() digest.Digest {
 | 
			
		||||
	return c.digest
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								vendor/github.com/docker/distribution/reference/regexp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/github.com/docker/distribution/reference/regexp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
package reference
 | 
			
		||||
 | 
			
		||||
import "regexp"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// alphaNumericRegexp defines the alpha numeric atom, typically a
 | 
			
		||||
	// component of names. This only allows lower case characters and digits.
 | 
			
		||||
	alphaNumericRegexp = match(`[a-z0-9]+`)
 | 
			
		||||
 | 
			
		||||
	// separatorRegexp defines the separators allowed to be embedded in name
 | 
			
		||||
	// components. This allow one period, one or two underscore and multiple
 | 
			
		||||
	// dashes.
 | 
			
		||||
	separatorRegexp = match(`(?:[._]|__|[-]*)`)
 | 
			
		||||
 | 
			
		||||
	// nameComponentRegexp restricts registry path component names to start
 | 
			
		||||
	// with at least one letter or number, with following parts able to be
 | 
			
		||||
	// separated by one period, one or two underscore and multiple dashes.
 | 
			
		||||
	nameComponentRegexp = expression(
 | 
			
		||||
		alphaNumericRegexp,
 | 
			
		||||
		optional(repeated(separatorRegexp, alphaNumericRegexp)))
 | 
			
		||||
 | 
			
		||||
	// domainComponentRegexp restricts the registry domain component of a
 | 
			
		||||
	// repository name to start with a component as defined by DomainRegexp
 | 
			
		||||
	// and followed by an optional port.
 | 
			
		||||
	domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
 | 
			
		||||
 | 
			
		||||
	// DomainRegexp defines the structure of potential domain components
 | 
			
		||||
	// that may be part of image names. This is purposely a subset of what is
 | 
			
		||||
	// allowed by DNS to ensure backwards compatibility with Docker image
 | 
			
		||||
	// names.
 | 
			
		||||
	DomainRegexp = expression(
 | 
			
		||||
		domainComponentRegexp,
 | 
			
		||||
		optional(repeated(literal(`.`), domainComponentRegexp)),
 | 
			
		||||
		optional(literal(`:`), match(`[0-9]+`)))
 | 
			
		||||
 | 
			
		||||
	// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
 | 
			
		||||
	TagRegexp = match(`[\w][\w.-]{0,127}`)
 | 
			
		||||
 | 
			
		||||
	// anchoredTagRegexp matches valid tag names, anchored at the start and
 | 
			
		||||
	// end of the matched string.
 | 
			
		||||
	anchoredTagRegexp = anchored(TagRegexp)
 | 
			
		||||
 | 
			
		||||
	// DigestRegexp matches valid digests.
 | 
			
		||||
	DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
 | 
			
		||||
 | 
			
		||||
	// anchoredDigestRegexp matches valid digests, anchored at the start and
 | 
			
		||||
	// end of the matched string.
 | 
			
		||||
	anchoredDigestRegexp = anchored(DigestRegexp)
 | 
			
		||||
 | 
			
		||||
	// NameRegexp is the format for the name component of references. The
 | 
			
		||||
	// regexp has capturing groups for the domain and name part omitting
 | 
			
		||||
	// the separating forward slash from either.
 | 
			
		||||
	NameRegexp = expression(
 | 
			
		||||
		optional(DomainRegexp, literal(`/`)),
 | 
			
		||||
		nameComponentRegexp,
 | 
			
		||||
		optional(repeated(literal(`/`), nameComponentRegexp)))
 | 
			
		||||
 | 
			
		||||
	// anchoredNameRegexp is used to parse a name value, capturing the
 | 
			
		||||
	// domain and trailing components.
 | 
			
		||||
	anchoredNameRegexp = anchored(
 | 
			
		||||
		optional(capture(DomainRegexp), literal(`/`)),
 | 
			
		||||
		capture(nameComponentRegexp,
 | 
			
		||||
			optional(repeated(literal(`/`), nameComponentRegexp))))
 | 
			
		||||
 | 
			
		||||
	// ReferenceRegexp is the full supported format of a reference. The regexp
 | 
			
		||||
	// is anchored and has capturing groups for name, tag, and digest
 | 
			
		||||
	// components.
 | 
			
		||||
	ReferenceRegexp = anchored(capture(NameRegexp),
 | 
			
		||||
		optional(literal(":"), capture(TagRegexp)),
 | 
			
		||||
		optional(literal("@"), capture(DigestRegexp)))
 | 
			
		||||
 | 
			
		||||
	// IdentifierRegexp is the format for string identifier used as a
 | 
			
		||||
	// content addressable identifier using sha256. These identifiers
 | 
			
		||||
	// are like digests without the algorithm, since sha256 is used.
 | 
			
		||||
	IdentifierRegexp = match(`([a-f0-9]{64})`)
 | 
			
		||||
 | 
			
		||||
	// ShortIdentifierRegexp is the format used to represent a prefix
 | 
			
		||||
	// of an identifier. A prefix may be used to match a sha256 identifier
 | 
			
		||||
	// within a list of trusted identifiers.
 | 
			
		||||
	ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
 | 
			
		||||
 | 
			
		||||
	// anchoredIdentifierRegexp is used to check or match an
 | 
			
		||||
	// identifier value, anchored at start and end of string.
 | 
			
		||||
	anchoredIdentifierRegexp = anchored(IdentifierRegexp)
 | 
			
		||||
 | 
			
		||||
	// anchoredShortIdentifierRegexp is used to check if a value
 | 
			
		||||
	// is a possible identifier prefix, anchored at start and end
 | 
			
		||||
	// of string.
 | 
			
		||||
	anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// match compiles the string to a regular expression.
 | 
			
		||||
var match = regexp.MustCompile
 | 
			
		||||
 | 
			
		||||
// literal compiles s into a literal regular expression, escaping any regexp
 | 
			
		||||
// reserved characters.
 | 
			
		||||
func literal(s string) *regexp.Regexp {
 | 
			
		||||
	re := match(regexp.QuoteMeta(s))
 | 
			
		||||
 | 
			
		||||
	if _, complete := re.LiteralPrefix(); !complete {
 | 
			
		||||
		panic("must be a literal")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return re
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// expression defines a full expression, where each regular expression must
 | 
			
		||||
// follow the previous.
 | 
			
		||||
func expression(res ...*regexp.Regexp) *regexp.Regexp {
 | 
			
		||||
	var s string
 | 
			
		||||
	for _, re := range res {
 | 
			
		||||
		s += re.String()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return match(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// optional wraps the expression in a non-capturing group and makes the
 | 
			
		||||
// production optional.
 | 
			
		||||
func optional(res ...*regexp.Regexp) *regexp.Regexp {
 | 
			
		||||
	return match(group(expression(res...)).String() + `?`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// repeated wraps the regexp in a non-capturing group to get one or more
 | 
			
		||||
// matches.
 | 
			
		||||
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
 | 
			
		||||
	return match(group(expression(res...)).String() + `+`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// group wraps the regexp in a non-capturing group.
 | 
			
		||||
func group(res ...*regexp.Regexp) *regexp.Regexp {
 | 
			
		||||
	return match(`(?:` + expression(res...).String() + `)`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// capture wraps the expression in a capturing group.
 | 
			
		||||
func capture(res ...*regexp.Regexp) *regexp.Regexp {
 | 
			
		||||
	return match(`(` + expression(res...).String() + `)`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// anchored anchors the regular expression by adding start and end delimiters.
 | 
			
		||||
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
 | 
			
		||||
	return match(`^` + expression(res...).String() + `$`)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										267
									
								
								vendor/github.com/docker/distribution/registry/api/errcode/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								vendor/github.com/docker/distribution/registry/api/errcode/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,267 @@
 | 
			
		||||
package errcode
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrorCoder is the base interface for ErrorCode and Error allowing
 | 
			
		||||
// users of each to just call ErrorCode to get the real ID of each
 | 
			
		||||
type ErrorCoder interface {
 | 
			
		||||
	ErrorCode() ErrorCode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrorCode represents the error type. The errors are serialized via strings
 | 
			
		||||
// and the integer format may change and should *never* be exported.
 | 
			
		||||
type ErrorCode int
 | 
			
		||||
 | 
			
		||||
var _ error = ErrorCode(0)
 | 
			
		||||
 | 
			
		||||
// ErrorCode just returns itself
 | 
			
		||||
func (ec ErrorCode) ErrorCode() ErrorCode {
 | 
			
		||||
	return ec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns the ID/Value
 | 
			
		||||
func (ec ErrorCode) Error() string {
 | 
			
		||||
	// NOTE(stevvooe): Cannot use message here since it may have unpopulated args.
 | 
			
		||||
	return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Descriptor returns the descriptor for the error code.
 | 
			
		||||
func (ec ErrorCode) Descriptor() ErrorDescriptor {
 | 
			
		||||
	d, ok := errorCodeToDescriptors[ec]
 | 
			
		||||
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return ErrorCodeUnknown.Descriptor()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns the canonical identifier for this error code.
 | 
			
		||||
func (ec ErrorCode) String() string {
 | 
			
		||||
	return ec.Descriptor().Value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message returned the human-readable error message for this error code.
 | 
			
		||||
func (ec ErrorCode) Message() string {
 | 
			
		||||
	return ec.Descriptor().Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalText encodes the receiver into UTF-8-encoded text and returns the
 | 
			
		||||
// result.
 | 
			
		||||
func (ec ErrorCode) MarshalText() (text []byte, err error) {
 | 
			
		||||
	return []byte(ec.String()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalText decodes the form generated by MarshalText.
 | 
			
		||||
func (ec *ErrorCode) UnmarshalText(text []byte) error {
 | 
			
		||||
	desc, ok := idToDescriptors[string(text)]
 | 
			
		||||
 | 
			
		||||
	if !ok {
 | 
			
		||||
		desc = ErrorCodeUnknown.Descriptor()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*ec = desc.Code
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithMessage creates a new Error struct based on the passed-in info and
 | 
			
		||||
// overrides the Message property.
 | 
			
		||||
func (ec ErrorCode) WithMessage(message string) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    ec,
 | 
			
		||||
		Message: message,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDetail creates a new Error struct based on the passed-in info and
 | 
			
		||||
// set the Detail property appropriately
 | 
			
		||||
func (ec ErrorCode) WithDetail(detail interface{}) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    ec,
 | 
			
		||||
		Message: ec.Message(),
 | 
			
		||||
	}.WithDetail(detail)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithArgs creates a new Error struct and sets the Args slice
 | 
			
		||||
func (ec ErrorCode) WithArgs(args ...interface{}) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    ec,
 | 
			
		||||
		Message: ec.Message(),
 | 
			
		||||
	}.WithArgs(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error provides a wrapper around ErrorCode with extra Details provided.
 | 
			
		||||
type Error struct {
 | 
			
		||||
	Code    ErrorCode   `json:"code"`
 | 
			
		||||
	Message string      `json:"message"`
 | 
			
		||||
	Detail  interface{} `json:"detail,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// TODO(duglin): See if we need an "args" property so we can do the
 | 
			
		||||
	// variable substitution right before showing the message to the user
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ error = Error{}
 | 
			
		||||
 | 
			
		||||
// ErrorCode returns the ID/Value of this Error
 | 
			
		||||
func (e Error) ErrorCode() ErrorCode {
 | 
			
		||||
	return e.Code
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error returns a human readable representation of the error.
 | 
			
		||||
func (e Error) Error() string {
 | 
			
		||||
	return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDetail will return a new Error, based on the current one, but with
 | 
			
		||||
// some Detail info added
 | 
			
		||||
func (e Error) WithDetail(detail interface{}) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    e.Code,
 | 
			
		||||
		Message: e.Message,
 | 
			
		||||
		Detail:  detail,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithArgs uses the passed-in list of interface{} as the substitution
 | 
			
		||||
// variables in the Error's Message string, but returns a new Error
 | 
			
		||||
func (e Error) WithArgs(args ...interface{}) Error {
 | 
			
		||||
	return Error{
 | 
			
		||||
		Code:    e.Code,
 | 
			
		||||
		Message: fmt.Sprintf(e.Code.Message(), args...),
 | 
			
		||||
		Detail:  e.Detail,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrorDescriptor provides relevant information about a given error code.
 | 
			
		||||
type ErrorDescriptor struct {
 | 
			
		||||
	// Code is the error code that this descriptor describes.
 | 
			
		||||
	Code ErrorCode
 | 
			
		||||
 | 
			
		||||
	// Value provides a unique, string key, often captilized with
 | 
			
		||||
	// underscores, to identify the error code. This value is used as the
 | 
			
		||||
	// keyed value when serializing api errors.
 | 
			
		||||
	Value string
 | 
			
		||||
 | 
			
		||||
	// Message is a short, human readable decription of the error condition
 | 
			
		||||
	// included in API responses.
 | 
			
		||||
	Message string
 | 
			
		||||
 | 
			
		||||
	// Description provides a complete account of the errors purpose, suitable
 | 
			
		||||
	// for use in documentation.
 | 
			
		||||
	Description string
 | 
			
		||||
 | 
			
		||||
	// HTTPStatusCode provides the http status code that is associated with
 | 
			
		||||
	// this error condition.
 | 
			
		||||
	HTTPStatusCode int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseErrorCode returns the value by the string error code.
 | 
			
		||||
// `ErrorCodeUnknown` will be returned if the error is not known.
 | 
			
		||||
func ParseErrorCode(value string) ErrorCode {
 | 
			
		||||
	ed, ok := idToDescriptors[value]
 | 
			
		||||
	if ok {
 | 
			
		||||
		return ed.Code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ErrorCodeUnknown
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Errors provides the envelope for multiple errors and a few sugar methods
 | 
			
		||||
// for use within the application.
 | 
			
		||||
type Errors []error
 | 
			
		||||
 | 
			
		||||
var _ error = Errors{}
 | 
			
		||||
 | 
			
		||||
func (errs Errors) Error() string {
 | 
			
		||||
	switch len(errs) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return "<nil>"
 | 
			
		||||
	case 1:
 | 
			
		||||
		return errs[0].Error()
 | 
			
		||||
	default:
 | 
			
		||||
		msg := "errors:\n"
 | 
			
		||||
		for _, err := range errs {
 | 
			
		||||
			msg += err.Error() + "\n"
 | 
			
		||||
		}
 | 
			
		||||
		return msg
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Len returns the current number of errors.
 | 
			
		||||
func (errs Errors) Len() int {
 | 
			
		||||
	return len(errs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON converts slice of error, ErrorCode or Error into a
 | 
			
		||||
// slice of Error - then serializes
 | 
			
		||||
func (errs Errors) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	var tmpErrs struct {
 | 
			
		||||
		Errors []Error `json:"errors,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, daErr := range errs {
 | 
			
		||||
		var err Error
 | 
			
		||||
 | 
			
		||||
		switch daErr := daErr.(type) {
 | 
			
		||||
		case ErrorCode:
 | 
			
		||||
			err = daErr.WithDetail(nil)
 | 
			
		||||
		case Error:
 | 
			
		||||
			err = daErr
 | 
			
		||||
		default:
 | 
			
		||||
			err = ErrorCodeUnknown.WithDetail(daErr)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If the Error struct was setup and they forgot to set the
 | 
			
		||||
		// Message field (meaning its "") then grab it from the ErrCode
 | 
			
		||||
		msg := err.Message
 | 
			
		||||
		if msg == "" {
 | 
			
		||||
			msg = err.Code.Message()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tmpErrs.Errors = append(tmpErrs.Errors, Error{
 | 
			
		||||
			Code:    err.Code,
 | 
			
		||||
			Message: msg,
 | 
			
		||||
			Detail:  err.Detail,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return json.Marshal(tmpErrs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON deserializes []Error and then converts it into slice of
 | 
			
		||||
// Error or ErrorCode
 | 
			
		||||
func (errs *Errors) UnmarshalJSON(data []byte) error {
 | 
			
		||||
	var tmpErrs struct {
 | 
			
		||||
		Errors []Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := json.Unmarshal(data, &tmpErrs); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var newErrs Errors
 | 
			
		||||
	for _, daErr := range tmpErrs.Errors {
 | 
			
		||||
		// If Message is empty or exactly matches the Code's message string
 | 
			
		||||
		// then just use the Code, no need for a full Error struct
 | 
			
		||||
		if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) {
 | 
			
		||||
			// Error's w/o details get converted to ErrorCode
 | 
			
		||||
			newErrs = append(newErrs, daErr.Code)
 | 
			
		||||
		} else {
 | 
			
		||||
			// Error's w/ details are untouched
 | 
			
		||||
			newErrs = append(newErrs, Error{
 | 
			
		||||
				Code:    daErr.Code,
 | 
			
		||||
				Message: daErr.Message,
 | 
			
		||||
				Detail:  daErr.Detail,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*errs = newErrs
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								vendor/github.com/docker/distribution/registry/api/errcode/handler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/github.com/docker/distribution/registry/api/errcode/handler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
package errcode
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ServeJSON attempts to serve the errcode in a JSON envelope. It marshals err
 | 
			
		||||
// and sets the content-type header to 'application/json'. It will handle
 | 
			
		||||
// ErrorCoder and Errors, and if necessary will create an envelope.
 | 
			
		||||
func ServeJSON(w http.ResponseWriter, err error) error {
 | 
			
		||||
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
 | 
			
		||||
	var sc int
 | 
			
		||||
 | 
			
		||||
	switch errs := err.(type) {
 | 
			
		||||
	case Errors:
 | 
			
		||||
		if len(errs) < 1 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err, ok := errs[0].(ErrorCoder); ok {
 | 
			
		||||
			sc = err.ErrorCode().Descriptor().HTTPStatusCode
 | 
			
		||||
		}
 | 
			
		||||
	case ErrorCoder:
 | 
			
		||||
		sc = errs.ErrorCode().Descriptor().HTTPStatusCode
 | 
			
		||||
		err = Errors{err} // create an envelope.
 | 
			
		||||
	default:
 | 
			
		||||
		// We just have an unhandled error type, so just place in an envelope
 | 
			
		||||
		// and move along.
 | 
			
		||||
		err = Errors{err}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if sc == 0 {
 | 
			
		||||
		sc = http.StatusInternalServerError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.WriteHeader(sc)
 | 
			
		||||
 | 
			
		||||
	return json.NewEncoder(w).Encode(err)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								vendor/github.com/docker/distribution/registry/api/errcode/register.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								vendor/github.com/docker/distribution/registry/api/errcode/register.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
package errcode
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{}
 | 
			
		||||
	idToDescriptors        = map[string]ErrorDescriptor{}
 | 
			
		||||
	groupToDescriptors     = map[string][]ErrorDescriptor{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrorCodeUnknown is a generic error that can be used as a last
 | 
			
		||||
	// resort if there is no situation-specific error message that can be used
 | 
			
		||||
	ErrorCodeUnknown = Register("errcode", ErrorDescriptor{
 | 
			
		||||
		Value:   "UNKNOWN",
 | 
			
		||||
		Message: "unknown error",
 | 
			
		||||
		Description: `Generic error returned when the error does not have an
 | 
			
		||||
			                                            API classification.`,
 | 
			
		||||
		HTTPStatusCode: http.StatusInternalServerError,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// ErrorCodeUnsupported is returned when an operation is not supported.
 | 
			
		||||
	ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{
 | 
			
		||||
		Value:   "UNSUPPORTED",
 | 
			
		||||
		Message: "The operation is unsupported.",
 | 
			
		||||
		Description: `The operation was unsupported due to a missing
 | 
			
		||||
		implementation or invalid set of parameters.`,
 | 
			
		||||
		HTTPStatusCode: http.StatusMethodNotAllowed,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// ErrorCodeUnauthorized is returned if a request requires
 | 
			
		||||
	// authentication.
 | 
			
		||||
	ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{
 | 
			
		||||
		Value:   "UNAUTHORIZED",
 | 
			
		||||
		Message: "authentication required",
 | 
			
		||||
		Description: `The access controller was unable to authenticate
 | 
			
		||||
		the client. Often this will be accompanied by a
 | 
			
		||||
		Www-Authenticate HTTP response header indicating how to
 | 
			
		||||
		authenticate.`,
 | 
			
		||||
		HTTPStatusCode: http.StatusUnauthorized,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// ErrorCodeDenied is returned if a client does not have sufficient
 | 
			
		||||
	// permission to perform an action.
 | 
			
		||||
	ErrorCodeDenied = Register("errcode", ErrorDescriptor{
 | 
			
		||||
		Value:   "DENIED",
 | 
			
		||||
		Message: "requested access to the resource is denied",
 | 
			
		||||
		Description: `The access controller denied access for the
 | 
			
		||||
		operation on a resource.`,
 | 
			
		||||
		HTTPStatusCode: http.StatusForbidden,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// ErrorCodeUnavailable provides a common error to report unavailability
 | 
			
		||||
	// of a service or endpoint.
 | 
			
		||||
	ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{
 | 
			
		||||
		Value:          "UNAVAILABLE",
 | 
			
		||||
		Message:        "service unavailable",
 | 
			
		||||
		Description:    "Returned when a service is not available",
 | 
			
		||||
		HTTPStatusCode: http.StatusServiceUnavailable,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// ErrorCodeTooManyRequests is returned if a client attempts too many
 | 
			
		||||
	// times to contact a service endpoint.
 | 
			
		||||
	ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{
 | 
			
		||||
		Value:   "TOOMANYREQUESTS",
 | 
			
		||||
		Message: "too many requests",
 | 
			
		||||
		Description: `Returned when a client attempts to contact a
 | 
			
		||||
		service too many times`,
 | 
			
		||||
		HTTPStatusCode: http.StatusTooManyRequests,
 | 
			
		||||
	})
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var nextCode = 1000
 | 
			
		||||
var registerLock sync.Mutex
 | 
			
		||||
 | 
			
		||||
// Register will make the passed-in error known to the environment and
 | 
			
		||||
// return a new ErrorCode
 | 
			
		||||
func Register(group string, descriptor ErrorDescriptor) ErrorCode {
 | 
			
		||||
	registerLock.Lock()
 | 
			
		||||
	defer registerLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	descriptor.Code = ErrorCode(nextCode)
 | 
			
		||||
 | 
			
		||||
	if _, ok := idToDescriptors[descriptor.Value]; ok {
 | 
			
		||||
		panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value))
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := errorCodeToDescriptors[descriptor.Code]; ok {
 | 
			
		||||
		panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	groupToDescriptors[group] = append(groupToDescriptors[group], descriptor)
 | 
			
		||||
	errorCodeToDescriptors[descriptor.Code] = descriptor
 | 
			
		||||
	idToDescriptors[descriptor.Value] = descriptor
 | 
			
		||||
 | 
			
		||||
	nextCode++
 | 
			
		||||
	return descriptor.Code
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type byValue []ErrorDescriptor
 | 
			
		||||
 | 
			
		||||
func (a byValue) Len() int           { return len(a) }
 | 
			
		||||
func (a byValue) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
			
		||||
func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value }
 | 
			
		||||
 | 
			
		||||
// GetGroupNames returns the list of Error group names that are registered
 | 
			
		||||
func GetGroupNames() []string {
 | 
			
		||||
	keys := []string{}
 | 
			
		||||
 | 
			
		||||
	for k := range groupToDescriptors {
 | 
			
		||||
		keys = append(keys, k)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
	return keys
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetErrorCodeGroup returns the named group of error descriptors
 | 
			
		||||
func GetErrorCodeGroup(name string) []ErrorDescriptor {
 | 
			
		||||
	desc := groupToDescriptors[name]
 | 
			
		||||
	sort.Sort(byValue(desc))
 | 
			
		||||
	return desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are
 | 
			
		||||
// registered, irrespective of what group they're in
 | 
			
		||||
func GetErrorAllDescriptors() []ErrorDescriptor {
 | 
			
		||||
	result := []ErrorDescriptor{}
 | 
			
		||||
 | 
			
		||||
	for _, group := range GetGroupNames() {
 | 
			
		||||
		result = append(result, GetErrorCodeGroup(group)...)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(byValue(result))
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user