knife-octo 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cd7bc1743508087e6be632056bceebb3b1d69a6f2a1d899283a9ec094ecd0db8
4
+ data.tar.gz: 81265673f87d32b4dd03725fbb62dd696a79922ee7094bc9b0b44e48d205357c
5
+ SHA512:
6
+ metadata.gz: 4696cecd5c395c10bbf7a0e58ead23413184096991644d5f54015ec0e65d16bb38fcc1036ba3b3d01cbd10f6fab52919786a491b825239d5d770aab3a6de66f4
7
+ data.tar.gz: 771c36c3e295aea4325dfba8bbb252ab9a7e2e101e70f5e5a7652a16ffff49ca645155793d34ecdf961a462bff8351994ef3264c720d96cb28076e36aad0c221
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # knife-octo
2
+ Chef Knife plugin for Octopus Deploy
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.push File.expand_path("lib", __dir__)
2
+ require "knife-octo/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "knife-octo"
6
+ s.version = Knife::Octo::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = "Vishal Basra"
9
+ s.email = "vishalbasra@live.com"
10
+ s.homepage = "https://github.com/vishalbasra/knife-octo"
11
+ s.summary = "Chef Knife plugin to help see stuff in Octopus Deploy"
12
+ s.description = "A knife plugin to things much faster in Octopus Deploy server."
13
+ s.date = '2018-10-16'
14
+ s.license = "Apache-2.0"
15
+ s.files = `git ls-files`.split("\n")
16
+ s.require_paths = ["lib"]
17
+ s.add_development_dependency "colorize", "~> 0.7.7"
18
+
19
+ end
@@ -0,0 +1,1139 @@
1
+ # chef gem install colorize
2
+ module Octo
3
+ class OctoHelperMethods < Chef::Knife
4
+ require 'json'
5
+ require 'uri'
6
+ require 'net/http'
7
+ require 'openssl'
8
+ require 'colorize'
9
+
10
+
11
+ def initialize(apikey, instance)
12
+ @apikey = apikey
13
+ @instance = instance
14
+ end
15
+
16
+ ## generic helper methods
17
+
18
+ def generic_call(method, resource, params)
19
+ instance = @instance
20
+ apikey = @apikey
21
+ output = {
22
+ 'response' => 'error',
23
+ 'message' => false
24
+ }
25
+ # Format URI object
26
+ uri = "https://#{instance}/api/#{resource}"
27
+ if params
28
+ # logic to do params here for a PUSH / POST, currently not supported
29
+ end
30
+ uri = URI.parse(uri)
31
+
32
+ # Format HTTPS request object
33
+ header = { 'X-Octopus-ApiKey' => apikey }
34
+ # Create HTTP object
35
+ http = Net::HTTP.new(uri.host, uri.port)
36
+ http.use_ssl = true
37
+ if method == 'GET'
38
+ # put try catch here
39
+ req = Net::HTTP::Get.new(uri.request_uri, header)
40
+ else
41
+ # put try catch here
42
+ req = Net::HTTP::Post.new(uri.request_uri, header)
43
+ end
44
+
45
+ # Execute HTTP request
46
+ begin
47
+ result = http.request(req)
48
+ rescue => exception
49
+ print "#{'ERROR'.red} : Could not send request to #{'https://'.red}#{instance.red} - #{exception}\n"
50
+ exit
51
+ else
52
+ "" # had an older colorize block here
53
+ end
54
+
55
+
56
+ ## Validate result
57
+ case result
58
+ when Net::HTTPOK
59
+ output['response'] = 'ok'
60
+ result_hash = {}
61
+ begin
62
+ result_hash = JSON.parse("#{result.body.force_encoding('UTF-8')}")
63
+ rescue
64
+ result_hash = {
65
+ 'HTTP success type' => "#{result.class.name}"
66
+ }
67
+ if result.body
68
+ result_hash['body'] = "#{result.body}"
69
+ end
70
+ if result.message
71
+ result_hash['message'] = "#{result.message}"
72
+ end
73
+ if result['location']
74
+ result_hash['location'] = "#{result['location']}"
75
+ end
76
+ end
77
+ output['message'] = result_hash
78
+ else
79
+ output['response'] = 'notok'
80
+ output['message'] = "#{'ERROR - '.red} HTTP Response: #{result.class.name}"
81
+ if result.message
82
+ output['message'] += " - Message: #{result.message}"
83
+ end
84
+ if result.body
85
+ output['message'] += "\nCaused by: #{JSON.parse(result.body)['ErrorMessage'].red}\n"
86
+ end
87
+ end
88
+ return output
89
+ end
90
+
91
+ def envname_from_id(env_id)
92
+ output = {
93
+ 'response' => 'error',
94
+ 'message' => false
95
+ }
96
+ request = generic_call('GET', "environments/#{env_id}", false)
97
+ if request['response'] == 'ok'
98
+ output['response'] = 'ok'
99
+ output['message'] = request['message']['Name']
100
+ else
101
+ output['response'] = request['response']
102
+ output ['message'] = request['message']
103
+ end
104
+ return output
105
+ end
106
+
107
+ def eid_from_name(env_name)
108
+ output = {
109
+ 'response' => 'error',
110
+ 'message' => false
111
+ }
112
+ request = generic_call('GET', 'environments/all', false)
113
+ if request['response'] == 'ok'
114
+ parsed_list = parse_env_list(request['message'],true)
115
+ JSON.parse(parsed_list).each do |some_env|
116
+ if some_env['name'] == env_name
117
+ output['response'] = 'ok'
118
+ output ['message'] = some_env['properties']['id']
119
+ end
120
+ end
121
+ if output['message'].class.to_s == 'FalseClass'
122
+ output['response'] = 'notok'
123
+ output ['message'] = 'Environment name not found'
124
+ end
125
+ else
126
+ output['response'] = request['response']
127
+ output ['message'] = request['message']
128
+ end
129
+ return output
130
+ end
131
+
132
+ def mid_from_mname(m_name)
133
+ output = {
134
+ 'response' => 'error',
135
+ 'message' => false
136
+ }
137
+ request = generic_call('GET', 'machines/all', false)
138
+ if request['response'] == 'ok'
139
+ parsed_list = parse_machine_list(request['message'],true)
140
+ JSON.parse(parsed_list).each do |some_machine|
141
+ if some_machine['name'] == m_name
142
+ output['response'] = 'ok'
143
+ output ['message'] = some_machine['properties']['id']
144
+ end
145
+ end
146
+ if output['message'].class.to_s == 'FalseClass'
147
+ output['response'] = 'notok'
148
+ output ['message'] = 'Machine name not found'
149
+ end
150
+ else
151
+ output['response'] = request['response']
152
+ output ['message'] = request['message']
153
+ end
154
+ return output
155
+ end
156
+
157
+ def create_local_file_by_id(thing)
158
+ tmp_location = "/tmp/ajnfieneom"
159
+ request = generic_call('GET', "#{thing}/all", false)
160
+ things = []
161
+ request['message'].each do |single|
162
+ things << {
163
+ "#{single['Id']}" => "#{single['Name']}"
164
+ }
165
+ end
166
+ if request['response'] == 'ok'
167
+ file = File.open("#{tmp_location}",'w+')
168
+ file.write(JSON.pretty_generate(things))
169
+ end
170
+ file.read
171
+ end
172
+
173
+ def create_local_file_by_name(thing)
174
+ tmp_location = "/tmp/ajnfieneoma"
175
+ request = generic_call('GET', "#{thing}/all", false)
176
+ things = []
177
+ request['message'].each do |single|
178
+ things << {
179
+ "#{single['Name']}" => "#{single['Id']}"
180
+ }
181
+ end
182
+ if request['response'] == 'ok'
183
+ file = File.open("#{tmp_location}",'w+')
184
+ file.write(JSON.pretty_generate(things))
185
+ file.close
186
+
187
+ end
188
+ end
189
+
190
+ def local_file_by_id(e_id,e_name) # couple with create_local_file_by_id ; e_name will be used in a future release
191
+ tmp_location = "/tmp/ajnfieneom"
192
+ file = File.read("#{tmp_location}")
193
+ json_envs = JSON.parse(file)
194
+ json_envs.each do |json_env|
195
+ unless json_env["#{e_id}"].nil?
196
+ return json_env["#{e_id}"]
197
+ end
198
+ end
199
+ end
200
+
201
+ def local_file_by_key(key,value) # couple with create_local_file_by_name ; e_name will be used in a future release
202
+ tmp_location = "/tmp/ajnfieneoma"
203
+ file = File.read("#{tmp_location}")
204
+ json_things = JSON.parse(file)
205
+ json_things.each do |json_thing|
206
+ unless json_thing["#{key}"].nil?
207
+ return json_thing["#{key}"]
208
+ end
209
+ end
210
+ end
211
+
212
+ def delete_local_file
213
+ tmp_location = "/tmp/ajnfieneom"
214
+ tmp_location_another = "/tmp/ajnfieneoma"
215
+ if File.file?(tmp_location)
216
+ File.delete(tmp_location)
217
+ end
218
+ if File.file?(tmp_location_another)
219
+ File.delete(tmp_location_another)
220
+ end
221
+ end
222
+
223
+
224
+ ## specific helper methods
225
+
226
+ def parse_variable_list (var_list_object,islong)
227
+ instance = @instance
228
+ variable_sets = []
229
+ var_list_object.each do |item|
230
+ if item['ContentType'] == 'Variables'
231
+ if islong.class.to_s == 'TrueClass'
232
+ variable_sets << {
233
+ name: item['Name'],
234
+ properties: {
235
+ description: item['Description'],
236
+ id: item['Id'],
237
+ link: "http://#{instance}/app#/library/variables/#{item['Id']}"
238
+ }
239
+ }
240
+ else
241
+ variable_sets << item['Name']
242
+ end
243
+ end
244
+ end
245
+ return JSON.pretty_generate(variable_sets)
246
+ end
247
+
248
+ def parse_variable_sets_helper (var_obj,islong)
249
+ variable_sets = []
250
+ var_obj['Variables'].each do |item|
251
+ if islong.class.to_s == 'TrueClass' # long
252
+ variable_sets << {
253
+ 'name' => item['Name'],
254
+ 'value' => item['Value'],
255
+ 'isencrypted' => item['IsSensitive'],
256
+ 'type' => item['Type'],
257
+ 'iseditable' => item['IsEditable'],
258
+ 'scope' => item['Scope']['Environment'] # alter this here if we ever use Roles
259
+ }
260
+ else # short
261
+ variable_sets.push(item['Name'])
262
+ end
263
+ end
264
+
265
+ ##
266
+ #DO env things here
267
+ if islong.class.to_s == 'TrueClass' # long substitue
268
+ # NEW CREATE ENV FILE
269
+ create_local_file_by_id('environments')
270
+ variable_sets.each do |single_var|
271
+ new_envs = []
272
+ unless single_var['scope'].nil?
273
+ single_var['scope'].each do |env_id|
274
+ new_envs.push(local_file_by_id(env_id,false))
275
+ end
276
+ end
277
+ # SECRET
278
+ single_var['scope'] = new_envs
279
+ if single_var['isencrypted'].class.to_s == 'TrueClass'
280
+ single_var['value'] = '************'
281
+ end
282
+ end
283
+ end
284
+
285
+ return JSON.pretty_generate(variable_sets)
286
+ end
287
+
288
+ def parse_variable_sets (var_name,islong)
289
+ output = {
290
+ 'response' => 'error',
291
+ 'message' => false
292
+ }
293
+ create_local_file_by_name('libraryvariablesets')
294
+ unless local_file_by_key(var_name,false).nil?
295
+ var_id = local_file_by_key(var_name,false)
296
+ case var_id
297
+ when String
298
+ ""
299
+ else
300
+ output['response'] = 'notok'
301
+ output['message'] = "Incorrect Variable Set provided"
302
+ return output
303
+ end
304
+
305
+ request = generic_call('GET', "/variables/variableset-#{var_id}", false)
306
+ if request['response'] == 'ok'
307
+ output['response'] = 'ok'
308
+ output['message'] = parse_variable_sets_helper(request['message'],islong)
309
+ else
310
+ output['response'] = 'notok'
311
+ output['message'] = request['message']
312
+ end
313
+ end
314
+ #end
315
+ if output['response'].class.to_s == 'FalseClass'
316
+ output['response'] = 'notok'
317
+ output['message'] = "Incorrect Variable Set provided"
318
+ end
319
+ delete_local_file
320
+ return output
321
+
322
+
323
+
324
+ end
325
+
326
+ def parse_env_list (env_list_object,islong)
327
+ variable_sets = []
328
+ env_list_object.each do |item|
329
+ if islong.class.to_s == 'TrueClass'
330
+ variable_sets << {
331
+ 'name' => item['Name'],
332
+ 'properties' => {
333
+ 'description'=> item['Description'],
334
+ 'id'=> item['Id']
335
+ }
336
+ }
337
+ else
338
+ variable_sets << item['Name']
339
+ end
340
+ end
341
+ return JSON.pretty_generate(variable_sets)
342
+ end
343
+
344
+ def parse_env_deets(env_object,islong)
345
+ output = {
346
+ 'response' => 'error',
347
+ 'message' => false
348
+ }
349
+ variable_sets = []
350
+ env_object['Items'].each do |item|
351
+ if islong.class.to_s == 'TrueClass'
352
+ variable_sets << {
353
+ 'name' => item['Name'],
354
+ 'properties' => {
355
+ 'description' => item['Description'],
356
+ 'id' => item['Id'],
357
+ 'uri' => item['Endpoint']['Uri'],
358
+ 'thumbprint' => item['Thumbprint'],
359
+ 'isdisabled' => item['IsDisabled'],
360
+ 'health' => item['HealthStatus'],
361
+ 'status' => item['Status'],
362
+ 'summary' => item['StatusSummary'],
363
+ 'isinprocess' => item['IsInProcess'],
364
+ 'roles' => item['Roles'],
365
+ 'environments' => item['EnvironmentIds']
366
+ }
367
+ }
368
+ else
369
+ variable_sets << item['Name']
370
+ end
371
+ end
372
+ if islong.class.to_s == 'TrueClass'
373
+ # TRANSPOSE ENVS
374
+ variable_sets.each do |single_var|
375
+ new_envs = []
376
+ unless single_var['properties']['environments'].nil?
377
+ single_var['properties']['environments'].each do |env_id|
378
+ new_envs.push(local_file_by_id(env_id,false))
379
+ end
380
+ end
381
+ single_var['properties']['environments'] = new_envs
382
+ end
383
+ # END OF ENV TRANSPOSING
384
+ end
385
+ JSON.pretty_generate(variable_sets)
386
+ end
387
+
388
+ def parse_machine_list (m_list_object,islong)
389
+ variable_sets = []
390
+ m_list_object.each do |item|
391
+ if islong.class.to_s == 'TrueClass'
392
+ variable_sets << {
393
+ 'name' => item['Name'],
394
+ 'properties' => {
395
+ 'health'=> item['HealthStatus'],
396
+ 'id'=> item['Id']
397
+ }
398
+ }
399
+ else
400
+ variable_sets << item['Name']
401
+ end
402
+ end
403
+ return JSON.pretty_generate(variable_sets)
404
+ end
405
+
406
+ def parse_machine_deets (m_object,islong)
407
+ output = {
408
+ 'response' => 'error',
409
+ 'message' => false
410
+ }
411
+ variable_sets = []
412
+ if islong.class.to_s == 'TrueClass'
413
+ variable_sets << {
414
+ 'name' => m_object['Name'],
415
+ 'properties' => {
416
+ 'id' => m_object['Id'],
417
+ 'uri' => m_object['Endpoint']['Uri'],
418
+ 'thumbprint' => m_object['Thumbprint'],
419
+ 'isdisabled' => m_object['IsDisabled'],
420
+ 'health' => m_object['HealthStatus'],
421
+ 'status' => m_object['Status'],
422
+ 'summary' => m_object['StatusSummary'],
423
+ 'isinprocess' => m_object['IsInProcess'],
424
+ 'roles' => m_object['Roles'],
425
+ 'environments' => m_object['EnvironmentIds']
426
+ }
427
+ }
428
+ else
429
+ variable_sets << {
430
+ 'name' => m_object['Name'],
431
+ 'uri' => m_object['Endpoint']['Uri'],
432
+ }
433
+ end
434
+ if islong.class.to_s == 'TrueClass'
435
+ variable_sets.each do |single_hash|
436
+ new_environments = []
437
+ single_hash['properties']['environments'].each do |env_id|
438
+ request = envname_from_id(env_id)
439
+ if request['response'] == 'ok'
440
+ new_environments.push(request['message'])
441
+ end
442
+ end
443
+ single_hash['properties']['environments'] = new_environments
444
+ end
445
+ end
446
+ JSON.pretty_generate(variable_sets)
447
+ end
448
+
449
+ def parse_project_list (p_list_object,islong,manyitems)
450
+ output = {
451
+ 'response' => 'error',
452
+ 'message' => false
453
+ }
454
+ instance = @instance
455
+ variable_sets = []
456
+ if manyitems.class.to_s == 'TrueClass' # it's a list
457
+ p_list_object.each do |item|
458
+ if islong.class.to_s == 'TrueClass'
459
+ variable_sets << {
460
+ 'name' => item['Name'],
461
+ 'properties' => {
462
+ 'id'=> item['Id'],
463
+ 'deploymentid'=> item['DeploymentProcessId'],
464
+ 'variableid'=> item['VariableSetId'],
465
+ 'variablesets'=> item['IncludedLibraryVariableSetIds'],
466
+ 'url' => "http://#{instance}#{item['Links']['Web']}"
467
+ }
468
+ }
469
+ else
470
+ variable_sets << {
471
+ 'name' => item['Name'],
472
+ 'url' => "http://#{instance}#{item['Links']['Web']}"
473
+ }
474
+ end
475
+ end
476
+ else # it is not a list and is a single item
477
+ if islong.class.to_s == 'TrueClass'
478
+ ## steps
479
+ steps = generic_call('GET', "deploymentprocesses/#{p_list_object['DeploymentProcessId']}", false)
480
+ if steps['response'] == 'ok'
481
+ process = []
482
+ steps['message']['Steps'].each do |step|
483
+ new_envs = []
484
+ step['Actions'][0]['Environments'].each do |envy_id|
485
+ new_envs.push(local_file_by_id(envy_id,false))
486
+ end
487
+
488
+ excl_envs = []
489
+ step['Actions'][0]['ExcludedEnvironments'].each do |excl_env_id|
490
+ excl_envs.push(local_file_by_id(excl_env_id,false))
491
+ end
492
+
493
+ process << { # not using Id here but it can be used
494
+ "name" => step['Name'],
495
+ "packages_required" => step['RequiresPackagesToBeAcquired'],
496
+ "roles" => step['Properties']['Octopus.Action.TargetRoles'],
497
+ "condition" => step['Condition'],
498
+ "start_trigger" => step['StartTrigger'],
499
+ "environments" => new_envs,
500
+ "excluded_environments" => excl_envs,
501
+ "isdisabled" => step['Actions'][0]['IsDisabled'],
502
+ "details" => step['Actions'][0]['Properties'],
503
+ }
504
+ end
505
+ end
506
+ ## steps end
507
+
508
+ variable_sets << {
509
+ 'name' => p_list_object['Name'],
510
+ 'properties' => {
511
+ 'id'=> p_list_object['Id'],
512
+ 'deploymentid'=> p_list_object['DeploymentProcessId'],
513
+ 'variableid'=> p_list_object['VariableSetId'],
514
+ 'variablesets'=> p_list_object['IncludedLibraryVariableSetIds'],
515
+ 'url' => "http://#{instance}#{p_list_object['Links']['Web']}",
516
+ },
517
+ 'releases' => [],
518
+ 'process' => process
519
+
520
+ }
521
+ else
522
+ ## steps
523
+ steps = generic_call('GET', "deploymentprocesses/#{p_list_object['DeploymentProcessId']}", false)
524
+ if steps['response'] == 'ok'
525
+ process = []
526
+ steps['message']['Steps'].each do |step|
527
+ process.push(step['Name'])
528
+ end
529
+ end
530
+ ## steps end
531
+ variable_sets << {
532
+ 'name' => p_list_object['Name'],
533
+ 'id' => p_list_object['Id'],
534
+ 'url' => "http://#{instance}#{p_list_object['Links']['Web']}",
535
+ 'process' => process
536
+ }
537
+ end
538
+ end
539
+ if islong.class.to_s == 'TrueClass'
540
+ # logic for transforming library variables will go here
541
+
542
+ create_local_file_by_id('libraryvariablesets')
543
+ variable_sets.each do |single_var|
544
+ new_vars = []
545
+ unless single_var['properties']['variablesets'].nil?
546
+ single_var['properties']['variablesets'].each do |var_id|
547
+ new_vars.push(local_file_by_id(var_id,false))
548
+ end
549
+ end
550
+ single_var['properties']['variablesets'] = new_vars
551
+ end
552
+ # end of logic for transforming library variables
553
+ case variable_sets
554
+ when Array # single item or manyitems is FALSE
555
+ p_id = variable_sets[0]['properties']['id']
556
+ release_obj = generic_call('GET', "projects/#{p_id}/releases", false)
557
+ if release_obj['response'] = 'ok'
558
+ releases = []
559
+ release_obj['message']['Items'].each do |item|
560
+ releases << {
561
+ 'version' => item['Version'],
562
+ 'properties' => {
563
+ 'url' => "https://#{instance}#{item['Links']['Web']}",
564
+ 'id' => item['Id'],
565
+ }
566
+ }
567
+ end
568
+ variable_sets[0]['releases'] = releases
569
+ else
570
+ output['response'] = 'notok'
571
+ output ['message'] = release_obj['message']
572
+ end
573
+ print "\n"
574
+
575
+ output['response'] = 'ok'
576
+ output['message'] = JSON.pretty_generate(variable_sets)
577
+
578
+ when Hash # many items or manyitems is TRUE
579
+ output['response'] = 'ok'
580
+ output['message'] = JSON.pretty_generate(variable_sets)
581
+
582
+ else
583
+ output['response'] = 'notok'
584
+ output['message'] = 'Could not parse data appropriately.'
585
+ return output
586
+ end
587
+ else
588
+ output['response'] = 'ok'
589
+ output['message'] = JSON.pretty_generate(variable_sets)
590
+ end
591
+ if output['message'].class.to_s == 'FalseClass'
592
+ output['response'] = 'notok'
593
+ output ['message'] = 'Could not parse data appropriately.'
594
+ end
595
+ return output
596
+ end
597
+
598
+ def parse_project_release(r_object)
599
+ output = {
600
+ 'response' => 'error',
601
+ 'message' => false
602
+ }
603
+ instance = @instance
604
+ variable_sets = []
605
+ begin
606
+ package_steps = []
607
+ r_object['SelectedPackages'].each do |package|
608
+ package_steps.push(package['StepName'])
609
+ end
610
+ variable_sets << {
611
+ 'Assembled' => r_object['Assembled'],
612
+ 'url' => "http://#{instance}#{r_object['Links']['Web']}",
613
+ 'package-steps' => package_steps,
614
+ 'id' => r_object['Id']
615
+
616
+ }
617
+ output['response'] = 'ok'
618
+ output['message'] = JSON.pretty_generate(variable_sets)
619
+ rescue
620
+ output['response'] = 'notok'
621
+ output ['message'] = 'Could not parse data appropriately.'
622
+ end
623
+ return output
624
+ end
625
+
626
+ def parse_deployment(d_object,islong)
627
+ output = {
628
+ 'response' => 'error',
629
+ 'message' => false
630
+ }
631
+ variable_sets = []
632
+ if islong.class.to_s == 'TrueClass' # long result
633
+ begin
634
+ steps = []
635
+ d_object['StepsToExecute'].each do |item|
636
+ steps << {
637
+ 'name' => item['ActionName'],
638
+ 'properties' => {
639
+ 'num' => item['ActionNumber'],
640
+ 'roles' => item['Roles'],
641
+ 'targets' => item['MachineNames'],
642
+ 'excluded_machines' => item['ExcludedMachines'],
643
+ }
644
+ }
645
+ end
646
+ variable_sets << {
647
+ 'steps' => steps
648
+ }
649
+ output['response'] = 'ok'
650
+ output ['message'] = JSON.pretty_generate(variable_sets)
651
+ rescue
652
+ output['response'] = 'notok'
653
+ output ['message'] = 'Could not parse data appropriately.'
654
+ end
655
+ return output
656
+ else # not long
657
+ begin
658
+ steps = []
659
+ d_object['StepsToExecute'].each do |item|
660
+ steps.push("#{item['ActionNumber']} : #{item['ActionName']}")
661
+ end
662
+ variable_sets << {
663
+ 'steps' => steps
664
+ }
665
+ output['response'] = 'ok'
666
+ output ['message'] = JSON.pretty_generate(variable_sets)
667
+ rescue
668
+ output['response'] = 'notok'
669
+ output ['message'] = 'Could not parse data appropriately.'
670
+ ensure
671
+ return output
672
+ end # ends
673
+ end
674
+ end
675
+
676
+
677
+ end
678
+
679
+ class Octo < Chef::Knife
680
+
681
+ def run
682
+ puts <<-EOH
683
+ ** OCTO COMMANDS **
684
+ You may use these flags
685
+ --long or -l
686
+ The long flag helps with more information
687
+
688
+ knife octo variables list
689
+ knife octo variables show 'name'
690
+ knife octo env list
691
+ knife octo env show 'name'
692
+ knife octo machine list
693
+ knife octo machine show 'name'
694
+
695
+ knife octo project list -- The long option for the said command will display the last 30 releases created for the said project with other details.
696
+
697
+ ** project show **
698
+ Supports two additional flags in addition to --long
699
+ --release or -r
700
+ --env or -e
701
+ knife octo project show 'name'
702
+ knife octo project show 'name' -r 'release number'
703
+ knife octo project show 'name' -r 'release number' -e 'environment' -- The said command will display the outcome of the process if a deployment were to be triggered on the said environment and release.
704
+
705
+
706
+ EOH
707
+ end
708
+ end
709
+
710
+ class OctoVariablesList < Chef::Knife
711
+ banner "** knife octo variables list ** \n knife octo variables list \n knife octo variables list --long \n knife octo variables list -l"
712
+ option :long,
713
+ :short => '-l',
714
+ :long => '--long',
715
+ :boolean => true,
716
+ :description => "Select to use long i.e detailed results"
717
+ def run
718
+ if Chef::Config[:knife][:octo_instance].nil?
719
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
720
+ exit
721
+ end
722
+ if Chef::Config[:knife][:octo_apikey].nil?
723
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
724
+ exit
725
+ end
726
+ unless name_args.size == 0
727
+ ui.error("This command does not take any arguments")
728
+ return
729
+ end
730
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
731
+ request = octo_methods.generic_call('GET', 'libraryvariablesets/all/', false)
732
+ if request['response'] == 'ok'
733
+ if config[:long]
734
+ print octo_methods.parse_variable_list(request['message'],true)
735
+ else
736
+ print octo_methods.parse_variable_list(request['message'],false)
737
+ end
738
+ print "\n"
739
+ else
740
+ print request['message']
741
+ end
742
+ end
743
+ end
744
+
745
+ class OctoVariablesShow < Chef::Knife
746
+ banner "** knife octo variables show 'name' ** \n knife octo variables 'name' -l \n knife octo variables show 'name' --long \n"
747
+ option :long,
748
+ :short => '-l',
749
+ :long => '--long',
750
+ :boolean => true,
751
+ :description => "Select to use long i.e detailed results"
752
+
753
+ def run
754
+ if Chef::Config[:knife][:octo_instance].nil?
755
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
756
+ exit
757
+ end
758
+ if Chef::Config[:knife][:octo_apikey].nil?
759
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
760
+ exit
761
+ end
762
+ if name_args.size == 0
763
+ ui.error('Specify a variable list!')
764
+ return
765
+ end
766
+ if name_args.size > 1
767
+ ui.error ("You specified two variables!\n Try giving the variable name in quotes.")
768
+ return
769
+ end
770
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
771
+
772
+ if config[:long]
773
+ if octo_methods.parse_variable_sets(name_args[0],true)['response'] == 'ok'
774
+ octo_methods.create_local_file_by_id('environments')
775
+ print octo_methods.parse_variable_sets(name_args[0],true)['message']
776
+ #delete_local_file
777
+ else
778
+ ui.error(octo_methods.parse_variable_sets(name_args[0],true)['message'])
779
+ exit
780
+ end
781
+ else
782
+ if octo_methods.parse_variable_sets(name_args[0],false)['response'] == 'ok'
783
+ print octo_methods.parse_variable_sets(name_args[0],false)['message']
784
+ else
785
+ ui.error(octo_methods.parse_variable_sets(name_args[0],false)['message'])
786
+ exit
787
+ end
788
+
789
+ end
790
+ print "\n"
791
+
792
+
793
+ end
794
+ end
795
+
796
+ class OctoEnvList < Chef::Knife
797
+ banner "** knife octo env list ** \n knife octo env list\n knife octo env list --long \n knife octo env list -l\n"
798
+ option :long,
799
+ :short => '-l',
800
+ :long => '--long',
801
+ :boolean => true,
802
+ :description => "Select to use long i.e detailed results"
803
+ def run
804
+ if Chef::Config[:knife][:octo_instance].nil?
805
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
806
+ exit
807
+ end
808
+ if Chef::Config[:knife][:octo_apikey].nil?
809
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
810
+ exit
811
+ end
812
+ unless name_args.size == 0
813
+ ui.error('This command does not take any arguments')
814
+ return
815
+ end
816
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
817
+ request = octo_methods.generic_call('GET', 'environments/all', false)
818
+ if request['response'] == 'ok'
819
+ if config[:long]
820
+ print octo_methods.parse_env_list(request['message'],true)
821
+ else
822
+ print octo_methods.parse_env_list(request['message'],false)
823
+ end
824
+ print "\n"
825
+ else
826
+ print request['message']
827
+ end
828
+ end
829
+ end
830
+
831
+ class OctoEnvShow < Chef::Knife
832
+
833
+ banner "** knife octo env show ** \n knife octo env show\n knife octo env show --long \n knife octo env show -l"
834
+ option :long,
835
+ :short => '-l',
836
+ :long => '--long',
837
+ :boolean => true,
838
+ :description => "Select to use long i.e detailed results"
839
+ def run
840
+ if Chef::Config[:knife][:octo_instance].nil?
841
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
842
+ exit
843
+ end
844
+ if Chef::Config[:knife][:octo_apikey].nil?
845
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
846
+ exit
847
+ end
848
+ if name_args.size > 1
849
+ ui.error("You provided two environments\nSupply only one environemnt or try quotes.")
850
+ return
851
+ end
852
+
853
+ unless name_args.size == 1
854
+ ui.error('Provide the env name!')
855
+ return
856
+ end
857
+
858
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
859
+ env_id = octo_methods.eid_from_name("#{name_args[0]}")
860
+ if env_id['response'] == 'ok'
861
+
862
+ request = octo_methods.generic_call('GET', "environments/#{env_id['message']}/machines", false)
863
+ if config[:long]
864
+ octo_methods.create_local_file_by_id('environments')
865
+ print octo_methods.parse_env_deets(request['message'],true)
866
+ print "\n"
867
+ octo_methods.delete_local_file
868
+ else
869
+ print octo_methods.parse_env_deets(request['message'],false)
870
+ print "\n"
871
+ end
872
+ else
873
+ ui.error(env_id['message'])
874
+ end
875
+ end
876
+ end
877
+
878
+ class OctoMachineList < Chef::Knife
879
+
880
+ banner "** knife octo machine list ** \n knife octo machine list \n knife octo machine list --long \n knife octo machine list -l\n"
881
+ option :long,
882
+ :short => '-l',
883
+ :long => '--long',
884
+ :boolean => true,
885
+ :description => "Select to use long i.e detailed results"
886
+ def run
887
+ if Chef::Config[:knife][:octo_instance].nil?
888
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
889
+ exit
890
+ end
891
+ if Chef::Config[:knife][:octo_apikey].nil?
892
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
893
+ exit
894
+ end
895
+ unless name_args.size == 0
896
+ ui.error('This command does not take any arguments')
897
+ return
898
+ end
899
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
900
+ request = octo_methods.generic_call('GET', 'machines/all', false)
901
+ if request['response'] == 'ok'
902
+ if config[:long]
903
+ print octo_methods.parse_machine_list(request['message'],true)
904
+ print "\n"
905
+ else
906
+ print octo_methods.parse_machine_list(request['message'],false)
907
+ print "\n"
908
+ end
909
+ else
910
+ ui.error(response['message'])
911
+ print "\n"
912
+ end
913
+ end
914
+
915
+ end
916
+
917
+ class OctoMachineShow < Chef::Knife
918
+ banner "** knife octo machine show 'name' ** \n knife octo machine show 'name' \n knife octo machine show 'name' --long \n knife octo machine show 'name' -l"
919
+ option :long,
920
+ :short => '-l',
921
+ :long => '--long',
922
+ :boolean => true,
923
+ :description => "Select to use long i.e detailed results"
924
+
925
+ def run
926
+ if Chef::Config[:knife][:octo_instance].nil?
927
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
928
+ exit
929
+ end
930
+ if Chef::Config[:knife][:octo_apikey].nil?
931
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
932
+ exit
933
+ end
934
+ if name_args.size > 1
935
+ ui.error("You provided two machines.\nSupply only one machine name or try quotes.")
936
+ return
937
+ end
938
+
939
+ unless name_args.size == 1
940
+ ui.error("#{'Provide the machine name!'.red}")
941
+ return
942
+ end
943
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
944
+ m_id = octo_methods.mid_from_mname("#{name_args[0]}")
945
+ if m_id['response'] == 'ok'
946
+ request = octo_methods.generic_call('GET', "machines/#{m_id['message']}", false)
947
+ if config[:long]
948
+ print octo_methods.parse_machine_deets(request['message'],true)
949
+ print "\n"
950
+ else
951
+ print octo_methods.parse_machine_deets(request['message'],false)
952
+ print "\n"
953
+ end
954
+ else
955
+ ui.error(m_id['message'])
956
+ end
957
+ end
958
+ end
959
+
960
+ class OctoProjectList < Chef::Knife
961
+ banner "** knife octo project list ** \n knife octo project list \n knife octo project list --long \n knife octo project list -l\n"
962
+ option :long,
963
+ :short => '-l',
964
+ :long => '--long',
965
+ :boolean => true,
966
+ :description => "Select to use long i.e detailed results"
967
+ def run
968
+ if Chef::Config[:knife][:octo_instance].nil?
969
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
970
+ exit
971
+ end
972
+ if Chef::Config[:knife][:octo_apikey].nil?
973
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
974
+ exit
975
+ end
976
+ unless name_args.size == 0
977
+ ui.error('This command does not take any arguments')
978
+ return
979
+ end
980
+
981
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
982
+ request = octo_methods.generic_call('GET', 'projects/all', false)
983
+ if request['response'] == 'ok'
984
+ if config[:long]
985
+ octo_methods.create_local_file_by_id('libraryvariablesets')
986
+ parser = octo_methods.parse_project_list(request['message'],true,true)
987
+ octo_methods.delete_local_file
988
+ else
989
+ parser = octo_methods.parse_project_list(request['message'],false,true)
990
+ end
991
+ if parser['response'] == 'ok'
992
+ print parser['message']
993
+ else
994
+ ui.error(parser['message'])
995
+ end
996
+ print "\n"
997
+ else
998
+ print request['message']
999
+ end
1000
+ end
1001
+
1002
+ end
1003
+
1004
+ class OctoProjectShow < Chef::Knife
1005
+ banner "** knife octo project show 'name' ** \n knife octo project show 'name' \n knife octo project show 'name' --long \n knife octo project show 'name' -l\n knife octo project show 'name' -r 'release number'\n knife octo project show 'name' -r 'release number' -e 'environment'\n \nknife octo project show 'name' --release 'release number'\n knife octo project show 'name' --release 'release number' --env 'environment'\n\n"
1006
+ option :long,
1007
+ :short => '-l',
1008
+ :long => '--long',
1009
+ :boolean => true,
1010
+ :description => "Select to use long i.e detailed results"
1011
+
1012
+ option :release,
1013
+ :short => '-r',
1014
+ :long => '--release',
1015
+ :boolean => true,
1016
+ :description => "Select to specify a release number"
1017
+
1018
+ option :env,
1019
+ :short => '-e',
1020
+ :long => '--env',
1021
+ :boolean => true,
1022
+ :description => "Select to specify an environment"
1023
+
1024
+
1025
+ def run
1026
+ if Chef::Config[:knife][:octo_instance].nil?
1027
+ ui.error("Please specify your Octopus Deploy instance in your knife config as -\nknife[:octo_instance] = 'MYOCTO.MYDOMAIN'")
1028
+ exit
1029
+ end
1030
+ if Chef::Config[:knife][:octo_apikey].nil?
1031
+ ui.error("Please specify your Octopus API key in your knife config as -\nknife[:octo_apikey] = 'MYKEY'\nSee Also : https://octopus.com/docs/api-and-integration/api/how-to-create-an-api-key")
1032
+ exit
1033
+ end
1034
+
1035
+ if name_args.size == 0
1036
+ ui.error("#{'Provide the project name!'.red}")
1037
+ return
1038
+ end
1039
+ if (config[:env] && !config[:release])
1040
+ ui.error('You cannot specify just an environment, specify a release and an environment.')
1041
+ exit
1042
+ end
1043
+ octo_methods = OctoHelperMethods.new(Chef::Config[:knife][:octo_apikey],Chef::Config[:knife][:octo_instance])
1044
+ request = octo_methods.generic_call('GET', "projects/#{name_args[0]}", false)
1045
+ case name_args.size
1046
+ when 1
1047
+ if config[:release]
1048
+ ui.error('You specified the release flag but did not provide a release number.')
1049
+ exit
1050
+ end
1051
+ if config[:env]
1052
+ ui.error('You cannot specify just an environment, specify a release and an environment.')
1053
+ exit
1054
+ end
1055
+ if request['response'] == 'ok'
1056
+ octo_methods.create_local_file_by_id('environments')
1057
+ if config[:long]
1058
+ parser = octo_methods.parse_project_list(request['message'],true,false)
1059
+ octo_methods.delete_local_file
1060
+ else
1061
+ parser = octo_methods.parse_project_list(request['message'],false,false)
1062
+ end
1063
+ if parser['response'] == 'ok'
1064
+ print parser['message']
1065
+ else
1066
+ ui.error(parser['message'])
1067
+ end
1068
+ else
1069
+ ui.error(request['message'])
1070
+ end
1071
+ octo_methods.delete_local_file
1072
+ when 2
1073
+ if config[:release]
1074
+ if request['response'] == 'ok'
1075
+ second_request = octo_methods.generic_call('GET', "projects/#{request['message']['Id']}/releases/#{name_args[1]}", false)
1076
+ if second_request['response'] == 'ok'
1077
+ release_result = octo_methods.parse_project_release(second_request['message'])
1078
+ if release_result['response'] == 'ok'
1079
+ print release_result['message']
1080
+ else
1081
+ ui.error(release_result['message'])
1082
+ end
1083
+
1084
+ else
1085
+ ui.error(second_request['message'])
1086
+ end
1087
+ else
1088
+ ui.error(request['message'])
1089
+ end
1090
+ end
1091
+
1092
+ when 3
1093
+ # if config env
1094
+ if (config[:release] && config[:env])
1095
+ if request['response'] == 'ok'
1096
+ second_request = octo_methods.generic_call('GET', "projects/#{request['message']['Id']}/releases/#{name_args[1]}", false)
1097
+ if second_request['response'] == 'ok'
1098
+ r_id = second_request['message']['Id']
1099
+ env = octo_methods.eid_from_name(name_args[2])
1100
+ if env['response'] == 'ok'
1101
+ third_request = octo_methods.generic_call('GET', "releases/#{r_id}/deployments/preview/#{env['message']}", false)
1102
+ if third_request['response'] == 'ok'
1103
+ if config[:long]
1104
+ d_deets = octo_methods.parse_deployment(third_request['message'],true)
1105
+ else
1106
+ d_deets = octo_methods.parse_deployment(third_request['message'],false)
1107
+ end
1108
+ if d_deets['response'] == 'ok'
1109
+ print d_deets['message']
1110
+ else
1111
+ ui.error(d_deets['message'])
1112
+ exit
1113
+ end
1114
+ else
1115
+ ui.error(third_request['message'])
1116
+ exit
1117
+ end
1118
+ else
1119
+ ui.error(env['message'])
1120
+ end
1121
+ else
1122
+ ui.error(second_request['message'])
1123
+ exit
1124
+ end
1125
+ else
1126
+ ui.error(request['message'])
1127
+ end
1128
+
1129
+ end
1130
+ # end env
1131
+ else
1132
+ ui.error('You can only specify two options')
1133
+ end
1134
+ print "\n"
1135
+ end
1136
+ end
1137
+
1138
+ # EOF
1139
+ end
@@ -0,0 +1,6 @@
1
+ module Knife
2
+ module Octo
3
+ VERSION = "0.0.1"
4
+ MAJOR, MINOR, TINY = VERSION.split('.')
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-octo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Vishal Basra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.7
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.7
27
+ description: A knife plugin to things much faster in Octopus Deploy server.
28
+ email: vishalbasra@live.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - README.md
35
+ - knife-octo.gemspec
36
+ - lib/chef/knife/octo.rb
37
+ - lib/knife-octo/version.rb
38
+ homepage: https://github.com/vishalbasra/knife-octo
39
+ licenses:
40
+ - Apache-2.0
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.7.6
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Chef Knife plugin to help see stuff in Octopus Deploy
62
+ test_files: []