diff --git a/README.md b/README.md index 4731a2e8..2794f485 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,9 @@ You can also load your JSONified config in from an ENV variable (e.g. `KUBE_CONF ``` Kubeclient::Config.new(JSON.parse(ENV['KUBE_CONFIG']), nil) ``` +###Supported kubernetes versions + +For 1.1 only the core api v1 is supported, all api groups are supported in later versions. ## Examples: diff --git a/lib/kubeclient.rb b/lib/kubeclient.rb index 35209e92..eaa61d7e 100644 --- a/lib/kubeclient.rb +++ b/lib/kubeclient.rb @@ -7,6 +7,7 @@ require 'kubeclient/watch_stream' require 'kubeclient/common' require 'kubeclient/config' +require 'kubeclient/missing_kind_compatibility' module Kubeclient # Kubernetes Client diff --git a/lib/kubeclient/common.rb b/lib/kubeclient/common.rb index 6102fba8..c2d54960 100644 --- a/lib/kubeclient/common.rb +++ b/lib/kubeclient/common.rb @@ -425,7 +425,9 @@ def api def load_entities @entities = {} fetch_entities['resources'].each do |resource| - next if resource['name'].include?('/') || resource['kind'].nil? + next if resource['name'].include?('/') + resource['kind'] = Kubeclient::Common::MissingKindCompatibility + .resource_kind(resource['name']) if resource['kind'].nil? entity = ClientMixin.parse_definition(resource['kind'], resource['name']) @entities[entity.method_names[0]] = entity if entity end diff --git a/lib/kubeclient/missing_kind_compatibility.rb b/lib/kubeclient/missing_kind_compatibility.rb new file mode 100644 index 00000000..77e50b36 --- /dev/null +++ b/lib/kubeclient/missing_kind_compatibility.rb @@ -0,0 +1,68 @@ +module Kubeclient + module Common + # Backward compatibility for old versions where kind is missing (e.g. OpenShift Enterprise 3.1) + class MissingKindCompatibility + MAPPING = { + 'bindings' => 'Binding', + 'componentstatuses' => 'ComponentStatus', + 'endpoints' => 'Endpoints', + 'events' => 'Event', + 'limitranges' => 'LimitRange', + 'namespaces' => 'Namespace', + 'nodes' => 'Node', + 'persistentvolumeclaims' => 'PersistentVolumeClaim', + 'persistentvolumes' => 'PersistentVolume', + 'pods' => 'Pod', + 'podtemplates' => 'PodTemplate', + 'replicationcontrollers' => 'ReplicationController', + 'resourcequotas' => 'ResourceQuota', + 'secrets' => 'Secret', + 'securitycontextconstraints' => 'SecurityContextConstraints', + 'serviceaccounts' => 'ServiceAccount', + 'services' => 'Service', + 'buildconfigs' => 'BuildConfig', + 'builds' => 'Build', + 'clusternetworks' => 'ClusterNetwork', + 'clusterpolicies' => 'ClusterPolicy', + 'clusterpolicybindings' => 'ClusterPolicyBinding', + 'clusterrolebindings' => 'ClusterRoleBinding', + 'clusterroles' => 'ClusterRole', + 'deploymentconfigrollbacks' => 'DeploymentConfigRollback', + 'deploymentconfigs' => 'DeploymentConfig', + 'generatedeploymentconfigs' => 'DeploymentConfig', + 'groups' => 'Group', + 'hostsubnets' => 'HostSubnet', + 'identities' => 'Identity', + 'images' => 'Image', + 'imagestreamimages' => 'ImageStreamImage', + 'imagestreammappings' => 'ImageStreamMapping', + 'imagestreams' => 'ImageStream', + 'imagestreamtags' => 'ImageStreamTag', + 'localresourceaccessreviews' => 'LocalResourceAccessReview', + 'localsubjectaccessreviews' => 'LocalSubjectAccessReview', + 'netnamespaces' => 'NetNamespace', + 'oauthaccesstokens' => 'OAuthAccessToken', + 'oauthauthorizetokens' => 'OAuthAuthorizeToken', + 'oauthclientauthorizations' => 'OAuthClientAuthorization', + 'oauthclients' => 'OAuthClient', + 'policies' => 'Policy', + 'policybindings' => 'PolicyBinding', + 'processedtemplates' => 'Template', + 'projectrequests' => 'ProjectRequest', + 'projects' => 'Project', + 'resourceaccessreviews' => 'ResourceAccessReview', + 'rolebindings' => 'RoleBinding', + 'roles' => 'Role', + 'routes' => 'Route', + 'subjectaccessreviews' => 'SubjectAccessReview', + 'templates' => 'Template', + 'useridentitymappings' => 'UserIdentityMapping', + 'users' => 'User' + } + + def self.resource_kind(name) + MAPPING[name] + end + end + end +end diff --git a/test/json/core_api_resource_list_without_kind.json b/test/json/core_api_resource_list_without_kind.json new file mode 100644 index 00000000..f60e113d --- /dev/null +++ b/test/json/core_api_resource_list_without_kind.json @@ -0,0 +1,129 @@ +{ + "groupVersion": "v1", + "resources": [ + { + "name": "bindings", + "namespaced": true + }, + { + "name": "componentstatuses", + "namespaced": true + }, + { + "name": "endpoints", + "namespaced": true + }, + { + "name": "events", + "namespaced": true + }, + { + "name": "limitranges", + "namespaced": true + }, + { + "name": "namespaces", + "namespaced": false + }, + { + "name": "namespaces/finalize", + "namespaced": false + }, + { + "name": "namespaces/status", + "namespaced": false + }, + { + "name": "nodes", + "namespaced": false + }, + { + "name": "nodes/status", + "namespaced": false + }, + { + "name": "persistentvolumeclaims", + "namespaced": true + }, + { + "name": "persistentvolumeclaims/status", + "namespaced": true + }, + { + "name": "persistentvolumes", + "namespaced": false + }, + { + "name": "persistentvolumes/status", + "namespaced": false + }, + { + "name": "pods", + "namespaced": true + }, + { + "name": "pods/attach", + "namespaced": true + }, + { + "name": "pods/binding", + "namespaced": true + }, + { + "name": "pods/exec", + "namespaced": true + }, + { + "name": "pods/log", + "namespaced": true + }, + { + "name": "pods/portforward", + "namespaced": true + }, + { + "name": "pods/proxy", + "namespaced": true + }, + { + "name": "pods/status", + "namespaced": true + }, + { + "name": "podtemplates", + "namespaced": true + }, + { + "name": "replicationcontrollers", + "namespaced": true + }, + { + "name": "replicationcontrollers/status", + "namespaced": true + }, + { + "name": "resourcequotas", + "namespaced": true + }, + { + "name": "resourcequotas/status", + "namespaced": true + }, + { + "name": "secrets", + "namespaced": true + }, + { + "name": "securitycontextconstraints", + "namespaced": false + }, + { + "name": "serviceaccounts", + "namespaced": true + }, + { + "name": "services", + "namespaced": true + } + ] +} diff --git a/test/json/core_oapi_resource_list_without_kind.json b/test/json/core_oapi_resource_list_without_kind.json new file mode 100644 index 00000000..a902a6a7 --- /dev/null +++ b/test/json/core_oapi_resource_list_without_kind.json @@ -0,0 +1,197 @@ +{ + "groupVersion": "v1", + "resources": [ + { + "name": "buildconfigs", + "namespaced": true + }, + { + "name": "buildconfigs/instantiate", + "namespaced": true + }, + { + "name": "buildconfigs/instantiatebinary", + "namespaced": true + }, + { + "name": "buildconfigs/webhooks", + "namespaced": true + }, + { + "name": "builds", + "namespaced": true + }, + { + "name": "builds/clone", + "namespaced": true + }, + { + "name": "builds/details", + "namespaced": true + }, + { + "name": "builds/log", + "namespaced": true + }, + { + "name": "clusternetworks", + "namespaced": false + }, + { + "name": "clusterpolicies", + "namespaced": false + }, + { + "name": "clusterpolicybindings", + "namespaced": false + }, + { + "name": "clusterrolebindings", + "namespaced": false + }, + { + "name": "clusterroles", + "namespaced": false + }, + { + "name": "deploymentconfigrollbacks", + "namespaced": true + }, + { + "name": "deploymentconfigs", + "namespaced": true + }, + { + "name": "deploymentconfigs/log", + "namespaced": true + }, + { + "name": "deploymentconfigs/scale", + "namespaced": true + }, + { + "name": "generatedeploymentconfigs", + "namespaced": true + }, + { + "name": "groups", + "namespaced": false + }, + { + "name": "hostsubnets", + "namespaced": false + }, + { + "name": "identities", + "namespaced": false + }, + { + "name": "images", + "namespaced": false + }, + { + "name": "imagestreamimages", + "namespaced": true + }, + { + "name": "imagestreammappings", + "namespaced": true + }, + { + "name": "imagestreams", + "namespaced": true + }, + { + "name": "imagestreams/status", + "namespaced": true + }, + { + "name": "imagestreamtags", + "namespaced": true + }, + { + "name": "localresourceaccessreviews", + "namespaced": true + }, + { + "name": "localsubjectaccessreviews", + "namespaced": true + }, + { + "name": "netnamespaces", + "namespaced": false + }, + { + "name": "oauthaccesstokens", + "namespaced": false + }, + { + "name": "oauthauthorizetokens", + "namespaced": false + }, + { + "name": "oauthclientauthorizations", + "namespaced": false + }, + { + "name": "oauthclients", + "namespaced": false + }, + { + "name": "policies", + "namespaced": true + }, + { + "name": "policybindings", + "namespaced": true + }, + { + "name": "processedtemplates", + "namespaced": true + }, + { + "name": "projectrequests", + "namespaced": false + }, + { + "name": "projects", + "namespaced": false + }, + { + "name": "resourceaccessreviews", + "namespaced": true + }, + { + "name": "rolebindings", + "namespaced": true + }, + { + "name": "roles", + "namespaced": true + }, + { + "name": "routes", + "namespaced": true + }, + { + "name": "routes/status", + "namespaced": true + }, + { + "name": "subjectaccessreviews", + "namespaced": true + }, + { + "name": "templates", + "namespaced": true + }, + { + "name": "useridentitymappings", + "namespaced": false + }, + { + "name": "users", + "namespaced": false + } + ] +} diff --git a/test/test_resource_list_without_kind.rb b/test/test_resource_list_without_kind.rb new file mode 100644 index 00000000..cc041b94 --- /dev/null +++ b/test/test_resource_list_without_kind.rb @@ -0,0 +1,78 @@ +require 'test_helper' + +# Core api resource list without kind tests +class TestResourceListWithoutKind < MiniTest::Test + def test_get_from_json_api_v1 + stub_request(:get, %r{/api/v1$}) + .to_return(body: open_test_file('core_api_resource_list_without_kind.json'), + status: 200) + + client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') + client.discover + + [ + { + entity: 'pod', + type: 'Pod', + name: 'pods', + methods: %w(pod pods) + }, + { + entity: 'node', + type: 'Node', + name: 'nodes', + methods: %w(node nodes) + }, + { + entity: 'service', + type: 'Service', + name: 'services', + methods: %w(service services) + } + ].each { |h| assert_entities(client.instance_variable_get(:@entities)[h[:entity]], h) } + + assert_requested(:get, + 'http://localhost:8080/api/v1', + times: 1) + end + + def test_get_from_json_oapi_v1 + stub_request(:get, %r{/oapi/v1$}) + .to_return(body: open_test_file('core_oapi_resource_list_without_kind.json'), + status: 200) + + client = Kubeclient::Client.new('http://localhost:8080/oapi/', 'v1') + client.discover + + [ + { + entity: 'template', + type: 'Template', + name: 'templates', + methods: %w(template templates) + }, + { + entity: 'build', + type: 'Build', + name: 'builds', + methods: %w(build builds) + }, + { + entity: 'project', + type: 'Project', + name: 'projects', + methods: %w(project projects) + } + ].each { |h| assert_entities(client.instance_variable_get(:@entities)[h[:entity]], h) } + + assert_requested(:get, + 'http://localhost:8080/oapi/v1', + times: 1) + end + + def assert_entities(entity, h) + assert_equal(entity.entity_type, h[:type]) + assert_equal(entity.resource_name, h[:name]) + assert_equal(entity.method_names, h[:methods]) + end +end