k8s-client 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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