kubeclient-4.6.0/0000755000004100000410000000000013612310022013664 5ustar www-datawww-datakubeclient-4.6.0/.travis.yml0000644000004100000410000000051613612310022015777 0ustar www-datawww-datalanguage: ruby sudo: false cache: bundler script: bundle exec rake $TASK os: - linux - osx rvm: - "2.2.0" - "2.3.0" - "2.4.0" - "2.5.0" - "2.6.3" env: - TASK=test matrix: exclude: - os: osx rvm: "2.2.0" - os: osx rvm: "2.3.0" include: - os: linux rvm: "2.5.0" env: TASK=rubocop kubeclient-4.6.0/test/0000755000004100000410000000000013612310022014643 5ustar www-datawww-datakubeclient-4.6.0/test/test_watch.rb0000644000004100000410000001331013612310022017333 0ustar www-datawww-datarequire_relative 'test_helper' # Watch entity tests class TestWatch < MiniTest::Test def test_watch_pod_success stub_core_api_list expected = [ { 'type' => 'ADDED', 'resourceVersion' => '1389' }, { 'type' => 'MODIFIED', 'resourceVersion' => '1390' }, { 'type' => 'DELETED', 'resourceVersion' => '1398' } ] stub_request(:get, %r{/watch/pods}) .to_return(body: open_test_file('watch_stream.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') client.watch_pods.to_enum.with_index do |notice, index| assert_instance_of(Kubeclient::Resource, notice) assert_equal(expected[index]['type'], notice.type) assert_equal('Pod', notice.object.kind) assert_equal('php', notice.object.metadata.name) assert_equal(expected[index]['resourceVersion'], notice.object.metadata.resourceVersion) end end def test_watch_pod_block stub_core_api_list stub_request(:get, %r{/watch/pods}) .to_return(body: open_test_file('watch_stream.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') yielded = [] client.watch_pods { |notice| yielded << notice.type } assert_equal %w[ADDED MODIFIED DELETED], yielded end def test_watch_pod_raw stub_core_api_list stub_request(:get, %r{/watch/pods}).to_return( body: open_test_file('watch_stream.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') got = nil client.watch_pods(as: :raw).each { |notice| got = notice } assert_match(/\A{"type":"DELETED"/, got) end def test_watch_pod_failure stub_core_api_list stub_request(:get, %r{/watch/pods}).to_return(status: 404) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') assert_raises(Kubeclient::HttpError) do client.watch_pods.each do end end end def test_watch_pod_follow_redirect stub_core_api_list redirect = 'http://localhost:1234/api/v1/watch/pods' stub_request(:get, %r{/watch/pods}) .to_return(status: 302, headers: { location: redirect }) stub_request(:get, redirect).to_return( body: open_test_file('watch_stream.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') got = nil client.watch_pods.each { |notice| got = notice } assert_equal('DELETED', got.type) end def test_watch_pod_max_redirect stub_core_api_list redirect = 'http://localhost:1234/api/v1/watcher/pods' stub_request(:get, %r{/watch/pods}) .to_return(status: 302, headers: { location: redirect }) stub_request(:get, redirect).to_return( body: open_test_file('watch_stream.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1', http_max_redirects: 0) assert_raises(Kubeclient::HttpError) do client.watch_pods.each do end end end # Ensure that WatchStream respects a format that's not JSON def test_watch_stream_text url = 'http://www.example.com/foobar' expected_lines = open_test_file('pod_log.txt').read.split("\n") stub_request(:get, url) .to_return(body: open_test_file('pod_log.txt'), status: 200) stream = Kubeclient::Common::WatchStream.new(URI.parse(url), {}, formatter: ->(v) { v }) stream.to_enum.with_index do |line, index| assert_instance_of(String, line) assert_equal(expected_lines[index], line) end end def test_watch_with_resource_version api_host = 'http://localhost:8080/api' version = '1995' stub_core_api_list stub_request(:get, %r{.*\/watch/events}) .to_return(body: open_test_file('watch_stream.json'), status: 200) client = Kubeclient::Client.new(api_host, 'v1') results = client.watch_events(version).to_enum assert_equal(3, results.count) assert_requested(:get, "#{api_host}/v1/watch/events?resourceVersion=#{version}", times: 1) end def test_watch_with_label_selector api_host = 'http://localhost:8080/api' selector = 'name=redis-master' stub_core_api_list stub_request(:get, %r{.*\/watch/events}) .to_return(body: open_test_file('watch_stream.json'), status: 200) client = Kubeclient::Client.new(api_host, 'v1') results = client.watch_events(label_selector: selector).to_enum assert_equal(3, results.count) assert_requested(:get, "#{api_host}/v1/watch/events?labelSelector=#{selector}", times: 1) end def test_watch_with_field_selector api_host = 'http://localhost:8080/api' selector = 'involvedObject.kind=Pod' stub_core_api_list stub_request(:get, %r{.*\/watch/events}) .to_return(body: open_test_file('watch_stream.json'), status: 200) client = Kubeclient::Client.new(api_host, 'v1') results = client.watch_events(field_selector: selector).to_enum assert_equal(3, results.count) assert_requested(:get, "#{api_host}/v1/watch/events?fieldSelector=#{selector}", times: 1) end def test_watch_with_finish_and_ebadf api_host = 'http://localhost:8080/api' stub_core_api_list stub_request(:get, %r{.*\/watch/events}) .to_return(body: open_test_file('watch_stream.json'), status: 200) client = Kubeclient::Client.new(api_host, 'v1') watcher = client.watch_events # explodes when StandardError is not caught watcher.each do watcher.finish raise StandardError end assert_requested(:get, "#{api_host}/v1/watch/events", times: 1) end end kubeclient-4.6.0/test/test_oidc_auth_provider.rb0000644000004100000410000000661013612310022022103 0ustar www-datawww-datarequire_relative 'test_helper' require 'openid_connect' class OIDCAuthProviderTest < MiniTest::Test def setup @client_id = 'client_id' @client_secret = 'client_secret' @idp_issuer_url = 'idp_issuer_url' @refresh_token = 'refresh_token' @id_token = 'id_token' @new_id_token = 'new_id_token' end def test_expired_token OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do OpenIDConnect::ResponseObject::IdToken.stub(:decode, id_token_mock(Time.now.to_i - 7200)) do OpenIDConnect::Client.stub(:new, openid_client_mock) do retrieved_id_token = Kubeclient::OIDCAuthProvider.token( 'client-id' => @client_id, 'client-secret' => @client_secret, 'id-token' => @id_token, 'idp-issuer-url' => @idp_issuer_url, 'refresh-token' => @refresh_token ) assert_equal(@new_id_token, retrieved_id_token) end end end end def test_valid_token OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do OpenIDConnect::ResponseObject::IdToken.stub(:decode, id_token_mock(Time.now.to_i + 7200)) do retrieved_id_token = Kubeclient::OIDCAuthProvider.token( 'client-id' => @client_id, 'client-secret' => @client_secret, 'id-token' => @id_token, 'idp-issuer-url' => @idp_issuer_url, 'refresh-token' => @refresh_token ) assert_equal(@id_token, retrieved_id_token) end end end def test_missing_id_token OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do OpenIDConnect::Client.stub(:new, openid_client_mock) do retrieved_id_token = Kubeclient::OIDCAuthProvider.token( 'client-id' => @client_id, 'client-secret' => @client_secret, 'idp-issuer-url' => @idp_issuer_url, 'refresh-token' => @refresh_token ) assert_equal(@new_id_token, retrieved_id_token) end end end def test_token_with_unknown_kid OpenIDConnect::Discovery::Provider::Config.stub(:discover!, discovery_mock) do OpenIDConnect::ResponseObject::IdToken.stub( :decode, ->(_token, _jwks) { raise JSON::JWK::Set::KidNotFound } ) do OpenIDConnect::Client.stub(:new, openid_client_mock) do retrieved_id_token = Kubeclient::OIDCAuthProvider.token( 'client-id' => @client_id, 'client-secret' => @client_secret, 'id-token' => @id_token, 'idp-issuer-url' => @idp_issuer_url, 'refresh-token' => @refresh_token ) assert_equal(@new_id_token, retrieved_id_token) end end end end private def openid_client_mock access_token = Minitest::Mock.new access_token.expect(@id_token, @new_id_token) openid_client = Minitest::Mock.new openid_client.expect(:refresh_token=, nil, [@refresh_token]) openid_client.expect(:access_token!, access_token) end def id_token_mock(expiry) id_token_mock = Minitest::Mock.new id_token_mock.expect(:exp, expiry) end def discovery_mock discovery = Minitest::Mock.new discovery.expect(:jwks, 'jwks') discovery.expect(:authorization_endpoint, 'authz_endpoint') discovery.expect(:token_endpoint, 'token_endpoint') discovery.expect(:userinfo_endpoint, 'userinfo_endpoint') discovery end end kubeclient-4.6.0/test/test_process_template.rb0000644000004100000410000000637113612310022021607 0ustar www-datawww-datarequire_relative 'test_helper' # Process Template tests class TestProcessTemplate < MiniTest::Test def test_process_template client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') template = {} template[:metadata] = {} template[:metadata][:name] = 'my-template' template[:metadata][:namespace] = 'default' template[:kind] = 'Template' template[:apiVersion] = 'v1' service = {} service[:metadata] = {} service[:metadata][:name] = '${NAME_PREFIX}my-service' service[:kind] = 'Service' service[:apiVersion] = 'v1' template[:objects] = [service] param = { name: 'NAME_PREFIX', value: 'test/' } template[:parameters] = [param] req_body = '{"metadata":{"name":"my-template","namespace":"default"},' \ '"kind":"Template","apiVersion":"v1","objects":[{"metadata":' \ '{"name":"${NAME_PREFIX}my-service"},"kind":"Service","apiVersion":"v1"}],' \ '"parameters":[{"name":"NAME_PREFIX","value":"test/"}]}' expected_url = 'http://localhost:8080/api/v1/namespaces/default/processedtemplates' stub_request(:post, expected_url) .with(body: req_body, headers: { 'Content-Type' => 'application/json' }) .to_return(body: open_test_file('processed_template.json'), status: 200) processed_template = client.process_template(template) assert_equal('test/my-service', processed_template['objects'].first['metadata']['name']) assert_requested(:post, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['kind'] == 'Template' && data['apiVersion'] == 'v1' && data['metadata']['name'] == 'my-template' && data['metadata']['namespace'] == 'default' end end # Ensure _template and _templates methods hit `/templates` rather than # `/processedtemplates` URL. def test_templates_methods stub_request(:get, %r{/apis/template\.openshift\.io/v1$}).to_return( body: open_test_file('template.openshift.io_api_resource_list.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/apis/template.openshift.io', 'v1') expected_url = 'http://localhost:8080/apis/template.openshift.io/v1/namespaces/default/templates' stub_request(:get, expected_url) .to_return(body: open_test_file('template_list.json'), status: 200) client.get_templates(namespace: 'default') assert_requested(:get, expected_url, times: 1) expected_url = 'http://localhost:8080/apis/template.openshift.io/v1/namespaces/default/templates/my-template' stub_request(:get, expected_url) .to_return(body: open_test_file('template.json'), status: 200) client.get_template('my-template', 'default') assert_requested(:get, expected_url, times: 1) end def test_no_processedtemplates_methods stub_request(:get, %r{/apis/template\.openshift\.io/v1$}).to_return( body: open_test_file('template.openshift.io_api_resource_list.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/apis/template.openshift.io', 'v1') client.discover refute_respond_to(client, :get_processedtemplates) refute_respond_to(client, :get_processedtemplate) refute_respond_to(client, :get_processed_templates) refute_respond_to(client, :get_processed_template) end end kubeclient-4.6.0/test/test_secret.rb0000644000004100000410000000422113612310022017513 0ustar www-datawww-datarequire_relative 'test_helper' # Namespace entity tests class TestSecret < MiniTest::Test def test_get_secret_v1 stub_core_api_list stub_request(:get, %r{/secrets}) .to_return(body: open_test_file('created_secret.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') secret = client.get_secret('test-secret', 'dev') assert_instance_of(Kubeclient::Resource, secret) assert_equal('4e38a198-2bcb-11e5-a483-0e840567604d', secret.metadata.uid) assert_equal('test-secret', secret.metadata.name) assert_equal('v1', secret.apiVersion) assert_equal('Y2F0J3MgYXJlIGF3ZXNvbWUK', secret.data['super-secret']) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/dev/secrets/test-secret', times: 1) end def test_delete_secret_v1 stub_core_api_list stub_request(:delete, %r{/secrets}) .to_return(status: 200, body: open_test_file('created_secret.json')) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') secret = client.delete_secret('test-secret', 'dev') assert_kind_of(RecursiveOpenStruct, secret) assert_requested(:delete, 'http://localhost:8080/api/v1/namespaces/dev/secrets/test-secret', times: 1) end def test_create_secret_v1 stub_core_api_list stub_request(:post, %r{/secrets}) .to_return(body: open_test_file('created_secret.json'), status: 201) secret = Kubeclient::Resource.new secret.metadata = {} secret.metadata.name = 'test-secret' secret.metadata.namespace = 'dev' secret.data = {} secret.data['super-secret'] = 'Y2F0J3MgYXJlIGF3ZXNvbWUK' client = Kubeclient::Client.new('http://localhost:8080/api/') created_secret = client.create_secret(secret) assert_instance_of(Kubeclient::Resource, created_secret) assert_equal(secret.metadata.name, created_secret.metadata.name) assert_equal(secret.metadata.namespace, created_secret.metadata.namespace) assert_equal( secret.data['super-secret'], created_secret.data['super-secret'] ) end end kubeclient-4.6.0/test/test_exec_credentials.rb0000644000004100000410000000620313612310022021531 0ustar www-datawww-datarequire_relative 'test_helper' require 'open3' # Unit tests for the ExecCredentials token provider class ExecCredentialsTest < MiniTest::Test def test_exec_opts_missing expected_msg = 'exec options are required' exception = assert_raises(ArgumentError) do Kubeclient::ExecCredentials.token(nil) end assert_equal(expected_msg, exception.message) end def test_exec_command_missing expected_msg = 'exec command is required' exception = assert_raises(KeyError) do Kubeclient::ExecCredentials.token({}) end assert_equal(expected_msg, exception.message) end def test_exec_command_failure err = 'Error' expected_msg = "exec command failed: #{err}" st = Minitest::Mock.new st.expect(:success?, false) opts = { 'command' => 'dummy' } Open3.stub(:capture3, [nil, err, st]) do exception = assert_raises(RuntimeError) do Kubeclient::ExecCredentials.token(opts) end assert_equal(expected_msg, exception.message) end end def test_token opts = { 'command' => 'dummy' } creds = JSON.dump( 'apiVersion': 'client.authentication.k8s.io/v1alpha1', 'status': { 'token': '0123456789ABCDEF0123456789ABCDEF' } ) st = Minitest::Mock.new st.expect(:success?, true) Open3.stub(:capture3, [creds, nil, st]) do assert_equal('0123456789ABCDEF0123456789ABCDEF', Kubeclient::ExecCredentials.token(opts)) end end def test_status_missing opts = { 'command' => 'dummy' } creds = JSON.dump('apiVersion': 'client.authentication.k8s.io/v1alpha1') st = Minitest::Mock.new st.expect(:success?, true) expected_msg = 'exec plugin didn\'t return a status field' Open3.stub(:capture3, [creds, nil, st]) do exception = assert_raises(RuntimeError) do Kubeclient::ExecCredentials.token(opts) end assert_equal(expected_msg, exception.message) end end def test_token_missing opts = { 'command' => 'dummy' } creds = JSON.dump( 'apiVersion': 'client.authentication.k8s.io/v1alpha1', 'status': {} ) st = Minitest::Mock.new st.expect(:success?, true) expected_msg = 'exec plugin didn\'t return a token' Open3.stub(:capture3, [creds, nil, st]) do exception = assert_raises(RuntimeError) do Kubeclient::ExecCredentials.token(opts) end assert_equal(expected_msg, exception.message) end end def test_api_version_mismatch api_version = 'client.authentication.k8s.io/v1alpha1' expected_version = 'client.authentication.k8s.io/v1beta1' opts = { 'command' => 'dummy', 'apiVersion' => expected_version } creds = JSON.dump( 'apiVersion': api_version ) st = Minitest::Mock.new st.expect(:success?, true) expected_msg = "exec plugin is configured to use API version #{expected_version}," \ " plugin returned version #{api_version}" Open3.stub(:capture3, [creds, nil, st]) do exception = assert_raises(RuntimeError) do Kubeclient::ExecCredentials.token(opts) end assert_equal(expected_msg, exception.message) end end end kubeclient-4.6.0/test/test_gcp_command_credentials.rb0000644000004100000410000000150513612310022023054 0ustar www-datawww-datarequire_relative 'test_helper' require 'open3' # Unit tests for the GCPCommandCredentials token provider class GCPCommandCredentialsTest < MiniTest::Test def test_token opts = { 'cmd-args' => 'config config-helper --format=json', 'cmd-path' => '/path/to/gcloud', 'expiry-key' => '{.credential.token_expiry}', 'token-key' => '{.credential.access_token}' } creds = JSON.dump( 'credential' => { 'access_token' => '9A3A941836F2458175BE18AA1971EBBF47949B07', 'token_expiry' => '2019-04-12T15:02:51Z' } ) st = Minitest::Mock.new st.expect(:success?, true) Open3.stub(:capture3, [creds, nil, st]) do assert_equal('9A3A941836F2458175BE18AA1971EBBF47949B07', Kubeclient::GCPCommandCredentials.token(opts)) end end end kubeclient-4.6.0/test/test_resource_list_without_kind.rb0000644000004100000410000000402013612310022023675 0ustar www-datawww-datarequire_relative '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 kubeclient-4.6.0/test/test_guestbook_go.rb0000644000004100000410000001662113612310022020724 0ustar www-datawww-datarequire_relative 'test_helper' require 'vcr' # creation of google's example of guest book class CreateGuestbookGo < MiniTest::Test def test_create_guestbook_entities VCR.configure do |c| c.cassette_library_dir = 'test/cassettes' c.hook_into(:webmock) end # WebMock.allow_net_connect! VCR.use_cassette('kubernetes_guestbook') do # , record: :new_episodes) do client = Kubeclient::Client.new('http://10.35.0.23:8080/api/', 'v1') testing_ns = Kubeclient::Resource.new testing_ns.metadata = {} testing_ns.metadata.name = 'kubeclient-ns' # delete in case they existed before so creation can be tested delete_namespace(client, testing_ns.metadata.name) delete_services( client, testing_ns.metadata.name, ['guestbook', 'redis-master', 'redis-slave'] ) delete_replication_controllers( client, testing_ns.metadata.name, ['guestbook', 'redis-master', 'redis-slave'] ) client.create_namespace(testing_ns) services = create_services(client, testing_ns.metadata.name) replicators = create_replication_controllers(client, testing_ns.metadata.name) get_namespaces(client) get_services(client, testing_ns.metadata.name) get_replication_controllers(client, testing_ns.metadata.name) delete_services(client, testing_ns.metadata.name, services) delete_replication_controllers(client, testing_ns.metadata.name, replicators) client.delete_namespace(testing_ns.metadata.name) end end def delete_namespace(client, namespace_name) client.delete_namespace(namespace_name) rescue Kubeclient::ResourceNotFoundError => exception assert_equal(404, exception.error_code) end def get_namespaces(client) namespaces = client.get_namespaces assert(true, namespaces.size > 2) end def get_services(client, ns) retrieved_services = client.get_services(namespace: ns) assert_equal(3, retrieved_services.size) end def get_replication_controllers(client, ns) retrieved_replicators = client.get_replication_controllers(namespace: ns) assert_equal(3, retrieved_replicators.size) end def create_services(client, ns) guestbook_service = client.create_service(guestbook_service(ns)) redis_service = client.create_service(redis_service(ns)) redis_slave_service = client.create_service(redis_slave_service(ns)) [guestbook_service, redis_service, redis_slave_service] end def create_replication_controllers(client, namespace) rc = client.create_replication_controller(guestbook_rc(namespace)) rc2 = client.create_replication_controller(redis_master_rc(namespace)) rc3 = client.create_replication_controller(redis_slave_rc(namespace)) [rc, rc2, rc3] end def delete_services(client, namespace, services) # if the entity is not found, no need to fail the test services.each do |service| begin if service.instance_of?(Kubeclient::Resource) client.delete_service(service.metadata.name, namespace) else # it's just a string - service name client.delete_service(service, namespace) end rescue Kubeclient::ResourceNotFoundError => exception assert_equal(404, exception.error_code) end end end def delete_replication_controllers(client, namespace, replication_controllers) # if the entity is not found, no need to fail the test replication_controllers.each do |rc| begin if rc.instance_of?(Kubeclient::Resource) client.delete_replication_controller(rc.metadata.name, namespace) else # it's just a string - rc name client.delete_replication_controller(rc, namespace) end rescue Kubeclient::ResourceNotFoundError => exception assert_equal(404, exception.error_code) end end end private def construct_base_rc(namespace) rc = Kubeclient::Resource.new rc.metadata = {} rc.metadata.namespace = namespace rc.metadata.labels = {} rc.spec = {} rc.spec.selector = {} rc.spec.template = {} rc.spec.template.metadata = {} rc.spec.template.spec = {} rc.spec.template.metadata.labels = {} rc end def redis_master_rc(namespace) rc = construct_base_rc(namespace) rc.metadata.name = 'redis-master' rc.metadata.labels.app = 'redis' rc.metadata.labels.role = 'master' rc.spec.replicas = 1 rc.spec.selector.app = 'redis' rc.spec.selector.role = 'master' rc.spec.template.metadata.labels.app = 'redis' rc.spec.template.metadata.labels.role = 'master' rc.spec.template.spec.containers = [{ 'name' => 'redis-master', 'image' => 'redis', 'ports' => [{ 'name' => 'redis-server', 'containerPort' => 6379 }] }] rc end def redis_slave_rc(namespace) rc = construct_base_rc(namespace) rc.metadata.name = 'redis-slave' rc.metadata.labels.app = 'redis' rc.metadata.labels.role = 'slave' rc.spec.replicas = 2 rc.spec.selector.app = 'redis' rc.spec.selector.role = 'slave' rc.spec.template.metadata.labels.app = 'redis' rc.spec.template.metadata.labels.role = 'slave' rc.spec.template.spec.containers = [{ 'name' => 'redis-slave', 'image' => 'kubernetes/redis-slave:v2', 'ports' => [{ 'name' => 'redis-server', 'containerPort' => 6379 }] }] rc end def guestbook_rc(namespace) rc = construct_base_rc(namespace) rc.metadata.name = 'guestbook' rc.metadata.labels.app = 'guestbook' rc.metadata.labels.role = 'slave' rc.spec.replicas = 3 rc.spec.selector.app = 'guestbook' rc.spec.template.metadata.labels.app = 'guestbook' rc.spec.template.spec.containers = [ { 'name' => 'guestbook', 'image' => 'kubernetes/guestbook:v2', 'ports' => [ { 'name' => 'http-server', 'containerPort' => 3000 } ] } ] rc end def base_service(namespace) our_service = Kubeclient::Resource.new our_service.metadata = {} our_service.metadata.namespace = namespace our_service.metadata.labels = {} our_service.spec = {} our_service.spec.selector = {} our_service end def redis_slave_service(namespace) our_service = base_service(namespace) our_service.metadata.name = 'redis-slave' our_service.metadata.labels.app = 'redis' our_service.metadata.labels.role = 'slave' our_service.spec.ports = [{ 'port' => 6379, 'targetPort' => 'redis-server' }] our_service.spec.selector.app = 'redis' our_service.spec.selector.role = 'slave' our_service end def redis_service(namespace) our_service = base_service(namespace) our_service.metadata.name = 'redis-master' our_service.metadata.labels.app = 'redis' our_service.metadata.labels.role = 'master' our_service.spec.ports = [{ 'port' => 6379, 'targetPort' => 'redis-server' }] our_service.spec.selector.app = 'redis' our_service.spec.selector.role = 'master' our_service end def guestbook_service(namespace) our_service = base_service(namespace) our_service.metadata.name = 'guestbook' our_service.metadata.labels.name = 'guestbook' our_service.spec.ports = [{ 'port' => 3000, 'targetPort' => 'http-server' }] our_service.spec.selector.app = 'guestbook' our_service.type = 'LoadBalancer' our_service end end kubeclient-4.6.0/test/json/0000755000004100000410000000000013612310022015614 5ustar www-datawww-datakubeclient-4.6.0/test/json/created_secret.json0000644000004100000410000000061313612310022021463 0ustar www-datawww-data{ "kind": "Secret", "apiVersion": "v1", "metadata": { "name": "test-secret", "namespace": "dev", "selfLink": "/api/v1/namespaces/dev/secrets/test-secret", "uid": "4e38a198-2bcb-11e5-a483-0e840567604d", "resourceVersion": "245569", "creationTimestamp": "2015-07-16T14:59:49Z" }, "data": { "super-secret": "Y2F0J3MgYXJlIGF3ZXNvbWUK" }, "type": "Opaque" } kubeclient-4.6.0/test/json/resource_quota.json0000644000004100000410000000204113612310022021544 0ustar www-datawww-data{ "kind": "ResourceQuota", "apiVersion": "v1", "metadata": { "name": "quota", "namespace": "quota-example", "selfLink": "/api/v1/namespaces/quota-example/resourcequotas/quota", "uid": "ab9f24a4-3c43-11e5-8214-0aaeec44370e", "resourceVersion": "12919", "creationTimestamp": "2015-08-06T14:01:44Z" }, "spec": { "hard": { "cpu": "20", "memory": "1Gi", "persistentvolumeclaims": "10", "pods": "10", "replicationcontrollers": "20", "resourcequotas": "1", "secrets": "10", "services": "5" } }, "status": { "hard": { "cpu": "20", "memory": "1Gi", "persistentvolumeclaims": "10", "pods": "10", "replicationcontrollers": "20", "resourcequotas": "1", "secrets": "10", "services": "5" }, "used": { "cpu": "0", "memory": "0", "persistentvolumeclaims": "0", "pods": "0", "replicationcontrollers": "1", "resourcequotas": "1", "secrets": "9", "services": "0" } } }kubeclient-4.6.0/test/json/pod_template_list.json0000644000004100000410000000023613612310022022220 0ustar www-datawww-data{ "kind": "PodTemplateList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/podtemplates", "resourceVersion": "672" }, "items": [] }kubeclient-4.6.0/test/json/replication_controller.json0000644000004100000410000000315013612310022023262 0ustar www-datawww-data{ "kind": "ReplicationController", "apiVersion": "v1", "metadata": { "name": "guestbook-controller", "namespace": "default", "selfLink": "/api/v1/replicationcontrollers/guestbook-controller?namespace=default", "uid": "c71aa4c0-a240-11e4-a265-3c970e4a436a", "resourceVersion": "8", "creationTimestamp": "2015-01-22T16:13:02+02:00", "labels": { "name": "guestbook" } }, "spec": { "replicas": 3, "selector": { "name": "guestbook" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "name": "guestbook" } }, "spec": { "volumes": null, "containers": [ { "name": "guestbook", "image": "kubernetes/guestbook", "ports": [ { "name": "http-server", "containerPort": 3000, "protocol": "TCP" } ], "memory": "0", "cpu": "0m", "imagePullPolicy": "" } ], "restartPolicy": { "always": { } }, "dnsPolicy": "ClusterFirst" } } }, "status": { "replicas": 3 } } kubeclient-4.6.0/test/json/created_security_context_constraint.json0000644000004100000410000000311513612310022026055 0ustar www-datawww-data{ "allowHostDirVolumePlugin": false, "allowHostIPC": false, "allowHostNetwork": false, "allowHostPID": false, "allowHostPorts": false, "allowPrivilegedContainer": false, "allowedCapabilities": null, "allowedFlexVolumes": null, "apiVersion": "security.openshift.io/v1", "defaultAddCapabilities": null, "fsGroup": { "type": "RunAsAny" }, "groups": [], "kind": "SecurityContextConstraints", "metadata": { "creationTimestamp": "2018-11-23T10:01:42Z", "name": "teleportation", "resourceVersion": "5274", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/teleportation", "uid": "c6e8e2ec-ef06-11e8-b4c0-68f728fac3ab" }, "priority": null, "readOnlyRootFilesystem": false, "requiredDropCapabilities": null, "runAsUser": { "type": "MustRunAs" }, "seLinuxContext": { "type": "MustRunAs" }, "supplementalGroups": { "type": "RunAsAny" }, "users": [], "volumes": [ "awsElasticBlockStore", "azureDisk", "azureFile", "cephFS", "cinder", "configMap", "downwardAPI", "emptyDir", "fc", "flexVolume", "flocker", "gcePersistentDisk", "gitRepo", "glusterfs", "iscsi", "nfs", "persistentVolumeClaim", "photonPersistentDisk", "portworxVolume", "projected", "quobyte", "rbd", "scaleIO", "secret", "storageOS", "vsphere" ] } kubeclient-4.6.0/test/json/config_map_list.json0000644000004100000410000000023213612310022021641 0ustar www-datawww-data{ "kind": "ConfigMapList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/configmaps", "resourceVersion": "665" }, "items": [] }kubeclient-4.6.0/test/json/secret_list.json0000644000004100000410000000221213612310022021024 0ustar www-datawww-data{ "kind": "SecretList", "apiVersion":"v1", "metadata":{ "selfLink":"/api/v1/secrets", "resourceVersion":"256788" }, "items": [ { "metadata": { "name":"default-token-my2pi", "namespace":"default", "selfLink":"/api/v1/namespaces/default/secrets/default-token-my2pi", "uid":"07b60654-2a65-11e5-a483-0e840567604d", "resourceVersion":"11", "creationTimestamp":"2015-07-14T20:15:11Z", "annotations": { "kubernetes.io/service-account.name":"default", "kubernetes.io/service-account.uid":"07b350a0-2a65-11e5-a483-0e840567604d" } }, "data":{ "ca.crt":"Y2F0J3MgYXJlIGF3ZXNvbWUK", "token":"Y2F0J3MgYXJlIGF3ZXNvbWUK" }, "type":"kubernetes.io/service-account-token" }, { "metadata": { "name": "test-secret", "namespace": "dev", "selfLink": "/api/v1/namespaces/dev/secrets/test-secret", "uid": "4e38a198-2bcb-11e5-a483-0e840567604d", "resourceVersion": "245569", "creationTimestamp": "2015-07-16T14:59:49Z" }, "data": { "super-secret": "Y2F0J3MgYXJlIGF3ZXNvbWUK" }, "type": "Opaque" } ] } kubeclient-4.6.0/test/json/core_oapi_resource_list_without_kind.json0000644000004100000410000000665713612310022026217 0ustar www-datawww-data{ "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 } ] } kubeclient-4.6.0/test/json/service_merge_patch.json0000644000004100000410000000072213612310022022506 0ustar www-datawww-data{ "status" : {}, "kind" : "Service", "apiVersion" : "v1", "spec" : { "ports" : [ { "targetPort" : 80, "nodePort" : 0, "port" : 80, "protocol" : "TCP" } ], "clusterIP" : "1.2.3.4", "type": "NodePort" }, "metadata" : { "name" : "my-service", "creationTimestamp" : null, "namespace" : "development", "resourceVersion" : "2", "annotations" : { "key" : "value" } } } kubeclient-4.6.0/test/json/watch_stream.json0000644000004100000410000000431313612310022021171 0ustar www-datawww-data{"type":"ADDED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"php","namespace":"default","selfLink":"/api/v1/pods/php","uid":"e75f2c07-b047-11e4-89e4-525400c903c1","resourceVersion":"1389","creationTimestamp":"2015-02-09T05:39:19-05:00","labels":{"name":"foo"}},"spec":{"volumes":null,"containers":[{"name":"nginx","image":"dockerfile/nginx","ports":[{"hostPort":9090,"containerPort":80,"protocol":"TCP"}],"resources":{},"livenessProbe":{"httpGet":{"path":"/index.html","port":"9090"},"initialDelaySeconds":30},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{}}}],"restartPolicy":{"always":{}},"dnsPolicy":"ClusterFirst"},"status":{"phase":"Pending"}}} {"type":"MODIFIED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"php","namespace":"default","selfLink":"/api/v1/pods/php","uid":"e75f2c07-b047-11e4-89e4-525400c903c1","resourceVersion":"1390","creationTimestamp":"2015-02-09T05:39:19-05:00","labels":{"name":"foo"}},"spec":{"volumes":null,"containers":[{"name":"nginx","image":"dockerfile/nginx","ports":[{"hostPort":9090,"containerPort":80,"protocol":"TCP"}],"resources":{},"livenessProbe":{"httpGet":{"path":"/index.html","port":"9090"},"initialDelaySeconds":30},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{}}}],"restartPolicy":{"always":{}},"dnsPolicy":"ClusterFirst"},"status":{"phase":"Pending","host":"127.0.0.1"}}} {"type":"DELETED","object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"php","namespace":"default","selfLink":"/api/v1/pods/php","uid":"e75f2c07-b047-11e4-89e4-525400c903c1","resourceVersion":"1398","creationTimestamp":"2015-02-09T05:39:19-05:00","labels":{"name":"foo"}},"spec":{"volumes":null,"containers":[{"name":"nginx","image":"dockerfile/nginx","ports":[{"hostPort":9090,"containerPort":80,"protocol":"TCP"}],"resources":{},"livenessProbe":{"httpGet":{"path":"/index.html","port":"9090"},"initialDelaySeconds":30},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent","securityContext":{"capabilities":{}}}],"restartPolicy":{"always":{}},"dnsPolicy":"ClusterFirst"},"status":{"phase":"Pending","host":"127.0.0.1"}}} kubeclient-4.6.0/test/json/created_namespace.json0000644000004100000410000000050613612310022022133 0ustar www-datawww-data{ "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "development", "selfLink": "/api/v1/namespaces/development", "uid": "13d820d6-df5b-11e4-bd42-f8b156af4ae1", "resourceVersion": "2533", "creationTimestamp": "2015-04-10T08:24:59Z" }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } } kubeclient-4.6.0/test/json/created_service.json0000644000004100000410000000141013612310022021632 0ustar www-datawww-data{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "guestbook", "namespace": "staging", "selfLink": "/api/v1/namespaces/staging/services/guestbook", "uid": "29885239-df58-11e4-bd42-f8b156af4ae1", "resourceVersion": "1908", "creationTimestamp": "2015-04-10T08:04:07Z", "labels": { "name": "guestbook" } }, "spec": { "ports": [ { "name": "", "protocol": "TCP", "port": 3000, "targetPort": "http-server" } ], "selector": { "name": "guestbook" }, "clusterIP": "10.0.0.99", "sessionAffinity": "None" }, "status": {} }kubeclient-4.6.0/test/json/persistent_volume_claim_list.json0000644000004100000410000000160513612310022024500 0ustar www-datawww-data{ "kind": "PersistentVolumeClaimList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/persistentvolumeclaims", "resourceVersion": "3188" }, "items": [ { "metadata": { "name": "myclaim-1", "namespace": "default", "selfLink": "/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1", "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe", "resourceVersion": "1584", "creationTimestamp": "2015-08-25T14:51:55Z" }, "spec": { "accessModes": [ "ReadWriteOnce" ], "resources": { "requests": { "storage": "3Gi" } }, "volumeName": "pv0001" }, "status": { "phase": "Bound", "accessModes": [ "ReadWriteOnce" ], "capacity": { "storage": "10Gi" } } } ] } kubeclient-4.6.0/test/json/persistent_volume_claim.json0000644000004100000410000000121713612310022023444 0ustar www-datawww-data{ "kind": "PersistentVolumeClaim", "apiVersion": "v1", "metadata": { "name": "myclaim-1", "namespace": "default", "selfLink": "/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1", "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe", "resourceVersion": "1584", "creationTimestamp": "2015-08-25T14:51:55Z" }, "spec": { "accessModes": [ "ReadWriteOnce" ], "resources": { "requests": { "storage": "3Gi" } }, "volumeName": "pv0001" }, "status": { "phase": "Bound", "accessModes": [ "ReadWriteOnce" ], "capacity": { "storage": "10Gi" } } } kubeclient-4.6.0/test/json/template_list.json0000644000004100000410000000156113612310022021360 0ustar www-datawww-data{ "kind": "TemplateList", "apiVersion": "template.openshift.io/v1", "metadata": { "selfLink": "/apis/template.openshift.io/v1/namespaces/default/templates", "resourceVersion": "22758" }, "items": [ { "metadata": { "name": "my-template", "namespace": "default", "selfLink": "/apis/template.openshift.io/v1/namespaces/default/templates/my-template", "uid": "6e03e3e6-0216-11e9-b1e0-68f728fac3ab", "resourceVersion": "21954", "creationTimestamp": "2018-12-17T16:11:36Z" }, "objects": [ { "apiVersion": "v1", "kind": "Service", "metadata": { "name": "${NAME_PREFIX}my-service" } } ], "parameters": [ { "name": "NAME_PREFIX", "description": "Prefix for names" } ] } ] } kubeclient-4.6.0/test/json/node_notice.json0000644000004100000410000001206513612310022021001 0ustar www-datawww-data{ "type": "ADDED", "object": { "apiVersion": "v1", "kind": "Node", "metadata": { "annotations": { "volumes.kubernetes.io/controller-managed-attach-detach": "true" }, "creationTimestamp": "2017-12-11T12:00:13Z", "labels": { "beta.kubernetes.io/arch": "amd64", "beta.kubernetes.io/os": "linux", "kubernetes.io/hostname": "openshift.local", "openshift-infra": "apiserver" }, "name": "openshift.local", "resourceVersion": "367410", "selfLink": "/api/v1/nodes/openshift.local", "uid": "d88c7af6-de6a-11e7-8725-52540080f1d2" }, "spec": { "externalID": "openshift.local" }, "status": { "addresses": [ { "address": "192.168.122.40", "type": "InternalIP" }, { "address": "openshift.local", "type": "Hostname" } ], "allocatable": { "cpu": "2", "memory": "8072896Ki", "pods": "20" }, "capacity": { "cpu": "2", "memory": "8175296Ki", "pods": "20" }, "conditions": [ { "lastHeartbeatTime": "2017-12-15T00:36:13Z", "lastTransitionTime": "2017-12-11T12:00:13Z", "message": "kubelet has sufficient disk space available", "reason": "KubeletHasSufficientDisk", "status": "False", "type": "OutOfDisk" }, { "lastHeartbeatTime": "2017-12-15T00:36:13Z", "lastTransitionTime": "2017-12-11T12:00:13Z", "message": "kubelet has sufficient memory available", "reason": "KubeletHasSufficientMemory", "status": "False", "type": "MemoryPressure" }, { "lastHeartbeatTime": "2017-12-15T00:36:13Z", "lastTransitionTime": "2017-12-11T12:00:13Z", "message": "kubelet has no disk pressure", "reason": "KubeletHasNoDiskPressure", "status": "False", "type": "DiskPressure" }, { "lastHeartbeatTime": "2017-12-15T00:36:13Z", "lastTransitionTime": "2017-12-14T15:43:39Z", "message": "kubelet is posting ready status", "reason": "KubeletReady", "status": "True", "type": "Ready" } ], "daemonEndpoints": { "kubeletEndpoint": { "Port": 10250 } }, "images": [ { "names": [ "docker.io/openshift/origin@sha256:908c6c9ccf0e0feefe2658899656c6e73d2854777fa340738fb903f0a40c328d", "docker.io/openshift/origin:latest" ], "sizeBytes": 1222636603 }, { "names": [ "docker.io/openshift/origin-deployer@sha256:3d324bce1870047edc418041cefdec88e0a5bbb5b3b9f6fd35b43f14919a656c", "docker.io/openshift/origin-deployer:v3.7.0" ], "sizeBytes": 1098951248 }, { "names": [ "docker.io/cockpit/kubernetes@sha256:a8e58cd5e6f5a4d12d1e2dfd339686b74f3c22586952ca7aa184dc254ab49714", "docker.io/cockpit/kubernetes:latest" ], "sizeBytes": 375926556 }, { "names": [ "docker.io/cockpit/kubernetes@sha256:0745b3823efc57e03a5ef378614dfcb6c2b1e3964220bbf908fb3046a91cef70" ], "sizeBytes": 350062743 }, { "names": [ "docker.io/openshift/origin-service-catalog@sha256:ef851e06276af96838a93320d0e4be51cc8de6e5afb2fb0efd4e56cec114b937" ], "sizeBytes": 284732029 }, { "names": [ "docker.io/openshift/origin-service-catalog@sha256:8addfd742d92d8da819b091d6bda40edc45e88d1446ffd1ad658b6d21b3c36fd" ], "sizeBytes": 284731998 }, { "names": [ "docker.io/openshift/origin-service-catalog@sha256:b3a737cc346b3cae85ef2f5d020b607781a1cac38fe70678cb78fee2c2a3bf8a" ], "sizeBytes": 284731943 }, { "names": [ "docker.io/openshift/origin-service-catalog@sha256:957934537721da33362693d4f1590dc79dc5da7438799bf14d645165768e53ef", "docker.io/openshift/origin-service-catalog:latest" ], "sizeBytes": 283929631 }, { "names": [ "docker.io/openshift/origin-pod@sha256:2c257d83a01607b229ef5e3dca09f52c3a2a2788c09dc33f0444ec4e572a9e1d", "docker.io/openshift/origin-pod:v3.7.0" ], "sizeBytes": 218423400 } ], "nodeInfo": { "architecture": "amd64", "bootID": "75be791d-88a2-4f56-a588-c071a80bf7cf", "containerRuntimeVersion": "docker://1.12.6", "kernelVersion": "3.10.0-693.11.1.el7.x86_64", "kubeProxyVersion": "v1.7.6+a08f5eeb62", "kubeletVersion": "v1.7.6+a08f5eeb62", "machineID": "adf09ffc2de2624aa5ed335727c7400d", "operatingSystem": "linux", "osImage": "CentOS Linux 7 (Core)", "systemUUID": "FC9FF0AD-E22D-4A62-A5ED-335727C7400D" } } } } kubeclient-4.6.0/test/json/pods_2.json0000644000004100000410000000635613612310022017707 0ustar www-datawww-data{ "kind":"PodList", "apiVersion":"v1", "metadata":{ "selfLink":"/api/v1/pods", "resourceVersion":"53226147" }, "items":[ { "metadata":{ "name":"topological-inventory-persister-9-hznds", "generateName":"topological-inventory-persister-9-", "namespace":"topological-inventory-ci", "selfLink":"/api/v1/namespaces/topological-inventory-ci/pods/topological-inventory-persister-9-hznds", "uid":"0c114dde-d865-11e8-ba7e-d094660d31fb", "resourceVersion":"51987342", "creationTimestamp":"2018-10-25T14:48:34Z", "labels":{ "name":"topological-inventory-persister" } }, "spec":{ "volumes":[ { "name":"default-token-5pdjl", "secret":{ "secretName":"default-token-5pdjl", "defaultMode":420 } } ], "containers":[ { "name":"topological-inventory-persister", "image":"docker-registry.default.svc:5000/topological-inventory-ci/topological-inventory-persister@sha256:0f654ea09e749019cf3bcc4b8ee43b8dd813fcbf487843b917cf190213741927", "resources":{ } } ], "restartPolicy":"Always", "terminationGracePeriodSeconds":30, "dnsPolicy":"ClusterFirst", "nodeSelector":{ "node-role.kubernetes.io/compute":"true" }, "serviceAccountName":"default", "serviceAccount":"default", "nodeName":"dell-r430-20.example.com", "schedulerName":"default-scheduler" }, "status":{ "phase":"Running", "hostIP":"10.8.96.55", "podIP":"10.129.1.108", "startTime":"2018-10-25T14:48:34Z", "qosClass":"BestEffort" } }, { "metadata":{ "name":"topological-inventory-persister-9-vzr6h", "generateName":"topological-inventory-persister-9-", "namespace":"topological-inventory-ci", "selfLink":"/api/v1/namespaces/topological-inventory-ci/pods/topological-inventory-persister-9-vzr6h", "uid":"3065d8ce-d86a-11e8-ba7e-d094660d31fb", "resourceVersion":"51996115", "creationTimestamp":"2018-10-25T15:25:22Z", "labels":{ "name":"topological-inventory-persister" } }, "spec":{ "volumes":null, "containers":[ { "name":"topological-inventory-persister", "image":"docker-registry.default.svc:5000/topological-inventory-ci/topological-inventory-persister@sha256:0f654ea09e749019cf3bcc4b8ee43b8dd813fcbf487843b917cf190213741927", "resources":{ } } ], "restartPolicy":"Always", "terminationGracePeriodSeconds":30, "dnsPolicy":"ClusterFirst", "nodeSelector":{ "node-role.kubernetes.io/compute":"true" }, "serviceAccountName":"default", "serviceAccount":"default", "nodeName":"dell-r430-20.example.com", "schedulerName":"default-scheduler" }, "status":{ "phase":"Running", "hostIP":"10.8.96.55", "podIP":"10.129.1.168", "startTime":"2018-10-25T15:25:22Z", "qosClass":"BestEffort" } } ] } kubeclient-4.6.0/test/json/empty_pod_list.json0000644000004100000410000000022213612310022021536 0ustar www-datawww-data{ "kind": "PodList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/pods", "resourceVersion": "565" }, "items": [] }kubeclient-4.6.0/test/json/endpoint_list.json0000644000004100000410000000221413612310022021361 0ustar www-datawww-data{ "kind": "EndpointsList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces/endpoints", "resourceVersion": "39" }, "items": [ { "metadata": { "name": "example", "namespace": "default", "selfLink": "/api/v1/namespaces/default/endpoints/example", "uid": "db467530-b6aa-11e4-974a-525400c903c1", "resourceVersion": "38", "creationTimestamp": "2015-02-17T08:42:46-05:00" }, "endpoints": [ "172.17.0.63:80", "172.17.0.64:80" ] }, { "metadata": { "name": "kubernetes", "namespace": "default", "selfLink": "/api/v1/namespaces/default/endpoints/kubernetes", "resourceVersion": "8", "creationTimestamp": null }, "endpoints": [ "192.168.122.4:6443" ] }, { "metadata": { "name": "kubernetes-ro", "namespace": "default", "selfLink": "/api/v1/namespaces/default/endpoints/kubernetes-ro", "resourceVersion": "7", "creationTimestamp": null }, "endpoints": [ "192.168.122.4:7080" ] } ] } kubeclient-4.6.0/test/json/versions_list.json0000644000004100000410000000007413612310022021413 0ustar www-datawww-data{ "versions": [ "v1beta3", "v1" ] } kubeclient-4.6.0/test/json/processed_template.json0000644000004100000410000000106313612310022022371 0ustar www-datawww-data{ "kind": "Template", "apiVersion": "v1", "metadata": { "name": "my-templtae", "namespace": "default", "selfLink": "/oapi/v1/namespaces/default/processedtemplates/my-templtae", "uid": "2240c61c-8f70-11e5-a806-001a4a231290", "resourceVersion": "1399", "creationTimestamp": "2015-11-20T10:19:07Z" }, "objects": [ { "apiVersion": "v1", "kind": "Service", "metadata": { "name": "test/my-service" } } ], "parameters": [ { "name": "NAME_PREFIX", "value": "test/" } ] } kubeclient-4.6.0/test/json/component_status.json0000644000004100000410000000052113612310022022112 0ustar www-datawww-data{ "kind": "ComponentStatus", "apiVersion": "v1", "metadata": { "name": "etcd-0", "selfLink": "/api/v1/namespaces/componentstatuses/etcd-0", "creationTimestamp": null }, "conditions": [ { "type": "Healthy", "status": "True", "message": "{\"health\": \"true\"}", "error": "nil" } ] }kubeclient-4.6.0/test/json/service_illegal_json_404.json0000644000004100000410000000002313612310022023253 0ustar www-datawww-data404: Page Not Foundkubeclient-4.6.0/test/json/extensions_v1beta1_api_resource_list.json0000644000004100000410000000765113612310022026035 0ustar www-datawww-data{ "kind": "APIResourceList", "groupVersion": "extensions/v1beta1", "resources": [ { "name": "daemonsets", "singularName": "", "namespaced": true, "kind": "DaemonSet", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "ds" ] }, { "name": "daemonsets/status", "singularName": "", "namespaced": true, "kind": "DaemonSet", "verbs": [ "get", "patch", "update" ] }, { "name": "deployments", "singularName": "", "namespaced": true, "kind": "Deployment", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "deploy" ] }, { "name": "deployments/rollback", "singularName": "", "namespaced": true, "kind": "DeploymentRollback", "verbs": [ "create" ] }, { "name": "deployments/scale", "singularName": "", "namespaced": true, "group": "extensions", "version": "v1beta1", "kind": "Scale", "verbs": [ "get", "patch", "update" ] }, { "name": "deployments/status", "singularName": "", "namespaced": true, "kind": "Deployment", "verbs": [ "get", "patch", "update" ] }, { "name": "ingresses", "singularName": "", "namespaced": true, "kind": "Ingress", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "ing" ] }, { "name": "ingresses/status", "singularName": "", "namespaced": true, "kind": "Ingress", "verbs": [ "get", "patch", "update" ] }, { "name": "networkpolicies", "singularName": "", "namespaced": true, "kind": "NetworkPolicy", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "netpol" ] }, { "name": "podsecuritypolicies", "singularName": "", "namespaced": false, "kind": "PodSecurityPolicy", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "psp" ] }, { "name": "replicasets", "singularName": "", "namespaced": true, "kind": "ReplicaSet", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "rs" ] }, { "name": "replicasets/scale", "singularName": "", "namespaced": true, "group": "extensions", "version": "v1beta1", "kind": "Scale", "verbs": [ "get", "patch", "update" ] }, { "name": "replicasets/status", "singularName": "", "namespaced": true, "kind": "ReplicaSet", "verbs": [ "get", "patch", "update" ] }, { "name": "replicationcontrollers", "singularName": "", "namespaced": true, "kind": "ReplicationControllerDummy", "verbs": [] }, { "name": "replicationcontrollers/scale", "singularName": "", "namespaced": true, "kind": "Scale", "verbs": [ "get", "patch", "update" ] } ] }kubeclient-4.6.0/test/json/core_api_resource_list.json0000644000004100000410000000664213612310022023242 0ustar www-datawww-data{ "kind": "APIResourceList", "groupVersion": "v1", "resources": [ { "name": "bindings", "namespaced": true, "kind": "Binding" }, { "name": "componentstatuses", "namespaced": false, "kind": "ComponentStatus" }, { "name": "configmaps", "namespaced": true, "kind": "ConfigMap" }, { "name": "endpoints", "namespaced": true, "kind": "Endpoints" }, { "name": "events", "namespaced": true, "kind": "Event" }, { "name": "limitranges", "namespaced": true, "kind": "LimitRange" }, { "name": "namespaces", "namespaced": false, "kind": "Namespace" }, { "name": "namespaces/finalize", "namespaced": false, "kind": "Namespace" }, { "name": "namespaces/status", "namespaced": false, "kind": "Namespace" }, { "name": "nodes", "namespaced": false, "kind": "Node" }, { "name": "nodes/proxy", "namespaced": false, "kind": "Node" }, { "name": "nodes/status", "namespaced": false, "kind": "Node" }, { "name": "persistentvolumeclaims", "namespaced": true, "kind": "PersistentVolumeClaim" }, { "name": "persistentvolumeclaims/status", "namespaced": true, "kind": "PersistentVolumeClaim" }, { "name": "persistentvolumes", "namespaced": false, "kind": "PersistentVolume" }, { "name": "persistentvolumes/status", "namespaced": false, "kind": "PersistentVolume" }, { "name": "pods", "namespaced": true, "kind": "Pod" }, { "name": "pods/attach", "namespaced": true, "kind": "Pod" }, { "name": "pods/binding", "namespaced": true, "kind": "Binding" }, { "name": "pods/exec", "namespaced": true, "kind": "Pod" }, { "name": "pods/log", "namespaced": true, "kind": "Pod" }, { "name": "pods/portforward", "namespaced": true, "kind": "Pod" }, { "name": "pods/proxy", "namespaced": true, "kind": "Pod" }, { "name": "pods/status", "namespaced": true, "kind": "Pod" }, { "name": "podtemplates", "namespaced": true, "kind": "PodTemplate" }, { "name": "replicationcontrollers", "namespaced": true, "kind": "ReplicationController" }, { "name": "replicationcontrollers/scale", "namespaced": true, "kind": "Scale" }, { "name": "replicationcontrollers/status", "namespaced": true, "kind": "ReplicationController" }, { "name": "resourcequotas", "namespaced": true, "kind": "ResourceQuota" }, { "name": "resourcequotas/status", "namespaced": true, "kind": "ResourceQuota" }, { "name": "secrets", "namespaced": true, "kind": "Secret" }, { "name": "serviceaccounts", "namespaced": true, "kind": "ServiceAccount" }, { "name": "services", "namespaced": true, "kind": "Service" }, { "name": "services/proxy", "namespaced": true, "kind": "Service" }, { "name": "services/status", "namespaced": true, "kind": "Service" } ] } kubeclient-4.6.0/test/json/created_endpoint.json0000644000004100000410000000105213612310022022014 0ustar www-datawww-data{ "kind": "Endpoints", "apiVersion": "v1", "metadata": { "name": "myendpoint", "namespace": "default", "selfLink": "/api/v1/namespaces/default/endpoints/myendpoint", "uid": "59d05b48-dadb-11e5-937e-18037327aaeb", "resourceVersion": "393", "creationTimestamp": "2016-02-24T09:45:34Z" }, "subsets": [ { "addresses": [ { "ip": "172.17.0.25" } ], "ports": [ { "name": "https", "port": 6443, "protocol": "TCP" } ] } ] }kubeclient-4.6.0/test/json/namespace_exception.json0000644000004100000410000000027113612310022022521 0ustar www-datawww-data{ "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "converting to : type names don't match (Pod, Namespace)", "code": 500 }kubeclient-4.6.0/test/json/resource_quota_list.json0000644000004100000410000000254313612310022022606 0ustar www-datawww-data{ "kind": "ResourceQuotaList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces/quota-example/resourcequotas/", "resourceVersion": "102452" }, "items": [ { "metadata": { "name": "quota", "namespace": "quota-example", "selfLink": "/api/v1/namespaces/quota-example/resourcequotas/quota", "uid": "ab9f24a4-3c43-11e5-8214-0aaeec44370e", "resourceVersion": "12919", "creationTimestamp": "2015-08-06T14:01:44Z" }, "spec": { "hard": { "cpu": "20", "memory": "1Gi", "persistentvolumeclaims": "10", "pods": "10", "replicationcontrollers": "20", "resourcequotas": "1", "secrets": "10", "services": "5" } }, "status": { "hard": { "cpu": "20", "memory": "1Gi", "persistentvolumeclaims": "10", "pods": "10", "replicationcontrollers": "20", "resourcequotas": "1", "secrets": "10", "services": "5" }, "used": { "cpu": "0", "memory": "0", "persistentvolumeclaims": "0", "pods": "0", "replicationcontrollers": "1", "resourcequotas": "1", "secrets": "9", "services": "0" } } } ] }kubeclient-4.6.0/test/json/service_list.json0000644000004100000410000000605113612310022021204 0ustar www-datawww-data{ "kind": "ServiceList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/services", "resourceVersion": "36727" }, "items": [ { "metadata": { "name": "kubernetes", "namespace": "default", "selfLink": "/api/v1/namespaces/default/services/kubernetes", "uid": "b6606490-db86-11e4-b293-f8b156af4ae1", "resourceVersion": "6", "creationTimestamp": "2015-04-05T11:27:15Z", "labels": { "component": "apiserver", "provider": "kubernetes" } }, "spec": { "ports": [ { "name": "", "protocol": "TCP", "port": 443, "targetPort": 443 } ], "selector": null, "clusterIP": "10.0.0.2", "sessionAffinity": "None" }, "status": {} }, { "metadata": { "name": "kubernetes-ro", "namespace": "default", "selfLink": "/api/v1/namespaces/default/services/kubernetes-ro", "uid": "b6606694-db86-11e4-b293-f8b156af4ae1", "resourceVersion": "5", "creationTimestamp": "2015-04-05T11:27:15Z", "labels": { "component": "apiserver", "provider": "kubernetes" } }, "spec": { "ports": [ { "name": "", "protocol": "TCP", "port": 80, "targetPort": 80 } ], "selector": null, "clusterIP": "10.0.0.1", "sessionAffinity": "None" }, "status": {} }, { "metadata": { "name": "redis-slave", "namespace": "development", "selfLink": "/api/v1/namespaces/development/services/redis-slave", "uid": "bdb80a8f-db93-11e4-b293-f8b156af4ae1", "resourceVersion": "2815", "creationTimestamp": "2015-04-05T13:00:31Z", "labels": { "name": "redis", "role": "slave" } }, "spec": { "ports": [ { "name": "", "protocol": "TCP", "port": 6379, "targetPort": "redis-server" } ], "selector": { "name": "redis", "role": "slave" }, "clusterIP": "10.0.0.140", "sessionAffinity": "None" }, "status": {} } ] }kubeclient-4.6.0/test/json/persistent_volume_claims_nil_items.json0000644000004100000410000000024713612310022025674 0ustar www-datawww-data{ "kind": "PersistentVolumeClaimList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/persistentvolumeclaims", "resourceVersion": "1089012" } }kubeclient-4.6.0/test/json/replication_controller_list.json0000644000004100000410000000455513612310022024327 0ustar www-datawww-data{ "kind": "ReplicationControllerList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces/default/replicationcontrollers", "resourceVersion": "1636" }, "items": [ { "metadata": { "name": "redis-master-controller", "namespace": "default", "selfLink": "/api/v1/namespaces/default/replicationcontrollers/redis-master-controller?namespace=default", "uid": "108eb547-cefa-11e4-ac24-3c970e4a436a", "resourceVersion": "1631", "creationTimestamp": "2015-03-20T14:10:14+02:00", "labels": { "name": "redis-master" } }, "spec": { "replicas": 1, "selector": { "name": "redis-master" }, "template": { "metadata": { "creationTimestamp": null, "labels": { "app": "redis", "name": "redis-master" } }, "spec": { "volumes": null, "containers": [ { "name": "redis-master", "image": "dockerfile/redis", "ports": [ { "containerPort": 6379, "protocol": "TCP" } ], "resources": {}, "terminationMessagePath": "/dev/termination-log", "imagePullPolicy": "IfNotPresent", "securityContext": { "capabilities": {} } } ], "restartPolicy": { "always": {} }, "dnsPolicy": "ClusterFirst" } } }, "status": { "replicas": 1 } } ] }kubeclient-4.6.0/test/json/config.istio.io_api_resource_list.json0000644000004100000410000003070713612310022025312 0ustar www-datawww-data{ "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "config.istio.io/v1alpha2", "resources": [ { "name": "metrics", "singularName": "metric", "namespaced": true, "kind": "metric", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "servicecontrolreports", "singularName": "servicecontrolreport", "namespaced": true, "kind": "servicecontrolreport", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "opas", "singularName": "opa", "namespaced": true, "kind": "opa", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "redisquotas", "singularName": "redisquota", "namespaced": true, "kind": "redisquota", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "authorizations", "singularName": "authorization", "namespaced": true, "kind": "authorization", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "listentries", "singularName": "listentry", "namespaced": true, "kind": "listentry", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "logentries", "singularName": "logentry", "namespaced": true, "kind": "logentry", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "tracespans", "singularName": "tracespan", "namespaced": true, "kind": "tracespan", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "quotaspecs", "singularName": "quotaspec", "namespaced": true, "kind": "QuotaSpec", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "fluentds", "singularName": "fluentd", "namespaced": true, "kind": "fluentd", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "rbacs", "singularName": "rbac", "namespaced": true, "kind": "rbac", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "checknothings", "singularName": "checknothing", "namespaced": true, "kind": "checknothing", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "edges", "singularName": "edge", "namespaced": true, "kind": "edge", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "apikeys", "singularName": "apikey", "namespaced": true, "kind": "apikey", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "kuberneteses", "singularName": "kubernetes", "namespaced": true, "kind": "kubernetes", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "httpapispecs", "singularName": "httpapispec", "namespaced": true, "kind": "HTTPAPISpec", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "attributemanifests", "singularName": "attributemanifest", "namespaced": true, "kind": "attributemanifest", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "kubernetesenvs", "singularName": "kubernetesenv", "namespaced": true, "kind": "kubernetesenv", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "listcheckers", "singularName": "listchecker", "namespaced": true, "kind": "listchecker", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "quotas", "singularName": "quota", "namespaced": true, "kind": "quota", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "instances", "singularName": "instance", "namespaced": true, "kind": "instance", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "memquotas", "singularName": "memquota", "namespaced": true, "kind": "memquota", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "noops", "singularName": "noop", "namespaced": true, "kind": "noop", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "prometheuses", "singularName": "prometheus", "namespaced": true, "kind": "prometheus", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "solarwindses", "singularName": "solarwinds", "namespaced": true, "kind": "solarwinds", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "cloudwatches", "singularName": "cloudwatch", "namespaced": true, "kind": "cloudwatch", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "reportnothings", "singularName": "reportnothing", "namespaced": true, "kind": "reportnothing", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "stackdrivers", "singularName": "stackdriver", "namespaced": true, "kind": "stackdriver", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "statsds", "singularName": "statsd", "namespaced": true, "kind": "statsd", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "httpapispecbindings", "singularName": "httpapispecbinding", "namespaced": true, "kind": "HTTPAPISpecBinding", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "quotaspecbindings", "singularName": "quotaspecbinding", "namespaced": true, "kind": "QuotaSpecBinding", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "bypasses", "singularName": "bypass", "namespaced": true, "kind": "bypass", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "circonuses", "singularName": "circonus", "namespaced": true, "kind": "circonus", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "deniers", "singularName": "denier", "namespaced": true, "kind": "denier", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "signalfxs", "singularName": "signalfx", "namespaced": true, "kind": "signalfx", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "adapters", "singularName": "adapter", "namespaced": true, "kind": "adapter", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "servicecontrols", "singularName": "servicecontrol", "namespaced": true, "kind": "servicecontrol", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "templates", "singularName": "template", "namespaced": true, "kind": "template", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "handlers", "singularName": "handler", "namespaced": true, "kind": "handler", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "rules", "singularName": "rule", "namespaced": true, "kind": "rule", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "dogstatsds", "singularName": "dogstatsd", "namespaced": true, "kind": "dogstatsd", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] }, { "name": "stdios", "singularName": "stdio", "namespaced": true, "kind": "stdio", "verbs": [ "delete", "deletecollection", "get", "list", "patch", "create", "update", "watch" ] } ] }kubeclient-4.6.0/test/json/service_account_list.json0000644000004100000410000000472713612310022022730 0ustar www-datawww-data{ "kind": "List", "apiVersion": "v1", "metadata": {}, "items": [ { "kind": "ServiceAccount", "apiVersion": "v1", "metadata": { "name": "builder", "namespace": "default", "selfLink": "/api/v1/namespaces/default/serviceaccounts/builder", "uid": "d40655f6-6bf0-11e5-843a-525400f8b93e", "resourceVersion": "133", "creationTimestamp": "2015-10-06T06:09:39Z" }, "secrets": [ { "name": "builder-token-5v6z2" }, { "name": "builder-dockercfg-qe2re" } ], "imagePullSecrets": [ { "name": "builder-dockercfg-qe2re" } ] }, { "kind": "ServiceAccount", "apiVersion": "v1", "metadata": { "name": "default", "namespace": "default", "selfLink": "/api/v1/namespaces/default/serviceaccounts/default", "uid": "d3d773f4-6bf0-11e5-843a-525400f8b93e", "resourceVersion": "94", "creationTimestamp": "2015-10-06T06:09:39Z" }, "secrets": [ { "name": "default-token-6s23q" }, { "name": "default-dockercfg-62tf3" } ], "imagePullSecrets": [ { "name": "default-dockercfg-62tf3" } ] }, { "kind": "ServiceAccount", "apiVersion": "v1", "metadata": { "name": "deployer", "namespace": "default", "selfLink": "/api/v1/namespaces/default/serviceaccounts/deployer", "uid": "d41d385e-6bf0-11e5-843a-525400f8b93e", "resourceVersion": "137", "creationTimestamp": "2015-10-06T06:09:39Z" }, "secrets": [ { "name": "deployer-token-h3i57" }, { "name": "deployer-dockercfg-qgjjj" } ], "imagePullSecrets": [ { "name": "deployer-dockercfg-qgjjj" } ] } ] } kubeclient-4.6.0/test/json/pod.json0000644000004100000410000000545613612310022017303 0ustar www-datawww-data{ "kind": "Pod", "apiVersion": "v1", "metadata": { "name": "redis-master3", "namespace": "default", "selfLink": "/api/v1/pods/redis-master3?namespace=default", "uid": "a344023f-a23c-11e4-a36b-3c970e4a436a", "resourceVersion": "9", "creationTimestamp": "2015-01-22T15:43:24+02:00", "labels": { "name": "redis-master" } }, "spec": { "volumes": null, "containers": [ { "name": "master", "image": "dockerfile/redis", "ports": [ { "hostPort": 6379, "containerPort": 6379, "protocol": "TCP" } ], "memory": "0", "cpu": "100m", "imagePullPolicy": "" }, { "name": "php-redis", "image": "kubernetes/example-guestbook-php-redis", "ports": [ { "hostPort": 8000, "containerPort": 80, "protocol": "TCP" } ], "memory": "50000000", "cpu": "100m", "imagePullPolicy": "" } ], "restartPolicy": { "always": { } }, "dnsPolicy": "ClusterFirst" }, "status": { "phase": "Running", "host": "127.0.0.1", "podIP": "172.17.0.2", "info": { "master": { "state": { "running": { "startedAt": "2015-01-22T13:43:29Z" } }, "restartCount": 0, "containerID": "docker://87458d9a12f9dc9a01b52c1eee5f09cf48939380271c0eaf31af298ce67b125e", "image": "dockerfile/redis" }, "net": { "state": { "running": { "startedAt": "2015-01-22T13:43:27Z" } }, "restartCount": 0, "containerID": "docker://3bb5ced1f831322d370f70b58137e1dd41216c2960b7a99394542b5230cbd259", "podIP": "172.17.0.2", "image": "kubernetes/pause:latest" }, "php-redis": { "state": { "running": { "startedAt": "2015-01-22T13:43:31Z" } }, "restartCount": 0, "containerID": "docker://5f08685c0a7a5c974d438a52c6560d72bb0aae7e805d2a34302b9b460f1297c7", "image": "kubernetes/example-guestbook-php-redis" } } } } kubeclient-4.6.0/test/json/persistent_volume.json0000644000004100000410000000145413612310022022302 0ustar www-datawww-data{ "kind": "PersistentVolume", "apiVersion": "v1", "metadata": { "name": "pv0001", "selfLink": "/api/v1/persistentvolumes/pv0001", "uid": "c83eece1-4b38-11e5-8d27-28d2447dcefe", "resourceVersion": "1585", "creationTimestamp": "2015-08-25T14:51:35Z", "labels": { "type": "local" } }, "spec": { "capacity": { "storage": "10Gi" }, "hostPath": { "path": "/tmp/data01" }, "accessModes": [ "ReadWriteOnce" ], "claimRef": { "kind": "PersistentVolumeClaim", "namespace": "default", "name": "myclaim-1", "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe", "apiVersion": "v1", "resourceVersion": "1582" }, "persistentVolumeReclaimPolicy": "Retain" }, "status": { "phase": "Bound" } } kubeclient-4.6.0/test/json/namespace_list.json0000644000004100000410000000163413612310022021502 0ustar www-datawww-data{ "kind": "NamespaceList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces", "resourceVersion": "1707" }, "items": [ { "metadata": { "name": "default", "selfLink": "/api/v1/namespaces/default", "uid": "56c3eb7c-c009-11e4-a514-3c970e4a436a", "resourceVersion": "4", "creationTimestamp": "2015-03-01T13:51:47+02:00" }, "spec": {}, "status": {} }, { "metadata": { "name": "staging", "selfLink": "/api/v1/namespaces/staging", "uid": "e388bc10-c021-11e4-a514-3c970e4a436a", "resourceVersion": "1168", "creationTimestamp": "2015-03-01T16:47:31+02:00" }, "spec": {}, "status": {} } ] }kubeclient-4.6.0/test/json/template.openshift.io_api_resource_list.json0000644000004100000410000000261013612310022026520 0ustar www-datawww-data{ "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "template.openshift.io/v1", "resources": [ { "name": "brokertemplateinstances", "singularName": "", "namespaced": false, "kind": "BrokerTemplateInstance", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ] }, { "name": "processedtemplates", "singularName": "", "namespaced": true, "kind": "Template", "verbs": [ "create" ] }, { "name": "templateinstances", "singularName": "", "namespaced": true, "kind": "TemplateInstance", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ] }, { "name": "templateinstances/status", "singularName": "", "namespaced": true, "kind": "TemplateInstance", "verbs": [ "get", "patch", "update" ] }, { "name": "templates", "singularName": "", "namespaced": true, "kind": "Template", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ] } ] }kubeclient-4.6.0/test/json/limit_range.json0000644000004100000410000000076013612310022021004 0ustar www-datawww-data{ "kind": "LimitRange", "apiVersion": "v1", "metadata": { "name": "limits", "namespace": "quota-example", "selfLink": "/api/v1/namespaces/quota-example/limitranges/limits", "uid": "7a76a44c-3e9d-11e5-8214-0aaeec44370e", "resourceVersion": "103384", "creationTimestamp": "2015-08-09T13:49:39Z" }, "spec": { "limits": [ { "type": "Container", "default": { "cpu": "100m", "memory": "512Mi" } } ] } }kubeclient-4.6.0/test/json/limit_range_list.json0000644000004100000410000000132313612310022022033 0ustar www-datawww-data{ "kind": "LimitRangeList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces/quota-example/limitranges/", "resourceVersion": "103421" }, "items": [ { "metadata": { "name": "limits", "namespace": "quota-example", "selfLink": "/api/v1/namespaces/quota-example/limitranges/limits", "uid": "7a76a44c-3e9d-11e5-8214-0aaeec44370e", "resourceVersion": "103384", "creationTimestamp": "2015-08-09T13:49:39Z" }, "spec": { "limits": [ { "type": "Container", "default": { "cpu": "100m", "memory": "512Mi" } } ] } } ] }kubeclient-4.6.0/test/json/security.openshift.io_api_resource_list.json0000644000004100000410000000257013612310022026561 0ustar www-datawww-data{ "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "security.openshift.io/v1", "resources": [ { "name": "podsecuritypolicyreviews", "singularName": "", "namespaced": true, "kind": "PodSecurityPolicyReview", "verbs": [ "create" ] }, { "name": "podsecuritypolicyselfsubjectreviews", "singularName": "", "namespaced": true, "kind": "PodSecurityPolicySelfSubjectReview", "verbs": [ "create" ] }, { "name": "podsecuritypolicysubjectreviews", "singularName": "", "namespaced": true, "kind": "PodSecurityPolicySubjectReview", "verbs": [ "create" ] }, { "name": "rangeallocations", "singularName": "", "namespaced": false, "kind": "RangeAllocation", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ] }, { "name": "securitycontextconstraints", "singularName": "", "namespaced": false, "kind": "SecurityContextConstraints", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "update", "watch" ], "shortNames": [ "scc" ] } ] }kubeclient-4.6.0/test/json/pods_1.json0000644000004100000410000001631713612310022017704 0ustar www-datawww-data{ "kind":"PodList", "apiVersion":"v1", "metadata":{ "selfLink":"/api/v1/pods", "resourceVersion":"53225946", "continue":"eyJ2IjoibWV0YS5rOHMua" }, "items":[ { "metadata":{ "name":"my-ruby-project-2-build", "namespace":"my-project", "selfLink":"/api/v1/namespaces/my-project/pods/my-ruby-project-2-build", "uid":"0c478b50-babb-11e8-ba7e-d094660d31fb", "resourceVersion":"42398462", "creationTimestamp":"2018-09-17T20:48:36Z", "deletionTimestamp":"2018-09-19T13:22:50Z", "deletionGracePeriodSeconds":0, "labels":{ "openshift.io/build.name":"my-ruby-project-2" }, "annotations":{ "openshift.io/build.name":"my-ruby-project-2", "openshift.io/scc":"privileged" }, "ownerReferences":[ { "apiVersion":"build.openshift.io/v1", "kind":"Build", "name":"my-ruby-project-2", "uid":"0c450e6d-babb-11e8-ba7e-d094660d31fb", "controller":true } ], "finalizers":[ "foregroundDeletion" ] }, "spec":{ "volumes":[ { "name":"buildworkdir", "emptyDir":{ } }, { "name":"docker-socket", "hostPath":{ "path":"/var/run/docker.sock", "type":"" } }, { "name":"crio-socket", "hostPath":{ "path":"/var/run/crio/crio.sock", "type":"" } }, { "name":"builder-dockercfg-rjgnm-push", "secret":{ "secretName":"builder-dockercfg-rjgnm", "defaultMode":384 } }, { "name":"builder-token-zkpb6", "secret":{ "secretName":"builder-token-zkpb6", "defaultMode":420 } } ], "containers":[ { "name":"sti-build", "image":"openshift3/ose-sti-builder:v3.9.25", "command":[ "openshift-sti-build" ] } ], "restartPolicy":"Never", "terminationGracePeriodSeconds":30, "dnsPolicy":"ClusterFirst", "nodeSelector":{ "node-role.kubernetes.io/compute":"true" }, "serviceAccountName":"builder", "serviceAccount":"builder", "nodeName":"dell-r430-20.example.com", "securityContext":{ }, "imagePullSecrets":[ { "name":"builder-dockercfg-rjgnm" } ], "schedulerName":"default-scheduler" }, "status":{ "phase":"Failed", "conditions":[ { "type":"Initialized", "status":"True", "lastProbeTime":null, "lastTransitionTime":"2018-09-17T20:48:49Z" }, { "type":"Ready", "status":"False", "lastProbeTime":null, "lastTransitionTime":"2018-09-17T20:49:08Z", "reason":"ContainersNotReady", "message":"containers with unready status: [sti-build]" }, { "type":"PodScheduled", "status":"True", "lastProbeTime":null, "lastTransitionTime":"2018-09-17T20:48:36Z" } ], "hostIP":"10.8.96.55", "podIP":"10.129.0.207", "startTime":"2018-09-17T20:48:36Z", "qosClass":"BestEffort" } }, { "metadata":{ "name":"redis-1-94zxb", "generateName":"redis-1-", "namespace":"customer-logging", "selfLink":"/api/v1/namespaces/customer-logging/pods/redis-1-94zxb", "uid":"a8aea5f4-5f91-11e8-ba7e-d094660d31fb", "resourceVersion":"47622190", "creationTimestamp":"2018-05-24T20:33:03Z", "labels":{ "app":"elastic-log-ripper", "deployment":"redis-1", "deploymentconfig":"redis", "name":"redis" }, "annotations":{ "openshift.io/deployment-config.latest-version":"1", "openshift.io/deployment-config.name":"redis", "openshift.io/deployment.name":"redis-1", "openshift.io/generated-by":"OpenShiftNewApp", "openshift.io/scc":"restricted" }, "ownerReferences":[ { "apiVersion":"v1", "kind":"ReplicationController", "name":"redis-1", "uid":"9e2e46b3-5f91-11e8-ba7e-d094660d31fb", "controller":true, "blockOwnerDeletion":true } ] }, "spec":{ "volumes":[ { "name":"default-token-n2wzs", "secret":{ "secretName":"default-token-n2wzs", "defaultMode":420 } } ], "containers":[ { "name":"redis", "image":"manageiq/redis:latest", "ports":[ { "containerPort":6379, "protocol":"TCP" } ], "resources":{ }, "volumeMounts":[ { "name":"default-token-n2wzs", "readOnly":true, "mountPath":"/var/run/secrets/kubernetes.io/serviceaccount" } ], "terminationMessagePath":"/dev/termination-log", "terminationMessagePolicy":"File", "imagePullPolicy":"Always", "securityContext":{ "capabilities":{ "drop":[ "KILL", "MKNOD", "SETGID", "SETUID" ] }, "runAsUser":1000260000 } } ], "restartPolicy":"Always", "terminationGracePeriodSeconds":30, "dnsPolicy":"ClusterFirst", "nodeSelector":{ "node-role.kubernetes.io/compute":"true" }, "serviceAccountName":"default", "serviceAccount":"default", "nodeName":"dell-r430-20.example.com", "securityContext":{ "seLinuxOptions":{ "level":"s0:c16,c10" }, "fsGroup":1000260000 }, "imagePullSecrets":[ { "name":"default-dockercfg-ck286" } ], "schedulerName":"default-scheduler" }, "status":{ "phase":"Running", "conditions":[ { "type":"Initialized", "status":"True", "lastProbeTime":null, "lastTransitionTime":"2018-05-24T20:33:04Z" }, { "type":"Ready", "status":"True", "lastProbeTime":null, "lastTransitionTime":"2018-09-18T12:06:18Z" }, { "type":"PodScheduled", "status":"True", "lastProbeTime":null, "lastTransitionTime":"2018-05-24T20:33:03Z" } ], "hostIP":"10.8.96.55", "podIP":"10.129.0.222", "startTime":"2018-05-24T20:33:04Z", "qosClass":"BestEffort" } } ] } kubeclient-4.6.0/test/json/namespace.json0000644000004100000410000000051613612310022020445 0ustar www-datawww-data{ "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "staging", "selfLink": "/api/v1/namespaces/staging", "uid": "e388bc10-c021-11e4-a514-3c970e4a436a", "resourceVersion": "1168", "creationTimestamp": "2015-03-01T16:47:31+02:00" }, "spec": {}, "status": {} }kubeclient-4.6.0/test/json/bindings_list.json0000644000004100000410000000031613612310022021337 0ustar www-datawww-data{ "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "the server could not find the requested resource", "reason": "NotFound", "details": {}, "code": 404 }kubeclient-4.6.0/test/json/pod_list.json0000644000004100000410000000534013612310022020326 0ustar www-datawww-data{ "kind": "PodList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/pods", "resourceVersion": "1315" }, "items": [ { "metadata": { "name": "redis-master3", "namespace": "default", "selfLink": "/api/v1/pods/redis-master3?namespace=default", "uid": "1da148b4-cef5-11e4-ac24-3c970e4a436a", "resourceVersion": "1301", "creationTimestamp": "2015-03-20T13:34:48+02:00", "labels": { "mylabel": "mylabelvalue", "role": "pod" } }, "spec": { "volumes": null, "containers": [ { "name": "master", "image": "dockerfile/redis", "ports": [ { "hostPort": 6379, "containerPort": 6379, "protocol": "TCP" } ], "resources": { "limits": { "cpu": "100m" } }, "terminationMessagePath": "/dev/termination-log", "imagePullPolicy": "IfNotPresent", "securityContext": { "capabilities": {} } }, { "name": "php-redis", "image": "kubernetes/example-guestbook-php-redis", "ports": [ { "hostPort": 8000, "containerPort": 80, "protocol": "TCP" } ], "resources": { "limits": { "cpu": "100m", "memory": "50000000" } }, "terminationMessagePath": "/dev/termination-log", "imagePullPolicy": "IfNotPresent", "securityContext": { "capabilities": {} } } ], "restartPolicy": { "always": {} }, "dnsPolicy": "ClusterFirst" }, "status": { "phase": "Pending" } } ] }kubeclient-4.6.0/test/json/service_update.json0000644000004100000410000000065613612310022021520 0ustar www-datawww-data{ "status" : {}, "kind" : "Service", "apiVersion" : "v1", "spec" : { "ports" : [ { "targetPort" : 80, "nodePort" : 0, "port" : 80, "protocol" : "TCP" } ], "clusterIP" : "1.2.3.4" }, "metadata" : { "name" : "my_service", "creationTimestamp" : null, "namespace" : "default", "resourceVersion" : "2" } } kubeclient-4.6.0/test/json/security_context_constraint_list.json0000644000004100000410000002574713612310022025440 0ustar www-datawww-data{ "kind": "SecurityContextConstraintsList", "apiVersion": "security.openshift.io/v1", "metadata": { "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints", "resourceVersion": "5751" }, "items": [ { "metadata": { "name": "anyuid", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/anyuid", "uid": "12ba8540-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "71", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "anyuid provides all features of the restricted SCC but allows users to run with any UID and any GID." } }, "priority": 10, "allowPrivilegedContainer": false, "defaultAddCapabilities": null, "requiredDropCapabilities": [ "MKNOD" ], "allowedCapabilities": null, "allowHostDirVolumePlugin": false, "volumes": [ "configMap", "downwardAPI", "emptyDir", "persistentVolumeClaim", "projected", "secret" ], "allowedFlexVolumes": null, "allowHostNetwork": false, "allowHostPorts": false, "allowHostPID": false, "allowHostIPC": false, "seLinuxContext": { "type": "MustRunAs" }, "runAsUser": { "type": "RunAsAny" }, "supplementalGroups": { "type": "RunAsAny" }, "fsGroup": { "type": "RunAsAny" }, "readOnlyRootFilesystem": false, "users": [], "groups": [ "system:cluster-admins" ] }, { "metadata": { "name": "hostaccess", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/hostaccess", "uid": "12b5b3a2-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "69", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "hostaccess allows access to all host namespaces but still requires pods to be run with a UID and SELinux context that are allocated to the namespace. WARNING: this SCC allows host access to namespaces, file systems, and PIDS. It should only be used by trusted pods. Grant with caution." } }, "priority": null, "allowPrivilegedContainer": false, "defaultAddCapabilities": null, "requiredDropCapabilities": [ "KILL", "MKNOD", "SETUID", "SETGID" ], "allowedCapabilities": null, "allowHostDirVolumePlugin": true, "volumes": [ "configMap", "downwardAPI", "emptyDir", "hostPath", "persistentVolumeClaim", "projected", "secret" ], "allowedFlexVolumes": null, "allowHostNetwork": true, "allowHostPorts": true, "allowHostPID": true, "allowHostIPC": true, "seLinuxContext": { "type": "MustRunAs" }, "runAsUser": { "type": "MustRunAsRange" }, "supplementalGroups": { "type": "RunAsAny" }, "fsGroup": { "type": "MustRunAs" }, "readOnlyRootFilesystem": false, "users": [], "groups": [] }, { "metadata": { "name": "hostmount-anyuid", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/hostmount-anyuid", "uid": "12b512c0-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "68", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "hostmount-anyuid provides all the features of the restricted SCC but allows host mounts and any UID by a pod. This is primarily used by the persistent volume recycler. WARNING: this SCC allows host file system access as any UID, including UID 0. Grant with caution." } }, "priority": null, "allowPrivilegedContainer": false, "defaultAddCapabilities": null, "requiredDropCapabilities": [ "MKNOD" ], "allowedCapabilities": null, "allowHostDirVolumePlugin": true, "volumes": [ "configMap", "downwardAPI", "emptyDir", "hostPath", "nfs", "persistentVolumeClaim", "projected", "secret" ], "allowedFlexVolumes": null, "allowHostNetwork": false, "allowHostPorts": false, "allowHostPID": false, "allowHostIPC": false, "seLinuxContext": { "type": "MustRunAs" }, "runAsUser": { "type": "RunAsAny" }, "supplementalGroups": { "type": "RunAsAny" }, "fsGroup": { "type": "RunAsAny" }, "readOnlyRootFilesystem": false, "users": [ "system:serviceaccount:openshift-infra:pv-recycler-controller" ], "groups": [] }, { "metadata": { "name": "hostnetwork", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/hostnetwork", "uid": "12bb0984-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "72", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "hostnetwork allows using host networking and host ports but still requires pods to be run with a UID and SELinux context that are allocated to the namespace." } }, "priority": null, "allowPrivilegedContainer": false, "defaultAddCapabilities": null, "requiredDropCapabilities": [ "KILL", "MKNOD", "SETUID", "SETGID" ], "allowedCapabilities": null, "allowHostDirVolumePlugin": false, "volumes": [ "configMap", "downwardAPI", "emptyDir", "persistentVolumeClaim", "projected", "secret" ], "allowedFlexVolumes": null, "allowHostNetwork": true, "allowHostPorts": true, "allowHostPID": false, "allowHostIPC": false, "seLinuxContext": { "type": "MustRunAs" }, "runAsUser": { "type": "MustRunAsRange" }, "supplementalGroups": { "type": "MustRunAs" }, "fsGroup": { "type": "MustRunAs" }, "readOnlyRootFilesystem": false, "users": [], "groups": [] }, { "metadata": { "name": "nonroot", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/nonroot", "uid": "12b37c59-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "67", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "nonroot provides all features of the restricted SCC but allows users to run with any non-root UID. The user must specify the UID or it must be specified on the by the manifest of the container runtime." } }, "priority": null, "allowPrivilegedContainer": false, "defaultAddCapabilities": null, "requiredDropCapabilities": [ "KILL", "MKNOD", "SETUID", "SETGID" ], "allowedCapabilities": null, "allowHostDirVolumePlugin": false, "volumes": [ "configMap", "downwardAPI", "emptyDir", "persistentVolumeClaim", "projected", "secret" ], "allowedFlexVolumes": null, "allowHostNetwork": false, "allowHostPorts": false, "allowHostPID": false, "allowHostIPC": false, "seLinuxContext": { "type": "MustRunAs" }, "runAsUser": { "type": "MustRunAsNonRoot" }, "supplementalGroups": { "type": "RunAsAny" }, "fsGroup": { "type": "RunAsAny" }, "readOnlyRootFilesystem": false, "users": [], "groups": [] }, { "metadata": { "name": "privileged", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/privileged", "uid": "12b18f4a-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "300", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "privileged allows access to all privileged and host features and the ability to run as any user, any group, any fsGroup, and with any SELinux context. WARNING: this is the most relaxed SCC and should be used only for cluster administration. Grant with caution." } }, "priority": null, "allowPrivilegedContainer": true, "defaultAddCapabilities": null, "requiredDropCapabilities": null, "allowedCapabilities": [ "*" ], "allowHostDirVolumePlugin": true, "volumes": [ "*" ], "allowedFlexVolumes": null, "allowHostNetwork": true, "allowHostPorts": true, "allowHostPID": true, "allowHostIPC": true, "seLinuxContext": { "type": "RunAsAny" }, "runAsUser": { "type": "RunAsAny" }, "supplementalGroups": { "type": "RunAsAny" }, "fsGroup": { "type": "RunAsAny" }, "readOnlyRootFilesystem": false, "users": [ "system:admin", "system:serviceaccount:openshift-infra:build-controller", "system:serviceaccount:default:pvinstaller", "system:serviceaccount:default:registry", "system:serviceaccount:default:router" ], "groups": [ "system:cluster-admins", "system:nodes", "system:masters" ], "seccompProfiles": [ "*" ] }, { "metadata": { "name": "restricted", "selfLink": "/apis/security.openshift.io/v1/securitycontextconstraints/restricted", "uid": "12b9a842-ef00-11e8-b4c0-68f728fac3ab", "resourceVersion": "70", "creationTimestamp": "2018-11-23T09:13:42Z", "annotations": { "kubernetes.io/description": "restricted denies access to all host features and requires pods to be run with a UID, and SELinux context that are allocated to the namespace. This is the most restrictive SCC and it is used by default for authenticated users." } }, "priority": null, "allowPrivilegedContainer": false, "defaultAddCapabilities": null, "requiredDropCapabilities": [ "KILL", "MKNOD", "SETUID", "SETGID" ], "allowedCapabilities": null, "allowHostDirVolumePlugin": false, "volumes": [ "configMap", "downwardAPI", "emptyDir", "persistentVolumeClaim", "projected", "secret" ], "allowedFlexVolumes": null, "allowHostNetwork": false, "allowHostPorts": false, "allowHostPID": false, "allowHostIPC": false, "seLinuxContext": { "type": "MustRunAs" }, "runAsUser": { "type": "MustRunAsRange" }, "supplementalGroups": { "type": "RunAsAny" }, "fsGroup": { "type": "MustRunAs" }, "readOnlyRootFilesystem": false, "users": [], "groups": [ "system:authenticated" ] } ] } kubeclient-4.6.0/test/json/core_api_resource_list_without_kind.json0000644000004100000410000000427113612310022026026 0ustar www-datawww-data{ "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 } ] } kubeclient-4.6.0/test/json/service_patch.json0000644000004100000410000000067213612310022021333 0ustar www-datawww-data{ "status" : {}, "kind" : "Service", "apiVersion" : "v1", "spec" : { "ports" : [ { "targetPort" : 80, "nodePort" : 0, "port" : 80, "protocol" : "TCP" } ], "clusterIP" : "1.2.3.4" }, "metadata" : { "name" : "my_service", "creationTimestamp" : null, "namespace" : "development", "resourceVersion" : "2", "annotations" : { "key" : "value" } } } kubeclient-4.6.0/test/json/template.json0000644000004100000410000000132513612310022020323 0ustar www-datawww-data{ "apiVersion": "template.openshift.io/v1", "kind": "Template", "metadata": { "creationTimestamp": "2018-12-17T16:11:36Z", "name": "my-template", "namespace": "default", "resourceVersion": "21954", "selfLink": "/apis/template.openshift.io/v1/namespaces/default/templates/my-template", "uid": "6e03e3e6-0216-11e9-b1e0-68f728fac3ab" }, "objects": [ { "apiVersion": "v1", "kind": "Service", "metadata": { "name": "${NAME_PREFIX}my-service" } } ], "parameters": [ { "description": "Prefix for names", "name": "NAME_PREFIX" } ] } kubeclient-4.6.0/test/json/service.json0000644000004100000410000000151013612310022020144 0ustar www-datawww-data{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "redis-slave", "namespace": "development", "selfLink": "/api/v1/namespaces/development/services/redis-slave", "uid": "bdb80a8f-db93-11e4-b293-f8b156af4ae1", "resourceVersion": "2815", "creationTimestamp": "2015-04-05T13:00:31Z", "labels": { "name": "redis", "role": "slave" } }, "spec": { "ports": [ { "name": "", "protocol": "TCP", "port": 6379, "targetPort": "redis-server" } ], "selector": { "name": "redis", "role": "slave" }, "clusterIP": "10.0.0.140", "sessionAffinity": "None" }, "status": {} }kubeclient-4.6.0/test/json/entity_list.json0000644000004100000410000000336413612310022021064 0ustar www-datawww-data{ "kind": "ServiceList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/services", "resourceVersion": "59" }, "items": [ { "metadata": { "name": "kubernetes", "namespace": "default", "selfLink": "/api/v1/services/kubernetes?namespace=default", "uid": "016e9dcd-ce39-11e4-ac24-3c970e4a436a", "resourceVersion": "6", "creationTimestamp": "2015-03-19T15:08:16+02:00", "labels": { "component": "apiserver", "provider": "kubernetes" } }, "spec": { "port": 443, "protocol": "TCP", "selector": null, "clusterIP": "10.0.0.2", "containerPort": 0, "sessionAffinity": "None" }, "status": {} }, { "metadata": { "name": "kubernetes-ro", "namespace": "default", "selfLink": "/api/v1/services/kubernetes-ro?namespace=default", "uid": "015b78bf-ce39-11e4-ac24-3c970e4a436a", "resourceVersion": "5", "creationTimestamp": "2015-03-19T15:08:15+02:00", "labels": { "component": "apiserver", "provider": "kubernetes" } }, "spec": { "port": 80, "protocol": "TCP", "selector": null, "clusterIP": "10.0.0.1", "containerPort": 0, "sessionAffinity": "None" }, "status": {} } ] }kubeclient-4.6.0/test/json/event_list.json0000644000004100000410000000226113612310022020664 0ustar www-datawww-data{ "kind": "EventList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/events", "resourceVersion": "152" }, "items": [ { "metadata": { "name": "php.13c130f78da4641e", "namespace": "default", "selfLink": "/api/v1/events/php.13c130f78da4641e?namespace=default", "uid": "f3a454d2-b03a-11e4-89e4-525400c903c1", "resourceVersion": "178", "creationTimestamp": "2015-02-09T04:06:37-05:00" }, "involvedObject": { "kind": "BoundPod", "namespace": "default", "name": "php", "uid": "64273d20-b03a-11e4-89e4-525400c903c1", "apiVersion": "v1beta1", "fieldPath": "spec.containers{nginx}" }, "reason": "created", "message": "Created with docker id 9ba2a714411d2d0dd1e826b2fe5c3222b5cbfd9dd9133c841585cbb96b8c2c0f", "source": { "component": "kubelet", "host": "127.0.0.1" }, "timestamp": "2015-02-09T04:06:37-05:00" } ] } kubeclient-4.6.0/test/json/service_json_patch.json0000644000004100000410000000072613612310022022364 0ustar www-datawww-data{ "status" : {}, "kind" : "Service", "apiVersion" : "v1", "spec" : { "ports" : [ { "targetPort" : 80, "nodePort" : 0, "port" : 80, "protocol" : "TCP" } ], "clusterIP" : "1.2.3.4", "type": "LoadBalancer" }, "metadata" : { "name" : "my-service", "creationTimestamp" : null, "namespace" : "development", "resourceVersion" : "2", "annotations" : { "key" : "value" } } } kubeclient-4.6.0/test/json/persistent_volume_list.json0000644000004100000410000000206113612310022023330 0ustar www-datawww-data{ "kind": "PersistentVolumeList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/persistentvolumes", "resourceVersion": "2999" }, "items": [ { "metadata": { "name": "pv0001", "selfLink": "/api/v1/persistentvolumes/pv0001", "uid": "c83eece1-4b38-11e5-8d27-28d2447dcefe", "resourceVersion": "1585", "creationTimestamp": "2015-08-25T14:51:35Z", "labels": { "type": "local" } }, "spec": { "capacity": { "storage": "10Gi" }, "hostPath": { "path": "/tmp/data01" }, "accessModes": [ "ReadWriteOnce" ], "claimRef": { "kind": "PersistentVolumeClaim", "namespace": "default", "name": "myclaim-1", "uid": "d47384a3-4b38-11e5-8d27-28d2447dcefe", "apiVersion": "v1", "resourceVersion": "1582" }, "persistentVolumeReclaimPolicy": "Retain" }, "status": { "phase": "Bound" } } ] } kubeclient-4.6.0/test/json/node.json0000644000004100000410000000144613612310022017441 0ustar www-datawww-data{ "kind": "Node", "apiVersion": "v1", "metadata": { "name": "127.0.0.1", "selfLink": "/api/v1/nodes/127.0.0.1", "uid": "041143c5-ce39-11e4-ac24-3c970e4a436a", "resourceVersion": "1724", "creationTimestamp": "2015-03-19T15:08:20+02:00" }, "spec": { "capacity": { "cpu": "1", "memory": "3Gi" } }, "status": { "hostIP": "127.0.0.1", "conditions": [ { "kind": "Ready", "status": "None", "lastProbeTime": "2015-03-20T14:16:52+02:00", "lastTransitionTime": "2015-03-19T15:08:20+02:00", "reason": "Node health check failed: kubelet /healthz endpoint returns not ok" } ] } }kubeclient-4.6.0/test/json/service_account.json0000644000004100000410000000113713612310022021665 0ustar www-datawww-data{ "kind": "ServiceAccount", "apiVersion": "v1", "metadata": { "name": "default", "namespace": "default", "selfLink": "/api/v1/namespaces/default/serviceaccounts/default", "uid": "d3d773f4-6bf0-11e5-843a-525400f8b93e", "resourceVersion": "94", "creationTimestamp": "2015-10-06T06:09:39Z" }, "secrets": [ { "name": "default-token-6s23q" }, { "name": "default-dockercfg-62tf3" } ], "imagePullSecrets": [ { "name": "default-dockercfg-62tf3" } ] } kubeclient-4.6.0/test/json/node_list.json0000644000004100000410000000217113612310022020470 0ustar www-datawww-data{ "kind": "NodeList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/nodes", "resourceVersion": "137" }, "items": [ { "metadata": { "name": "127.0.0.1", "selfLink": "/api/v1/nodes/127.0.0.1", "uid": "041143c5-ce39-11e4-ac24-3c970e4a436a", "resourceVersion": "137", "creationTimestamp": "2015-03-19T15:08:20+02:00" }, "spec": { "capacity": { "cpu": "1", "memory": "3Gi" } }, "status": { "hostIP": "127.0.0.1", "conditions": [ { "kind": "Ready", "status": "None", "lastProbeTime": "2015-03-19T15:29:33+02:00", "lastTransitionTime": "2015-03-19T15:08:20+02:00", "reason": "Node health check failed: kubelet /healthz endpoint returns not ok" } ] } } ] }kubeclient-4.6.0/test/json/pods_410.json0000644000004100000410000000036613612310022020045 0ustar www-datawww-data{ "kind":"Status", "apiVersion":"v1", "metadata":{}, "status":"Failure", "message":"The provided from parameter is too old to display a consistent list result. You must start a new list without the from.", "reason":"Expired", "code":410 } kubeclient-4.6.0/test/json/component_status_list.json0000644000004100000410000000237713612310022023160 0ustar www-datawww-data{ "kind": "ComponentStatusList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/componentstatuses" }, "items": [ { "metadata": { "name": "controller-manager", "selfLink": "/api/v1/namespaces/componentstatuses/controller-manager", "creationTimestamp": null }, "conditions": [ { "type": "Healthy", "status": "Unknown", "error": "Get http://127.0.0.1:10252/healthz: dial tcp 127.0.0.1:10252: connection refused" } ] }, { "metadata": { "name": "scheduler", "selfLink": "/api/v1/namespaces/componentstatuses/scheduler", "creationTimestamp": null }, "conditions": [ { "type": "Healthy", "status": "Unknown", "error": "Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: connection refused" } ] }, { "metadata": { "name": "etcd-0", "selfLink": "/api/v1/namespaces/componentstatuses/etcd-0", "creationTimestamp": null }, "conditions": [ { "type": "Healthy", "status": "True", "message": "{\"health\": \"true\"}", "error": "nil" } ] } ] }kubeclient-4.6.0/test/test_replication_controller.rb0000644000004100000410000000353013612310022023004 0ustar www-datawww-datarequire_relative 'test_helper' # Replication Controller entity tests class TestReplicationController < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/replicationcontrollers}) .to_return(body: open_test_file('replication_controller.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') rc = client.get_replication_controller('frontendController', 'default') assert_instance_of(Kubeclient::Resource, rc) assert_equal('guestbook-controller', rc.metadata.name) assert_equal('c71aa4c0-a240-11e4-a265-3c970e4a436a', rc.metadata.uid) assert_equal('default', rc.metadata.namespace) assert_equal(3, rc.spec.replicas) assert_equal('guestbook', rc.spec.selector.name) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController', times: 1) end def test_delete_replicaset_cascade stub_core_api_list client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') opts = Kubeclient::Resource.new( apiVersion: 'meta/v1', gracePeriodSeconds: 0, kind: 'DeleteOptions', propagationPolicy: 'Foreground' ) stub_request(:delete, 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController') .with(body: opts.to_hash.to_json) .to_return(status: 200, body: open_test_file('replication_controller.json'), headers: {}) rc = client.delete_replication_controller('frontendController', 'default', delete_options: opts) assert_kind_of(RecursiveOpenStruct, rc) assert_requested(:delete, 'http://localhost:8080/api/v1/namespaces/default/replicationcontrollers/frontendController', times: 1) end end kubeclient-4.6.0/test/test_persistent_volume_claim.rb0000644000004100000410000000161313612310022023164 0ustar www-datawww-datarequire_relative 'test_helper' # PersistentVolumeClaim tests class TestPersistentVolumeClaim < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/persistentvolumeclaims}) .to_return(body: open_test_file('persistent_volume_claim.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') claim = client.get_persistent_volume_claim('myclaim-1', 'default') assert_instance_of(Kubeclient::Resource, claim) assert_equal('myclaim-1', claim.metadata.name) assert_equal('3Gi', claim.spec.resources.requests.storage) assert_equal('pv0001', claim.spec.volumeName) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/namespaces/default/persistentvolumeclaims/myclaim-1', times: 1 ) end end kubeclient-4.6.0/test/test_limit_range.rb0000644000004100000410000000152413612310022020523 0ustar www-datawww-datarequire_relative 'test_helper' # LimitRange tests class TestLimitRange < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/limitranges}) .to_return(body: open_test_file('limit_range.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') limit_range = client.get_limit_range('limits', 'quota-example') assert_instance_of(Kubeclient::Resource, limit_range) assert_equal('limits', limit_range.metadata.name) assert_equal('Container', limit_range.spec.limits[0].type) assert_equal('100m', limit_range.spec.limits[0].default.cpu) assert_equal('512Mi', limit_range.spec.limits[0].default.memory) assert_requested( :get, 'http://localhost:8080/api/v1/namespaces/quota-example/limitranges/limits', times: 1 ) end end kubeclient-4.6.0/test/test_pod.rb0000644000004100000410000000427113612310022017015 0ustar www-datawww-datarequire_relative 'test_helper' # Pod entity tests class TestPod < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/pods}) .to_return(body: open_test_file('pod.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') pod = client.get_pod('redis-master-pod', 'default') assert_instance_of(Kubeclient::Resource, pod) assert_equal('redis-master3', pod.metadata.name) assert_equal('dockerfile/redis', pod.spec.containers[0]['image']) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod', times: 1 ) end def test_get_chunks stub_core_api_list stub_request(:get, %r{/pods}) .to_return(body: open_test_file('pods_1.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') pods = client.get_pods(limit: 2) assert_equal(2, pods.count) assert_equal('eyJ2IjoibWV0YS5rOHMua', pods.continue) continue = pods.continue stub_request(:get, %r{/pods}) .to_return(body: open_test_file('pods_2.json'), status: 200) pods = client.get_pods(limit: 2, continue: continue) assert_equal(2, pods.count) assert_nil(pods.continue) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/pods?limit=2', times: 1 ) assert_requested( :get, "http://localhost:8080/api/v1/pods?continue=#{continue}&limit=2", times: 1 ) end def test_get_chunks_410_gone stub_core_api_list stub_request(:get, %r{/pods}) .to_return(body: open_test_file('pods_410.json'), status: 410) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') err = assert_raises Kubeclient::HttpError do client.get_pods(limit: 2, continue: 'eyJ2IjoibWV0YS5') end assert_equal(err.message, "The provided from parameter is too old to display a consistent list result. \ You must start a new list without the from.") end end kubeclient-4.6.0/test/valid_token_file0000644000004100000410000000001413612310022020057 0ustar www-datawww-datavalid_token kubeclient-4.6.0/test/test_node.rb0000644000004100000410000000400213612310022017150 0ustar www-datawww-datarequire_relative 'test_helper' # Node entity tests class TestNode < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/nodes}) .to_return(body: open_test_file('node.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') node = client.get_node('127.0.0.1') assert_instance_of(Kubeclient::Resource, node) assert_equal('041143c5-ce39-11e4-ac24-3c970e4a436a', node.metadata.uid) assert_equal('127.0.0.1', node.metadata.name) assert_equal('1724', node.metadata.resourceVersion) assert_equal('v1', node.apiVersion) assert_equal('2015-03-19T15:08:20+02:00', node.metadata.creationTimestamp) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/nodes/127.0.0.1', times: 1 ) end def test_get_from_json_v1_raw stub_core_api_list stub_request(:get, %r{/nodes}) .to_return(body: open_test_file('node.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') response = client.get_node('127.0.0.1', nil, as: :raw) assert_equal(open_test_file('node.json').read, response) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/nodes/127.0.0.1', times: 1 ) end def test_get_from_json_v1_raw_error stub_request(:get, %r{/nodes}) .to_return(body: open_test_file('node.json'), status: 200) stub_request(:get, %r{/api/v1$}) .to_return(body: open_test_file('core_api_resource_list.json'), status: 500) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') exception = assert_raises(Kubeclient::HttpError) do client.get_node('127.0.0.1', nil, as: :raw) end assert_instance_of(Kubeclient::HttpError, exception) assert_equal('500 Internal Server Error', exception.message) end end kubeclient-4.6.0/test/test_endpoint.rb0000644000004100000410000000423513612310022020053 0ustar www-datawww-datarequire_relative 'test_helper' # kind: 'Endpoints' entity tests. # This is one of the unusual `kind`s that are already plural (https://github.com/kubernetes/kubernetes/issues/8115). # We force singular in method names like 'create_endpoint', # but `kind` should remain plural as in kubernetes. class TestEndpoint < MiniTest::Test def test_create_endpoint stub_core_api_list testing_ep = Kubeclient::Resource.new testing_ep.metadata = {} testing_ep.metadata.name = 'myendpoint' testing_ep.metadata.namespace = 'default' testing_ep.subsets = [ { 'addresses' => [{ 'ip' => '172.17.0.25' }], 'ports' => [{ 'name' => 'https', 'port' => 6443, 'protocol' => 'TCP' }] } ] req_body = '{"metadata":{"name":"myendpoint","namespace":"default"},' \ '"subsets":[{"addresses":[{"ip":"172.17.0.25"}],"ports":[{"name":"https",' \ '"port":6443,"protocol":"TCP"}]}],"kind":"Endpoints","apiVersion":"v1"}' stub_request(:post, 'http://localhost:8080/api/v1/namespaces/default/endpoints') .with(body: req_body) .to_return(body: open_test_file('created_endpoint.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') created_ep = client.create_endpoint(testing_ep) assert_equal('Endpoints', created_ep.kind) assert_equal('v1', created_ep.apiVersion) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1', as: :parsed_symbolized) created_ep = client.create_endpoint(testing_ep) assert_equal('Endpoints', created_ep[:kind]) assert_equal('v1', created_ep[:apiVersion]) end def test_get_endpoints stub_core_api_list stub_request(:get, %r{/endpoints}) .to_return(body: open_test_file('endpoint_list.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') collection = client.get_endpoints(as: :parsed_symbolized) assert_equal('EndpointsList', collection[:kind]) assert_equal('v1', collection[:apiVersion]) # Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic. collection = client.get_endpoints assert_equal('Endpoints', collection.kind) end end kubeclient-4.6.0/test/test_common.rb0000644000004100000410000000633513612310022017526 0ustar www-datawww-data require_relative 'test_helper' # Unit tests for the common module class CommonTest < MiniTest::Test class ClientStub include Kubeclient::ClientMixin end def client @client ||= ClientStub.new end def test_underscore_entity %w[ Pod pod Service service ReplicationController replication_controller Node node Event event Endpoint endpoint Namespace namespace Secret secret ResourceQuota resource_quota LimitRange limit_range PersistentVolume persistent_volume PersistentVolumeClaim persistent_volume_claim ComponentStatus component_status ServiceAccount service_account Project project Route route ClusterRoleBinding cluster_role_binding Build build BuildConfig build_config Image image ImageStream image_stream dogstatsd dogstatsd lowerCamelUPPERCase lower_camel_uppercase HTTPAPISpecBinding httpapispec_binding APIGroup apigroup APIGroupList apigroup_list APIResourceList apiresource_list APIService apiservice APIServiceList apiservice_list APIVersions apiversions OAuthAccessToken oauth_access_token OAuthAccessTokenList oauth_access_token_list OAuthAuthorizeToken oauth_authorize_token OAuthAuthorizeTokenList oauth_authorize_token_list OAuthClient oauth_client OAuthClientAuthorization oauth_client_authorization OAuthClientAuthorizationList oauth_client_authorization_list OAuthClientList oauth_client_list ].each_slice(2) do |kind, expected_underscore| underscore = Kubeclient::ClientMixin.underscore_entity(kind) assert_equal(underscore, expected_underscore) end end def test_format_datetime_with_string value = '2018-04-27T18:30:17.480321984Z' formatted = client.send(:format_datetime, value) assert_equal(formatted, value) end def test_format_datetime_with_datetime value = DateTime.new(2018, 4, 30, 19, 20, 33) formatted = client.send(:format_datetime, value) assert_equal(formatted, '2018-04-30T19:20:33.000000000+00:00') end def test_format_datetime_with_time value = Time.new(2018, 4, 30, 19, 20, 33, 0) formatted = client.send(:format_datetime, value) assert_equal(formatted, '2018-04-30T19:20:33.000000000+00:00') end def test_parse_definition_with_unconventional_names %w[ PluralPolicy pluralpolicies plural_policy plural_policies LatinDatum latindata latin_datum latin_data Noseparator noseparators noseparator noseparators lowercase lowercases lowercase lowercases TestWithDash test-with-dashes test_with_dash test_with_dashes TestUnderscore test_underscores test_underscore test_underscores TestMismatch other-odd-name testmismatch otheroddname MixedDashMinus mixed-dash_minuses mixed_dash_minus mixed_dash_minuses SameUptoWordboundary sameup-toword-boundarys sameuptowordboundary sameuptowordboundarys ].each_slice(4) do |kind, plural, expected_single, expected_plural| method_names = Kubeclient::ClientMixin.parse_definition(kind, plural).method_names assert_equal(method_names[0], expected_single) assert_equal(method_names[1], expected_plural) end end end kubeclient-4.6.0/test/test_security_context_constraint.rb0000644000004100000410000000542113612310022024110 0ustar www-datawww-datarequire_relative 'test_helper' # kind: 'SecurityContextConstraints' entity tests. # This is one of the unusual `kind`s that are already plural (https://github.com/kubernetes/kubernetes/issues/8115). # We force singular in method names like 'create_endpoint', # but `kind` should remain plural as in kubernetes. class TestSecurityContextConstraints < MiniTest::Test def test_create_security_context_constraint stub_request(:get, %r{/apis/security.openshift.io/v1$}).to_return( body: open_test_file('security.openshift.io_api_resource_list.json'), status: 200 ) testing_scc = Kubeclient::Resource.new( metadata: { name: 'teleportation' }, runAsUser: { type: 'MustRunAs' }, seLinuxContext: { type: 'MustRunAs' } ) req_body = '{"metadata":{"name":"teleportation"},"runAsUser":{"type":"MustRunAs"},' \ '"seLinuxContext":{"type":"MustRunAs"},' \ '"kind":"SecurityContextConstraints","apiVersion":"security.openshift.io/v1"}' stub_request(:post, 'http://localhost:8080/apis/security.openshift.io/v1/securitycontextconstraints') .with(body: req_body) .to_return(body: open_test_file('created_security_context_constraint.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1') created_scc = client.create_security_context_constraint(testing_scc) assert_equal('SecurityContextConstraints', created_scc.kind) assert_equal('security.openshift.io/v1', created_scc.apiVersion) client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1', as: :parsed_symbolized) created_scc = client.create_security_context_constraint(testing_scc) assert_equal('SecurityContextConstraints', created_scc[:kind]) assert_equal('security.openshift.io/v1', created_scc[:apiVersion]) end def test_get_security_context_constraints stub_request(:get, %r{/apis/security.openshift.io/v1$}).to_return( body: open_test_file('security.openshift.io_api_resource_list.json'), status: 200 ) stub_request(:get, %r{/securitycontextconstraints}) .to_return(body: open_test_file('security_context_constraint_list.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1') collection = client.get_security_context_constraints(as: :parsed_symbolized) assert_equal('SecurityContextConstraintsList', collection[:kind]) assert_equal('security.openshift.io/v1', collection[:apiVersion]) # Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic. collection = client.get_security_context_constraints assert_equal('SecurityContextConstraints', collection.kind) end end kubeclient-4.6.0/test/test_missing_methods.rb0000644000004100000410000000646413612310022021435 0ustar www-datawww-datarequire_relative 'test_helper' # Test method_missing, respond_to? and respond_to_missing behaviour class TestMissingMethods < MiniTest::Test def test_missing stub_core_api_list client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') assert_equal(true, client.respond_to?(:get_pod)) assert_equal(true, client.respond_to?(:get_pods)) assert_equal(false, client.respond_to?(:get_pie)) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') # Reset discovery assert_equal(false, client.respond_to?(:get_pie)) assert_equal(true, client.respond_to?(:get_pods)) assert_equal(true, client.respond_to?(:get_pod)) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') # Reset discovery assert_instance_of(Method, client.method(:get_pods)) assert_raises(NameError) do client.method(:get_pies) end client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') # Reset discovery assert_raises(NameError) do client.method(:get_pies) end assert_instance_of(Method, client.method(:get_pods)) stub_request(:get, %r{/api/v1$}).to_return( body: '', status: 404 ) # If discovery fails we expect the below raise an exception client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') assert_raises(Kubeclient::HttpError) do client.discover end client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') assert_raises(Kubeclient::HttpError) do client.method(:get_pods) end client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') assert_raises(Kubeclient::HttpError) do client.respond_to?(:get_pods) end end def test_nonsuffix_plurals stub_request(:get, %r{/apis/extensions/v1beta1$}).to_return( body: open_test_file('extensions_v1beta1_api_resource_list.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/apis/extensions', 'v1beta1') assert_equal(true, client.respond_to?(:get_network_policy)) assert_equal(true, client.respond_to?(:get_network_policies)) assert_equal(true, client.respond_to?(:get_pod_security_policy)) assert_equal(true, client.respond_to?(:get_pod_security_policies)) end def test_irregular_names stub_core_api_list client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') assert_equal(true, client.respond_to?(:get_endpoint)) assert_equal(true, client.respond_to?(:get_endpoints)) stub_request(:get, %r{/apis/security.openshift.io/v1$}).to_return( body: open_test_file('security.openshift.io_api_resource_list.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/apis/security.openshift.io', 'v1') assert_equal(true, client.respond_to?(:get_security_context_constraint)) assert_equal(true, client.respond_to?(:get_security_context_constraints)) end def test_lowercase_kind stub_request(:get, %r{/apis/config.istio.io/v1alpha2$}).to_return( body: open_test_file('config.istio.io_api_resource_list.json'), status: 200 ) client = Kubeclient::Client.new('http://localhost:8080/apis/config.istio.io', 'v1alpha2') assert_equal(true, client.respond_to?(:get_servicecontrolreport)) assert_equal(true, client.respond_to?(:get_servicecontrolreports)) end end kubeclient-4.6.0/test/cassettes/0000755000004100000410000000000013612310022016641 5ustar www-datawww-datakubeclient-4.6.0/test/cassettes/kubernetes_guestbook.yml0000644000004100000410000010072013612310022023615 0ustar www-datawww-data--- http_interactions: - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '253' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "namespaces \"kubeclient-ns\" not found", "reason": "NotFound", "details": { "name": "kubeclient-ns", "kind": "namespaces" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/guestbook body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '239' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "service \"guestbook\" not found", "reason": "NotFound", "details": { "name": "guestbook", "kind": "service" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-master body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '245' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "service \"redis-master\" not found", "reason": "NotFound", "details": { "name": "redis-master", "kind": "service" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-slave body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '243' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "service \"redis-slave\" not found", "reason": "NotFound", "details": { "name": "redis-slave", "kind": "service" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '269' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "replicationControllers \"guestbook\" not found", "reason": "NotFound", "details": { "name": "guestbook", "kind": "replicationControllers" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '275' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "replicationControllers \"redis-master\" not found", "reason": "NotFound", "details": { "name": "redis-master", "kind": "replicationControllers" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 404 message: Not Found headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '273' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Failure", "message": "replicationControllers \"redis-slave\" not found", "reason": "NotFound", "details": { "name": "redis-slave", "kind": "replicationControllers" }, "code": 404 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces body: encoding: UTF-8 string: '{"metadata":{"name":"kubeclient-ns"},"kind":"Namespace","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '74' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '297' body: encoding: UTF-8 string: '{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns","uid":"f41e6b27-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"534","creationTimestamp":"2015-08-09T10:03:59Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services body: encoding: UTF-8 string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"name":"guestbook"},"name":"guestbook"},"spec":{"selector":{"app":"guestbook"},"ports":[{"port":3000,"targetPort":"http-server"}]},"type":"LoadBalancer","kind":"Service","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '239' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '521' body: encoding: UTF-8 string: '{"kind":"Service","apiVersion":"v1","metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/guestbook","uid":"f42187e1-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"538","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"name":"guestbook"}},"spec":{"ports":[{"protocol":"TCP","port":3000,"targetPort":"http-server","nodePort":0}],"selector":{"app":"guestbook"},"clusterIP":"10.0.0.80","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services body: encoding: UTF-8 string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"master"},"name":"redis-master"},"spec":{"selector":{"app":"redis","role":"master"},"ports":[{"port":6379,"targetPort":"redis-server"}]},"kind":"Service","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '244' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '552' body: encoding: UTF-8 string: '{"kind":"Service","apiVersion":"v1","metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-master","uid":"f423bf8b-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"542","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"master"},"clusterIP":"10.0.0.140","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services body: encoding: UTF-8 string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"slave"},"name":"redis-slave"},"spec":{"selector":{"app":"redis","role":"slave"},"ports":[{"port":6379,"targetPort":"redis-server"}]},"kind":"Service","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '241' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '548' body: encoding: UTF-8 string: '{"kind":"Service","apiVersion":"v1","metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-slave","uid":"f4264678-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"545","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"slave"},"clusterIP":"10.0.0.154","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers body: encoding: UTF-8 string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"guestbook","role":"slave"},"name":"guestbook"},"spec":{"selector":{"app":"guestbook"},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"name":"guestbook","image":"kubernetes/guestbook:v2","ports":[{"name":"http-server","containerPort":3000}]}]}},"replicas":3},"kind":"ReplicationController","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '395' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '815' body: encoding: UTF-8 string: '{"kind":"ReplicationController","apiVersion":"v1","metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook","uid":"f4287784-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"547","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"guestbook","role":"slave"}},"spec":{"replicas":3,"selector":{"app":"guestbook"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"guestbook"}},"spec":{"containers":[{"name":"guestbook","image":"kubernetes/guestbook:v2","ports":[{"name":"http-server","containerPort":3000,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers body: encoding: UTF-8 string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"master"},"name":"redis-master"},"spec":{"selector":{"app":"redis","role":"master"},"template":{"metadata":{"labels":{"app":"redis","role":"master"}},"spec":{"containers":[{"name":"redis-master","image":"redis","ports":[{"name":"redis-server","containerPort":6379}]}]}},"replicas":1},"kind":"ReplicationController","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '405' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '828' body: encoding: UTF-8 string: '{"kind":"ReplicationController","apiVersion":"v1","metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master","uid":"f42a9800-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"558","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"replicas":1,"selector":{"app":"redis","role":"master"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"master"}},"spec":{"containers":[{"name":"redis-master","image":"redis","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: post uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers body: encoding: UTF-8 string: '{"metadata":{"namespace":"kubeclient-ns","labels":{"app":"redis","role":"slave"},"name":"redis-slave"},"spec":{"selector":{"app":"redis","role":"slave"},"template":{"metadata":{"labels":{"app":"redis","role":"slave"}},"spec":{"containers":[{"name":"redis-slave","image":"kubernetes/redis-slave:v2","ports":[{"name":"redis-server","containerPort":6379}]}]}},"replicas":2},"kind":"ReplicationController","apiVersion":"v1"}' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate Content-Length: - '420' User-Agent: - Ruby response: status: code: 201 message: Created headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '842' body: encoding: UTF-8 string: '{"kind":"ReplicationController","apiVersion":"v1","metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave","uid":"f42e1d09-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"567","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"replicas":2,"selector":{"app":"redis","role":"slave"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"slave"}},"spec":{"containers":[{"name":"redis-slave","image":"kubernetes/redis-slave:v2","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: get uri: http://10.35.0.23:8080/api/v1/namespaces body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '629' body: encoding: UTF-8 string: '{"kind":"NamespaceList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces","resourceVersion":"570"},"items":[{"metadata":{"name":"default","selfLink":"/api/v1/namespaces/default","uid":"37360c82-3e77-11e5-a75a-18037327aaeb","resourceVersion":"6","creationTimestamp":"2015-08-09T09:15:45Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}},{"metadata":{"name":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns","uid":"f41e6b27-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"534","creationTimestamp":"2015-08-09T10:03:59Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Active"}}]}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: get uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '1661' body: encoding: UTF-8 string: '{"kind":"ServiceList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/kubeclient-ns/services","resourceVersion":"571"},"items":[{"metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/guestbook","uid":"f42187e1-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"538","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"name":"guestbook"}},"spec":{"ports":[{"protocol":"TCP","port":3000,"targetPort":"http-server","nodePort":0}],"selector":{"app":"guestbook"},"clusterIP":"10.0.0.80","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}},{"metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-master","uid":"f423bf8b-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"542","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"master"},"clusterIP":"10.0.0.140","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}},{"metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/services/redis-slave","uid":"f4264678-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"545","creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"ports":[{"protocol":"TCP","port":6379,"targetPort":"redis-server","nodePort":0}],"selector":{"app":"redis","role":"slave"},"clusterIP":"10.0.0.154","type":"ClusterIP","sessionAffinity":"None"},"status":{"loadBalancer":{}}}]}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: get uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Transfer-Encoding: - chunked body: encoding: UTF-8 string: '{"kind":"ReplicationControllerList","apiVersion":"v1","metadata":{"selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers","resourceVersion":"571"},"items":[{"metadata":{"name":"guestbook","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook","uid":"f4287784-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"557","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"guestbook","role":"slave"}},"spec":{"replicas":3,"selector":{"app":"guestbook"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"guestbook"}},"spec":{"containers":[{"name":"guestbook","image":"kubernetes/guestbook:v2","ports":[{"name":"http-server","containerPort":3000,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":3,"observedGeneration":1}},{"metadata":{"name":"redis-master","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master","uid":"f42a9800-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"565","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"master"}},"spec":{"replicas":1,"selector":{"app":"redis","role":"master"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"master"}},"spec":{"containers":[{"name":"redis-master","image":"redis","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0,"observedGeneration":1}},{"metadata":{"name":"redis-slave","namespace":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave","uid":"f42e1d09-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"567","generation":1,"creationTimestamp":"2015-08-09T10:03:59Z","labels":{"app":"redis","role":"slave"}},"spec":{"replicas":2,"selector":{"app":"redis","role":"slave"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"redis","role":"slave"}},"spec":{"containers":[{"name":"redis-slave","image":"kubernetes/redis-slave:v2","ports":[{"name":"redis-server","containerPort":6379,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","dnsPolicy":"ClusterFirst"}}},"status":{"replicas":0}}]}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/guestbook body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '100' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "code": 200 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-master body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '100' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "code": 200 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/services/redis-slave body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '100' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "code": 200 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/guestbook body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '100' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "code": 200 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-master body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '100' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "code": 200 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns/replicationcontrollers/redis-slave body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '100' body: encoding: UTF-8 string: |- { "kind": "Status", "apiVersion": "v1", "metadata": {}, "status": "Success", "code": 200 } http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: delete uri: http://10.35.0.23:8080/api/v1/namespaces/kubeclient-ns body: encoding: US-ASCII string: '' headers: Accept: - '*/*; q=0.5, application/xml' Accept-Encoding: - gzip, deflate User-Agent: - Ruby response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Sun, 09 Aug 2015 10:03:59 GMT Content-Length: - '345' body: encoding: UTF-8 string: '{"kind":"Namespace","apiVersion":"v1","metadata":{"name":"kubeclient-ns","selfLink":"/api/v1/namespaces/kubeclient-ns","uid":"f41e6b27-3e7d-11e5-a75a-18037327aaeb","resourceVersion":"584","creationTimestamp":"2015-08-09T10:03:59Z","deletionTimestamp":"2015-08-09T10:03:59Z"},"spec":{"finalizers":["kubernetes"]},"status":{"phase":"Terminating"}}' http_version: recorded_at: Sun, 09 Aug 2015 10:00:02 GMT - request: method: get uri: http://10.35.0.23:8080/api/v1 body: encoding: US-ASCII string: '' headers: Accept: - "*/*" Accept-Encoding: - gzip, deflate User-Agent: - rest-client/2.0.0 (linux-gnu x86_64) ruby/2.3.0p0 Host: - localhost:8080 response: status: code: 200 message: OK headers: Content-Type: - application/json Date: - Mon, 29 Aug 2016 15:51:30 GMT Transfer-Encoding: - chunked body: encoding: UTF-8 string: '{"kind":"APIResourceList","groupVersion":"v1","resources":[{"name":"bindings","namespaced":true,"kind":"Binding"},{"name":"componentstatuses","namespaced":false,"kind":"ComponentStatus"},{"name":"configmaps","namespaced":true,"kind":"ConfigMap"},{"name":"endpoints","namespaced":true,"kind":"Endpoints"},{"name":"events","namespaced":true,"kind":"Event"},{"name":"limitranges","namespaced":true,"kind":"LimitRange"},{"name":"namespaces","namespaced":false,"kind":"Namespace"},{"name":"namespaces/finalize","namespaced":false,"kind":"Namespace"},{"name":"namespaces/status","namespaced":false,"kind":"Namespace"},{"name":"nodes","namespaced":false,"kind":"Node"},{"name":"nodes/proxy","namespaced":false,"kind":"Node"},{"name":"nodes/status","namespaced":false,"kind":"Node"},{"name":"persistentvolumeclaims","namespaced":true,"kind":"PersistentVolumeClaim"},{"name":"persistentvolumeclaims/status","namespaced":true,"kind":"PersistentVolumeClaim"},{"name":"persistentvolumes","namespaced":false,"kind":"PersistentVolume"},{"name":"persistentvolumes/status","namespaced":false,"kind":"PersistentVolume"},{"name":"pods","namespaced":true,"kind":"Pod"},{"name":"pods/attach","namespaced":true,"kind":"Pod"},{"name":"pods/binding","namespaced":true,"kind":"Binding"},{"name":"pods/exec","namespaced":true,"kind":"Pod"},{"name":"pods/log","namespaced":true,"kind":"Pod"},{"name":"pods/portforward","namespaced":true,"kind":"Pod"},{"name":"pods/proxy","namespaced":true,"kind":"Pod"},{"name":"pods/status","namespaced":true,"kind":"Pod"},{"name":"podtemplates","namespaced":true,"kind":"PodTemplate"},{"name":"replicationcontrollers","namespaced":true,"kind":"ReplicationController"},{"name":"replicationcontrollers/scale","namespaced":true,"kind":"Scale"},{"name":"replicationcontrollers/status","namespaced":true,"kind":"ReplicationController"},{"name":"resourcequotas","namespaced":true,"kind":"ResourceQuota"},{"name":"resourcequotas/status","namespaced":true,"kind":"ResourceQuota"},{"name":"secrets","namespaced":true,"kind":"Secret"},{"name":"serviceaccounts","namespaced":true,"kind":"ServiceAccount"},{"name":"services","namespaced":true,"kind":"Service"},{"name":"services/proxy","namespaced":true,"kind":"Service"},{"name":"services/status","namespaced":true,"kind":"Service"}]} ' http_version: recorded_at: Mon, 29 Aug 2016 15:51:30 GMT recorded_with: VCR 3.0.3 kubeclient-4.6.0/test/test_persistent_volume.rb0000644000004100000410000000151113612310022022014 0ustar www-datawww-datarequire_relative 'test_helper' # PersistentVolume tests class TestPersistentVolume < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/persistentvolumes}) .to_return(body: open_test_file('persistent_volume.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') volume = client.get_persistent_volume('pv0001') assert_instance_of(Kubeclient::Resource, volume) assert_equal('pv0001', volume.metadata.name) assert_equal('10Gi', volume.spec.capacity.storage) assert_equal('/tmp/data01', volume.spec.hostPath.path) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/persistentvolumes/pv0001', times: 1 ) end end kubeclient-4.6.0/test/config/0000755000004100000410000000000013612310022016110 5ustar www-datawww-datakubeclient-4.6.0/test/config/external-key.rsa0000644000004100000410000000321713612310022021232 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAu3uk81X6Dl0jAsKxWvYUcz5SYJPnR/sHLOPCRcZbCxYYLKeS p636q1aMQuk2FrHz5lH092iU2MUYipRrNUWPoBQ1ZBue/B2Onpmywhr2bVrepiZ6 ONOP92J2YGaMTPmtW2+EG8owDri/CnMO+05B/iMIlBcnz60tyWGoomLy2oYUh3p0 /XUnfxVbF63FwH2+87hJmiuIi3N4sI+cP6tPxg143LU9l4H440DM+YeGCi5RnSIF iTB7N/7xPqNxUd+TeYcBZb2/baE8+4UzzlAVfrJpnZM7H4/KVPalcQOBUp4zGSNL NayzsUfjljE35IWxB0cI/JAcq6q5PcuDyfzR+QIDAQABAoIBAAJMzgU+NMcgVWGf SdW31yBiz1fn+Jpldf0S/jsk59EHAFzr4Vy1QbIAadAYVFWK8VcOAldWrRzLrjVl BLuuksh/9ve3bwfgxPMlJ5BCDGvjBYA8UJuJ3OZICvO7zlTTa1EH2TMOE/DaS12x c6mZmC2p+Fz4u8BMe5dQ32S+mgQcQ5vWAvclvKDwr/16cjl+O7D0cMYFURiE42zA fNDj1oL+IQyOMr4WYAUT3IHzWHPEYdmF0bLzk+VrLt1F730R5M/XD6s3xhBjdfwF wRbP4ItK1ID2o116eWDA8f8Zt6UH+8YGt576bnPZPGS1AT/pFg+W3Bgv829//LxW UmZ2HhECgYEA35NNUr09H1n9SIifxEqN4Aoy0klf6BOpRKsWyRJKz60oAgYzMGh/ 2BqFdDeY70vyELklW81AcBGp+keOSBl98Gm0eH0gEEQccvJmowju27wfCwFNErCe ONBiEH6F9k410LwE6XPQyIRFbFDl9jRKF7zQIXLFdm/K123C50G/M+sCgYEA1qxW RYsjuZnZXkmAuigZhgQ5xX9BFQ64Y+WouUQYGAgildLtQpXRDyCmdr9udelF7X95 ugvFG2PCaCxSDYRb8XmTAnOejLj0HPx3XaU7OMA6R8A1fwDTXYt7gdQDrIe10c2t xxuiJk9/NGxU0pdKQTH46mORdLyx9NBVbm6abKsCgYEAx0P3V7YYgxfmmj5C/2Vf e/46+5ewJh1X8T73yPBHwz9WeC9kclHBGC/TIBjpcuZicitCM8cFFp13vP0HwBUQ XTUpR3ZmHHlITd7kQY0VrrzFgj5/nhfSfVqk/IR4C2X7qvXY/uVdgUn6wiZ8P2qt WujLK/SqL/xFcqp+WzULIE8CgYASf1EDtwSCBparQCweXjwsxwpvURK7jMQon25V YF+vGmxYeo9FEIGuCwMs8jvDXzFOUJCdjkwSmxryPPdpAMGnYfFf4bSErZ8jLEHg yM78idu7uP0jD2AnDFC9FBao9Byfklhp8qCbKnhsfPUmHDEiwvqOv7uV0niG/riN xmv59wKBgQC77xfwwiEL4G1tBH5W5DXIxkiguyjOyJtJNRTEPUrLRV9wNRLk1goo yyTzZX5hLSzii7h7PeQGj9TtWupX7TX2moAjSQQ82V/4yfJOpuiJ+Px9hDZ0sH6p Yvah+3J+RLIe2i3kqS4x5/qNqoParx2NMhehHtsFqj5s0gBW3Gnq/g== -----END RSA PRIVATE KEY----- kubeclient-4.6.0/test/config/external.kubeconfig0000644000004100000410000000076313612310022021776 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: certificate-authority: external-ca.pem server: https://localhost:8443 name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: system:admin/localhost:8443 name: default/localhost:8443/system:admin current-context: default/localhost:8443/system:admin kind: Config preferences: {} users: - name: system:admin/localhost:8443 user: client-certificate: external-cert.pem client-key: external-key.rsa kubeclient-4.6.0/test/config/allinone.kubeconfig0000644000004100000410000001304613612310022021753 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM2akNDQWRLZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREFtTVNRd0lnWURWUVFEREJ0dmNHVnUKYzJocFpuUXRjMmxuYm1WeVFERTFOREUzTVRZMU9USXdIaGNOTVRneE1UQTRNakl6TmpNeFdoY05Nak14TVRBMwpNakl6TmpNeVdqQW1NU1F3SWdZRFZRUUREQnR2Y0dWdWMyaHBablF0YzJsbmJtVnlRREUxTkRFM01UWTFPVEl3CmdnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUREaS9CdG9YRFowL0l0b3lGVTRaTXAKMG5xUXZ6MWFEblcvVHpDRHhpZVJFL09sQk5EcVJCUlFHWEtoNWE4MkN0bno1SjM4VzJZY3Y2RXY1QlJKcFJNTgpiYWNmQVBqczZmK1ZKSE92MFVUc3lRc2FWd2N5OHVIQ2ZKck5IR3h5UVZZYnZPdnFTditOZ2ZUUTIwZ3ZhTXgwCkZuTUxnMmtFaFhRUnhOdEsrRlgrM2l2V1FmLzFvREJkU1BTeEZYell0emsxWlhSWmM3b1NobUZTZHlxWkpvZHYKb3lqcFBIZjR1ZmtuQ3V1TFhOWlRBKzFyT2dyRWJRT1RhdncraGVZZjZXS05uMU9PbHBIQ0lEZ3cycXRYRm0vawpSZDJHVHlpV0pCQjBNbTBRclRvdkVQbkMvWGhZekJ3TmZrZ2Y1a2oray94cXZUUUhTeUgxc0NZT2JJY0hUbWt4CkFnTUJBQUdqSXpBaE1BNEdBMVVkRHdFQi93UUVBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUcKU0liM0RRRUJDd1VBQTRJQkFRQ2hmTE9SZDZTN3habVZBSUpvUnZwRHdXWVJ0bngvSnJZeTNYMXhWWEh4dDQ4NgpBVlBLYWxXYm9qektpRmQ3YXF2NTJCdkNINVVJYlQzM3VueXFNODdmWE1CZzFQMWFUTno3YXpHVDJuY2JEU1VVClNjSjJ1aWNLSGZpMjMzd2o0cUtzb2RXR29NNG8xS0doSnFhVUN4WGtDZkZXVmVZRjJyK3BUeVFUVFhxWGhGbVkKeWpTT2o3UnorUEhIK3E0dVcxK05kWCtFRmxNN3RWUUcrUys3dk1jTkQ4MkdLVzZZWlZFTW15NXV3eDlNUUZnQwpoTzNJbkEvN1NqT0tIeDBlcmR0RXpGNWFjYUxqZER5S2JCemFUT2xrS041NVhyRzlMNmRhdDBSbEtmOFB3V1FUCmFKRW0xSldBRE9mZUljZGdrNWVXRnpQUy85TUZrMVNiQnhPQVJLVVEKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= server: https://localhost:8443 name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: system:admin/localhost:8443 name: default/localhost:8443/system:admin current-context: default/localhost:8443/system:admin kind: Config preferences: {} users: - name: system:admin/localhost:8443 user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURKRENDQWd5Z0F3SUJBZ0lCQ0RBTkJna3Foa2lHOXcwQkFRc0ZBREFtTVNRd0lnWURWUVFEREJ0dmNHVnUKYzJocFpuUXRjMmxuYm1WeVFERTFOREUzTVRZMU9USXdIaGNOTVRneE1UQTRNakl6TmpNMFdoY05NakF4TVRBMwpNakl6TmpNMVdqQk9NVFV3SEFZRFZRUUtFeFZ6ZVhOMFpXMDZZMngxYzNSbGNpMWhaRzFwYm5Nd0ZRWURWUVFLCkV3NXplWE4wWlcwNmJXRnpkR1Z5Y3pFVk1CTUdBMVVFQXhNTWMzbHpkR1Z0T21Ga2JXbHVNSUlCSWpBTkJna3EKaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF1M3VrODFYNkRsMGpBc0t4V3ZZVWN6NVNZSlBuUi9zSApMT1BDUmNaYkN4WVlMS2VTcDYzNnExYU1RdWsyRnJIejVsSDA5MmlVMk1VWWlwUnJOVVdQb0JRMVpCdWUvQjJPCm5wbXl3aHIyYlZyZXBpWjZPTk9QOTJKMllHYU1UUG10VzIrRUc4b3dEcmkvQ25NTyswNUIvaU1JbEJjbno2MHQKeVdHb29tTHkyb1lVaDNwMC9YVW5meFZiRjYzRndIMis4N2hKbWl1SWkzTjRzSStjUDZ0UHhnMTQzTFU5bDRINAo0MERNK1llR0NpNVJuU0lGaVRCN04vN3hQcU54VWQrVGVZY0JaYjIvYmFFOCs0VXp6bEFWZnJKcG5aTTdINC9LClZQYWxjUU9CVXA0ekdTTkxOYXl6c1VmamxqRTM1SVd4QjBjSS9KQWNxNnE1UGN1RHlmelIrUUlEQVFBQm96VXcKTXpBT0JnTlZIUThCQWY4RUJBTUNCYUF3RXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVIQXdJd0RBWURWUjBUQVFILwpCQUl3QURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQUJvMHRDUkhwd3JXSUk4ajRBNFU1Mk5VZDNVNTdLK1F2Ci9oSjUyVkNoZzcvTlhnNnp5bE44dUowbVBBVlRxZnFLaHhxTDVnR2ozYXlPeWY5SEFLdFRkdHBKSXZYTStlcW4KV0NWRWRTZVNKTGxEbGlLQkt6aE9KMTR1VDNhRGZkYmRrMm5zbFRHejhRZzVtaE5OOXRHNys3ZS9DVXpJcFpPNQo0OXFoUC8wN3VVZStvMEd4WEFLM1dITDFJMjhpT2pxbExlMHI1QlpSZ29yN1ZhMzRGZ3pud2JFRTZrSDJ1MzNwClU4cDA3QmNVQkRNckJXeVc4SFJDUHVhNjdLRjhFMW80SDB6a0phaTl1SzhlRm9UWm1kVDVLMlk5RDYrMmZtdDEKenErRisxUWhCUnVqeWVTSXptdnVkK2R4dFR1RDliUTJhWmpXVHRmbnN1bjJsb2NSQmNLc3VRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdTN1azgxWDZEbDBqQXNLeFd2WVVjejVTWUpQblIvc0hMT1BDUmNaYkN4WVlMS2VTCnA2MzZxMWFNUXVrMkZySHo1bEgwOTJpVTJNVVlpcFJyTlVXUG9CUTFaQnVlL0IyT25wbXl3aHIyYlZyZXBpWjYKT05PUDkySjJZR2FNVFBtdFcyK0VHOG93RHJpL0NuTU8rMDVCL2lNSWxCY256NjB0eVdHb29tTHkyb1lVaDNwMAovWFVuZnhWYkY2M0Z3SDIrODdoSm1pdUlpM040c0krY1A2dFB4ZzE0M0xVOWw0SDQ0MERNK1llR0NpNVJuU0lGCmlUQjdOLzd4UHFOeFVkK1RlWWNCWmIyL2JhRTgrNFV6emxBVmZySnBuWk03SDQvS1ZQYWxjUU9CVXA0ekdTTkwKTmF5enNVZmpsakUzNUlXeEIwY0kvSkFjcTZxNVBjdUR5ZnpSK1FJREFRQUJBb0lCQUFKTXpnVStOTWNnVldHZgpTZFczMXlCaXoxZm4rSnBsZGYwUy9qc2s1OUVIQUZ6cjRWeTFRYklBYWRBWVZGV0s4VmNPQWxkV3JSekxyalZsCkJMdXVrc2gvOXZlM2J3Zmd4UE1sSjVCQ0RHdmpCWUE4VUp1SjNPWklDdk83emxUVGExRUgyVE1PRS9EYVMxMngKYzZtWm1DMnArRno0dThCTWU1ZFEzMlMrbWdRY1E1dldBdmNsdktEd3IvMTZjamwrTzdEMGNNWUZVUmlFNDJ6QQpmTkRqMW9MK0lReU9NcjRXWUFVVDNJSHpXSFBFWWRtRjBiTHprK1ZyTHQxRjczMFI1TS9YRDZzM3hoQmpkZndGCndSYlA0SXRLMUlEMm8xMTZlV0RBOGY4WnQ2VUgrOFlHdDU3NmJuUFpQR1MxQVQvcEZnK1czQmd2ODI5Ly9MeFcKVW1aMkhoRUNnWUVBMzVOTlVyMDlIMW45U0lpZnhFcU40QW95MGtsZjZCT3BSS3NXeVJKS3o2MG9BZ1l6TUdoLwoyQnFGZERlWTcwdnlFTGtsVzgxQWNCR3Ara2VPU0JsOThHbTBlSDBnRUVRY2N2Sm1vd2p1Mjd3ZkN3Rk5FckNlCk9OQmlFSDZGOWs0MTBMd0U2WFBReUlSRmJGRGw5alJLRjd6UUlYTEZkbS9LMTIzQzUwRy9NK3NDZ1lFQTFxeFcKUllzanVablpYa21BdWlnWmhnUTV4WDlCRlE2NFkrV291VVFZR0FnaWxkTHRRcFhSRHlDbWRyOXVkZWxGN1g5NQp1Z3ZGRzJQQ2FDeFNEWVJiOFhtVEFuT2VqTGowSFB4M1hhVTdPTUE2UjhBMWZ3RFRYWXQ3Z2RRRHJJZTEwYzJ0Cnh4dWlKazkvTkd4VTBwZEtRVEg0Nm1PUmRMeXg5TkJWYm02YWJLc0NnWUVBeDBQM1Y3WVlneGZtbWo1Qy8yVmYKZS80Nis1ZXdKaDFYOFQ3M3lQQkh3ejlXZUM5a2NsSEJHQy9USUJqcGN1WmljaXRDTThjRkZwMTN2UDBId0JVUQpYVFVwUjNabUhIbElUZDdrUVkwVnJyekZnajUvbmhmU2ZWcWsvSVI0QzJYN3F2WFkvdVZkZ1VuNndpWjhQMnF0Cld1akxLL1NxTC94RmNxcCtXelVMSUU4Q2dZQVNmMUVEdHdTQ0JwYXJRQ3dlWGp3c3h3cHZVUks3ak1Rb24yNVYKWUYrdkdteFllbzlGRUlHdUN3TXM4anZEWHpGT1VKQ2Rqa3dTbXhyeVBQZHBBTUduWWZGZjRiU0VyWjhqTEVIZwp5TTc4aWR1N3VQMGpEMkFuREZDOUZCYW85Qnlma2xocDhxQ2JLbmhzZlBVbUhERWl3dnFPdjd1VjBuaUcvcmlOCnhtdjU5d0tCZ1FDNzd4Znd3aUVMNEcxdEJINVc1RFhJeGtpZ3V5ak95SnRKTlJURVBVckxSVjl3TlJMazFnb28KeXlUelpYNWhMU3ppaTdoN1BlUUdqOVR0V3VwWDdUWDJtb0FqU1FRODJWLzR5ZkpPcHVpSitQeDloRFowc0g2cApZdmFoKzNKK1JMSWUyaTNrcVM0eDUvcU5xb1BhcngyTk1oZWhIdHNGcWo1czBnQlczR25xL2c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= kubeclient-4.6.0/test/config/userauth.kubeconfig0000644000004100000410000000120613612310022022005 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: server: https://localhost:8443 insecure-skip-tls-verify: true name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: system:admin:token name: localhost/system:admin:token - context: cluster: localhost:8443 namespace: default user: system:admin:userpass name: localhost/system:admin:userpass current-context: localhost/system:admin:token kind: Config preferences: {} users: - name: system:admin:token user: token: 0123456789ABCDEF0123456789ABCDEF - name: system:admin:userpass user: username: admin password: pAssw0rd123 kubeclient-4.6.0/test/config/external-cert.pem0000644000004100000410000000217713612310022021377 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDJDCCAgygAwIBAgIBCDANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu c2hpZnQtc2lnbmVyQDE1NDE3MTY1OTIwHhcNMTgxMTA4MjIzNjM0WhcNMjAxMTA3 MjIzNjM1WjBOMTUwHAYDVQQKExVzeXN0ZW06Y2x1c3Rlci1hZG1pbnMwFQYDVQQK Ew5zeXN0ZW06bWFzdGVyczEVMBMGA1UEAxMMc3lzdGVtOmFkbWluMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3uk81X6Dl0jAsKxWvYUcz5SYJPnR/sH LOPCRcZbCxYYLKeSp636q1aMQuk2FrHz5lH092iU2MUYipRrNUWPoBQ1ZBue/B2O npmywhr2bVrepiZ6ONOP92J2YGaMTPmtW2+EG8owDri/CnMO+05B/iMIlBcnz60t yWGoomLy2oYUh3p0/XUnfxVbF63FwH2+87hJmiuIi3N4sI+cP6tPxg143LU9l4H4 40DM+YeGCi5RnSIFiTB7N/7xPqNxUd+TeYcBZb2/baE8+4UzzlAVfrJpnZM7H4/K VPalcQOBUp4zGSNLNayzsUfjljE35IWxB0cI/JAcq6q5PcuDyfzR+QIDAQABozUw MzAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/ BAIwADANBgkqhkiG9w0BAQsFAAOCAQEABo0tCRHpwrWII8j4A4U52NUd3U57K+Qv /hJ52VChg7/NXg6zylN8uJ0mPAVTqfqKhxqL5gGj3ayOyf9HAKtTdtpJIvXM+eqn WCVEdSeSJLlDliKBKzhOJ14uT3aDfdbdk2nslTGz8Qg5mhNN9tG7+7e/CUzIpZO5 49qhP/07uUe+o0GxXAK3WHL1I28iOjqlLe0r5BZRgor7Va34FgznwbEE6kH2u33p U8p07BcUBDMrBWyW8HRCPua67KF8E1o4H0zkJai9uK8eFoTZmdT5K2Y9D6+2fmt1 zq+F+1QhBRujyeSIzmvud+dxtTuD9bQ2aZjWTtfnsun2locRBcKsuQ== -----END CERTIFICATE----- kubeclient-4.6.0/test/config/gcpcmdauth.kubeconfig0000644000004100000410000000125213612310022022265 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: server: https://localhost:8443 insecure-skip-tls-verify: true name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: application-default-credentials name: localhost/application-default-credentials kind: Config preferences: {} users: - name: application-default-credentials user: auth-provider: config: access-token: cmd-args: config config-helper --format=json cmd-path: /path/to/gcloud expiry: 2019-04-09T19:26:18Z expiry-key: '{.credential.token_expiry}' token-key: '{.credential.access_token}' name: gcp kubeclient-4.6.0/test/config/nouser.kubeconfig0000644000004100000410000000052213612310022021460 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: server: https://localhost:8443 insecure-skip-tls-verify: true name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: "" name: default/localhost:8443/nouser current-context: default/localhost:8443/nouser kind: Config preferences: {} users: [] kubeclient-4.6.0/test/config/gcpauth.kubeconfig0000644000004100000410000000077613612310022021613 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: server: https://localhost:8443 insecure-skip-tls-verify: true name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: application-default-credentials name: localhost/application-default-credentials kind: Config preferences: {} users: - name: application-default-credentials user: auth-provider: config: access-token: expiry: 2019-02-19T11:07:29.827352-05:00 name: gcp kubeclient-4.6.0/test/config/timestamps.kubeconfig0000644000004100000410000000140513612310022022334 0ustar www-datawww-dataapiVersion: v1 users: - name: gke_username user: auth-provider: config: access-token: REDACTED cmd-args: config config-helper --format=json cmd-path: /Users/tannerbruce/opt/google-cloud-sdk/bin/gcloud expiry: 2018-07-07T18:25:36Z expiry-key: '{.credential.token_expiry}' token-key: '{.credential.access_token}' name: gcp # More syntaxes from go-yaml tests, hopefully covering all types possible in kubeconfig IPv4: 1.2.3.4 Duration: 3s date_only: 2015-01-01 rfc3339: 2015-02-24T18:19:39Z longer: 2015-02-24T18:19:39.123456789-03:00 shorter: 2015-2-3T3:4:5Z iso_lower_t: 2015-02-24t18:19:39Z space_no_tz: 2015-02-24 18:19:39 space_tz: 2001-12-14 21:59:43.10 -5 timestamp_like_string: "2015-02-24T18:19:39Z" kubeclient-4.6.0/test/config/oidcauth.kubeconfig0000644000004100000410000000111613612310022021745 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: server: https://localhost:8443 insecure-skip-tls-verify: true name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: oidc-auth-provider name: localhost/oidc-auth-provider kind: Config preferences: {} users: - name: oidc-auth-provider user: auth-provider: config: client-id: fake-client-id client-secret: fake-client-secret id-token: fake-id-token idp-issuer-url: https://accounts.google.com refresh-token: fake-refresh-token name: oidc kubeclient-4.6.0/test/config/execauth.kubeconfig0000644000004100000410000000351113612310022021754 0ustar www-datawww-dataapiVersion: v1 clusters: - cluster: server: https://localhost:8443 insecure-skip-tls-verify: true name: localhost:8443 contexts: - context: cluster: localhost:8443 namespace: default user: system:admin:exec-search-path name: localhost/system:admin:exec-search-path - context: cluster: localhost:8443 namespace: default user: system:admin:exec-relative-path name: localhost/system:admin:exec-relative-path - context: cluster: localhost:8443 namespace: default user: system:admin:exec-absolute-path name: localhost/system:admin:exec-absolute-path kind: Config preferences: {} users: - name: system:admin:exec-search-path user: exec: # Command to execute. Required. command: "example-exec-plugin" # API version to use when decoding the ExecCredentials resource. Required. # # The API version returned by the plugin MUST match the version listed here. # # To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1alpha1), # set an environment variable or pass an argument to the tool that indicates which version the exec plugin expects. apiVersion: "client.authentication.k8s.io/v1beta1" # Environment variables to set when executing the plugin. Optional. env: - name: "FOO" value: "bar" # Arguments to pass when executing the plugin. Optional. args: - "arg1" - "arg2" - name: system:admin:exec-relative-path user: exec: # Command to execute. Required. command: "dir/example-exec-plugin" apiVersion: "client.authentication.k8s.io/v1beta1" - name: system:admin:exec-absolute-path user: exec: # Command to execute. Required. command: "/abs/path/example-exec-plugin" apiVersion: "client.authentication.k8s.io/v1beta1" kubeclient-4.6.0/test/config/external-ca.pem0000644000004100000410000000205613612310022021021 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIC6jCCAdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVu c2hpZnQtc2lnbmVyQDE1NDE3MTY1OTIwHhcNMTgxMTA4MjIzNjMxWhcNMjMxMTA3 MjIzNjMyWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE1NDE3MTY1OTIw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDi/BtoXDZ0/ItoyFU4ZMp 0nqQvz1aDnW/TzCDxieRE/OlBNDqRBRQGXKh5a82Ctnz5J38W2Ycv6Ev5BRJpRMN bacfAPjs6f+VJHOv0UTsyQsaVwcy8uHCfJrNHGxyQVYbvOvqSv+NgfTQ20gvaMx0 FnMLg2kEhXQRxNtK+FX+3ivWQf/1oDBdSPSxFXzYtzk1ZXRZc7oShmFSdyqZJodv oyjpPHf4ufknCuuLXNZTA+1rOgrEbQOTavw+heYf6WKNn1OOlpHCIDgw2qtXFm/k Rd2GTyiWJBB0Mm0QrTovEPnC/XhYzBwNfkgf5kj+k/xqvTQHSyH1sCYObIcHTmkx AgMBAAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG SIb3DQEBCwUAA4IBAQChfLORd6S7xZmVAIJoRvpDwWYRtnx/JrYy3X1xVXHxt486 AVPKalWbojzKiFd7aqv52BvCH5UIbT33unyqM87fXMBg1P1aTNz7azGT2ncbDSUU ScJ2uicKHfi233wj4qKsodWGoM4o1KGhJqaUCxXkCfFWVeYF2r+pTyQTTXqXhFmY yjSOj7Rz+PHH+q4uW1+NdX+EFlM7tVQG+S+7vMcND82GKW6YZVEMmy5uwx9MQFgC hO3InA/7SjOKHx0erdtEzF5acaLjdDyKbBzaTOlkKN55XrG9L6dat0RlKf8PwWQT aJEm1JWADOfeIcdgk5eWFzPS/9MFk1SbBxOARKUQ -----END CERTIFICATE----- kubeclient-4.6.0/test/test_namespace.rb0000644000004100000410000000365413612310022020173 0ustar www-datawww-datarequire_relative 'test_helper' # Namespace entity tests class TestNamespace < MiniTest::Test def test_get_namespace_v1 stub_core_api_list stub_request(:get, %r{/namespaces}) .to_return(body: open_test_file('namespace.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') namespace = client.get_namespace('staging') assert_instance_of(Kubeclient::Resource, namespace) assert_equal('e388bc10-c021-11e4-a514-3c970e4a436a', namespace.metadata.uid) assert_equal('staging', namespace.metadata.name) assert_equal('1168', namespace.metadata.resourceVersion) assert_equal('v1', namespace.apiVersion) assert_requested( :get, 'http://localhost:8080/api/v1/namespaces/staging', times: 1 ) end def test_delete_namespace_v1 our_namespace = Kubeclient::Resource.new our_namespace.metadata = {} our_namespace.metadata.name = 'staging' stub_core_api_list stub_request(:delete, %r{/namespaces}) .to_return(body: open_test_file('namespace.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') our_namespace = client.delete_namespace(our_namespace.metadata.name) assert_kind_of(RecursiveOpenStruct, our_namespace) assert_requested( :delete, 'http://localhost:8080/api/v1/namespaces/staging', times: 1 ) end def test_create_namespace stub_core_api_list stub_request(:post, %r{/namespaces}) .to_return(body: open_test_file('created_namespace.json'), status: 201) namespace = Kubeclient::Resource.new namespace.metadata = {} namespace.metadata.name = 'development' client = Kubeclient::Client.new('http://localhost:8080/api/') created_namespace = client.create_namespace(namespace) assert_instance_of(Kubeclient::Resource, created_namespace) assert_equal(namespace.metadata.name, created_namespace.metadata.name) end end kubeclient-4.6.0/test/test_component_status.rb0000644000004100000410000000162013612310022021633 0ustar www-datawww-datarequire_relative 'test_helper' # ComponentStatus tests class TestComponentStatus < MiniTest::Test def test_get_from_json_v3 stub_core_api_list stub_request(:get, %r{/componentstatuses}) .to_return(body: open_test_file('component_status.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') component_status = client.get_component_status('etcd-0', 'default') assert_instance_of(Kubeclient::Resource, component_status) assert_equal('etcd-0', component_status.metadata.name) assert_equal('Healthy', component_status.conditions[0].type) assert_equal('True', component_status.conditions[0].status) assert_requested( :get, 'http://localhost:8080/api/v1', times: 1 ) assert_requested( :get, 'http://localhost:8080/api/v1/namespaces/default/componentstatuses/etcd-0', times: 1 ) end end kubeclient-4.6.0/test/test_google_application_default_credentials.rb0000644000004100000410000000073313612310022026152 0ustar www-datawww-datarequire_relative 'test_helper' require 'googleauth' # Unit tests for the ApplicationDefaultCredentials token provider class GoogleApplicationDefaultCredentialsTest < MiniTest::Test def test_token auth = Minitest::Mock.new auth.expect(:apply, nil, [{}]) auth.expect(:access_token, 'valid_token') Google::Auth.stub(:get_application_default, auth) do assert_equal('valid_token', Kubeclient::GoogleApplicationDefaultCredentials.token) end end end kubeclient-4.6.0/test/test_kubeclient.rb0000644000004100000410000007117513612310022020367 0ustar www-datawww-datarequire_relative 'test_helper' # Kubernetes client entity tests class KubeclientTest < MiniTest::Test def test_json our_object = Kubeclient::Resource.new our_object.foo = 'bar' our_object.nested = {} our_object.nested.again = {} our_object.nested.again.again = {} our_object.nested.again.again.name = 'aaron' expected = { 'foo' => 'bar', 'nested' => { 'again' => { 'again' => { 'name' => 'aaron' } } } } assert_equal(expected, JSON.parse(JSON.dump(our_object.to_h))) end def test_pass_uri # URI::Generic#hostname= was added in ruby 1.9.3 and will automatically # wrap an ipv6 address in [] uri = URI::HTTP.build(port: 8080) uri.hostname = 'localhost' client = Kubeclient::Client.new(uri) rest_client = client.rest_client assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s) end def test_no_path_in_uri client = Kubeclient::Client.new('http://localhost:8080', 'v1') rest_client = client.rest_client assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s) end def test_no_version_passed client = Kubeclient::Client.new('http://localhost:8080') rest_client = client.rest_client assert_equal('http://localhost:8080/api/v1', rest_client.url.to_s) end def test_pass_proxy uri = URI::HTTP.build(host: 'localhost', port: 8080) proxy_uri = URI::HTTP.build(host: 'myproxyhost', port: 8888) stub_core_api_list client = Kubeclient::Client.new(uri, http_proxy_uri: proxy_uri) rest_client = client.rest_client assert_equal(proxy_uri.to_s, rest_client.options[:proxy]) watch_client = client.watch_pods assert_equal(watch_client.send(:build_client_options)[:proxy][:proxy_address], proxy_uri.host) assert_equal(watch_client.send(:build_client_options)[:proxy][:proxy_port], proxy_uri.port) end def test_pass_max_redirects max_redirects = 0 client = Kubeclient::Client.new('http://localhost:8080/api/', http_max_redirects: max_redirects) rest_client = client.rest_client assert_equal(max_redirects, rest_client.options[:max_redirects]) stub_request(:get, 'http://localhost:8080/api') .to_return(status: 302, headers: { location: 'http://localhost:1234/api' }) exception = assert_raises(Kubeclient::HttpError) { client.api } assert_equal(302, exception.error_code) end def test_exception stub_core_api_list stub_request(:post, %r{/services}) .to_return(body: open_test_file('namespace_exception.json'), status: 409) service = Kubeclient::Resource.new service.metadata = {} service.metadata.name = 'redisslave' service.metadata.namespace = 'default' # service.port = 80 # service.container_port = 6379 # service.protocol = 'TCP' client = Kubeclient::Client.new('http://localhost:8080/api/') exception = assert_raises(Kubeclient::HttpError) do service = client.create_service(service) end assert_instance_of(Kubeclient::HttpError, exception) assert_equal("converting to : type names don't match (Pod, Namespace)", exception.message) assert_includes(exception.to_s, ' for POST http://localhost:8080/api') assert_equal(409, exception.error_code) end def test_deprecated_exception error_message = 'certificate verify failed' stub_request(:get, 'http://localhost:8080/api') .to_raise(OpenSSL::SSL::SSLError.new(error_message)) client = Kubeclient::Client.new('http://localhost:8080/api/') exception = assert_raises(KubeException) { client.api } assert_equal(error_message, exception.message) end def test_api stub_request(:get, 'http://localhost:8080/api') .to_return(status: 200, body: open_test_file('versions_list.json')) response = client.api assert_includes(response, 'versions') end def test_api_ssl_failure error_message = 'certificate verify failed' stub_request(:get, 'http://localhost:8080/api') .to_raise(OpenSSL::SSL::SSLError.new(error_message)) client = Kubeclient::Client.new('http://localhost:8080/api/') exception = assert_raises(Kubeclient::HttpError) { client.api } assert_equal(error_message, exception.message) end def test_api_timeout stub_request(:get, 'http://localhost:8080/api').to_timeout client = Kubeclient::Client.new('http://localhost:8080/api/') exception = assert_raises(Kubeclient::HttpError) { client.api } assert_match(/(timed out|timeout)/i, exception.message) end def test_api_valid stub_request(:get, 'http://localhost:8080/api') .to_return(status: 200, body: open_test_file('versions_list.json')) args = ['http://localhost:8080/api/'] [nil, 'v1beta3', 'v1'].each do |version| client = Kubeclient::Client.new(*(version ? args + [version] : args)) assert client.api_valid? end end def test_api_valid_with_invalid_version stub_request(:get, 'http://localhost:8080/api') .to_return(status: 200, body: open_test_file('versions_list.json')) client = Kubeclient::Client.new('http://localhost:8080/api/', 'foobar1') refute client.api_valid? end def test_api_valid_with_unreported_versions stub_request(:get, 'http://localhost:8080/api') .to_return(status: 200, body: '{}') client = Kubeclient::Client.new('http://localhost:8080/api/') refute client.api_valid? end def test_api_valid_with_invalid_json stub_request(:get, 'http://localhost:8080/api') .to_return(status: 200, body: '[]') client = Kubeclient::Client.new('http://localhost:8080/api/') refute client.api_valid? end def test_api_valid_with_bad_endpoint stub_request(:get, 'http://localhost:8080/api') .to_return(status: [404, 'Resource Not Found']) client = Kubeclient::Client.new('http://localhost:8080/api/') assert_raises(Kubeclient::HttpError) { client.api_valid? } end def test_api_valid_with_non_json stub_request(:get, 'http://localhost:8080/api') .to_return(status: 200, body: '') client = Kubeclient::Client.new('http://localhost:8080/api/') assert_raises(JSON::ParserError) { client.api_valid? } end def test_nonjson_exception stub_core_api_list stub_request(:get, %r{/servic}) .to_return(body: open_test_file('service_illegal_json_404.json'), status: 404) exception = assert_raises(Kubeclient::ResourceNotFoundError) do client.get_services end assert(exception.message.include?('Not Found')) assert_equal(404, exception.error_code) end def test_nonjson_exception_raw stub_core_api_list stub_request(:get, %r{/servic}) .to_return(body: open_test_file('service_illegal_json_404.json'), status: 404) exception = assert_raises(Kubeclient::ResourceNotFoundError) do client.get_services(as: :raw) end assert(exception.message.include?('Not Found')) assert_equal(404, exception.error_code) end def test_entity_list stub_core_api_list stub_get_services services = client.get_services refute_empty(services) assert_instance_of(Kubeclient::Common::EntityList, services) # Stripping of 'List' in collection.kind RecursiveOpenStruct mode only is historic. assert_equal('Service', services.kind) assert_equal(2, services.size) assert_instance_of(Kubeclient::Resource, services[0]) assert_instance_of(Kubeclient::Resource, services[1]) assert_requested(:get, 'http://localhost:8080/api/v1/services', times: 1) end def test_entity_list_raw stub_core_api_list stub_get_services response = client.get_services(as: :raw) refute_empty(response) assert_equal(open_test_file('entity_list.json').read, response) assert_requested(:get, 'http://localhost:8080/api/v1/services', times: 1) end def test_entity_list_parsed stub_core_api_list stub_get_services response = client.get_services(as: :parsed) assert_equal Hash, response.class assert_equal 'ServiceList', response['kind'] assert_equal %w[metadata spec status], response['items'].first.keys end def test_entity_list_parsed_symbolized stub_core_api_list stub_get_services response = client.get_services(as: :parsed_symbolized) assert_equal Hash, response.class assert_equal 'ServiceList', response[:kind] assert_equal %i[metadata spec status], response[:items].first.keys end def test_entity_list_unknown stub_core_api_list stub_get_services e = assert_raises(ArgumentError) { client.get_services(as: :whoops) } assert_equal 'Unsupported format :whoops', e.message end def test_entity_list_raw_failure stub_core_api_list stub_request(:get, %r{/services}) .to_return(body: open_test_file('entity_list.json'), status: 500) exception = assert_raises(Kubeclient::HttpError) { client.get_services(as: :raw) } assert_equal('500 Internal Server Error', exception.message) assert_equal(500, exception.error_code) end def test_entities_with_label_selector selector = 'component=apiserver' stub_core_api_list stub_get_services services = client.get_services(label_selector: selector) assert_instance_of(Kubeclient::Common::EntityList, services) assert_requested( :get, "http://localhost:8080/api/v1/services?labelSelector=#{selector}", times: 1 ) end def test_entities_with_resource_version version = '329' stub_core_api_list stub_get_services services = client.get_services(resource_version: version) assert_instance_of(Kubeclient::Common::EntityList, services) assert_requested( :get, "http://localhost:8080/api/v1/services?resourceVersion=#{version}", times: 1 ) end def test_entities_with_field_selector selector = 'involvedObject.name=redis-master' stub_core_api_list stub_get_services services = client.get_services(field_selector: selector) assert_instance_of(Kubeclient::Common::EntityList, services) assert_requested( :get, "http://localhost:8080/api/v1/services?fieldSelector=#{selector}", times: 1 ) end def test_empty_list stub_core_api_list stub_request(:get, %r{/pods}) .to_return(body: open_test_file('empty_pod_list.json'), status: 200) pods = client.get_pods assert_instance_of(Kubeclient::Common::EntityList, pods) assert_equal(0, pods.size) end def test_get_all stub_core_api_list stub_request(:get, %r{/bindings}) .to_return(body: open_test_file('bindings_list.json'), status: 404) stub_request(:get, %r{/configmaps}) .to_return(body: open_test_file('config_map_list.json'), status: 200) stub_request(:get, %r{/podtemplates}) .to_return(body: open_test_file('pod_template_list.json'), status: 200) stub_request(:get, %r{/services}) .to_return(body: open_test_file('service_list.json'), status: 200) stub_request(:get, %r{/pods}) .to_return(body: open_test_file('pod_list.json'), status: 200) stub_request(:get, %r{/nodes}) .to_return(body: open_test_file('node_list.json'), status: 200) stub_request(:get, %r{/replicationcontrollers}) .to_return(body: open_test_file('replication_controller_list.json'), status: 200) stub_request(:get, %r{/events}) .to_return(body: open_test_file('event_list.json'), status: 200) stub_request(:get, %r{/endpoints}) .to_return(body: open_test_file('endpoint_list.json'), status: 200) stub_request(:get, %r{/namespaces}) .to_return(body: open_test_file('namespace_list.json'), status: 200) stub_request(:get, %r{/secrets}) .to_return(body: open_test_file('secret_list.json'), status: 200) stub_request(:get, %r{/resourcequotas}) .to_return(body: open_test_file('resource_quota_list.json'), status: 200) stub_request(:get, %r{/limitranges}) .to_return(body: open_test_file('limit_range_list.json'), status: 200) stub_request(:get, %r{/persistentvolumes}) .to_return(body: open_test_file('persistent_volume_list.json'), status: 200) stub_request(:get, %r{/persistentvolumeclaims}) .to_return(body: open_test_file('persistent_volume_claim_list.json'), status: 200) stub_request(:get, %r{/componentstatuses}) .to_return(body: open_test_file('component_status_list.json'), status: 200) stub_request(:get, %r{/serviceaccounts}) .to_return(body: open_test_file('service_account_list.json'), status: 200) result = client.all_entities assert_equal(16, result.keys.size) assert_instance_of(Kubeclient::Common::EntityList, result['node']) assert_instance_of(Kubeclient::Common::EntityList, result['service']) assert_instance_of(Kubeclient::Common::EntityList, result['replication_controller']) assert_instance_of(Kubeclient::Common::EntityList, result['pod']) assert_instance_of(Kubeclient::Common::EntityList, result['event']) assert_instance_of(Kubeclient::Common::EntityList, result['namespace']) assert_instance_of(Kubeclient::Common::EntityList, result['secret']) assert_instance_of(Kubeclient::Resource, result['service'][0]) assert_instance_of(Kubeclient::Resource, result['node'][0]) assert_instance_of(Kubeclient::Resource, result['event'][0]) assert_instance_of(Kubeclient::Resource, result['endpoint'][0]) assert_instance_of(Kubeclient::Resource, result['namespace'][0]) assert_instance_of(Kubeclient::Resource, result['secret'][0]) assert_instance_of(Kubeclient::Resource, result['resource_quota'][0]) assert_instance_of(Kubeclient::Resource, result['limit_range'][0]) assert_instance_of(Kubeclient::Resource, result['persistent_volume'][0]) assert_instance_of(Kubeclient::Resource, result['persistent_volume_claim'][0]) assert_instance_of(Kubeclient::Resource, result['component_status'][0]) assert_instance_of(Kubeclient::Resource, result['service_account'][0]) end def test_get_all_raw stub_core_api_list stub_request(:get, %r{/bindings}) .to_return(body: open_test_file('bindings_list.json'), status: 404) stub_request(:get, %r{/configmaps}) .to_return(body: open_test_file('config_map_list.json'), status: 200) stub_request(:get, %r{/podtemplates}) .to_return(body: open_test_file('pod_template_list.json'), status: 200) stub_request(:get, %r{/services}) .to_return(body: open_test_file('service_list.json'), status: 200) stub_request(:get, %r{/pods}) .to_return(body: open_test_file('pod_list.json'), status: 200) stub_request(:get, %r{/nodes}) .to_return(body: open_test_file('node_list.json'), status: 200) stub_request(:get, %r{/replicationcontrollers}) .to_return(body: open_test_file('replication_controller_list.json'), status: 200) stub_request(:get, %r{/events}) .to_return(body: open_test_file('event_list.json'), status: 200) stub_request(:get, %r{/endpoints}) .to_return(body: open_test_file('endpoint_list.json'), status: 200) stub_request(:get, %r{/namespaces}) .to_return(body: open_test_file('namespace_list.json'), status: 200) stub_request(:get, %r{/secrets}) .to_return(body: open_test_file('secret_list.json'), status: 200) stub_request(:get, %r{/resourcequotas}) .to_return(body: open_test_file('resource_quota_list.json'), status: 200) stub_request(:get, %r{/limitranges}) .to_return(body: open_test_file('limit_range_list.json'), status: 200) stub_request(:get, %r{/persistentvolumes}) .to_return(body: open_test_file('persistent_volume_list.json'), status: 200) stub_request(:get, %r{/persistentvolumeclaims}) .to_return(body: open_test_file('persistent_volume_claim_list.json'), status: 200) stub_request(:get, %r{/componentstatuses}) .to_return(body: open_test_file('component_status_list.json'), status: 200) stub_request(:get, %r{/serviceaccounts}) .to_return(body: open_test_file('service_account_list.json'), status: 200) result = client.all_entities(as: :raw) assert_equal(16, result.keys.size) %w[ component_status config_map endpoint event limit_range namespace node persistent_volume persistent_volume_claim pod replication_controller resource_quota secret service service_account ].each do |entity| assert_equal(open_test_file("#{entity}_list.json").read, result[entity]) end end def test_api_bearer_token_with_params_success stub_request(:get, 'http://localhost:8080/api/v1/pods?labelSelector=name=redis-master') .with(headers: { Authorization: 'Bearer valid_token' }) .to_return(body: open_test_file('pod_list.json'), status: 200) stub_request(:get, %r{/api/v1$}) .with(headers: { Authorization: 'Bearer valid_token' }) .to_return(body: open_test_file('core_api_resource_list.json'), status: 200) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { bearer_token: 'valid_token' } ) pods = client.get_pods(label_selector: 'name=redis-master') assert_equal('Pod', pods.kind) assert_equal(1, pods.size) end def test_api_bearer_token_success stub_core_api_list stub_request(:get, 'http://localhost:8080/api/v1/pods') .with(headers: { Authorization: 'Bearer valid_token' }) .to_return( body: open_test_file('pod_list.json'), status: 200 ) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { bearer_token: 'valid_token' } ) pods = client.get_pods assert_equal('Pod', pods.kind) assert_equal(1, pods.size) end def test_api_bearer_token_failure error_message = '"/api/v1" is forbidden because ' \ 'system:anonymous cannot list on pods in' response = OpenStruct.new(code: 401, message: error_message) stub_request(:get, 'http://localhost:8080/api/v1') .with(headers: { Authorization: 'Bearer invalid_token' }) .to_raise(Kubeclient::HttpError.new(403, error_message, response)) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { bearer_token: 'invalid_token' } ) exception = assert_raises(Kubeclient::HttpError) { client.get_pods } assert_equal(403, exception.error_code) assert_equal(error_message, exception.message) assert_equal(response, exception.response) end def test_api_bearer_token_failure_raw error_message = '"/api/v1" is forbidden because ' \ 'system:anonymous cannot list on pods in' response = OpenStruct.new(code: 401, message: error_message) stub_request(:get, 'http://localhost:8080/api/v1') .with(headers: { Authorization: 'Bearer invalid_token' }) .to_raise(Kubeclient::HttpError.new(403, error_message, response)) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { bearer_token: 'invalid_token' } ) exception = assert_raises(Kubeclient::HttpError) { client.get_pods(as: :raw) } assert_equal(403, exception.error_code) assert_equal(error_message, exception.message) assert_equal(response, exception.response) end def test_api_basic_auth_success stub_request(:get, 'http://localhost:8080/api/v1') .with(basic_auth: %w[username password]) .to_return(body: open_test_file('core_api_resource_list.json'), status: 200) stub_request(:get, 'http://localhost:8080/api/v1/pods') .with(basic_auth: %w[username password]) .to_return(body: open_test_file('pod_list.json'), status: 200) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { username: 'username', password: 'password' } ) pods = client.get_pods assert_equal('Pod', pods.kind) assert_equal(1, pods.size) assert_requested( :get, 'http://localhost:8080/api/v1/pods', times: 1 ) end def test_api_basic_auth_back_comp_success stub_request(:get, 'http://localhost:8080/api/v1') .with(basic_auth: %w[username password]) .to_return(body: open_test_file('core_api_resource_list.json'), status: 200) stub_request(:get, 'http://localhost:8080/api/v1/pods') .with(basic_auth: %w[username password]) .to_return(body: open_test_file('pod_list.json'), status: 200) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { user: 'username', password: 'password' } ) pods = client.get_pods assert_equal('Pod', pods.kind) assert_equal(1, pods.size) assert_requested(:get, 'http://localhost:8080/api/v1/pods', times: 1) end def test_api_basic_auth_failure error_message = 'HTTP status code 401, 401 Unauthorized' response = OpenStruct.new(code: 401, message: '401 Unauthorized') stub_request(:get, 'http://localhost:8080/api/v1') .with(basic_auth: %w[username password]) .to_raise(Kubeclient::HttpError.new(401, error_message, response)) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { username: 'username', password: 'password' } ) exception = assert_raises(Kubeclient::HttpError) { client.get_pods } assert_equal(401, exception.error_code) assert_equal(error_message, exception.message) assert_equal(response, exception.response) assert_requested(:get, 'http://localhost:8080/api/v1', times: 1) end def test_api_basic_auth_failure_raw error_message = 'HTTP status code 401, 401 Unauthorized' response = OpenStruct.new(code: 401, message: '401 Unauthorized') stub_request(:get, 'http://localhost:8080/api/v1') .with(basic_auth: %w[username password]) .to_raise(Kubeclient::HttpError.new(401, error_message, response)) client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { username: 'username', password: 'password' } ) exception = assert_raises(Kubeclient::HttpError) { client.get_pods(as: :raw) } assert_equal(401, exception.error_code) assert_equal(error_message, exception.message) assert_equal(response, exception.response) assert_requested(:get, 'http://localhost:8080/api/v1', times: 1) end def test_init_username_no_password expected_msg = 'Basic auth requires both username & password' exception = assert_raises(ArgumentError) do Kubeclient::Client.new( 'http://localhost:8080', auth_options: { username: 'username' } ) end assert_equal(expected_msg, exception.message) end def test_init_user_no_password expected_msg = 'Basic auth requires both username & password' exception = assert_raises(ArgumentError) do Kubeclient::Client.new( 'http://localhost:8080', auth_options: { user: 'username' } ) end assert_equal(expected_msg, exception.message) end def test_init_username_and_bearer_token expected_msg = 'Invalid auth options: specify only one of username/password,' \ ' bearer_token or bearer_token_file' exception = assert_raises(ArgumentError) do Kubeclient::Client.new( 'http://localhost:8080', auth_options: { username: 'username', bearer_token: 'token' } ) end assert_equal(expected_msg, exception.message) end def test_init_username_and_bearer_token_file expected_msg = 'Invalid auth options: specify only one of username/password,' \ ' bearer_token or bearer_token_file' exception = assert_raises(ArgumentError) do Kubeclient::Client.new( 'http://localhost:8080', auth_options: { username: 'username', bearer_token_file: 'token-file' } ) end assert_equal(expected_msg, exception.message) end def test_bearer_token_and_bearer_token_file expected_msg = 'Invalid auth options: specify only one of username/password,' \ ' bearer_token or bearer_token_file' exception = assert_raises(ArgumentError) do Kubeclient::Client.new( 'http://localhost:8080', auth_options: { bearer_token: 'token', bearer_token_file: 'token-file' } ) end assert_equal(expected_msg, exception.message) end def test_bearer_token_file_not_exist expected_msg = 'Token file token-file does not exist' exception = assert_raises(ArgumentError) do Kubeclient::Client.new( 'http://localhost:8080', auth_options: { bearer_token_file: 'token-file' } ) end assert_equal(expected_msg, exception.message) end def test_api_bearer_token_file_success stub_core_api_list stub_request(:get, 'http://localhost:8080/api/v1/pods') .with(headers: { Authorization: 'Bearer valid_token' }) .to_return(body: open_test_file('pod_list.json'), status: 200) file = File.join(File.dirname(__FILE__), 'valid_token_file') client = Kubeclient::Client.new( 'http://localhost:8080/api/', auth_options: { bearer_token_file: file } ) pods = client.get_pods assert_equal('Pod', pods.kind) assert_equal(1, pods.size) end def test_proxy_url stub_core_api_list client = Kubeclient::Client.new('http://host:8080', 'v1') assert_equal( 'http://host:8080/api/v1/namespaces/ns/services/srvname:srvportname/proxy', client.proxy_url('service', 'srvname', 'srvportname', 'ns') ) assert_equal( 'http://host:8080/api/v1/namespaces/ns/services/srvname:srvportname/proxy', client.proxy_url('services', 'srvname', 'srvportname', 'ns') ) assert_equal( 'http://host:8080/api/v1/namespaces/ns/pods/srvname:srvportname/proxy', client.proxy_url('pod', 'srvname', 'srvportname', 'ns') ) assert_equal( 'http://host:8080/api/v1/namespaces/ns/pods/srvname:srvportname/proxy', client.proxy_url('pods', 'srvname', 'srvportname', 'ns') ) # Check no namespace provided assert_equal( 'http://host:8080/api/v1/nodes/srvname:srvportname/proxy', client.proxy_url('nodes', 'srvname', 'srvportname') ) assert_equal( 'http://host:8080/api/v1/nodes/srvname:srvportname/proxy', client.proxy_url('node', 'srvname', 'srvportname') ) # Check integer port assert_equal( 'http://host:8080/api/v1/nodes/srvname:5001/proxy', client.proxy_url('nodes', 'srvname', 5001) ) assert_equal( 'http://host:8080/api/v1/nodes/srvname:5001/proxy', client.proxy_url('node', 'srvname', 5001) ) end def test_attr_readers client = Kubeclient::Client.new( 'http://localhost:8080/api/', ssl_options: { client_key: 'secret' }, auth_options: { bearer_token: 'token' } ) assert_equal('/api', client.api_endpoint.path) assert_equal('secret', client.ssl_options[:client_key]) assert_equal('token', client.auth_options[:bearer_token]) assert_equal('Bearer token', client.headers[:Authorization]) end def test_nil_items # handle https://github.com/kubernetes/kubernetes/issues/13096 stub_core_api_list stub_request(:get, %r{/persistentvolumeclaims}) .to_return(body: open_test_file('persistent_volume_claims_nil_items.json'), status: 200) client.get_persistent_volume_claims end # Timeouts def test_timeouts_defaults client = Kubeclient::Client.new( 'http://localhost:8080/api/' ) rest_client = client.rest_client assert_default_open_timeout(rest_client.open_timeout) assert_equal(60, rest_client.read_timeout) end def test_timeouts_open client = Kubeclient::Client.new( 'http://localhost:8080/api/', timeouts: { open: 10 } ) rest_client = client.rest_client assert_equal(10, rest_client.open_timeout) assert_equal(60, rest_client.read_timeout) end def test_timeouts_read client = Kubeclient::Client.new( 'http://localhost:8080/api/', timeouts: { read: 300 } ) rest_client = client.rest_client assert_default_open_timeout(rest_client.open_timeout) assert_equal(300, rest_client.read_timeout) end def test_timeouts_both client = Kubeclient::Client.new( 'http://localhost:8080/api/', timeouts: { open: 10, read: 300 } ) rest_client = client.rest_client assert_equal(10, rest_client.open_timeout) assert_equal(300, rest_client.read_timeout) end def test_timeouts_infinite client = Kubeclient::Client.new( 'http://localhost:8080/api/', timeouts: { open: nil, read: nil } ) rest_client = client.rest_client assert_nil(rest_client.open_timeout) assert_nil(rest_client.read_timeout) end def assert_default_open_timeout(actual) if RUBY_VERSION >= '2.3' assert_equal(60, actual) else assert_nil(actual) end end private def stub_get_services stub_request(:get, %r{/services}) .to_return(body: open_test_file('entity_list.json'), status: 200) end def client @client ||= Kubeclient::Client.new('http://localhost:8080/api/', 'v1') end # dup method creates a shallow copy which is not good in this case # since rename_keys changes the input hash # hence need to create a deep_copy def deep_copy(hash) Marshal.load(Marshal.dump(hash)) end end kubeclient-4.6.0/test/test_config.rb0000644000004100000410000002176513612310022017507 0ustar www-datawww-datarequire_relative 'test_helper' require 'yaml' require 'open3' # Testing Kubernetes client configuration class KubeclientConfigTest < MiniTest::Test def test_allinone config = Kubeclient::Config.read(config_file('allinone.kubeconfig')) assert_equal(['default/localhost:8443/system:admin'], config.contexts) check_context(config.context, ssl: true) end def test_external config = Kubeclient::Config.read(config_file('external.kubeconfig')) assert_equal(['default/localhost:8443/system:admin'], config.contexts) check_context(config.context, ssl: true) end def test_allinone_nopath yaml = File.read(config_file('allinone.kubeconfig')) # A self-contained config shouldn't depend on kcfg_path. config = Kubeclient::Config.new(YAML.safe_load(yaml), nil) assert_equal(['default/localhost:8443/system:admin'], config.contexts) check_context(config.context, ssl: true) end def test_external_nopath yaml = File.read(config_file('external.kubeconfig')) # kcfg_path = nil should prevent file access config = Kubeclient::Config.new(YAML.safe_load(yaml), nil) assert_raises(StandardError) do config.context end end def test_external_nopath_absolute yaml = File.read(config_file('external.kubeconfig')) # kcfg_path = nil should prevent file access, even if absolute path specified ca_absolute_path = File.absolute_path(config_file('external-')) yaml = yaml.gsub('external-', ca_absolute_path) config = Kubeclient::Config.new(YAML.safe_load(yaml), nil) assert_raises(StandardError) do config.context end end def test_nouser config = Kubeclient::Config.read(config_file('nouser.kubeconfig')) assert_equal(['default/localhost:8443/nouser'], config.contexts) check_context(config.context, ssl: false) end def test_user_token config = Kubeclient::Config.read(config_file('userauth.kubeconfig')) assert_equal(['localhost/system:admin:token', 'localhost/system:admin:userpass'], config.contexts) context = config.context('localhost/system:admin:token') check_context(context, ssl: false) assert_equal('0123456789ABCDEF0123456789ABCDEF', context.auth_options[:bearer_token]) end def test_user_password config = Kubeclient::Config.read(config_file('userauth.kubeconfig')) assert_equal(['localhost/system:admin:token', 'localhost/system:admin:userpass'], config.contexts) context = config.context('localhost/system:admin:userpass') check_context(context, ssl: false) assert_equal('admin', context.auth_options[:username]) assert_equal('pAssw0rd123', context.auth_options[:password]) end def test_timestamps # Test YAML parsing doesn't crash on YAML timestamp syntax. Kubeclient::Config.read(config_file('timestamps.kubeconfig')) end def test_user_exec token = '0123456789ABCDEF0123456789ABCDEF' creds = { 'apiVersion': 'client.authentication.k8s.io/v1beta1', 'status': { 'token': token } } config = Kubeclient::Config.read(config_file('execauth.kubeconfig')) assert_equal(['localhost/system:admin:exec-search-path', 'localhost/system:admin:exec-relative-path', 'localhost/system:admin:exec-absolute-path'], config.contexts) # A bare command name in config means search PATH, so it's executed as bare command. stub_exec(%r{^example-exec-plugin$}, creds) do context = config.context('localhost/system:admin:exec-search-path') check_context(context, ssl: false) assert_equal(token, context.auth_options[:bearer_token]) end # A relative path is taken relative to the dir of the kubeconfig. stub_exec(%r{.*config/dir/example-exec-plugin$}, creds) do context = config.context('localhost/system:admin:exec-relative-path') check_context(context, ssl: false) assert_equal(token, context.auth_options[:bearer_token]) end # An absolute path is taken as-is. stub_exec(%r{^/abs/path/example-exec-plugin$}, creds) do context = config.context('localhost/system:admin:exec-absolute-path') check_context(context, ssl: false) assert_equal(token, context.auth_options[:bearer_token]) end end def test_user_exec_nopath yaml = File.read(config_file('execauth.kubeconfig')) config = Kubeclient::Config.new(YAML.safe_load(yaml), nil) config.contexts.each do |context_name| Open3.stub(:capture3, proc { flunk 'should not execute command' }) do assert_raises(StandardError) do config.context(context_name) end end end end def test_gcp_default_auth Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token1').once parsed = YAML.safe_load(File.read(config_file('gcpauth.kubeconfig')), [Date, Time]) config = Kubeclient::Config.new(parsed, nil) config.context(config.contexts.first) end # Each call to .context() obtains a new token, calling .auth_options doesn't change anything. # NOTE: this is not a guarantee, may change, just testing current behavior. def test_gcp_default_auth_renew Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token1').once parsed = YAML.safe_load(File.read(config_file('gcpauth.kubeconfig')), [Date, Time]) config = Kubeclient::Config.new(parsed, nil) context = config.context(config.contexts.first) assert_equal({ bearer_token: 'token1' }, context.auth_options) assert_equal({ bearer_token: 'token1' }, context.auth_options) Kubeclient::GoogleApplicationDefaultCredentials.expects(:token).returns('token2').once context2 = config.context(config.contexts.first) assert_equal({ bearer_token: 'token2' }, context2.auth_options) assert_equal({ bearer_token: 'token1' }, context.auth_options) end def test_gcp_command_auth Kubeclient::GCPCommandCredentials.expects(:token) .with('access-token' => '', 'cmd-args' => 'config config-helper --format=json', 'cmd-path' => '/path/to/gcloud', 'expiry' => '2019-04-09 19:26:18 UTC', 'expiry-key' => '{.credential.token_expiry}', 'token-key' => '{.credential.access_token}') .returns('token1') .once config = Kubeclient::Config.read(config_file('gcpcmdauth.kubeconfig')) config.context(config.contexts.first) end def test_oidc_auth_provider Kubeclient::OIDCAuthProvider.expects(:token) .with('client-id' => 'fake-client-id', 'client-secret' => 'fake-client-secret', 'id-token' => 'fake-id-token', 'idp-issuer-url' => 'https://accounts.google.com', 'refresh-token' => 'fake-refresh-token') .returns('token1') .once parsed = YAML.safe_load(File.read(config_file('oidcauth.kubeconfig'))) config = Kubeclient::Config.new(parsed, nil) config.context(config.contexts.first) end private def check_context(context, ssl: true) assert_equal('https://localhost:8443', context.api_endpoint) assert_equal('v1', context.api_version) assert_equal('default', context.namespace) if ssl assert_equal(OpenSSL::SSL::VERIFY_PEER, context.ssl_options[:verify_ssl]) assert_kind_of(OpenSSL::X509::Store, context.ssl_options[:cert_store]) assert_kind_of(OpenSSL::X509::Certificate, context.ssl_options[:client_cert]) assert_kind_of(OpenSSL::PKey::RSA, context.ssl_options[:client_key]) # When certificates expire the quickest way to recreate them is using # an OpenShift tool (100% compatible with kubernetes): # # $ oc adm ca create-master-certs --hostnames=localhost # # At the time of this writing the files to be updated are: # # cp openshift.local.config/master/admin.kubeconfig test/config/allinone.kubeconfig # cp openshift.local.config/master/ca.crt test/config/external-ca.pem # cp openshift.local.config/master/admin.crt test/config/external-cert.pem # cp openshift.local.config/master/admin.key test/config/external-key.rsa assert(context.ssl_options[:cert_store].verify(context.ssl_options[:client_cert])) else assert_equal(OpenSSL::SSL::VERIFY_NONE, context.ssl_options[:verify_ssl]) end end def config_file(name) File.join(File.dirname(__FILE__), 'config', name) end def stub_exec(command_regexp, creds) st = Minitest::Mock.new st.expect(:success?, true) capture3_stub = lambda do |_env, command, *_args| assert_match command_regexp, command [JSON.dump(creds), nil, st] end Open3.stub(:capture3, capture3_stub) do yield end end end kubeclient-4.6.0/test/test_resource_quota.rb0000644000004100000410000000141313612310022021266 0ustar www-datawww-datarequire_relative 'test_helper' # ResourceQuota tests class TestResourceQuota < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/resourcequotas}) .to_return(body: open_test_file('resource_quota.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') quota = client.get_resource_quota('quota', 'quota-example') assert_instance_of(Kubeclient::Resource, quota) assert_equal('quota', quota.metadata.name) assert_equal('20', quota.spec.hard.cpu) assert_equal('10', quota.spec.hard.secrets) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/quota-example/resourcequotas/quota', times: 1) end end kubeclient-4.6.0/test/test_service.rb0000644000004100000410000002542213612310022017674 0ustar www-datawww-datarequire_relative 'test_helper' # Service entity tests class TestService < MiniTest::Test def test_construct_our_own_service our_service = Kubeclient::Resource.new our_service.metadata = {} our_service.metadata.name = 'guestbook' our_service.metadata.namespace = 'staging' our_service.metadata.labels = {} our_service.metadata.labels.name = 'guestbook' our_service.spec = {} our_service.spec.ports = [{ 'port' => 3000, 'targetPort' => 'http-server', 'protocol' => 'TCP' }] assert_equal('guestbook', our_service.metadata.labels.name) hash = our_service.to_h assert_equal(our_service.metadata.labels.name, hash[:metadata][:labels][:name]) expected_url = 'http://localhost:8080/api/v1/namespaces/staging/services' stub_core_api_list stub_request(:post, expected_url) .to_return(body: open_test_file('created_service.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/api/') created = client.create_service(our_service) assert_instance_of(Kubeclient::Resource, created) assert_equal(created.metadata.name, our_service.metadata.name) assert_equal(created.spec.ports.size, our_service.spec.ports.size) # Check that original entity_config is not modified by kind/apiVersion patches: assert_nil(our_service.kind) assert_requested(:post, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['kind'] == 'Service' && data['apiVersion'] == 'v1' && data['metadata']['name'] == 'guestbook' && data['metadata']['namespace'] == 'staging' end end def test_construct_service_from_symbol_keys service = Kubeclient::Resource.new service.metadata = { labels: { tier: 'frontend' }, name: 'test-service', namespace: 'staging' } service.spec = { ports: [{ port: 3000, targetPort: 'http-server', protocol: 'TCP' }] } expected_url = 'http://localhost:8080/api/v1/namespaces/staging/services' stub_core_api_list stub_request(:post, expected_url) .to_return(body: open_test_file('created_service.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/api/') client.create_service(service) assert_requested(:post, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['kind'] == 'Service' && data['apiVersion'] == 'v1' && data['metadata']['name'] == 'test-service' && data['metadata']['labels']['tier'] == 'frontend' && data['metadata']['namespace'] == 'staging' end end def test_construct_service_from_string_keys service = Kubeclient::Resource.new service.metadata = { 'labels' => { 'tier' => 'frontend' }, 'name' => 'test-service', 'namespace' => 'staging' } service.spec = { 'ports' => [{ 'port' => 3000, 'targetPort' => 'http-server', 'protocol' => 'TCP' }] } stub_core_api_list expected_url = 'http://localhost:8080/api/v1/namespaces/staging/services' stub_request(:post, %r{namespaces/staging/services}) .to_return(body: open_test_file('created_service.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/api/') client.create_service(service) assert_requested(:post, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['kind'] == 'Service' && data['apiVersion'] == 'v1' && data['metadata']['name'] == 'test-service' && data['metadata']['labels']['tier'] == 'frontend' && data['metadata']['namespace'] == 'staging' end end def test_conversion_from_json_v1 stub_core_api_list stub_request(:get, %r{/services}) .to_return(body: open_test_file('service.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/') service = client.get_service('redis-slave', 'development') assert_instance_of(Kubeclient::Resource, service) assert_equal('2015-04-05T13:00:31Z', service.metadata.creationTimestamp) assert_equal('bdb80a8f-db93-11e4-b293-f8b156af4ae1', service.metadata.uid) assert_equal('redis-slave', service.metadata.name) assert_equal('2815', service.metadata.resourceVersion) assert_equal('v1', service.apiVersion) assert_equal('10.0.0.140', service.spec.clusterIP) assert_equal('development', service.metadata.namespace) assert_equal('TCP', service.spec.ports[0].protocol) assert_equal(6379, service.spec.ports[0].port) assert_equal('', service.spec.ports[0].name) assert_equal('redis-server', service.spec.ports[0].targetPort) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/development/services/redis-slave', times: 1) end def test_delete_service our_service = Kubeclient::Resource.new our_service.name = 'redis-service' # TODO, new ports assignment to be added our_service.labels = {} our_service.labels.component = 'apiserver' our_service.labels.provider = 'kubernetes' stub_core_api_list stub_request(:delete, %r{/namespaces/default/services}) .to_return(body: open_test_file('service.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') our_service = client.delete_service(our_service.name, 'default') assert_kind_of(RecursiveOpenStruct, our_service) assert_requested(:delete, 'http://localhost:8080/api/v1/namespaces/default/services/redis-service', times: 1) end def test_get_service_no_ns stub_core_api_list # when not specifying namespace for entities which # are not node or namespace, the request will fail stub_request(:get, %r{/services/redis-slave}) .to_return(status: 404) client = Kubeclient::Client.new('http://localhost:8080/api/') exception = assert_raises(Kubeclient::HttpError) do client.get_service('redis-slave') end assert_equal(404, exception.error_code) end def test_get_service stub_core_api_list stub_request(:get, %r{/namespaces/development/services/redis-slave}) .to_return(body: open_test_file('service.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/') service = client.get_service('redis-slave', 'development') assert_equal('redis-slave', service.metadata.name) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/development/services/redis-slave', times: 1) end def test_update_service service = Kubeclient::Resource.new name = 'my_service' service.metadata = {} service.metadata.name = name service.metadata.namespace = 'development' stub_core_api_list expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}" stub_request(:put, expected_url) .to_return(body: open_test_file('service_update.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') service = client.update_service(service) assert_kind_of(RecursiveOpenStruct, service) assert_requested(:put, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['metadata']['name'] == name && data['metadata']['namespace'] == 'development' end end def test_update_service_with_string_keys service = Kubeclient::Resource.new name = 'my_service' service.metadata = { 'name' => name, 'namespace' => 'development' } stub_core_api_list expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}" stub_request(:put, expected_url) .to_return(body: open_test_file('service_update.json'), status: 201) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') service = client.update_service(service) assert_kind_of(RecursiveOpenStruct, service) assert_requested(:put, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['metadata']['name'] == name && data['metadata']['namespace'] == 'development' end end def test_patch_service service = Kubeclient::Resource.new name = 'my_service' service.metadata = {} service.metadata.name = name service.metadata.namespace = 'development' stub_core_api_list expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}" stub_request(:patch, expected_url) .to_return(body: open_test_file('service_patch.json'), status: 200) patch = { metadata: { annotations: { key: 'value' } } } client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') service = client.patch_service(name, patch, 'development') assert_kind_of(RecursiveOpenStruct, service) assert_requested(:patch, expected_url, times: 1) do |req| data = JSON.parse(req.body) data['metadata']['annotations']['key'] == 'value' end end def test_json_patch_service service = Kubeclient::Resource.new name = 'my-service' service.metadata = {} service.metadata.name = name service.metadata.namespace = 'development' stub_core_api_list expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}" stub_request(:patch, expected_url) .to_return(body: open_test_file('service_json_patch.json'), status: 200) patch = [ { 'op' => 'add', 'path' => '/spec/type', 'value' => 'LoadBalancer' } ] client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') service = client.json_patch_service(name, patch, 'development') assert_kind_of(RecursiveOpenStruct, service) assert_requested(:patch, expected_url, times: 1) do |req| data = JSON.parse(req.body) req.headers['Content-Type'] == 'application/json-patch+json' && data == patch end end def test_merge_patch_service service = Kubeclient::Resource.new name = 'my-service' service.metadata = {} service.metadata.name = name service.metadata.namespace = 'development' stub_core_api_list expected_url = "http://localhost:8080/api/v1/namespaces/development/services/#{name}" stub_request(:patch, expected_url) .to_return(body: open_test_file('service_merge_patch.json'), status: 200) patch = { spec: { type: 'NodePort' } } client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') service = client.merge_patch_service(name, patch, 'development') assert_kind_of(RecursiveOpenStruct, service) assert_requested(:patch, expected_url, times: 1) do |req| data = JSON.parse(req.body) req.headers['Content-Type'] == 'application/merge-patch+json' && data['spec']['type'] == 'NodePort' end end end kubeclient-4.6.0/test/test_service_account.rb0000644000004100000410000000161413612310022021405 0ustar www-datawww-datarequire_relative 'test_helper' # ServiceAccount tests class TestServiceAccount < MiniTest::Test def test_get_from_json_v1 stub_core_api_list stub_request(:get, %r{/serviceaccounts}) .to_return(body: open_test_file('service_account.json'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') account = client.get_service_account('default') assert_instance_of(Kubeclient::Resource, account) assert_equal('default', account.metadata.name) assert_equal('default-token-6s23q', account.secrets[0].name) assert_equal('default-dockercfg-62tf3', account.secrets[1].name) assert_requested(:get, 'http://localhost:8080/api/v1/serviceaccounts/default', times: 1) assert_requested(:get, 'http://localhost:8080/api/v1', times: 1) end end kubeclient-4.6.0/test/txt/0000755000004100000410000000000013612310022015462 5ustar www-datawww-datakubeclient-4.6.0/test/txt/pod_log.txt0000644000004100000410000000017513612310022017651 0ustar www-datawww-dataInitializing server... ...loaded configuration ...updated settings ...discovered local servers ...frobinated disks Complete! kubeclient-4.6.0/test/test_pod_log.rb0000644000004100000410000001317013612310022017654 0ustar www-datawww-datarequire_relative 'test_helper' # Pod log tests class TestPodLog < MiniTest::Test def test_get_pod_log stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log}) .to_return(body: open_test_file('pod_log.txt'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') retrieved_log = client.get_pod_log('redis-master-pod', 'default') assert_equal(open_test_file('pod_log.txt').read, retrieved_log) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log', times: 1) end def test_get_pod_log_container stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log}) .to_return(body: open_test_file('pod_log.txt'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') retrieved_log = client.get_pod_log('redis-master-pod', 'default', container: 'ruby') assert_equal(open_test_file('pod_log.txt').read, retrieved_log) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?container=ruby', times: 1) end def test_get_pod_log_since_time stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log}) .to_return(body: open_test_file('pod_log.txt'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') retrieved_log = client.get_pod_log('redis-master-pod', 'default', timestamps: true, since_time: '2018-04-27T18:30:17.480321984Z') assert_equal(open_test_file('pod_log.txt').read, retrieved_log) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?sinceTime=2018-04-27T18:30:17.480321984Z×tamps=true', times: 1) end def test_get_pod_log_tail_lines selected_lines = open_test_file('pod_log.txt').to_a[-2..1].join stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log}) .to_return(body: selected_lines, status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') retrieved_log = client.get_pod_log('redis-master-pod', 'default', tail_lines: 2) assert_equal(selected_lines, retrieved_log) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?tailLines=2', times: 1) end def test_get_pod_limit_bytes selected_bytes = open_test_file('pod_log.txt').read(10) stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log}) .to_return(body: selected_bytes, status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') retrieved_log = client.get_pod_log('redis-master-pod', 'default', limit_bytes: 10) assert_equal(selected_bytes, retrieved_log) assert_requested(:get, 'http://localhost:8080/api/v1/namespaces/default/pods/redis-master-pod/log?limitBytes=10', times: 1) end def test_watch_pod_log file = open_test_file('pod_log.txt') expected_lines = file.read.split("\n") stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow}) .to_return(body: file, status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') stream = client.watch_pod_log('redis-master-pod', 'default') stream.to_enum.with_index do |notice, index| assert_instance_of(String, notice) assert_equal(expected_lines[index], notice) end end def test_watch_pod_log_with_block file = open_test_file('pod_log.txt') first = file.readlines.first.chomp stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow}) .to_return(body: file, status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') client.watch_pod_log('redis-master-pod', 'default') do |line| assert_equal first, line break end end def test_watch_pod_log_follow_redirect expected_lines = open_test_file('pod_log.txt').read.split("\n") redirect = 'http://localhost:1234/api/namespaces/default/pods/redis-master-pod/log' stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow}) .to_return(status: 302, headers: { location: redirect }) stub_request(:get, redirect) .to_return(body: open_test_file('pod_log.txt'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1') stream = client.watch_pod_log('redis-master-pod', 'default') stream.to_enum.with_index do |notice, index| assert_instance_of(String, notice) assert_equal(expected_lines[index], notice) end end def test_watch_pod_log_max_redirect redirect = 'http://localhost:1234/api/namespaces/default/pods/redis-master-pod/log' stub_request(:get, %r{/namespaces/default/pods/[a-z0-9-]+/log\?.*follow}) .to_return(status: 302, headers: { location: redirect }) stub_request(:get, redirect) .to_return(body: open_test_file('pod_log.txt'), status: 200) client = Kubeclient::Client.new('http://localhost:8080/api/', 'v1', http_max_redirects: 0) assert_raises(Kubeclient::HttpError) do client.watch_pod_log('redis-master-pod', 'default').each do end end end end kubeclient-4.6.0/test/test_helper.rb0000644000004100000410000000105513612310022017507 0ustar www-datawww-datarequire 'bundler/setup' require 'minitest/autorun' require 'minitest/rg' require 'webmock/minitest' require 'mocha/minitest' require 'json' require 'kubeclient' # Assumes test files will be in a subdirectory with the same name as the # file suffix. e.g. a file named foo.json would be a "json" subdirectory. def open_test_file(name) File.new(File.join(File.dirname(__FILE__), name.split('.').last, name)) end def stub_core_api_list stub_request(:get, %r{/api/v1$}) .to_return(body: open_test_file('core_api_resource_list.json'), status: 200) end kubeclient-4.6.0/README.md0000644000004100000410000006506513612310022015157 0ustar www-datawww-data# Kubeclient [![Gem Version](https://badge.fury.io/rb/kubeclient.svg)](http://badge.fury.io/rb/kubeclient) [![Build Status](https://travis-ci.org/abonas/kubeclient.svg?branch=master)](https://travis-ci.org/abonas/kubeclient) [![Code Climate](http://img.shields.io/codeclimate/github/abonas/kubeclient.svg)](https://codeclimate.com/github/abonas/kubeclient) A Ruby client for Kubernetes REST api. The client supports GET, POST, PUT, DELETE on all the entities available in kubernetes in both the core and group apis. The client currently supports Kubernetes REST api version v1. To learn more about groups and versions in kubernetes refer to [k8s docs](https://kubernetes.io/docs/api/) ## Installation Add this line to your application's Gemfile: ```ruby gem 'kubeclient' ``` And then execute: ```Bash bundle ``` Or install it yourself as: ```Bash gem install kubeclient ``` ## Usage Initialize the client: ```ruby client = Kubeclient::Client.new('http://localhost:8080/api/', "v1") ``` Or without specifying version (it will be set by default to "v1") ```ruby client = Kubeclient::Client.new('http://localhost:8080/api/') ``` For A Group Api: ```ruby client = Kubeclient::Client.new('http://localhost:8080/apis/batch', 'v1') ``` Another option is to initialize the client with URI object: ```ruby uri = URI::HTTP.build(host: "somehostname", port: 8080) client = Kubeclient::Client.new(uri) ``` ### SSL It is also possible to use https and configure ssl with: ```ruby ssl_options = { client_cert: OpenSSL::X509::Certificate.new(File.read('/path/to/client.crt')), client_key: OpenSSL::PKey::RSA.new(File.read('/path/to/client.key')), ca_file: '/path/to/ca.crt', verify_ssl: OpenSSL::SSL::VERIFY_PEER } client = Kubeclient::Client.new( 'https://localhost:8443/api/', "v1", ssl_options: ssl_options ) ``` As an alternative to the `ca_file` it's possible to use the `cert_store`: ```ruby cert_store = OpenSSL::X509::Store.new cert_store.add_cert(OpenSSL::X509::Certificate.new(ca_cert_data)) ssl_options = { cert_store: cert_store, verify_ssl: OpenSSL::SSL::VERIFY_PEER } client = Kubeclient::Client.new( 'https://localhost:8443/api/', "v1", ssl_options: ssl_options ) ``` For testing and development purpose you can disable the ssl check with: ```ruby ssl_options = { verify_ssl: OpenSSL::SSL::VERIFY_NONE } client = Kubeclient::Client.new( 'https://localhost:8443/api/', 'v1', ssl_options: ssl_options ) ``` ### Authentication If you are using basic authentication or bearer tokens as described [here](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/authentication.md) then you can specify one of the following: ```ruby auth_options = { username: 'username', password: 'password' } client = Kubeclient::Client.new( 'https://localhost:8443/api/', 'v1', auth_options: auth_options ) ``` or ```ruby auth_options = { bearer_token: 'MDExMWJkMjItOWY1Ny00OGM5LWJlNDEtMjBiMzgxODkxYzYz' } client = Kubeclient::Client.new( 'https://localhost:8443/api/', 'v1', auth_options: auth_options ) ``` or ```ruby auth_options = { bearer_token_file: '/path/to/token_file' } client = Kubeclient::Client.new( 'https://localhost:8443/api/', 'v1', auth_options: auth_options ) ``` #### Inside a Kubernetes cluster The [recommended way to locate the API server](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod) within the pod is with the `kubernetes.default.svc` DNS name, which resolves to a Service IP which in turn will be routed to an API server. The recommended way to authenticate to the API server is with a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/). kube-system associates a pod with a service account and a bearer token for that service account is placed into the filesystem tree of each container in that pod at `/var/run/secrets/kubernetes.io/serviceaccount/token`. If available, a certificate bundle is placed into the filesystem tree of each container at `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`, and should be used to verify the serving certificate of the API server. For example: ```ruby auth_options = { bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token' } ssl_options = {} if File.exist?("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt") ssl_options[:ca_file] = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" end client = Kubeclient::Client.new( 'https://kubernetes.default.svc', 'v1', auth_options: auth_options, ssl_options: ssl_options ) ``` Finally, the default namespace to be used for namespaced API operations is placed in a file at `/var/run/secrets/kubernetes.io/serviceaccount/namespace` in each container. It is recommended that you use this namespace when issuing API commands below. ```ruby namespace = File.read('/var/run/secrets/kubernetes.io/serviceaccount/namespace') ``` You can find information about tokens in [this guide](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod) and in [this reference](http://kubernetes.io/docs/admin/authentication/). ### Non-blocking IO You can also use kubeclient with non-blocking sockets such as Celluloid::IO, see [here](https://github.com/httprb/http/wiki/Parallel-requests-with-Celluloid%3A%3AIO) for details. For example: ```ruby require 'celluloid/io' socket_options = { socket_class: Celluloid::IO::TCPSocket, ssl_socket_class: Celluloid::IO::SSLSocket } client = Kubeclient::Client.new( 'https://localhost:8443/api/', 'v1', socket_options: socket_options ) ``` This affects only `.watch_*` sockets, not one-off actions like `.get_*`, `.delete_*` etc. ### Proxies You can also use kubeclient with an http proxy server such as tinyproxy. It can be entered as a string or a URI object. For example: ```ruby proxy_uri = URI::HTTP.build(host: "myproxyhost", port: 8443) client = Kubeclient::Client.new( 'https://localhost:8443/api/', http_proxy_uri: proxy_uri ) ``` ### Redirects You can optionally not allow redirection with kubeclient. For example: ```ruby client = Kubeclient::Client.new( 'https://localhost:8443/api/', http_max_redirects: 0 ) ``` ### Timeouts Watching never times out. One-off actions like `.get_*`, `.delete_*` have a configurable timeout: ```ruby timeouts = { open: 10, # unit is seconds read: nil # nil means never time out } client = Kubeclient::Client.new( 'https://localhost:8443/api/', timeouts: timeouts ) ``` Default timeouts match `Net::HTTP` and `RestClient`, which unfortunately depends on ruby version: - open was infinite up to ruby 2.2, 60 seconds in 2.3+. - read is 60 seconds. If you want ruby-independent behavior, always specify `:open`. ### Discovery Discovery from the kube-apiserver is done lazily on method calls so it would not change behavior. It can also be done explicitly: ```ruby client = Kubeclient::Client.new('http://localhost:8080/api', 'v1') client.discover ``` It is possible to check the status of discovery ```ruby unless client.discovered client.discover end ``` ### Kubeclient::Config If you've been using `kubectl` and have a `.kube/config` file (possibly referencing other files in fields such as `client-certificate`), you can auto-populate a config object using `Kubeclient::Config`: ```ruby # assuming $KUBECONFIG is one file, won't merge multiple like kubectl config = Kubeclient::Config.read(ENV['KUBECONFIG'] || '/path/to/.kube/config') ``` This will lookup external files; relative paths will be resolved relative to the file's directory, if config refers to them with relative path. This includes external [`exec:` credential plugins][exec] to be executed. [exec]: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins You can also construct `Config` directly from nested data. For example if you have JSON or YAML config data in a variable: ```ruby config = Kubeclient::Config.new(YAML.safe_load(yaml_text), nil) # or config = Kubeclient::Config.new(JSON.parse(json_text), nil) ``` The 2nd argument is a base directory for finding external files, if config refers to them with relative path. Setting it to `nil` disables file lookups, and `exec:` execution - such configs will raise an exception. (A config can be self-contained by using inline fields such as `client-certificate-data`.) To create a client based on a Config object: ```ruby # default context according to `current-context` field: context = config.context # or to use a specific context, by name: context = config.context('default/192-168-99-100:8443/system:admin') Kubeclient::Client.new( context.api_endpoint, 'v1', ssl_options: context.ssl_options, auth_options: context.auth_options ) ``` #### Amazon EKS Credentials On Amazon EKS by default the authentication method is IAM. When running kubectl a temporary token is generated by shelling out to the aws-iam-authenticator binary which is sent to authenticate the user. See [aws-iam-authenticator](https://github.com/kubernetes-sigs/aws-iam-authenticator). To replicate that functionality, the `Kubeclient::AmazonEksCredentials` class can accept a set of IAM credentials and contains a helper method to generate the authentication token for you. This requires a set of gems which are _not_ included in `kubeclient` dependencies (`aws-sigv4`) so you should add them to your bundle. You will also require either the `aws-sdk` v2 or `aws-sdk-core` v3 gems to generate the required `Aws:Credentials` object to pass to this method. To obtain a token: ```ruby require 'aws-sdk-core' # Use keys credentials = Aws::Credentials.new(access_key, secret_key) # Or a profile credentials = Aws::SharedCredentials.new(profile_name: 'default').credentials auth_options = { bearer_token: Kubeclient::AmazonEksCredentials.token(credentials, eks_cluster_name) } client = Kubeclient::Client.new( eks_cluster_https_endpoint, 'v1', auth_options: auth_options ) ``` Note that this returns a token good for one minute. If your code requires authorization for longer than that, you should plan to acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section. #### Google GCP credential plugin If kubeconfig file has `user: {auth-provider: {name: gcp, cmd-path: ..., cmd-args: ..., token-key: ...}}`, the command will be executed to obtain a token. (Normally this would be a `gcloud config config-helper` command.) Note that this returns an expiring token. If your code requires authorization for a long time, you should plan to acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section. #### Google's Application Default Credentials On Google Compute Engine, Google App Engine, or Google Cloud Functions, as well as `gcloud`-configured systems with [application default credentials](https://developers.google.com/identity/protocols/application-default-credentials), kubeclient can use `googleauth` gem to authorize. This requires the [`googleauth` gem](https://github.com/google/google-auth-library-ruby) that is _not_ included in `kubeclient` dependencies so you should add it to your bundle. If you use `Config.context(...).auth_options` and the kubeconfig file has `user: {auth-provider: {name: gcp}}`, but does not contain `cmd-path` key, kubeclient will automatically try this (raising LoadError if you don't have `googleauth` in your bundle). Or you can obtain a token manually: ```ruby require 'googleauth' auth_options = { bearer_token: Kubeclient::GoogleApplicationDefaultCredentials.token } client = Kubeclient::Client.new( 'https://localhost:8443/api/', 'v1', auth_options: auth_options ) ``` Note that this returns a token good for one hour. If your code requires authorization for longer than that, you should plan to acquire a new one, see [How to manually renew](#how-to-manually-renew-expired-credentials) section. #### OIDC Auth Provider If the cluster you are using has OIDC authentication enabled you can use the `openid_connect` gem to obtain id-tokens if the one in your kubeconfig has expired. This requires the [`openid_connect` gem](https://github.com/nov/openid_connect) which is not included in the `kubeclient` dependencies so should be added to your own applications bundle. The OIDC Auth Provider will not perform the initial setup of your `$KUBECONFIG` file. You will need to use something like [`dexter`](https://github.com/gini/dexter) in order to configure the auth-provider in your `$KUBECONFIG` file. If you use `Config.context(...).auth_options` and the `$KUBECONFIG` file has user: `{auth-provider: {name: oidc}}`, kubeclient will automatically obtain a token (or use `id-token` if still valid) Tokens are typically short-lived (e.g. 1 hour) and the expiration time is determined by the OIDC Provider (e.g. Google). If your code requires authentication for longer than that you should obtain a new token periodically, see [How to manually renew](#how-to-manually-renew-expired-credentials) section. Note: id-tokens retrieved via this provider are not written back to the `$KUBECONFIG` file as they would be when using `kubectl`. #### How to manually renew expired credentials Kubeclient [does not yet](https://github.com/abonas/kubeclient/issues/393) help with this. The division of labor between `Config` and `Context` objects may change, for now please make no assumptions at which stage `exec:` and `auth-provider:` are handled and whether they're cached. The currently guaranteed way to renew is create a new `Config` object. The more painful part is that you'll then need to create new `Client` object(s) with the credentials from new config. So repeat all of this: ```ruby config = Kubeclient::Config.read(ENV['KUBECONFIG'] || '/path/to/.kube/config') context = config.context ssl_options = context.ssl_options auth_options = context.auth_options client = Kubeclient::Client.new( context.api_endpoint, 'v1', ssl_options: ssl_options, auth_options: auth_options ) # and additional Clients if needed... ``` #### Security: Don't use config from untrusted sources `Config.read` is catastrophically unsafe — it will execute arbitrary command lines specified by the config! `Config.new(data, nil)` is better but Kubeclient was never reviewed for behaving safely with malicious / malformed config. It might crash / misbehave in unexpected ways... #### namespace Additionally, the `config.context` object will contain a `namespace` attribute, if it was defined in the file. It is recommended that you use this namespace when issuing API commands below. This is the same behavior that is implemented by `kubectl` command. You can read it as follows: ```ruby puts config.context.namespace ``` ### Supported kubernetes versions We try to support the last 3 minor versions, matching the [official support policy for Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/release/versioning.md#supported-releases-and-component-skew). Kubernetes 1.2 and below have known issues and are unsupported. Kubernetes 1.3 presumed to still work although nobody is really testing on such old versions... ## Examples: #### Get all instances of a specific entity type Such as: `get_pods`, `get_secrets`, `get_services`, `get_nodes`, `get_replication_controllers`, `get_resource_quotas`, `get_limit_ranges`, `get_persistent_volumes`, `get_persistent_volume_claims`, `get_component_statuses`, `get_service_accounts` ```ruby pods = client.get_pods ``` Get all entities of a specific type in a namespace:
```ruby services = client.get_services(namespace: 'development') ``` You can get entities which have specific labels by specifying a parameter named `label_selector` (named `labelSelector` in Kubernetes server): ```ruby pods = client.get_pods(label_selector: 'name=redis-master') ``` You can specify multiple labels (that option will return entities which have both labels: ```ruby pods = client.get_pods(label_selector: 'name=redis-master,app=redis') ``` You can get entities at a specific version by specifying a parameter named `resource_version` (named `resourceVersion` in Kubernetes server): ```ruby pods = client.get_pods(resource_version: '0') ``` Get all entities of a specific type in chunks: ```ruby continue = nil loop do entities = client.get_pods(limit: 1_000, continue: continue) continue = entities.continue break if entities.last? end ``` See https://kubernetes.io/docs/reference/using-api/api-concepts/#retrieving-large-results-sets-in-chunks for more information. The continue tokens expire after a short amount of time, so similar to a watch if you don't request a subsequent page within aprox. 5 minutes of the previous page being returned the server will return a `410 Gone` error and the client must request the list from the start (i.e. omit the continue token for the next call). Support for chunking was added in v1.9 so previous versions will ignore the option and return the full collection. #### Get a specific instance of an entity (by name) Such as: `get_service "service name"` , `get_pod "pod name"` , `get_replication_controller "rc name"`, `get_secret "secret name"`, `get_resource_quota "resource quota name"`, `get_limit_range "limit range name"` , `get_persistent_volume "persistent volume name"` , `get_persistent_volume_claim "persistent volume claim name"`, `get_component_status "component name"`, `get_service_account "service account name"` The GET request should include the namespace name, except for nodes and namespaces entities. ```ruby node = client.get_node "127.0.0.1" ``` ```ruby service = client.get_service "guestbook", 'development' ``` Note - Kubernetes doesn't work with the uid, but rather with the 'name' property. Querying with uid causes 404. #### Getting raw responses To avoid overhead from parsing and building `RecursiveOpenStruct` objects for each reply, pass the `as: :raw` option when initializing `Kubeclient::Client` or when calling `get_` / `watch_` methods. The result can then be printed, or searched with a regex, or parsed via `JSON.parse(r)`. ```ruby client = Kubeclient::Client.new(as: :raw) ``` or ```ruby pods = client.get_pods as: :raw node = client.get_node "127.0.0.1", as: :raw ``` Other formats are: - `:ros` (default) for `RecursiveOpenStruct` - `:parsed` for `JSON.parse` - `:parsed_symbolized` for `JSON.parse(..., symbolize_names: true)` #### Delete an entity (by name) For example: `delete_pod "pod name"` , `delete_replication_controller "rc name"`, `delete_node "node name"`, `delete_secret "secret name"` Input parameter - name (string) specifying service name, pod name, replication controller name. ```ruby deleted = client.delete_service("redis-service") ``` If you want to cascade delete, for example a deployment, you can use the `delete_options` parameter. ```ruby deployment_name = 'redis-deployment' namespace = 'default' delete_options = Kubeclient::Resource.new( apiVersion: 'meta/v1', gracePeriodSeconds: 0, kind: 'DeleteOptions', propagationPolicy: 'Foreground' # Orphan, Foreground, or Background ) client.delete_deployment(deployment_name, namespace, delete_options: delete_options) ``` #### Create an entity For example: `create_pod pod_object`, `create_replication_controller rc_obj`, `create_secret secret_object`, `create_resource_quota resource_quota_object`, `create_limit_range limit_range_object`, `create_persistent_volume persistent_volume_object`, `create_persistent_volume_claim persistent_volume_claim_object`, `create_service_account service_account_object` Input parameter - object of type `Service`, `Pod`, `ReplicationController`. The below example is for v1 ```ruby service = Kubeclient::Resource.new service.metadata = {} service.metadata.name = "redis-master" service.metadata.namespace = 'staging' service.spec = {} service.spec.ports = [{ 'port' => 6379, 'targetPort' => 'redis-server' }] service.spec.selector = {} service.spec.selector.name = "redis" service.spec.selector.role = "master" service.metadata.labels = {} service.metadata.labels.app = 'redis' service.metadata.labels.role = 'slave' client.create_service(service) ``` #### Update an entity For example: `update_pod`, `update_service`, `update_replication_controller`, `update_secret`, `update_resource_quota`, `update_limit_range`, `update_persistent_volume`, `update_persistent_volume_claim`, `update_service_account` Input parameter - object of type `Pod`, `Service`, `ReplicationController` etc. The below example is for v1 ```ruby updated = client.update_service(service1) ``` #### Patch an entity (by name) For example: `patch_pod`, `patch_service`, `patch_secret`, `patch_resource_quota`, `patch_persistent_volume` Input parameters - name (string) specifying the entity name, patch (hash) to be applied to the resource, optional: namespace name (string) The PATCH request should include the namespace name, except for nodes and namespaces entities. The below example is for v1 ```ruby patched = client.patch_pod("docker-registry", {metadata: {annotations: {key: 'value'}}}, "default") ``` `patch_#{entity}` is called using a [strategic merge patch](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch). `json_patch_#{entity}` and `merge_patch_#{entity}` are also available that use JSON patch and JSON merge patch, respectively. These strategies are useful for resources that do not support strategic merge patch, such as Custom Resources. Consult the [Kubernetes docs](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment) for more information about the different patch strategies. #### Get all entities of all types : all_entities Returns a hash with the following keys (node, secret, service, pod, replication_controller, namespace, resource_quota, limit_range, endpoint, event, persistent_volume, persistent_volume_claim, component_status and service_account). Each key points to an EntityList of same type. This method is a convenience method instead of calling each entity's get method separately. ```ruby client.all_entities ``` #### Receive entity updates It is possible to receive live update notices watching the relevant entities: ```ruby client.watch_pods do |notice| # process notice data end ``` It is possible to interrupt the watcher from another thread with: ```ruby watcher = client.watch_pods watcher.each do |notice| # process notice data end watcher.finish # other thread ``` #### Watch events for a particular object You can use the `field_selector` option as part of the watch methods. ```ruby client.watch_events(namespace: 'development', field_selector: 'involvedObject.name=redis-master') do |notice| # process notice date end ``` #### Get a proxy URL You can get a complete URL for connecting a kubernetes entity via the proxy. ```ruby client.proxy_url('service', 'srvname', 'srvportname', 'ns') # => "https://localhost.localdomain:8443/api/v1/proxy/namespaces/ns/services/srvname:srvportname" ``` Note the third parameter, port, is a port name for services and an integer for pods: ```ruby client.proxy_url('pod', 'podname', 5001, 'ns') # => "https://localhost.localdomain:8443/api/v1/namespaces/ns/pods/podname:5001/proxy" ``` #### Get the logs of a pod You can get the logs of a running pod, specifying the name of the pod and the namespace where the pod is running: ```ruby client.get_pod_log('pod-name', 'default') # => "Running...\nRunning...\nRunning...\n" ``` If that pod has more than one container, you must specify the container: ```ruby client.get_pod_log('pod-name', 'default', container: 'ruby') # => "..." ``` If a container in a pod terminates, a new container is started, and you want to retrieve the logs of the dead container, you can pass in the `:previous` option: ```ruby client.get_pod_log('pod-name', 'default', previous: true) # => "..." ``` Kubernetes can add timestamps to every log line or filter by lines time: ```ruby client.get_pod_log('pod-name', 'default', timestamps: true, since_time: '2018-04-27T18:30:17.480321984Z') # => "..." ``` `since_time` can be a a `Time`, `DateTime` or `String` formatted according to RFC3339 Kubernetes can fetch a specific number of lines from the end of the logs: ```ruby client.get_pod_log('pod-name', 'default', tail_lines: 10) # => "..." ``` Kubernetes can fetch a specific number of bytes from the log, but the exact size is not guaranteed and last line may not be terminated: ```ruby client.get_pod_log('pod-name', 'default', limit_bytes: 10) # => "..." ``` You can also watch the logs of a pod to get a stream of data: ```ruby client.watch_pod_log('pod-name', 'default', container: 'ruby') do |line| puts line end ``` #### Process a template Returns a processed template containing a list of objects to create. Input parameter - template (hash) Besides its metadata, the template should include a list of objects to be processed and a list of parameters to be substituted. Note that for a required parameter that does not provide a generated value, you must supply a value. ##### Note: This functionality is not supported by K8s at this moment. See the following [issue](https://github.com/kubernetes/kubernetes/issues/23896) ```ruby client.process_template template ``` ## Upgrading Kubeclient release versioning follows [SemVer](https://semver.org/). See [CHANGELOG.md](CHANGELOG.md) for full changelog. #### past version 4.0 Old kubernetes versions < 1.3 no longer supported. #### past version 3.0 Ruby versions < 2.2 are no longer supported Specific entity classes mentioned in [past version 1.2.0](#past_version_1.2.0) have been dropped. Return values and expected classes are always Kubeclient::Resource. Checking the type of a resource can be done using: ``` > pod.kind => "Pod" ``` update_* delete_* and patch_* now return a RecursiveOpenStruct like the get_* methods The `Kubeclient::Client` class raises `Kubeclient::HttpError` or subclasses now. Catching `KubeException` still works but is deprecated. `Kubeclient::Config#context` raises `KeyError` instead of `RuntimeError` for non-existent context name. #### past version 1.2.0 Replace Specific Entity class references: ```ruby Kubeclient::Service ``` with the generic ```ruby Kubeclient::Resource.new ``` Where ever possible. ## Contributing 1. Fork it ( https://github.com/[my-github-username]/kubeclient/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Test your changes with `rake test rubocop`, add new tests if needed. 4. If you added a new functionality, add it to README 5. Commit your changes (`git commit -am 'Add some feature'`) 6. Push to the branch (`git push origin my-new-feature`) 7. Create a new Pull Request ## Tests This client is tested with Minitest and also uses VCR recordings in some tests. Please run all tests before submitting a Pull Request, and add new tests for new functionality. Running tests: ```ruby rake test ``` kubeclient-4.6.0/RELEASING.md0000644000004100000410000000345013612310022015521 0ustar www-datawww-data# Releasing Kubeclient ## Versioning Kubeclient release versioning follows [SemVer](https://semver.org/). At some point in time it is decided to release version x.y.z. ```bash RELEASE_BRANCH="master" ``` ## 0. (once) Install gem-release, needed for several commands here: ```bash gem install gem-release ``` ## 1. PR(s) for changelog & bump Edit `CHANGELOG.md` as necessary. Even if all included changes remembered to update it, you should replace "Unreleased" section header with appropriate "x.y.z — 20yy-mm-dd" header. Bump `lib/kubeclient/version.rb` manually, or by using: ```bash git checkout -b release-$RELEASE_VERSION # Won't work with uncommitted changes, you have to commit the changelog first. gem bump --version $RELEASE_VERSION git show # View version bump change. ``` Open a PR with target branch $RELEASE_BRANCH and get it reviewed & merged (if open for long, remember to update date in CHANGELOG to actual day of release). ## 2. (once) Grabbing an authentication token for rubygems.org api ```bash RUBYGEMS_USERNAME=bob curl -u $RUBYGEMS_USERNAME https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials cat ~/.gem/credentials # Should look like this: :rubygems_api_key: **** ``` ## 3. Actual release Make sure we're locally after the bump PR *merge commit*: ```bash git checkout $RELEASE_BRANCH git status # Make sure there are no local changes git pull --ff-only https://github.com/abonas/kubeclient $RELEASE_BRANCH git log -n1 ``` Last sanity check: ```bash bundle install bundle exec rake test rubocop ``` Create and push the tag: ```bash gem tag --no-push git push --tags --dry-run https://github.com/abonas/kubeclient # Check for unexpected tags git push --tags https://github.com/abonas/kubeclient ``` Release onto rubygems.org: ```bash gem release ``` kubeclient-4.6.0/CHANGELOG.md0000644000004100000410000001705413612310022015504 0ustar www-datawww-data# Changelog Notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). Kubeclient release versioning follows [SemVer](https://semver.org/). ## 4.6.0 — 2019-12-30 ### Fixed - AmazonEksCredentials was sometimes leaving base64 padding that IAM auth of the EKS cluster rejects. Now padding is always stripped. (#424, #423) ### Added - Allow calling `watch_foos` methods with a block, simpler to use and guarantees closing the connection. (#425) - Support `limitBytes` query parameter for `get_pod_log`. (#426) ## 4.5.0 — 2019-09-27 ### Added - Support `:resourceVersion` parameter in `get_foos` methods (similar to existing support in `watch_foos` methods). (#420) - Relax dependency on `http` gem to allow both 3.x and 4.x. (#413) ## 4.4.0 — 2019-05-03 ### Added - GCP configs with `user[auth-provider][name] == 'gcp'` will execute credential plugin (normally the `gcloud config config-helper` subcommand) when the config specifies it in `cmd-path`, `cmd-args` fields (similar to `exec` support). This code path works without `googleauth` gem. Otherwise, `GoogleApplicationDefaultCredentials` path will be tried as before. (#410) - `AmazonEksCredentials` helper for obtaining a token to authenticate against Amazon EKS. This is not currently integrated in `Config`, you will need to invoke it yourself. You'll need some aws gems that Kubeclient _does not_ include. (#404, #406) ### Changed - OpenID Connect tokens which cannot be validaded because we cannot identify the key they were signed with will be considered expired and refreshed as usual. (#407) ## 4.3.0 — 2019-03-03 ### Changed - `GoogleApplicationDefaultCredentials` will now automatically be used by `Config` if the `user[auth-provider][name] == 'gcp'` in the provided context. Note that `user[exec]` is checked first in anticipation of this functionality being added to GCP sometime in the future. Kubeclient _does not_ include the required `googleauth` gem, so you will need to include it in your calling application. (#394) ### Added - OpenID Connect credentials will automatically be used if the `user[auth-provider][name] == 'oidc'` in the provided context. Note that `user[exec]` is checked first. Kubeclient _does not_ include the required `openid_connect` gem, so you will need to include it in your calling application. (#396) - Support for `json_patch_#{entity}` and `merge_patch_#{entity}`. `patch_#{entity}` will continue to use strategic merge patch. (#390) ## 4.2.2 — 2019-01-09 ### Added - New `http_max_redirects` option (#374). ### Changed - Default max redirects for watch increased from 4 to 10, to match other verbs (#374). ## 4.2.1 — 2018-12-26 ### Fixed - For resources that contain dashes in name, there will be an attempt to resolve the method name based on singular name prefix or by replacing the dash in names with underscores (#383). ## 4.2.0 — 2018-12-20 ### Added - Support `user: exec: ...` credential plugins like in Go client (#363, #375). ### Security - Really made `Kubeclient::Config.new(data, nil)` prevent external file lookups. (#372) README documented this since 3.1.1 (#334) but alas that was a lie — absolute paths always worked. Now this also prevents credential plugin execution. Even in this mode, using config from untrusted sources is not recommended. This release included all changes up to 4.1.1, but NOT 4.1.2 which was branched off later (4.2.1 does include same fix). ## 4.1.2 — 2018-12-26 ### Fixed - For resources that contain dashes in name, there will be an attempt to resolve the method name based on singular name prefix or by replacing the dash in names with underscores (#382). ## 4.1.1 — 2018-12-17 ### Fixed - Fixed method names for non-suffix plurals such as y -> ies (#377). ## 4.1.0 — 2018-11-28 — REGRESSION This version broke method names where plural is not just adding a suffix, notably y -> ies (bug #376). ### Fixed - Support custom resources with lowercase `kind` (#361). - `create_security_context_constraint` now works (#366). - `get_security_context_constraints.kind`, `get_endpoints.kind` are now plural as in kubernetes (#366). ### Added - Add support for retrieving large lists of objects in chunks (#356). ## 4.0.0 — 2018-07-23 ### Removed - Bumped officially supported kubernetes versions to >= 1.3. - Specifically `proxy_url` no longer works for <= 1.2 (#323). ### Fixed - `proxy_url` now works for kubernetes 1.10 and later (#323). ### Changed - Switched `http` gem dependency from 2.y to 3.y (#321). ## 3.1.2 — 2018-06-11 ### Fixed - Fixed `Kubeclient::Config.read` regression, no longer crashes on YAML timestamps (#338). ## 3.1.1 - 2018-06-01 — REGRESSION In this version `Kubeclient::Config.read` raises Psych::DisallowedClass on legal yaml configs containing a timestamp, for example gcp access-token expiry (bug #337). ### Security - Changed `Kubeclient::Config.read` to use `YAML.safe_load` (#334). Previously, could deserialize arbitrary ruby classes. The risk depends on ruby classes available in the application; sometimes a class may have side effects - up to arbitrary code execution - when instantiated and/or built up with `x[key] = value` during YAML parsing. Despite this fix, using config from untrusted sources is not recommended. ## 3.1.0 - 2018-05-27 ### Fixed - Fixed watch `.finish` sometimes caused `HTTP::ConnectionError` exception from the reading loop (#315). ### Added - `get_pod_log` now has `timestamps`, `since_time` (#319) and `tail_lines` (#326) params. - `Kubeclient::Config::Context#namespace` now set, if present in kubeconfig file (#308). - Improved README directions for authenticating within a kubernetes cluster (#316). - `Kubeclient::GoogleApplicationDefaultCredentials` helper for Google application default credentials (#213). Needs `googleauth` gem. - New `as: :parsed` and `as: :parsed_symbolized` formats (#306). - Allow setting default `as:` format for the whole client (#299, #305). - Relaxed `recursive-open-struct` dependency to allow 1.1+ as well (#313). ## 3.0.0 - 2018-02-04 ### Removed - Dropped entity classes (`Kubeclient::Pod` etc.), only `Kubeclient::Resource` exists now (#292, #288). - Ruby 2.0, 2.1 no longer supported (#253, #291). ### Fixed - Added missing singular `get_security_context_constraint`, fixed `get_security_context_constraints` to mean plural (#261). - Fixed `@http_proxy_uri` undefined warning (#261). - Documentation fixes & improvements (#225, #229, #243, #296). ### Added - `delete_options:` parameter to `delete_*` methods, useful for cascade delete (#267). - `as: :raw` option for watch (#285). - Now raises `Kubeclient::HttpError`. Rescuing `KubeException` still works but is deprecated. (#195, #288) - 404 error raise `Kubeclient::ResourceNotFoundError`, a subclass of `HttpError` (#233). - Include request info in exception message (#221). - Ruby 2.4 and 2.5 are now supported & tested (#247, #295). ### Changed - `Kubeclient::Config#context(nonexistent_context_name)` raises `KeyError` instead of `RuntimeError`. - `update_*`, `delete_*`, `patch_*` now all return `RecursiveOpenStruct` consistently (#290). - Many dependencies bumped (#204, #231, #253, #269). ## 2.5.2 - 2018-02-04 - Watch results are now `RecursiveOpenStruct` inside arrays too (#279). - Fixed watch `.finish` sometimes caused `Errno::EBADF` exception from the reading loop (#280). - Easing dependency version (#287, #301) ## 2.5.1 - 2017-10-12 No changes since 2.5.0, fixed packaging mistake. ## [2.5.0 - 2017-10-12 was YANKED] ### Added - `as: raw` option for `get_*` methods returning a string (#262 via #271). ## 2.4.0 - 2017-05-10 kubeclient-4.6.0/.rubocop.yml0000644000004100000410000000154013612310022016136 0ustar www-datawww-dataAllCops: DisplayCopNames: true TargetRubyVersion: 2.2 # Oldest version kubeclient supports MethodLength: Enabled: false ClassLength: Enabled: false Metrics/AbcSize: Enabled: false Metrics/LineLength: Max: 100 Metrics/ParameterLists: Max: 5 CountKeywordArgs: false Metrics/CyclomaticComplexity: Max: 8 Metrics/PerceivedComplexity: Max: 8 Metrics/ModuleLength: Enabled: false Style/MethodCallWithArgsParentheses: Enabled: true IgnoredMethods: [require, raise, include, attr_reader, refute, assert] Exclude: [Gemfile, Rakefile, kubeclient.gemspec, Gemfile.dev.rb] Metrics/BlockLength: Exclude: [kubeclient.gemspec] Security/MarshalLoad: Exclude: [test/**/*] Style/FileName: Exclude: [Gemfile, Rakefile, Gemfile.dev.rb] Style/MethodCallWithArgsParentheses: IgnoredMethods: - require_relative Style/RegexpLiteral: Enabled: false kubeclient-4.6.0/.gitignore0000644000004100000410000000021613612310022015653 0ustar www-datawww-data/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ *.bundle *.so *.o *.a mkmf.log *.idea* /Gemfile.dev.rb kubeclient-4.6.0/Rakefile0000644000004100000410000000027713612310022015337 0ustar www-datawww-datarequire 'bundler/gem_tasks' require 'rake/testtask' require 'rubocop/rake_task' require 'yaml' task default: %i[test rubocop] # same as .travis.yml Rake::TestTask.new RuboCop::RakeTask.new kubeclient-4.6.0/lib/0000755000004100000410000000000013612310022014432 5ustar www-datawww-datakubeclient-4.6.0/lib/kubeclient.rb0000644000004100000410000000140413612310022017103 0ustar www-datawww-datarequire 'json' require 'rest-client' require 'kubeclient/aws_eks_credentials' require 'kubeclient/common' require 'kubeclient/config' require 'kubeclient/entity_list' require 'kubeclient/exec_credentials' require 'kubeclient/gcp_auth_provider' require 'kubeclient/http_error' require 'kubeclient/missing_kind_compatibility' require 'kubeclient/oidc_auth_provider' require 'kubeclient/resource' require 'kubeclient/resource_not_found_error' require 'kubeclient/version' require 'kubeclient/watch_stream' module Kubeclient # Kubernetes Client class Client include ClientMixin def initialize( uri, version = 'v1', **options ) initialize_client( uri, '/api', version, options ) end end end kubeclient-4.6.0/lib/kubeclient/0000755000004100000410000000000013612310022016557 5ustar www-datawww-datakubeclient-4.6.0/lib/kubeclient/version.rb0000644000004100000410000000011613612310022020567 0ustar www-datawww-data# Kubernetes REST-API Client module Kubeclient VERSION = '4.6.0'.freeze end kubeclient-4.6.0/lib/kubeclient/aws_eks_credentials.rb0000644000004100000410000000322013612310022023112 0ustar www-datawww-data# frozen_string_literal: true module Kubeclient # Get a bearer token to authenticate against aws eks. class AmazonEksCredentials class AmazonEksDependencyError < LoadError # rubocop:disable Lint/InheritException end class << self def token(credentials, eks_cluster) begin require 'aws-sigv4' require 'base64' require 'cgi' rescue LoadError => e raise AmazonEksDependencyError, 'Error requiring aws gems. Kubeclient itself does not include the following ' \ 'gems: [aws-sigv4]. To support auth-provider eks, you must ' \ "include it in your calling application. Failed with: #{e.message}" end # https://github.com/aws/aws-sdk-ruby/pull/1848 # Get a signer # Note - sts only has ONE endpoint (not regional) so 'us-east-1' hardcoding should be OK signer = Aws::Sigv4::Signer.new( service: 'sts', region: 'us-east-1', credentials: credentials ) # https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Sigv4/Signer.html#presign_url-instance_method presigned_url_string = signer.presign_url( http_method: 'GET', url: 'https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15', body: '', credentials: credentials, expires_in: 60, headers: { 'X-K8s-Aws-Id' => eks_cluster } ) kube_token = 'k8s-aws-v1.' + Base64.urlsafe_encode64(presigned_url_string.to_s).sub(/=*$/, '') # rubocop:disable Metrics/LineLength kube_token end end end end kubeclient-4.6.0/lib/kubeclient/google_application_default_credentials.rb0000644000004100000410000000161013612310022027022 0ustar www-datawww-data# frozen_string_literal: true module Kubeclient # Get a bearer token from the Google's application default credentials. class GoogleApplicationDefaultCredentials class GoogleDependencyError < LoadError # rubocop:disable Lint/InheritException end class << self def token begin require 'googleauth' rescue LoadError => e raise GoogleDependencyError, 'Error requiring googleauth gem. Kubeclient itself does not include the ' \ 'googleauth gem. To support auth-provider gcp, you must include it in your ' \ "calling application. Failed with: #{e.message}" end scopes = ['https://www.googleapis.com/auth/cloud-platform'] authorization = Google::Auth.get_application_default(scopes) authorization.apply({}) authorization.access_token end end end end kubeclient-4.6.0/lib/kubeclient/watch_stream.rb0000644000004100000410000000504213612310022021566 0ustar www-datawww-datarequire 'json' require 'http' module Kubeclient module Common # HTTP Stream used to watch changes on entities class WatchStream def initialize(uri, http_options, formatter:) @uri = uri @http_client = nil @http_options = http_options @http_options[:http_max_redirects] ||= Kubeclient::Client::DEFAULT_HTTP_MAX_REDIRECTS @formatter = formatter end def each @finished = false @http_client = build_client response = @http_client.request(:get, @uri, build_client_options) unless response.code < 300 raise Kubeclient::HttpError.new(response.code, response.reason, response) end buffer = '' response.body.each do |chunk| buffer << chunk while (line = buffer.slice!(/.+\n/)) yield @formatter.call(line.chomp) end end rescue StandardError raise unless @finished end def finish @finished = true @http_client.close unless @http_client.nil? end private def max_hops @http_options[:http_max_redirects] + 1 end def follow_option if max_hops > 1 { max_hops: max_hops } else # i.e. Do not follow redirects as we have set http_max_redirects to 0 # Setting `{ max_hops: 1 }` does not work FWIW false end end def build_client client = HTTP::Client.new(follow: follow_option) if @http_options[:basic_auth_user] && @http_options[:basic_auth_password] client = client.basic_auth( user: @http_options[:basic_auth_user], pass: @http_options[:basic_auth_password] ) end client end def using_proxy proxy = @http_options[:http_proxy_uri] return nil unless proxy p_uri = URI.parse(proxy) { proxy_address: p_uri.hostname, proxy_port: p_uri.port, proxy_username: p_uri.user, proxy_password: p_uri.password } end def build_client_options client_options = { headers: @http_options[:headers], proxy: using_proxy } if @http_options[:ssl] client_options[:ssl] = @http_options[:ssl] socket_option = :ssl_socket_class else socket_option = :socket_class end client_options[socket_option] = @http_options[socket_option] if @http_options[socket_option] client_options end end end end kubeclient-4.6.0/lib/kubeclient/exec_credentials.rb0000644000004100000410000000335713612310022022415 0ustar www-datawww-data# frozen_string_literal: true module Kubeclient # An exec-based client auth provide # https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuration # Inspired by https://github.com/kubernetes/client-go/blob/master/plugin/pkg/client/auth/exec/exec.go class ExecCredentials class << self def token(opts) require 'open3' require 'json' raise ArgumentError, 'exec options are required' if opts.nil? cmd = opts['command'] args = opts['args'] env = map_env(opts['env']) # Validate exec options validate_opts(opts) out, err, st = Open3.capture3(env, cmd, *args) raise "exec command failed: #{err}" unless st.success? creds = JSON.parse(out) validate_credentials(opts, creds) creds['status']['token'] end private def validate_opts(opts) raise KeyError, 'exec command is required' unless opts['command'] end def validate_credentials(opts, creds) # out should have ExecCredential structure raise 'invalid credentials' if creds.nil? # Verify apiVersion? api_version = opts['apiVersion'] if api_version && api_version != creds['apiVersion'] raise "exec plugin is configured to use API version #{api_version}, " \ "plugin returned version #{creds['apiVersion']}" end raise 'exec plugin didn\'t return a status field' if creds['status'].nil? raise 'exec plugin didn\'t return a token' if creds['status']['token'].nil? end # Transform name/value pairs to hash def map_env(env) return {} unless env Hash[env.map { |e| [e['name'], e['value']] }] end end end end kubeclient-4.6.0/lib/kubeclient/oidc_auth_provider.rb0000644000004100000410000000361513612310022022762 0ustar www-datawww-data# frozen_string_literal: true module Kubeclient # Uses OIDC id-tokens and refreshes them if they are stale. class OIDCAuthProvider class OpenIDConnectDependencyError < LoadError # rubocop:disable Lint/InheritException end class << self def token(provider_config) begin require 'openid_connect' rescue LoadError => e raise OpenIDConnectDependencyError, 'Error requiring openid_connect gem. Kubeclient itself does not include the ' \ 'openid_connect gem. To support auth-provider oidc, you must include it in your ' \ "calling application. Failed with: #{e.message}" end issuer_url = provider_config['idp-issuer-url'] discovery = OpenIDConnect::Discovery::Provider::Config.discover! issuer_url if provider_config.key? 'id-token' return provider_config['id-token'] unless expired?(provider_config['id-token'], discovery) end client = OpenIDConnect::Client.new( identifier: provider_config['client-id'], secret: provider_config['client-secret'], authorization_endpoint: discovery.authorization_endpoint, token_endpoint: discovery.token_endpoint, userinfo_endpoint: discovery.userinfo_endpoint ) client.refresh_token = provider_config['refresh-token'] client.access_token!.id_token end def expired?(id_token, discovery) decoded_token = OpenIDConnect::ResponseObject::IdToken.decode( id_token, discovery.jwks ) # If token expired or expiring within 60 seconds Time.now.to_i + 60 > decoded_token.exp.to_i rescue JSON::JWK::Set::KidNotFound # Token cannot be verified: the kid it was signed with is not available for discovery # Consider it expired and fetch a new one. true end end end end kubeclient-4.6.0/lib/kubeclient/gcp_command_credentials.rb0000644000004100000410000000131113612310022023724 0ustar www-datawww-data# frozen_string_literal: true module Kubeclient # Generates a bearer token for Google Cloud Platform. class GCPCommandCredentials class << self def token(config) require 'open3' require 'shellwords' require 'json' require 'jsonpath' cmd = config['cmd-path'] args = config['cmd-args'] token_key = config['token-key'] out, err, st = Open3.capture3(cmd, *args.split(' ')) raise "exec command failed: #{err}" unless st.success? extract_token(out, token_key) end private def extract_token(output, token_key) JsonPath.on(output, token_key.gsub(/^{|}$/, '')).first end end end end kubeclient-4.6.0/lib/kubeclient/entity_list.rb0000644000004100000410000000074313612310022021457 0ustar www-datawww-datarequire 'delegate' module Kubeclient module Common # Kubernetes Entity List class EntityList < DelegateClass(Array) attr_reader :continue, :kind, :resourceVersion def initialize(kind, resource_version, list, continue = nil) @kind = kind # rubocop:disable Style/VariableName @resourceVersion = resource_version @continue = continue super(list) end def last? continue.nil? end end end end kubeclient-4.6.0/lib/kubeclient/gcp_auth_provider.rb0000644000004100000410000000077213612310022022616 0ustar www-datawww-data# frozen_string_literal: true require 'kubeclient/google_application_default_credentials' require 'kubeclient/gcp_command_credentials' module Kubeclient # Handle different ways to get a bearer token for Google Cloud Platform. class GCPAuthProvider class << self def token(config) if config.key?('cmd-path') Kubeclient::GCPCommandCredentials.token(config) else Kubeclient::GoogleApplicationDefaultCredentials.token end end end end end kubeclient-4.6.0/lib/kubeclient/http_error.rb0000644000004100000410000000122113612310022021270 0ustar www-datawww-data# TODO: remove this on next major version bump # Deprected http exception class KubeException < StandardError attr_reader :error_code, :message, :response def initialize(error_code, message, response) @error_code = error_code @message = message @response = response end def to_s string = "HTTP status code #{@error_code}, #{@message}" if @response.is_a?(RestClient::Response) && @response.request string << " for #{@response.request.method.upcase} #{@response.request.url}" end string end end module Kubeclient # Exception that is raised when a http request fails class HttpError < KubeException end end kubeclient-4.6.0/lib/kubeclient/resource.rb0000644000004100000410000000041413612310022020732 0ustar www-datawww-datarequire 'recursive_open_struct' module Kubeclient # Represents all the objects returned by Kubeclient class Resource < RecursiveOpenStruct def initialize(hash = nil, args = {}) args[:recurse_over_arrays] = true super(hash, args) end end end kubeclient-4.6.0/lib/kubeclient/common.rb0000644000004100000410000005505013612310022020401 0ustar www-datawww-datarequire 'json' require 'rest-client' module Kubeclient # Common methods # this is mixed in by other gems module ClientMixin ENTITY_METHODS = %w[get watch delete create update patch json_patch merge_patch].freeze DEFAULT_SSL_OPTIONS = { client_cert: nil, client_key: nil, ca_file: nil, cert_store: nil, verify_ssl: OpenSSL::SSL::VERIFY_PEER }.freeze DEFAULT_AUTH_OPTIONS = { username: nil, password: nil, bearer_token: nil, bearer_token_file: nil }.freeze DEFAULT_SOCKET_OPTIONS = { socket_class: nil, ssl_socket_class: nil }.freeze DEFAULT_TIMEOUTS = { # These do NOT affect watch, watching never times out. open: Net::HTTP.new('127.0.0.1').open_timeout, # depends on ruby version read: Net::HTTP.new('127.0.0.1').read_timeout }.freeze DEFAULT_HTTP_PROXY_URI = nil DEFAULT_HTTP_MAX_REDIRECTS = 10 SEARCH_ARGUMENTS = { 'labelSelector' => :label_selector, 'fieldSelector' => :field_selector, 'resourceVersion' => :resource_version, 'limit' => :limit, 'continue' => :continue }.freeze WATCH_ARGUMENTS = { 'labelSelector' => :label_selector, 'fieldSelector' => :field_selector, 'resourceVersion' => :resource_version }.freeze attr_reader :api_endpoint attr_reader :ssl_options attr_reader :auth_options attr_reader :http_proxy_uri attr_reader :http_max_redirects attr_reader :headers attr_reader :discovered def initialize_client( uri, path, version, ssl_options: DEFAULT_SSL_OPTIONS, auth_options: DEFAULT_AUTH_OPTIONS, socket_options: DEFAULT_SOCKET_OPTIONS, timeouts: DEFAULT_TIMEOUTS, http_proxy_uri: DEFAULT_HTTP_PROXY_URI, http_max_redirects: DEFAULT_HTTP_MAX_REDIRECTS, as: :ros ) validate_auth_options(auth_options) handle_uri(uri, path) @entities = {} @discovered = false @api_version = version @headers = {} @ssl_options = ssl_options @auth_options = auth_options @socket_options = socket_options # Allow passing partial timeouts hash, without unspecified # @timeouts[:foo] == nil resulting in infinite timeout. @timeouts = DEFAULT_TIMEOUTS.merge(timeouts) @http_proxy_uri = http_proxy_uri ? http_proxy_uri.to_s : nil @http_max_redirects = http_max_redirects @as = as if auth_options[:bearer_token] bearer_token(@auth_options[:bearer_token]) elsif auth_options[:bearer_token_file] validate_bearer_token_file bearer_token(File.read(@auth_options[:bearer_token_file])) end end def method_missing(method_sym, *args, &block) if discovery_needed?(method_sym) discover send(method_sym, *args, &block) else super end end def respond_to_missing?(method_sym, include_private = false) if discovery_needed?(method_sym) discover respond_to?(method_sym, include_private) else super end end def discovery_needed?(method_sym) !@discovered && ENTITY_METHODS.any? { |x| method_sym.to_s.start_with?(x) } end def handle_exception yield rescue RestClient::Exception => e json_error_msg = begin JSON.parse(e.response || '') || {} rescue JSON::ParserError {} end err_message = json_error_msg['message'] || e.message error_klass = e.http_code == 404 ? ResourceNotFoundError : HttpError raise error_klass.new(e.http_code, err_message, e.response) end def discover load_entities define_entity_methods @discovered = true end def self.parse_definition(kind, name) # Kubernetes gives us 3 inputs: # kind: "ComponentStatus", "NetworkPolicy", "Endpoints" # name: "componentstatuses", "networkpolicies", "endpoints" # singularName: "componentstatus" etc (usually omitted, defaults to kind.downcase) # and want to derive singular and plural method names, with underscores: # "network_policy" # "network_policies" # kind's CamelCase word boundaries determine our placement of underscores. if IRREGULAR_NAMES[kind] # In a few cases, the given kind / singularName itself is still plural. # We require a distinct singular method name, so force it. method_names = IRREGULAR_NAMES[kind] else # TODO: respect singularName from discovery? # But how? If it differs from kind.downcase, kind's word boundaries don't apply. singular_name = kind.downcase if !(/[A-Z]/ =~ kind) # Some custom resources have a fully lowercase kind - can't infer underscores. method_names = [singular_name, name] else # Some plurals are not exact suffixes, e.g. NetworkPolicy -> networkpolicies. # So don't expect full last word to match. /^(?(.*[A-Z]))(?[^A-Z]*)$/ =~ kind # "NetworkP", "olicy" if name.start_with?(prefix.downcase) plural_suffix = name[prefix.length..-1] # "olicies" prefix_underscores = ClientMixin.underscore_entity(prefix) # "network_p" method_names = [prefix_underscores + singular_suffix, # "network_policy" prefix_underscores + plural_suffix] # "network_policies" else method_names = resolve_unconventional_method_names(name, kind, singular_name) end end end OpenStruct.new( entity_type: kind, resource_name: name, method_names: method_names ) end def self.resolve_unconventional_method_names(name, kind, singular_name) underscored_name = name.tr('-', '_') singular_underscores = ClientMixin.underscore_entity(kind) if underscored_name.start_with?(singular_underscores) [singular_underscores, underscored_name] else # fallback to lowercase, no separators for both names [singular_name, underscored_name.tr('_', '')] end end def handle_uri(uri, path) raise ArgumentError, 'Missing uri' unless uri @api_endpoint = (uri.is_a?(URI) ? uri : URI.parse(uri)) @api_endpoint.path = path if @api_endpoint.path.empty? @api_endpoint.path = @api_endpoint.path.chop if @api_endpoint.path.end_with?('/') components = @api_endpoint.path.to_s.split('/') # ["", "api"] or ["", "apis", batch] @api_group = components.length > 2 ? components[2] + '/' : '' end def build_namespace_prefix(namespace) namespace.to_s.empty? ? '' : "namespaces/#{namespace}/" end # rubocop:disable Metrics/BlockLength def define_entity_methods @entities.values.each do |entity| # get all entities of a type e.g. get_nodes, get_pods, etc. define_singleton_method("get_#{entity.method_names[1]}") do |options = {}| get_entities(entity.entity_type, entity.resource_name, options) end # watch all entities of a type e.g. watch_nodes, watch_pods, etc. define_singleton_method("watch_#{entity.method_names[1]}") do |options = {}, &block| # This method used to take resource_version as a param, so # this conversion is to keep backwards compatibility options = { resource_version: options } unless options.is_a?(Hash) watch_entities(entity.resource_name, options, &block) end # get a single entity of a specific type by name define_singleton_method("get_#{entity.method_names[0]}") \ do |name, namespace = nil, opts = {}| get_entity(entity.resource_name, name, namespace, opts) end define_singleton_method("delete_#{entity.method_names[0]}") \ do |name, namespace = nil, opts = {}| delete_entity(entity.resource_name, name, namespace, opts) end define_singleton_method("create_#{entity.method_names[0]}") do |entity_config| create_entity(entity.entity_type, entity.resource_name, entity_config) end define_singleton_method("update_#{entity.method_names[0]}") do |entity_config| update_entity(entity.resource_name, entity_config) end define_singleton_method("patch_#{entity.method_names[0]}") \ do |name, patch, namespace = nil| patch_entity(entity.resource_name, name, patch, 'strategic-merge-patch', namespace) end define_singleton_method("json_patch_#{entity.method_names[0]}") \ do |name, patch, namespace = nil| patch_entity(entity.resource_name, name, patch, 'json-patch', namespace) end define_singleton_method("merge_patch_#{entity.method_names[0]}") \ do |name, patch, namespace = nil| patch_entity(entity.resource_name, name, patch, 'merge-patch', namespace) end end end # rubocop:enable Metrics/BlockLength def self.underscore_entity(entity_name) entity_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase end def create_rest_client(path = nil) path ||= @api_endpoint.path options = { ssl_ca_file: @ssl_options[:ca_file], ssl_cert_store: @ssl_options[:cert_store], verify_ssl: @ssl_options[:verify_ssl], ssl_client_cert: @ssl_options[:client_cert], ssl_client_key: @ssl_options[:client_key], proxy: @http_proxy_uri, max_redirects: @http_max_redirects, user: @auth_options[:username], password: @auth_options[:password], open_timeout: @timeouts[:open], read_timeout: @timeouts[:read] } RestClient::Resource.new(@api_endpoint.merge(path).to_s, options) end def rest_client @rest_client ||= begin create_rest_client("#{@api_endpoint.path}/#{@api_version}") end end # Accepts the following options: # :namespace (string) - the namespace of the entity. # :name (string) - the name of the entity to watch. # :label_selector (string) - a selector to restrict the list of returned objects by labels. # :field_selector (string) - a selector to restrict the list of returned objects by fields. # :resource_version (string) - shows changes that occur after passed version of a resource. # :as (:raw|:ros) - defaults to :ros # :raw - return the raw response body as a string # :ros - return a collection of RecursiveOpenStruct objects # Accepts an optional block, that will be called with each entity, # otherwise returns a WatchStream def watch_entities(resource_name, options = {}, &block) ns = build_namespace_prefix(options[:namespace]) path = "watch/#{ns}#{resource_name}" path += "/#{options[:name]}" if options[:name] uri = @api_endpoint.merge("#{@api_endpoint.path}/#{@api_version}/#{path}") params = {} WATCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] } uri.query = URI.encode_www_form(params) if params.any? watcher = Kubeclient::Common::WatchStream.new( uri, http_options(uri), formatter: ->(value) { format_response(options[:as] || @as, value) } ) return_or_yield_to_watcher(watcher, &block) end # Accepts the following options: # :namespace (string) - the namespace of the entity. # :label_selector (string) - a selector to restrict the list of returned objects by labels. # :field_selector (string) - a selector to restrict the list of returned objects by fields. # :limit (integer) - a maximum number of items to return in each response # :continue (string) - a token used to retrieve the next chunk of entities # :as (:raw|:ros) - defaults to :ros # :raw - return the raw response body as a string # :ros - return a collection of RecursiveOpenStruct objects def get_entities(entity_type, resource_name, options = {}) params = {} SEARCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] } ns_prefix = build_namespace_prefix(options[:namespace]) response = handle_exception do rest_client[ns_prefix + resource_name] .get({ 'params' => params }.merge(@headers)) end format_response(options[:as] || @as, response.body, entity_type) end # Accepts the following options: # :as (:raw|:ros) - defaults to :ros # :raw - return the raw response body as a string # :ros - return a collection of RecursiveOpenStruct objects def get_entity(resource_name, name, namespace = nil, options = {}) ns_prefix = build_namespace_prefix(namespace) response = handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .get(@headers) end format_response(options[:as] || @as, response.body) end # delete_options are passed as a JSON payload in the delete request def delete_entity(resource_name, name, namespace = nil, delete_options: {}) delete_options_hash = delete_options.to_hash ns_prefix = build_namespace_prefix(namespace) payload = delete_options_hash.to_json unless delete_options_hash.empty? response = handle_exception do rs = rest_client[ns_prefix + resource_name + "/#{name}"] RestClient::Request.execute( rs.options.merge( method: :delete, url: rs.url, headers: { 'Content-Type' => 'application/json' }.merge(@headers), payload: payload ) ) end format_response(@as, response.body) end def create_entity(entity_type, resource_name, entity_config) # Duplicate the entity_config to a hash so that when we assign # kind and apiVersion, this does not mutate original entity_config obj. hash = entity_config.to_hash ns_prefix = build_namespace_prefix(hash[:metadata][:namespace]) # TODO: temporary solution to add "kind" and apiVersion to request # until this issue is solved # https://github.com/GoogleCloudPlatform/kubernetes/issues/6439 hash[:kind] = entity_type hash[:apiVersion] = @api_group + @api_version response = handle_exception do rest_client[ns_prefix + resource_name] .post(hash.to_json, { 'Content-Type' => 'application/json' }.merge(@headers)) end format_response(@as, response.body) end def update_entity(resource_name, entity_config) name = entity_config[:metadata][:name] ns_prefix = build_namespace_prefix(entity_config[:metadata][:namespace]) response = handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .put(entity_config.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers)) end format_response(@as, response.body) end def patch_entity(resource_name, name, patch, strategy, namespace) ns_prefix = build_namespace_prefix(namespace) response = handle_exception do rest_client[ns_prefix + resource_name + "/#{name}"] .patch( patch.to_json, { 'Content-Type' => "application/#{strategy}+json" }.merge(@headers) ) end format_response(@as, response.body) end def all_entities(options = {}) discover unless @discovered @entities.values.each_with_object({}) do |entity, result_hash| # method call for get each entities # build hash of entity name to array of the entities method_name = "get_#{entity.method_names[1]}" begin result_hash[entity.method_names[0]] = send(method_name, options) rescue Kubeclient::HttpError next # do not fail due to resources not supporting get end end end def get_pod_log(pod_name, namespace, container: nil, previous: false, timestamps: false, since_time: nil, tail_lines: nil, limit_bytes: nil) params = {} params[:previous] = true if previous params[:container] = container if container params[:timestamps] = timestamps if timestamps params[:sinceTime] = format_datetime(since_time) if since_time params[:tailLines] = tail_lines if tail_lines params[:limitBytes] = limit_bytes if limit_bytes ns = build_namespace_prefix(namespace) handle_exception do rest_client[ns + "pods/#{pod_name}/log"] .get({ 'params' => params }.merge(@headers)) end end def watch_pod_log(pod_name, namespace, container: nil, &block) # Adding the "follow=true" query param tells the Kubernetes API to keep # the connection open and stream updates to the log. params = { follow: true } params[:container] = container if container ns = build_namespace_prefix(namespace) uri = @api_endpoint.dup uri.path += "/#{@api_version}/#{ns}pods/#{pod_name}/log" uri.query = URI.encode_www_form(params) watcher = Kubeclient::Common::WatchStream.new( uri, http_options(uri), formatter: ->(value) { value } ) return_or_yield_to_watcher(watcher, &block) end def proxy_url(kind, name, port, namespace = '') discover unless @discovered entity_name_plural = if %w[services pods nodes].include?(kind.to_s) kind.to_s else @entities[kind.to_s].resource_name end ns_prefix = build_namespace_prefix(namespace) rest_client["#{ns_prefix}#{entity_name_plural}/#{name}:#{port}/proxy"].url end def process_template(template) ns_prefix = build_namespace_prefix(template[:metadata][:namespace]) response = handle_exception do rest_client[ns_prefix + 'processedtemplates'] .post(template.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(@headers)) end JSON.parse(response) end def api_valid? result = api result.is_a?(Hash) && (result['versions'] || []).any? do |group| @api_group.empty? ? group.include?(@api_version) : group['version'] == @api_version end end def api response = handle_exception { create_rest_client.get(@headers) } JSON.parse(response) end private IRREGULAR_NAMES = { # In a few cases, the given kind itself is still plural. # https://github.com/kubernetes/kubernetes/issues/8115 'Endpoints' => %w[endpoint endpoints], 'SecurityContextConstraints' => %w[security_context_constraint security_context_constraints] }.freeze # Format datetime according to RFC3339 def format_datetime(value) case value when DateTime, Time value.strftime('%FT%T.%9N%:z') when String value else raise ArgumentError, "unsupported type '#{value.class}' of time value '#{value}'" end end def format_response(as, body, list_type = nil) case as when :raw body when :parsed JSON.parse(body) when :parsed_symbolized JSON.parse(body, symbolize_names: true) when :ros result = JSON.parse(body) if list_type resource_version = result.fetch('resourceVersion') do result.fetch('metadata', {}).fetch('resourceVersion', nil) end # If 'limit' was passed save the continue token # see https://kubernetes.io/docs/reference/using-api/api-concepts/#retrieving-large-results-sets-in-chunks continue = result.fetch('metadata', {}).fetch('continue', nil) # result['items'] might be nil due to https://github.com/kubernetes/kubernetes/issues/13096 collection = result['items'].to_a.map { |item| Kubeclient::Resource.new(item) } Kubeclient::Common::EntityList.new(list_type, resource_version, collection, continue) else Kubeclient::Resource.new(result) end else raise ArgumentError, "Unsupported format #{as.inspect}" end end def load_entities @entities = {} fetch_entities['resources'].each do |resource| next if resource['name'].include?('/') # Not a regular entity, special functionality covered by `process_template`. # https://github.com/openshift/origin/issues/21668 next if resource['kind'] == 'Template' && resource['name'] == 'processedtemplates' resource['kind'] ||= Kubeclient::Common::MissingKindCompatibility.resource_kind(resource['name']) entity = ClientMixin.parse_definition(resource['kind'], resource['name']) @entities[entity.method_names[0]] = entity if entity end end def fetch_entities JSON.parse(handle_exception { rest_client.get(@headers) }) end def bearer_token(bearer_token) @headers ||= {} @headers[:Authorization] = "Bearer #{bearer_token}" end def validate_auth_options(opts) # maintain backward compatibility: opts[:username] = opts[:user] if opts[:user] if %i[bearer_token bearer_token_file username].count { |key| opts[key] } > 1 raise( ArgumentError, 'Invalid auth options: specify only one of username/password,' \ ' bearer_token or bearer_token_file' ) elsif %i[username password].count { |key| opts[key] } == 1 raise ArgumentError, 'Basic auth requires both username & password' end end def validate_bearer_token_file msg = "Token file #{@auth_options[:bearer_token_file]} does not exist" raise ArgumentError, msg unless File.file?(@auth_options[:bearer_token_file]) msg = "Cannot read token file #{@auth_options[:bearer_token_file]}" raise ArgumentError, msg unless File.readable?(@auth_options[:bearer_token_file]) end def return_or_yield_to_watcher(watcher, &block) return watcher unless block_given? begin watcher.each(&block) ensure watcher.finish end end def http_options(uri) options = { basic_auth_user: @auth_options[:username], basic_auth_password: @auth_options[:password], headers: @headers, http_proxy_uri: @http_proxy_uri, http_max_redirects: http_max_redirects } if uri.scheme == 'https' options[:ssl] = { ca_file: @ssl_options[:ca_file], cert: @ssl_options[:client_cert], cert_store: @ssl_options[:cert_store], key: @ssl_options[:client_key], # ruby HTTP uses verify_mode instead of verify_ssl # http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html verify_mode: @ssl_options[:verify_ssl] } end options.merge(@socket_options) end end end kubeclient-4.6.0/lib/kubeclient/missing_kind_compatibility.rb0000644000004100000410000000652013612310022024516 0ustar www-datawww-datamodule 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' }.freeze def self.resource_kind(name) MAPPING[name] end end end end kubeclient-4.6.0/lib/kubeclient/resource_not_found_error.rb0000644000004100000410000000010613612310022024214 0ustar www-datawww-datamodule Kubeclient class ResourceNotFoundError < HttpError end end kubeclient-4.6.0/lib/kubeclient/config.rb0000644000004100000410000001321213612310022020350 0ustar www-datawww-datarequire 'yaml' require 'base64' require 'pathname' module Kubeclient # Kubernetes client configuration class class Config # Kubernetes client configuration context class class Context attr_reader :api_endpoint, :api_version, :ssl_options, :auth_options, :namespace def initialize(api_endpoint, api_version, ssl_options, auth_options, namespace) @api_endpoint = api_endpoint @api_version = api_version @ssl_options = ssl_options @auth_options = auth_options @namespace = namespace end end # data (Hash) - Parsed kubeconfig data. # kcfg_path (string) - Base directory for resolving relative references to external files. # If set to nil, all external lookups & commands are disabled (even for absolute paths). # See also the more convenient Config.read def initialize(data, kcfg_path) @kcfg = data @kcfg_path = kcfg_path raise 'Unknown kubeconfig version' if @kcfg['apiVersion'] != 'v1' end # Builds Config instance by parsing given file, with lookups relative to file's directory. def self.read(filename) parsed = YAML.safe_load(File.read(filename), [Date, Time]) Config.new(parsed, File.dirname(filename)) end def contexts @kcfg['contexts'].map { |x| x['name'] } end def context(context_name = nil) cluster, user, namespace = fetch_context(context_name || @kcfg['current-context']) ca_cert_data = fetch_cluster_ca_data(cluster) client_cert_data = fetch_user_cert_data(user) client_key_data = fetch_user_key_data(user) auth_options = fetch_user_auth_options(user) ssl_options = {} if !ca_cert_data.nil? cert_store = OpenSSL::X509::Store.new cert_store.add_cert(OpenSSL::X509::Certificate.new(ca_cert_data)) ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER ssl_options[:cert_store] = cert_store else ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE end unless client_cert_data.nil? ssl_options[:client_cert] = OpenSSL::X509::Certificate.new(client_cert_data) end unless client_key_data.nil? ssl_options[:client_key] = OpenSSL::PKey.read(client_key_data) end Context.new(cluster['server'], @kcfg['apiVersion'], ssl_options, auth_options, namespace) end private def allow_external_lookups? @kcfg_path != nil end def ext_file_path(path) unless allow_external_lookups? raise "Kubeclient::Config: external lookups disabled, can't load '#{path}'" end Pathname(path).absolute? ? path : File.join(@kcfg_path, path) end def ext_command_path(path) unless allow_external_lookups? raise "Kubeclient::Config: external lookups disabled, can't execute '#{path}'" end # Like go client https://github.com/kubernetes/kubernetes/pull/59495#discussion_r171138995, # distinguish 3 cases: # - absolute (e.g. /path/to/foo) # - $PATH-based (e.g. curl) # - relative to config file's dir (e.g. ./foo) if Pathname(path).absolute? path elsif File.basename(path) == path path else File.join(@kcfg_path, path) end end def fetch_context(context_name) context = @kcfg['contexts'].detect do |x| break x['context'] if x['name'] == context_name end raise KeyError, "Unknown context #{context_name}" unless context cluster = @kcfg['clusters'].detect do |x| break x['cluster'] if x['name'] == context['cluster'] end raise KeyError, "Unknown cluster #{context['cluster']}" unless cluster user = @kcfg['users'].detect do |x| break x['user'] if x['name'] == context['user'] end || {} namespace = context['namespace'] [cluster, user, namespace] end def fetch_cluster_ca_data(cluster) if cluster.key?('certificate-authority') File.read(ext_file_path(cluster['certificate-authority'])) elsif cluster.key?('certificate-authority-data') Base64.decode64(cluster['certificate-authority-data']) end end def fetch_user_cert_data(user) if user.key?('client-certificate') File.read(ext_file_path(user['client-certificate'])) elsif user.key?('client-certificate-data') Base64.decode64(user['client-certificate-data']) end end def fetch_user_key_data(user) if user.key?('client-key') File.read(ext_file_path(user['client-key'])) elsif user.key?('client-key-data') Base64.decode64(user['client-key-data']) end end def fetch_user_auth_options(user) options = {} if user.key?('token') options[:bearer_token] = user['token'] elsif user.key?('exec') exec_opts = expand_command_option(user['exec'], 'command') options[:bearer_token] = Kubeclient::ExecCredentials.token(exec_opts) elsif user.key?('auth-provider') options[:bearer_token] = fetch_token_from_provider(user['auth-provider']) else %w[username password].each do |attr| options[attr.to_sym] = user[attr] if user.key?(attr) end end options end def fetch_token_from_provider(auth_provider) case auth_provider['name'] when 'gcp' config = expand_command_option(auth_provider['config'], 'cmd-path') Kubeclient::GCPAuthProvider.token(config) when 'oidc' Kubeclient::OIDCAuthProvider.token(auth_provider['config']) end end def expand_command_option(config, key) config = config.dup config[key] = ext_command_path(config[key]) if config[key] config end end end kubeclient-4.6.0/kubeclient.gemspec0000644000004100000410000000303513612310022017357 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'kubeclient/version' Gem::Specification.new do |spec| spec.name = 'kubeclient' spec.version = Kubeclient::VERSION spec.authors = ['Alissa Bonas'] spec.email = ['abonas@redhat.com'] spec.summary = 'A client for Kubernetes REST api' spec.description = 'A client for Kubernetes REST api' spec.homepage = 'https://github.com/abonas/kubeclient' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.2.0' spec.add_development_dependency 'bundler', '>= 1.6' spec.add_development_dependency 'rake', '~> 12.0' spec.add_development_dependency 'minitest' spec.add_development_dependency 'minitest-rg' spec.add_development_dependency 'webmock', '~> 3.0' spec.add_development_dependency 'vcr' spec.add_development_dependency 'rubocop', '= 0.49.1' spec.add_development_dependency 'googleauth', '~> 0.5.1' spec.add_development_dependency('mocha', '~> 1.5') spec.add_development_dependency 'openid_connect', '~> 1.1' spec.add_development_dependency 'jsonpath', '~> 1.0' spec.add_dependency 'rest-client', '~> 2.0' spec.add_dependency 'recursive-open-struct', '~> 1.0', '>= 1.0.4' spec.add_dependency 'http', '>= 3.0', '< 5.0' end kubeclient-4.6.0/Gemfile0000644000004100000410000000032013612310022015152 0ustar www-datawww-datasource 'https://rubygems.org' dev_gemfile = File.expand_path('Gemfile.dev.rb', __dir__) eval_gemfile(dev_gemfile) if File.exist?(dev_gemfile) # Specify your gem's dependencies in kubeclient.gemspec gemspec kubeclient-4.6.0/LICENSE.txt0000644000004100000410000000205513612310022015511 0ustar www-datawww-dataCopyright (c) 2014 Alissa Bonas MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.