GitRepo: Add observed content config in status

Replace content config checksum with explicit artifact content config
observations. It makes the observations of the controller more
transparent and easier to debug.

Introduces `observedIgnore`, `observedRecurseSubmodules` and
`observedInclude` status fields.

Signed-off-by: Sunny <darkowlzz@protonmail.com>
This commit is contained in:
Sunny 2022-10-04 00:47:13 +05:30
parent 278a223bc6
commit e996848555
7 changed files with 639 additions and 106 deletions

View File

@ -224,9 +224,27 @@ type GitRepositoryStatus struct {
// be used to determine if the content of the included repository has
// changed.
// It has the format of `<algo>:<checksum>`, for example: `sha256:<checksum>`.
//
// Deprecated: Replaced with explicit fields for observed artifact content
// config in the status.
// +optional
ContentConfigChecksum string `json:"contentConfigChecksum,omitempty"`
// ObservedIgnore is the observed exclusion patterns used for constructing
// the source artifact.
// +optional
ObservedIgnore *string `json:"observedIgnore,omitempty"`
// ObservedRecurseSubmodules is the observed resource submodules
// configuration used to produce the current Artifact.
// +optional
ObservedRecurseSubmodules bool `json:"observedRecurseSubmodules,omitempty"`
// ObservedInclude is the observed list of GitRepository resources used to
// to produce the current Artifact.
// +optional
ObservedInclude []GitRepositoryInclude `json:"observedInclude,omitempty"`
meta.ReconcileRequestStatus `json:",inline"`
}

View File

@ -346,6 +346,16 @@ func (in *GitRepositoryStatus) DeepCopyInto(out *GitRepositoryStatus) {
}
}
}
if in.ObservedIgnore != nil {
in, out := &in.ObservedIgnore, &out.ObservedIgnore
*out = new(string)
**out = **in
}
if in.ObservedInclude != nil {
in, out := &in.ObservedInclude, &out.ObservedInclude
*out = make([]GitRepositoryInclude, len(*in))
copy(*out, *in)
}
out.ReconcileRequestStatus = in.ReconcileRequestStatus
}

View File

@ -658,13 +658,14 @@ spec:
type: object
type: array
contentConfigChecksum:
description: 'ContentConfigChecksum is a checksum of all the configurations
description: "ContentConfigChecksum is a checksum of all the configurations
related to the content of the source artifact: - .spec.ignore -
.spec.recurseSubmodules - .spec.included and the checksum of the
included artifacts observed in .status.observedGeneration version
of the object. This can be used to determine if the content of the
included repository has changed. It has the format of `<algo>:<checksum>`,
for example: `sha256:<checksum>`.'
for example: `sha256:<checksum>`. \n Deprecated: Replaced with explicit
fields for observed artifact content config in the status."
type: string
includedArtifacts:
description: IncludedArtifacts contains a list of the last successfully
@ -723,6 +724,44 @@ spec:
the GitRepository object.
format: int64
type: integer
observedIgnore:
description: ObservedIgnore is the observed exclusion patterns used
for constructing the source artifact.
type: string
observedInclude:
description: ObservedInclude is the observed list of GitRepository
resources used to to produce the current Artifact.
items:
description: GitRepositoryInclude specifies a local reference to
a GitRepository which Artifact (sub-)contents must be included,
and where they should be placed.
properties:
fromPath:
description: FromPath specifies the path to copy contents from,
defaults to the root of the Artifact.
type: string
repository:
description: GitRepositoryRef specifies the GitRepository which
Artifact contents must be included.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
toPath:
description: ToPath specifies the path to copy contents to,
defaults to the name of the GitRepositoryRef.
type: string
required:
- repository
type: object
type: array
observedRecurseSubmodules:
description: ObservedRecurseSubmodules is the observed resource submodules
configuration used to produce the current Artifact.
type: boolean
url:
description: URL is the dynamic fetch link for the latest Artifact.
It is provided on a "best effort" basis, and using the precise GitRepositoryStatus.Artifact

View File

@ -18,12 +18,10 @@ package controllers
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@ -33,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
kuberecorder "k8s.io/client-go/tools/record"
"k8s.io/utils/pointer"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
@ -507,8 +506,8 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
// If it's a partial commit obtained from an existing artifact, check if the
// reconciliation can be skipped if other configurations have not changed.
if !git.IsConcreteCommit(*commit) {
// Calculate content configuration checksum.
if r.calculateContentConfigChecksum(obj, includes) == obj.Status.ContentConfigChecksum {
// Check if the content config contributing to the artifact has changed.
if !gitContentConfigChanged(obj, includes) {
ge := serror.NewGeneric(
fmt.Errorf("no changes since last reconcilation: observed revision '%s'",
commit.String()), sourcev1.GitOperationSucceedReason,
@ -559,27 +558,24 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
//
// The inspection of the given data to the object is differed, ensuring any
// stale observations like v1beta2.ArtifactOutdatedCondition are removed.
// If the given Artifact and/or artifactSet (includes) and the content config
// checksum do not differ from the object's current, it returns early.
// If the given Artifact and/or artifactSet (includes) and observed artifact
// content config do not differ from the object's current, it returns early.
// Source ignore patterns are loaded, and the given directory is archived while
// taking these patterns into account.
// On a successful archive, the Artifact, Includes and new content config
// checksum in the Status of the object are set, and the symlink in the Storage
// is updated to its path.
// On a successful archive, the Artifact, Includes, observed ignore, recurse
// submodules and observed include in the Status of the object are set, and the
// symlink in the Storage is updated to its path.
func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
obj *sourcev1.GitRepository, commit *git.Commit, includes *artifactSet, dir string) (sreconcile.Result, error) {
// Create potential new artifact with current available metadata
artifact := r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), commit.String(), fmt.Sprintf("%s.tar.gz", commit.Hash.String()))
// Calculate the content config checksum.
ccc := r.calculateContentConfigChecksum(obj, includes)
// Set the ArtifactInStorageCondition if there's no drift.
defer func() {
if obj.GetArtifact().HasRevision(artifact.Revision) &&
!includes.Diff(obj.Status.IncludedArtifacts) &&
obj.Status.ContentConfigChecksum == ccc {
!gitContentConfigChanged(obj, includes) {
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason,
"stored artifact for revision '%s'", artifact.Revision)
@ -589,7 +585,7 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
// The artifact is up-to-date
if obj.GetArtifact().HasRevision(artifact.Revision) &&
!includes.Diff(obj.Status.IncludedArtifacts) &&
obj.Status.ContentConfigChecksum == ccc {
!gitContentConfigChanged(obj, includes) {
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.ArtifactUpToDateReason, "artifact up-to-date with remote revision: '%s'", artifact.Revision)
return sreconcile.ResultSuccess, nil
}
@ -652,10 +648,13 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
return sreconcile.ResultEmpty, e
}
// Record it on the object
// Record the observations on the object.
obj.Status.Artifact = artifact.DeepCopy()
obj.Status.IncludedArtifacts = *includes
obj.Status.ContentConfigChecksum = ccc
obj.Status.ContentConfigChecksum = "" // To be removed in the next API version.
obj.Status.ObservedIgnore = obj.Spec.Ignore
obj.Status.ObservedRecurseSubmodules = obj.Spec.RecurseSubmodules
obj.Status.ObservedInclude = obj.Spec.Include
// Update symlink on a "best effort" basis
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
@ -825,39 +824,6 @@ func (r *GitRepositoryReconciler) fetchIncludes(ctx context.Context, obj *source
return &artifacts, nil
}
// calculateContentConfigChecksum calculates a checksum of all the
// configurations that result in a change in the source artifact. It can be used
// to decide if further reconciliation is needed when an artifact already exists
// for a set of configurations.
func (r *GitRepositoryReconciler) calculateContentConfigChecksum(obj *sourcev1.GitRepository, includes *artifactSet) string {
c := []byte{}
// Consider the ignore rules and recurse submodules.
if obj.Spec.Ignore != nil {
c = append(c, []byte(*obj.Spec.Ignore)...)
}
c = append(c, []byte(strconv.FormatBool(obj.Spec.RecurseSubmodules))...)
// Consider the included repository attributes.
for _, incl := range obj.Spec.Include {
c = append(c, []byte(incl.GitRepositoryRef.Name+incl.FromPath+incl.ToPath)...)
}
// Consider the checksum and revision of all the included remote artifact.
// This ensures that if the included repos get updated, this checksum changes.
// NOTE: The content of an artifact may change at the same revision if the
// ignore rules change. Hence, consider both checksum and revision to
// capture changes in artifact checksum as well.
// TODO: Fix artifactSet.Diff() to consider checksum as well.
if includes != nil {
for _, incl := range *includes {
c = append(c, []byte(incl.Checksum)...)
c = append(c, []byte(incl.Revision)...)
}
}
return fmt.Sprintf("sha256:%x", sha256.Sum256(c))
}
// verifyCommitSignature verifies the signature of the given Git commit, if a
// verification mode is specified on the object.
// If the signature can not be verified or the verification fails, it records
@ -978,3 +944,64 @@ func (r *GitRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Obj
}
r.Eventf(obj, eventType, reason, msg)
}
// gitContentConfigChanged evaluates the current spec with the observations of
// the artifact in the status to determine if artifact content configuration has
// changed and requires rebuilding the artifact.
func gitContentConfigChanged(obj *sourcev1.GitRepository, includes *artifactSet) bool {
if !pointer.StringEqual(obj.Spec.Ignore, obj.Status.ObservedIgnore) {
return true
}
if obj.Spec.RecurseSubmodules != obj.Status.ObservedRecurseSubmodules {
return true
}
if len(obj.Spec.Include) != len(obj.Status.ObservedInclude) {
return true
}
// Convert artifactSet to index addressable artifacts and ensure that it and
// the included artifacts include all the include from the spec.
artifacts := []*sourcev1.Artifact(*includes)
if len(obj.Spec.Include) != len(artifacts) {
return true
}
if len(obj.Spec.Include) != len(obj.Status.IncludedArtifacts) {
return true
}
// The order of spec.include, status.IncludeArtifacts and
// status.observedInclude are the same. Compare the values by index.
for index, incl := range obj.Spec.Include {
observedIncl := obj.Status.ObservedInclude[index]
observedInclArtifact := obj.Status.IncludedArtifacts[index]
currentIncl := artifacts[index]
// Check if the include are the same in spec and status.
if !gitRepositoryIncludeEqual(incl, observedIncl) {
return true
}
// Check if the included repositories are still the same.
if observedInclArtifact.Revision != currentIncl.Revision {
return true
}
if observedInclArtifact.Checksum != currentIncl.Checksum {
return true
}
}
return false
}
// Returns true if both GitRepositoryIncludes are equal.
func gitRepositoryIncludeEqual(a, b sourcev1.GitRepositoryInclude) bool {
if a.GitRepositoryRef != b.GitRepositoryRef {
return false
}
if a.FromPath != b.FromPath {
return false
}
if a.ToPath != b.ToPath {
return false
}
return true
}

View File

@ -143,7 +143,6 @@ Oomb3gD/TRf/nAdVED+k81GdLzciYdUGtI71/qI47G0nMBluLRE=
=/4e+
-----END PGP PUBLIC KEY BLOCK-----
`
emptyContentConfigChecksum = "sha256:fcbcf165908dd18a9e49f7ff27810176db8e9f63b4352213741664245224f8aa"
)
var (
@ -685,8 +684,6 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
Revision: "staging/" + latestRev,
Path: randStringRunes(10),
},
// Checksum with all the relevant fields unset.
ContentConfigChecksum: emptyContentConfigChecksum,
}
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason, "foo")
},
@ -709,8 +706,6 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
Revision: "staging/" + latestRev,
Path: randStringRunes(10),
},
// Checksum with all the relevant fields unset.
ContentConfigChecksum: emptyContentConfigChecksum,
}
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason, "foo")
},
@ -835,6 +830,9 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
includes: artifactSet{&sourcev1.Artifact{Revision: "main/revision"}},
beforeFunc: func(obj *sourcev1.GitRepository) {
obj.Spec.Interval = metav1.Duration{Duration: interval}
obj.Spec.Include = []sourcev1.GitRepositoryInclude{
{GitRepositoryRef: meta.LocalObjectReference{Name: "foo"}},
}
},
afterFunc: func(t *WithT, obj *sourcev1.GitRepository) {
t.Expect(obj.GetArtifact()).ToNot(BeNil())
@ -850,12 +848,15 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
{
name: "Up-to-date artifact should not update status",
dir: "testdata/git/repository",
includes: artifactSet{&sourcev1.Artifact{Revision: "main/revision"}},
includes: artifactSet{&sourcev1.Artifact{Revision: "main/revision", Checksum: "some-checksum"}},
beforeFunc: func(obj *sourcev1.GitRepository) {
obj.Spec.Interval = metav1.Duration{Duration: interval}
obj.Spec.Include = []sourcev1.GitRepositoryInclude{
{GitRepositoryRef: meta.LocalObjectReference{Name: "foo"}},
}
obj.Status.Artifact = &sourcev1.Artifact{Revision: "main/revision"}
obj.Status.IncludedArtifacts = []*sourcev1.Artifact{{Revision: "main/revision", Checksum: "some-checksum"}}
obj.Status.ContentConfigChecksum = "sha256:f825d11a1c5987e033d2cb36449a3b0435a6abc9b2bfdbcdcc7c49bf40e9285d"
obj.Status.ObservedInclude = obj.Spec.Include
},
afterFunc: func(t *WithT, obj *sourcev1.GitRepository) {
t.Expect(obj.Status.URL).To(BeEmpty())
@ -2145,53 +2146,6 @@ func TestGitRepositoryReconciler_fetchIncludes(t *testing.T) {
}
}
func TestGitRepositoryReconciler_calculateContentConfigChecksum(t *testing.T) {
g := NewWithT(t)
obj := &sourcev1.GitRepository{}
r := &GitRepositoryReconciler{}
emptyChecksum := r.calculateContentConfigChecksum(obj, nil)
g.Expect(emptyChecksum).To(Equal(emptyContentConfigChecksum))
// Ignore modified.
obj.Spec.Ignore = pointer.String("some-rule")
ignoreModChecksum := r.calculateContentConfigChecksum(obj, nil)
g.Expect(emptyChecksum).ToNot(Equal(ignoreModChecksum))
// Recurse submodules modified.
obj.Spec.RecurseSubmodules = true
submodModChecksum := r.calculateContentConfigChecksum(obj, nil)
g.Expect(ignoreModChecksum).ToNot(Equal(submodModChecksum))
// Include modified.
obj.Spec.Include = []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "aaa",
ToPath: "bbb",
},
}
artifacts := &artifactSet{
&sourcev1.Artifact{Revision: "some-revision-1", Checksum: "some-checksum-1"},
}
includeModChecksum := r.calculateContentConfigChecksum(obj, artifacts)
g.Expect(submodModChecksum).ToNot(Equal(includeModChecksum))
// Artifact modified revision.
artifacts = &artifactSet{
&sourcev1.Artifact{Revision: "some-revision-2", Checksum: "some-checksum-1"},
}
artifactModChecksum := r.calculateContentConfigChecksum(obj, artifacts)
g.Expect(includeModChecksum).ToNot(Equal(artifactModChecksum))
// Artifact modified checksum.
artifacts = &artifactSet{
&sourcev1.Artifact{Revision: "some-revision-2", Checksum: "some-checksum-2"},
}
artifactCsumModChecksum := r.calculateContentConfigChecksum(obj, artifacts)
g.Expect(artifactModChecksum).ToNot(Equal(artifactCsumModChecksum))
}
func resetChmod(path string, dirMode os.FileMode, fileMode os.FileMode) error {
err := filepath.Walk(path,
func(path string, info os.FileInfo, err error) error {
@ -2212,3 +2166,371 @@ func resetChmod(path string, dirMode os.FileMode, fileMode os.FileMode) error {
return nil
}
func TestGitRepositoryIncludeEqual(t *testing.T) {
tests := []struct {
name string
a sourcev1.GitRepositoryInclude
b sourcev1.GitRepositoryInclude
want bool
}{
{
name: "empty",
want: true,
},
{
name: "different refs",
a: sourcev1.GitRepositoryInclude{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
},
b: sourcev1.GitRepositoryInclude{
GitRepositoryRef: meta.LocalObjectReference{Name: "bar"},
},
want: false,
},
{
name: "same refs",
a: sourcev1.GitRepositoryInclude{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
},
b: sourcev1.GitRepositoryInclude{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
},
want: true,
},
{
name: "different from paths",
a: sourcev1.GitRepositoryInclude{FromPath: "foo"},
b: sourcev1.GitRepositoryInclude{FromPath: "bar"},
want: false,
},
{
name: "same from paths",
a: sourcev1.GitRepositoryInclude{FromPath: "foo"},
b: sourcev1.GitRepositoryInclude{FromPath: "foo"},
want: true,
},
{
name: "different to paths",
a: sourcev1.GitRepositoryInclude{ToPath: "foo"},
b: sourcev1.GitRepositoryInclude{ToPath: "bar"},
want: false,
},
{
name: "same to paths",
a: sourcev1.GitRepositoryInclude{ToPath: "foo"},
b: sourcev1.GitRepositoryInclude{ToPath: "foo"},
want: true,
},
{
name: "same all",
a: sourcev1.GitRepositoryInclude{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo-ref"},
FromPath: "foo-path",
ToPath: "bar-path",
},
b: sourcev1.GitRepositoryInclude{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo-ref"},
FromPath: "foo-path",
ToPath: "bar-path",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
g.Expect(gitRepositoryIncludeEqual(tt.a, tt.b)).To(Equal(tt.want))
})
}
}
func TestGitContentConfigChanged(t *testing.T) {
tests := []struct {
name string
obj sourcev1.GitRepository
artifacts []*sourcev1.Artifact
want bool
}{
{
name: "no content config",
want: false,
},
{
name: "unobserved ignore",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{Ignore: pointer.String("foo")},
},
want: true,
},
{
name: "observed ignore",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{Ignore: pointer.String("foo")},
Status: sourcev1.GitRepositoryStatus{ObservedIgnore: pointer.String("foo")},
},
want: false,
},
{
name: "unobserved recurse submodules",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{RecurseSubmodules: true},
},
want: true,
},
{
name: "observed recurse submodules",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{RecurseSubmodules: true},
Status: sourcev1.GitRepositoryStatus{ObservedRecurseSubmodules: true},
},
want: false,
},
{
name: "unobserved include",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{GitRepositoryRef: meta.LocalObjectReference{Name: "foo"}, FromPath: "bar", ToPath: "baz"},
},
},
},
want: true,
},
{
name: "observed include",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
ObservedInclude: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
IncludedArtifacts: []*sourcev1.Artifact{{Revision: "aaa", Checksum: "bbb"}},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
},
want: false,
},
{
name: "observed include but different artifact revision",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
ObservedInclude: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
IncludedArtifacts: []*sourcev1.Artifact{{Revision: "aaa", Checksum: "bbb"}},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "ccc", Checksum: "bbb"},
},
want: true,
},
{
name: "observed include but different artifact checksum",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
ObservedInclude: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
IncludedArtifacts: []*sourcev1.Artifact{{Revision: "aaa", Checksum: "bbb"}},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "ddd"},
},
want: true,
},
{
name: "observed include but updated spec",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo2"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
ObservedInclude: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
},
IncludedArtifacts: []*sourcev1.Artifact{{Revision: "aaa", Checksum: "bbb"}},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
},
want: true,
},
{
name: "different number of include and observed include",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo2"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
IncludedArtifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
{Revision: "ccc", Checksum: "ccc"},
},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
{Revision: "ccc", Checksum: "ddd"},
},
want: true,
},
{
name: "different number of include and artifactset",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo2"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
ObservedInclude: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo2"},
FromPath: "bar",
ToPath: "baz",
},
},
IncludedArtifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
{Revision: "ccc", Checksum: "ccc"},
},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
},
want: true,
},
{
name: "different number of include and included artifacts",
obj: sourcev1.GitRepository{
Spec: sourcev1.GitRepositorySpec{
Include: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo2"},
FromPath: "bar",
ToPath: "baz",
},
},
},
Status: sourcev1.GitRepositoryStatus{
ObservedInclude: []sourcev1.GitRepositoryInclude{
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo"},
FromPath: "bar",
ToPath: "baz",
},
{
GitRepositoryRef: meta.LocalObjectReference{Name: "foo2"},
FromPath: "bar",
ToPath: "baz",
},
},
IncludedArtifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
},
},
},
artifacts: []*sourcev1.Artifact{
{Revision: "aaa", Checksum: "bbb"},
{Revision: "ccc", Checksum: "ccc"},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
includes := artifactSet(tt.artifacts)
g.Expect(gitContentConfigChanged(&tt.obj, &includes)).To(Equal(tt.want))
})
}
}

View File

@ -1539,7 +1539,8 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
</h3>
<p>
(<em>Appears on:</em>
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">GitRepositorySpec</a>)
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositorySpec">GitRepositorySpec</a>,
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryStatus">GitRepositoryStatus</a>)
</p>
<p>GitRepositoryInclude specifies a local reference to a GitRepository which
Artifact (sub-)contents must be included, and where they should be placed.</p>
@ -1969,6 +1970,49 @@ observed in .status.observedGeneration version of the object. This can
be used to determine if the content of the included repository has
changed.
It has the format of <code>&lt;algo&gt;:&lt;checksum&gt;</code>, for example: <code>sha256:&lt;checksum&gt;</code>.</p>
<p>Deprecated: Replaced with explicit fields for observed artifact content
config in the status.</p>
</td>
</tr>
<tr>
<td>
<code>observedIgnore</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>ObservedIgnore is the observed exclusion patterns used for constructing
the source artifact.</p>
</td>
</tr>
<tr>
<td>
<code>observedRecurseSubmodules</code><br>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>ObservedRecurseSubmodules is the observed resource submodules
configuration used to produce the current Artifact.</p>
</td>
</tr>
<tr>
<td>
<code>observedInclude</code><br>
<em>
<a href="#source.toolkit.fluxcd.io/v1beta2.GitRepositoryInclude">
[]GitRepositoryInclude
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>ObservedInclude is the observed list of GitRepository resources used to
to produce the current Artifact.</p>
</td>
</tr>
<tr>

View File

@ -854,6 +854,79 @@ configurations of the GitRepository that indicate a change in source and
records it in `.status.contentConfigChecksum`. This field is used to determine
if the source artifact needs to be rebuilt.
**Deprecation Note:** `contentConfigChecksum` is no longer used and will be
removed in the next API version. The individual components used for generating
content configuration checksum now have explicit fields in the status. This
makes the observations used by the controller for making artifact rebuild
decisions more transparent and easier to debug.
### Observed Ignore
The source-controller reports an observed ignore in the GitRepository's
`.status.observedIgnore`. The observed ignore is the latest `.spec.ignore` value
which resulted in a [ready state](#ready-gitrepository), or stalled due to error
it can not recover from without human intervention.
The value is the same as the [ignore in spec](#ignore).
It indicates the ignore rules used in building the current artifact in storage.
It is also used by the controller to determine if an artifact needs to be
rebuilt.
Example:
```yaml
status:
...
observedIgnore: |
cue
pkg
...
```
### Observed Recurse Submodules
The source-controller reports an observed recurse submodule in the
GitRepository's `.status.observedRecurseSubmodules`. The observed recurse
submodules is the latest `.spec.recurseSubmodules` value which resulted in a
[ready state](#ready-gitrepository), or stalled due to error it can not recover
from without human intervention. The value is the same as the
[recurse submodules in spec](#recurse-submodules). It indicates the recurse
submodules configuration used in building the current artifact in storage. It is
also used by the controller to determine if an artifact needs to be rebuilt.
Example:
```yaml
status:
...
observedRecurseSubmodules: true
...
```
### Observed Include
The source-controller reports observed include in the GitRepository's
`.status.observedInclude`. The observed include is the latest
`.spec.recurseSubmodules` value which resulted in a
[ready state](#ready-gitrepository), or stalled due to error it can not recover
from without human intervention. The value is the same as the
[include in spec](#include). It indicates the include configuration used in
building the current artifact in storage. It is also used by the controller to
determine if an artifact needs to be rebuilt.
Example:
```yaml
status:
...
observedInclude:
- fromPath: deploy/webapp
repository:
name: repo1
toPath: foo
- fromPath: deploy/secure
repository:
name: repo2
toPath: bar
...
```
### Observed Generation
The source-controller reports an [observed generation][typical-status-properties]