github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/pkg/registry/reference.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package registry // import "helm.sh/helm/pkg/registry" 18 19 import ( 20 "errors" 21 "regexp" 22 "strings" 23 24 "github.com/containerd/containerd/reference" 25 ) 26 27 var ( 28 validPortRegEx = regexp.MustCompile(`^([1-9]\d{0,3}|0|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`) // adapted from https://stackoverflow.com/a/12968117 29 errEmptyRepo = errors.New("parsed repo was empty") 30 errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number") 31 ) 32 33 type ( 34 // Reference defines the main components of a reference specification 35 Reference struct { 36 *reference.Spec 37 Tag string 38 Repo string 39 } 40 ) 41 42 // ParseReference converts a string to a Reference 43 func ParseReference(s string) (*Reference, error) { 44 spec, err := reference.Parse(s) 45 if err != nil { 46 return nil, err 47 } 48 49 // convert to our custom type and make necessary mods 50 ref := Reference{Spec: &spec} 51 ref.setExtraFields() 52 53 // ensure the reference is valid 54 err = ref.validate() 55 if err != nil { 56 return nil, err 57 } 58 59 return &ref, nil 60 } 61 62 // setExtraFields adds the Repo and Tag fields to a Reference 63 func (ref *Reference) setExtraFields() { 64 ref.Tag = ref.Object 65 ref.Repo = ref.Locator 66 ref.fixNoTag() 67 ref.fixNoRepo() 68 } 69 70 // fixNoTag is a fix for ref strings such as "mychart:1.0.0", which result in missing tag 71 func (ref *Reference) fixNoTag() { 72 if ref.Tag == "" { 73 parts := strings.Split(ref.Repo, ":") 74 numParts := len(parts) 75 if 0 < numParts { 76 lastIndex := numParts - 1 77 lastPart := parts[lastIndex] 78 if !strings.Contains(lastPart, "/") { 79 ref.Repo = strings.Join(parts[:lastIndex], ":") 80 ref.Tag = lastPart 81 } 82 } 83 } 84 } 85 86 // fixNoRepo is a fix for ref strings such as "mychart", which have the repo swapped with tag 87 func (ref *Reference) fixNoRepo() { 88 if ref.Repo == "" { 89 ref.Repo = ref.Tag 90 ref.Tag = "" 91 } 92 } 93 94 // validate makes sure the ref meets our criteria 95 func (ref *Reference) validate() error { 96 err := ref.validateRepo() 97 if err != nil { 98 return err 99 } 100 return ref.validateNumColons() 101 } 102 103 // validateRepo checks that the Repo field is non-empty 104 func (ref *Reference) validateRepo() error { 105 if ref.Repo == "" { 106 return errEmptyRepo 107 } 108 return nil 109 } 110 111 // validateNumColon ensures the ref only contains a single colon character (:) 112 // (or potentially two, there might be a port number specified i.e. :5000) 113 func (ref *Reference) validateNumColons() error { 114 if strings.Contains(ref.Tag, ":") { 115 return errTooManyColons 116 } 117 parts := strings.Split(ref.Repo, ":") 118 lastIndex := len(parts) - 1 119 if 1 < lastIndex { 120 return errTooManyColons 121 } 122 if 0 < lastIndex { 123 port := strings.Split(parts[lastIndex], "/")[0] 124 if !isValidPort(port) { 125 return errTooManyColons 126 } 127 } 128 return nil 129 } 130 131 // isValidPort returns whether or not a string looks like a valid port 132 func isValidPort(s string) bool { 133 return validPortRegEx.MatchString(s) 134 }