Take into account the oci-digest
This commit add the oci artifact digest into the release observed snapshot. This is used to later to add that value as an annotation. Signed-off-by: Soule BA <bah.soule@gmail.com>
This commit is contained in:
parent
49b47d4c44
commit
edec322a3d
|
|
@ -1002,11 +1002,16 @@ type HelmReleaseStatus struct {
|
|||
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`
|
||||
|
||||
// LastAttemptedRevision is the Source revision of the last reconciliation
|
||||
// attempt. For OCIRegistry sources, the 12 first characters of the digest are
|
||||
// attempt. For OCIRepository sources, the 12 first characters of the digest are
|
||||
// appended to the chart version e.g. "1.2.3+1234567890ab".
|
||||
// +optional
|
||||
LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"`
|
||||
|
||||
// LastAttemptedRevisionDigest is the digest of the last reconciliation attempt.
|
||||
// This is only set for OCIRepository sources.
|
||||
// +optional
|
||||
LastAttemptedRevisionDigest string `json:"lastAttemptedRevisionDigest,omitempty"`
|
||||
|
||||
// LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last
|
||||
// reconciliation attempt.
|
||||
// Deprecated: Use LastAttemptedConfigDigest instead.
|
||||
|
|
|
|||
|
|
@ -156,6 +156,9 @@ type Snapshot struct {
|
|||
// run by the controller.
|
||||
// +optional
|
||||
TestHooks *map[string]*TestHookStatus `json:"testHooks,omitempty"`
|
||||
// OCIDigest is the digest of the OCI artifact associated with the release.
|
||||
// +optional
|
||||
OCIDigest string `json:"ociDigest,omitempty"`
|
||||
}
|
||||
|
||||
// FullReleaseName returns the full name of the release in the format
|
||||
|
|
|
|||
|
|
@ -1100,6 +1100,10 @@ spec:
|
|||
description: Namespace is the namespace the release is deployed
|
||||
to.
|
||||
type: string
|
||||
ociDigest:
|
||||
description: OCIDigest is the digest of the OCI artifact associated
|
||||
with the release.
|
||||
type: string
|
||||
status:
|
||||
description: Status is the current state of the release.
|
||||
type: string
|
||||
|
|
@ -2374,6 +2378,10 @@ spec:
|
|||
description: Namespace is the namespace the release is deployed
|
||||
to.
|
||||
type: string
|
||||
ociDigest:
|
||||
description: OCIDigest is the digest of the OCI artifact associated
|
||||
with the release.
|
||||
type: string
|
||||
status:
|
||||
description: Status is the current state of the release.
|
||||
type: string
|
||||
|
|
@ -2452,9 +2460,14 @@ spec:
|
|||
lastAttemptedRevision:
|
||||
description: |-
|
||||
LastAttemptedRevision is the Source revision of the last reconciliation
|
||||
attempt. For OCIRegistry sources, the 12 first characters of the digest are
|
||||
attempt. For OCIRepository sources, the 12 first characters of the digest are
|
||||
appended to the chart version e.g. "1.2.3+1234567890ab".
|
||||
type: string
|
||||
lastAttemptedRevisionDigest:
|
||||
description: |-
|
||||
LastAttemptedRevisionDigest is the digest of the last reconciliation attempt.
|
||||
This is only set for OCIRepository sources.
|
||||
type: string
|
||||
lastAttemptedValuesChecksum:
|
||||
description: |-
|
||||
LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last
|
||||
|
|
|
|||
|
|
@ -1584,12 +1584,25 @@ string
|
|||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>LastAttemptedRevision is the Source revision of the last reconciliation
|
||||
attempt. For OCIRegistry sources, the 12 first characters of the digest are
|
||||
attempt. For OCIRepository sources, the 12 first characters of the digest are
|
||||
appended to the chart version e.g. “1.2.3+1234567890ab”.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>lastAttemptedRevisionDigest</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>LastAttemptedRevisionDigest is the digest of the last reconciliation attempt.
|
||||
This is only set for OCIRepository sources.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>lastAttemptedValuesChecksum</code><br>
|
||||
<em>
|
||||
string
|
||||
|
|
@ -2380,6 +2393,18 @@ TestHookStatus
|
|||
run by the controller.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>ociDigest</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>OCIDigest is the digest of the OCI artifact associated with the release.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -242,26 +242,10 @@ metadata:
|
|||
namespace: default
|
||||
spec:
|
||||
interval: 10m
|
||||
timeout: 5m
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: podinfo
|
||||
namespace: default
|
||||
releaseName: podinfo
|
||||
install:
|
||||
remediation:
|
||||
retries: 3
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 3
|
||||
test:
|
||||
enable: true
|
||||
driftDetection:
|
||||
mode: enabled
|
||||
ignore:
|
||||
- paths: ["/spec/replicas"]
|
||||
target:
|
||||
kind: Deployment
|
||||
values:
|
||||
replicaCount: 2
|
||||
```
|
||||
|
|
|
|||
|
|
@ -126,6 +126,11 @@ func VerifyReleaseObject(snapshot *v2.Snapshot, rls *helmrelease.Release) error
|
|||
verifier := relDig.Verifier()
|
||||
|
||||
obs := release.ObserveRelease(rls)
|
||||
|
||||
// unfortunately we have to pass in the OciDigest as is, because helmrelease.Release
|
||||
// does not have a field for it.
|
||||
obs.OCIDigest = snapshot.OCIDigest
|
||||
|
||||
if err = obs.Encode(verifier); err != nil {
|
||||
// We are expected to be able to encode valid JSON, error out without a
|
||||
// typed error assuming malfunction to signal to e.g. retry.
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe
|
|||
conditions.MarkUnknown(obj, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
|
||||
}
|
||||
|
||||
err = mutateChartWithSourceRevision(loadedChart, source)
|
||||
ociDigest, err := mutateChartWithSourceRevision(loadedChart, source)
|
||||
if err != nil {
|
||||
conditions.MarkFalse(obj, meta.ReadyCondition, "ChartMutateError", err.Error())
|
||||
return ctrl.Result{}, err
|
||||
|
|
@ -388,6 +388,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe
|
|||
// Set last attempt values.
|
||||
obj.Status.LastAttemptedGeneration = obj.Generation
|
||||
obj.Status.LastAttemptedRevision = loadedChart.Metadata.Version
|
||||
obj.Status.LastAttemptedRevisionDigest = ociDigest
|
||||
obj.Status.LastAttemptedConfigDigest = chartutil.DigestValues(digest.Canonical, values).String()
|
||||
obj.Status.LastAttemptedValuesChecksum = ""
|
||||
obj.Status.LastReleaseRevision = 0
|
||||
|
|
@ -865,24 +866,28 @@ func getNamespacedName(obj *v2.HelmRelease) (types.NamespacedName, error) {
|
|||
return namespacedName, nil
|
||||
}
|
||||
|
||||
func mutateChartWithSourceRevision(chart *chart.Chart, source source.Source) error {
|
||||
func mutateChartWithSourceRevision(chart *chart.Chart, source source.Source) (string, error) {
|
||||
// If the source is an OCIRepository, we can try to mutate the chart version
|
||||
// with the artifact revision. The revision is either a <tag>@<digest> or
|
||||
// just a digest.
|
||||
obj, ok := source.(*sourcev1.OCIRepository)
|
||||
if !ok {
|
||||
return nil
|
||||
// if not make sure to return an empty string to delete the digest of the
|
||||
// last attempted revision
|
||||
return "", nil
|
||||
}
|
||||
ver, err := semver.NewVersion(chart.Metadata.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
var ociDigest string
|
||||
revision := obj.GetArtifact().Revision
|
||||
switch {
|
||||
case strings.Contains(revision, "@"):
|
||||
tagD := strings.Split(revision, "@")
|
||||
if len(tagD) != 2 || tagD[0] != chart.Metadata.Version {
|
||||
return fmt.Errorf("artifact revision %s does not match chart version %s", tagD[0], chart.Metadata.Version)
|
||||
return "", fmt.Errorf("artifact revision %s does not match chart version %s", tagD[0], chart.Metadata.Version)
|
||||
}
|
||||
// algotithm are sha256, sha384, sha512 with the canonical being sha256
|
||||
// So every digest starts with a sha algorithm and a colon
|
||||
|
|
@ -890,17 +895,19 @@ func mutateChartWithSourceRevision(chart *chart.Chart, source source.Source) err
|
|||
// add the digest to the chart version to make sure mutable tags are detected
|
||||
*ver, err = ver.SetMetadata(sha[0:12])
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
ociDigest = tagD[1]
|
||||
default:
|
||||
// default to the digest
|
||||
sha := strings.Split(revision, ":")[1]
|
||||
*ver, err = ver.SetMetadata(sha[0:12])
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
ociDigest = revision
|
||||
}
|
||||
|
||||
chart.Metadata.Version = ver.String()
|
||||
return nil
|
||||
return ociDigest, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,6 +186,10 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
|
|||
g := NewWithT(t)
|
||||
|
||||
chart := &sourcev1b2.HelmChart{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: sourcev1b2.GroupVersion.String(),
|
||||
Kind: sourcev1b2.HelmChartKind,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "chart",
|
||||
Namespace: "mock",
|
||||
|
|
@ -238,6 +242,10 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
|
|||
g := NewWithT(t)
|
||||
|
||||
chart := &sourcev1b2.HelmChart{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: sourcev1b2.GroupVersion.String(),
|
||||
Kind: sourcev1b2.HelmChartKind,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "chart",
|
||||
Namespace: "mock",
|
||||
|
|
@ -960,6 +968,10 @@ func TestHelmReleaseReconciler_reconcileReleaseFromOCIRepositorySource(t *testin
|
|||
g := NewWithT(t)
|
||||
|
||||
ocirepo := &sourcev1b2.OCIRepository{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: sourcev1b2.GroupVersion.String(),
|
||||
Kind: sourcev1b2.OCIRepositoryKind,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ocirepo",
|
||||
Namespace: "mock",
|
||||
|
|
@ -2972,7 +2984,7 @@ func Test_TryMutateChartWithSourceRevision(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err := mutateChartWithSourceRevision(c, s)
|
||||
_, err := mutateChartWithSourceRevision(c, s)
|
||||
if tt.wantErr {
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@ func (r *CorrectClusterDrift) report(obj *v2.HelmRelease, changeSet *ssa.ChangeS
|
|||
sb.WriteString(changeSet.String())
|
||||
}
|
||||
|
||||
r.eventRecorder.AnnotatedEventf(obj, eventMeta(cur.ChartVersion, cur.ConfigDigest), corev1.EventTypeWarning,
|
||||
r.eventRecorder.AnnotatedEventf(obj, eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)), corev1.EventTypeWarning,
|
||||
"DriftCorrectionFailed", sb.String())
|
||||
case changeSet != nil && len(changeSet.Entries) > 0:
|
||||
r.eventRecorder.AnnotatedEventf(obj, eventMeta(cur.ChartVersion, cur.ConfigDigest), corev1.EventTypeNormal,
|
||||
r.eventRecorder.AnnotatedEventf(obj, eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)), corev1.EventTypeNormal,
|
||||
"DriftCorrected", "Cluster state of release %s has been corrected:\n%s",
|
||||
obj.Status.History.Latest().FullReleaseName(), changeSet.String())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ func (r *Install) Reconcile(ctx context.Context, req *Request) error {
|
|||
_, err := action.Install(ctx, cfg, req.Object, req.Chart, req.Values)
|
||||
|
||||
// Record the history of releases observed during the install.
|
||||
obsReleases.recordOnObject(req.Object)
|
||||
obsReleases.recordOnObject(req.Object, mutateOCIDigest)
|
||||
|
||||
if err != nil {
|
||||
r.failure(req, logBuf, err)
|
||||
|
|
@ -154,7 +154,8 @@ func (r *Install) failure(req *Request, buffer *action.LogBuffer, err error) {
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(req.Chart.Metadata.Version, chartutil.DigestValues(digest.Canonical, req.Values).String()),
|
||||
eventMeta(req.Chart.Metadata.Version, chartutil.DigestValues(digest.Canonical, req.Values).String(),
|
||||
addOCIDigest(req.Object.Status.LastAttemptedRevisionDigest)),
|
||||
corev1.EventTypeWarning,
|
||||
v2.InstallFailedReason,
|
||||
eventMessageWithLog(msg, buffer),
|
||||
|
|
@ -181,7 +182,7 @@ func (r *Install) success(req *Request) {
|
|||
// Record event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
v2.InstallSucceededReason,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -307,6 +307,9 @@ func TestInstall_failure(t *testing.T) {
|
|||
ReleaseName: mockReleaseName,
|
||||
TargetNamespace: mockReleaseNamespace,
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
LastAttemptedRevisionDigest: "sha256:1234567890",
|
||||
},
|
||||
}
|
||||
chrt = testutil.BuildChart()
|
||||
err = errors.New("installation error")
|
||||
|
|
@ -337,6 +340,7 @@ func TestInstall_failure(t *testing.T) {
|
|||
Message: expectMsg,
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
eventMetaGroupKey(metaOCIDigestKey): obj.Status.LastAttemptedRevisionDigest,
|
||||
eventMetaGroupKey(eventv1.MetaRevisionKey): chrt.Metadata.Version,
|
||||
eventMetaGroupKey(eventv1.MetaTokenKey): chartutil.DigestValues(digest.Canonical, req.Values).String(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ var (
|
|||
ErrReleaseMismatch = errors.New("release mismatch")
|
||||
)
|
||||
|
||||
// mutateObservedRelease is a function that mutates the Observation with the
|
||||
// given HelmRelease object.
|
||||
type mutateObservedRelease func(*v2.HelmRelease, release.Observation) release.Observation
|
||||
|
||||
// observedReleases is a map of Helm releases as observed to be written to the
|
||||
// Helm storage. The key is the version of the release.
|
||||
type observedReleases map[int]release.Observation
|
||||
|
|
@ -58,7 +62,7 @@ func (r observedReleases) sortedVersions() (versions []int) {
|
|||
}
|
||||
|
||||
// recordOnObject records the observed releases on the HelmRelease object.
|
||||
func (r observedReleases) recordOnObject(obj *v2.HelmRelease) {
|
||||
func (r observedReleases) recordOnObject(obj *v2.HelmRelease, mutators ...mutateObservedRelease) {
|
||||
switch len(r) {
|
||||
case 0:
|
||||
return
|
||||
|
|
@ -67,17 +71,25 @@ func (r observedReleases) recordOnObject(obj *v2.HelmRelease) {
|
|||
for _, o := range r {
|
||||
obs = o
|
||||
}
|
||||
for _, mut := range mutators {
|
||||
obs = mut(obj, obs)
|
||||
}
|
||||
obj.Status.History = append(v2.Snapshots{release.ObservedToSnapshot(obs)}, obj.Status.History...)
|
||||
default:
|
||||
versions := r.sortedVersions()
|
||||
|
||||
obj.Status.History = append(v2.Snapshots{release.ObservedToSnapshot(r[versions[0]])}, obj.Status.History...)
|
||||
obs := r[versions[0]]
|
||||
for _, mut := range mutators {
|
||||
obs = mut(obj, obs)
|
||||
}
|
||||
obj.Status.History = append(v2.Snapshots{release.ObservedToSnapshot(obs)}, obj.Status.History...)
|
||||
|
||||
for _, ver := range versions[1:] {
|
||||
for i := range obj.Status.History {
|
||||
snap := obj.Status.History[i]
|
||||
if snap.Targets(r[ver].Name, r[ver].Namespace, r[ver].Version) {
|
||||
newSnap := release.ObservedToSnapshot(r[ver])
|
||||
obs := r[ver]
|
||||
obs.OCIDigest = snap.OCIDigest
|
||||
newSnap := release.ObservedToSnapshot(obs)
|
||||
newSnap.SetTestHooks(snap.GetTestHooks())
|
||||
obj.Status.History[i] = newSnap
|
||||
return
|
||||
|
|
@ -87,6 +99,11 @@ func (r observedReleases) recordOnObject(obj *v2.HelmRelease) {
|
|||
}
|
||||
}
|
||||
|
||||
func mutateOCIDigest(obj *v2.HelmRelease, obs release.Observation) release.Observation {
|
||||
obs.OCIDigest = obj.Status.LastAttemptedRevisionDigest
|
||||
return obs
|
||||
}
|
||||
|
||||
// observeRelease returns a storage.ObserveFunc that stores the observed
|
||||
// releases in the given observedReleases map.
|
||||
// It can be used for Helm actions that modify multiple releases in the
|
||||
|
|
@ -174,9 +191,15 @@ func eventMessageWithLog(msg string, log *action.LogBuffer) string {
|
|||
return msg
|
||||
}
|
||||
|
||||
// addMeta is a function that adds metadata to an event map.
|
||||
type addMeta func(map[string]string)
|
||||
|
||||
// metaOCIDigestKey is the key for the OCI digest metadata.
|
||||
const metaOCIDigestKey = "oci-digest"
|
||||
|
||||
// eventMeta returns the event (annotation) metadata based on the given
|
||||
// parameters.
|
||||
func eventMeta(revision, token string) map[string]string {
|
||||
func eventMeta(revision, token string, metas ...addMeta) map[string]string {
|
||||
var metadata map[string]string
|
||||
if revision != "" || token != "" {
|
||||
metadata = make(map[string]string)
|
||||
|
|
@ -187,9 +210,25 @@ func eventMeta(revision, token string) map[string]string {
|
|||
metadata[eventMetaGroupKey(eventv1.MetaTokenKey)] = token
|
||||
}
|
||||
}
|
||||
|
||||
for _, add := range metas {
|
||||
add(metadata)
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
func addOCIDigest(digest string) addMeta {
|
||||
return func(m map[string]string) {
|
||||
if digest != "" {
|
||||
if m == nil {
|
||||
m = make(map[string]string)
|
||||
}
|
||||
m[eventMetaGroupKey(metaOCIDigestKey)] = digest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eventMetaGroupKey returns the event (annotation) metadata key prefixed with
|
||||
// the group.
|
||||
func eventMetaGroupKey(key string) string {
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||
package reconcile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
. "github.com/onsi/gomega"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
|
@ -454,3 +456,179 @@ func mockLogBuffer(size int, lines int) *action.LogBuffer {
|
|||
}
|
||||
return log
|
||||
}
|
||||
|
||||
func Test_RecordOnObject(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
obj *v2.HelmRelease
|
||||
r observedReleases
|
||||
mutate bool
|
||||
testFunc func(*v2.HelmRelease) error
|
||||
}{
|
||||
{
|
||||
name: "record observed releases",
|
||||
obj: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: mockReleaseName,
|
||||
Namespace: mockReleaseNamespace,
|
||||
},
|
||||
},
|
||||
r: observedReleases{
|
||||
1: {
|
||||
Name: mockReleaseName,
|
||||
Version: 1,
|
||||
ChartMetadata: chart.Metadata{
|
||||
Name: mockReleaseName,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
testFunc: func(obj *v2.HelmRelease) error {
|
||||
if len(obj.Status.History) != 1 {
|
||||
return fmt.Errorf("history length is not 1")
|
||||
}
|
||||
if obj.Status.History[0].Name != mockReleaseName {
|
||||
return fmt.Errorf("release name is not %s", mockReleaseName)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "record observed releases with multiple versions",
|
||||
obj: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: mockReleaseName,
|
||||
Namespace: mockReleaseNamespace,
|
||||
},
|
||||
},
|
||||
r: observedReleases{
|
||||
1: {
|
||||
Name: mockReleaseName,
|
||||
Version: 1,
|
||||
ChartMetadata: chart.Metadata{
|
||||
Name: mockReleaseName,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
2: {
|
||||
Name: mockReleaseName,
|
||||
Version: 2,
|
||||
ChartMetadata: chart.Metadata{
|
||||
Name: mockReleaseName,
|
||||
Version: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
testFunc: func(obj *v2.HelmRelease) error {
|
||||
if len(obj.Status.History) != 1 {
|
||||
return fmt.Errorf("want history length 1, got %d", len(obj.Status.History))
|
||||
}
|
||||
if obj.Status.History[0].Name != mockReleaseName {
|
||||
return fmt.Errorf("release name is not %s", mockReleaseName)
|
||||
}
|
||||
if obj.Status.History[0].ChartVersion != "2.0.0" {
|
||||
return fmt.Errorf("want chart version %s, got %s", "2.0.0", obj.Status.History[0].ChartVersion)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "record observed releases with status digest",
|
||||
obj: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: mockReleaseName,
|
||||
Namespace: mockReleaseNamespace,
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
LastAttemptedRevisionDigest: "sha256:123456",
|
||||
},
|
||||
},
|
||||
r: observedReleases{
|
||||
1: {
|
||||
Name: mockReleaseName,
|
||||
Version: 1,
|
||||
ChartMetadata: chart.Metadata{
|
||||
Name: mockReleaseName,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
mutate: true,
|
||||
testFunc: func(obj *v2.HelmRelease) error {
|
||||
h := obj.Status.History.Latest()
|
||||
if h.Name != mockReleaseName {
|
||||
return fmt.Errorf("release name is not %s", mockReleaseName)
|
||||
}
|
||||
if h.ChartVersion != "1.0.0" {
|
||||
return fmt.Errorf("want chart version %s, got %s", "1.0.0", h.ChartVersion)
|
||||
}
|
||||
if h.OCIDigest != obj.Status.LastAttemptedRevisionDigest {
|
||||
return fmt.Errorf("want digest %s, got %s", obj.Status.LastAttemptedRevisionDigest, h.OCIDigest)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "record observed releases with multiple versions and status digest",
|
||||
obj: &v2.HelmRelease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: mockReleaseName,
|
||||
Namespace: mockReleaseNamespace,
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
LastAttemptedRevisionDigest: "sha256:123456",
|
||||
},
|
||||
},
|
||||
r: observedReleases{
|
||||
1: {
|
||||
Name: mockReleaseName,
|
||||
Version: 1,
|
||||
ChartMetadata: chart.Metadata{
|
||||
Name: mockReleaseName,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
2: {
|
||||
Name: mockReleaseName,
|
||||
Version: 2,
|
||||
ChartMetadata: chart.Metadata{
|
||||
Name: mockReleaseName,
|
||||
Version: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
mutate: true,
|
||||
testFunc: func(obj *v2.HelmRelease) error {
|
||||
if len(obj.Status.History) != 1 {
|
||||
return fmt.Errorf("want history length 1, got %d", len(obj.Status.History))
|
||||
}
|
||||
h := obj.Status.History.Latest()
|
||||
if h.Name != mockReleaseName {
|
||||
return fmt.Errorf("release name is not %s", mockReleaseName)
|
||||
}
|
||||
if h.ChartVersion != "2.0.0" {
|
||||
return fmt.Errorf("want chart version %s, got %s", "2.0.0", h.ChartVersion)
|
||||
}
|
||||
if h.OCIDigest != obj.Status.LastAttemptedRevisionDigest {
|
||||
return fmt.Errorf("want digest %s, got %s", obj.Status.LastAttemptedRevisionDigest, h.OCIDigest)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
if tt.mutate {
|
||||
tt.r.recordOnObject(tt.obj, mutateOCIDigest)
|
||||
} else {
|
||||
tt.r.recordOnObject(tt.obj)
|
||||
}
|
||||
err := tt.testFunc(tt.obj)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func (r *RollbackRemediation) failure(req *Request, prev *v2.Snapshot, buffer *a
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(prev.ChartVersion, chartutil.DigestValues(digest.Canonical, req.Values).String()),
|
||||
eventMeta(prev.ChartVersion, chartutil.DigestValues(digest.Canonical, req.Values).String(), addOCIDigest(prev.OCIDigest)),
|
||||
corev1.EventTypeWarning,
|
||||
v2.RollbackFailedReason,
|
||||
eventMessageWithLog(msg, buffer),
|
||||
|
|
@ -162,7 +162,7 @@ func (r *RollbackRemediation) success(req *Request, prev *v2.Snapshot) {
|
|||
// Record event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(prev.ChartVersion, chartutil.DigestValues(digest.Canonical, req.Values).String()),
|
||||
eventMeta(prev.ChartVersion, chartutil.DigestValues(digest.Canonical, req.Values).String(), addOCIDigest(prev.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
v2.RollbackSucceededReason,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ func (r *Test) failure(req *Request, err error) {
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeWarning,
|
||||
v2.TestFailedReason,
|
||||
msg,
|
||||
|
|
@ -181,7 +181,7 @@ func (r *Test) success(req *Request) {
|
|||
// Record event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
v2.TestSucceededReason,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ func (r *Uninstall) failure(req *Request, buffer *action.LogBuffer, err error) {
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeWarning, v2.UninstallFailedReason,
|
||||
eventMessageWithLog(msg, buffer),
|
||||
)
|
||||
|
|
@ -201,7 +201,7 @@ func (r *Uninstall) success(req *Request) {
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
v2.UninstallSucceededReason,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ func (r *UninstallRemediation) failure(req *Request, buffer *action.LogBuffer, e
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeWarning,
|
||||
v2.UninstallFailedReason,
|
||||
eventMessageWithLog(msg, buffer),
|
||||
|
|
@ -175,7 +175,7 @@ func (r *UninstallRemediation) success(req *Request) {
|
|||
// Record event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
v2.UninstallSucceededReason,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ func (r *Unlock) failure(req *Request, cur *v2.Snapshot, status helmrelease.Stat
|
|||
// Record warning event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeWarning,
|
||||
"PendingRelease",
|
||||
msg,
|
||||
|
|
@ -138,7 +138,7 @@ func (r *Unlock) success(req *Request, cur *v2.Snapshot, status helmrelease.Stat
|
|||
// Record event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
"PendingRelease",
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func (r *Upgrade) Reconcile(ctx context.Context, req *Request) error {
|
|||
_, err := action.Upgrade(ctx, cfg, req.Object, req.Chart, req.Values)
|
||||
|
||||
// Record the history of releases observed during the upgrade.
|
||||
obsReleases.recordOnObject(req.Object)
|
||||
obsReleases.recordOnObject(req.Object, mutateOCIDigest)
|
||||
|
||||
if err != nil {
|
||||
r.failure(req, logBuf, err)
|
||||
|
|
@ -144,7 +144,8 @@ func (r *Upgrade) failure(req *Request, buffer *action.LogBuffer, err error) {
|
|||
// Condition summary.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(req.Chart.Metadata.Version, chartutil.DigestValues(digest.Canonical, req.Values).String()),
|
||||
eventMeta(req.Chart.Metadata.Version, chartutil.DigestValues(digest.Canonical, req.Values).String(),
|
||||
addOCIDigest(req.Object.Status.LastAttemptedRevisionDigest)),
|
||||
corev1.EventTypeWarning,
|
||||
v2.UpgradeFailedReason,
|
||||
eventMessageWithLog(msg, buffer),
|
||||
|
|
@ -171,7 +172,7 @@ func (r *Upgrade) success(req *Request) {
|
|||
// Record event.
|
||||
r.eventRecorder.AnnotatedEventf(
|
||||
req.Object,
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest),
|
||||
eventMeta(cur.ChartVersion, cur.ConfigDigest, addOCIDigest(cur.OCIDigest)),
|
||||
corev1.EventTypeNormal,
|
||||
v2.UpgradeSucceededReason,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -438,6 +438,9 @@ func TestUpgrade_failure(t *testing.T) {
|
|||
ReleaseName: mockReleaseName,
|
||||
TargetNamespace: mockReleaseNamespace,
|
||||
},
|
||||
Status: v2.HelmReleaseStatus{
|
||||
LastAttemptedRevisionDigest: "sha256:1234567890",
|
||||
},
|
||||
}
|
||||
chrt = testutil.BuildChart()
|
||||
err = errors.New("upgrade error")
|
||||
|
|
@ -468,6 +471,7 @@ func TestUpgrade_failure(t *testing.T) {
|
|||
Message: expectMsg,
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
eventMetaGroupKey(metaOCIDigestKey): obj.Status.LastAttemptedRevisionDigest,
|
||||
eventMetaGroupKey(eventv1.MetaRevisionKey): chrt.Metadata.Version,
|
||||
eventMetaGroupKey(eventv1.MetaTokenKey): chartutil.DigestValues(digest.Canonical, req.Values).String(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func TestDigest(t *testing.T) {
|
|||
rel: Observation{
|
||||
Name: "foo",
|
||||
},
|
||||
exp: "sha256:91b6773f7696d3eb405708a07e2daedc6e69664dabac8e10af7d570d09f947d5",
|
||||
exp: "sha256:ca8901e499a79368647134cc4f1c2118f8e7ec64c8a4703b281d17fb01acfbed",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ type Observation struct {
|
|||
Hooks []helmrelease.Hook `json:"hooks"`
|
||||
// Namespace is the Kubernetes namespace of the release.
|
||||
Namespace string `json:"namespace"`
|
||||
// OCIDigest is the digest of the OCI artifact that was used to
|
||||
OCIDigest string `json:"ociDigest"`
|
||||
}
|
||||
|
||||
// Targets returns if the release matches the given name, namespace and
|
||||
|
|
@ -166,6 +168,7 @@ func ObservedToSnapshot(rls Observation) *v2.Snapshot {
|
|||
LastDeployed: metav1.NewTime(rls.Info.LastDeployed.Time),
|
||||
Deleted: metav1.NewTime(rls.Info.Deleted.Time),
|
||||
Status: rls.Info.Status.String(),
|
||||
OCIDigest: rls.OCIDigest,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue