6
6
"errors"
7
7
"fmt"
8
8
"reflect"
9
+ "sort"
9
10
"strings"
10
11
"sync"
11
12
"time"
@@ -22,6 +23,7 @@ import (
22
23
k8serrors "k8s.io/apimachinery/pkg/api/errors"
23
24
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24
25
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26
+ "k8s.io/apimachinery/pkg/labels"
25
27
"k8s.io/apimachinery/pkg/runtime/schema"
26
28
utilclock "k8s.io/apimachinery/pkg/util/clock"
27
29
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -66,6 +68,8 @@ const (
66
68
roleKind = "Role"
67
69
roleBindingKind = "RoleBinding"
68
70
generatedByKey = "olm.generated-by"
71
+ maxInstallPlanCount = 5
72
+ maxDeletesPerSweep = 5
69
73
)
70
74
71
75
// Operator represents a Kubernetes operator that executes InstallPlans by
@@ -770,6 +774,8 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
770
774
"id" : queueinformer .NewLoopID (),
771
775
})
772
776
777
+ o .gcInstallPlans (logger , namespace )
778
+
773
779
// get the set of sources that should be used for resolution and best-effort get their connections working
774
780
logger .Debug ("resolving sources" )
775
781
@@ -1173,6 +1179,77 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
1173
1179
return unpacked , out , nil
1174
1180
}
1175
1181
1182
+ // gcInstallPlans garbage collects installplans that are too old
1183
+ // installplans are ownerrefd to all subscription inputs, so they will not otherwise
1184
+ // be GCd unless all inputs have been deleted.
1185
+ func (o * Operator ) gcInstallPlans (log logrus.FieldLogger , namespace string ) {
1186
+ allIps , err := o .lister .OperatorsV1alpha1 ().InstallPlanLister ().InstallPlans (namespace ).List (labels .Everything ())
1187
+ if err != nil {
1188
+ log .Warn ("unable to list installplans for GC" )
1189
+ }
1190
+
1191
+ if len (allIps ) <= maxInstallPlanCount {
1192
+ return
1193
+ }
1194
+
1195
+ // we only consider maxDeletesPerSweep more than the allowed number of installplans for delete at one time
1196
+ ips := allIps
1197
+ if len (ips ) > maxInstallPlanCount + maxDeletesPerSweep {
1198
+ ips = allIps [:maxInstallPlanCount + maxDeletesPerSweep ]
1199
+ }
1200
+
1201
+ byGen := map [int ][]* v1alpha1.InstallPlan {}
1202
+ for _ , ip := range ips {
1203
+ gen , ok := byGen [ip .Spec .Generation ]
1204
+ if ! ok {
1205
+ gen = make ([]* v1alpha1.InstallPlan , 0 )
1206
+ }
1207
+ byGen [ip .Spec .Generation ] = append (gen , ip )
1208
+ }
1209
+
1210
+ gens := make ([]int , 0 )
1211
+ for i := range byGen {
1212
+ gens = append (gens , i )
1213
+ }
1214
+
1215
+ sort .Ints (gens )
1216
+
1217
+ toDelete := make ([]* v1alpha1.InstallPlan , 0 )
1218
+
1219
+ for _ , i := range gens {
1220
+ g := byGen [i ]
1221
+
1222
+ if len (ips )- len (toDelete ) <= maxInstallPlanCount {
1223
+ break
1224
+ }
1225
+
1226
+ // if removing all installplans at this generation doesn't dip below the max, safe to delete all of them
1227
+ if len (ips )- len (toDelete )- len (g ) >= maxInstallPlanCount {
1228
+ toDelete = append (toDelete , g ... )
1229
+ continue
1230
+ }
1231
+
1232
+ // CreationTimestamp sorting shouldn't ever be hit unless there is a bug that causes installplans to be
1233
+ // generated without bumping the generation. It is here as a safeguard only.
1234
+
1235
+ // sort by creation time
1236
+ sort .Slice (g , func (i , j int ) bool {
1237
+ if ! g [i ].CreationTimestamp .Equal (& g [j ].CreationTimestamp ) {
1238
+ return g [i ].CreationTimestamp .Before (& g [j ].CreationTimestamp )
1239
+ }
1240
+ // final fallback to lexicographic sort, in case many installplans are created with the same timestamp
1241
+ return g [i ].GetName () < g [j ].GetName ()
1242
+ })
1243
+ toDelete = append (toDelete , g [:len (ips )- len (toDelete )- maxInstallPlanCount ]... )
1244
+ }
1245
+
1246
+ for _ , i := range toDelete {
1247
+ if err := o .client .OperatorsV1alpha1 ().InstallPlans (namespace ).Delete (i .GetName (), & metav1.DeleteOptions {}); err != nil {
1248
+ log .WithField ("deleting" , i .GetName ()).WithError (err ).Warn ("error GCing old installplan - may have already been deleted" )
1249
+ }
1250
+ }
1251
+ }
1252
+
1176
1253
func (o * Operator ) syncInstallPlans (obj interface {}) (syncError error ) {
1177
1254
plan , ok := obj .(* v1alpha1.InstallPlan )
1178
1255
if ! ok {
0 commit comments