@@ -179,6 +179,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
179179 assertArtifact * meta.Artifact
180180 assertConditions []metav1.Condition
181181 assertPaths []string
182+ assertEvicted bool
182183 }{
183184 {
184185 name : "garbage collects" ,
@@ -244,6 +245,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
244245 assertPaths : []string {
245246 "!/reconcile-storage/invalid.txt" ,
246247 },
248+ assertEvicted : true ,
247249 assertConditions : []metav1.Condition {
248250 * conditions .TrueCondition (meta .ReconcilingCondition , meta .ProgressingReason , "building artifact: disappeared from storage" ),
249251 * conditions .UnknownCondition (meta .ReadyCondition , meta .ProgressingReason , "building artifact: disappeared from storage" ),
@@ -275,6 +277,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
275277 assertPaths : []string {
276278 "!/reconcile-storage/empty-digest.txt" ,
277279 },
280+ assertEvicted : true ,
278281 assertConditions : []metav1.Condition {
279282 * conditions .TrueCondition (meta .ReconcilingCondition , meta .ProgressingReason , "building artifact: disappeared from storage" ),
280283 * conditions .UnknownCondition (meta .ReadyCondition , meta .ProgressingReason , "building artifact: disappeared from storage" ),
@@ -306,6 +309,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
306309 assertPaths : []string {
307310 "!/reconcile-storage/digest-mismatch.txt" ,
308311 },
312+ assertEvicted : true ,
309313 assertConditions : []metav1.Condition {
310314 * conditions .TrueCondition (meta .ReconcilingCondition , meta .ProgressingReason , "building artifact: disappeared from storage" ),
311315 * conditions .UnknownCondition (meta .ReadyCondition , meta .ProgressingReason , "building artifact: disappeared from storage" ),
@@ -356,6 +360,8 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
356360 Build (),
357361 EventRecorder : record .NewFakeRecorder (32 ),
358362 Storage : testStorage ,
363+ Cache : cache .New (10 , time .Minute ),
364+ TTL : time .Minute ,
359365 patchOptions : getPatchOptions (helmRepositoryReadyCondition .Owned , "sc" ),
360366 }
361367
@@ -369,6 +375,12 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
369375 g .Expect (tt .beforeFunc (obj , testStorage )).To (Succeed ())
370376 }
371377
378+ var cachedPath string
379+ if a := obj .GetArtifact (); a != nil {
380+ cachedPath = a .Path
381+ g .Expect (r .Cache .Set (cachedPath , & repo.IndexFile {}, time .Minute )).To (Succeed ())
382+ }
383+
372384 g .Expect (r .Client .Create (context .TODO (), obj )).ToNot (HaveOccurred ())
373385 defer func () {
374386 g .Expect (r .Client .Delete (context .TODO (), obj )).ToNot (HaveOccurred ())
@@ -388,6 +400,11 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) {
388400 }
389401 g .Expect (obj .Status .Conditions ).To (conditions .MatchConditions (tt .assertConditions ))
390402
403+ if cachedPath != "" {
404+ _ , ok := r .Cache .Get (cachedPath )
405+ g .Expect (ok ).To (Equal (! tt .assertEvicted ))
406+ }
407+
391408 for _ , p := range tt .assertPaths {
392409 absoluteP := filepath .Join (testStorage .BasePath , p )
393410 if ! strings .HasPrefix (p , "!" ) {
@@ -1073,6 +1090,10 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) {
10731090}
10741091
10751092func TestHelmRepositoryReconciler_reconcileArtifact (t * testing.T ) {
1093+ // The cache is at capacity with the previous revision and an index of
1094+ // another repository, the new revision can only be added after eviction.
1095+ evictCache := cache .New (2 , time .Minute )
1096+
10761097 tests := []struct {
10771098 name string
10781099 cache * cache.Cache
@@ -1118,6 +1139,32 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
11181139 * conditions .TrueCondition (sourcev1 .ArtifactInStorageCondition , meta .SucceededReason , "stored artifact: revision 'existing'" ),
11191140 },
11201141 },
1142+ {
1143+ name : "Archiving new revision evicts the previous revision from cache" ,
1144+ cache : evictCache ,
1145+ beforeFunc : func (t * WithT , obj * sourcev1.HelmRepository , artifact meta.Artifact , index * repository.ChartRepository ) {
1146+ index .Index = & repo.IndexFile {
1147+ APIVersion : "v1" ,
1148+ Generated : time .Now (),
1149+ }
1150+ obj .Spec .Interval = metav1.Duration {Duration : interval }
1151+ prev := testStorage .NewArtifactFor (obj .Kind , obj , "previous" , "index-previous.yaml" )
1152+ obj .Status .Artifact = & prev
1153+ t .Expect (evictCache .Set (prev .Path , & repo.IndexFile {}, time .Minute )).To (Succeed ())
1154+ t .Expect (evictCache .Set ("helmrepository/default/other/index-other.yaml" , & repo.IndexFile {}, time .Minute )).To (Succeed ())
1155+ },
1156+ want : sreconcile .ResultSuccess ,
1157+ afterFunc : func (t * WithT , obj * sourcev1.HelmRepository , c * cache.Cache ) {
1158+ t .Expect (c .ItemCount ()).To (Equal (2 ))
1159+ _ , ok := c .Get (obj .GetArtifact ().Path )
1160+ t .Expect (ok ).To (BeTrue ())
1161+ _ , ok = c .Get ("helmrepository/default/other/index-other.yaml" )
1162+ t .Expect (ok ).To (BeTrue ())
1163+ },
1164+ assertConditions : []metav1.Condition {
1165+ * conditions .TrueCondition (sourcev1 .ArtifactInStorageCondition , meta .SucceededReason , "stored artifact: revision 'existing'" ),
1166+ },
1167+ },
11211168 {
11221169 name : "Up-to-date artifact should not update status" ,
11231170 beforeFunc : func (t * WithT , obj * sourcev1.HelmRepository , artifact meta.Artifact , index * repository.ChartRepository ) {
@@ -1224,6 +1271,59 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
12241271 }
12251272}
12261273
1274+ func TestHelmRepositoryReconciler_garbageCollectEvictsCache (t * testing.T ) {
1275+ tests := []struct {
1276+ name string
1277+ beforeFunc func (obj * sourcev1.HelmRepository )
1278+ }{
1279+ {
1280+ name : "deleted object" ,
1281+ beforeFunc : func (obj * sourcev1.HelmRepository ) {
1282+ obj .DeletionTimestamp = & metav1.Time {Time : time .Now ()}
1283+ },
1284+ },
1285+ {
1286+ name : "type changed to OCI" ,
1287+ beforeFunc : func (obj * sourcev1.HelmRepository ) {
1288+ obj .Spec .Type = sourcev1 .HelmRepositoryTypeOCI
1289+ },
1290+ },
1291+ }
1292+
1293+ for _ , tt := range tests {
1294+ t .Run (tt .name , func (t * testing.T ) {
1295+ g := NewWithT (t )
1296+
1297+ c := cache .New (10 , time .Minute )
1298+ r := & HelmRepositoryReconciler {
1299+ EventRecorder : record .NewFakeRecorder (32 ),
1300+ Storage : testStorage ,
1301+ Cache : c ,
1302+ TTL : time .Minute ,
1303+ }
1304+
1305+ obj := & sourcev1.HelmRepository {
1306+ TypeMeta : metav1.TypeMeta {
1307+ Kind : sourcev1 .HelmRepositoryKind ,
1308+ },
1309+ ObjectMeta : metav1.ObjectMeta {
1310+ Name : "cache-evict" ,
1311+ Namespace : "default" ,
1312+ },
1313+ }
1314+ artifact := testStorage .NewArtifactFor (obj .Kind , obj , "existing" , "index-existing.yaml" )
1315+ obj .Status .Artifact = & artifact
1316+ g .Expect (c .Set (artifact .Path , & repo.IndexFile {}, time .Minute )).To (Succeed ())
1317+
1318+ tt .beforeFunc (obj )
1319+
1320+ g .Expect (r .garbageCollect (context .TODO (), obj )).To (Succeed ())
1321+ g .Expect (obj .GetArtifact ()).To (BeNil ())
1322+ g .Expect (c .ItemCount ()).To (Equal (0 ))
1323+ })
1324+ }
1325+ }
1326+
12271327func TestHelmRepositoryReconciler_reconcileSubRecs (t * testing.T ) {
12281328 // Helper to build simple helmRepositoryReconcileFunc with result and error.
12291329 buildReconcileFuncs := func (r sreconcile.Result , e error ) helmRepositoryReconcileFunc {
0 commit comments