diff --git a/test/config/update_certs_k0s.rb b/test/config/update_certs_k0s.rb index 830d8f4e..9487e204 100755 --- a/test/config/update_certs_k0s.rb +++ b/test/config/update_certs_k0s.rb @@ -36,6 +36,12 @@ def sh!(*cmd) sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.crt > test/config/external-cert.pem" sh! "#{DOCKER} exec #{CONTAINER} cat /var/lib/k0s/pki/admin.key > test/config/external-key.rsa" -sh! 'bundle exec rake test' +# Wait for apiserver to be up. To speed startup, this only retries connection errors; +# without `--fail-with-body` curl still returns 0 for well-formed 4xx or 5xx responses. +sleep(1) until sh?('curl --cacert test/config/external-ca.pem --key test/config/external-key.rsa --cert test/config/external-cert.pem https://127.0.0.1:6443/healthz') + +sh! 'env KUBECLIENT_TEST_REAL_CLUSTER=true bundle exec rake test' sh! "#{DOCKER} rm -f #{CONTAINER}" + +puts 'If you run this only for tests, cleanup by running: git restore test/config/' diff --git a/test/test_config.rb b/test/test_config.rb index 6097e35e..98d2114f 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -193,10 +193,6 @@ def check_context(context, ssl: true) 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) diff --git a/test/test_helper.rb b/test/test_helper.rb index 56965813..08ddadb7 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -12,6 +12,14 @@ def open_test_file(name) File.new(File.join(File.dirname(__FILE__), name.split('.').last, name)) end +# kubeconfig files deviate from above convention. +# They link to relaved certs etc. with various extensions, all in same dir. +def config_file(name) + File.join(File.dirname(__FILE__), 'config', name) +end + +WebMock.disable_net_connect! + def stub_core_api_list stub_request(:get, %r{/api/v1$}) .to_return(body: open_test_file('core_api_resource_list.json'), status: 200) diff --git a/test/test_real_cluster.rb b/test/test_real_cluster.rb new file mode 100644 index 00000000..59c9deb0 --- /dev/null +++ b/test/test_real_cluster.rb @@ -0,0 +1,82 @@ +require_relative 'test_helper' + +class KubeclientRealClusterTest < MiniTest::Test + # Tests here actually connect to a cluster! + # For simplicity, these tests use same config/*.kubeconfig files as test_config.rb, + # so are intended to run from config/update_certs_k0s.rb script. + def setup + if ENV['KUBECLIENT_TEST_REAL_CLUSTER'] == 'true' + WebMock.enable_net_connect! + else + skip('Requires real cluster, see test/config/update_certs_k0s.rb.') + end + end + + def teardown + WebMock.disable_net_connect! # Don't allow any connections in other tests. + end + + def test_real_cluster_verify_peer + config = Kubeclient::Config.read(config_file('external.kubeconfig')) + context = config.context + # localhost and 127.0.0.1 are among names on the certificate + client1 = Kubeclient::Client.new( + 'https://127.0.0.1:6443', 'v1', + ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER), + auth_options: context.auth_options + ) + client1.discover + client1.get_nodes + exercise_watcher_with_timeout(client1.watch_nodes) + # 127.0.0.2 also means localhost but is not included in the certificate. + client2 = Kubeclient::Client.new( + 'https://127.0.0.2:6443', 'v1', + ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_PEER), + auth_options: context.auth_options + ) + # TODO: all OpenSSL exceptions should be wrapped with Kubeclient error. + assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do + client2.discover + end + # Since discovery fails, methods like .get_nodes, .watch_nodes would all fail + # on method_missing -> discover. Call lower-level methods to test actual connection. + assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do + client2.get_entities('Node', 'nodes', {}) + end + assert_raises(Kubeclient::HttpError, OpenSSL::SSL::SSLError) do + exercise_watcher_with_timeout(client2.watch_entities('nodes')) + end + end + + def test_real_cluster_verify_none + config = Kubeclient::Config.read(config_file('external.kubeconfig')) + context = config.context + # localhost and 127.0.0.1 are among names on the certificate + client1 = Kubeclient::Client.new( + 'https://127.0.0.1:6443', 'v1', + ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_NONE), + auth_options: context.auth_options + ) + client1.get_nodes + # 127.0.0.2 also means localhost but is not included in the certificate. + client2 = Kubeclient::Client.new( + 'https://127.0.0.2:6443', 'v1', + ssl_options: context.ssl_options.merge(verify_ssl: OpenSSL::SSL::VERIFY_NONE), + auth_options: context.auth_options + ) + client2.get_nodes + end + + private + + def exercise_watcher_with_timeout(watcher) + thread = Thread.new do + sleep(1) + watcher.finish + end + watcher.each do |_notice| + break + end + thread.join + end +end