@@ -54,10 +54,12 @@ type reconciler struct {
5454
5555func (r * reconciler ) reconcile (ctx context.Context , cl client.Client , cache cache.Cache , req * kubebindv1alpha2.APIServiceExportRequest ) error {
5656 if err := r .ensureBoundSchemas (ctx , cl , cache , req ); err != nil {
57+ conditions .SetSummary (req )
5758 return err
5859 }
5960
6061 if err := r .ensureExports (ctx , cl , cache , req ); err != nil {
62+ conditions .SetSummary (req )
6163 return err
6264 }
6365
@@ -75,7 +77,7 @@ func (r *reconciler) ensureBoundSchemas(ctx context.Context, cl client.Client, c
7577 for _ , res := range req .Spec .Resources {
7678 parts := strings .SplitN (r .schemaSource , "." , 3 )
7779 if len (parts ) != 3 { // We check this in validation, but just in case.
78- return fmt .Errorf ("invalid schema source: %q" , r .schemaSource )
80+ return fmt .Errorf ("malformed schema source: %q" , r .schemaSource )
7981 }
8082
8183 gvk := schema.GroupVersionKind {
@@ -106,20 +108,57 @@ func (r *reconciler) ensureBoundSchemas(ctx context.Context, cl client.Client, c
106108 }
107109
108110 for _ , item := range list .Items {
111+ var schemaFailed bool
109112 obj := item .UnstructuredContent ()
110- spec , ok := obj [ "spec" ].( map [ string ] interface {} )
111- if ! ok {
112- klog .FromContext (ctx ).Error (nil , "Skipping invalid schema: missing spec " , "ns" , item .GetNamespace (), "name" , item .GetName ())
113- continue
113+ group , ok , err := unstructured . NestedString ( obj , "spec" , "group" )
114+ if ! ok || err != nil || group == "" {
115+ klog .FromContext (ctx ).Error (err , "Skipping invalid schema: missing group " , "ns" , item .GetNamespace (), "name" , item .GetName ())
116+ schemaFailed = true
114117 }
115- g , _ := spec ["group" ].(string )
116- names , _ := spec ["names" ].(map [string ]interface {})
117- p , _ := names ["plural" ].(string )
118- if g == "" || p == "" {
119- klog .FromContext (ctx ).Error (nil , "Skipping invalid schema: missing group or names.plural" , "ns" , item .GetNamespace (), "name" , item .GetName ())
120- continue
118+ plural , ok , err := unstructured .NestedString (obj , "spec" , "names" , "plural" )
119+ if ! ok || err != nil || plural == "" {
120+ klog .FromContext (ctx ).Error (err , "Skipping invalid schema: missing names.plural" , "ns" , item .GetNamespace (), "name" , item .GetName ())
121+ schemaFailed = true
121122 }
122- if g == res .Group && p == res .Resource {
123+
124+ scope , ok , err := unstructured .NestedString (obj , "spec" , "scope" )
125+ if ! ok || err != nil || scope == "" {
126+ klog .FromContext (ctx ).Error (err , "Skipping invalid schema: missing scope" , "ns" , item .GetNamespace (), "name" , item .GetName ())
127+ schemaFailed = true
128+ }
129+
130+ if schemaFailed {
131+ conditions .MarkFalse (
132+ req ,
133+ kubebindv1alpha2 .APIServiceExportRequestConditionExportsReady ,
134+ "APIServiceExportRequestInvalid" ,
135+ conditionsapi .ConditionSeverityError ,
136+ "APIServiceExportRequest %s is invalid: resource %s/%s has invalid schema" ,
137+ req .Name , group , plural ,
138+ )
139+ req .Status .Phase = kubebindv1alpha2 .APIServiceExportRequestPhaseFailed
140+ return fmt .Errorf ("resource %s/%s is invalid" , group , plural )
141+ }
142+
143+ if group == res .Group && plural == res .Resource {
144+ // Important: This checks if the resource are correctly scoped. If consumer is namespaced, we can't allow this.
145+ // We terminate early to prevent triggering other controllers.
146+ if r .informerScope .String () != scope && r .informerScope != kubebindv1alpha2 .ClusterScope {
147+ conditions .MarkFalse (
148+ req ,
149+ kubebindv1alpha2 .APIServiceExportRequestConditionExportsReady ,
150+ "APIServiceExportRequestInvalid" ,
151+ conditionsapi .ConditionSeverityError ,
152+ "APIServiceExportRequest %s is invalid: resource %s/%s has scope %q which is incompatible with backend informer scope %q" ,
153+ req .Name , group , plural , scope , r .informerScope ,
154+ )
155+ req .Status .Phase = kubebindv1alpha2 .APIServiceExportRequestPhaseFailed
156+ req .Status .TerminalMessage = conditions .GetMessage (req , kubebindv1alpha2 .APIServiceExportRequestConditionExportsReady )
157+ // We can't proceed with this request.
158+ return fmt .Errorf ("resource %s/%s has scope %q which is incompatible with backend informer scope %q" , group , plural , scope , r .informerScope )
159+ }
160+
161+ // https://github.com/kube-bind/kube-bind/issues/297 to fix.
123162 boundSchema , err := helpers .UnstructuredToBoundSchema (item )
124163 if err != nil {
125164 return err
@@ -163,9 +202,9 @@ func (r *reconciler) ensureExports(ctx context.Context, cl client.Client, cache
163202 conditions .MarkFalse (
164203 req ,
165204 kubebindv1alpha2 .APIServiceExportRequestConditionExportsReady ,
166- "APIResourceSchemaNotFound " ,
205+ "BoundSchemaNotFound " ,
167206 conditionsapi .ConditionSeverityError ,
168- "APIResourceSchema %s in the service provider cluster not found" ,
207+ "BoundSchema %s in the service provider cluster not found" ,
169208 name ,
170209 )
171210 return err
@@ -189,7 +228,11 @@ func (r *reconciler) ensureExports(ctx context.Context, cl client.Client, cache
189228 return nil
190229 }
191230
192- hash := helpers .BoundSchemasSpecHash (schemas )
231+ // https://github.com/kube-bind/kube-bind/issues/297 To fix.
232+ hash , err := helpers .BoundSchemasSpecHash (schemas )
233+ if err != nil {
234+ return err
235+ }
193236 export := & kubebindv1alpha2.APIServiceExport {
194237 ObjectMeta : metav1.ObjectMeta {
195238 Name : req .Name ,
0 commit comments