Skip to content

Commit 40cf6a9

Browse files
committed
add resource group support
1 parent 4f048cc commit 40cf6a9

File tree

6 files changed

+175
-28
lines changed

6 files changed

+175
-28
lines changed

pkg/controller/composable/composable_controller.go

+50-27
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const (
5151
namespace = "namespace"
5252
metadata = "metadata"
5353
kind = "kind"
54-
version = "version"
54+
group = "group"
5555
spec = "spec"
5656
status = "status"
5757
state = "state"
@@ -240,15 +240,25 @@ func (r *ReconcileComposable) GetServerPreferredResources() ([]*metav1.APIResour
240240
}
241241

242242

243-
func NameMatchesResource(name string, apiResource metav1.APIResource, group string) bool {
243+
func NameMatchesResource(name string, objGroup string, resource metav1.APIResource, resGroup string) bool {
244244
lowerCaseName := strings.ToLower(name)
245-
if lowerCaseName == apiResource.Name ||
246-
lowerCaseName == apiResource.SingularName ||
247-
lowerCaseName == strings.ToLower(apiResource.Kind) ||
248-
lowerCaseName == fmt.Sprintf("%s.%s", apiResource.Name, group) {
245+
if len(objGroup) > 0 {
246+
if objGroup == resGroup &&
247+
( lowerCaseName == resource.Name ||
248+
lowerCaseName == resource.SingularName ||
249+
lowerCaseName == strings.ToLower(resource.Kind) ) {
250+
return true
251+
} else {
252+
return false
253+
}
254+
}
255+
if lowerCaseName == resource.Name ||
256+
lowerCaseName == resource.SingularName ||
257+
lowerCaseName == strings.ToLower(resource.Kind) ||
258+
lowerCaseName == fmt.Sprintf("%s.%s", resource.Name, resGroup) {
249259
return true
250260
}
251-
for _, shortName := range apiResource.ShortNames {
261+
for _, shortName := range resource.ShortNames {
252262
if lowerCaseName == strings.ToLower(shortName) {
253263
return true
254264
}
@@ -272,17 +282,21 @@ func GroupQualifiedName(apiResource metav1.APIResource) string {
272282
return fmt.Sprintf("%s.%s", apiResource.Name, apiResource.Group)
273283
}
274284

275-
func (r *ReconcileComposable) LookupAPIResource(kind, apiVersion string, cache *composableCache) (*metav1.APIResource, error) {
276-
285+
func (r *ReconcileComposable) LookupAPIResource(objKind, objGroup string, cache *composableCache) (*metav1.APIResource, error) {
277286
var resources []*metav1.APIResourceList
278-
if len(apiVersion) > 0 {
279-
// TODO comment it out
280-
res, err := r.discoveryClient.ServerResourcesForGroupVersion(apiVersion)
281-
if err != nil {
282-
return nil, err
283-
}
284-
resources = []*metav1.APIResourceList{res}
285-
} else {
287+
//if len(apiVersion) > 0 {
288+
//if cache.resourceMap == nil {
289+
// res, err := r.discoveryClient.ServerResourcesForGroupVersion(apiVersion)
290+
// if err != nil {
291+
// fmt.Printf(" apiVersion error %s\n", err)
292+
// return nil, err
293+
// }
294+
// cache.resourceMap = make(map[string]*metav1.APIResourceList)
295+
// cache.resourceMap[apiVersion] = res
296+
//}
297+
//resources = []*metav1.APIResourceList{cache.resourceMap[apiVersion]}
298+
//fmt.Printf(" apiVersion2 %s %v \n", apiVersion, resources)
299+
//} else {
286300
if cache.resources == nil {
287301
klog.V(6).Infoln("Resources is nil")
288302
resourceList, err := r.GetServerPreferredResources()
@@ -292,10 +306,12 @@ func (r *ReconcileComposable) LookupAPIResource(kind, apiVersion string, cache *
292306
cache.resources = resourceList
293307
}
294308
resources = cache.resources
295-
}
309+
// }
296310

297311
var targetResource *metav1.APIResource
298312
var matchedResources []string
313+
coreGroupObject := false
314+
Loop:
299315
for _, resourceList := range resources {
300316
// The list holds the GroupVersion for its list of APIResources
301317
gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
@@ -305,7 +321,16 @@ func (r *ReconcileComposable) LookupAPIResource(kind, apiVersion string, cache *
305321

306322
for _, resource := range resourceList.APIResources {
307323
group := gv.Group
308-
if NameMatchesResource(kind, resource, group) {
324+
if NameMatchesResource(objKind, objGroup, resource, group) {
325+
if len(group) == 0 && len(objGroup) == 0 {
326+
// K8s core group object
327+
coreGroupObject = true
328+
targetResource = resource.DeepCopy()
329+
targetResource.Group = group
330+
targetResource.Version = gv.Version
331+
coreGroupObject = true
332+
break Loop
333+
}
309334
if targetResource == nil {
310335
targetResource = resource.DeepCopy()
311336
targetResource.Group = group
@@ -315,7 +340,7 @@ func (r *ReconcileComposable) LookupAPIResource(kind, apiVersion string, cache *
315340
}
316341
}
317342
}
318-
if len(matchedResources) > 1 {
343+
if !coreGroupObject && len(matchedResources) > 1 {
319344
return nil, fmt.Errorf("Multiple resources are matched by %q: %s. A group-qualified plural name must be provided.", kind, strings.Join(matchedResources, ", "))
320345
}
321346

@@ -327,14 +352,13 @@ func (r *ReconcileComposable) LookupAPIResource(kind, apiVersion string, cache *
327352
}
328353

329354
func (r *ReconcileComposable) resolveValue(value interface{}, composableNamespace string, cache *composableCache) (interface{}, error) {
330-
klog.V(5).Infof(" resolve value type %T %v\n", value, value)
331355
if val, ok := value.(map[string]interface{}); ok {
332-
if kind, ok := val[kind].(string); ok {
333-
vers := ""
334-
if vers, ok = val[version].(string); !ok {
335-
vers = ""
356+
if objKind, ok := val[kind].(string); ok {
357+
objGroup := ""
358+
if objGroup, ok = val[group].(string); !ok {
359+
objGroup = ""
336360
}
337-
res, err := r.LookupAPIResource(kind, vers, cache)
361+
res, err := r.LookupAPIResource(objKind, objGroup, cache)
338362
if err != nil {
339363
// If an input object API resource is not installed, we return error even if a default value is set.
340364
return nil, err
@@ -372,7 +396,6 @@ func (r *ReconcileComposable) resolveValue(value interface{}, composableNamespac
372396
unstrObj = unstructured.Unstructured{}
373397
//unstrObj.SetAPIVersion(res.Version)
374398
unstrObj.SetGroupVersionKind(groupVersionKind)
375-
klog.V(5).Infof("Get Object %s %s r type = %T\n", objNamespacedname, groupVersionKind, r.Client )
376399
err = r.Get(context.TODO(), objNamespacedname, &unstrObj)
377400
if err != nil {
378401
klog.V(5).Infof("Get object returned %s", err.Error())

pkg/controller/composable/composable_controller_test.go

+39-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ var _ = Describe("test Composable operations", func() {
227227
unstrObj.SetGroupVersionKind(gvkIn)
228228
Ω(test.GetUnstructuredObject(testContext, objNamespacednameIn, &unstrObj)()).Should(HaveOccurred())
229229

230-
By("check taht output object doesn't exist. If it does => remove it ") // the object should not exist, or we delete it
230+
By("check that output object doesn't exist. If it does => remove it ") // the object should not exist, or we delete it
231231
unstrObj.SetGroupVersionKind(gvkOut)
232232
err2 := test.GetUnstructuredObject(testContext, objNamespacednameOut, &unstrObj)()
233233
if err2 == nil {
@@ -301,6 +301,44 @@ var _ = Describe("test Composable operations", func() {
301301

302302
})
303303

304+
var _ = Describe("Validate group separation", func() {
305+
Context("There are 3 groups that have Kind = `Service`. They are: Service/v1; Service.ibmcloud.ibm.com/v1alpha1 and Service.test.ibmcloud.ibm.com/v1", func() {
306+
It("Composable should correctly discover required objects", func() {
307+
dataDir := "testdata/"
308+
309+
By("deploy K8s Service")
310+
kObj := test.LoadObject(dataDir+"serviceK8s.yaml", &v1.Service{})
311+
test.CreateObject(testContext, kObj, true, 0)
312+
Eventually(test.GetObject(testContext, kObj)).ShouldNot(BeNil())
313+
314+
By("deploy test Service")
315+
tObj := test.LoadObject(dataDir+"serviceTest.yaml", &unstructured.Unstructured{})
316+
test.CreateObject(testContext, tObj, true, 0)
317+
Eventually(test.GetObject(testContext, tObj)).ShouldNot(BeNil())
318+
319+
By("deploy Composable object")
320+
comp := test.LoadCompasable(dataDir + "compServices.yaml")
321+
test.PostInNs(testContext, &comp, true, 0)
322+
Eventually(test.GetObject(testContext, &comp)).ShouldNot(BeNil())
323+
324+
By("get the output object and validate its fields")
325+
unstrObj := unstructured.Unstructured{}
326+
gvk := schema.GroupVersionKind{Kind: "OutputValue", Version: "v1", Group: "test.ibmcloud.ibm.com"}
327+
objNamespacedname := types.NamespacedName{Namespace: testContext.Namespace(), Name: "services-out"}
328+
329+
unstrObj.SetGroupVersionKind(gvk)
330+
Eventually(test.GetUnstructuredObject(testContext, objNamespacedname, &unstrObj)).Should(Succeed())
331+
testSpec, ok := unstrObj.Object[spec].(map[string]interface{})
332+
Ω(ok).Should(BeTrue())
333+
334+
Ω(testSpec["k8sValue"]).Should(Equal("None"))
335+
Ω(testSpec["testValue"]).Should(Equal("Test"))
336+
337+
})
338+
339+
})
340+
})
341+
304342
var _ = Describe("IBM cloud-operators compatibility", func() {
305343
dataDir := "testdata/cloud-operators-data/"
306344
groupVersionKind := schema.GroupVersionKind{Kind: "Service", Version: "v1alpha1", Group: "ibmcloud.ibm.com"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
apiVersion: ibmcloud.ibm.com/v1alpha1
2+
kind: Composable
3+
metadata:
4+
name: services
5+
spec:
6+
template:
7+
apiVersion: "test.ibmcloud.ibm.com/v1"
8+
kind: OutputValue
9+
metadata:
10+
name: services-out
11+
spec:
12+
instancename: services-out
13+
k8sValue:
14+
getValueFrom:
15+
kind: Service
16+
name: test-service
17+
namespace: default
18+
path: '{.spec.sessionAffinity}'
19+
testValue:
20+
getValueFrom:
21+
kind: Service
22+
name: test-service
23+
group: test.ibmcloud.ibm.com
24+
namespace: default
25+
path: '{.spec.sessionAffinity}'
26+
27+
28+
29+
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
apiVersion: apiextensions.k8s.io/v1beta1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
# name must match the spec fields below, and be in the form: <plural>.<group>
5+
name: services.test.ibmcloud.ibm.com
6+
spec:
7+
# group name to use for REST API: /apis/<group>/<version>
8+
group: test.ibmcloud.ibm.com
9+
# list of versions supported by this CustomResourceDefinition
10+
version: v1
11+
# either Namespaced or Cluster
12+
scope: Namespaced
13+
names:
14+
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
15+
plural: services
16+
# singular name to be used as an alias on the CLI and for display
17+
singular: service
18+
# kind is normally the CamelCased singular type. Your resource manifests use this.
19+
kind: Service
20+
21+
# preserveUnknownFields: false
22+
validation:
23+
openAPIV3Schema:
24+
type: object
25+
properties:
26+
spec:
27+
type: object
28+
properties:
29+
type:
30+
type: object
31+
sessionAffinity:
32+
type: string
33+
34+
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: test-service
5+
namespace: default
6+
spec:
7+
sessionAffinity: None
8+
type: ClusterIP
9+
selector:
10+
app: MyApp
11+
ports:
12+
- protocol: TCP
13+
port: 80
14+
targetPort: 9376
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: test.ibmcloud.ibm.com/v1
2+
kind: Service
3+
metadata:
4+
name: test-service
5+
namespace: default
6+
spec:
7+
sessionAffinity: "Test"

0 commit comments

Comments
 (0)