HelmChartReconciler refactor
Signed-off-by: Hidde Beydals <hello@hidde.co>
This commit is contained in:
parent
e0e048ad6d
commit
8e107ea60e
|
|
@ -32,6 +32,10 @@ const (
|
||||||
// Source may be outdated.
|
// Source may be outdated.
|
||||||
// This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True.
|
// This is a "negative polarity" or "abnormal-true" type, and is only present on the resource if it is True.
|
||||||
FetchFailedCondition string = "FetchFailed"
|
FetchFailedCondition string = "FetchFailed"
|
||||||
|
|
||||||
|
// BuildFailedCondition indicates a transient or persistent build failure of a Source's Artifact.
|
||||||
|
// If True, the Source can be in an ArtifactOutdatedCondition
|
||||||
|
BuildFailedCondition string = "BuildFailed"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,10 @@ package v1beta2
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/acl"
|
"github.com/fluxcd/pkg/apis/acl"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/conditions"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HelmChartKind is the string representation of a HelmChart.
|
// HelmChartKind is the string representation of a HelmChart.
|
||||||
|
|
@ -115,6 +113,16 @@ type HelmChartStatus struct {
|
||||||
// +optional
|
// +optional
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||||
|
|
||||||
|
// ObservedSourceArtifactRevision is the last observed Artifact.Revision
|
||||||
|
// of the Source reference.
|
||||||
|
// +optional
|
||||||
|
ObservedSourceArtifactRevision string `json:"observedSourceArtifactRevision,omitempty"`
|
||||||
|
|
||||||
|
// ObservedChartName is the last observed chart name as defined by the
|
||||||
|
// resolved chart reference.
|
||||||
|
// +optional
|
||||||
|
ObservedChartName string `json:"observedChartName,omitempty"`
|
||||||
|
|
||||||
// Conditions holds the conditions for the HelmChart.
|
// Conditions holds the conditions for the HelmChart.
|
||||||
// +optional
|
// +optional
|
||||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||||
|
|
@ -148,46 +156,6 @@ const (
|
||||||
ChartPackageSucceededReason string = "ChartPackageSucceeded"
|
ChartPackageSucceededReason string = "ChartPackageSucceeded"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HelmChartProgressing resets the conditions of the HelmChart to meta.Condition
|
|
||||||
// of type meta.ReadyCondition with status 'Unknown' and meta.ProgressingReason
|
|
||||||
// reason and message. It returns the modified HelmChart.
|
|
||||||
func HelmChartProgressing(chart HelmChart) HelmChart {
|
|
||||||
chart.Status.ObservedGeneration = chart.Generation
|
|
||||||
chart.Status.URL = ""
|
|
||||||
chart.Status.Conditions = []metav1.Condition{}
|
|
||||||
conditions.MarkUnknown(&chart, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
|
|
||||||
return chart
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelmChartReady sets the given Artifact and URL on the HelmChart and sets the
|
|
||||||
// meta.ReadyCondition to 'True', with the given reason and message. It returns
|
|
||||||
// the modified HelmChart.
|
|
||||||
func HelmChartReady(chart HelmChart, artifact Artifact, url, reason, message string) HelmChart {
|
|
||||||
chart.Status.Artifact = &artifact
|
|
||||||
chart.Status.URL = url
|
|
||||||
conditions.MarkTrue(&chart, meta.ReadyCondition, reason, message)
|
|
||||||
return chart
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelmChartNotReady sets the meta.ReadyCondition on the given HelmChart to
|
|
||||||
// 'False', with the given reason and message. It returns the modified
|
|
||||||
// HelmChart.
|
|
||||||
func HelmChartNotReady(chart HelmChart, reason, message string) HelmChart {
|
|
||||||
conditions.MarkFalse(&chart, meta.ReadyCondition, reason, message)
|
|
||||||
return chart
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelmChartReadyMessage returns the message of the meta.ReadyCondition with
|
|
||||||
// status 'True', or an empty string.
|
|
||||||
func HelmChartReadyMessage(chart HelmChart) string {
|
|
||||||
if c := apimeta.FindStatusCondition(chart.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
if c.Status == metav1.ConditionTrue {
|
|
||||||
return c.Message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConditions returns the status conditions of the object.
|
// GetConditions returns the status conditions of the object.
|
||||||
func (in HelmChart) GetConditions() []metav1.Condition {
|
func (in HelmChart) GetConditions() []metav1.Condition {
|
||||||
return in.Status.Conditions
|
return in.Status.Conditions
|
||||||
|
|
|
||||||
|
|
@ -517,10 +517,18 @@ spec:
|
||||||
reconcile request value, so a change of the annotation value can
|
reconcile request value, so a change of the annotation value can
|
||||||
be detected.
|
be detected.
|
||||||
type: string
|
type: string
|
||||||
|
observedChartName:
|
||||||
|
description: ObservedChartName is the last observed chart name as
|
||||||
|
defined by the resolved chart reference.
|
||||||
|
type: string
|
||||||
observedGeneration:
|
observedGeneration:
|
||||||
description: ObservedGeneration is the last observed generation.
|
description: ObservedGeneration is the last observed generation.
|
||||||
format: int64
|
format: int64
|
||||||
type: integer
|
type: integer
|
||||||
|
observedSourceArtifactRevision:
|
||||||
|
description: ObservedSourceArtifactRevision is the last observed Artifact.Revision
|
||||||
|
of the Source reference.
|
||||||
|
type: string
|
||||||
url:
|
url:
|
||||||
description: URL is the download link for the last chart pulled.
|
description: URL is the download link for the last chart pulled.
|
||||||
type: string
|
type: string
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/gittestserver"
|
"github.com/fluxcd/pkg/gittestserver"
|
||||||
"github.com/fluxcd/pkg/helmtestserver"
|
"github.com/fluxcd/pkg/helmtestserver"
|
||||||
|
"github.com/fluxcd/pkg/runtime/conditions"
|
||||||
"github.com/go-git/go-billy/v5/memfs"
|
"github.com/go-git/go-billy/v5/memfs"
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/config"
|
"github.com/go-git/go-git/v5/config"
|
||||||
|
|
@ -49,7 +50,7 @@ import (
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("HelmChartReconciler", func() {
|
var _ = FDescribe("HelmChartReconciler", func() {
|
||||||
|
|
||||||
const (
|
const (
|
||||||
timeout = time.Second * 30
|
timeout = time.Second * 30
|
||||||
|
|
@ -270,7 +271,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
got := &sourcev1.HelmChart{}
|
got := &sourcev1.HelmChart{}
|
||||||
Eventually(func() bool {
|
Eventually(func() bool {
|
||||||
_ = k8sClient.Get(context.Background(), key, got)
|
_ = k8sClient.Get(context.Background(), key, got)
|
||||||
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration &&
|
return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && got.GetArtifact() != nil &&
|
||||||
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
|
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact)
|
||||||
}, timeout, interval).Should(BeTrue())
|
}, timeout, interval).Should(BeTrue())
|
||||||
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
|
f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact))
|
||||||
|
|
@ -292,6 +293,9 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
Eventually(func() bool {
|
Eventually(func() bool {
|
||||||
_ = k8sClient.Get(context.Background(), key, updated)
|
_ = k8sClient.Get(context.Background(), key, updated)
|
||||||
for _, c := range updated.Status.Conditions {
|
for _, c := range updated.Status.Conditions {
|
||||||
|
fmt.Fprintf(GinkgoWriter, "condition type: %s\n", c.Type)
|
||||||
|
fmt.Fprintf(GinkgoWriter, "condition reason: %s\n", c.Reason)
|
||||||
|
fmt.Fprintf(GinkgoWriter, "condition message: %s\n", c.Message)
|
||||||
if c.Reason == sourcev1.ChartPullFailedReason &&
|
if c.Reason == sourcev1.ChartPullFailedReason &&
|
||||||
strings.Contains(c.Message, "failed to retrieve source") {
|
strings.Contains(c.Message, "failed to retrieve source") {
|
||||||
return true
|
return true
|
||||||
|
|
@ -394,12 +398,8 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
Expect(k8sClient.Update(context.Background(), chart)).Should(Succeed())
|
Expect(k8sClient.Update(context.Background(), chart)).Should(Succeed())
|
||||||
Eventually(func() bool {
|
Eventually(func() bool {
|
||||||
_ = k8sClient.Get(context.Background(), key, chart)
|
_ = k8sClient.Get(context.Background(), key, chart)
|
||||||
for _, c := range chart.Status.Conditions {
|
return conditions.GetReason(chart, sourcev1.FetchFailedCondition) == "InvalidChartReference" &&
|
||||||
if c.Reason == sourcev1.ChartPullFailedReason {
|
conditions.IsStalled(chart)
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}, timeout, interval).Should(BeTrue())
|
}, timeout, interval).Should(BeTrue())
|
||||||
Expect(chart.GetArtifact()).NotTo(BeNil())
|
Expect(chart.GetArtifact()).NotTo(BeNil())
|
||||||
Expect(chart.Status.Artifact.Revision).Should(Equal("0.1.1"))
|
Expect(chart.Status.Artifact.Revision).Should(Equal("0.1.1"))
|
||||||
|
|
@ -495,13 +495,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
got := &sourcev1.HelmChart{}
|
got := &sourcev1.HelmChart{}
|
||||||
Eventually(func() bool {
|
Eventually(func() bool {
|
||||||
_ = k8sClient.Get(context.Background(), key, got)
|
_ = k8sClient.Get(context.Background(), key, got)
|
||||||
for _, c := range got.Status.Conditions {
|
return conditions.GetReason(got, sourcev1.FetchFailedCondition) == sourcev1.AuthenticationFailedReason
|
||||||
if c.Reason == sourcev1.AuthenticationFailedReason &&
|
|
||||||
strings.Contains(c.Message, "auth secret error") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}, timeout, interval).Should(BeTrue())
|
}, timeout, interval).Should(BeTrue())
|
||||||
|
|
||||||
By("Applying secret with missing keys")
|
By("Applying secret with missing keys")
|
||||||
|
|
@ -515,7 +509,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
got := &sourcev1.HelmChart{}
|
got := &sourcev1.HelmChart{}
|
||||||
_ = k8sClient.Get(context.Background(), key, got)
|
_ = k8sClient.Get(context.Background(), key, got)
|
||||||
for _, c := range got.Status.Conditions {
|
for _, c := range got.Status.Conditions {
|
||||||
if c.Reason == sourcev1.ChartPullFailedReason &&
|
if c.Reason == "ChartPullError" &&
|
||||||
strings.Contains(c.Message, "401 Unauthorized") {
|
strings.Contains(c.Message, "401 Unauthorized") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -833,7 +827,7 @@ var _ = Describe("HelmChartReconciler", func() {
|
||||||
// if the artifact was changed due to the current update.
|
// if the artifact was changed due to the current update.
|
||||||
// Use status condition to be sure.
|
// Use status condition to be sure.
|
||||||
for _, condn := range got.Status.Conditions {
|
for _, condn := range got.Status.Conditions {
|
||||||
if strings.Contains(condn.Message, "with merged values files [./testdata/charts/helmchart/override.yaml]") &&
|
if strings.Contains(condn.Message, "merged values files [./testdata/charts/helmchart/override.yaml]") &&
|
||||||
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) {
|
ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,9 +137,9 @@ var _ = BeforeSuite(func() {
|
||||||
Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler")
|
Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler")
|
||||||
|
|
||||||
err = (&HelmChartReconciler{
|
err = (&HelmChartReconciler{
|
||||||
Client: k8sManager.GetClient(),
|
Client: k8sManager.GetClient(),
|
||||||
Scheme: scheme.Scheme,
|
Storage: ginkgoTestStorage,
|
||||||
Storage: ginkgoTestStorage,
|
EventRecorder: record.NewFakeRecorder(32),
|
||||||
Getters: getter.Providers{getter.Provider{
|
Getters: getter.Providers{getter.Provider{
|
||||||
Schemes: []string{"http", "https"},
|
Schemes: []string{"http", "https"},
|
||||||
New: getter.NewHTTPGetter,
|
New: getter.NewHTTPGetter,
|
||||||
|
|
|
||||||
|
|
@ -115,15 +115,16 @@ func (o BuildOptions) GetValuesFiles() []string {
|
||||||
return o.ValuesFiles
|
return o.ValuesFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build contains the Builder.Build result, including specific
|
// Build contains the (partial) Builder.Build result, including specific
|
||||||
// information about the built chart like ResolvedDependencies.
|
// information about the built chart like ResolvedDependencies.
|
||||||
type Build struct {
|
type Build struct {
|
||||||
// Path is the absolute path to the packaged chart.
|
// Name of the chart.
|
||||||
Path string
|
|
||||||
// Name of the packaged chart.
|
|
||||||
Name string
|
Name string
|
||||||
// Version of the packaged chart.
|
// Version of the chart.
|
||||||
Version string
|
Version string
|
||||||
|
// Path is the absolute path to the packaged chart.
|
||||||
|
// Can be empty, in which case a failure should be assumed.
|
||||||
|
Path string
|
||||||
// ValuesFiles is the list of files used to compose the chart's
|
// ValuesFiles is the list of files used to compose the chart's
|
||||||
// default "values.yaml".
|
// default "values.yaml".
|
||||||
ValuesFiles []string
|
ValuesFiles []string
|
||||||
|
|
@ -138,30 +139,45 @@ type Build struct {
|
||||||
|
|
||||||
// Summary returns a human-readable summary of the Build.
|
// Summary returns a human-readable summary of the Build.
|
||||||
func (b *Build) Summary() string {
|
func (b *Build) Summary() string {
|
||||||
if b == nil || b.Name == "" || b.Version == "" {
|
if !b.HasMetadata() {
|
||||||
return "No chart build."
|
return "No chart build"
|
||||||
}
|
}
|
||||||
|
|
||||||
var s strings.Builder
|
var s strings.Builder
|
||||||
|
|
||||||
var action = "Pulled"
|
var action = "New"
|
||||||
if b.Packaged {
|
if b.Path != "" {
|
||||||
action = "Packaged"
|
action = "Pulled"
|
||||||
|
if b.Packaged {
|
||||||
|
action = "Packaged"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.WriteString(fmt.Sprintf("%s '%s' chart with version '%s'", action, b.Name, b.Version))
|
s.WriteString(fmt.Sprintf("%s '%s' chart with version '%s'", action, b.Name, b.Version))
|
||||||
|
|
||||||
if b.Packaged && len(b.ValuesFiles) > 0 {
|
if len(b.ValuesFiles) > 0 {
|
||||||
s.WriteString(fmt.Sprintf(", with merged values files %v", b.ValuesFiles))
|
s.WriteString(fmt.Sprintf(" and merged values files %v", b.ValuesFiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Packaged && b.ResolvedDependencies > 0 {
|
|
||||||
s.WriteString(fmt.Sprintf(", resolving %d dependencies before packaging", b.ResolvedDependencies))
|
|
||||||
}
|
|
||||||
|
|
||||||
s.WriteString(".")
|
|
||||||
return s.String()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasMetadata returns if the Build contains chart metadata.
|
||||||
|
//
|
||||||
|
// NOTE: This may return True while the build did not Complete successfully.
|
||||||
|
// Which means it was able to successfully collect the metadata from the chart,
|
||||||
|
// but failed further into the process.
|
||||||
|
func (b *Build) HasMetadata() bool {
|
||||||
|
if b == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return b.Name != "" && b.Version != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete returns if the Build completed successfully.
|
||||||
|
func (b *Build) Complete() bool {
|
||||||
|
return b.HasMetadata() && b.Path != ""
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the Path of the Build.
|
// String returns the Path of the Build.
|
||||||
func (b *Build) String() string {
|
func (b *Build) String() string {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,9 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
result.Version = ver.String()
|
result.Version = ver.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isChartDir := pathIsDir(localRef.Path)
|
||||||
|
requiresPackaging := isChartDir || opts.VersionMetadata != "" || len(opts.GetValuesFiles()) != 0
|
||||||
|
|
||||||
// If all the following is true, we do not need to package the chart:
|
// If all the following is true, we do not need to package the chart:
|
||||||
// - Chart name from cached chart matches resolved name
|
// - Chart name from cached chart matches resolved name
|
||||||
// - Chart version from cached chart matches calculated version
|
// - Chart version from cached chart matches calculated version
|
||||||
|
|
@ -112,7 +115,9 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
if err = curMeta.Validate(); err == nil {
|
if err = curMeta.Validate(); err == nil {
|
||||||
if result.Name == curMeta.Name && result.Version == curMeta.Version {
|
if result.Name == curMeta.Name && result.Version == curMeta.Version {
|
||||||
result.Path = opts.CachedChart
|
result.Path = opts.CachedChart
|
||||||
result.ValuesFiles = opts.ValuesFiles
|
result.ValuesFiles = opts.GetValuesFiles()
|
||||||
|
result.Packaged = requiresPackaging
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,10 +126,9 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
|
|
||||||
// If the chart at the path is already packaged and no custom values files
|
// If the chart at the path is already packaged and no custom values files
|
||||||
// options are set, we can copy the chart without making modifications
|
// options are set, we can copy the chart without making modifications
|
||||||
isChartDir := pathIsDir(localRef.Path)
|
if !requiresPackaging {
|
||||||
if !isChartDir && len(opts.GetValuesFiles()) == 0 {
|
|
||||||
if err = copyFileToPath(localRef.Path, p); err != nil {
|
if err = copyFileToPath(localRef.Path, p); err != nil {
|
||||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
return result, &BuildError{Reason: ErrChartPull, Err: err}
|
||||||
}
|
}
|
||||||
result.Path = p
|
result.Path = p
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
@ -134,7 +138,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
var mergedValues map[string]interface{}
|
var mergedValues map[string]interface{}
|
||||||
if len(opts.GetValuesFiles()) > 0 {
|
if len(opts.GetValuesFiles()) > 0 {
|
||||||
if mergedValues, err = mergeFileValues(localRef.WorkDir, opts.ValuesFiles); err != nil {
|
if mergedValues, err = mergeFileValues(localRef.WorkDir, opts.ValuesFiles); err != nil {
|
||||||
return nil, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
return result, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +147,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
// or because we have merged values and need to repackage
|
// or because we have merged values and need to repackage
|
||||||
chart, err := loader.Load(localRef.Path)
|
chart, err := loader.Load(localRef.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &BuildError{Reason: ErrChartPackage, Err: err}
|
return result, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||||
}
|
}
|
||||||
// Set earlier resolved version (with metadata)
|
// Set earlier resolved version (with metadata)
|
||||||
chart.Metadata.Version = result.Version
|
chart.Metadata.Version = result.Version
|
||||||
|
|
@ -151,7 +155,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
// Overwrite default values with merged values, if any
|
// Overwrite default values with merged values, if any
|
||||||
if ok, err = OverwriteChartDefaultValues(chart, mergedValues); ok || err != nil {
|
if ok, err = OverwriteChartDefaultValues(chart, mergedValues); ok || err != nil {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
return result, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||||
}
|
}
|
||||||
result.ValuesFiles = opts.GetValuesFiles()
|
result.ValuesFiles = opts.GetValuesFiles()
|
||||||
}
|
}
|
||||||
|
|
@ -160,19 +164,19 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string,
|
||||||
if isChartDir {
|
if isChartDir {
|
||||||
if b.dm == nil {
|
if b.dm == nil {
|
||||||
err = fmt.Errorf("local chart builder requires dependency manager for unpackaged charts")
|
err = fmt.Errorf("local chart builder requires dependency manager for unpackaged charts")
|
||||||
return nil, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
return result, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
||||||
}
|
}
|
||||||
if result.ResolvedDependencies, err = b.dm.Build(ctx, ref, chart); err != nil {
|
if result.ResolvedDependencies, err = b.dm.Build(ctx, ref, chart); err != nil {
|
||||||
return nil, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
return result, &BuildError{Reason: ErrDependencyBuild, Err: err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package the chart
|
// Package the chart
|
||||||
if err = packageToPath(chart, p); err != nil {
|
if err = packageToPath(chart, p); err != nil {
|
||||||
return nil, &BuildError{Reason: ErrChartPackage, Err: err}
|
return result, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||||
}
|
}
|
||||||
result.Path = p
|
result.Path = p
|
||||||
result.Packaged = true
|
result.Packaged = requiresPackaging
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,12 +82,13 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
||||||
cv, err := b.remote.Get(remoteRef.Name, remoteRef.Version)
|
cv, err := b.remote.Get(remoteRef.Name, remoteRef.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("failed to get chart version for remote reference: %w", err)
|
err = fmt.Errorf("failed to get chart version for remote reference: %w", err)
|
||||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
return nil, &BuildError{Reason: ErrChartReference, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &Build{}
|
result := &Build{}
|
||||||
result.Name = cv.Name
|
result.Name = cv.Name
|
||||||
result.Version = cv.Version
|
result.Version = cv.Version
|
||||||
|
|
||||||
// Set build specific metadata if instructed
|
// Set build specific metadata if instructed
|
||||||
if opts.VersionMetadata != "" {
|
if opts.VersionMetadata != "" {
|
||||||
ver, err := semver.NewVersion(result.Version)
|
ver, err := semver.NewVersion(result.Version)
|
||||||
|
|
@ -102,6 +103,8 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
||||||
result.Version = ver.String()
|
result.Version = ver.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requiresPackaging := len(opts.GetValuesFiles()) != 0 || opts.VersionMetadata != ""
|
||||||
|
|
||||||
// If all the following is true, we do not need to download and/or build the chart:
|
// If all the following is true, we do not need to download and/or build the chart:
|
||||||
// - Chart name from cached chart matches resolved name
|
// - Chart name from cached chart matches resolved name
|
||||||
// - Chart version from cached chart matches calculated version
|
// - Chart version from cached chart matches calculated version
|
||||||
|
|
@ -114,6 +117,7 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
||||||
if result.Name == curMeta.Name && result.Version == curMeta.Version {
|
if result.Name == curMeta.Name && result.Version == curMeta.Version {
|
||||||
result.Path = opts.CachedChart
|
result.Path = opts.CachedChart
|
||||||
result.ValuesFiles = opts.GetValuesFiles()
|
result.ValuesFiles = opts.GetValuesFiles()
|
||||||
|
result.Packaged = requiresPackaging
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,12 +128,12 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
||||||
res, err := b.remote.DownloadChart(cv)
|
res, err := b.remote.DownloadChart(cv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("failed to download chart for remote reference: %w", err)
|
err = fmt.Errorf("failed to download chart for remote reference: %w", err)
|
||||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
return result, &BuildError{Reason: ErrChartPull, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use literal chart copy from remote if no custom values files options are
|
// Use literal chart copy from remote if no custom values files options are
|
||||||
// set or build option version metadata isn't set.
|
// set or version metadata isn't set.
|
||||||
if len(opts.GetValuesFiles()) == 0 && opts.VersionMetadata == "" {
|
if !requiresPackaging {
|
||||||
if err = validatePackageAndWriteToPath(res, p); err != nil {
|
if err = validatePackageAndWriteToPath(res, p); err != nil {
|
||||||
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
return nil, &BuildError{Reason: ErrChartPull, Err: err}
|
||||||
}
|
}
|
||||||
|
|
@ -141,14 +145,14 @@ func (b *remoteChartBuilder) Build(_ context.Context, ref Reference, p string, o
|
||||||
var chart *helmchart.Chart
|
var chart *helmchart.Chart
|
||||||
if chart, err = loader.LoadArchive(res); err != nil {
|
if chart, err = loader.LoadArchive(res); err != nil {
|
||||||
err = fmt.Errorf("failed to load downloaded chart: %w", err)
|
err = fmt.Errorf("failed to load downloaded chart: %w", err)
|
||||||
return nil, &BuildError{Reason: ErrChartPackage, Err: err}
|
return result, &BuildError{Reason: ErrChartPackage, Err: err}
|
||||||
}
|
}
|
||||||
chart.Metadata.Version = result.Version
|
chart.Metadata.Version = result.Version
|
||||||
|
|
||||||
mergedValues, err := mergeChartValues(chart, opts.ValuesFiles)
|
mergedValues, err := mergeChartValues(chart, opts.ValuesFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("failed to merge chart values: %w", err)
|
err = fmt.Errorf("failed to merge chart values: %w", err)
|
||||||
return nil, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
return result, &BuildError{Reason: ErrValuesFilesMerge, Err: err}
|
||||||
}
|
}
|
||||||
// Overwrite default values with merged values, if any
|
// Overwrite default values with merged values, if any
|
||||||
if ok, err = OverwriteChartDefaultValues(chart, mergedValues); ok || err != nil {
|
if ok, err = OverwriteChartDefaultValues(chart, mergedValues); ok || err != nil {
|
||||||
|
|
|
||||||
|
|
@ -138,12 +138,32 @@ func TestChartBuildResult_Summary(t *testing.T) {
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Simple",
|
name: "Build with metadata",
|
||||||
build: &Build{
|
build: &Build{
|
||||||
Name: "chart",
|
Name: "chart",
|
||||||
Version: "1.2.3-rc.1+bd6bf40",
|
Version: "1.2.3-rc.1+bd6bf40",
|
||||||
},
|
},
|
||||||
want: "Pulled 'chart' chart with version '1.2.3-rc.1+bd6bf40'.",
|
want: "New 'chart' chart with version '1.2.3-rc.1+bd6bf40'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Pulled chart",
|
||||||
|
build: &Build{
|
||||||
|
Name: "chart",
|
||||||
|
Version: "1.2.3-rc.1+bd6bf40",
|
||||||
|
Path: "chart.tgz",
|
||||||
|
},
|
||||||
|
want: "Pulled 'chart' chart with version '1.2.3-rc.1+bd6bf40'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Packaged chart",
|
||||||
|
build: &Build{
|
||||||
|
Name: "chart",
|
||||||
|
Version: "arbitrary-version",
|
||||||
|
Packaged: true,
|
||||||
|
ValuesFiles: []string{"a.yaml", "b.yaml"},
|
||||||
|
Path: "chart.tgz",
|
||||||
|
},
|
||||||
|
want: "Packaged 'chart' chart with version 'arbitrary-version' and merged values files [a.yaml b.yaml]",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "With values files",
|
name: "With values files",
|
||||||
|
|
@ -152,28 +172,19 @@ func TestChartBuildResult_Summary(t *testing.T) {
|
||||||
Version: "arbitrary-version",
|
Version: "arbitrary-version",
|
||||||
Packaged: true,
|
Packaged: true,
|
||||||
ValuesFiles: []string{"a.yaml", "b.yaml"},
|
ValuesFiles: []string{"a.yaml", "b.yaml"},
|
||||||
|
Path: "chart.tgz",
|
||||||
},
|
},
|
||||||
want: "Packaged 'chart' chart with version 'arbitrary-version', with merged values files [a.yaml b.yaml].",
|
want: "Packaged 'chart' chart with version 'arbitrary-version' and merged values files [a.yaml b.yaml]",
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "With dependencies",
|
|
||||||
build: &Build{
|
|
||||||
Name: "chart",
|
|
||||||
Version: "arbitrary-version",
|
|
||||||
Packaged: true,
|
|
||||||
ResolvedDependencies: 5,
|
|
||||||
},
|
|
||||||
want: "Packaged 'chart' chart with version 'arbitrary-version', resolving 5 dependencies before packaging.",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Empty build",
|
name: "Empty build",
|
||||||
build: &Build{},
|
build: &Build{},
|
||||||
want: "No chart build.",
|
want: "No chart build",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nil build",
|
name: "Nil build",
|
||||||
build: nil,
|
build: nil,
|
||||||
want: "No chart build.",
|
want: "No chart build",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
||||||
|
|
@ -22,22 +22,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildErrorReason is the descriptive reason for a BuildError.
|
// BuildErrorReason is the descriptive reason for a BuildError.
|
||||||
type BuildErrorReason string
|
type BuildErrorReason struct {
|
||||||
|
// Reason is the programmatic build error reason in CamelCase.
|
||||||
|
Reason string
|
||||||
|
|
||||||
|
// Summary is the human build error reason, used to provide
|
||||||
|
// the Error string, and further context to the BuildError.
|
||||||
|
Summary string
|
||||||
|
}
|
||||||
|
|
||||||
// Error returns the string representation of BuildErrorReason.
|
// Error returns the string representation of BuildErrorReason.
|
||||||
func (e BuildErrorReason) Error() string {
|
func (e BuildErrorReason) Error() string {
|
||||||
return string(e)
|
return e.Summary
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildError contains a wrapped Err and a Reason indicating why it occurred.
|
// BuildError contains a wrapped Err and a Reason indicating why it occurred.
|
||||||
type BuildError struct {
|
type BuildError struct {
|
||||||
Reason error
|
Reason BuildErrorReason
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns Err as a string, prefixed with the Reason to provide context.
|
// Error returns Err as a string, prefixed with the Reason to provide context.
|
||||||
func (e *BuildError) Error() string {
|
func (e *BuildError) Error() string {
|
||||||
if e.Reason == nil {
|
if e.Reason.Error() == "" {
|
||||||
return e.Err.Error()
|
return e.Err.Error()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s: %s", e.Reason.Error(), e.Err.Error())
|
return fmt.Sprintf("%s: %s", e.Reason.Error(), e.Err.Error())
|
||||||
|
|
@ -49,7 +56,7 @@ func (e *BuildError) Error() string {
|
||||||
// err := &BuildError{Reason: ErrChartPull, Err: errors.New("arbitrary transport error")}
|
// err := &BuildError{Reason: ErrChartPull, Err: errors.New("arbitrary transport error")}
|
||||||
// errors.Is(err, ErrChartPull)
|
// errors.Is(err, ErrChartPull)
|
||||||
func (e *BuildError) Is(target error) bool {
|
func (e *BuildError) Is(target error) bool {
|
||||||
if e.Reason != nil && e.Reason == target {
|
if e.Reason == target {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return errors.Is(e.Err, target)
|
return errors.Is(e.Err, target)
|
||||||
|
|
@ -60,11 +67,21 @@ func (e *BuildError) Unwrap() error {
|
||||||
return e.Err
|
return e.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsPersistentBuildErrorReason(err error) bool {
|
||||||
|
switch err {
|
||||||
|
case ErrChartReference, ErrChartMetadataPatch, ErrValuesFilesMerge:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrChartReference = BuildErrorReason("chart reference error")
|
ErrChartReference = BuildErrorReason{Reason: "InvalidChartReference", Summary: "invalid chart reference"}
|
||||||
ErrChartPull = BuildErrorReason("chart pull error")
|
ErrChartPull = BuildErrorReason{Reason: "ChartPullError", Summary: "chart pull error"}
|
||||||
ErrChartMetadataPatch = BuildErrorReason("chart metadata patch error")
|
ErrChartMetadataPatch = BuildErrorReason{Reason: "MetadataPatchError", Summary: "chart metadata patch error"}
|
||||||
ErrValuesFilesMerge = BuildErrorReason("values files merge error")
|
ErrValuesFilesMerge = BuildErrorReason{Reason: "ValuesFilesError", Summary: "values files merge error"}
|
||||||
ErrDependencyBuild = BuildErrorReason("dependency build error")
|
ErrDependencyBuild = BuildErrorReason{Reason: "DependencyBuildError", Summary: "dependency build error"}
|
||||||
ErrChartPackage = BuildErrorReason("chart package error")
|
ErrChartPackage = BuildErrorReason{Reason: "ChartPackageError", Summary: "chart package error"}
|
||||||
|
ErrUnknown = BuildErrorReason{Reason: "Unknown", Summary: "unknown build error"}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
func TestBuildErrorReason_Error(t *testing.T) {
|
func TestBuildErrorReason_Error(t *testing.T) {
|
||||||
g := NewWithT(t)
|
g := NewWithT(t)
|
||||||
|
|
||||||
err := BuildErrorReason("reason")
|
err := BuildErrorReason{"Reason", "reason"}
|
||||||
g.Expect(err.Error()).To(Equal("reason"))
|
g.Expect(err.Error()).To(Equal("reason"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ func TestBuildError_Error(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "with reason",
|
name: "with reason",
|
||||||
err: &BuildError{
|
err: &BuildError{
|
||||||
Reason: BuildErrorReason("reason"),
|
Reason: BuildErrorReason{"Reason", "reason"},
|
||||||
Err: errors.New("error"),
|
Err: errors.New("error"),
|
||||||
},
|
},
|
||||||
want: "reason: error",
|
want: "reason: error",
|
||||||
|
|
|
||||||
11
main.go
11
main.go
|
|
@ -189,12 +189,11 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err = (&controllers.HelmChartReconciler{
|
if err = (&controllers.HelmChartReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: mgr.GetScheme(),
|
Storage: storage,
|
||||||
Storage: storage,
|
Getters: getters,
|
||||||
Getters: getters,
|
EventRecorder: eventRecorder,
|
||||||
EventRecorder: eventRecorder,
|
Metrics: metricsH,
|
||||||
MetricsRecorder: metricsH.MetricsRecorder,
|
|
||||||
}).SetupWithManagerAndOptions(mgr, controllers.HelmChartReconcilerOptions{
|
}).SetupWithManagerAndOptions(mgr, controllers.HelmChartReconcilerOptions{
|
||||||
MaxConcurrentReconciles: concurrent,
|
MaxConcurrentReconciles: concurrent,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue