biosphere 0.2.18 → 0.2.19

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4240a0aae37ea32a3009555e90b6fe696c87b3cd
4
- data.tar.gz: 63d451c4752617971b87f0044f06353e95d14904
3
+ metadata.gz: 26bb5b9c83385f48f0fb3039200337e9a38d9c9f
4
+ data.tar.gz: be31681e9d3442d22e9b6a0f8099e291efff6649
5
5
  SHA512:
6
- metadata.gz: 032af64c9500c6e7b6ef43fa1451512cf400fd196839d1b6b284f8f4d8d8f1e3b1299ae0b650a5ae84afe1ceede8bbebb84803d4d2342c75861a9537f2e633b2
7
- data.tar.gz: bf6187383e49fc4fdc7be8a8c9a511b7dece267a37df6b0c19afe78466423ab1339755554a458affb869ee86d776142f1ac160c1c2f3a50f4cc5bfbc96eb5a3e
6
+ metadata.gz: 27e7d343a95f38e83d3f4c31515f196a9df41c7f767d8e6d51dfdac580eb332958d54e9cf70c4064b334d53b169765bfea8ccff988ca36d45b4d274a2cd6be64
7
+ data.tar.gz: 1c52390455f83cc190afdf9de14e1665416dcac88ea45951c37f98169bde96f6ead943bf740b9ad23a3331c33a699ebaf50e520a38f1d6d6b7088af260e6e821
@@ -89,7 +89,7 @@ if !STDOUT.isatty
89
89
  end
90
90
 
91
91
  update_info = Biosphere::CLI::UpdateManager::check_for_update()
92
- if !update_info[:up_to_date]
92
+ if update_info && !update_info[:up_to_date]
93
93
  STDERR.puts "Notice. There is a new #{update_info[:latest]} biosphere version available. Your current version is #{update_info[:current]}\nUse \"gem install biosphere\" to update".colorize(:yellow)
94
94
  end
95
95
 
@@ -173,22 +173,7 @@ if options.src
173
173
  end
174
174
 
175
175
  unless state.node[:deployments][""].nil?
176
- answer = ""
177
- if !options.force
178
- while answer.empty? || (answer != "y" && answer != "n")
179
- puts "\e[31mDeployment with an empty name, do you want to clean these out now? [y/n]\e[0m"
180
- answer = STDIN.gets.chomp
181
- end
182
-
183
- if answer == "n"
184
- puts "Remember to clean these out later, this state won't work with Kubernetes"
185
- elsif answer == "y"
186
- state.node[:deployments].delete("")
187
- puts "\e[32mRemoved deployment with empty name\e[0m"
188
- end
189
- else
190
- puts "Forcing since --force was set"
191
- end
176
+ puts "State contains deployments with empty names. Remember to clean these out with rename-deployment, this state won't work with Kubernetes".colorize(:red)
192
177
  end
193
178
  end
194
179
 
@@ -426,6 +411,9 @@ elsif ARGV[0] == "destroy" && options.src
426
411
 
427
412
  s3.release_lock()
428
413
 
414
+ elsif ARGV[0] == "rename-deployment"
415
+ Biosphere::CLI::RenameDeployment::renamedeployment(suite, s3, options.build_dir, ARGV[1], ARGV[2], force: options.force)
416
+
429
417
  elsif ARGV[0] == "lock"
430
418
  if localmode
431
419
  STDERR.puts "lock not supported in local mode (set in Settings :biosphere[:local] = true"
@@ -446,4 +434,3 @@ else
446
434
  STDERR.puts "\nERROR: Unknown command #{ARGV[0]}. Maybe you wanted to do: \"biosphere action #{ARGV[0]}\"?"
447
435
  exit -1
448
436
  end
449
-
@@ -13,6 +13,7 @@ require "biosphere/deployment"
13
13
  require "biosphere/terraformproxy"
14
14
  require "biosphere/suite"
15
15
  require "biosphere/cli/terraformplanning"
16
+ require "biosphere/cli/terraformutils"
16
17
  require "biosphere/cli/updatemanager"
18
+ require "biosphere/cli/renamedeployment"
17
19
  require "biosphere/s3"
18
-
@@ -0,0 +1,82 @@
1
+ require 'biosphere'
2
+ require 'pp'
3
+ require "awesome_print"
4
+ require 'colorize'
5
+ require 'biosphere/s3.rb'
6
+ require 'pty'
7
+
8
+ class Biosphere
9
+ class CLI
10
+ class RenameDeployment
11
+ def self.renamedeployment(suite, s3, build_dir, deployment, new_name, terraform: Biosphere::CLI::TerraformUtils.new(), localmode: false, force: false)
12
+
13
+ if !suite.kind_of?(::Biosphere::Suite)
14
+ raise ArgumentError, "RenameDeployment needs a proper suite as the first argument"
15
+ end
16
+
17
+ if !s3.kind_of?(S3)
18
+ raise ArgumentError, "RenameDeployment requires an s3 client as the second argument"
19
+ end
20
+
21
+ localmode = suite.biosphere_settings[:local] || localmode
22
+
23
+ if !deployment
24
+ puts "Please specify deployment name as the second parameter."
25
+ puts "Available deployments:"
26
+ suite.deployments.each do |name, deployment|
27
+ puts "\t#{name}"
28
+ end
29
+ exit(-1)
30
+ end
31
+
32
+ if !new_name
33
+ puts "Please specify the new name as the third parameter."
34
+ exit(-1)
35
+ end
36
+
37
+ if !suite.deployments[deployment]
38
+ puts "Deployment #{deployment} doesn't exist in the current suite".colorize(:red)
39
+ exit(-1)
40
+ elsif suite.deployments[new_name]
41
+ puts "The current suite already contains a deployment called #{new_name}".colorize(:red)
42
+ exit(-1)
43
+ end
44
+
45
+ puts "Renaming #{deployment} to #{new_name}"
46
+
47
+ suite.deployments[deployment].all_resources.each do |r|
48
+ r[:new_name] = r[:name].sub(deployment,new_name)
49
+ end
50
+
51
+ state_file = "#{build_dir}/#{deployment}.tfstate"
52
+ suite.deployments[deployment].all_resources.each do |r|
53
+ terraform.move(state_file, r[:type], r[:name], r[:new_name])
54
+ r[:name] = r[:new_name]
55
+ r.delete(:new_name)
56
+ end
57
+ suite.state.node[:deployments][new_name] = suite.state.node[:deployments].delete(deployment)
58
+
59
+ puts "State renaming done!".colorize(:green)
60
+ puts "Remember to change the deployment name in the deployment specifications too!".colorize(:yellow)
61
+
62
+ count = 0
63
+ suite.write_json_to(build_dir) do |file_name, destination, str, suite_deployment|
64
+ puts "Wrote #{str.length} bytes from #{file_name} to #{destination} (#{suite_deployment.export["resource"].length} resources)"
65
+ count = count + 1
66
+ end
67
+
68
+ puts "Wrote #{count} files under #{build_dir}"
69
+
70
+ unless suite.state.node[:biosphere].nil?
71
+ suite.state.node[:biosphere][:last_build_time] = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
72
+ end
73
+
74
+ FileUtils.mv("#{build_dir}/#{deployment}.tfstate", "#{build_dir}/#{new_name}.tfstate")
75
+ suite.state.save()
76
+ s3.save("#{build_dir}/state.node") unless localmode
77
+ s3.delete_object("#{build_dir}/#{deployment}.tfstate") unless localmode
78
+ s3.save("#{build_dir}/#{new_name}.tfstate") unless localmode
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,34 @@
1
+ require 'biosphere'
2
+ require 'colorize'
3
+
4
+ class Biosphere
5
+ class CLI
6
+ class TerraformUtils
7
+ def initialize()
8
+
9
+ end
10
+
11
+ def move(tfstate_file, resource_type, old_name, new_name)
12
+ if tfstate_file.nil? || resource_type.nil? || old_name.nil? || new_name.nil?
13
+ puts "Can't run terraform mv without a tfstate_file, resource_type, old_resource_name and a new_resource_name"
14
+ puts "tfstate_file: #{tfstate_file}"
15
+ puts "resource_type: #{resource_type}"
16
+ puts "old_name: #{old_name}"
17
+ puts "new_name: #{new_name}"
18
+ exit(-1)
19
+ end
20
+
21
+ begin
22
+ PTY.spawn("terraform state mv -state=#{tfstate_file} #{resource_type}.#{old_name} #{resource_type}.#{new_name}") do |stdout, stdin, pid|
23
+ begin
24
+ stdout.each { |line| puts line }
25
+ rescue Errno::EIO
26
+ end
27
+ end
28
+ rescue PTY::ChildExited
29
+ puts "The child process exited!"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -3,6 +3,8 @@ require 'kubeclient'
3
3
  require 'erb'
4
4
  require 'hashdiff'
5
5
  require 'ostruct'
6
+ require 'jsonpath'
7
+ require 'deep_dup'
6
8
 
7
9
  class String
8
10
  # Converts "CamelCase"" into "camel_case"
@@ -26,6 +28,83 @@ end
26
28
  class Biosphere
27
29
  module Kube
28
30
 
31
+ #
32
+ # Encapsulates a single resource inside kube apiserver (eg. a Deployment or a DaemonSet).
33
+ # A KubeResource comes from a manifest which has one or more resources (yaml allows separating
34
+ # multiple documents with --- inside a single file).
35
+ #
36
+ #
37
+ class KubeResource
38
+ attr_accessor :resource, :document, :source_file, :preserve_current_values
39
+ def initialize(document, source_file)
40
+ @document = document
41
+ @source_file = source_file
42
+ @preserve_current_values = []
43
+ end
44
+
45
+ # Merges the resource with the current resource version in the api server, which
46
+ # has important properties such as:
47
+ # - metadata.selfLink
48
+ # - metadata.uid
49
+ # - metadata.resourceVersion
50
+ #
51
+ # A document can't be updated (PUT verb) unless these are carried over
52
+ #
53
+ def merge_for_put(current)
54
+ new_version = DeepDup.deep_dup(@document)
55
+
56
+ if current["metadata"]
57
+ new_version["metadata"]["selfLink"] = current["metadata"]["selfLink"] if current["metadata"]["selfLink"]
58
+ new_version["metadata"]["uid"] = current["metadata"]["uid"] if current["metadata"]["uid"]
59
+ new_version["metadata"]["resourceVersion"] = current["metadata"]["resourceVersion"] if current["metadata"]["resourceVersion"]
60
+ end
61
+
62
+ if current["spec"]
63
+ new_version["spec"] = {} if !new_version["spec"]
64
+
65
+ # handle spec.clusterIP
66
+ if new_version["spec"]["clusterIP"] && new_version["spec"]["clusterIP"] != current["spec"]["clusterIP"]
67
+ raise ArgumentError, "#{@source_file}: Tried to modify spec.clusterIP from #{current["spec"]["clusterIP"]} to #{new_version["spec"]["clusterIP"]} but the field is immutable"
68
+ end
69
+ new_version["spec"]["clusterIP"] = current["spec"]["clusterIP"] if current["spec"]["clusterIP"]
70
+
71
+ end
72
+
73
+ @preserve_current_values.each do |jsonpath_query|
74
+ jp = JsonPath.new(jsonpath_query)
75
+ current_value = jp.on(current)
76
+ if current_value.length > 1
77
+ raise ArgumentError, "#{@source_file}: A JSONPath query \"#{jsonpath_query}\" matched more than one element: #{current_value}. This is not allowed because it should be used to preserve the current value in the Kubernets API server for the property."
78
+ end
79
+
80
+ new_value = jp.on(new_version)
81
+ if new_value.length > 1
82
+ raise ArgumentError, "#{@source_file}: A JSONPath query \"#{jsonpath_query}\" matched more than one element: #{new_value}. This is not allowed because it should be used to preserve the current value in the Kubernets API server for the property."
83
+ end
84
+
85
+ if current_value.first != new_value.first
86
+ new_version = JsonPath.for(new_version).gsub(jsonpath_query) { |proposed_value| current_value.first }.to_hash
87
+ end
88
+ end
89
+
90
+ return new_version
91
+ end
92
+ end
93
+
94
+ class KubeResourceERBBinding < OpenStruct
95
+ attr_accessor :preserve_current_values
96
+
97
+ def initialize(object)
98
+ @preserve_current_values = []
99
+ super(object)
100
+ end
101
+
102
+ def preserve_current_value(jsonpathquery)
103
+ @preserve_current_values << jsonpathquery
104
+ end
105
+
106
+ end
107
+
29
108
  class Client
30
109
  def initialize(hostname, ssl_options)
31
110
  @clients = []
@@ -48,7 +127,7 @@ class Biosphere
48
127
 
49
128
  def get_resource_name(resource)
50
129
  resource_name = nil
51
- kind = resource[:kind].underscore_case
130
+ kind = resource["kind"].underscore_case
52
131
  @clients.each do |c|
53
132
  if c.instance_variable_get("@entities")[kind]
54
133
  return c.instance_variable_get("@entities")[kind].resource_name
@@ -58,9 +137,9 @@ class Biosphere
58
137
  end
59
138
 
60
139
  def get_client(resource)
61
- kind = resource[:kind].underscore_case
140
+ kind = resource["kind"].underscore_case
62
141
  @clients.each do |c|
63
- if c.instance_variable_get("@api_group") + c.instance_variable_get("@api_version") == resource[:apiVersion]
142
+ if c.instance_variable_get("@api_group") + c.instance_variable_get("@api_version") == resource["apiVersion"]
64
143
  return c
65
144
  end
66
145
  end
@@ -68,7 +147,7 @@ class Biosphere
68
147
  end
69
148
 
70
149
  def post(resource)
71
- name = resource[:metadata][:name]
150
+ name = resource["metadata"]["name"]
72
151
  client = get_client(resource)
73
152
  resource_name = get_resource_name(resource)
74
153
 
@@ -76,9 +155,10 @@ class Biosphere
76
155
  raise ArgumentError, "Unknown resource #{resource[:kind]} of #{name} for kubernetes. Maybe this is in a new extension api?"
77
156
  end
78
157
 
79
- ns_prefix = client.build_namespace_prefix(resource[:metadata][:namespace])
158
+ ns_prefix = client.build_namespace_prefix(resource["metadata"]["namespace"])
159
+ body = JSON.pretty_generate(resource.to_h)
80
160
  begin
81
- ret = client.rest_client[ns_prefix + resource_name].post(resource.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(client.instance_variable_get("@headers")))
161
+ ret = client.rest_client[ns_prefix + resource_name].post(body, { 'Content-Type' => 'application/json' }.merge(client.instance_variable_get("@headers")))
82
162
  rescue RestClient::MethodNotAllowed => e
83
163
  if !resource[:metadata][:namespace]
84
164
  puts "Error doing api call: #{e}".colorize(:red)
@@ -88,7 +168,11 @@ class Biosphere
88
168
  end
89
169
  puts "rest_client: #{ns_prefix + resource_name}, client: #{client.rest_client[ns_prefix + resource_name]}"
90
170
  puts "Dumpin resource request:"
91
- pp resource.to_h.to_json
171
+ pp body
172
+ raise e
173
+
174
+ rescue RestClient::BadRequest => e
175
+ handle_bad_request(client, e, body, ns_prefix, resource_name)
92
176
  raise e
93
177
 
94
178
  rescue RestClient::Exception => e
@@ -101,51 +185,59 @@ class Biosphere
101
185
  return {
102
186
  action: :post,
103
187
  resource: ns_prefix + resource_name + "/#{name}",
104
- body: JSON.parse(ret.body, :symbolize_names => true)
188
+ body: JSON.parse(ret.body)
105
189
  }
106
190
  end
107
191
 
108
192
  def get(resource)
109
- name = resource[:metadata][:name]
193
+ name = resource["metadata"]["name"]
110
194
  client = get_client(resource)
111
195
  resource_name = get_resource_name(resource)
112
196
 
113
197
  if !client
114
- raise ArgumentError, "Unknown resource #{resource[:kind]} of #{name} for kubernetes. Maybe this is in a new extension api?"
198
+ raise ArgumentError, "Unknown resource #{resource["kind"]} of #{name} for kubernetes. Maybe this is in a new extension api?"
115
199
  end
116
200
 
117
- ns_prefix = client.build_namespace_prefix(resource[:metadata][:namespace])
201
+ ns_prefix = client.build_namespace_prefix(resource["metadata"]["namespace"])
118
202
  key = ns_prefix + resource_name + "/#{name}"
119
203
  ret = client.rest_client[key].get(client.instance_variable_get("@headers"))
120
204
  return {
121
205
  action: :get,
122
206
  resource: key,
123
- body: JSON.parse(ret.body, :symbolize_names => true)
207
+ body: JSON.parse(ret.body)
124
208
  }
125
209
  end
126
210
 
127
211
  def put(resource)
128
- name = resource[:metadata][:name]
212
+ name = resource["metadata"]["name"]
129
213
  client = get_client(resource)
130
214
  resource_name = get_resource_name(resource)
131
215
 
132
216
  if !client
133
- raise ArgumentError, "Unknown resource #{resource[:kind]} of #{name} for kubernetes. Maybe this is in a new extension api?"
217
+ raise ArgumentError, "Unknown resource #{resource["kind"]} of #{name} for kubernetes. Maybe this is in a new extension api?"
134
218
  end
135
219
 
136
- ns_prefix = client.build_namespace_prefix(resource[:metadata][:namespace])
220
+ ns_prefix = client.build_namespace_prefix(resource["metadata"]["namespace"])
137
221
  key = ns_prefix + resource_name + "/#{name}"
138
222
  ret = client.rest_client[key].put(resource.to_h.to_json, { 'Content-Type' => 'application/json' }.merge(client.instance_variable_get("@headers")))
139
223
  return {
140
224
  action: :put,
141
225
  resource: key,
142
- body: JSON.parse(ret.body, :symbolize_names => true)
226
+ body: JSON.parse(ret.body)
143
227
  }
144
228
 
145
229
  end
146
230
 
147
- def apply_resource(resource)
148
- name = resource[:metadata][:name]
231
+ # Applies the KubeResource into the api server
232
+ #
233
+ # The update process has the following sequence:
234
+ #
235
+ # 1) Try to fetch the resource to check if the resource is already there
236
+ # 2.1) If a new resource: issue a POST
237
+ # 2.2) If resource exists: merge existing resource with the KubeResource and issue a PUT (update)
238
+ def apply_resource(kuberesource)
239
+ resource = kuberesource.document
240
+ name = resource["metadata"]["name"]
149
241
  responses = []
150
242
  not_found = false
151
243
  begin
@@ -175,21 +267,65 @@ class Biosphere
175
267
  # Get the current full resource from apiserver
176
268
  current_resource = response[:body]
177
269
 
178
- update_resource = Kube.kube_merge_resource_for_put!(current_resource, resource)
270
+ update_resource = kuberesource.merge_for_put(current_resource)
179
271
 
180
272
  begin
181
273
  responses << put(update_resource)
274
+ rescue RestClient::BadRequest => e
275
+ handle_bad_request(client, e, body, ns_prefix, resource_name)
276
+ raise e
182
277
  rescue RestClient::Exception => e
183
278
  puts "Error updating resource: #{e} #{e.class}"
184
279
  pp JSON.parse(e.response)
185
- rescue RestClient::Exception => e
186
- puts "Misc exception: #{e}, #{e.class}, #{e.response}"
187
280
  end
188
281
 
189
282
  return responses
190
283
  end
191
284
  end
192
285
 
286
+ def print_error_location(lines, linenumber)
287
+ start_line = [0, linenumber - 3].max
288
+ end_line = [lines.length - 1, linenumber + 3].min
289
+ lines[start_line..end_line].each_with_index do |line, num|
290
+ num += start_line
291
+ if num == linenumber
292
+ STDERR.printf("%04d> %s\n".red, num, line)
293
+ else
294
+ STDERR.printf("%04d| %s\n", num, line)
295
+ end
296
+ end
297
+ end
298
+
299
+ def handle_bad_request(client, e, body, ns_prefix, resource_name)
300
+ puts "Error calling API (on RestClient::BadRequest rescue): #{e}"
301
+ puts "rest_client: #{ns_prefix + resource_name}, client: #{client.rest_client[ns_prefix + resource_name]}"
302
+
303
+ begin
304
+ msg = JSON.parse(e.http_body)
305
+ if msg["message"]
306
+ m = msg["message"].match(/\[pos ([0-9]+?)\]:\s?(.+)/)
307
+ if m
308
+ error_pos = m[1].to_i
309
+ if error_pos < body.length
310
+ # Find the line number where the error is
311
+ line_number = 0
312
+ for pos in 0..body.length - 1
313
+ if body[pos] == "\n"
314
+ line_number += 1
315
+ end
316
+ if pos >= m[1].to_i
317
+ break
318
+ end
319
+ end
320
+ print_error_location(body.split("\n"), line_number)
321
+ end
322
+ end
323
+ end
324
+ rescue
325
+ puts "Error message from body #{e.http_body}"
326
+ end
327
+ end
328
+
193
329
  end # end of class Client
194
330
 
195
331
  def kube_test(str)
@@ -211,23 +347,45 @@ class Biosphere
211
347
  def self.load_resources(file, context={})
212
348
  resources = []
213
349
  puts "Loading file #{File.absolute_path(file)}"
350
+
351
+ # Let's wrap the context into an OpenStruct based class so that context["foo"]
352
+ # can be accessed as contextstruct.foo
353
+ #
354
+ # This is required for two reasons:
355
+ # 1) The ERB template evaluation requires a binding object so that
356
+ # we can pass variables to the template (from the context object)
357
+ # 2) We want to push some metadata from the ERB template to back here
358
+ # such as the preserve_current_value
359
+ contextstruct = KubeResourceERBBinding.new(context)
360
+
361
+ # Read the file in. A single .yaml can contain multiple documents
214
362
  data = IO.read(file)
363
+
364
+ # First phase: Evaluate ERB templating. We'll use the context to pass
365
+ # all the template variables into the erb.
366
+ # Handle all ERB error reporting with the rescue clauses,
367
+ # so that user gets as good error message as possible.
215
368
  begin
216
- str = ERB.new(data).result(OpenStruct.new(context).instance_eval { binding })
369
+ str = ERB.new(data).result(contextstruct.instance_eval { binding })
217
370
  rescue NoMethodError => e
218
371
  puts "Error evaluating erb templating for #{file}. Error: #{e}"
219
372
  m = /\(erb\):([0-9]+):/.match(e.backtrace.first)
220
373
  if m
221
374
  puts "Error at line #{m[1]}. This is before ERB templating. Remember to run biosphere build if you changed settings."
222
375
  linenumber = m[1].to_i
223
- if linenumber > 0 # Linenumbers seems to be off with 1 as the array is starting at zero
224
- linenumber = linenumber - 1
225
- end
226
376
  lines = data.split("\n")
227
- start_line = [0, linenumber - 3].max
228
- end_line = [lines.length - 1, linenumber + 3].min
377
+ start_line = [0, linenumber - 4].max
378
+ end_line = [lines.length - 1, linenumber + 2].min
379
+
380
+ # Lines is the full .yaml file, one line in each index
381
+ # the [start_line..end_line] slices the subset of the lines
382
+ # which we want to display
229
383
  lines[start_line..end_line].each_with_index do |line, num|
230
- num += start_line
384
+
385
+ # num is the current line number from the subset of the lines
386
+ # We need to add start_line + 1 to it so that it shows
387
+ # the current line number when we print it out
388
+ num += start_line + 1
231
389
  if num == linenumber
232
390
  STDERR.printf("%04d> %s\n".red, num, line)
233
391
  else
@@ -238,10 +396,17 @@ class Biosphere
238
396
  raise e
239
397
  end
240
398
 
399
+ # Second phase: Parse YAML into an array of KubeResource.
400
+ # Again handle error reporting on the rescue clause.
241
401
  begin
242
402
  Psych.load_stream(str) do |document|
243
403
  kind = document["kind"]
244
- resource = ::Kubeclient::Resource.new(document)
404
+ resource = KubeResource.new(document, file)
405
+
406
+ # The preserve_current_values is a metadata field which is used later
407
+ # when merging the updated resource with the current object from apiserver.
408
+ resource.preserve_current_values = contextstruct.preserve_current_values
409
+
245
410
  resources << resource
246
411
  end
247
412
  rescue Psych::SyntaxError => e
@@ -251,14 +416,13 @@ class Biosphere
251
416
  STDERR.puts "Notice that yaml is very picky about indentation when you have arrays and maps. Check those first."
252
417
  lines = str.split("\n")
253
418
  linenumber = e.line
254
- if linenumber > 0 # Linenumbers seems to be off with 1 as the array is starting at zero
255
- linenumber = linenumber - 1
256
- end
257
-
258
- start_line = [0, linenumber - 3].max
259
- end_line = [lines.length - 1, linenumber + 3].min
419
+ start_line = [0, linenumber - 4].max
420
+ end_line = [lines.length - 1, linenumber + 2].min
260
421
  lines[start_line..end_line].each_with_index do |line, num|
261
- num += start_line
422
+ # num is the current line number from the subset of the lines
423
+ # We need to add start_line + 1 to it so that it shows
424
+ # the current line number when we print it out
425
+ num += start_line + 1
262
426
  if num == linenumber
263
427
  STDERR.printf("%04d> %s\n".red, num, line)
264
428
  else
@@ -282,30 +446,5 @@ class Biosphere
282
446
  end
283
447
  end
284
448
 
285
- #
286
- # Applies seleted properties from the current resource (as fetched from apiserver) to the new_version (as read from manifest file)
287
- #
288
- #
289
- def self.kube_merge_resource_for_put!(current, new_version)
290
- if current[:metadata]
291
- new_version[:metadata][:selfLink] = current[:metadata][:selfLink] if current[:metadata][:selfLink]
292
- new_version[:metadata][:uid] = current[:metadata][:uid] if current[:metadata][:uid]
293
- new_version[:metadata][:resourceVersion] = current[:metadata][:resourceVersion] if current[:metadata][:resourceVersion]
294
- end
295
-
296
- if current[:spec]
297
- new_version[:spec] = {} if !new_version[:spec]
298
-
299
- # handle spec.clusterIP
300
- if new_version[:spec][:clusterIP] && new_version[:spec][:clusterIP] != current[:spec][:clusterIP]
301
- raise ArgumentError, "Tried to modify spec.clusterIP from #{current[:spec][:clusterIP]} to #{new_version[:spec][:clusterIP]} but the field is immutable"
302
- end
303
- new_version[:spec][:clusterIP] = current[:spec][:clusterIP] if current[:spec][:clusterIP]
304
-
305
- end
306
- return new_version
307
- end
308
-
309
-
310
449
  end
311
450
  end
@@ -1,3 +1,3 @@
1
1
  class Biosphere
2
- Version = "0.2.18"
2
+ Version = "0.2.19"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: biosphere
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.18
4
+ version: 0.2.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juho Mäkinen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-07 00:00:00.000000000 Z
11
+ date: 2017-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - '='
165
165
  - !ruby/object:Gem::Version
166
166
  version: 2.0.2
167
+ - !ruby/object:Gem::Dependency
168
+ name: jsonpath
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: 0.8.5
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '='
179
+ - !ruby/object:Gem::Version
180
+ version: 0.8.5
167
181
  description: "Terraform's HCL lacks quite many programming features like iterators,
168
182
  true variables, advanced string manipulation, functions etc.\n\n This Ruby tool
169
183
  provides an easy-to-use DSL to define Terraform compatible .json files which can
@@ -177,7 +191,9 @@ files:
177
191
  - bin/biosphere
178
192
  - examples/example.rb
179
193
  - lib/biosphere.rb
194
+ - lib/biosphere/cli/renamedeployment.rb
180
195
  - lib/biosphere/cli/terraformplanning.rb
196
+ - lib/biosphere/cli/terraformutils.rb
181
197
  - lib/biosphere/cli/updatemanager.rb
182
198
  - lib/biosphere/deployment.rb
183
199
  - lib/biosphere/exceptions.rb