@@ -2,24 +2,39 @@ package action
22
33import  (
44	"context" 
5+ 	"errors" 
56	"fmt" 
7+ 	"time" 
68
9+ 	"github.com/operator-framework/kubectl-operator/pkg/action" 
10+ 	ocv1 "github.com/operator-framework/operator-controller/api/v1" 
11+ 	olmv1 "github.com/operator-framework/operator-controller/api/v1" 
712	"k8s.io/apimachinery/pkg/api/meta" 
813	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 
9- 	"k8s.io/apimachinery/pkg/types" 
1014	"k8s.io/apimachinery/pkg/util/wait" 
15+ 	"sigs.k8s.io/controller-runtime/pkg/client" 
1116
12- 	olmv1 "github.com/operator-framework/operator-controller/api/v1" 
13- 
14- 	"github.com/operator-framework/kubectl-operator/pkg/action" 
17+ 	corev1 "k8s.io/api/core/v1" 
18+ 	rbacv1 "k8s.io/api/rbac/v1" 
1519)
1620
1721type  ExtensionInstall  struct  {
18- 	config  * action.Configuration 
19- 
20- 	Package  string 
21- 
22- 	Logf  func (string , ... interface {})
22+ 	config                          * action.Configuration 
23+ 	ExtensionName                   string 
24+ 	Namespace                       NamespaceConfig 
25+ 	PackageName                     string 
26+ 	Channels                        []string 
27+ 	Version                         string 
28+ 	ServiceAccount                  string 
29+ 	CatalogSelector                 metav1.LabelSelector 
30+ 	UnsafeCreateClusterRoleBinding  bool 
31+ 	CleanupTimeout                  time.Duration 
32+ 	Logf                            func (string , ... interface {})
33+ }
34+ type  NamespaceConfig  struct  {
35+ 	Name         string 
36+ 	Labels       map [string ]string 
37+ 	Annotations  map [string ]string 
2338}
2439
2540func  NewExtensionInstall (cfg  * action.Configuration ) * ExtensionInstall  {
@@ -29,47 +44,121 @@ func NewExtensionInstall(cfg *action.Configuration) *ExtensionInstall {
2944	}
3045}
3146
32- func  (i  * ExtensionInstall ) Run (ctx  context.Context ) (* olmv1.ClusterExtension , error ) {
33- 	// TODO(developer): Lookup package information when the OLMv1 equivalent of the 
34- 	//     packagemanifests API is available. That way, we can check to see if the 
35- 	//     package is actually available to the cluster before creating the Extension 
36- 	//     object. 
37- 
38- 	opKey  :=  types.NamespacedName {Name : i .Package }
39- 	op  :=  & olmv1.ClusterExtension {
40- 		ObjectMeta : metav1.ObjectMeta {Name : opKey .Name },
47+ func  (i  * ExtensionInstall ) buildClusterExtension () olmv1.ClusterExtension  {
48+ 	extension  :=  olmv1.ClusterExtension {
49+ 		ObjectMeta : metav1.ObjectMeta {
50+ 			Name : i .ExtensionName ,
51+ 		},
4152		Spec : olmv1.ClusterExtensionSpec {
4253			Source : olmv1.SourceConfig {
43- 				SourceType : "Catalog" ,
54+ 				SourceType : olmv1 . SourceTypeCatalog ,
4455				Catalog : & olmv1.CatalogFilter {
45- 					PackageName : i .Package ,
56+ 					PackageName : i .PackageName ,
57+ 					Version :     i .Version ,
4658				},
4759			},
60+ 			Namespace : i .Namespace .Name ,
61+ 			ServiceAccount : olmv1.ServiceAccountReference {
62+ 				Name : i .ServiceAccount ,
63+ 			},
64+ 		},
65+ 	}
66+ 
67+ 	return  extension 
68+ }
69+ 
70+ func  (i  * ExtensionInstall ) Run (ctx  context.Context ) (* ocv1.ClusterExtension , error ) {
71+ 	extension  :=  i .buildClusterExtension ()
72+ 	// Add catalog selector to extension 
73+ 	if  len (i .CatalogSelector .MatchLabels ) >  0  {
74+ 		extension .Spec .Source .Catalog .Selector  =  & i .CatalogSelector 
75+ 	}
76+ 	// Add Channels to extension 
77+ 	if  len (i .Channels ) >  0  {
78+ 		extension .Spec .Source .Catalog .Channels  =  i .Channels 
79+ 	}
80+ 	//Add CatalogSelector to extension 
81+ 	if  len (i .CatalogSelector .MatchLabels ) >  0  {
82+ 		extension .Spec .Source .Catalog .Selector  =  & i .CatalogSelector 
83+ 	}
84+ 	// Create namespace 
85+ 	namespace  :=  & corev1.Namespace {
86+ 		ObjectMeta : metav1.ObjectMeta {
87+ 			Name :        i .Namespace .Name ,
88+ 			Labels :      i .Namespace .Labels ,
89+ 			Annotations : i .Namespace .Annotations ,
4890		},
4991	}
50- 	if  err  :=  i .config .Client .Create (ctx , op ); err  !=  nil  {
92+ 	if  err  :=  i .config .Client .Create (ctx , namespace ); err  !=  nil  {
5193		return  nil , err 
5294	}
95+ 	if  err  :=  i .config .Client .Create (ctx , & extension ); err  !=  nil  {
96+ 		return  nil , err 
97+ 	}
98+ 	clusterExtension , err  :=  i .waitForClusterExtensionInstalled (ctx )
99+ 	if  err  !=  nil  {
100+ 		cleanupCtx , cancelCleanup  :=  context .WithTimeout (context .Background (), i .CleanupTimeout )
101+ 		defer  cancelCleanup ()
102+ 		cleanupErr  :=  i .cleanup (cleanupCtx )
103+ 		return  nil , errors .Join (err , cleanupErr )
104+ 	}
105+ 	return  clusterExtension , nil 
106+ }
53107
54- 	// TODO(developer): Improve the logic in this poll wait once the Extension reconciler 
55- 	//     and conditions types and reasons are improved. For now, this will stop waiting as 
56- 	//     soon as a Ready condition is found, but we should probably wait until the Extension 
57- 	//     stops progressing. 
58- 	// All Types will exist, so Ready may have a false Status. So, wait until 
59- 	// Type=Ready,Status=True happens 
60- 
61- 	if  err  :=  wait .PollUntilContextCancel (ctx , pollInterval , true , func (conditionCtx  context.Context ) (bool , error ) {
62- 		if  err  :=  i .config .Client .Get (conditionCtx , opKey , op ); err  !=  nil  {
108+ func  (i  * ExtensionInstall ) waitForClusterExtensionInstalled (ctx  context.Context ) (* ocv1.ClusterExtension , error ) {
109+ 	clusterExtension  :=  & ocv1.ClusterExtension {
110+ 		ObjectMeta : metav1.ObjectMeta {
111+ 			Name : i .ExtensionName ,
112+ 		},
113+ 	}
114+ 	errMsg  :=  "" 
115+ 	key  :=  client .ObjectKeyFromObject (clusterExtension )
116+ 	if  err  :=  wait .PollUntilContextCancel (ctx , time .Millisecond * 250 , true , func (conditionCtx  context.Context ) (bool , error ) {
117+ 		if  err  :=  i .config .Client .Get (conditionCtx , key , clusterExtension ); err  !=  nil  {
63118			return  false , err 
64119		}
65- 		installedCondition  :=  meta .FindStatusCondition (op .Status .Conditions , olmv1 .TypeInstalled )
66- 		if  installedCondition  !=  nil  &&  installedCondition .Status  ==  metav1 .ConditionTrue  {
67- 			return  true , nil 
120+ 		progressingCondition  :=  meta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeProgressing )
121+ 		if  progressingCondition  !=  nil  &&  progressingCondition .Reason  !=  ocv1 .ReasonSucceeded  {
122+ 			errMsg  =  progressingCondition .Message 
123+ 			return  false , nil 
124+ 		}
125+ 		if  ! meta .IsStatusConditionPresentAndEqual (clusterExtension .Status .Conditions , ocv1 .TypeInstalled , metav1 .ConditionTrue ) {
126+ 			return  false , nil 
68127		}
69- 		return  false , nil 
128+ 		return  true , nil 
70129	}); err  !=  nil  {
71- 		return  nil , fmt .Errorf ("waiting for extension to become ready: %v" , err )
130+ 		if  errMsg  ==  ""  {
131+ 			errMsg  =  err .Error ()
132+ 		}
133+ 		return  nil , fmt .Errorf ("cluster extension %q did not finish installing: %s" , clusterExtension .Name , errMsg )
72134	}
135+ 	return  clusterExtension , nil 
136+ }
73137
74- 	return  op , nil 
138+ func  (i  * ExtensionInstall ) cleanup (ctx  context.Context ) error  {
139+ 	clusterExtension  :=  & ocv1.ClusterExtension {
140+ 		ObjectMeta : metav1.ObjectMeta {
141+ 			Name : i .ExtensionName ,
142+ 		},
143+ 	}
144+ 	clusterRoleBinding  :=  & rbacv1.ClusterRoleBinding {
145+ 		ObjectMeta : metav1.ObjectMeta {
146+ 			Name : fmt .Sprintf ("kubectl-operator-%s-cluster-admin" , i .ServiceAccount ),
147+ 		},
148+ 	}
149+ 	serviceAccount  :=  & corev1.ServiceAccount {
150+ 		ObjectMeta : metav1.ObjectMeta {
151+ 			Namespace : i .Namespace .Name ,
152+ 			Name :      i .ServiceAccount ,
153+ 		},
154+ 	}
155+ 	namespace  :=  & corev1.Namespace {
156+ 		ObjectMeta : metav1.ObjectMeta {
157+ 			Name : i .Namespace .Name ,
158+ 		},
159+ 	}
160+ 	if  err  :=  waitForDeletion (ctx , i .config .Client , clusterExtension ); err  !=  nil  {
161+ 		return  fmt .Errorf ("delete clusterextension %q: %v" , i .ExtensionName , err )
162+ 	}
163+ 	return  waitForDeletion (ctx , i .config .Client , clusterRoleBinding , serviceAccount , namespace )
75164}
0 commit comments