Support proper semver ranges for Helm charts
This commit changes the semver range parser to `blang/semver`, which is also used to parse semver tags for GitRepository sources.
This commit is contained in:
parent
25f0552ef9
commit
d38b8fe193
|
@ -4,7 +4,7 @@ metadata:
|
|||
name: helmchart-sample
|
||||
spec:
|
||||
name: podinfo
|
||||
version: '^2.0.0'
|
||||
version: '>=2.0.0 <3.0.0'
|
||||
helmRepositoryRef:
|
||||
name: helmrepository-sample
|
||||
interval: 1m
|
||||
|
|
|
@ -26,7 +26,6 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
@ -36,9 +35,9 @@ import (
|
|||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/fluxcd/pkg/recorder"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
"github.com/fluxcd/source-controller/internal/helm"
|
||||
)
|
||||
|
@ -174,30 +173,8 @@ func (r *HelmChartReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, opts
|
|||
}
|
||||
|
||||
func (r *HelmChartReconciler) reconcile(ctx context.Context, repository sourcev1.HelmRepository, chart sourcev1.HelmChart) (sourcev1.HelmChart, error) {
|
||||
indexBytes, err := ioutil.ReadFile(repository.Status.Artifact.Path)
|
||||
cv, err := helm.GetDownloadableChartVersionFromIndex(repository.Status.Artifact.Path, chart.Spec.Name, chart.Spec.Version)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to read Helm repository index file: %w", err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
index := &repo.IndexFile{}
|
||||
if err := yaml.Unmarshal(indexBytes, index); err != nil {
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// find referenced chart in index
|
||||
cv, err := index.Get(chart.Spec.Name, chart.Spec.Version)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case repo.ErrNoChartName:
|
||||
err = fmt.Errorf("chart '%s' could not be found in Helm repository '%s'", chart.Spec.Name, repository.Name)
|
||||
case repo.ErrNoChartVersion:
|
||||
err = fmt.Errorf("no chart with version '%s' found for '%s'", chart.Spec.Version, chart.Spec.Name)
|
||||
}
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
if len(cv.URLs) == 0 {
|
||||
err = fmt.Errorf("chart '%s' has no downloadable URLs", cv.Name)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
|
@ -207,7 +184,6 @@ func (r *HelmChartReconciler) reconcile(ctx context.Context, repository sourcev1
|
|||
u, err := url.Parse(ref)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid chart URL format '%s': %w", ref, err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
c, err := r.Getters.ByScheme(u.Scheme)
|
||||
|
|
|
@ -101,7 +101,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
|||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Name: "helmchart",
|
||||
Version: "*",
|
||||
Version: "",
|
||||
HelmRepositoryRef: corev1.LocalObjectReference{Name: repositoryKey.Name},
|
||||
Interval: metav1.Duration{Duration: pullInterval},
|
||||
},
|
||||
|
@ -203,7 +203,6 @@ var _ = Describe("HelmChartReconciler", func() {
|
|||
},
|
||||
Spec: sourcev1.HelmChartSpec{
|
||||
Name: "helmchart",
|
||||
Version: "*",
|
||||
HelmRepositoryRef: corev1.LocalObjectReference{Name: repositoryKey.Name},
|
||||
Interval: metav1.Duration{Duration: 1 * time.Hour},
|
||||
},
|
||||
|
@ -218,7 +217,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
|||
return ""
|
||||
}, timeout, interval).Should(Equal("1.0.0"))
|
||||
|
||||
chart.Spec.Version = "~0.1.0"
|
||||
chart.Spec.Version = "<0.2.0"
|
||||
Expect(k8sClient.Update(context.Background(), chart)).Should(Succeed())
|
||||
Eventually(func() string {
|
||||
_ = k8sClient.Get(context.Background(), key, chart)
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
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 helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func GetDownloadableChartVersionFromIndex(path, chart, version string) (*repo.ChartVersion, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read Helm repository index file: %w", err)
|
||||
}
|
||||
index := &repo.IndexFile{}
|
||||
if err := yaml.Unmarshal(b, index); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal Helm repository index file: %w", err)
|
||||
}
|
||||
|
||||
var cv *repo.ChartVersion
|
||||
if version == "" || version == "*" {
|
||||
cv, err = index.Get(chart, version)
|
||||
if err != nil {
|
||||
if err == repo.ErrNoChartName {
|
||||
err = fmt.Errorf("chart '%s' could not be found in Helm repository index", chart)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
entries, ok := index.Entries[chart]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("chart '%s' could not be found in Helm repository index", chart)
|
||||
}
|
||||
|
||||
rng, err := semver.ParseRange(version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("semver range parse error: %w", err)
|
||||
}
|
||||
versionEntryLookup := make(map[string]*repo.ChartVersion)
|
||||
var versionsInRange []semver.Version
|
||||
for _, e := range entries {
|
||||
v, _ := semver.ParseTolerant(e.Version)
|
||||
if rng(v) {
|
||||
versionsInRange = append(versionsInRange, v)
|
||||
versionEntryLookup[v.String()] = e
|
||||
}
|
||||
}
|
||||
if len(versionsInRange) == 0 {
|
||||
return nil, fmt.Errorf("no match found for semver: %s", version)
|
||||
}
|
||||
semver.Sort(versionsInRange)
|
||||
|
||||
latest := versionsInRange[len(versionsInRange)-1]
|
||||
cv = versionEntryLookup[latest.String()]
|
||||
}
|
||||
|
||||
if len(cv.URLs) == 0 {
|
||||
return nil, fmt.Errorf("no downloadable URLs for chart '%s' with version '%s'", cv.Name, cv.Version)
|
||||
}
|
||||
|
||||
return cv, nil
|
||||
}
|
Loading…
Reference in New Issue