k8s-client 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/k8s-client.gemspec CHANGED
@@ -28,9 +28,11 @@ Gem::Specification.new do |spec|
28
28
  spec.add_runtime_dependency "deep_merge", "~> 1.2.1"
29
29
  spec.add_runtime_dependency "recursive-open-struct", "~> 1.1.0"
30
30
  spec.add_runtime_dependency 'hashdiff', '~> 0.3.7'
31
+ spec.add_runtime_dependency 'jsonpath', '~> 0.9.5'
31
32
 
32
33
  spec.add_development_dependency "bundler", "~> 1.16"
33
34
  spec.add_development_dependency "rake", "~> 10.0"
34
35
  spec.add_development_dependency "rspec", "~> 3.7"
35
36
  spec.add_development_dependency "webmock", "~> 3.4.2"
37
+ spec.add_development_dependency "rubocop", "~> 0.59"
36
38
  end
data/lib/k8s-client.rb CHANGED
@@ -1,17 +1,3 @@
1
- # Kubernetes client library
2
- module K8s
3
- require 'k8s/api/metav1'
4
- require 'k8s/api/version'
1
+ # frozen_string_literal: true
5
2
 
6
- require 'k8s/config'
7
- require 'k8s/logging'
8
-
9
- require 'k8s/api_client'
10
- require "k8s/client"
11
- require "k8s/error"
12
- require 'k8s/resource'
13
- require 'k8s/resource_client'
14
- require 'k8s/stack'
15
- require 'k8s/transport'
16
- require 'k8s/util'
17
- end
3
+ require_relative 'k8s/client'
data/lib/k8s/api.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry-types'
2
4
  require 'dry-struct'
3
5
 
@@ -17,7 +19,7 @@ module K8s
17
19
  # @param data [Hash]
18
20
  # @return [self]
19
21
  def self.from_json(data)
20
- return new(data)
22
+ new(data)
21
23
  end
22
24
 
23
25
  # @return [String]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'k8s/api'
2
4
 
3
5
  module K8s
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  module API
3
5
  module MetaV1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  module API
3
5
  module MetaV1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  module API
3
5
  module MetaV1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'k8s/api/metav1/status'
2
4
 
3
5
  module K8s
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  module API
3
5
  module MetaV1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  module API
3
5
  # GET /version
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  # Per-APIGroup/version client.
3
5
  #
@@ -11,7 +13,6 @@ module K8s
11
13
  else
12
14
  File.join('/api', api_version)
13
15
  end
14
-
15
16
  end
16
17
 
17
18
  # @param transport [K8s::Transport]
@@ -22,9 +23,7 @@ module K8s
22
23
  end
23
24
 
24
25
  # @return [String]
25
- def api_version
26
- @api_version
27
- end
26
+ attr_reader :api_version
28
27
 
29
28
  # @param path [Array<String>] join path from parts
30
29
  # @return [String]
@@ -38,17 +37,14 @@ module K8s
38
37
  end
39
38
 
40
39
  # @param api_resources [Array<K8s::API::MetaV1::APIResource>]
41
- def api_resources=(api_resources)
42
- @api_resources = api_resources
43
- end
40
+ attr_writer :api_resources
44
41
 
45
42
  # Force-update APIResources
46
43
  #
47
44
  # @return [Array<K8s::API::MetaV1::APIResource>]
48
45
  def api_resources!
49
- @api_resources = @transport.get(self.path,
50
- response_class: K8s::API::MetaV1::APIResourceList,
51
- ).resources
46
+ @api_resources = @transport.get(path,
47
+ response_class: K8s::API::MetaV1::APIResourceList).resources
52
48
  end
53
49
 
54
50
  # Cached APIResources
@@ -58,18 +54,20 @@ module K8s
58
54
  @api_resources || api_resources!
59
55
  end
60
56
 
57
+ # @param resource_name [String]
58
+ def find_api_resource(resource_name)
59
+ found_resource = api_resources.find{ |api_resource| api_resource.name == resource_name }
60
+ raise K8s::Error::UndefinedResource, "Unknown resource #{resource_name} for #{@api_version}" unless found_resource
61
+
62
+ found_resource
63
+ end
64
+
61
65
  # @param resource_name [String]
62
66
  # @param namespace [String, nil]
63
67
  # @raise [K8s::Error] unknown resource
64
68
  # @return [K8s::ResourceClient]
65
69
  def resource(resource_name, namespace: nil)
66
- unless api_resource = api_resources.find{ |api_resource| api_resource.name == resource_name }
67
- raise K8s::Error::UndefinedResource, "Unknown resource #{resource_name} for #{@api_version}"
68
- end
69
-
70
- ResourceClient.new(@transport, self, api_resource,
71
- namespace: namespace,
72
- )
70
+ ResourceClient.new(@transport, self, find_api_resource(resource_name), namespace: namespace)
73
71
  end
74
72
 
75
73
  # @param resource [K8s::Resource]
@@ -82,13 +80,11 @@ module K8s
82
80
  raise K8s::Error::UndefinedResource, "Invalid apiVersion=#{resource.apiVersion} for #{@api_version} client"
83
81
  end
84
82
 
85
- unless api_resource = api_resources.find{ |api_resource| api_resource.kind == resource.kind }
86
- raise K8s::Error::UndefinedResource, "Unknown resource kind=#{resource.kind} for #{@api_version}"
87
- end
83
+ found_resource = api_resources.find{ |api_resource| api_resource.kind == resource.kind }
84
+ raise K8s::Error::UndefinedResource, "Unknown resource kind=#{resource.kind} for #{@api_version}" unless found_resource
88
85
 
89
- ResourceClient.new(@transport, self, api_resource,
90
- namespace: resource.metadata.namespace || namespace,
91
- )
86
+ ResourceClient.new(@transport, self, found_resource,
87
+ namespace: resource.metadata.namespace || namespace)
92
88
  end
93
89
 
94
90
  # TODO: skip non-namespaced resources if namespace is given, or ignore namespace?
@@ -96,9 +92,10 @@ module K8s
96
92
  # @param namespace [String, nil]
97
93
  # @return [Array<K8s::ResourceClient>]
98
94
  def resources(namespace: nil)
99
- api_resources.map{ |api_resource| ResourceClient.new(@transport, self, api_resource,
100
- namespace: namespace,
101
- ) }
95
+ api_resources.map{ |api_resource|
96
+ ResourceClient.new(@transport, self, api_resource,
97
+ namespace: namespace)
98
+ }
102
99
  end
103
100
 
104
101
  # Pipeline list requests for multiple resource types.
@@ -109,9 +106,9 @@ module K8s
109
106
  # @param options @see [K8s::ResourceClient#list]
110
107
  # @return [Array<K8s::Resource>]
111
108
  def list_resources(resources = nil, **options)
112
- resources ||= self.resources.select{|resource| resource.list? }
109
+ resources ||= self.resources.select(&:list?)
113
110
 
114
- ResourceClient.list(resource, @transport, **options)
111
+ ResourceClient.list(resources, @transport, **options)
115
112
  end
116
113
  end
117
114
  end
data/lib/k8s/client.rb CHANGED
@@ -1,6 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
  require 'base64'
3
5
 
6
+ require 'k8s/api/metav1'
7
+ require 'k8s/api/version'
8
+
9
+ require 'k8s/config'
10
+ require 'k8s/logging'
11
+
12
+ require 'k8s/api_client'
13
+ require "k8s/error"
14
+ require 'k8s/resource'
15
+ require 'k8s/resource_client'
16
+ require 'k8s/stack'
17
+ require 'k8s/transport'
18
+ require 'k8s/util'
19
+
4
20
  module K8s
5
21
  # @param server [String] http/s URL
6
22
  # @param options [Hash] @see Transport.new
@@ -18,8 +34,9 @@ module K8s
18
34
  # @param options [Hash] @see Transport.config
19
35
  # @return [K8s::Client]
20
36
  def self.config(config, namespace: nil, **options)
21
- new(Transport.config(config, **options),
22
- namespace: namespace,
37
+ new(
38
+ Transport.config(config, **options),
39
+ namespace: namespace
23
40
  )
24
41
  end
25
42
 
@@ -40,8 +57,9 @@ module K8s
40
57
  # @raise [K8s::Error]
41
58
  # @return [K8s::API::Version]
42
59
  def version
43
- @transport.get('/version',
44
- response_class: K8s::API::Version,
60
+ @transport.get(
61
+ '/version',
62
+ response_class: K8s::API::Version
45
63
  )
46
64
  end
47
65
 
@@ -56,9 +74,10 @@ module K8s
56
74
  #
57
75
  # @return [Array<String>]
58
76
  def api_groups!
59
- @api_groups = @transport.get('/apis',
60
- response_class: K8s::API::MetaV1::APIGroupList,
61
- ).groups.map{|api_group| api_group.versions.map{|api_version| api_version.groupVersion} }.flatten
77
+ @api_groups = @transport.get(
78
+ '/apis',
79
+ response_class: K8s::API::MetaV1::APIGroupList
80
+ ).groups.map{ |api_group| api_group.versions.map(&:groupVersion) }.flatten
62
81
  end
63
82
 
64
83
  # Cached /apis preferred group apiVersions
@@ -72,14 +91,14 @@ module K8s
72
91
  # @param skip_missing [Boolean] return APIClient without api_resources? if 404
73
92
  # @return [Array<APIClient>]
74
93
  def apis(api_versions = nil, prefetch_resources: false, skip_missing: false)
75
- api_versions ||= ['v1'] + self.api_groups
94
+ api_versions ||= ['v1'] + api_groups
76
95
 
77
96
  if prefetch_resources
78
97
  # api groups that are missing their api_resources
79
98
  api_paths = api_versions
80
- .uniq
81
- .select{|api_version| !api(api_version).api_resources? }
82
- .map{|api_version| APIClient.path(api_version) }
99
+ .uniq
100
+ .reject{ |api_version| api(api_version).api_resources? }
101
+ .map{ |api_version| APIClient.path(api_version) }
83
102
 
84
103
  # load into APIClient.api_resources=
85
104
  @transport.gets(*api_paths, response_class: K8s::API::MetaV1::APIResourceList, skip_missing: skip_missing).each do |api_resource_list|
@@ -87,7 +106,7 @@ module K8s
87
106
  end
88
107
  end
89
108
 
90
- api_versions.map{|api_version| api(api_version) }
109
+ api_versions.map{ |api_version| api(api_version) }
91
110
  end
92
111
 
93
112
  # @param namespace [String, nil]
@@ -104,7 +123,7 @@ module K8s
104
123
  # @param options @see K8s::ResourceClient#list
105
124
  # @return [Array<K8s::Resource>]
106
125
  def list_resources(resources = nil, **options)
107
- resources ||= self.resources.select{|resource| resource.list? }
126
+ resources ||= self.resources.select(&:list?)
108
127
 
109
128
  ResourceClient.list(resources, @transport, **options)
110
129
  end
@@ -137,7 +156,7 @@ module K8s
137
156
  # @return [Array<K8s::Resource, nil>] matching resources array 1:1
138
157
  def get_resources(resources)
139
158
  # prefetch api resources, skip missing APIs
140
- resource_apis = apis(resources.map{ |resource| resource.apiVersion }, prefetch_resources: true, skip_missing: true)
159
+ resource_apis = apis(resources.map(&:apiVersion), prefetch_resources: true, skip_missing: true)
141
160
 
142
161
  # map each resource to excon request options, or nil if resource is not (yet) defined
143
162
  requests = resources.zip(resource_apis).map{ |resource, api_client|
@@ -148,7 +167,7 @@ module K8s
148
167
  {
149
168
  method: 'GET',
150
169
  path: resource_client.path(resource.metadata.name, namespace: resource.metadata.namespace),
151
- response_class: resource_client.resource_class,
170
+ response_class: resource_client.resource_class
152
171
  }
153
172
  }
154
173
 
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module K8s
2
4
  class Client
3
5
  # Updated on releases using semver.
4
- VERSION = "0.4.2"
6
+ VERSION = "0.5.0"
5
7
  end
6
8
  end
data/lib/k8s/config.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry-struct'
2
4
  require 'dry-types'
3
5
  require 'yaml'
@@ -11,7 +13,7 @@ module K8s
11
13
  transform_keys do |key|
12
14
  case key
13
15
  when String
14
- key.gsub('-', '_').to_sym
16
+ key.tr('-', '_').to_sym
15
17
  else
16
18
  key
17
19
  end
@@ -20,7 +22,6 @@ module K8s
20
22
 
21
23
  # @see https://godoc.org/k8s.io/client-go/tools/clientcmd/api/v1#Config
22
24
  class Config < ConfigStruct
23
-
24
25
  # Common dry-types for config
25
26
  class Types
26
27
  include Dry::Types.module
@@ -41,6 +42,12 @@ module K8s
41
42
  attribute :cluster, Cluster
42
43
  end
43
44
 
45
+ # structured user auth provider
46
+ class UserAuthProvider < ConfigStruct
47
+ attribute :name, Types::String
48
+ attribute :config, Types::Strict::Hash
49
+ end
50
+
44
51
  # structured user
45
52
  class User < ConfigStruct
46
53
  attribute :client_certificate, Types::String.optional.default(nil)
@@ -54,7 +61,7 @@ module K8s
54
61
  attribute :as_user_extra, Types::Hash.optional.default(nil)
55
62
  attribute :username, Types::String.optional.default(nil)
56
63
  attribute :password, Types::String.optional.default(nil)
57
- attribute :auth_provider, Types::Strict::Hash.optional.default(nil)
64
+ attribute :auth_provider, UserAuthProvider.optional.default(nil)
58
65
  attribute :exec, Types::Strict::Hash.optional.default(nil)
59
66
  attribute :extensions, Types::Strict::Array.optional.default(nil)
60
67
  end
@@ -93,23 +100,23 @@ module K8s
93
100
  # @param path [String]
94
101
  # @return [K8s::Config]
95
102
  def self.load_file(path)
96
- return new(YAML.load_file(path))
103
+ new(YAML.load_file(path))
97
104
  end
98
105
 
99
106
  # TODO: raise error if not found
100
107
  # @return [K8s::Config::Context]
101
108
  def context(name = current_context)
102
- contexts.find{|context| context.name == name}.context
109
+ contexts.find{ |context| context.name == name }.context
103
110
  end
104
111
 
105
112
  # @return [K8s::Config::Cluster]
106
113
  def cluster(name = context.cluster)
107
- clusters.find{|cluster| cluster.name == name}.cluster
114
+ clusters.find{ |cluster| cluster.name == name }.cluster
108
115
  end
109
116
 
110
117
  # @return [K8s::Config::User]
111
118
  def user(name = context.user)
112
- users.find{|user| user.name == name}.user
119
+ users.find{ |user| user.name == name }.user
113
120
  end
114
121
  end
115
122
  end
data/lib/k8s/error.rb CHANGED
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  module K8s
4
6
  # Top-level class for all errors raised by this gem.
5
7
  class Error < StandardError
6
-
7
8
  # Kube API error, related to a HTTP response with a non-2xx code
8
9
  class API < Error
9
10
  extend Forwardable
@@ -30,34 +31,34 @@ module K8s
30
31
  end
31
32
  end
32
33
 
33
- # specific API error subtypes for HTTP status codes
34
- # Hash{Integer => Class<API>}
35
- HTTP_STATUS_ERRORS = {}
36
-
37
- # define a new API error type on the module for the given HTTP status code
38
- #
39
- # @param code [Integer] HTTP status code
40
- # @param name [Symbol] API error class name
41
- def self.define_status_error(code, name)
42
- HTTP_STATUS_ERRORS[code] = self.const_set(name, Class.new(API))
43
- end
34
+ BadRequest = Class.new(API).freeze
35
+ Unauthorized = Class.new(API).freeze
36
+ Forbidden = Class.new(API).freeze
37
+ NotFound = Class.new(API).freeze
38
+ MethodNotAllowed = Class.new(API).freeze
39
+ Conflict = Class.new(API).freeze # XXX: also AlreadyExists?
40
+ Invalid = Class.new(API).freeze
41
+ Timeout = Class.new(API).freeze
42
+ InternalError = Class.new(API).freeze
43
+ ServiceUnavailable = Class.new(API).freeze
44
+ ServerTimeout = Class.new(API).freeze
44
45
 
45
- define_status_error 400, :BadRequest
46
- define_status_error 401, :Unauthorized
47
- define_status_error 403, :Forbidden
48
- define_status_error 404, :NotFound
49
- define_status_error 405, :MethodNotAllowed
50
- define_status_error 409, :Conflict # XXX: also AlreadyExists?
51
- define_status_error 422, :Invalid
52
- define_status_error 429, :Timeout
53
- define_status_error 500, :InternalError
54
- define_status_error 503, :ServiceUnavailable
55
- define_status_error 504, :ServerTimeout
46
+ HTTP_STATUS_ERRORS = {
47
+ 400 => BadRequest,
48
+ 401 => Unauthorized,
49
+ 403 => Forbidden,
50
+ 404 => NotFound,
51
+ 405 => MethodNotAllowed,
52
+ 409 => Conflict,
53
+ 422 => Invalid,
54
+ 429 => Timeout,
55
+ 500 => InternalError,
56
+ 503 => ServiceUnavailable,
57
+ 504 => ServerTimeout
58
+ }.freeze
56
59
 
57
60
  # Attempt to create a ResourceClient for an unknown resource type.
58
61
  # The client cannot construct the correct API URL without having the APIResource definition.
59
- class UndefinedResource < Error
60
-
61
- end
62
+ UndefinedResource = Class.new(Error)
62
63
  end
63
64
  end