You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
helm/pkg/plugin/installer/vcs_installer.go

175 lines
4.1 KiB

/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "helm.sh/helm/v3/pkg/plugin/installer"
import (
"os"
"sort"
"github.com/Masterminds/semver/v3"
"github.com/Masterminds/vcs"
"github.com/pkg/errors"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/plugin/cache"
)
// VCSInstaller installs plugins from remote a repository.
type VCSInstaller struct {
Repo vcs.Repo
Version string
base
}
func existingVCSRepo(location string) (Installer, error) {
repo, err := vcs.NewRepo("", location)
if err != nil {
return nil, err
}
i := &VCSInstaller{
Repo: repo,
base: newBase(repo.Remote()),
}
return i, err
}
// NewVCSInstaller creates a new VCSInstaller.
func NewVCSInstaller(source, version string) (*VCSInstaller, error) {
key, err := cache.Key(source)
if err != nil {
return nil, err
}
cachedpath := helmpath.CachePath("plugins", key)
repo, err := vcs.NewRepo(source, cachedpath)
if err != nil {
return nil, err
}
i := &VCSInstaller{
Repo: repo,
Version: version,
base: newBase(source),
}
return i, err
}
// Install clones a remote repository and creates a symlink to the plugin directory.
//
// Implements Installer.
func (i *VCSInstaller) Install() error {
if err := i.sync(i.Repo); err != nil {
return err
}
ref, err := i.solveVersion(i.Repo)
if err != nil {
return err
}
if ref != "" {
if err := i.setVersion(i.Repo, ref); err != nil {
return err
}
}
if !isPlugin(i.Repo.LocalPath()) {
return ErrMissingMetadata
}
return i.link(i.Repo.LocalPath())
}
// Update updates a remote repository
func (i *VCSInstaller) Update() error {
debug("updating %s", i.Repo.Remote())
if i.Repo.IsDirty() {
return errors.New("plugin repo was modified")
}
if err := i.Repo.Update(); err != nil {
return err
}
if !isPlugin(i.Repo.LocalPath()) {
return ErrMissingMetadata
}
return nil
}
func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) {
if i.Version == "" {
return "", nil
}
if repo.IsReference(i.Version) {
return i.Version, nil
}
// Create the constraint first to make sure it's valid before
// working on the repo.
constraint, err := semver.NewConstraint(i.Version)
if err != nil {
return "", err
}
// Get the tags
refs, err := repo.Tags()
if err != nil {
return "", err
}
debug("found refs: %s", refs)
// Convert and filter the list to semver.Version instances
semvers := getSemVers(refs)
// Sort semver list
sort.Sort(sort.Reverse(semver.Collection(semvers)))
for _, v := range semvers {
if constraint.Check(v) {
// If the constrint passes get the original reference
ver := v.Original()
debug("setting to %s", ver)
return ver, nil
}
}
return "", errors.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote())
}
// setVersion attempts to checkout the version
func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error {
debug("setting version to %q", i.Version)
return repo.UpdateVersion(ref)
}
// sync will clone or update a remote repo.
func (i *VCSInstaller) sync(repo vcs.Repo) error {
if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) {
debug("cloning %s to %s", repo.Remote(), repo.LocalPath())
return repo.Get()
}
debug("updating %s", repo.Remote())
return repo.Update()
}
// Filter a list of versions to only included semantic versions. The response
// is a mapping of the original version to the semantic version.
func getSemVers(refs []string) []*semver.Version {
var sv []*semver.Version
for _, r := range refs {
if v, err := semver.NewVersion(r); err == nil {
sv = append(sv, v)
}
}
return sv
}