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  }