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