Skip to content

Commit ae46ed3

Browse files
committed
Added GetVersions() support.
Simplified the directory layout. Incorporated feedback.
1 parent 292bc12 commit ae46ed3

File tree

2 files changed

+200
-114
lines changed

2 files changed

+200
-114
lines changed

cmd/clusterctl/pkg/client/repository/repository_local_unix.go

+83-58
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,23 @@ import (
3333
// path to the components yaml on the local filesystem.
3434
// In order to support different versions, the directories containing provider
3535
// specific data must adhere to the following layout:
36-
// {basepath}/{owner}/{provider-name}/releases/{version}/{path/components.yaml}
36+
// {basepath}/{provider-name}/{version}/{components.yaml}
3737
//
3838
// (1): {provider-name} must match the value returned by Provider.Name()
3939
// (2): {version} must exactly obey the syntax and semantics of
4040
// the "Semantic Versioning" specification (http://semver.org/).
4141
//
4242
// Concrete example:
43-
// /home/user/go/src/sigs.k8s.io/cluster-api-provider-aws/releases/v0.4.7/infrastructure-components.yaml
44-
// basepath: /home/user/go/src
45-
// owner : sigs.k8s.io
46-
// provider-name: cluster-api-provider-aws
43+
// /home/user/go/src/sigs.k8s.io/aws/v0.4.7/infrastructure-components.yaml
44+
// basepath: /home/user/go/src/sigs.k8s.io
45+
// provider-name: aws
4746
// version: v0.4.7
48-
// path/component.yaml: infrastructure-components.yaml
49-
//
47+
// component.yaml: infrastructure-components.yaml
5048

5149
type localRepository struct {
5250
providerConfig config.Provider
5351
configVariablesClient config.VariablesClient
5452
basepath string
55-
owner string
5653
providerName string
5754
defaultVersion string
5855
rootPath string
@@ -89,7 +86,7 @@ func (r *localRepository) GetFile(version, fileName string) ([]byte, error) {
8986
version = r.defaultVersion
9087
}
9188

92-
absolutePath := filepath.Join(r.basepath, r.owner, r.providerName, "releases", version, r.rootPath, fileName)
89+
absolutePath := filepath.Join(r.basepath, r.providerName, version, r.rootPath, fileName)
9390

9491
if f, err := os.Stat(absolutePath); err == nil {
9592
if f.IsDir() {
@@ -105,59 +102,64 @@ func (r *localRepository) GetFile(version, fileName string) ([]byte, error) {
105102

106103
}
107104

105+
// GetVersion returns the list of versions that are available for a local repository
106+
func (r *localRepository) GetVersions() ([]string, error) {
107+
versions, err := r.getVersions()
108+
if err != nil {
109+
return nil, errors.Wrapf(err, "failed to get local repository versions")
110+
}
111+
return versions, nil
112+
}
113+
108114
// newLocalRepository returns a localRepository implementation
109115
func newLocalRepository(providerConfig config.Provider, configVariablesClient config.VariablesClient) (*localRepository, error) {
110116

111117
var err error
112118

113-
u, err := url.Parse(providerConfig.URL())
119+
urlSplit, err := url.Parse(providerConfig.URL())
114120
if err != nil {
115121
return nil, errors.Wrap(err, "invalid url")
116122
}
117-
absPath := u.Path
118-
if u.RawPath != "" {
119-
absPath = u.RawPath
123+
absPath := urlSplit.Path
124+
if urlSplit.RawPath != "" {
125+
absPath = urlSplit.RawPath
120126
}
121127

122128
if !filepath.IsAbs(absPath) {
123129
return nil, errors.Errorf("invalid path: path %q must be an absolute path", providerConfig.URL())
124130
}
125131

126132
parts := strings.Split(providerConfig.URL(), "/")
127-
// find the first occurance of provider name and use it as a seperator between base and layout
128-
providerNameIndex := 0
129-
for i, p := range parts {
130-
if p == providerConfig.Name() {
131-
providerNameIndex = i
133+
// {basepath}/{provider-name}/{version}/{components.yaml}
134+
if len(parts) < 3 {
135+
return nil, errors.Errorf("invalid path: path should be in the form {basepath}/{provider-name}/{version}/{components.yaml}")
136+
}
137+
// We work our way backwards with {components.yaml} being the last part of the path
138+
componentsPath := parts[len(parts)-1]
139+
defaultVersion := parts[len(parts)-2]
140+
if defaultVersion != "latest" {
141+
_, err = version.ParseSemantic(defaultVersion)
142+
if err != nil {
143+
return nil, errors.Errorf("invalid version: %q. Version must obey the syntax and semantics of the \"Semantic Versioning\" specification (http://semver.org/) and path format {basepath}/{provider-name}/{version}/{components.yaml}", defaultVersion)
132144
}
133145
}
134-
if providerNameIndex < 1 {
135-
return nil, errors.Errorf("invalid path: path %q must contain provider name %q", providerConfig.URL(), providerConfig.Name())
146+
providerName := parts[len(parts)-3]
147+
if providerName != providerConfig.Name() {
148+
return nil, errors.Errorf("invalid path: path %q must contain provider name %q in the format {basepath}/{provider-name}/{version}/{components.yaml}", providerConfig.URL(), providerConfig.Name())
149+
}
150+
var basePath string
151+
if len(parts) > 3 {
152+
basePath = filepath.Join(parts[:len(parts)-3]...)
136153
}
137-
138-
basePath := filepath.Join(parts[:providerNameIndex-1]...)
139154
basePath = filepath.Clean("/" + basePath) // ensure basePath starts with "/"
140-
layoutPath := filepath.Join(parts[providerNameIndex-1:]...)
141-
layoutParts := strings.Split(layoutPath, "/")
142-
// Check if the layoutPath is in the expected format.
143-
if len(layoutParts) < 5 {
144-
return nil, errors.Errorf("invalid path: path should be in the form {basepath}/{owner}/{provider-name}/releases/{version}/{path/components.yaml}")
145-
}
146-
owner := layoutParts[0]
147-
providerName := layoutParts[1]
148-
defaultVersion := layoutParts[3]
149-
path := filepath.Join(layoutParts[4:]...)
150-
// rootpath is the relative path starting after {version} directory and excludes component fiename
151-
rootPath, componentsPath := filepath.Split(path)
152155

153156
repo := &localRepository{
154157
providerConfig: providerConfig,
155158
configVariablesClient: configVariablesClient,
156159
basepath: basePath,
157-
owner: owner,
158160
providerName: providerName,
159161
defaultVersion: defaultVersion,
160-
rootPath: rootPath,
162+
rootPath: "", // Not applicable with localRepository
161163
componentsPath: componentsPath,
162164
}
163165

@@ -174,36 +176,32 @@ func newLocalRepository(providerConfig config.Provider, configVariablesClient co
174176
// getLatestRelease returns the latest release for the local repository.
175177
func (r *localRepository) getLatestRelease() (string, error) {
176178

177-
// get all the sub-directories under {releases} directory
178-
releasesPath := filepath.Join(r.basepath, r.owner, r.providerName, "releases")
179+
// get all the sub-directories under {basepath}/{provider-name}/
180+
releasesPath := filepath.Join(r.basepath, r.providerName)
179181
files, err := ioutil.ReadDir(releasesPath)
180182
if err != nil {
181183
return "", errors.Wrap(err, "failed to list release directories")
182184
}
183-
var releases []string
184-
for _, f := range files {
185-
if f.IsDir() {
186-
releases = append(releases, f.Name())
187-
}
188-
}
189-
190185
// search for the latest release according to semantic version
191186
// releases with names that are not semantic version number are ignored
192187
var latestTag string
193188
var latestReleaseVersion *version.Version
194-
for _, r := range releases {
195-
sv, err := version.ParseSemantic(r)
196-
if err != nil {
197-
// discard releases with tags that are not a valid semantic versions (the user can point explicitly to such releases)
198-
continue
199-
}
200-
if sv.PreRelease() != "" || sv.BuildMetadata() != "" {
201-
// discard pre-releases or build releases (the user can point explicitly to such releases)
202-
continue
203-
}
204-
if latestReleaseVersion == nil || latestReleaseVersion.LessThan(sv) {
205-
latestTag = r
206-
latestReleaseVersion = sv
189+
for _, f := range files {
190+
if f.IsDir() {
191+
r := f.Name()
192+
sv, err := version.ParseSemantic(r)
193+
if err != nil {
194+
// discard releases with tags that are not a valid semantic versions (the user can point explicitly to such releases)
195+
continue
196+
}
197+
if sv.PreRelease() != "" || sv.BuildMetadata() != "" {
198+
// discard pre-releases or build releases (the user can point explicitly to such releases)
199+
continue
200+
}
201+
if latestReleaseVersion == nil || latestReleaseVersion.LessThan(sv) {
202+
latestTag = r
203+
latestReleaseVersion = sv
204+
}
207205
}
208206
}
209207

@@ -213,3 +211,30 @@ func (r *localRepository) getLatestRelease() (string, error) {
213211

214212
return latestTag, nil
215213
}
214+
215+
// getVersions returns all the release versions for a local repository
216+
func (r *localRepository) getVersions() ([]string, error) {
217+
// get all the sub-directories under {basepath}/{provider-name}/
218+
releasesPath := filepath.Join(r.basepath, r.providerName)
219+
files, err := ioutil.ReadDir(releasesPath)
220+
if err != nil {
221+
return nil, errors.Wrap(err, "failed to list release directories")
222+
}
223+
var versions []string
224+
for _, f := range files {
225+
if f.IsDir() {
226+
r := f.Name()
227+
sv, err := version.ParseSemantic(r)
228+
if err != nil {
229+
// discard releases with tags that are not a valid semantic versions (the user can point explicitly to such releases)
230+
continue
231+
}
232+
if sv.PreRelease() != "" || sv.BuildMetadata() != "" {
233+
// discard pre-releases or build releases (the user can point explicitly to such releases)
234+
continue
235+
}
236+
versions = append(versions, r)
237+
}
238+
}
239+
return versions, nil
240+
}

0 commit comments

Comments
 (0)