airshipctl/pkg/clusterctl/implementations/repository.go
Kostiantyn Kalynovskyi 391525a165 Add reader interface
With this implementation reader is an in memory interface that allows
to build clusterctl config based airshipctl documents

Relates-To: #200
Closes: #200

Change-Id: If4a5fbd5c8402c958563cdfc939fc579289b0bfb
2020-05-06 13:13:29 -05:00

140 lines
4.2 KiB
Go

/*
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
https://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 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 (
metaDataFilePath = "metadata.yaml"
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
func (r *Repository) GetFile(version string, filePath 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}
}
kustomizePath := filepath.Join(r.root, path)
log.Debugf("Building cluster-api provider component documents from kustomize path at %s", kustomizePath)
bundle, err := document.NewBundleByPath(kustomizePath)
if err != nil {
return nil, err
}
// TODO when clusterctl will have a defined set of expected files in repository
// revisit this implementation
// metadata.yaml should return a bytes containing clusterctlv1.Metadata or error
if filePath == metaDataFilePath {
doc, errMeta := bundle.SelectOne(document.NewClusterctlMetadataSelector())
if errMeta != nil {
return nil, errMeta
}
return doc.AsYAML()
}
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.
fmtMsg := "Invalid version %s in repository versions map %q, ignoring it. " +
"Version must obey the the Semantic Versioning specification (http://semver.org/)"
log.Debugf(fmtMsg, 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
}