github.com/koderover/helm@v2.17.0+incompatible/pkg/plugin/installer/vcs_installer.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package installer // import "k8s.io/helm/pkg/plugin/installer"
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"os"
    22  	"sort"
    23  
    24  	"github.com/Masterminds/semver"
    25  	"github.com/Masterminds/vcs"
    26  
    27  	"k8s.io/helm/pkg/helm/helmpath"
    28  	"k8s.io/helm/pkg/plugin/cache"
    29  )
    30  
    31  // VCSInstaller installs plugins from remote a repository.
    32  type VCSInstaller struct {
    33  	Repo    vcs.Repo
    34  	Version string
    35  	base
    36  }
    37  
    38  func existingVCSRepo(location string, home helmpath.Home) (Installer, error) {
    39  	repo, err := vcs.NewRepo("", location)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	i := &VCSInstaller{
    44  		Repo: repo,
    45  		base: newBase(repo.Remote(), home),
    46  	}
    47  	return i, err
    48  }
    49  
    50  // NewVCSInstaller creates a new VCSInstaller.
    51  func NewVCSInstaller(source, version string, home helmpath.Home) (*VCSInstaller, error) {
    52  	key, err := cache.Key(source)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	cachedpath := home.Path("cache", "plugins", key)
    57  	repo, err := vcs.NewRepo(source, cachedpath)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	i := &VCSInstaller{
    62  		Repo:    repo,
    63  		Version: version,
    64  		base:    newBase(source, home),
    65  	}
    66  	return i, err
    67  }
    68  
    69  // Install clones a remote repository and creates a symlink to the plugin directory in HELM_HOME.
    70  //
    71  // Implements Installer.
    72  func (i *VCSInstaller) Install() error {
    73  	if err := i.sync(i.Repo); err != nil {
    74  		return err
    75  	}
    76  
    77  	ref, err := i.solveVersion(i.Repo)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	if ref != "" {
    82  		if err := i.setVersion(i.Repo, ref); err != nil {
    83  			return err
    84  		}
    85  	}
    86  
    87  	if !isPlugin(i.Repo.LocalPath()) {
    88  		return ErrMissingMetadata
    89  	}
    90  
    91  	return i.link(i.Repo.LocalPath())
    92  }
    93  
    94  // Update updates a remote repository
    95  func (i *VCSInstaller) Update() error {
    96  	debug("updating %s", i.Repo.Remote())
    97  	if i.Repo.IsDirty() {
    98  		return errors.New("plugin repo was modified")
    99  	}
   100  	if err := i.Repo.Update(); err != nil {
   101  		return err
   102  	}
   103  	if !isPlugin(i.Repo.LocalPath()) {
   104  		return ErrMissingMetadata
   105  	}
   106  	return nil
   107  }
   108  
   109  func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) {
   110  	if i.Version == "" {
   111  		return "", nil
   112  	}
   113  
   114  	if repo.IsReference(i.Version) {
   115  		return i.Version, nil
   116  	}
   117  
   118  	// Create the constraint first to make sure it's valid before
   119  	// working on the repo.
   120  	constraint, err := semver.NewConstraint(i.Version)
   121  	if err != nil {
   122  		return "", err
   123  	}
   124  
   125  	// Get the tags
   126  	refs, err := repo.Tags()
   127  	if err != nil {
   128  		return "", err
   129  	}
   130  	debug("found refs: %s", refs)
   131  
   132  	// Convert and filter the list to semver.Version instances
   133  	semvers := getSemVers(refs)
   134  
   135  	// Sort semver list
   136  	sort.Sort(sort.Reverse(semver.Collection(semvers)))
   137  	for _, v := range semvers {
   138  		if constraint.Check(v) {
   139  			// If the constraint passes get the original reference
   140  			ver := v.Original()
   141  			debug("setting to %s", ver)
   142  			return ver, nil
   143  		}
   144  	}
   145  
   146  	return "", fmt.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote())
   147  }
   148  
   149  // setVersion attempts to checkout the version
   150  func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error {
   151  	debug("setting version to %q", i.Version)
   152  	return repo.UpdateVersion(ref)
   153  }
   154  
   155  // sync will clone or update a remote repo.
   156  func (i *VCSInstaller) sync(repo vcs.Repo) error {
   157  	if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) {
   158  		debug("cloning %s to %s", repo.Remote(), repo.LocalPath())
   159  		return repo.Get()
   160  	}
   161  	debug("updating %s", repo.Remote())
   162  	return repo.Update()
   163  }
   164  
   165  // Filter a list of versions to only included semantic versions. The response
   166  // is a mapping of the original version to the semantic version.
   167  func getSemVers(refs []string) []*semver.Version {
   168  	var sv []*semver.Version
   169  	for _, r := range refs {
   170  		if v, err := semver.NewVersion(r); err == nil {
   171  			sv = append(sv, v)
   172  		}
   173  	}
   174  	return sv
   175  }