mirror of https://github.com/k3s-io/k3s
Merge pull request #36909 from sttts/sttts-discovery-with-verbs
Automatic merge from submit-queue (batch tested with PRs 37370, 37003, 36909) Add verbs to APIResourceInfo for discovery Verbs will be used by generic controllers (gc, namespace) to avoid unnecessary API calls, reducing load on the apiserver. E.g. not all objects can be deleted. Example: ```json { "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "batch/v1", "resources": [ { "name": "jobs", "namespaced": true, "kind": "Job", "verbs": [ "create", "delete", "deletecollection", "get", "list", "update", "watch" ] }, { "name": "jobs/status", "namespaced": true, "kind": "Job", "verbs": [ "create", "get" ] } ] } ```pull/6/head
commit
45a436ac24
|
@ -32308,7 +32308,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
|
@ -32322,6 +32323,13 @@
|
|||
"namespaced": {
|
||||
"description": "namespaced indicates if a resource is namespaced or not.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"verbs": {
|
||||
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2885,7 +2885,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -2899,6 +2900,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,7 +300,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -314,6 +315,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -513,7 +513,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -527,6 +528,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1445,7 +1445,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -1459,6 +1460,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2865,7 +2865,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -2879,6 +2880,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -82,6 +83,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1133,7 +1133,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -1147,6 +1148,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10737,7 +10737,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -10751,6 +10752,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1455,7 +1455,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -1469,6 +1470,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3363,7 +3363,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -3377,6 +3378,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -968,7 +968,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -982,6 +983,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19982,7 +19982,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -19996,6 +19997,12 @@
|
|||
"kind": {
|
||||
"type": "string",
|
||||
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
|
||||
},
|
||||
"verbs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ go_library(
|
|||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/componentconfig:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/clientset_generated/release_1_5:go_default_library",
|
||||
"//pkg/client/clientset_generated/release_1_5/typed/core/v1:go_default_library",
|
||||
"//pkg/client/leaderelection:go_default_library",
|
||||
|
|
|
@ -36,6 +36,8 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||
v1core "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/core/v1"
|
||||
"k8s.io/kubernetes/pkg/client/leaderelection"
|
||||
|
@ -393,29 +395,26 @@ func StartControllers(s *options.CMServer, rootClientBuilder, clientBuilder cont
|
|||
namespaceKubeClient := clientBuilder.ClientOrDie("namespace-controller")
|
||||
namespaceClientPool := dynamic.NewClientPool(rootClientBuilder.ConfigOrDie("namespace-controller"), restMapper, dynamic.LegacyAPIPathResolverFunc)
|
||||
// TODO: consider using a list-watch + cache here rather than polling
|
||||
var gvrFn func() ([]schema.GroupVersionResource, error)
|
||||
rsrcs, err := namespaceKubeClient.Discovery().ServerResources()
|
||||
resources, err := namespaceKubeClient.Discovery().ServerResources()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get group version resources: %v", err)
|
||||
return fmt.Errorf("failed to get preferred server resources: %v", err)
|
||||
}
|
||||
for _, rsrcList := range rsrcs {
|
||||
for ix := range rsrcList.APIResources {
|
||||
rsrc := &rsrcList.APIResources[ix]
|
||||
if rsrc.Kind == "ThirdPartyResource" {
|
||||
gvrFn = namespaceKubeClient.Discovery().ServerPreferredNamespacedResources
|
||||
}
|
||||
}
|
||||
gvrs, err := discovery.GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse preferred server resources: %v", err)
|
||||
}
|
||||
if gvrFn == nil {
|
||||
gvr, err := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources()
|
||||
discoverResourcesFn := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources
|
||||
if _, found := gvrs[extensions.SchemeGroupVersion.WithResource("thirdpartyresource")]; found {
|
||||
// make discovery static
|
||||
snapshot, err := discoverResourcesFn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get resources: %v", err)
|
||||
return fmt.Errorf("failed to get server resources: %v", err)
|
||||
}
|
||||
gvrFn = func() ([]schema.GroupVersionResource, error) {
|
||||
return gvr, nil
|
||||
discoverResourcesFn = func() ([]*metav1.APIResourceList, error) {
|
||||
return snapshot, nil
|
||||
}
|
||||
}
|
||||
namespaceController := namespacecontroller.NewNamespaceController(namespaceKubeClient, namespaceClientPool, gvrFn, s.NamespaceSyncPeriod.Duration, v1.FinalizerKubernetes)
|
||||
namespaceController := namespacecontroller.NewNamespaceController(namespaceKubeClient, namespaceClientPool, discoverResourcesFn, s.NamespaceSyncPeriod.Duration, v1.FinalizerKubernetes)
|
||||
go namespaceController.Run(int(s.ConcurrentNamespaceSyncs), stop)
|
||||
time.Sleep(wait.Jitter(s.ControllerStartInterval.Duration, ControllerStartJitter))
|
||||
|
||||
|
@ -548,17 +547,22 @@ func StartControllers(s *options.CMServer, rootClientBuilder, clientBuilder cont
|
|||
|
||||
if s.EnableGarbageCollector {
|
||||
gcClientset := clientBuilder.ClientOrDie("generic-garbage-collector")
|
||||
groupVersionResources, err := gcClientset.Discovery().ServerPreferredResources()
|
||||
preferredResources, err := gcClientset.Discovery().ServerPreferredResources()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get supported resources from server: %v", err)
|
||||
}
|
||||
deletableResources := discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"delete"}}, preferredResources)
|
||||
deletableGroupVersionResources, err := discovery.GroupVersionResources(deletableResources)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to parse resources from server: %v", err)
|
||||
}
|
||||
|
||||
config := rootClientBuilder.ConfigOrDie("generic-garbage-collector")
|
||||
config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()}
|
||||
metaOnlyClientPool := dynamic.NewClientPool(config, restMapper, dynamic.LegacyAPIPathResolverFunc)
|
||||
config.ContentConfig = dynamic.ContentConfig()
|
||||
clientPool := dynamic.NewClientPool(config, restMapper, dynamic.LegacyAPIPathResolverFunc)
|
||||
garbageCollector, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, restMapper, groupVersionResources)
|
||||
garbageCollector, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, restMapper, deletableGroupVersionResources)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to start the generic garbage collector: %v", err)
|
||||
} else {
|
||||
|
|
|
@ -2130,6 +2130,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -4444,7 +4451,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:06:41 UTC
|
||||
Last updated 2016-12-05 09:52:09 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -885,6 +885,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -900,7 +907,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:06:49 UTC
|
||||
Last updated 2016-12-05 09:52:16 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -754,6 +754,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -1161,7 +1168,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:06:55 UTC
|
||||
Last updated 2016-12-05 09:52:22 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1297,6 +1297,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -1368,7 +1375,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:01 UTC
|
||||
Last updated 2016-12-05 09:52:30 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -2041,6 +2041,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -4376,7 +4383,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:08 UTC
|
||||
Last updated 2016-12-05 09:52:37 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -472,6 +472,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -487,7 +494,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:16 UTC
|
||||
Last updated 2016-12-05 09:52:43 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1274,6 +1274,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -1345,7 +1352,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:23 UTC
|
||||
Last updated 2016-12-05 09:52:51 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -5731,6 +5731,13 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -6818,7 +6825,7 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:30 UTC
|
||||
Last updated 2016-12-05 09:52:58 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1290,6 +1290,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -1402,7 +1409,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:37 UTC
|
||||
Last updated 2016-12-05 09:53:07 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1487,6 +1487,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -1730,7 +1737,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:43 UTC
|
||||
Last updated 2016-12-05 09:53:16 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1135,6 +1135,13 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -1206,7 +1213,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:07:50 UTC
|
||||
Last updated 2016-12-05 09:53:24 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -6719,6 +6719,13 @@ Examples:<br>
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">verbs</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -8613,7 +8620,7 @@ Examples:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-12-03 22:06:34 UTC
|
||||
Last updated 2016-12-05 09:52:00 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -8555,7 +8555,8 @@
|
|||
"required": [
|
||||
"name",
|
||||
"namespaced",
|
||||
"kind"
|
||||
"kind",
|
||||
"verbs"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
|
@ -8569,6 +8570,13 @@
|
|||
"namespaced": {
|
||||
"description": "namespaced indicates if a resource is namespaced or not.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"verbs": {
|
||||
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -38,6 +38,7 @@ go_library(
|
|||
"//vendor:github.com/gogo/protobuf/proto",
|
||||
"//vendor:github.com/gogo/protobuf/sortkeys",
|
||||
"//vendor:github.com/google/gofuzz",
|
||||
"//vendor:github.com/ugorji/go/codec",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -48,6 +49,7 @@ go_test(
|
|||
"group_version_test.go",
|
||||
"helpers_test.go",
|
||||
"time_test.go",
|
||||
"types_test.go",
|
||||
],
|
||||
library = "go_default_library",
|
||||
tags = ["automanaged"],
|
||||
|
|
|
@ -50,6 +50,7 @@ limitations under the License.
|
|||
Time
|
||||
Timestamp
|
||||
TypeMeta
|
||||
Verbs
|
||||
*/
|
||||
package v1
|
||||
|
||||
|
@ -180,6 +181,10 @@ func (m *TypeMeta) Reset() { *m = TypeMeta{} }
|
|||
func (*TypeMeta) ProtoMessage() {}
|
||||
func (*TypeMeta) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{24} }
|
||||
|
||||
func (m *Verbs) Reset() { *m = Verbs{} }
|
||||
func (*Verbs) ProtoMessage() {}
|
||||
func (*Verbs) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{25} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*APIGroup)(nil), "k8s.io.kubernetes.pkg.apis.meta.v1.APIGroup")
|
||||
proto.RegisterType((*APIGroupList)(nil), "k8s.io.kubernetes.pkg.apis.meta.v1.APIGroupList")
|
||||
|
@ -206,6 +211,7 @@ func init() {
|
|||
proto.RegisterType((*Time)(nil), "k8s.io.kubernetes.pkg.apis.meta.v1.Time")
|
||||
proto.RegisterType((*Timestamp)(nil), "k8s.io.kubernetes.pkg.apis.meta.v1.Timestamp")
|
||||
proto.RegisterType((*TypeMeta)(nil), "k8s.io.kubernetes.pkg.apis.meta.v1.TypeMeta")
|
||||
proto.RegisterType((*Verbs)(nil), "k8s.io.kubernetes.pkg.apis.meta.v1.Verbs")
|
||||
}
|
||||
func (m *APIGroup) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
|
@ -322,6 +328,16 @@ func (m *APIResource) MarshalTo(data []byte) (int, error) {
|
|||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Kind)))
|
||||
i += copy(data[i:], m.Kind)
|
||||
if m.Verbs != nil {
|
||||
data[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(m.Verbs.Size()))
|
||||
n2, err := m.Verbs.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n2
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
@ -836,11 +852,11 @@ func (m *Status) MarshalTo(data []byte) (int, error) {
|
|||
data[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(m.ListMeta.Size()))
|
||||
n2, err := m.ListMeta.MarshalTo(data[i:])
|
||||
n3, err := m.ListMeta.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n2
|
||||
i += n3
|
||||
data[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.Status)))
|
||||
|
@ -857,11 +873,11 @@ func (m *Status) MarshalTo(data []byte) (int, error) {
|
|||
data[i] = 0x2a
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(m.Details.Size()))
|
||||
n3, err := m.Details.MarshalTo(data[i:])
|
||||
n4, err := m.Details.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n3
|
||||
i += n4
|
||||
}
|
||||
data[i] = 0x30
|
||||
i++
|
||||
|
@ -994,6 +1010,39 @@ func (m *TypeMeta) MarshalTo(data []byte) (int, error) {
|
|||
return i, nil
|
||||
}
|
||||
|
||||
func (m Verbs) Marshal() (data []byte, err error) {
|
||||
size := m.Size()
|
||||
data = make([]byte, size)
|
||||
n, err := m.MarshalTo(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func (m Verbs) MarshalTo(data []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m) > 0 {
|
||||
for _, s := range m {
|
||||
data[i] = 0xa
|
||||
i++
|
||||
l = len(s)
|
||||
for l >= 1<<7 {
|
||||
data[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||
l >>= 7
|
||||
i++
|
||||
}
|
||||
data[i] = uint8(l)
|
||||
i++
|
||||
i += copy(data[i:], s)
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeFixed64Generated(data []byte, offset int, v uint64) int {
|
||||
data[offset] = uint8(v)
|
||||
data[offset+1] = uint8(v >> 8)
|
||||
|
@ -1063,6 +1112,10 @@ func (m *APIResource) Size() (n int) {
|
|||
n += 2
|
||||
l = len(m.Kind)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
if m.Verbs != nil {
|
||||
l = m.Verbs.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -1321,6 +1374,18 @@ func (m *TypeMeta) Size() (n int) {
|
|||
return n
|
||||
}
|
||||
|
||||
func (m Verbs) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if len(m) > 0 {
|
||||
for _, s := range m {
|
||||
l = len(s)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovGenerated(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
|
@ -1365,6 +1430,7 @@ func (this *APIResource) String() string {
|
|||
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
|
||||
`Namespaced:` + fmt.Sprintf("%v", this.Namespaced) + `,`,
|
||||
`Kind:` + fmt.Sprintf("%v", this.Kind) + `,`,
|
||||
`Verbs:` + strings.Replace(fmt.Sprintf("%v", this.Verbs), "Verbs", "Verbs", 1) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
|
@ -1917,6 +1983,39 @@ func (m *APIResource) Unmarshal(data []byte) error {
|
|||
}
|
||||
m.Kind = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Verbs", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Verbs == nil {
|
||||
m.Verbs = Verbs{}
|
||||
}
|
||||
if err := m.Verbs.Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(data[iNdEx:])
|
||||
|
@ -4465,6 +4564,85 @@ func (m *TypeMeta) Unmarshal(data []byte) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Verbs) Unmarshal(data []byte) error {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Verbs: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Verbs: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
*m = append(*m, string(data[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(data[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipGenerated(data []byte) (n int, err error) {
|
||||
l := len(data)
|
||||
iNdEx := 0
|
||||
|
@ -4571,94 +4749,97 @@ var (
|
|||
)
|
||||
|
||||
var fileDescriptorGenerated = []byte{
|
||||
// 1416 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x57, 0x4f, 0x6f, 0xdb, 0xc6,
|
||||
0x12, 0x17, 0x25, 0x4b, 0x91, 0x46, 0xd6, 0xb3, 0xc3, 0xe7, 0xe0, 0x31, 0x06, 0x9e, 0xa4, 0xc7,
|
||||
0x3c, 0x14, 0x0e, 0x90, 0x48, 0xb0, 0x51, 0x14, 0x41, 0xda, 0x14, 0x30, 0x6d, 0x27, 0x30, 0x12,
|
||||
0x27, 0xc6, 0x3a, 0x48, 0x8b, 0x34, 0x87, 0xd2, 0xe4, 0x5a, 0x66, 0x25, 0x91, 0xec, 0xee, 0x4a,
|
||||
0x88, 0xd0, 0x43, 0x83, 0x02, 0x05, 0x7a, 0x28, 0x8a, 0x1c, 0x8b, 0x1e, 0x8a, 0x18, 0xe8, 0x07,
|
||||
0xe8, 0x57, 0xe8, 0x2d, 0xb7, 0xe6, 0xd8, 0x43, 0x61, 0x34, 0x2e, 0x7a, 0xea, 0x37, 0xf0, 0xa9,
|
||||
0xd8, 0xe5, 0x2e, 0x45, 0xca, 0x51, 0x4c, 0x23, 0x3d, 0xf4, 0x24, 0xee, 0xfc, 0xf9, 0xcd, 0x6f,
|
||||
0x67, 0x66, 0x67, 0x57, 0xb0, 0xd2, 0xbd, 0x46, 0x5b, 0x5e, 0xd0, 0xee, 0x0e, 0x76, 0x31, 0xf1,
|
||||
0x31, 0xc3, 0xb4, 0x1d, 0x76, 0x3b, 0x6d, 0x3b, 0xf4, 0x68, 0xbb, 0x8f, 0x99, 0xdd, 0x1e, 0x2e,
|
||||
0xb7, 0x3b, 0xd8, 0xc7, 0xc4, 0x66, 0xd8, 0x6d, 0x85, 0x24, 0x60, 0x81, 0x6e, 0x46, 0x3e, 0xad,
|
||||
0xb1, 0x4f, 0x2b, 0xec, 0x76, 0x5a, 0xdc, 0xa7, 0xc5, 0x7d, 0x5a, 0xc3, 0xe5, 0xc5, 0xab, 0x1d,
|
||||
0x8f, 0xed, 0x0f, 0x76, 0x5b, 0x4e, 0xd0, 0x6f, 0x77, 0x82, 0x4e, 0xd0, 0x16, 0xae, 0xbb, 0x83,
|
||||
0x3d, 0xb1, 0x12, 0x0b, 0xf1, 0x15, 0x41, 0x2e, 0x5e, 0x7d, 0x35, 0x0d, 0x32, 0xf0, 0x99, 0xd7,
|
||||
0xc7, 0x93, 0x0c, 0x16, 0xdf, 0x7e, 0xbd, 0x39, 0x75, 0xf6, 0x71, 0xdf, 0x3e, 0xe1, 0xb5, 0xfc,
|
||||
0x6a, 0xaf, 0x01, 0xf3, 0x7a, 0x6d, 0xcf, 0x67, 0x94, 0x91, 0x49, 0x17, 0xf3, 0xa7, 0x02, 0x94,
|
||||
0x57, 0xb7, 0x37, 0x6f, 0x91, 0x60, 0x10, 0xea, 0x4d, 0x98, 0xf1, 0xed, 0x3e, 0x36, 0xb4, 0xa6,
|
||||
0xb6, 0x54, 0xb1, 0x66, 0x9f, 0x1f, 0x36, 0x72, 0x47, 0x87, 0x8d, 0x99, 0xbb, 0x76, 0x1f, 0x23,
|
||||
0xa1, 0xd1, 0x3f, 0x81, 0xf2, 0x10, 0x13, 0xea, 0x05, 0x3e, 0x35, 0xf2, 0xcd, 0xc2, 0x52, 0x75,
|
||||
0xe5, 0xbd, 0xd6, 0xe9, 0xc9, 0x6a, 0x09, 0xf8, 0x07, 0x91, 0xe3, 0xcd, 0x80, 0xac, 0x7b, 0xd4,
|
||||
0x09, 0x86, 0x98, 0x8c, 0xac, 0x79, 0x19, 0xa3, 0x2c, 0x95, 0x14, 0xc5, 0xf8, 0xfa, 0x17, 0x1a,
|
||||
0xcc, 0x87, 0x04, 0xef, 0x61, 0x42, 0xb0, 0x2b, 0xf5, 0x46, 0xa1, 0xa9, 0xbd, 0x71, 0x50, 0x43,
|
||||
0x06, 0x9d, 0xdf, 0x9e, 0x40, 0x47, 0x27, 0xe2, 0xe9, 0x07, 0x1a, 0x2c, 0x52, 0x4c, 0x86, 0x98,
|
||||
0xac, 0xba, 0x2e, 0xc1, 0x94, 0x5a, 0xa3, 0xb5, 0x9e, 0x87, 0x7d, 0xb6, 0xb6, 0xb9, 0x8e, 0xa8,
|
||||
0x31, 0x23, 0x72, 0x70, 0x23, 0x0b, 0x9d, 0x9d, 0x69, 0x28, 0x96, 0x29, 0xf9, 0x2c, 0x4e, 0x35,
|
||||
0xa1, 0xe8, 0x35, 0x24, 0x4c, 0x17, 0x66, 0x55, 0x09, 0xef, 0x78, 0x94, 0xe9, 0xf7, 0xa1, 0xd4,
|
||||
0xe1, 0x0b, 0x6a, 0x68, 0x82, 0xde, 0x95, 0x2c, 0xf4, 0x14, 0x82, 0xf5, 0x2f, 0xc9, 0xa6, 0x24,
|
||||
0x96, 0x14, 0x49, 0x2c, 0xf3, 0x4b, 0x0d, 0xaa, 0xab, 0xdb, 0x9b, 0x08, 0xd3, 0x60, 0x40, 0x1c,
|
||||
0x9c, 0xa1, 0x59, 0x56, 0x00, 0xf8, 0x2f, 0x0d, 0x6d, 0x07, 0xbb, 0x46, 0xbe, 0xa9, 0x2d, 0x95,
|
||||
0x2d, 0x5d, 0xda, 0xc1, 0xdd, 0x58, 0x83, 0x12, 0x56, 0x1c, 0xb5, 0xeb, 0xf9, 0xae, 0xa8, 0x73,
|
||||
0x02, 0xf5, 0xb6, 0xe7, 0xbb, 0x48, 0x68, 0xcc, 0x1f, 0x35, 0x98, 0x4b, 0xf0, 0x10, 0x3b, 0xbe,
|
||||
0x06, 0xb3, 0x9d, 0x44, 0xb5, 0x25, 0xa7, 0x05, 0xe9, 0x3d, 0x9b, 0xec, 0x04, 0x94, 0xb2, 0xd4,
|
||||
0x1d, 0xa8, 0x10, 0x89, 0xa4, 0x3a, 0xba, 0x9d, 0x31, 0x5d, 0x8a, 0xc1, 0x38, 0x4e, 0x42, 0x48,
|
||||
0xd1, 0x18, 0xd7, 0xfc, 0x23, 0x4a, 0x9d, 0xea, 0x71, 0x7d, 0x29, 0x71, 0x8a, 0x78, 0x89, 0x2a,
|
||||
0xd6, 0xec, 0x94, 0x33, 0x70, 0x4a, 0xfb, 0xe5, 0xff, 0x01, 0xed, 0x77, 0xbd, 0xfc, 0xed, 0xb3,
|
||||
0x46, 0xee, 0xc9, 0xaf, 0xcd, 0x9c, 0xb9, 0x09, 0xe5, 0xf5, 0x01, 0xb1, 0x19, 0x4f, 0xec, 0x0d,
|
||||
0x28, 0xbb, 0xf2, 0x5b, 0x94, 0xa3, 0x60, 0xfd, 0x4f, 0x9d, 0x75, 0x65, 0x73, 0x7c, 0xd8, 0xa8,
|
||||
0xf1, 0x71, 0xd6, 0x52, 0x02, 0x14, 0xbb, 0x98, 0x8f, 0xa0, 0xb6, 0xf1, 0x38, 0x0c, 0x08, 0xbb,
|
||||
0x17, 0x32, 0x91, 0x89, 0xb7, 0xa0, 0x84, 0x85, 0x40, 0xa0, 0x95, 0xc7, 0x6d, 0x1a, 0x99, 0x21,
|
||||
0xa9, 0xd5, 0x2f, 0x41, 0x11, 0x3f, 0xb6, 0x1d, 0x26, 0xfb, 0xad, 0x26, 0xcd, 0x8a, 0x1b, 0x5c,
|
||||
0x88, 0x22, 0x9d, 0x79, 0x0f, 0xe0, 0x16, 0x8e, 0xa1, 0x57, 0x61, 0x4e, 0xd5, 0x2a, 0xdd, 0x40,
|
||||
0xff, 0x91, 0xce, 0x73, 0x28, 0xad, 0x46, 0x93, 0xf6, 0xe6, 0x23, 0xa8, 0x88, 0x26, 0xe3, 0x7d,
|
||||
0xca, 0x29, 0x88, 0x1e, 0x93, 0x28, 0x31, 0x05, 0x61, 0x81, 0x22, 0x5d, 0xdc, 0xe8, 0xf9, 0x69,
|
||||
0x8d, 0x9e, 0xc8, 0x6b, 0x0f, 0x6a, 0x91, 0xaf, 0x3a, 0x7b, 0x99, 0x22, 0x5c, 0x81, 0xb2, 0xa2,
|
||||
0x29, 0xa3, 0xc4, 0xd3, 0x56, 0x01, 0xa1, 0xd8, 0x22, 0x11, 0x6d, 0x1f, 0x52, 0x07, 0x26, 0x5b,
|
||||
0xb0, 0xcb, 0x70, 0x4e, 0x36, 0xad, 0x8c, 0x35, 0x27, 0xcd, 0xce, 0xa9, 0x9c, 0x29, 0x7d, 0x22,
|
||||
0xd2, 0xe7, 0x60, 0x4c, 0x1b, 0xd2, 0x6f, 0x70, 0xa4, 0xb3, 0x53, 0x31, 0xbf, 0xd1, 0x60, 0x3e,
|
||||
0x89, 0x94, 0xbd, 0x7c, 0xd9, 0x83, 0x9c, 0x3e, 0xd2, 0x12, 0x19, 0xf9, 0x5e, 0x83, 0x85, 0xd4,
|
||||
0xd6, 0xce, 0x54, 0xf1, 0x33, 0x90, 0x4a, 0x36, 0x47, 0xe1, 0x0c, 0xcd, 0xf1, 0x73, 0x1e, 0x6a,
|
||||
0x77, 0xec, 0x5d, 0xdc, 0xdb, 0xc1, 0x3d, 0xec, 0xb0, 0x80, 0xe8, 0x23, 0xa8, 0xf6, 0x6d, 0xe6,
|
||||
0xec, 0x0b, 0xa9, 0xba, 0x72, 0xac, 0x2c, 0x23, 0x29, 0x85, 0xd3, 0xda, 0x1a, 0x83, 0x6c, 0xf8,
|
||||
0x8c, 0x8c, 0xac, 0x7f, 0x4b, 0x42, 0xd5, 0x84, 0x06, 0x25, 0x63, 0x89, 0x17, 0x82, 0x58, 0x6f,
|
||||
0x3c, 0x0e, 0xf9, 0x5c, 0x3a, 0xeb, 0xb3, 0x24, 0x45, 0x00, 0xe1, 0x4f, 0x07, 0x1e, 0xc1, 0x7d,
|
||||
0xec, 0xb3, 0xf1, 0x0b, 0x61, 0x6b, 0x02, 0x1d, 0x9d, 0x88, 0xb7, 0xf8, 0x3e, 0xcc, 0x4f, 0x52,
|
||||
0xd7, 0xe7, 0xa1, 0xd0, 0xc5, 0xa3, 0xa8, 0x56, 0x88, 0x7f, 0xea, 0x0b, 0x50, 0x1c, 0xda, 0xbd,
|
||||
0x81, 0x3c, 0x89, 0x28, 0x5a, 0x5c, 0xcf, 0x5f, 0xd3, 0xcc, 0x1f, 0x34, 0x30, 0xa6, 0x11, 0xd1,
|
||||
0xff, 0x9b, 0x00, 0xb2, 0xaa, 0x92, 0x55, 0xe1, 0x36, 0x1e, 0x45, 0xa8, 0x1b, 0x50, 0x0e, 0x42,
|
||||
0xfe, 0x9e, 0x0b, 0x88, 0xac, 0xf8, 0x65, 0x55, 0xc5, 0x7b, 0x52, 0x7e, 0x7c, 0xd8, 0xb8, 0x90,
|
||||
0x82, 0x57, 0x0a, 0x14, 0xbb, 0xea, 0x26, 0x94, 0x04, 0x1f, 0x6a, 0x14, 0xc4, 0x6d, 0x04, 0x7c,
|
||||
0xae, 0x3e, 0x10, 0x12, 0x24, 0x35, 0xe6, 0x67, 0x50, 0xe6, 0x57, 0xed, 0x16, 0x66, 0x36, 0x6f,
|
||||
0x1e, 0x8a, 0x7b, 0x7b, 0x77, 0x3c, 0xbf, 0x2b, 0xa9, 0xc5, 0xcd, 0xb3, 0x23, 0xe5, 0x28, 0xb6,
|
||||
0x78, 0xd5, 0x78, 0xcd, 0x9f, 0x71, 0xbc, 0x5e, 0x81, 0x0a, 0x0a, 0x02, 0xb6, 0x6d, 0xb3, 0x7d,
|
||||
0xaa, 0x37, 0xa0, 0x18, 0xf2, 0x0f, 0x79, 0x75, 0x56, 0xf8, 0x31, 0x10, 0x1a, 0x14, 0xc9, 0xcd,
|
||||
0xaf, 0x35, 0xb8, 0x38, 0xf5, 0x2e, 0xe3, 0xaf, 0x12, 0x27, 0x5e, 0x49, 0xfa, 0xf1, 0xab, 0x64,
|
||||
0x6c, 0x87, 0x12, 0x56, 0xfa, 0xbb, 0x50, 0x4b, 0x5d, 0x80, 0x72, 0x03, 0x17, 0xa4, 0x5b, 0x2d,
|
||||
0x15, 0x0d, 0xa5, 0x6d, 0xcd, 0x3f, 0xf3, 0x50, 0xda, 0x61, 0x36, 0x1b, 0x50, 0xfd, 0x21, 0x94,
|
||||
0x79, 0xef, 0xb9, 0x36, 0xb3, 0x45, 0xe4, 0x8c, 0x6f, 0x33, 0x95, 0xf8, 0x71, 0x9a, 0x95, 0x04,
|
||||
0xc5, 0x78, 0xfc, 0x82, 0xa4, 0x22, 0x8a, 0x24, 0x17, 0x5f, 0x90, 0x51, 0x6c, 0x24, 0xb5, 0x7c,
|
||||
0x48, 0xf4, 0x31, 0xa5, 0x76, 0x47, 0x1d, 0xfc, 0x78, 0x48, 0x6c, 0x45, 0x62, 0xa4, 0xf4, 0xfa,
|
||||
0x3b, 0x50, 0x22, 0xd8, 0xa6, 0x81, 0x6f, 0xcc, 0x08, 0xcb, 0xba, 0x82, 0x44, 0x42, 0x7a, 0x7c,
|
||||
0xd8, 0x98, 0x95, 0xe0, 0x62, 0x8d, 0xa4, 0xb5, 0xfe, 0x21, 0x9c, 0x73, 0x31, 0xb3, 0xbd, 0x1e,
|
||||
0x35, 0x8a, 0x62, 0x97, 0xcb, 0x99, 0x5e, 0x28, 0x02, 0x6a, 0x3d, 0x72, 0xb4, 0xaa, 0x9c, 0x91,
|
||||
0x5c, 0x20, 0x05, 0xc7, 0x67, 0xa9, 0x13, 0xb8, 0xd8, 0x28, 0x35, 0xb5, 0xa5, 0xe2, 0x78, 0x96,
|
||||
0xae, 0x05, 0x2e, 0x46, 0x42, 0x63, 0x3e, 0xd5, 0xa0, 0x1a, 0x21, 0xad, 0xd9, 0x03, 0x8a, 0xf5,
|
||||
0xe5, 0x78, 0x0f, 0x51, 0xa9, 0x2f, 0x2a, 0x9f, 0xfb, 0xa3, 0x10, 0x1f, 0x1f, 0x36, 0x2a, 0xc2,
|
||||
0x8c, 0x2f, 0x62, 0xfa, 0x89, 0x0c, 0xe5, 0x4f, 0xc9, 0xd0, 0x25, 0x28, 0xee, 0x79, 0xb8, 0xa7,
|
||||
0x86, 0x7b, 0x3c, 0x96, 0x6f, 0x72, 0x21, 0x8a, 0x74, 0xe6, 0x77, 0x79, 0xa8, 0xa5, 0x36, 0x97,
|
||||
0xe1, 0xed, 0x1c, 0xcf, 0xfb, 0x7c, 0x86, 0x37, 0xc4, 0xd4, 0x9b, 0x45, 0xff, 0x00, 0x4a, 0x0e,
|
||||
0xdf, 0x9f, 0xfa, 0xa7, 0xd2, 0xce, 0x5e, 0x08, 0x91, 0x97, 0x71, 0x17, 0x89, 0x25, 0x45, 0x12,
|
||||
0x4e, 0xbf, 0x05, 0xe7, 0x09, 0x66, 0x64, 0xb4, 0xba, 0xc7, 0x30, 0xd9, 0xc1, 0x4e, 0xe0, 0xbb,
|
||||
0x51, 0xb1, 0x8b, 0x71, 0x86, 0xcf, 0xa3, 0x49, 0x03, 0x74, 0xd2, 0xc7, 0xec, 0xc1, 0xcc, 0x7d,
|
||||
0xaf, 0x8f, 0x79, 0xd2, 0xa9, 0x84, 0x89, 0x9e, 0x8b, 0x71, 0xd2, 0x95, 0xb3, 0xd2, 0xf3, 0xdc,
|
||||
0xf8, 0xb6, 0x1f, 0x44, 0x8d, 0x5e, 0x1c, 0xe7, 0xe6, 0x2e, 0x17, 0xa2, 0x48, 0x77, 0x7d, 0x81,
|
||||
0x5f, 0x59, 0x5f, 0x1d, 0x34, 0x72, 0x4f, 0x0f, 0x1a, 0xb9, 0x67, 0x07, 0xf2, 0xfa, 0xfa, 0x08,
|
||||
0x2a, 0x3c, 0x1a, 0x65, 0x76, 0x3f, 0xfc, 0xbb, 0x43, 0x9a, 0x1f, 0x43, 0x99, 0xf7, 0x91, 0x18,
|
||||
0x91, 0xaa, 0x34, 0xda, 0xd4, 0xd2, 0xac, 0x00, 0xd8, 0xa1, 0x97, 0x9e, 0x88, 0xf1, 0x1c, 0x1a,
|
||||
0xff, 0x5b, 0x40, 0x09, 0x2b, 0xeb, 0xff, 0xcf, 0x5f, 0xd6, 0x73, 0x2f, 0x5e, 0xd6, 0x73, 0xbf,
|
||||
0xbc, 0xac, 0xe7, 0x9e, 0x1c, 0xd5, 0xb5, 0xe7, 0x47, 0x75, 0xed, 0xc5, 0x51, 0x5d, 0xfb, 0xed,
|
||||
0xa8, 0xae, 0x3d, 0xfd, 0xbd, 0x9e, 0x7b, 0x98, 0x1f, 0x2e, 0xff, 0x15, 0x00, 0x00, 0xff, 0xff,
|
||||
0xf6, 0xd1, 0x29, 0xc5, 0xf3, 0x10, 0x00, 0x00,
|
||||
// 1459 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x57, 0x4f, 0x6f, 0x1b, 0x45,
|
||||
0x14, 0xf7, 0xda, 0xb1, 0x6b, 0x3f, 0xc7, 0x24, 0x5d, 0x52, 0xe1, 0x46, 0xc2, 0x36, 0x5b, 0x84,
|
||||
0x52, 0xa9, 0xb5, 0x95, 0x08, 0xa1, 0xaa, 0x50, 0xa4, 0x6c, 0x92, 0x56, 0xa1, 0x4d, 0x1b, 0x4d,
|
||||
0xaa, 0x82, 0x4a, 0x0f, 0x6c, 0xbc, 0x13, 0x67, 0xb1, 0xbd, 0xbb, 0xcc, 0x8c, 0xad, 0x5a, 0x1c,
|
||||
0xa8, 0x38, 0x71, 0x40, 0xa8, 0x47, 0xc4, 0x01, 0x35, 0x12, 0x1f, 0x80, 0xaf, 0xc0, 0xad, 0x37,
|
||||
0xca, 0x8d, 0x03, 0xb2, 0x68, 0x10, 0x27, 0xbe, 0x41, 0x4e, 0x68, 0x66, 0x67, 0xf6, 0x8f, 0x53,
|
||||
0x37, 0x1b, 0x95, 0x03, 0x27, 0xef, 0xbc, 0x3f, 0xbf, 0xf7, 0x9b, 0xf7, 0xde, 0xbc, 0x19, 0xc3,
|
||||
0x4a, 0xf7, 0x0a, 0x6d, 0x3a, 0x5e, 0xab, 0x3b, 0xd8, 0xc5, 0xc4, 0xc5, 0x0c, 0xd3, 0x96, 0xdf,
|
||||
0xed, 0xb4, 0x2c, 0xdf, 0xa1, 0xad, 0x3e, 0x66, 0x56, 0x6b, 0xb8, 0xdc, 0xea, 0x60, 0x17, 0x13,
|
||||
0x8b, 0x61, 0xbb, 0xe9, 0x13, 0x8f, 0x79, 0xba, 0x11, 0xf8, 0x34, 0x23, 0x9f, 0xa6, 0xdf, 0xed,
|
||||
0x34, 0xb9, 0x4f, 0x93, 0xfb, 0x34, 0x87, 0xcb, 0x8b, 0x97, 0x3b, 0x0e, 0xdb, 0x1f, 0xec, 0x36,
|
||||
0xdb, 0x5e, 0xbf, 0xd5, 0xf1, 0x3a, 0x5e, 0x4b, 0xb8, 0xee, 0x0e, 0xf6, 0xc4, 0x4a, 0x2c, 0xc4,
|
||||
0x57, 0x00, 0xb9, 0x78, 0xf9, 0xc5, 0x34, 0xc8, 0xc0, 0x65, 0x4e, 0x1f, 0x4f, 0x32, 0x58, 0x7c,
|
||||
0xf7, 0xe5, 0xe6, 0xb4, 0xbd, 0x8f, 0xfb, 0xd6, 0x31, 0xaf, 0xe5, 0x17, 0x7b, 0x0d, 0x98, 0xd3,
|
||||
0x6b, 0x39, 0x2e, 0xa3, 0x8c, 0x4c, 0xba, 0x18, 0xbf, 0xe4, 0xa0, 0xb8, 0xba, 0xbd, 0x79, 0x83,
|
||||
0x78, 0x03, 0x5f, 0x6f, 0xc0, 0x8c, 0x6b, 0xf5, 0x71, 0x55, 0x6b, 0x68, 0x4b, 0x25, 0x73, 0xf6,
|
||||
0xe9, 0xb8, 0x9e, 0x39, 0x1c, 0xd7, 0x67, 0x6e, 0x5b, 0x7d, 0x8c, 0x84, 0x46, 0xff, 0x1c, 0x8a,
|
||||
0x43, 0x4c, 0xa8, 0xe3, 0xb9, 0xb4, 0x9a, 0x6d, 0xe4, 0x96, 0xca, 0x2b, 0x1f, 0x34, 0x4f, 0x4e,
|
||||
0x56, 0x53, 0xc0, 0xdf, 0x0b, 0x1c, 0xaf, 0x7b, 0x64, 0xdd, 0xa1, 0x6d, 0x6f, 0x88, 0xc9, 0xc8,
|
||||
0x9c, 0x97, 0x31, 0x8a, 0x52, 0x49, 0x51, 0x88, 0xaf, 0x7f, 0xad, 0xc1, 0xbc, 0x4f, 0xf0, 0x1e,
|
||||
0x26, 0x04, 0xdb, 0x52, 0x5f, 0xcd, 0x35, 0xb4, 0x57, 0x0e, 0x5a, 0x95, 0x41, 0xe7, 0xb7, 0x27,
|
||||
0xd0, 0xd1, 0xb1, 0x78, 0xfa, 0x81, 0x06, 0x8b, 0x14, 0x93, 0x21, 0x26, 0xab, 0xb6, 0x4d, 0x30,
|
||||
0xa5, 0xe6, 0x68, 0xad, 0xe7, 0x60, 0x97, 0xad, 0x6d, 0xae, 0x23, 0x5a, 0x9d, 0x11, 0x39, 0xb8,
|
||||
0x96, 0x86, 0xce, 0xce, 0x34, 0x14, 0xd3, 0x90, 0x7c, 0x16, 0xa7, 0x9a, 0x50, 0xf4, 0x12, 0x12,
|
||||
0x86, 0x0d, 0xb3, 0xaa, 0x84, 0xb7, 0x1c, 0xca, 0xf4, 0xbb, 0x50, 0xe8, 0xf0, 0x05, 0xad, 0x6a,
|
||||
0x82, 0xde, 0xa5, 0x34, 0xf4, 0x14, 0x82, 0xf9, 0x9a, 0x64, 0x53, 0x10, 0x4b, 0x8a, 0x24, 0x96,
|
||||
0xf1, 0x9b, 0x06, 0xe5, 0xd5, 0xed, 0x4d, 0x84, 0xa9, 0x37, 0x20, 0x6d, 0x9c, 0xa2, 0x59, 0x56,
|
||||
0x00, 0xf8, 0x2f, 0xf5, 0xad, 0x36, 0xb6, 0xab, 0xd9, 0x86, 0xb6, 0x54, 0x34, 0x75, 0x69, 0x07,
|
||||
0xb7, 0x43, 0x0d, 0x8a, 0x59, 0x71, 0xd4, 0xae, 0xe3, 0xda, 0xa2, 0xce, 0x31, 0xd4, 0x9b, 0x8e,
|
||||
0x6b, 0x23, 0xa1, 0xd1, 0x3f, 0x82, 0xfc, 0x10, 0x93, 0x5d, 0x9e, 0x7b, 0xde, 0x0a, 0x17, 0xd3,
|
||||
0x6c, 0xee, 0x1e, 0x77, 0x30, 0x4b, 0x87, 0xe3, 0x7a, 0x5e, 0x7c, 0xa2, 0x00, 0xc2, 0xf8, 0x59,
|
||||
0x83, 0xb9, 0xd8, 0x9e, 0x44, 0xf6, 0xae, 0xc0, 0x6c, 0x27, 0xd6, 0x39, 0x72, 0x7f, 0x0b, 0x92,
|
||||
0xc9, 0x6c, 0xbc, 0xab, 0x50, 0xc2, 0x52, 0x6f, 0x43, 0x89, 0x48, 0x24, 0x75, 0x3a, 0x5a, 0x29,
|
||||
0x53, 0xaf, 0x18, 0x44, 0x71, 0x62, 0x42, 0x8a, 0x22, 0x5c, 0xe3, 0xef, 0xa0, 0x0c, 0xea, 0xbc,
|
||||
0xe8, 0x4b, 0xb1, 0x13, 0xc9, 0xcb, 0x5d, 0x32, 0x67, 0xa7, 0x9c, 0xa7, 0x13, 0x5a, 0x39, 0xfb,
|
||||
0x3f, 0x68, 0xe5, 0xab, 0xc5, 0xef, 0x9f, 0xd4, 0x33, 0x8f, 0xfe, 0x68, 0x64, 0x8c, 0x4d, 0x28,
|
||||
0xae, 0x0f, 0x88, 0xc5, 0x78, 0x62, 0xaf, 0x41, 0xd1, 0x96, 0xdf, 0xa2, 0x1c, 0x39, 0xf3, 0x2d,
|
||||
0x35, 0x37, 0x94, 0xcd, 0xd1, 0xb8, 0x5e, 0xe1, 0xa3, 0xb1, 0xa9, 0x04, 0x28, 0x74, 0x31, 0x1e,
|
||||
0x40, 0x65, 0xe3, 0xa1, 0xef, 0x11, 0x76, 0xc7, 0x67, 0x22, 0x13, 0xef, 0x40, 0x01, 0x0b, 0x81,
|
||||
0x40, 0x2b, 0x46, 0x2d, 0x1f, 0x98, 0x21, 0xa9, 0xd5, 0x2f, 0x40, 0x1e, 0x3f, 0xb4, 0xda, 0x4c,
|
||||
0xf6, 0x6e, 0x45, 0x9a, 0xe5, 0x37, 0xb8, 0x10, 0x05, 0x3a, 0xe3, 0x0e, 0xc0, 0x0d, 0x1c, 0x42,
|
||||
0xaf, 0xc2, 0x9c, 0xaa, 0x55, 0xb2, 0x81, 0xde, 0x90, 0xce, 0x73, 0x28, 0xa9, 0x46, 0x93, 0xf6,
|
||||
0xc6, 0x03, 0x28, 0x89, 0x26, 0xe3, 0x3d, 0xcf, 0x29, 0x88, 0x1e, 0x93, 0x28, 0x21, 0x05, 0x61,
|
||||
0x81, 0x02, 0x5d, 0x78, 0x68, 0xb2, 0xd3, 0x0e, 0x4d, 0x2c, 0xaf, 0x3d, 0xa8, 0x04, 0xbe, 0xea,
|
||||
0x1c, 0xa7, 0x8a, 0x70, 0x09, 0x8a, 0x8a, 0xa6, 0x8c, 0x12, 0x4e, 0x6e, 0x05, 0x84, 0x42, 0x8b,
|
||||
0x58, 0xb4, 0x7d, 0x48, 0x1c, 0x98, 0x74, 0xc1, 0x2e, 0xc2, 0x19, 0xd9, 0xb4, 0x32, 0xd6, 0x9c,
|
||||
0x34, 0x3b, 0xa3, 0x72, 0xa6, 0xf4, 0xb1, 0x48, 0x5f, 0x41, 0x75, 0xda, 0xc0, 0x7f, 0x85, 0x23,
|
||||
0x9d, 0x9e, 0x8a, 0xf1, 0x9d, 0x06, 0xf3, 0x71, 0xa4, 0xf4, 0xe5, 0x4b, 0x1f, 0xe4, 0xe4, 0xf1,
|
||||
0x18, 0xcb, 0xc8, 0x8f, 0x1a, 0x2c, 0x24, 0xb6, 0x76, 0xaa, 0x8a, 0x9f, 0x82, 0x54, 0xbc, 0x39,
|
||||
0x72, 0xa7, 0x68, 0x8e, 0x5f, 0xb3, 0x50, 0xb9, 0x65, 0xed, 0xe2, 0xde, 0x0e, 0xee, 0xe1, 0x36,
|
||||
0xf3, 0x88, 0x3e, 0x82, 0x72, 0xdf, 0x62, 0xed, 0x7d, 0x21, 0x55, 0xd7, 0x97, 0x99, 0x66, 0x24,
|
||||
0x25, 0x70, 0x9a, 0x5b, 0x11, 0xc8, 0x86, 0xcb, 0xc8, 0xc8, 0x7c, 0x5d, 0x12, 0x2a, 0xc7, 0x34,
|
||||
0x28, 0x1e, 0x4b, 0xbc, 0x36, 0xc4, 0x7a, 0xe3, 0xa1, 0xcf, 0xe7, 0xd2, 0x69, 0x9f, 0x38, 0x09,
|
||||
0x02, 0x08, 0x7f, 0x31, 0x70, 0x08, 0xee, 0x63, 0x97, 0x45, 0xaf, 0x8d, 0xad, 0x09, 0x74, 0x74,
|
||||
0x2c, 0xde, 0xe2, 0x87, 0x30, 0x3f, 0x49, 0x5d, 0x9f, 0x87, 0x5c, 0x17, 0x8f, 0x82, 0x5a, 0x21,
|
||||
0xfe, 0xa9, 0x2f, 0x40, 0x7e, 0x68, 0xf5, 0x06, 0xf2, 0x24, 0xa2, 0x60, 0x71, 0x35, 0x7b, 0x45,
|
||||
0x33, 0x7e, 0xd2, 0xa0, 0x3a, 0x8d, 0x88, 0xfe, 0x66, 0x0c, 0xc8, 0x2c, 0x4b, 0x56, 0xb9, 0x9b,
|
||||
0x78, 0x14, 0xa0, 0x6e, 0x40, 0xd1, 0xf3, 0xf9, 0xdb, 0xd0, 0x23, 0xb2, 0xe2, 0x17, 0x55, 0x15,
|
||||
0xef, 0x48, 0xf9, 0xd1, 0xb8, 0x7e, 0x2e, 0x01, 0xaf, 0x14, 0x28, 0x74, 0xd5, 0x0d, 0x28, 0x08,
|
||||
0x3e, 0xb4, 0x9a, 0x13, 0xb7, 0x11, 0xf0, 0xb9, 0x7a, 0x4f, 0x48, 0x90, 0xd4, 0x18, 0x5f, 0x42,
|
||||
0x91, 0x5f, 0xb5, 0x5b, 0x98, 0x59, 0xbc, 0x79, 0x28, 0xee, 0xed, 0xdd, 0x72, 0xdc, 0xae, 0xa4,
|
||||
0x16, 0x36, 0xcf, 0x8e, 0x94, 0xa3, 0xd0, 0xe2, 0x45, 0xe3, 0x35, 0x7b, 0xca, 0xf1, 0x7a, 0x09,
|
||||
0x4a, 0xc8, 0xf3, 0xd8, 0xb6, 0xc5, 0xf6, 0xa9, 0x5e, 0x87, 0xbc, 0xcf, 0x3f, 0xe4, 0xd5, 0x29,
|
||||
0x5e, 0x08, 0x42, 0x83, 0x02, 0xb9, 0xf1, 0xad, 0x06, 0xe7, 0xa7, 0xde, 0x65, 0xfc, 0x85, 0xd3,
|
||||
0x0e, 0x57, 0x92, 0x7e, 0xf8, 0xc2, 0x89, 0xec, 0x50, 0xcc, 0x4a, 0x7f, 0x1f, 0x2a, 0x89, 0x0b,
|
||||
0x50, 0x6e, 0xe0, 0x9c, 0x74, 0xab, 0x24, 0xa2, 0xa1, 0xa4, 0xad, 0xf1, 0x4f, 0x16, 0x0a, 0x3b,
|
||||
0xcc, 0x62, 0x03, 0xaa, 0xdf, 0x87, 0x22, 0xef, 0x3d, 0xdb, 0x62, 0x96, 0x88, 0x9c, 0xf2, 0x9d,
|
||||
0xa7, 0x12, 0x1f, 0xa5, 0x59, 0x49, 0x50, 0x88, 0xc7, 0x2f, 0x48, 0x2a, 0xa2, 0x48, 0x72, 0xe1,
|
||||
0x05, 0x19, 0xc4, 0x46, 0x52, 0xcb, 0x87, 0x44, 0x1f, 0x53, 0x6a, 0x75, 0xd4, 0xc1, 0x0f, 0x87,
|
||||
0xc4, 0x56, 0x20, 0x46, 0x4a, 0xaf, 0xbf, 0x07, 0x05, 0x82, 0x2d, 0xea, 0xb9, 0xe2, 0xdd, 0x56,
|
||||
0x32, 0x6b, 0x0a, 0x12, 0x09, 0xe9, 0xd1, 0xb8, 0x3e, 0x2b, 0xc1, 0xc5, 0x1a, 0x49, 0x6b, 0xfd,
|
||||
0x13, 0x38, 0x63, 0x63, 0x66, 0x39, 0x3d, 0x5a, 0xcd, 0x8b, 0x5d, 0x2e, 0xa7, 0x7a, 0xa1, 0x08,
|
||||
0xa8, 0xf5, 0xc0, 0xd1, 0x2c, 0x73, 0x46, 0x72, 0x81, 0x14, 0x1c, 0x9f, 0xa5, 0x6d, 0xcf, 0xc6,
|
||||
0xd5, 0x42, 0x43, 0x5b, 0xca, 0x47, 0xb3, 0x74, 0xcd, 0xb3, 0x31, 0x12, 0x1a, 0xe3, 0xb1, 0x06,
|
||||
0xe5, 0x00, 0x69, 0xcd, 0x1a, 0x50, 0xac, 0x2f, 0x87, 0x7b, 0x08, 0x4a, 0x7d, 0x5e, 0xf9, 0xdc,
|
||||
0x1d, 0xf9, 0xf8, 0x68, 0x5c, 0x2f, 0x09, 0x33, 0xbe, 0x08, 0xe9, 0xc7, 0x32, 0x94, 0x3d, 0x21,
|
||||
0x43, 0x17, 0x20, 0xbf, 0xe7, 0xe0, 0x9e, 0x1a, 0xee, 0xe1, 0x58, 0xbe, 0xce, 0x85, 0x28, 0xd0,
|
||||
0x19, 0x3f, 0x64, 0xa1, 0x92, 0xd8, 0x5c, 0x8a, 0x77, 0x78, 0x38, 0xef, 0xb3, 0x29, 0xde, 0x10,
|
||||
0xd3, 0x1f, 0xde, 0x1f, 0x43, 0xa1, 0xcd, 0xf7, 0xa7, 0xfe, 0xf5, 0xb4, 0xd2, 0x17, 0x42, 0xe4,
|
||||
0x25, 0xea, 0x22, 0xb1, 0xa4, 0x48, 0xc2, 0xe9, 0x37, 0xe0, 0x2c, 0xc1, 0x8c, 0x8c, 0x56, 0xf7,
|
||||
0x18, 0x26, 0x3b, 0xb8, 0xed, 0xb9, 0x76, 0x50, 0xec, 0x7c, 0x98, 0xe1, 0xb3, 0x68, 0xd2, 0x00,
|
||||
0x1d, 0xf7, 0x31, 0x7a, 0x30, 0x73, 0xd7, 0xe9, 0x63, 0x9e, 0x74, 0x2a, 0x61, 0x82, 0xe7, 0x62,
|
||||
0x98, 0x74, 0xe5, 0xac, 0xf4, 0x3c, 0x37, 0xae, 0xe5, 0x7a, 0x41, 0xa3, 0xe7, 0xa3, 0xdc, 0xdc,
|
||||
0xe6, 0x42, 0x14, 0xe8, 0xae, 0x2e, 0xf0, 0x2b, 0xeb, 0x9b, 0x83, 0x7a, 0xe6, 0xf1, 0x41, 0x3d,
|
||||
0xf3, 0xe4, 0x40, 0x5e, 0x5f, 0x9f, 0x42, 0x89, 0x47, 0xa3, 0xcc, 0xea, 0xfb, 0xff, 0x75, 0x48,
|
||||
0xe3, 0x33, 0x28, 0xf2, 0x3e, 0x12, 0x23, 0x52, 0x95, 0x46, 0x9b, 0x5a, 0x9a, 0x15, 0x00, 0xcb,
|
||||
0x77, 0x92, 0x13, 0x31, 0x9c, 0x43, 0xd1, 0xbf, 0x05, 0x14, 0xb3, 0x32, 0x56, 0x20, 0xf8, 0x2f,
|
||||
0xc4, 0x67, 0xa0, 0xc3, 0x70, 0x3f, 0x31, 0x03, 0x37, 0xb9, 0x00, 0x05, 0xf2, 0xe8, 0xc6, 0x36,
|
||||
0xdf, 0x7e, 0xfa, 0xbc, 0x96, 0x79, 0xf6, 0xbc, 0x96, 0xf9, 0xfd, 0x79, 0x2d, 0xf3, 0xe8, 0xb0,
|
||||
0xa6, 0x3d, 0x3d, 0xac, 0x69, 0xcf, 0x0e, 0x6b, 0xda, 0x9f, 0x87, 0x35, 0xed, 0xf1, 0x5f, 0xb5,
|
||||
0xcc, 0xfd, 0xec, 0x70, 0xf9, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc7, 0xa0, 0x5e, 0x60, 0x73,
|
||||
0x11, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@ message APIResource {
|
|||
|
||||
// kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')
|
||||
optional string kind = 3;
|
||||
|
||||
// verbs is a list of supported kube verbs (this includes get, list, watch, create,
|
||||
// update, patch, delete, deletecollection, and proxy)
|
||||
optional Verbs verbs = 4;
|
||||
}
|
||||
|
||||
// APIResourceList is a list of APIResource, it is used to expose the name of the
|
||||
|
@ -408,3 +412,13 @@ message TypeMeta {
|
|||
optional string apiVersion = 2;
|
||||
}
|
||||
|
||||
// Verbs masks the value so protobuf can generate
|
||||
//
|
||||
// +protobuf.nullable=true
|
||||
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
||||
message Verbs {
|
||||
// items, if empty, will result in an empty slice
|
||||
|
||||
repeated string items = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,12 @@ limitations under the License.
|
|||
// separate packages.
|
||||
package v1
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ugorji/go/codec"
|
||||
)
|
||||
|
||||
// TypeMeta describes an individual object in an API response or request
|
||||
// with strings representing the type of the object and its API schema version.
|
||||
|
@ -403,6 +408,40 @@ type APIResource struct {
|
|||
Namespaced bool `json:"namespaced" protobuf:"varint,2,opt,name=namespaced"`
|
||||
// kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')
|
||||
Kind string `json:"kind" protobuf:"bytes,3,opt,name=kind"`
|
||||
// verbs is a list of supported kube verbs (this includes get, list, watch, create,
|
||||
// update, patch, delete, deletecollection, and proxy)
|
||||
Verbs Verbs `json:"verbs" protobuf:"bytes,4,opt,name=verbs"`
|
||||
}
|
||||
|
||||
// Verbs masks the value so protobuf can generate
|
||||
//
|
||||
// +protobuf.nullable=true
|
||||
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
||||
type Verbs []string
|
||||
|
||||
func (vs Verbs) String() string {
|
||||
return fmt.Sprintf("%v", []string(vs))
|
||||
}
|
||||
|
||||
// CodecEncodeSelf is part of the codec.Selfer interface.
|
||||
func (vs *Verbs) CodecEncodeSelf(encoder *codec.Encoder) {
|
||||
encoder.Encode(vs)
|
||||
}
|
||||
|
||||
// CodecDecodeSelf is part of the codec.Selfer interface. It is overwritten here to make sure
|
||||
// that an empty verbs list is not decoded as nil. On the other hand, an undefined verbs list
|
||||
// will lead to nil because this decoding for Verbs is not invoked.
|
||||
//
|
||||
// TODO(sttts): this is due to a ugorji regression: https://github.com/ugorji/go/issues/119. Remove the
|
||||
// workaround when the regression is fixed.
|
||||
func (vs *Verbs) CodecDecodeSelf(decoder *codec.Decoder) {
|
||||
m := []string{}
|
||||
decoder.Decode(&m)
|
||||
if len(m) == 0 {
|
||||
*vs = []string{}
|
||||
} else {
|
||||
*vs = m
|
||||
}
|
||||
}
|
||||
|
||||
// APIResourceList is a list of APIResource, it is used to expose the name of the
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ugorji/go/codec"
|
||||
)
|
||||
|
||||
func TestVerbsMarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
input APIResource
|
||||
result string
|
||||
}{
|
||||
{APIResource{}, `{"name":"","namespaced":false,"kind":"","verbs":null}`},
|
||||
{APIResource{Verbs: Verbs([]string{})}, `{"name":"","namespaced":false,"kind":"","verbs":[]}`},
|
||||
{APIResource{Verbs: Verbs([]string{"delete"})}, `{"name":"","namespaced":false,"kind":"","verbs":["delete"]}`},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
result, err := json.Marshal(&c.input)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] Failed to marshal input: '%v': %v", i, c.input, err)
|
||||
}
|
||||
if string(result) != c.result {
|
||||
t.Errorf("[%d] Failed to marshal input: '%v': expected %+v, got %q", i, c.input, c.result, string(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerbsUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
result APIResource
|
||||
}{
|
||||
{`{}`, APIResource{}},
|
||||
{`{"verbs":null}`, APIResource{}},
|
||||
{`{"verbs":[]}`, APIResource{Verbs: Verbs([]string{})}},
|
||||
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
var result APIResource
|
||||
if err := codec.NewDecoderBytes([]byte(c.input), new(codec.JsonHandle)).Decode(&result); err != nil {
|
||||
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, c.result) {
|
||||
t.Errorf("[%d] Failed to unmarshal input '%v': expected %+v, got %+v", i, c.input, c.result, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerbsUgorjiUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
result APIResource
|
||||
}{
|
||||
{`{}`, APIResource{}},
|
||||
{`{"verbs":null}`, APIResource{}},
|
||||
{`{"verbs":[]}`, APIResource{Verbs: Verbs([]string{})}},
|
||||
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
var result APIResource
|
||||
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
|
||||
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, c.result) {
|
||||
t.Errorf("[%d] Failed to unmarshal input '%v': expected %+v, got %+v", i, c.input, c.result, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerbsProto(t *testing.T) {
|
||||
cases := []APIResource{
|
||||
{},
|
||||
{Verbs: Verbs([]string{})},
|
||||
{Verbs: Verbs([]string{"delete"})},
|
||||
}
|
||||
|
||||
for _, input := range cases {
|
||||
data, err := input.Marshal()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal input: '%v': %v", input, err)
|
||||
}
|
||||
resource := APIResource{}
|
||||
if err := resource.Unmarshal(data); err != nil {
|
||||
t.Fatalf("Failed to unmarshal output: '%v': %v", input, err)
|
||||
}
|
||||
if !reflect.DeepEqual(input, resource) {
|
||||
t.Errorf("Marshal->Unmarshal is not idempotent: '%v' vs '%v'", input, resource)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -81,6 +81,13 @@ func DeepCopy_v1_APIResource(in interface{}, out interface{}, c *conversion.Clon
|
|||
out.Name = in.Name
|
||||
out.Namespaced = in.Namespaced
|
||||
out.Kind = in.Kind
|
||||
if in.Verbs != nil {
|
||||
in, out := &in.Verbs, &out.Verbs
|
||||
*out = make(Verbs, len(*in))
|
||||
copy(*out, *in)
|
||||
} else {
|
||||
out.Verbs = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +102,9 @@ func DeepCopy_v1_APIResourceList(in interface{}, out interface{}, c *conversion.
|
|||
in, out := &in.APIResources, &out.APIResources
|
||||
*out = make([]APIResource, len(*in))
|
||||
for i := range *in {
|
||||
(*out)[i] = (*in)[i]
|
||||
if err := DeepCopy_v1_APIResource(&(*in)[i], &(*out)[i], c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.APIResources = nil
|
||||
|
|
|
@ -62,6 +62,21 @@ type documentable interface {
|
|||
SwaggerDoc() map[string]string
|
||||
}
|
||||
|
||||
// toDiscoveryKubeVerb maps an action.Verb to the logical kube verb, used for discovery
|
||||
var toDiscoveryKubeVerb = map[string]string{
|
||||
"CONNECT": "", // do not list in discovery.
|
||||
"DELETE": "delete",
|
||||
"DELETECOLLECTION": "deletecollection",
|
||||
"GET": "get",
|
||||
"LIST": "list",
|
||||
"PATCH": "patch",
|
||||
"POST": "create",
|
||||
"PROXY": "proxy",
|
||||
"PUT": "update",
|
||||
"WATCH": "watch",
|
||||
"WATCHLIST": "watch",
|
||||
}
|
||||
|
||||
// errEmptyName is returned when API requests do not fill the name section of the path.
|
||||
var errEmptyName = errors.NewBadRequest("name must be provided")
|
||||
|
||||
|
@ -490,6 +505,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
allMediaTypes := append(mediaTypes, streamMediaTypes...)
|
||||
ws.Produces(allMediaTypes...)
|
||||
|
||||
kubeVerbs := map[string]struct{}{}
|
||||
reqScope := RequestScope{
|
||||
ContextFunc: ctxFn,
|
||||
Serializer: a.group.Serializer,
|
||||
|
@ -518,6 +534,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
namespaced = ""
|
||||
}
|
||||
|
||||
if kubeVerb, found := toDiscoveryKubeVerb[action.Verb]; found {
|
||||
if len(kubeVerb) != 0 {
|
||||
kubeVerbs[kubeVerb] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("unknown action verb for discovery: %s", action.Verb)
|
||||
}
|
||||
|
||||
switch action.Verb {
|
||||
case "GET": // Get a resource.
|
||||
var handler restful.RouteFunction
|
||||
|
@ -754,6 +778,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||
}
|
||||
// Note: update GetAuthorizerAttributes() when adding a custom handler.
|
||||
}
|
||||
|
||||
apiResource.Verbs = make([]string, 0, len(kubeVerbs))
|
||||
for kubeVerb := range kubeVerbs {
|
||||
apiResource.Verbs = append(apiResource.Verbs, kubeVerb)
|
||||
}
|
||||
sort.Strings(apiResource.Verbs)
|
||||
|
||||
return &apiResource, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ type Attributes interface {
|
|||
// GetUser returns the user.Info object to authorize
|
||||
GetUser() user.Info
|
||||
|
||||
// GetVerb returns the kube verb associated with API requests (this includes get, list, watch, create, update, patch, delete, and proxy),
|
||||
// GetVerb returns the kube verb associated with API requests (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy),
|
||||
// or the lowercased HTTP verb associated with non-API requests (this includes get, put, post, patch, and delete)
|
||||
GetVerb() string
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ type Fake struct {
|
|||
// for every request in the order they are tried.
|
||||
ProxyReactionChain []ProxyReactor
|
||||
|
||||
Resources map[string]*metav1.APIResourceList
|
||||
Resources []*metav1.APIResourceList
|
||||
}
|
||||
|
||||
// Reactor is an interface to allow the composition of reaction functions.
|
||||
|
@ -225,10 +225,16 @@ func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
|
|||
Resource: schema.GroupVersionResource{Resource: "resource"},
|
||||
}
|
||||
c.Invokes(action, nil)
|
||||
return c.Resources[groupVersion], nil
|
||||
for _, rl := range c.Resources {
|
||||
if rl.GroupVersion == groupVersion {
|
||||
return rl, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("GroupVersion %q not found", groupVersion)
|
||||
}
|
||||
|
||||
func (c *FakeDiscovery) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
action := ActionImpl{
|
||||
Verb: "get",
|
||||
Resource: schema.GroupVersionResource{Resource: "resource"},
|
||||
|
|
|
@ -53,6 +53,7 @@ go_test(
|
|||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/restclient/fake:go_default_library",
|
||||
"//pkg/runtime/schema:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//vendor:github.com/emicklei/go-restful/swagger",
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
|
@ -73,5 +74,6 @@ go_test(
|
|||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/schema:go_default_library",
|
||||
"//pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -36,6 +36,9 @@ import (
|
|||
"k8s.io/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
// defaultRetries is the number of times a resource discovery is repeated if an api group disappears on the fly (e.g. ThirdPartyResources).
|
||||
const defaultRetries = 2
|
||||
|
||||
// DiscoveryInterface holds the methods that discover server-supported API groups,
|
||||
// versions and resources.
|
||||
type DiscoveryInterface interface {
|
||||
|
@ -67,13 +70,13 @@ type ServerResourcesInterface interface {
|
|||
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
|
||||
ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error)
|
||||
// ServerResources returns the supported resources for all groups and versions.
|
||||
ServerResources() (map[string]*metav1.APIResourceList, error)
|
||||
ServerResources() ([]*metav1.APIResourceList, error)
|
||||
// ServerPreferredResources returns the supported resources with the version preferred by the
|
||||
// server.
|
||||
ServerPreferredResources() ([]schema.GroupVersionResource, error)
|
||||
ServerPreferredResources() ([]*metav1.APIResourceList, error)
|
||||
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
|
||||
// version preferred by the server.
|
||||
ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error)
|
||||
ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error)
|
||||
}
|
||||
|
||||
// ServerVersionInterface has a method for retrieving the server's version.
|
||||
|
@ -154,7 +157,9 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (r
|
|||
} else {
|
||||
url.Path = "/apis/" + groupVersion
|
||||
}
|
||||
resources = &metav1.APIResourceList{}
|
||||
resources = &metav1.APIResourceList{
|
||||
GroupVersion: groupVersion,
|
||||
}
|
||||
err = d.restClient.Get().AbsPath(url.String()).Do().Into(resources)
|
||||
if err != nil {
|
||||
// ignore 403 or 404 error to be compatible with an v1.0 server.
|
||||
|
@ -166,22 +171,43 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (r
|
|||
return resources, nil
|
||||
}
|
||||
|
||||
// ServerResources returns the supported resources for all groups and versions.
|
||||
func (d *DiscoveryClient) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
// serverResources returns the supported resources for all groups and versions.
|
||||
func (d *DiscoveryClient) serverResources(failEarly bool) ([]*metav1.APIResourceList, error) {
|
||||
apiGroups, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupVersions := metav1.ExtractGroupVersions(apiGroups)
|
||||
result := map[string]*metav1.APIResourceList{}
|
||||
for _, groupVersion := range groupVersions {
|
||||
resources, err := d.ServerResourcesForGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
result := []*metav1.APIResourceList{}
|
||||
failedGroups := make(map[schema.GroupVersion]error)
|
||||
|
||||
for _, apiGroup := range apiGroups.Groups {
|
||||
for _, version := range apiGroup.Versions {
|
||||
gv := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version}
|
||||
resources, err := d.ServerResourcesForGroupVersion(version.GroupVersion)
|
||||
if err != nil {
|
||||
// TODO: maybe restrict this to NotFound errors
|
||||
failedGroups[gv] = err
|
||||
if failEarly {
|
||||
return nil, &ErrGroupDiscoveryFailed{Groups: failedGroups}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, resources)
|
||||
}
|
||||
result[groupVersion] = resources
|
||||
}
|
||||
return result, nil
|
||||
|
||||
if len(failedGroups) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return result, &ErrGroupDiscoveryFailed{Groups: failedGroups}
|
||||
}
|
||||
|
||||
// ServerResources returns the supported resources for all groups and versions.
|
||||
func (d *DiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
return withRetries(defaultRetries, d.serverResources)
|
||||
}
|
||||
|
||||
// ErrGroupDiscoveryFailed is returned if one or more API groups fail to load.
|
||||
|
@ -207,78 +233,86 @@ func IsGroupDiscoveryFailedError(err error) bool {
|
|||
return err != nil && ok
|
||||
}
|
||||
|
||||
// serverPreferredResources returns the supported resources with the version preferred by the
|
||||
// server. If namespaced is true, only namespaced resources will be returned.
|
||||
func (d *DiscoveryClient) serverPreferredResources(namespaced bool) ([]schema.GroupVersionResource, error) {
|
||||
// retry in case the groups supported by the server change after ServerGroup() returns.
|
||||
const maxRetries = 2
|
||||
var failedGroups map[schema.GroupVersion]error
|
||||
var results []schema.GroupVersionResource
|
||||
var resources map[schema.GroupResource]string
|
||||
RetrieveGroups:
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
results = []schema.GroupVersionResource{}
|
||||
resources = map[schema.GroupResource]string{}
|
||||
failedGroups = make(map[schema.GroupVersion]error)
|
||||
serverGroupList, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
// serverPreferredResources returns the supported resources with the version preferred by the server.
|
||||
func (d *DiscoveryClient) serverPreferredResources(failEarly bool) ([]*metav1.APIResourceList, error) {
|
||||
serverGroupList, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, apiGroup := range serverGroupList.Groups {
|
||||
versions := apiGroup.Versions
|
||||
for _, version := range versions {
|
||||
groupVersion := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version}
|
||||
apiResourceList, err := d.ServerResourcesForGroupVersion(version.GroupVersion)
|
||||
if err != nil {
|
||||
if i < maxRetries-1 {
|
||||
continue RetrieveGroups
|
||||
}
|
||||
failedGroups[groupVersion] = err
|
||||
result := []*metav1.APIResourceList{}
|
||||
failedGroups := make(map[schema.GroupVersion]error)
|
||||
|
||||
grVersions := map[schema.GroupResource]string{} // selected version of a GroupResource
|
||||
grApiResources := map[schema.GroupResource]*metav1.APIResource{} // selected APIResource for a GroupResource
|
||||
gvApiResourceLists := map[schema.GroupVersion]*metav1.APIResourceList{} // blueprint for a APIResourceList for later grouping
|
||||
|
||||
for _, apiGroup := range serverGroupList.Groups {
|
||||
for _, version := range apiGroup.Versions {
|
||||
groupVersion := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version}
|
||||
apiResourceList, err := d.ServerResourcesForGroupVersion(version.GroupVersion)
|
||||
if err != nil {
|
||||
// TODO: maybe restrict this to NotFound errors
|
||||
failedGroups[groupVersion] = err
|
||||
if failEarly {
|
||||
return nil, &ErrGroupDiscoveryFailed{Groups: failedGroups}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// create empty list which is filled later in another loop
|
||||
emptyApiResourceList := metav1.APIResourceList{
|
||||
GroupVersion: version.GroupVersion,
|
||||
}
|
||||
gvApiResourceLists[groupVersion] = &emptyApiResourceList
|
||||
result = append(result, &emptyApiResourceList)
|
||||
|
||||
for i := range apiResourceList.APIResources {
|
||||
apiResource := &apiResourceList.APIResources[i]
|
||||
if strings.Contains(apiResource.Name, "/") {
|
||||
continue
|
||||
}
|
||||
for _, apiResource := range apiResourceList.APIResources {
|
||||
// ignore the root scoped resources if "namespaced" is true.
|
||||
if namespaced && !apiResource.Namespaced {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(apiResource.Name, "/") {
|
||||
continue
|
||||
}
|
||||
gvr := groupVersion.WithResource(apiResource.Name)
|
||||
if _, ok := resources[gvr.GroupResource()]; ok {
|
||||
if gvr.Version != apiGroup.PreferredVersion.Version {
|
||||
continue
|
||||
}
|
||||
// remove previous entry, because it will be replaced with a preferred one
|
||||
for i := range results {
|
||||
if results[i].GroupResource() == gvr.GroupResource() {
|
||||
results = append(results[:i], results[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
resources[gvr.GroupResource()] = gvr.Version
|
||||
results = append(results, gvr)
|
||||
gv := schema.GroupResource{Group: apiGroup.Name, Resource: apiResource.Name}
|
||||
if _, ok := grApiResources[gv]; ok && version.Version != apiGroup.PreferredVersion.Version {
|
||||
// only override with preferred version
|
||||
continue
|
||||
}
|
||||
grVersions[gv] = version.Version
|
||||
grApiResources[gv] = apiResource
|
||||
}
|
||||
}
|
||||
if len(failedGroups) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
}
|
||||
return results, &ErrGroupDiscoveryFailed{Groups: failedGroups}
|
||||
|
||||
// group selected APIResources according to GroupVersion into APIResourceLists
|
||||
for groupResource, apiResource := range grApiResources {
|
||||
version := grVersions[groupResource]
|
||||
groupVersion := schema.GroupVersion{Group: groupResource.Group, Version: version}
|
||||
apiResourceList := gvApiResourceLists[groupVersion]
|
||||
apiResourceList.APIResources = append(apiResourceList.APIResources, *apiResource)
|
||||
}
|
||||
|
||||
if len(failedGroups) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return result, &ErrGroupDiscoveryFailed{Groups: failedGroups}
|
||||
}
|
||||
|
||||
// ServerPreferredResources returns the supported resources with the version preferred by the
|
||||
// server.
|
||||
func (d *DiscoveryClient) ServerPreferredResources() ([]schema.GroupVersionResource, error) {
|
||||
return d.serverPreferredResources(false)
|
||||
func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||
return withRetries(defaultRetries, func(retryEarly bool) ([]*metav1.APIResourceList, error) {
|
||||
return d.serverPreferredResources(retryEarly)
|
||||
})
|
||||
}
|
||||
|
||||
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
|
||||
// version preferred by the server.
|
||||
func (d *DiscoveryClient) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
return d.serverPreferredResources(true)
|
||||
func (d *DiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
||||
all, err := d.ServerPreferredResources()
|
||||
return FilteredBy(ResourcePredicateFunc(func(groupVersion string, r *metav1.APIResource) bool {
|
||||
return r.Namespaced
|
||||
}), all), err
|
||||
}
|
||||
|
||||
// ServerVersion retrieves and parses the server's version (git version).
|
||||
|
@ -329,6 +363,23 @@ func (d *DiscoveryClient) SwaggerSchema(version schema.GroupVersion) (*swagger.A
|
|||
return &schema, nil
|
||||
}
|
||||
|
||||
// withRetries retries the given recovery function in case the groups supported by the server change after ServerGroup() returns.
|
||||
func withRetries(maxRetries int, f func(failEarly bool) ([]*metav1.APIResourceList, error)) ([]*metav1.APIResourceList, error) {
|
||||
var result []*metav1.APIResourceList
|
||||
var err error
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
failEarly := i < maxRetries-1
|
||||
result, err = f(failEarly)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if _, ok := err.(*ErrGroupDiscoveryFailed); !ok {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func setDiscoveryDefaults(config *restclient.Config) error {
|
||||
config.APIPath = ""
|
||||
config.GroupVersion = nil
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
|
@ -141,14 +142,14 @@ func TestGetServerResourcesWithV1Server(t *testing.T) {
|
|||
defer server.Close()
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
// ServerResources should not return an error even if server returns error at /api/v1.
|
||||
resourceMap, err := client.ServerResources()
|
||||
serverResources, err := client.ServerResources()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if _, found := resourceMap["v1"]; !found {
|
||||
t.Errorf("missing v1 in resource map")
|
||||
gvs := groupVersions(serverResources)
|
||||
if !sets.NewString(gvs...).Has("v1") {
|
||||
t.Errorf("missing v1 in resource list: %v", serverResources)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetServerResources(t *testing.T) {
|
||||
|
@ -161,7 +162,7 @@ func TestGetServerResources(t *testing.T) {
|
|||
},
|
||||
}
|
||||
beta := metav1.APIResourceList{
|
||||
GroupVersion: "extensions/v1",
|
||||
GroupVersion: "extensions/v1beta1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||
{Name: "ingresses", Namespaced: true, Kind: "Ingress"},
|
||||
|
@ -249,13 +250,14 @@ func TestGetServerResources(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
resourceMap, err := client.ServerResources()
|
||||
serverResources, err := client.ServerResources()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
serverGroupVersions := sets.NewString(groupVersions(serverResources)...)
|
||||
for _, api := range []string{"v1", "extensions/v1beta1"} {
|
||||
if _, found := resourceMap[api]; !found {
|
||||
t.Errorf("missing expected api: %s", api)
|
||||
if !serverGroupVersions.Has(api) {
|
||||
t.Errorf("missing expected api %q in %v", api, serverResources)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,12 +334,12 @@ func TestServerPreferredResources(t *testing.T) {
|
|||
},
|
||||
}
|
||||
tests := []struct {
|
||||
resourcesList *metav1.APIResourceList
|
||||
resourcesList []*metav1.APIResourceList
|
||||
response func(w http.ResponseWriter, req *http.Request)
|
||||
expectErr func(err error) bool
|
||||
}{
|
||||
{
|
||||
resourcesList: &stable,
|
||||
resourcesList: []*metav1.APIResourceList{&stable},
|
||||
expectErr: IsGroupDiscoveryFailedError,
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
|
@ -426,7 +428,7 @@ func TestServerPreferredResources(t *testing.T) {
|
|||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.ServerPreferredResources()
|
||||
resources, err := client.ServerPreferredResources()
|
||||
if test.expectErr != nil {
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
|
@ -438,7 +440,13 @@ func TestServerPreferredResources(t *testing.T) {
|
|||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.resourcesList) {
|
||||
got, err := GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
expected, _ := GroupVersionResources(test.resourcesList)
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected:\n%v\ngot:\n%v\n", test.resourcesList, got)
|
||||
}
|
||||
server.Close()
|
||||
|
@ -533,10 +541,14 @@ func TestServerPreferredResourcesRetries(t *testing.T) {
|
|||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.ServerPreferredResources()
|
||||
resources, err := client.ServerPreferredResources()
|
||||
if !tc.expectedError(err) {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
got, err := GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
if len(got) != tc.expectResources {
|
||||
t.Errorf("case %d: expect %d resources, got %#v", i, tc.expectResources, got)
|
||||
}
|
||||
|
@ -575,7 +587,7 @@ func TestServerPreferredNamespacedResources(t *testing.T) {
|
|||
}
|
||||
tests := []struct {
|
||||
response func(w http.ResponseWriter, req *http.Request)
|
||||
expected []schema.GroupVersionResource
|
||||
expected map[schema.GroupVersionResource]struct{}
|
||||
}{
|
||||
{
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
|
@ -603,9 +615,9 @@ func TestServerPreferredNamespacedResources(t *testing.T) {
|
|||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
expected: []schema.GroupVersionResource{
|
||||
{Group: "", Version: "v1", Resource: "pods"},
|
||||
{Group: "", Version: "v1", Resource: "services"},
|
||||
expected: map[schema.GroupVersionResource]struct{}{
|
||||
schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}: {},
|
||||
schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -646,9 +658,9 @@ func TestServerPreferredNamespacedResources(t *testing.T) {
|
|||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
expected: []schema.GroupVersionResource{
|
||||
{Group: "batch", Version: "v1", Resource: "jobs"},
|
||||
{Group: "batch", Version: "v2alpha1", Resource: "cronjobs"},
|
||||
expected: map[schema.GroupVersionResource]struct{}{
|
||||
schema.GroupVersionResource{Group: "batch", Version: "v1", Resource: "jobs"}: {},
|
||||
schema.GroupVersionResource{Group: "batch", Version: "v2alpha1", Resource: "cronjobs"}: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -689,27 +701,39 @@ func TestServerPreferredNamespacedResources(t *testing.T) {
|
|||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
expected: []schema.GroupVersionResource{
|
||||
{Group: "batch", Version: "v2alpha1", Resource: "jobs"},
|
||||
{Group: "batch", Version: "v2alpha1", Resource: "cronjobs"},
|
||||
expected: map[schema.GroupVersionResource]struct{}{
|
||||
schema.GroupVersionResource{Group: "batch", Version: "v2alpha1", Resource: "jobs"}: {},
|
||||
schema.GroupVersionResource{Group: "batch", Version: "v2alpha1", Resource: "cronjobs"}: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
for i, test := range tests {
|
||||
server := httptest.NewServer(http.HandlerFunc(test.response))
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.ServerPreferredNamespacedResources()
|
||||
resources, err := client.ServerPreferredNamespacedResources()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
// we need deterministic order and since during processing in ServerPreferredNamespacedResources
|
||||
// a map comes into play the result needs sorting
|
||||
got, err := GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, test.expected) {
|
||||
t.Errorf("expected:\n%v\ngot:\n%v\n", test.expected, got)
|
||||
t.Errorf("[%d] expected:\n%v\ngot:\n%v\n", i, test.expected, got)
|
||||
}
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func groupVersions(resources []*metav1.APIResourceList) []string {
|
||||
result := []string{}
|
||||
for _, resourceList := range resources {
|
||||
result = append(result, resourceList.GroupVersion)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -17,7 +17,10 @@ limitations under the License.
|
|||
package fake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful/swagger"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
|
@ -36,10 +39,15 @@ func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*me
|
|||
Resource: schema.GroupVersionResource{Resource: "resource"},
|
||||
}
|
||||
c.Invokes(action, nil)
|
||||
return c.Resources[groupVersion], nil
|
||||
for _, resourceList := range c.Resources {
|
||||
if resourceList.GroupVersion == groupVersion {
|
||||
return resourceList, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("GroupVersion %q not found", groupVersion)
|
||||
}
|
||||
|
||||
func (c *FakeDiscovery) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
func (c *FakeDiscovery) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
action := core.ActionImpl{
|
||||
Verb: "get",
|
||||
Resource: schema.GroupVersionResource{Resource: "resource"},
|
||||
|
@ -48,11 +56,11 @@ func (c *FakeDiscovery) ServerResources() (map[string]*metav1.APIResourceList, e
|
|||
return c.Resources, nil
|
||||
}
|
||||
|
||||
func (c *FakeDiscovery) ServerPreferredResources() ([]schema.GroupVersionResource, error) {
|
||||
func (c *FakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *FakeDiscovery) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
func (c *FakeDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -108,3 +108,55 @@ func NegotiateVersion(client DiscoveryInterface, requiredGV *schema.GroupVersion
|
|||
return nil, fmt.Errorf("failed to negotiate an api version; server supports: %v, client supports: %v",
|
||||
serverVersions, clientVersions)
|
||||
}
|
||||
|
||||
// GroupVersionResources converts APIResourceLists to the GroupVersionResources.
|
||||
func GroupVersionResources(rls []*metav1.APIResourceList) (map[schema.GroupVersionResource]struct{}, error) {
|
||||
gvrs := map[schema.GroupVersionResource]struct{}{}
|
||||
for _, rl := range rls {
|
||||
gv, err := schema.ParseGroupVersion(rl.GroupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range rl.APIResources {
|
||||
gvrs[schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: rl.APIResources[i].Name}] = struct{}{}
|
||||
}
|
||||
}
|
||||
return gvrs, nil
|
||||
}
|
||||
|
||||
// FilteredBy filters by the given predicate. Empty APIResourceLists are dropped.
|
||||
func FilteredBy(pred ResourcePredicate, rls []*metav1.APIResourceList) []*metav1.APIResourceList {
|
||||
result := []*metav1.APIResourceList{}
|
||||
for _, rl := range rls {
|
||||
filtered := *rl
|
||||
filtered.APIResources = nil
|
||||
for i := range rl.APIResources {
|
||||
if pred.Match(rl.GroupVersion, &rl.APIResources[i]) {
|
||||
filtered.APIResources = append(filtered.APIResources, rl.APIResources[i])
|
||||
}
|
||||
}
|
||||
if filtered.APIResources != nil {
|
||||
result = append(result, &filtered)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type ResourcePredicate interface {
|
||||
Match(groupVersion string, r *metav1.APIResource) bool
|
||||
}
|
||||
|
||||
type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool
|
||||
|
||||
func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool {
|
||||
return fn(groupVersion, r)
|
||||
}
|
||||
|
||||
// SupportsAllVerbs is a predicate matching a resource iff all given verbs are supported.
|
||||
type SupportsAllVerbs struct {
|
||||
Verbs []string
|
||||
}
|
||||
|
||||
func (p SupportsAllVerbs) Match(groupVersion string, r *metav1.APIResource) bool {
|
||||
return sets.NewString([]string(r.Verbs)...).HasAll(p.Verbs...)
|
||||
}
|
||||
|
|
|
@ -29,12 +29,14 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
uapi "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/restclient/fake"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
func objBody(object interface{}) io.ReadCloser {
|
||||
|
@ -155,3 +157,74 @@ func TestNegotiateVersion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteredBy(t *testing.T) {
|
||||
all := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return true
|
||||
})
|
||||
none := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return false
|
||||
})
|
||||
onlyV2 := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return strings.HasSuffix(gv, "/v2") || gv == "v2"
|
||||
})
|
||||
onlyBar := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return r.Kind == "Bar"
|
||||
})
|
||||
|
||||
foo := []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "foo/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "bar", Kind: "Bar"},
|
||||
{Name: "test", Kind: "Test"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "foo/v2",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "bar", Kind: "Bar"},
|
||||
{Name: "test", Kind: "Test"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "foo/v3",
|
||||
APIResources: []metav1.APIResource{},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
input []*metav1.APIResourceList
|
||||
pred discovery.ResourcePredicate
|
||||
expectedResources []string
|
||||
}{
|
||||
{nil, all, []string{}},
|
||||
{[]*metav1.APIResourceList{
|
||||
{GroupVersion: "foo/v1"},
|
||||
}, all, []string{}},
|
||||
{foo, all, []string{"foo/v1.bar", "foo/v1.test", "foo/v2.bar", "foo/v2.test"}},
|
||||
{foo, onlyV2, []string{"foo/v2.bar", "foo/v2.test"}},
|
||||
{foo, onlyBar, []string{"foo/v1.bar", "foo/v2.bar"}},
|
||||
{foo, none, []string{}},
|
||||
}
|
||||
for i, test := range tests {
|
||||
filtered := discovery.FilteredBy(test.pred, test.input)
|
||||
|
||||
if expected, got := sets.NewString(test.expectedResources...), sets.NewString(stringify(filtered)...); !expected.Equal(got) {
|
||||
t.Errorf("[%d] unexpected group versions: expected=%v, got=%v", i, test.expectedResources, stringify(filtered))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stringify(rls []*metav1.APIResourceList) []string {
|
||||
result := []string{}
|
||||
for _, rl := range rls {
|
||||
for _, r := range rl.APIResources {
|
||||
result = append(result, rl.GroupVersion+"."+r.Name)
|
||||
}
|
||||
if len(rl.APIResources) == 0 {
|
||||
result = append(result, rl.GroupVersion)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -290,31 +290,34 @@ func (c *fakeCachedDiscoveryInterface) ServerResourcesForGroupVersion(groupVersi
|
|||
return nil, errors.NewNotFound(schema.GroupResource{}, "")
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
func (c *fakeCachedDiscoveryInterface) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
if c.enabledA {
|
||||
av1, _ := c.ServerResourcesForGroupVersion("a/v1")
|
||||
return map[string]*metav1.APIResourceList{
|
||||
"a/v1": av1,
|
||||
}, nil
|
||||
return []*metav1.APIResourceList{av1}, nil
|
||||
}
|
||||
return map[string]*metav1.APIResourceList{}, nil
|
||||
return []*metav1.APIResourceList{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerPreferredResources() ([]schema.GroupVersionResource, error) {
|
||||
func (c *fakeCachedDiscoveryInterface) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||
if c.enabledA {
|
||||
return []schema.GroupVersionResource{
|
||||
return []*metav1.APIResourceList{
|
||||
{
|
||||
Group: "a",
|
||||
Version: "v1",
|
||||
Resource: "foo",
|
||||
GroupVersion: "a/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Name: "foo",
|
||||
Kind: "Foo",
|
||||
Verbs: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return []schema.GroupVersionResource{}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
return []schema.GroupVersionResource{}, nil
|
||||
func (c *fakeCachedDiscoveryInterface) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerVersion() (*version.Info, error) {
|
||||
|
|
|
@ -537,7 +537,7 @@ var ignoredResources = map[schema.GroupVersionResource]struct{}{
|
|||
schema.GroupVersionResource{Group: "authorization.k8s.io", Version: "v1beta1", Resource: "localsubjectaccessreviews"}: {},
|
||||
}
|
||||
|
||||
func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynamic.ClientPool, mapper meta.RESTMapper, resources []schema.GroupVersionResource) (*GarbageCollector, error) {
|
||||
func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynamic.ClientPool, mapper meta.RESTMapper, deletableResources map[schema.GroupVersionResource]struct{}) (*GarbageCollector, error) {
|
||||
gc := &GarbageCollector{
|
||||
metaOnlyClientPool: metaOnlyClientPool,
|
||||
clientPool: clientPool,
|
||||
|
@ -545,8 +545,8 @@ func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynam
|
|||
clock: clock.RealClock{},
|
||||
dirtyQueue: workqueue.NewTimedWorkQueue(),
|
||||
orphanQueue: workqueue.NewTimedWorkQueue(),
|
||||
registeredRateLimiter: NewRegisteredRateLimiter(resources),
|
||||
registeredRateLimiterForMonitors: NewRegisteredRateLimiter(resources),
|
||||
registeredRateLimiter: NewRegisteredRateLimiter(deletableResources),
|
||||
registeredRateLimiterForMonitors: NewRegisteredRateLimiter(deletableResources),
|
||||
absentOwnerCache: NewUIDCache(500),
|
||||
}
|
||||
gc.propagator = &Propagator{
|
||||
|
@ -557,7 +557,7 @@ func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynam
|
|||
},
|
||||
gc: gc,
|
||||
}
|
||||
for _, resource := range resources {
|
||||
for resource := range deletableResources {
|
||||
if _, ok := ignoredResources[resource]; ok {
|
||||
glog.V(6).Infof("ignore resource %#v", resource)
|
||||
continue
|
||||
|
|
|
@ -49,7 +49,7 @@ func TestNewGarbageCollector(t *testing.T) {
|
|||
metaOnlyClientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
config.ContentConfig.NegotiatedSerializer = nil
|
||||
clientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
podResource := []schema.GroupVersionResource{{Version: "v1", Resource: "pods"}}
|
||||
podResource := map[schema.GroupVersionResource]struct{}{schema.GroupVersionResource{Version: "v1", Resource: "pods"}: {}}
|
||||
gc, err := NewGarbageCollector(metaOnlyClientPool, clientPool, registered.RESTMapper(), podResource)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -113,7 +113,7 @@ func setupGC(t *testing.T, config *restclient.Config) *GarbageCollector {
|
|||
metaOnlyClientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
config.ContentConfig.NegotiatedSerializer = nil
|
||||
clientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
podResource := []schema.GroupVersionResource{{Version: "v1", Resource: "pods"}}
|
||||
podResource := map[schema.GroupVersionResource]struct{}{schema.GroupVersionResource{Version: "v1", Resource: "pods"}: {}}
|
||||
gc, err := NewGarbageCollector(metaOnlyClientPool, clientPool, registered.RESTMapper(), podResource)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -35,9 +35,9 @@ type RegisteredRateLimiter struct {
|
|||
// NewRegisteredRateLimiter returns a new RegisteredRateLimiater.
|
||||
// TODO: NewRegisteredRateLimiter is not dynamic. We need to find a better way
|
||||
// when GC dynamically change the resources it monitors.
|
||||
func NewRegisteredRateLimiter(resources []schema.GroupVersionResource) *RegisteredRateLimiter {
|
||||
func NewRegisteredRateLimiter(resources map[schema.GroupVersionResource]struct{}) *RegisteredRateLimiter {
|
||||
rateLimiters := make(map[schema.GroupVersion]*sync.Once)
|
||||
for _, resource := range resources {
|
||||
for resource := range resources {
|
||||
gv := resource.GroupVersion()
|
||||
if _, found := rateLimiters[gv]; !found {
|
||||
rateLimiters[gv] = &sync.Once{}
|
||||
|
|
|
@ -24,6 +24,7 @@ go_library(
|
|||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/cache:go_default_library",
|
||||
"//pkg/client/clientset_generated/release_1_5:go_default_library",
|
||||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/client/typed/dynamic:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
|
@ -53,6 +54,7 @@ go_test(
|
|||
"//pkg/client/clientset_generated/release_1_5/fake:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/testing/core:go_default_library",
|
||||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/client/typed/dynamic:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/schema:go_default_library",
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||
"k8s.io/kubernetes/pkg/client/typed/dynamic"
|
||||
|
@ -28,6 +29,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/util/metrics"
|
||||
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/util/workqueue"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
|
@ -57,8 +59,8 @@ type NamespaceController struct {
|
|||
controller *cache.Controller
|
||||
// namespaces that have been queued up for processing by workers
|
||||
queue workqueue.RateLimitingInterface
|
||||
// function to list of preferred group versions and their corresponding resource set for namespace deletion
|
||||
groupVersionResourcesFn func() ([]schema.GroupVersionResource, error)
|
||||
// function to list of preferred resources for namespace deletion
|
||||
discoverResourcesFn func() ([]*metav1.APIResourceList, error)
|
||||
// opCache is a cache to remember if a particular operation is not supported to aid dynamic client.
|
||||
opCache *operationNotSupportedCache
|
||||
// finalizerToken is the finalizer token managed by this controller
|
||||
|
@ -69,36 +71,55 @@ type NamespaceController struct {
|
|||
func NewNamespaceController(
|
||||
kubeClient clientset.Interface,
|
||||
clientPool dynamic.ClientPool,
|
||||
groupVersionResourcesFn func() ([]schema.GroupVersionResource, error),
|
||||
discoverResourcesFn func() ([]*metav1.APIResourceList, error),
|
||||
resyncPeriod time.Duration,
|
||||
finalizerToken v1.FinalizerName) *NamespaceController {
|
||||
|
||||
// the namespace deletion code looks at the discovery document to enumerate the set of resources on the server.
|
||||
// it then finds all namespaced resources, and in response to namespace deletion, will call delete on all of them.
|
||||
// unfortunately, the discovery information does not include the list of supported verbs/methods. if the namespace
|
||||
// controller calls LIST/DELETECOLLECTION for a resource, it will get a 405 error from the server and cache that that was the case.
|
||||
// we found in practice though that some auth engines when encountering paths they don't know about may return a 50x.
|
||||
// until we have verbs, we pre-populate resources that do not support list or delete for well-known apis rather than
|
||||
// probing the server once in order to be told no.
|
||||
opCache := &operationNotSupportedCache{
|
||||
m: make(map[operationKey]bool),
|
||||
}
|
||||
ignoredGroupVersionResources := []schema.GroupVersionResource{
|
||||
{Group: "", Version: "v1", Resource: "bindings"},
|
||||
|
||||
// pre-fill opCache with the discovery info
|
||||
//
|
||||
// TODO(sttts): get rid of opCache and http 405 logic around it and trust discovery info
|
||||
resources, err := discoverResourcesFn()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to get supported resources: %v", err)
|
||||
}
|
||||
for _, ignoredGroupVersionResource := range ignoredGroupVersionResources {
|
||||
opCache.setNotSupported(operationKey{op: operationDeleteCollection, gvr: ignoredGroupVersionResource})
|
||||
opCache.setNotSupported(operationKey{op: operationList, gvr: ignoredGroupVersionResource})
|
||||
deletableGroupVersionResources := []schema.GroupVersionResource{}
|
||||
for _, rl := range resources {
|
||||
gv, err := schema.ParseGroupVersion(rl.GroupVersion)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to parse GroupVersion %q, skipping: %v", rl.GroupVersion, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, r := range rl.APIResources {
|
||||
gvr := schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: r.Name}
|
||||
verbs := sets.NewString([]string(r.Verbs)...)
|
||||
|
||||
if !verbs.Has("delete") {
|
||||
glog.V(6).Infof("Skipping resource %v because it cannot be deleted.", gvr)
|
||||
}
|
||||
|
||||
for _, op := range []operation{operationList, operationDeleteCollection} {
|
||||
if !verbs.Has(string(op)) {
|
||||
opCache.setNotSupported(operationKey{op: op, gvr: gvr})
|
||||
}
|
||||
}
|
||||
|
||||
deletableGroupVersionResources = append(deletableGroupVersionResources, gvr)
|
||||
}
|
||||
}
|
||||
|
||||
// create the controller so we can inject the enqueue function
|
||||
namespaceController := &NamespaceController{
|
||||
kubeClient: kubeClient,
|
||||
clientPool: clientPool,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
|
||||
groupVersionResourcesFn: groupVersionResourcesFn,
|
||||
opCache: opCache,
|
||||
finalizerToken: finalizerToken,
|
||||
kubeClient: kubeClient,
|
||||
clientPool: clientPool,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
|
||||
discoverResourcesFn: discoverResourcesFn,
|
||||
opCache: opCache,
|
||||
finalizerToken: finalizerToken,
|
||||
}
|
||||
|
||||
if kubeClient != nil && kubeClient.Core().RESTClient().GetRateLimiter() != nil {
|
||||
|
@ -203,7 +224,7 @@ func (nm *NamespaceController) syncNamespaceFromKey(key string) (err error) {
|
|||
return err
|
||||
}
|
||||
namespace := obj.(*v1.Namespace)
|
||||
return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.groupVersionResourcesFn, namespace, nm.finalizerToken)
|
||||
return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.discoverResourcesFn, namespace, nm.finalizerToken)
|
||||
}
|
||||
|
||||
// Run starts observing the system with the specified number of workers.
|
||||
|
|
|
@ -34,6 +34,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/fake"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/testing/core"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/client/typed/dynamic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
|
@ -113,8 +114,9 @@ func testSyncNamespaceThatIsTerminating(t *testing.T, versions *metav1.APIVersio
|
|||
|
||||
// when doing a delete all of content, we will do a GET of a collection, and DELETE of a collection by default
|
||||
dynamicClientActionSet := sets.NewString()
|
||||
groupVersionResources := testGroupVersionResources()
|
||||
for _, groupVersionResource := range groupVersionResources {
|
||||
resources := testResources()
|
||||
groupVersionResources, _ := discovery.GroupVersionResources(resources)
|
||||
for groupVersionResource := range groupVersionResources {
|
||||
urlPath := path.Join([]string{
|
||||
dynamic.LegacyAPIPathResolverFunc(schema.GroupVersionKind{Group: groupVersionResource.Group, Version: groupVersionResource.Version}),
|
||||
groupVersionResource.Group,
|
||||
|
@ -170,8 +172,8 @@ func testSyncNamespaceThatIsTerminating(t *testing.T, versions *metav1.APIVersio
|
|||
mockClient := fake.NewSimpleClientset(testInput.testNamespace)
|
||||
clientPool := dynamic.NewClientPool(clientConfig, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
|
||||
fn := func() ([]schema.GroupVersionResource, error) {
|
||||
return groupVersionResources, nil
|
||||
fn := func() ([]*metav1.APIResourceList, error) {
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
err := syncNamespace(mockClient, clientPool, &operationNotSupportedCache{m: make(map[operationKey]bool)}, fn, testInput.testNamespace, v1.FinalizerKubernetes)
|
||||
|
@ -243,8 +245,8 @@ func TestSyncNamespaceThatIsActive(t *testing.T) {
|
|||
Phase: v1.NamespaceActive,
|
||||
},
|
||||
}
|
||||
fn := func() ([]schema.GroupVersionResource, error) {
|
||||
return testGroupVersionResources(), nil
|
||||
fn := func() ([]*metav1.APIResourceList, error) {
|
||||
return testResources(), nil
|
||||
}
|
||||
err := syncNamespace(mockClient, nil, &operationNotSupportedCache{m: make(map[operationKey]bool)}, fn, testNamespace, v1.FinalizerKubernetes)
|
||||
if err != nil {
|
||||
|
@ -295,11 +297,37 @@ func (f *fakeActionHandler) ServeHTTP(response http.ResponseWriter, request *htt
|
|||
response.Write([]byte("{\"kind\": \"List\",\"items\":null}"))
|
||||
}
|
||||
|
||||
// testGroupVersionResources returns a mocked up set of resources across different api groups for testing namespace controller.
|
||||
func testGroupVersionResources() []schema.GroupVersionResource {
|
||||
results := []schema.GroupVersionResource{}
|
||||
results = append(results, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"})
|
||||
results = append(results, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"})
|
||||
results = append(results, schema.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "deployments"})
|
||||
// testResources returns a mocked up set of resources across different api groups for testing namespace controller.
|
||||
func testResources() []*metav1.APIResourceList {
|
||||
results := []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Name: "pods",
|
||||
Namespaced: true,
|
||||
Kind: "Pod",
|
||||
Verbs: []string{"get", "list", "delete", "deletecollection", "create", "update"},
|
||||
},
|
||||
{
|
||||
Name: "services",
|
||||
Namespaced: true,
|
||||
Kind: "Service",
|
||||
Verbs: []string{"get", "list", "delete", "deletecollection", "create", "update"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "extensions/v1beta1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Name: "deployments",
|
||||
Namespaced: true,
|
||||
Kind: "Deployment",
|
||||
Verbs: []string{"get", "list", "delete", "deletecollection", "create", "update"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package namespace
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -26,6 +25,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/client/typed/dynamic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
|
@ -344,16 +344,13 @@ func deleteAllContent(
|
|||
kubeClient clientset.Interface,
|
||||
clientPool dynamic.ClientPool,
|
||||
opCache *operationNotSupportedCache,
|
||||
groupVersionResources []schema.GroupVersionResource,
|
||||
groupVersionResources map[schema.GroupVersionResource]struct{},
|
||||
namespace string,
|
||||
namespaceDeletedAt metav1.Time,
|
||||
) (int64, error) {
|
||||
estimate := int64(0)
|
||||
glog.V(4).Infof("namespace controller - deleteAllContent - namespace: %s, gvrs: %v", namespace, groupVersionResources)
|
||||
// iterate over each group version, and attempt to delete all of its resources
|
||||
// we sort resources to delete in a priority order that deletes pods LAST
|
||||
sort.Sort(sortableGroupVersionResources(groupVersionResources))
|
||||
for _, gvr := range groupVersionResources {
|
||||
for gvr := range groupVersionResources {
|
||||
gvrEstimate, err := deleteAllContentForGroupVersionResource(kubeClient, clientPool, opCache, gvr, namespace, namespaceDeletedAt)
|
||||
if err != nil {
|
||||
return estimate, err
|
||||
|
@ -371,7 +368,7 @@ func syncNamespace(
|
|||
kubeClient clientset.Interface,
|
||||
clientPool dynamic.ClientPool,
|
||||
opCache *operationNotSupportedCache,
|
||||
groupVersionResourcesFn func() ([]schema.GroupVersionResource, error),
|
||||
discoverResourcesFn func() ([]*metav1.APIResourceList, error),
|
||||
namespace *v1.Namespace,
|
||||
finalizerToken v1.FinalizerName,
|
||||
) error {
|
||||
|
@ -422,7 +419,13 @@ func syncNamespace(
|
|||
}
|
||||
|
||||
// there may still be content for us to remove
|
||||
groupVersionResources, err := groupVersionResourcesFn()
|
||||
resources, err := discoverResourcesFn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(sttts): get rid of opCache and pass the verbs (especially "deletecollection") down into the deleter
|
||||
deletableResources := discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"delete"}}, resources)
|
||||
groupVersionResources, err := discovery.GroupVersionResources(deletableResources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -502,20 +505,3 @@ func estimateGracefulTerminationForPods(kubeClient clientset.Interface, ns strin
|
|||
}
|
||||
return estimate, nil
|
||||
}
|
||||
|
||||
// sortableGroupVersionResources sorts the input set of resources for deletion, and orders pods to always be last.
|
||||
// the idea is that the namespace controller will delete all things that spawn pods first in order to reduce the time
|
||||
// those controllers spend creating pods only to be told NO in admission and potentially overwhelming cluster especially if they lack rate limiting.
|
||||
type sortableGroupVersionResources []schema.GroupVersionResource
|
||||
|
||||
func (list sortableGroupVersionResources) Len() int {
|
||||
return len(list)
|
||||
}
|
||||
|
||||
func (list sortableGroupVersionResources) Swap(i, j int) {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
func (list sortableGroupVersionResources) Less(i, j int) bool {
|
||||
return list[j].Group == "" && list[j].Resource == "pods"
|
||||
}
|
||||
|
|
|
@ -5968,8 +5968,22 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"verbs": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"name", "namespaced", "kind"},
|
||||
Required: []string{"name", "namespaced", "kind", "verbs"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
|
|
|
@ -97,9 +97,11 @@ go_test(
|
|||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/meta:go_default_library",
|
||||
"//pkg/api/rest:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apimachinery:go_default_library",
|
||||
"//pkg/apimachinery/registered:go_default_library",
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
|
@ -109,6 +111,7 @@ go_test(
|
|||
"//pkg/auth/user:go_default_library",
|
||||
"//pkg/generated/openapi:go_default_library",
|
||||
"//pkg/genericapiserver/options:go_default_library",
|
||||
"//pkg/runtime:go_default_library",
|
||||
"//pkg/runtime/schema:go_default_library",
|
||||
"//pkg/storage/etcd/testing:go_default_library",
|
||||
"//pkg/storage/storagebackend:go_default_library",
|
||||
|
|
|
@ -28,14 +28,18 @@ import (
|
|||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apimachinery"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
openapigen "k8s.io/kubernetes/pkg/generated/openapi"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
|
@ -99,52 +103,165 @@ func TestInstallAPIGroups(t *testing.T) {
|
|||
defer etcdserver.Terminate(t)
|
||||
|
||||
config.LegacyAPIGroupPrefixes = sets.NewString("/apiPrefix")
|
||||
config.DiscoveryAddresses = DefaultDiscoveryAddresses{DefaultAddress: "ExternalAddress"}
|
||||
|
||||
s, err := config.SkipComplete().New()
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the server: %v", err)
|
||||
}
|
||||
|
||||
apiGroupMeta := registered.GroupOrDie(api.GroupName)
|
||||
extensionsGroupMeta := registered.GroupOrDie(extensions.GroupName)
|
||||
s.InstallLegacyAPIGroup("/apiPrefix", &APIGroupInfo{
|
||||
// legacy group version
|
||||
GroupMeta: *apiGroupMeta,
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
|
||||
ParameterCodec: api.ParameterCodec,
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
})
|
||||
testAPI := func(gv schema.GroupVersion) APIGroupInfo {
|
||||
getter, noVerbs := testGetterStorage{}, testNoVerbsStorage{}
|
||||
|
||||
apiGroupsInfo := []APIGroupInfo{
|
||||
{
|
||||
// extensions group version
|
||||
GroupMeta: *extensionsGroupMeta,
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
|
||||
OptionsExternalVersion: &apiGroupMeta.GroupVersion,
|
||||
ParameterCodec: api.ParameterCodec,
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
},
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(gv.WithKind("Getter"), getter.New())
|
||||
scheme.AddKnownTypeWithName(gv.WithKind("NoVerb"), noVerbs.New())
|
||||
scheme.AddKnownTypes(v1.SchemeGroupVersion,
|
||||
&v1.ListOptions{},
|
||||
&v1.DeleteOptions{},
|
||||
&metav1.ExportOptions{},
|
||||
&metav1.Status{},
|
||||
)
|
||||
|
||||
interfacesFor := func(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
|
||||
return &meta.VersionInterfaces{
|
||||
ObjectConvertor: scheme,
|
||||
MetadataAccessor: meta.NewAccessor(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
mapper := api.NewDefaultRESTMapperFromScheme([]schema.GroupVersion{gv}, interfacesFor, "", sets.NewString(), sets.NewString(), scheme)
|
||||
groupMeta := apimachinery.GroupMeta{
|
||||
GroupVersion: gv,
|
||||
GroupVersions: []schema.GroupVersion{gv},
|
||||
RESTMapper: mapper,
|
||||
InterfacesFor: interfacesFor,
|
||||
}
|
||||
|
||||
return APIGroupInfo{
|
||||
GroupMeta: groupMeta,
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{
|
||||
gv.Version: {
|
||||
"getter": &testGetterStorage{Version: gv.Version},
|
||||
"noverbs": &testNoVerbsStorage{Version: gv.Version},
|
||||
},
|
||||
},
|
||||
OptionsExternalVersion: &schema.GroupVersion{Version: "v1"},
|
||||
ParameterCodec: api.ParameterCodec,
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
Scheme: scheme,
|
||||
}
|
||||
}
|
||||
for i := range apiGroupsInfo {
|
||||
s.InstallAPIGroup(&apiGroupsInfo[i])
|
||||
|
||||
apis := []APIGroupInfo{
|
||||
testAPI(schema.GroupVersion{Group: "", Version: "v1"}),
|
||||
testAPI(schema.GroupVersion{Group: "extensions", Version: "v1"}),
|
||||
testAPI(schema.GroupVersion{Group: "batch", Version: "v1"}),
|
||||
}
|
||||
|
||||
err = s.InstallLegacyAPIGroup("/apiPrefix", &apis[0])
|
||||
assert.NoError(err)
|
||||
groupPaths := []string{
|
||||
config.LegacyAPIGroupPrefixes.List()[0], // /apiPrefix
|
||||
}
|
||||
for _, api := range apis[1:] {
|
||||
err = s.InstallAPIGroup(&api)
|
||||
assert.NoError(err)
|
||||
groupPaths = append(groupPaths, APIGroupPrefix+"/"+api.GroupMeta.GroupVersion.Group) // /apis/<group>
|
||||
}
|
||||
|
||||
server := httptest.NewServer(s.InsecureHandler)
|
||||
defer server.Close()
|
||||
validPaths := []string{
|
||||
// "/api"
|
||||
config.LegacyAPIGroupPrefixes.List()[0],
|
||||
// "/api/v1"
|
||||
config.LegacyAPIGroupPrefixes.List()[0] + "/" + apiGroupMeta.GroupVersion.Version,
|
||||
// "/apis/extensions"
|
||||
APIGroupPrefix + "/" + extensionsGroupMeta.GroupVersion.Group,
|
||||
// "/apis/extensions/v1beta1"
|
||||
APIGroupPrefix + "/" + extensionsGroupMeta.GroupVersion.String(),
|
||||
}
|
||||
for _, path := range validPaths {
|
||||
_, err := http.Get(server.URL + path)
|
||||
if !assert.NoError(err) {
|
||||
t.Errorf("unexpected error: %v, for path: %s", err, path)
|
||||
|
||||
for i := range apis {
|
||||
// should serve APIGroup at group path
|
||||
info := &apis[i]
|
||||
path := groupPaths[i]
|
||||
resp, err := http.Get(server.URL + path)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error getting path %q path: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error reading body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("[%d] json at %s: %s", i, path, string(body))
|
||||
|
||||
if i == 0 {
|
||||
// legacy API returns APIVersions
|
||||
group := metav1.APIVersions{}
|
||||
err = json.Unmarshal(body, &group)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// API groups return APIGroup
|
||||
group := metav1.APIGroup{}
|
||||
err = json.Unmarshal(body, &group)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if got, expected := group.Name, info.GroupMeta.GroupVersion.Group; got != expected {
|
||||
t.Errorf("[%d] unexpected group name at path %q: got=%q expected=%q", i, path, got, expected)
|
||||
continue
|
||||
}
|
||||
|
||||
if got, expected := group.PreferredVersion.Version, info.GroupMeta.GroupVersion.Version; got != expected {
|
||||
t.Errorf("[%d] unexpected group version at path %q: got=%q expected=%q", i, path, got, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// should serve APIResourceList at group path + /<group-version>
|
||||
path = path + "/" + info.GroupMeta.GroupVersion.Version
|
||||
resp, err = http.Get(server.URL + path)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error getting path %q path: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error reading body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("[%d] json at %s: %s", i, path, string(body))
|
||||
|
||||
resources := metav1.APIResourceList{}
|
||||
err = json.Unmarshal(body, &resources)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if got, expected := resources.GroupVersion, info.GroupMeta.GroupVersion.String(); got != expected {
|
||||
t.Errorf("[%d] unexpected groupVersion at path %q: got=%q expected=%q", i, path, got, expected)
|
||||
continue
|
||||
}
|
||||
|
||||
// the verbs should match the features of resources
|
||||
for _, r := range resources.APIResources {
|
||||
switch r.Name {
|
||||
case "getter":
|
||||
if got, expected := sets.NewString([]string(r.Verbs)...), sets.NewString("get"); !got.Equal(expected) {
|
||||
t.Errorf("[%d] unexpected verbs for resource %s/%s: got=%v expected=%v", i, resources.GroupVersion, r.Name, got, expected)
|
||||
}
|
||||
case "noverbs":
|
||||
if r.Verbs == nil {
|
||||
t.Errorf("[%d] unexpected nil verbs slice. Expected: []string{}", i)
|
||||
}
|
||||
if got, expected := sets.NewString([]string(r.Verbs)...), sets.NewString(); !got.Equal(expected) {
|
||||
t.Errorf("[%d] unexpected verbs for resource %s/%s: got=%v expected=%v", i, resources.GroupVersion, r.Name, got, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,3 +579,33 @@ func TestGetServerAddressByClientCIDRs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testGetterStorage struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
func (p *testGetterStorage) New() runtime.Object {
|
||||
return &metav1.APIGroup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Getter",
|
||||
APIVersion: p.Version,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *testGetterStorage) Get(ctx api.Context, name string) (runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type testNoVerbsStorage struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
func (p *testNoVerbsStorage) New() runtime.Object {
|
||||
return &metav1.APIGroup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "NoVerbs",
|
||||
APIVersion: p.Version,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ go_library(
|
|||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
|
||||
"//pkg/client/restclient:go_default_library",
|
||||
"//pkg/client/typed/discovery:go_default_library",
|
||||
"//pkg/client/unversioned:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd:go_default_library",
|
||||
"//pkg/client/unversioned/portforward:go_default_library",
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
conditions "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
|
@ -367,20 +368,11 @@ func Run(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobr
|
|||
}
|
||||
|
||||
// TODO turn this into reusable method checking available resources
|
||||
func contains(resourcesList map[string]*metav1.APIResourceList, resource schema.GroupVersionResource) bool {
|
||||
if resourcesList == nil {
|
||||
return false
|
||||
}
|
||||
resourcesGroup, ok := resourcesList[resource.GroupVersion().String()]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, item := range resourcesGroup.APIResources {
|
||||
if resource.Resource == item.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
func contains(resourcesList []*metav1.APIResourceList, resource schema.GroupVersionResource) bool {
|
||||
resources := discovery.FilteredBy(discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return resource.GroupVersion().String() == gv && resource.Resource == r.Name
|
||||
}), resourcesList)
|
||||
return len(resources) != 0
|
||||
}
|
||||
|
||||
// waitForPod watches the given pod until the exitCondition is true. Each two seconds
|
||||
|
|
|
@ -86,19 +86,19 @@ func (d *CachedDiscoveryClient) ServerResourcesForGroupVersion(groupVersion stri
|
|||
}
|
||||
|
||||
// ServerResources returns the supported resources for all groups and versions.
|
||||
func (d *CachedDiscoveryClient) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
func (d *CachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
apiGroups, err := d.ServerGroups()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupVersions := metav1.ExtractGroupVersions(apiGroups)
|
||||
result := map[string]*metav1.APIResourceList{}
|
||||
result := []*metav1.APIResourceList{}
|
||||
for _, groupVersion := range groupVersions {
|
||||
resources, err := d.ServerResourcesForGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[groupVersion] = resources
|
||||
result = append(result, resources)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
@ -209,11 +209,11 @@ func (d *CachedDiscoveryClient) RESTClient() restclient.Interface {
|
|||
return d.delegate.RESTClient()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]schema.GroupVersionResource, error) {
|
||||
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||
return d.delegate.ServerPreferredResources()
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
||||
return d.delegate.ServerPreferredNamespacedResources()
|
||||
}
|
||||
|
||||
|
|
|
@ -139,19 +139,19 @@ func (c *fakeDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string
|
|||
return nil, errors.NewNotFound(schema.GroupResource{}, "")
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerResources() (map[string]*metav1.APIResourceList, error) {
|
||||
func (c *fakeDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
return map[string]*metav1.APIResourceList{}, nil
|
||||
return []*metav1.APIResourceList{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerPreferredResources() ([]schema.GroupVersionResource, error) {
|
||||
func (c *fakeDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
return []schema.GroupVersionResource{}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerPreferredNamespacedResources() ([]schema.GroupVersionResource, error) {
|
||||
func (c *fakeDiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
||||
c.resourceCalls = c.resourceCalls + 1
|
||||
return []schema.GroupVersionResource{}, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) ServerVersion() (*version.Info, error) {
|
||||
|
|
|
@ -51,21 +51,14 @@ func (e ShortcutExpander) getAll() []schema.GroupResource {
|
|||
return e.All
|
||||
}
|
||||
|
||||
availableResources := []schema.GroupVersionResource{}
|
||||
for groupVersionString, resourceList := range apiResources {
|
||||
currVersion, err := schema.ParseGroupVersion(groupVersionString)
|
||||
if err != nil {
|
||||
return e.All
|
||||
}
|
||||
|
||||
for _, resource := range resourceList.APIResources {
|
||||
availableResources = append(availableResources, currVersion.WithResource(resource.Name))
|
||||
}
|
||||
availableResources, err := discovery.GroupVersionResources(apiResources)
|
||||
if err != nil {
|
||||
return e.All
|
||||
}
|
||||
|
||||
availableAll := []schema.GroupResource{}
|
||||
for _, requestedResource := range e.All {
|
||||
for _, availableResource := range availableResources {
|
||||
for availableResource := range availableResources {
|
||||
if requestedResource.Group == availableResource.Group &&
|
||||
requestedResource.Resource == availableResource.Resource {
|
||||
availableAll = append(availableAll, requestedResource)
|
||||
|
|
|
@ -1084,7 +1084,11 @@ func hasRemainingContent(c clientset.Interface, clientPool dynamic.ClientPool, n
|
|||
}
|
||||
|
||||
// find out what content is supported on the server
|
||||
groupVersionResources, err := c.Discovery().ServerPreferredNamespacedResources()
|
||||
resources, err := c.Discovery().ServerPreferredNamespacedResources()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
groupVersionResources, err := discovery.GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -1095,7 +1099,7 @@ func hasRemainingContent(c clientset.Interface, clientPool dynamic.ClientPool, n
|
|||
contentRemaining := false
|
||||
|
||||
// dump how many of resource type is on the server in a log.
|
||||
for _, gvr := range groupVersionResources {
|
||||
for gvr := range groupVersionResources {
|
||||
// get a client for this group version...
|
||||
dynamicClient, err := clientPool.ClientForGroupVersionResource(gvr)
|
||||
if err != nil {
|
||||
|
|
|
@ -56,8 +56,8 @@ func (n *NamespaceController) Start() error {
|
|||
return err
|
||||
}
|
||||
clientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
gvrFn := client.Discovery().ServerPreferredNamespacedResources
|
||||
nc := namespacecontroller.NewNamespaceController(client, clientPool, gvrFn, ncResyncPeriod, v1.FinalizerKubernetes)
|
||||
discoverResourcesFn := client.Discovery().ServerPreferredNamespacedResources
|
||||
nc := namespacecontroller.NewNamespaceController(client, clientPool, discoverResourcesFn, ncResyncPeriod, v1.FinalizerKubernetes)
|
||||
go nc.Run(ncConcurrency, n.stopCh)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,12 +29,14 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/client/typed/dynamic"
|
||||
"k8s.io/kubernetes/pkg/controller/garbagecollector"
|
||||
"k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly"
|
||||
|
@ -128,16 +130,21 @@ func setup(t *testing.T) (*httptest.Server, *garbagecollector.GarbageCollector,
|
|||
if err != nil {
|
||||
t.Fatalf("Error in create clientset: %v", err)
|
||||
}
|
||||
groupVersionResources, err := clientSet.Discovery().ServerPreferredResources()
|
||||
preferredResources, err := clientSet.Discovery().ServerPreferredResources()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get supported resources from server: %v", err)
|
||||
}
|
||||
deletableResources := discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"delete"}}, preferredResources)
|
||||
deletableGroupVersionResources, err := discovery.GroupVersionResources(deletableResources)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse supported resources from server: %v", err)
|
||||
}
|
||||
config := &restclient.Config{Host: s.URL}
|
||||
config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()}
|
||||
metaOnlyClientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
config.ContentConfig.NegotiatedSerializer = nil
|
||||
clientPool := dynamic.NewClientPool(config, registered.RESTMapper(), dynamic.LegacyAPIPathResolverFunc)
|
||||
gc, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, registered.RESTMapper(), groupVersionResources)
|
||||
gc, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, registered.RESTMapper(), deletableGroupVersionResources)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create garbage collector")
|
||||
}
|
||||
|
|
|
@ -571,6 +571,7 @@ k8s.io/kubernetes/pkg/apis/componentconfig,jbeda,1
|
|||
k8s.io/kubernetes/pkg/apis/extensions,bgrant0607,1
|
||||
k8s.io/kubernetes/pkg/apis/extensions/v1beta1,madhusudancs,1
|
||||
k8s.io/kubernetes/pkg/apis/extensions/validation,nikhiljindal,1
|
||||
k8s.io/kubernetes/pkg/apis/meta/v1,sttts,0
|
||||
k8s.io/kubernetes/pkg/apis/policy/validation,deads2k,1
|
||||
k8s.io/kubernetes/pkg/apis/rbac/validation,erictune,0
|
||||
k8s.io/kubernetes/pkg/apis/storage/validation,caesarxuchao,1
|
||||
|
|
|
Loading…
Reference in New Issue