Browse code

Expose a way for clients to discover OpenShift API resources

Add a method to clientcmd.Factory that enables retrieving a list of
valid resource names.

Clayton Coleman authored on 2016/06/12 08:16:33
Showing 3 changed files
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	kapi "k8s.io/kubernetes/pkg/api"
11 11
 	"k8s.io/kubernetes/pkg/api/errors"
12 12
 	"k8s.io/kubernetes/pkg/client/restclient"
13
+	"k8s.io/kubernetes/pkg/client/typed/discovery"
13 14
 
14 15
 	"github.com/openshift/origin/pkg/api/latest"
15 16
 	"github.com/openshift/origin/pkg/version"
... ...
@@ -283,6 +284,12 @@ func New(c *restclient.Config) (*Client, error) {
283 283
 	return &Client{client}, nil
284 284
 }
285 285
 
286
+// DiscoveryClient returns a discovery client.
287
+func (c *Client) Discovery() discovery.DiscoveryInterface {
288
+	d := NewDiscoveryClient(c.RESTClient)
289
+	return d
290
+}
291
+
286 292
 // SetOpenShiftDefaults sets the default settings on the passed
287 293
 // client configuration
288 294
 func SetOpenShiftDefaults(config *restclient.Config) error {
... ...
@@ -41,9 +41,8 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (r
41 41
 		// ignore 403 or 404 error to be compatible with an v1.0 server.
42 42
 		if groupVersion == "v1" && (errors.IsNotFound(err) || errors.IsForbidden(err)) {
43 43
 			return parentList, nil
44
-		} else {
45
-			return nil, err
46 44
 		}
45
+		return nil, err
47 46
 	}
48 47
 
49 48
 	parentList.APIResources = append(parentList.APIResources, originResources.APIResources...)
... ...
@@ -25,6 +25,7 @@ import (
25 25
 	"k8s.io/kubernetes/pkg/apis/batch"
26 26
 	"k8s.io/kubernetes/pkg/apis/extensions"
27 27
 	"k8s.io/kubernetes/pkg/client/restclient"
28
+	"k8s.io/kubernetes/pkg/client/typed/discovery"
28 29
 	kclient "k8s.io/kubernetes/pkg/client/unversioned"
29 30
 	kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
30 31
 	kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
... ...
@@ -774,3 +775,57 @@ func (c *clientCache) ClientForVersion(version *unversioned.GroupVersion) (*clie
774 774
 	c.clients[config.GroupVersion.String()] = client
775 775
 	return client, nil
776 776
 }
777
+
778
+// FindAllCanonicalResources returns all resource names that map directly to their kind (Kind -> Resource -> Kind)
779
+// and are not subresources. This is the closest mapping possible from the client side to resources that can be
780
+// listed and updated. Note that this may return some virtual resources (like imagestreamtags) that can be otherwise
781
+// represented.
782
+// TODO: add a field to APIResources for "virtual" (or that points to the canonical resource).
783
+// TODO: fallback to the scheme when discovery is not possible.
784
+func FindAllCanonicalResources(d discovery.DiscoveryInterface, m meta.RESTMapper) ([]unversioned.GroupResource, error) {
785
+	set := make(map[unversioned.GroupResource]struct{})
786
+	all, err := d.ServerResources()
787
+	if err != nil {
788
+		return nil, err
789
+	}
790
+	for apiVersion, v := range all {
791
+		gv, err := unversioned.ParseGroupVersion(apiVersion)
792
+		if err != nil {
793
+			continue
794
+		}
795
+		for _, r := range v.APIResources {
796
+			// ignore subresources
797
+			if strings.Contains(r.Name, "/") {
798
+				continue
799
+			}
800
+			// because discovery info doesn't tell us whether the object is virtual or not, perform a lookup
801
+			// by the kind for resource (which should be the canonical resource) and then verify that the reverse
802
+			// lookup (KindsFor) does not error.
803
+			if mapping, err := m.RESTMapping(unversioned.GroupKind{Group: gv.Group, Kind: r.Kind}, gv.Version); err == nil {
804
+				if _, err := m.KindsFor(mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource)); err == nil {
805
+					set[unversioned.GroupResource{Group: mapping.GroupVersionKind.Group, Resource: mapping.Resource}] = struct{}{}
806
+				}
807
+			}
808
+		}
809
+	}
810
+	var groupResources []unversioned.GroupResource
811
+	for k := range set {
812
+		groupResources = append(groupResources, k)
813
+	}
814
+	sort.Sort(groupResourcesByName(groupResources))
815
+	return groupResources, nil
816
+}
817
+
818
+type groupResourcesByName []unversioned.GroupResource
819
+
820
+func (g groupResourcesByName) Len() int { return len(g) }
821
+func (g groupResourcesByName) Less(i, j int) bool {
822
+	if g[i].Resource < g[j].Resource {
823
+		return true
824
+	}
825
+	if g[i].Resource > g[j].Resource {
826
+		return false
827
+	}
828
+	return g[i].Group < g[j].Group
829
+}
830
+func (g groupResourcesByName) Swap(i, j int) { g[i], g[j] = g[j], g[i] }