airshipctl/pkg/clusterctl/implementations/repository.go
Kostiantyn Kalynovskyi 0ab9b6d7ea Add impl of Repository interface from cluster-api
Implementation would allow us to build our own cluster-api components
based on airship document bundle.

Relates-To: #170

Change-Id: Ic029fd07ca5221747e49c3cf48e80a3e279ac1f0
2020-04-27 13:44:14 -05:00

117 lines
3.6 KiB
Go

package implementations
import (
"bytes"
"path/filepath"
"k8s.io/apimachinery/pkg/util/version"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/log"
)
const (
// TODO Add metadata support. Clusterctl uses repository to read metadata.yaml file
// to get clusterctl metadata which is a link between provider version and api version
// that it supports, for example v0.3.0 --> v1alpha3
dummyComponentPath = "components.yaml"
)
// Repository implements Repository from clusterctl project
type Repository struct {
root string
versions map[string]string
defaultVersion string
}
var _ repository.Repository = &Repository{}
// ComponentsPath always returns same value, since it is not relevant without real filesystem
func (r *Repository) ComponentsPath() string {
return dummyComponentPath
}
// GetVersions retrieve all versions from the repository
func (r *Repository) GetVersions() ([]string, error) {
versions := make([]string, 0, len(r.versions))
for availableVersion := range r.versions {
_, err := version.ParseSemantic(availableVersion)
if err != nil {
// discard releases with tags that are not a valid semantic versions (the user can point explicitly to such releases)
continue
}
versions = append(versions, availableVersion)
}
return versions, nil
}
// DefaultVersion highest version available
func (r *Repository) DefaultVersion() string {
return r.defaultVersion
}
// RootPath not relevant without real filesystem
func (r *Repository) RootPath() string {
return r.root
}
// GetFile returns all kubernetes resources that belong to cluster-api
// TODO Add metadata support(don't ignore filepath). Clusterctl uses repository to read
// metadata.yaml file to get clusterctl metadata which is a link between provider version
// and api version that it supports, for example v0.3.0 --> v1alpha3
func (r *Repository) GetFile(version string, _ string) ([]byte, error) {
if version == "latest" {
// default should be latest
version = r.defaultVersion
}
path, ok := r.versions[version]
if !ok {
return nil, ErrVersionNotDefined{Version: version}
}
bundle, err := document.NewBundleByPath(filepath.Join(r.root, path))
if err != nil {
return nil, err
}
filteredBundle, err := bundle.SelectBundle(document.NewDeployToK8sSelector())
if err != nil {
return nil, err
}
buffer := bytes.NewBuffer([]byte{})
err = filteredBundle.Write(buffer)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
// NewRepository builds instance of repository
func NewRepository(root string, versions map[string]string) (repository.Repository, error) {
var latestVersion *version.Version
var latestStringVersion string
// calculate latest version and delete versions that do not obey version syntax
for ver := range versions {
availableSemVersion, err := version.ParseSemantic(ver)
if err != nil {
// ignore and delete version if we can't parse it.
log.Debugf(`Invalid version %s in repository versions map %q, ignoring it. Version must obey the syntax,
semantics of the "Semantic Versioning" specification (http://semver.org/)`, ver, versions)
// delete the version so actual version list is clean
delete(versions, ver)
continue
}
if latestVersion == nil || latestVersion.LessThan(availableSemVersion) {
latestVersion = availableSemVersion
latestStringVersion = ver
}
}
if latestStringVersion == "" {
return nil, ErrNoVersionsAvailable{Versions: versions}
}
return &Repository{
root: root,
versions: versions,
defaultVersion: latestStringVersion}, nil
}