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 }