github.com/x-helm/helm@v3.0.0-beta.3+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 "helm.sh/helm/pkg/plugin/installer" 17 18 import ( 19 "os" 20 "sort" 21 22 "github.com/Masterminds/semver" 23 "github.com/Masterminds/vcs" 24 "github.com/pkg/errors" 25 26 "helm.sh/helm/pkg/helmpath" 27 "helm.sh/helm/pkg/plugin/cache" 28 ) 29 30 // VCSInstaller installs plugins from remote a repository. 31 type VCSInstaller struct { 32 Repo vcs.Repo 33 Version string 34 base 35 } 36 37 func existingVCSRepo(location string) (Installer, error) { 38 repo, err := vcs.NewRepo("", location) 39 if err != nil { 40 return nil, err 41 } 42 i := &VCSInstaller{ 43 Repo: repo, 44 base: newBase(repo.Remote()), 45 } 46 return i, err 47 } 48 49 // NewVCSInstaller creates a new VCSInstaller. 50 func NewVCSInstaller(source, version string) (*VCSInstaller, error) { 51 key, err := cache.Key(source) 52 if err != nil { 53 return nil, err 54 } 55 cachedpath := helmpath.CachePath("plugins", key) 56 repo, err := vcs.NewRepo(source, cachedpath) 57 if err != nil { 58 return nil, err 59 } 60 i := &VCSInstaller{ 61 Repo: repo, 62 Version: version, 63 base: newBase(source), 64 } 65 return i, err 66 } 67 68 // Install clones a remote repository and creates a symlink to the plugin directory. 69 // 70 // Implements Installer. 71 func (i *VCSInstaller) Install() error { 72 if err := i.sync(i.Repo); err != nil { 73 return err 74 } 75 76 ref, err := i.solveVersion(i.Repo) 77 if err != nil { 78 return err 79 } 80 if ref != "" { 81 if err := i.setVersion(i.Repo, ref); err != nil { 82 return err 83 } 84 } 85 86 if !isPlugin(i.Repo.LocalPath()) { 87 return ErrMissingMetadata 88 } 89 90 return i.link(i.Repo.LocalPath()) 91 } 92 93 // Update updates a remote repository 94 func (i *VCSInstaller) Update() error { 95 debug("updating %s", i.Repo.Remote()) 96 if i.Repo.IsDirty() { 97 return errors.New("plugin repo was modified") 98 } 99 if err := i.Repo.Update(); err != nil { 100 return err 101 } 102 if !isPlugin(i.Repo.LocalPath()) { 103 return ErrMissingMetadata 104 } 105 return nil 106 } 107 108 func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { 109 if i.Version == "" { 110 return "", nil 111 } 112 113 if repo.IsReference(i.Version) { 114 return i.Version, nil 115 } 116 117 // Create the constraint first to make sure it's valid before 118 // working on the repo. 119 constraint, err := semver.NewConstraint(i.Version) 120 if err != nil { 121 return "", err 122 } 123 124 // Get the tags 125 refs, err := repo.Tags() 126 if err != nil { 127 return "", err 128 } 129 debug("found refs: %s", refs) 130 131 // Convert and filter the list to semver.Version instances 132 semvers := getSemVers(refs) 133 134 // Sort semver list 135 sort.Sort(sort.Reverse(semver.Collection(semvers))) 136 for _, v := range semvers { 137 if constraint.Check(v) { 138 // If the constrint passes get the original reference 139 ver := v.Original() 140 debug("setting to %s", ver) 141 return ver, nil 142 } 143 } 144 145 return "", errors.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote()) 146 } 147 148 // setVersion attempts to checkout the version 149 func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error { 150 debug("setting version to %q", i.Version) 151 return repo.UpdateVersion(ref) 152 } 153 154 // sync will clone or update a remote repo. 155 func (i *VCSInstaller) sync(repo vcs.Repo) error { 156 if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) { 157 debug("cloning %s to %s", repo.Remote(), repo.LocalPath()) 158 return repo.Get() 159 } 160 debug("updating %s", repo.Remote()) 161 return repo.Update() 162 } 163 164 // Filter a list of versions to only included semantic versions. The response 165 // is a mapping of the original version to the semantic version. 166 func getSemVers(refs []string) []*semver.Version { 167 var sv []*semver.Version 168 for _, r := range refs { 169 if v, err := semver.NewVersion(r); err == nil { 170 sv = append(sv, v) 171 } 172 } 173 return sv 174 }