github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+incompatible/pkg/plugin/installer/vcs_installer.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 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 "os" 20 "sort" 21 22 "github.com/Masterminds/semver" 23 "github.com/Masterminds/vcs" 24 25 "k8s.io/helm/pkg/helm/helmpath" 26 "k8s.io/helm/pkg/plugin/cache" 27 ) 28 29 // VCSInstaller installs plugins from remote a repository. 30 type VCSInstaller struct { 31 Repo vcs.Repo 32 Version string 33 base 34 } 35 36 // NewVCSInstaller creates a new VCSInstaller. 37 func NewVCSInstaller(source, version string, home helmpath.Home) (*VCSInstaller, error) { 38 key, err := cache.Key(source) 39 if err != nil { 40 return nil, err 41 } 42 cachedpath := home.Path("cache", "plugins", key) 43 repo, err := vcs.NewRepo(source, cachedpath) 44 if err != nil { 45 return nil, err 46 } 47 i := &VCSInstaller{ 48 Repo: repo, 49 Version: version, 50 base: newBase(source, home), 51 } 52 return i, err 53 } 54 55 // Install clones a remote repository and creates a symlink to the plugin directory in HELM_HOME. 56 // 57 // Implements Installer. 58 func (i *VCSInstaller) Install() error { 59 if err := i.sync(i.Repo); err != nil { 60 return err 61 } 62 63 ref, err := i.solveVersion(i.Repo) 64 if err != nil { 65 return err 66 } 67 68 if err := i.setVersion(i.Repo, ref); err != nil { 69 return err 70 } 71 72 if !isPlugin(i.Repo.LocalPath()) { 73 return ErrMissingMetadata 74 } 75 76 return i.link(i.Repo.LocalPath()) 77 } 78 79 func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { 80 if i.Version == "" { 81 return "", nil 82 } 83 84 if repo.IsReference(i.Version) { 85 return i.Version, nil 86 } 87 88 // Create the constraint first to make sure it's valid before 89 // working on the repo. 90 constraint, err := semver.NewConstraint(i.Version) 91 if err != nil { 92 return "", err 93 } 94 95 // Get the tags and branches (in that order) 96 refs, err := repo.Tags() 97 if err != nil { 98 return "", err 99 } 100 debug("found refs: %s", refs) 101 102 // Convert and filter the list to semver.Version instances 103 semvers := getSemVers(refs) 104 105 // Sort semver list 106 sort.Sort(sort.Reverse(semver.Collection(semvers))) 107 for _, v := range semvers { 108 if constraint.Check(v) { 109 // If the constrint passes get the original reference 110 ver := v.Original() 111 debug("setting to %s", ver) 112 return ver, nil 113 } 114 } 115 return "", nil 116 } 117 118 // setVersion attempts to checkout the version 119 func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error { 120 debug("setting version to %q", i.Version) 121 return repo.UpdateVersion(ref) 122 } 123 124 // sync will clone or update a remote repo. 125 func (i *VCSInstaller) sync(repo vcs.Repo) error { 126 if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) { 127 debug("cloning %s to %s", repo.Remote(), repo.LocalPath()) 128 return repo.Get() 129 } 130 debug("updating %s", repo.Remote()) 131 return repo.Update() 132 } 133 134 // Filter a list of versions to only included semantic versions. The response 135 // is a mapping of the original version to the semantic version. 136 func getSemVers(refs []string) []*semver.Version { 137 var sv []*semver.Version 138 for _, r := range refs { 139 v, err := semver.NewVersion(r) 140 if err == nil { 141 sv = append(sv, v) 142 } 143 } 144 return sv 145 }