66 "errors"
77 "fmt"
88 "reflect"
9+ "sort"
910 "strings"
1011 "sync"
1112 "time"
@@ -22,6 +23,7 @@ import (
2223 extinf "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
2324 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2425 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26+ "k8s.io/apimachinery/pkg/labels"
2527 "k8s.io/apimachinery/pkg/runtime/schema"
2628 utilclock "k8s.io/apimachinery/pkg/util/clock"
2729 utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -67,6 +69,8 @@ const (
6769 roleKind = "Role"
6870 roleBindingKind = "RoleBinding"
6971 generatedByKey = "olm.generated-by"
72+ maxInstallPlanCount = 5
73+ maxDeletesPerSweep = 5
7074)
7175
7276// Operator represents a Kubernetes operator that executes InstallPlans by
@@ -777,6 +781,8 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
777781 "id" : queueinformer .NewLoopID (),
778782 })
779783
784+ o .gcInstallPlans (logger , namespace )
785+
780786 // get the set of sources that should be used for resolution and best-effort get their connections working
781787 logger .Debug ("resolving sources" )
782788
@@ -1180,6 +1186,77 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
11801186 return unpacked , out , nil
11811187}
11821188
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+
11831260func (o * Operator ) syncInstallPlans (obj interface {}) (syncError error ) {
11841261 plan , ok := obj .(* v1alpha1.InstallPlan )
11851262 if ! ok {
0 commit comments