5
5
"encoding/json"
6
6
"errors"
7
7
"fmt"
8
+ "k8s.io/apimachinery/pkg/labels"
8
9
"reflect"
10
+ "sort"
9
11
"strings"
10
12
"sync"
11
13
"time"
@@ -67,6 +69,7 @@ const (
67
69
roleKind = "Role"
68
70
roleBindingKind = "RoleBinding"
69
71
generatedByKey = "olm.generated-by"
72
+ maxInstallPlanCount = 5
70
73
)
71
74
72
75
// Operator represents a Kubernetes operator that executes InstallPlans by
@@ -1180,6 +1183,64 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
1180
1183
return unpacked , out , nil
1181
1184
}
1182
1185
1186
+ func (o * Operator ) gcInstallPlans (log logrus.FieldLogger , namespace string ) {
1187
+ ips , err := o .lister .OperatorsV1alpha1 ().InstallPlanLister ().InstallPlans (namespace ).List (labels .Everything ())
1188
+ if err != nil {
1189
+ log .Warn ("unable to list installplans for GC" )
1190
+ }
1191
+
1192
+ if len (ips ) <= maxInstallPlanCount {
1193
+ return
1194
+ }
1195
+
1196
+ byGen := map [int ][]* v1alpha1.InstallPlan {}
1197
+ for _ , ip := range ips {
1198
+ gen , ok := byGen [ip .Spec .Generation ]
1199
+ if ! ok {
1200
+ gen = make ([]* v1alpha1.InstallPlan , 0 )
1201
+ }
1202
+ byGen [ip .Spec .Generation ] = append (gen , ip )
1203
+ }
1204
+
1205
+ gens := make ([]int , 0 )
1206
+ for i := range byGen {
1207
+ gens = append (gens , i )
1208
+ }
1209
+
1210
+ sort .Ints (gens )
1211
+
1212
+ toDelete := make ([]* v1alpha1.InstallPlan , 0 )
1213
+
1214
+ for _ , i := range gens {
1215
+ g := byGen [i ]
1216
+
1217
+ if len (ips ) - len (toDelete ) <= maxInstallPlanCount {
1218
+ break
1219
+ }
1220
+
1221
+ // if removing all installplans at this generation doesn't dip below the max, safe to delete all of them
1222
+ if len (ips ) - len (toDelete ) - len (g ) >= maxInstallPlanCount {
1223
+ toDelete = append (toDelete , g ... )
1224
+ continue
1225
+ }
1226
+
1227
+ // CreationTimestamp sorting shouldn't ever be hit unless there is a bug that causes installplans to be
1228
+ // generated without bumping the generation. It is here as a safeguard only.
1229
+
1230
+ // sort by creation time
1231
+ sort .Slice (g , func (i , j int ) bool {
1232
+ return g [i ].CreationTimestamp .Before (& g [j ].CreationTimestamp )
1233
+ })
1234
+ toDelete = append (toDelete , g [:len (ips ) - len (toDelete ) - maxInstallPlanCount ]... )
1235
+ }
1236
+
1237
+ for _ , i := range toDelete {
1238
+ if err := o .client .OperatorsV1alpha1 ().InstallPlans (namespace ).Delete (context .TODO (), i .GetName (), metav1.DeleteOptions {}); err != nil {
1239
+ log .WithField ("deleting" , i .GetName ()).WithError (err ).Warn ("error GCing old installplan" )
1240
+ }
1241
+ }
1242
+ }
1243
+
1183
1244
func (o * Operator ) syncInstallPlans (obj interface {}) (syncError error ) {
1184
1245
plan , ok := obj .(* v1alpha1.InstallPlan )
1185
1246
if ! ok {
@@ -1196,6 +1257,8 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
1196
1257
1197
1258
logger .Info ("syncing" )
1198
1259
1260
+ o .gcInstallPlans (logger , plan .GetNamespace ())
1261
+
1199
1262
if len (plan .Status .Plan ) == 0 && len (plan .Status .BundleLookups ) == 0 {
1200
1263
logger .Info ("skip processing installplan without status - subscription sync responsible for initial status" )
1201
1264
return
0 commit comments