Skip to content

Support kube config #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,10 @@ Please follow the [installation](#installation) procedure and then run the follo
```ruby
# Load the gem
require 'kubernetes'
require 'kubernetes/utils'

# Setup authorization
Kubernetes.configure do |config|
# Configure API key authorization: BearerToken
config.api_key['authorization'] = 'YOUR API KEY'
# Uncomment the following line to set a prefix for the API key, e.g. 'Bearer' (defaults to nil)
#config.api_key_prefix['authorization'] = 'Bearer'
end
# Configs can be set in Configuration class directly or using helper utility
Kubernetes.load_kube_config

api_instance = Kubernetes::AdmissionregistrationApi.new

Expand Down
17 changes: 17 additions & 0 deletions kubernetes/lib/kubernetes/config/error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Kubernetes
class ConfigError < RuntimeError; end
end
70 changes: 70 additions & 0 deletions kubernetes/lib/kubernetes/config/incluster_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'kubernetes/configuration'
require 'kubernetes/config/error'

module Kubernetes

class InClusterConfig

SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST"
SERVICE_PORT_ENV_NAME = "KUBERNETES_SERVICE_PORT"
SERVICE_TOKEN_FILENAME = "/var/run/secrets/kubernetes.io/serviceaccount/token"
SERVICE_CA_CERT_FILENAME = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"

attr_accessor :host
attr_accessor :port
attr_accessor :token

def validate
unless (self.host = self.env[SERVICE_HOST_ENV_NAME]) && (self.port = self.env[SERVICE_PORT_ENV_NAME])
raise ConfigError.new("Service host/port is not set")
end
raise ConfigError.new("Service token file does not exists") unless File.file?(self.token_file)
raise ConfigError.new("Service token file does not exists") unless File.file?(self.ca_cert)
end

def env
@env ||= ENV
@env
end

def ca_cert
@ca_cert ||= SERVICE_CA_CERT_FILENAME
@ca_cert
end

def token_file
@token_file ||= SERVICE_TOKEN_FILENAME
@token_file
end

def load_token
open(self.token_file) do |io|
self.token = io.read.chomp
end
end

def configure(configuration)
validate
load_token
configuration.api_key['authorization'] = "Bearer #{self.token}"
configuration.scheme = 'https'
configuration.host = "#{self.host}:#{self.port}"
configuration.ssl_ca_cert = self.ca_cert
end
end

end
147 changes: 147 additions & 0 deletions kubernetes/lib/kubernetes/config/kube_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'base64'
require 'yaml'
require 'tempfile'
require 'uri'

require 'kubernetes/api_client'
require 'kubernetes/configuration'
require 'kubernetes/config/error'

module Kubernetes

class KubeConfig

KUBE_CONFIG_DEFAULT_LOCATION = File.expand_path('~/.kube/config')

class << self

def list_context_names(config_file=KUBE_CONFIG_DEFAULT_LOCATION)
config = self.new(config_file)
return config.list_context_names
end

end

@@temp_files = {}
attr_accessor :path
attr_writer :config

def initialize(path, config_hash=nil)
@path = path
@config = config_hash
end

def base_path
File.dirname(self.path)
end

def config
@config ||= open(self.path) do |io|
::YAML.load(io.read)
end
end

def configure(configuration, context_name=nil)
context = context_name ? self.find_context(context_name) : self.current_context
user = context['user'] || {}
cluster = context['cluster'] || {}

configuration.tap do |c|
if user['authorization']
c.api_key['authorization'] = user['authorization']
end
if server = cluster['server']
server = URI.parse(server)
c.scheme = server.scheme
host = "#{server.host}:#{server.port}"
host = "#{server.userinfo}@#{host}" if server.userinfo
c.host = host
c.base_path = server.path

if server.scheme == 'https'
c.verify_ssl = cluster['verify_ssl']
c.ssl_ca_cert = cluster['certificate-authority']
c.cert_file = user['client-certificate']
c.key_file = user['client-key']
end
end
end
end

def find_cluster(name)
self.find_by_name(self.config['clusters'], 'cluster', name).tap do |cluster|
create_temp_file_and_set(cluster, 'certificate-authority')
cluster['verify_ssl'] = !cluster['insecure-skip-tls-verify']
end
end

def find_user(name)
self.find_by_name(self.config['users'], 'user', name).tap do |user|
create_temp_file_and_set(user, 'client-certificate')
create_temp_file_and_set(user, 'client-key')
# If tokenFile is specified, then set token
if !user['token'] && user['tokenFile']
open(user['tokenFile']) do |io|
user['token'] = io.read.chomp
end
end
# Convert token field to http header
if user['token']
user['authorization'] = "Bearer #{user['token']}"
elsif user['username'] && user['password']
user_pass = "#{user['username']}:#{user['password']}"
user['authorization'] = "Basic #{Base64.strict_encode64(user_pass)}"
end
end
end

def list_context_names
self.config['contexts'].map { |e| e['name'] }
end

def find_context(name)
self.find_by_name(self.config['contexts'], 'context', name).tap do |context|
context['cluster'] = find_cluster(context['cluster']) if context['cluster']
context['user'] = find_user(context['user']) if context['user']
end
end

def current_context
find_context(self.config['current-context'])
end

protected
def find_by_name(list, key, name)
obj = list.find {|item| item['name'] == name }
raise ConfigError.new("#{key}: #{name} not found") unless obj
obj[key].dup
end

def create_temp_file_and_set(obj, key)
if !obj[key] && obj["#{key}-data"]
obj[key] = create_temp_file_with_base64content(obj["#{key}-data"])
end
end

def create_temp_file_with_base64content(content)
@@temp_files[content] ||= Tempfile.open('kube') do |temp|
temp.write(Base64.strict_decode64(content))
temp.path
end
end
end
end
64 changes: 64 additions & 0 deletions kubernetes/lib/kubernetes/utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'kubernetes/config/incluster_config'
require 'kubernetes/config/kube_config'

module Kubernetes

#
# Use the service account kubernetes gives to pods to connect to kubernetes
# cluster. It's intended for clients that expect to be running inside a pod
# running on kubernetes. It will raise an exception if called from a process
# not running in a kubernetes environment.
def load_incluster_config(client_configuration: Configuration.default)
config = InClusterConfig.new
config.configure(client_configuration)
end

#
# Loads authentication and cluster information from kube-config file
# and stores them in Kubernetes::Configuration.
# @param config_file [String] Path of the kube-config file.
# @param context [String] Set the active context. If is set to nil, current_context from config file will be used.
# @param client_configuration [Kubernetes::Configuration] The Kubernetes::Configuration tp set configs to.
def load_kube_config(
config_file=ENV['KUBECONFIG'],
context: nil,
client_configuration: Configuration.default
)
config_file ||= KubeConfig::KUBE_CONFIG_DEFAULT_LOCATION
config = KubeConfig.new(config_file)
config.configure(client_configuration, context)
end

#
# Loads configuration the same as load_kube_config but returns an ApiClient
# to be used with any API object. This will allow the caller to concurrently
# talk with multiple clusters.
# @param config_file [String] Path of the kube-config file.
# @param context [String] Set the active context. If is set to nil, current_context from config file will be used.
# @return [Kubernetes::ApiClient] Api client for Kubernetes cluster
def new_client_from_config(
config_file=ENV['KUBECONFIG'],
context: nil
)
config_file ||= KubeConfig::KUBE_CONFIG_DEFAULT_LOCATION
client_configuration = Configuration.new
load_kube_config(config_file, context: context, client_configuration: client_configuration)
ApiClient.new(client_configuration)
end

extend self
end
Loading