octopus-serverspec-extensions 0.15.5 → 0.17.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/docs/authentication.md +45 -0
  3. data/docs/octopus_deploy_account.md +37 -0
  4. data/docs/octopus_deploy_doc_template.md +17 -0
  5. data/docs/octopus_deploy_environment.md +33 -0
  6. data/docs/octopus_deploy_project_group.md +31 -0
  7. data/docs/octopus_deploy_smtp_config.md +39 -0
  8. data/docs/octopus_deploy_space.md +32 -0
  9. data/docs/octopus_deploy_team.md +26 -0
  10. data/docs/octopus_deploy_tentacle.md +41 -0
  11. data/docs/octopus_deploy_upgrade_config.md +34 -0
  12. data/docs/octopus_deploy_user.md +34 -0
  13. data/docs/octopus_deploy_worker.md +39 -0
  14. data/docs/octopus_deploy_worker_pool.md +26 -0
  15. data/lib/octopus_serverspec_extensions.rb +70 -0
  16. data/lib/octopus_serverspec_extensions/matcher/allow_dynamic_infrastructure.rb +13 -0
  17. data/lib/octopus_serverspec_extensions/matcher/use_guided_failure.rb +13 -0
  18. data/lib/octopus_serverspec_extensions/type/octopus_deploy_account.rb +72 -61
  19. data/lib/octopus_serverspec_extensions/type/octopus_deploy_environment.rb +70 -11
  20. data/lib/octopus_serverspec_extensions/type/octopus_deploy_project_group.rb +77 -52
  21. data/lib/octopus_serverspec_extensions/type/octopus_deploy_smtp_config.rb +109 -0
  22. data/lib/octopus_serverspec_extensions/type/octopus_deploy_space.rb +92 -0
  23. data/lib/octopus_serverspec_extensions/type/octopus_deploy_team.rb +82 -0
  24. data/lib/octopus_serverspec_extensions/type/octopus_deploy_tentacle.rb +7 -8
  25. data/lib/octopus_serverspec_extensions/type/octopus_deploy_upgrade_config.rb +112 -0
  26. data/lib/octopus_serverspec_extensions/type/octopus_deploy_user.rb +111 -0
  27. data/lib/octopus_serverspec_extensions/type/octopus_deploy_worker.rb +173 -0
  28. data/lib/octopus_serverspec_extensions/type/octopus_deploy_worker_pool.rb +33 -3
  29. data/lib/octopus_serverspec_extensions/version.rb +1 -1
  30. metadata +25 -3
@@ -0,0 +1,111 @@
1
+ require 'serverspec/type/base'
2
+ require 'net/http'
3
+ require 'json'
4
+
5
+ module Serverspec::Type
6
+ class OctopusDeployUser < Base
7
+ @serverUrl = nil
8
+ @apiKey = nil
9
+ @userAccount = nil
10
+
11
+ def initialize(*url_and_api_key, userName)
12
+ serverUrl, apiKey = get_octopus_creds(url_and_api_key)
13
+
14
+ @name = "Octopus Deploy User Account #{userName}"
15
+ @runner = Specinfra::Runner
16
+ @serverUrl = serverUrl
17
+ @apiKey = apiKey
18
+
19
+ # is our auth info still nil?
20
+ if (serverUrl.nil?)
21
+ raise "'serverUrl' was not provided. Unable to connect to Octopus server to validate configuration."
22
+ end
23
+ if (apiKey.nil?)
24
+ raise "'apiKey' was not provided. Unable to connect to Octopus server to validate configuration."
25
+ end
26
+
27
+ if(userName.nil?)
28
+ raise "'userName' was not provided"
29
+ end
30
+
31
+ @userAccount = get_user_via_api(serverUrl, apiKey, userName)
32
+ end
33
+
34
+ def service_account?
35
+ return false if @userAccount.nil?
36
+ @userAccount['IsService'] == true
37
+ end
38
+
39
+ def exists?
40
+ (!@userAccount.nil?) && (@userAccount != [])
41
+ end
42
+
43
+ def active?
44
+ return false if @userAccount.nil?
45
+ @userAccount['IsActive'] == true
46
+ end
47
+
48
+ def has_email?(email)
49
+ return false if @userAccount.nil?
50
+ @userAccount['EmailAddress'] == email
51
+ end
52
+
53
+ def has_display_name?(name)
54
+ return false if @userAccount.nil?
55
+ @userAccount['DisplayName'] == name
56
+ end
57
+
58
+ def has_api_key?(purpose)
59
+ return false if @userAccount.nil?
60
+
61
+ user_api_key = nil
62
+ user_id = @userAccount['Id']
63
+ url = "#{@serverUrl}/api/users/#{user_id}/apikeys?api-key=#{@apiKey}&take=9999"
64
+
65
+ begin
66
+ resp = Net::HTTP.get_response(URI.parse(url))
67
+ body = JSON.parse(resp.body)
68
+ keys = body unless body.nil?
69
+ user_api_key = keys['Items'].select {|i| i['Purpose'] == purpose }.first unless keys.nil?
70
+
71
+ rescue => e
72
+ raise "has_api_key: Unable to connect to #{url}: #{e}"
73
+ end
74
+
75
+ !user_api_key.nil?
76
+ end
77
+ end
78
+
79
+ # module-level constructors/entrypoints
80
+
81
+ def octopus_deploy_user(*url_and_api_key, user_name)
82
+ serverUrl, apiKey = get_octopus_creds(url_and_api_key)
83
+ OctopusDeployUser.new(serverUrl, apiKey, user_name)
84
+ end
85
+
86
+ def octopus_user(*url_and_api_key, user_name)
87
+ serverUrl, apiKey = get_octopus_creds(url_and_api_key)
88
+ octopus_deploy_user(serverUrl, apiKey, user_name)
89
+ end
90
+
91
+ private
92
+
93
+ def get_user_via_api(serverUrl, apiKey, user_name)
94
+ user = nil
95
+
96
+ url = "#{serverUrl}/api/users/all?api-key=#{apiKey}"
97
+
98
+ begin
99
+ resp = Net::HTTP.get_response(URI.parse(url))
100
+ body = JSON.parse(resp.body)
101
+ users = body unless body.nil?
102
+ user = users.select {|i| i['Username'] == user_name }.first unless users.nil?
103
+ rescue => e
104
+ raise "get_user_via_api: Unable to connect to #{url}: #{e}"
105
+ end
106
+
107
+ user
108
+ end
109
+ end
110
+
111
+ include Serverspec::Type
@@ -0,0 +1,173 @@
1
+ require 'serverspec'
2
+ require 'serverspec/type/base'
3
+ require 'net/http'
4
+ require 'json'
5
+
6
+ module Serverspec::Type
7
+ class OctopusDeployWorker < Base
8
+ @worker = nil
9
+ @serverUrl = nil
10
+ @apiKey = nil
11
+ @serverSupportsSpaces = nil
12
+ @spaceId = nil
13
+ @spaceFragment = ""
14
+
15
+ def initialize(serverUrl, apiKey, instance, spaceId = 'Spaces-1')
16
+ @name = "Octopus Deploy Worker #{instance}"
17
+ @runner = Specinfra::Runner
18
+ @serverUrl = serverUrl
19
+ @apiKey = apiKey
20
+ @spaceId = spaceId
21
+
22
+ if (serverUrl.nil?)
23
+ raise "'serverUrl' was not provided. Unable to connect to Octopus server to validate configuration."
24
+ end
25
+ if (apiKey.nil?)
26
+ raise "'apiKey' was not provided. Unable to connect to Octopus server to validate configuration."
27
+ end
28
+
29
+ if (exists?)
30
+ thumbprint = `"c:\\program files\\Octopus Deploy\\Tentacle\\Tentacle.exe" show-thumbprint --console --nologo --instance #{instance}`
31
+ thumbprint = thumbprint.gsub('==== ShowThumbprintCommand starting ====', '').strip
32
+ thumbprint = thumbprint.gsub('The thumbprint of this Tentacle is: ', '').strip
33
+ thumbprint = thumbprint.gsub('==== ShowThumbprintCommand completed ====', '').strip
34
+ thumbprint = thumbprint.gsub('==== ShowThumbprintCommand ====', '').strip
35
+
36
+ @serverSupportsSpaces = check_supports_spaces(serverUrl)
37
+
38
+ if (@serverSupportsSpaces)
39
+ @spaceFragment = "#{@spaceId}/"
40
+ end
41
+
42
+ @worker = get_worker_via_api(serverUrl, apiKey, thumbprint)
43
+ else
44
+ raise "tentacle.exe does not exist"
45
+ end
46
+ end
47
+
48
+ def registered_with_the_server?
49
+ !@worker.nil?
50
+ end
51
+
52
+ def online?
53
+ return nil if @worker.nil?
54
+ @worker = poll_until_worker_has_completed_healthcheck(@serverUrl, @apiKey, @worker["Thumbprint"])
55
+ status = @worker['Status']
56
+ if ("#{status}" == "")
57
+ status = @worker['HealthStatus'] if "#{status}" == ""
58
+ puts "Expected status 'Healthy|HasWarnings' for Worker #{@name}, but got '#{status}'" if (status != "Healthy" && status != "HasWarnings")
59
+ status == "Healthy" || status == "HasWarnings"
60
+ else
61
+ puts "Expected status 'Online|CalamariNeedsUpgrade|NeedsUpgrade' for Worker #{@name}, but got '#{status}'" if (status != "Online" && status != "CalamariNeedsUpgrade" && status != "NeedsUpgrade")
62
+ status == "Online" || status == "CalamariNeedsUpgrade" || status == "NeedsUpgrade"
63
+ end
64
+ end
65
+
66
+ def in_space?(space_name)
67
+ return false if @worker.nil?
68
+ return false if @serverSupportsSpaces
69
+ url = "#{@serverUrl}/api/spaces/all?api-key=#{@apiKey}"
70
+ resp = Net::HTTP.get_response(URI.parse(url))
71
+ spaces = JSON.parse(resp.body)
72
+ space_id = spaces.select {|e| e["Name"] == space_name}.first["Id"]
73
+ @worker["SpaceId"] == space_id
74
+ end
75
+
76
+ def has_policy?(policy_name)
77
+ return false if @worker.nil?
78
+ url = "#{@serverUrl}/api/#{@spaceFragment}machinepolicies/all?api-key=#{@apiKey}"
79
+ resp = Net::HTTP.get_response(URI.parse(url))
80
+ policies = JSON.parse(resp.body)
81
+ policy_id = policies.select {|e| e["Name"] == policy_name}.first["Id"]
82
+ @worker["MachinePolicyId"] == policy_id
83
+ end
84
+
85
+ def has_display_name?(name)
86
+ return false if @worker.nil?
87
+ @worker["Name"] == name
88
+ end
89
+
90
+ def has_endpoint?(uri)
91
+ return false if @worker.nil?
92
+ return false if @worker["Uri"].nil? # polling tentacles have null endpoint. catch that.
93
+ puts "Expected uri '#{uri}' for Worker #{@name}, but got '#{@worker["Uri"]}'" unless (@worker["Uri"].casecmp(uri) == 0)
94
+ @worker["Uri"].casecmp(uri) == 0
95
+ end
96
+
97
+ def listening?
98
+ return false if @worker.nil?
99
+ puts "Expected CommunicationStyle 'TentaclePassive' for Tentacle #{@name}, but got '#{@worker["Endpoint"]["CommunicationStyle"]}'" if (@worker["Endpoint"]["CommunicationStyle"] != "TentaclePassive")
100
+ @worker["Endpoint"]["CommunicationStyle"] == "TentaclePassive"
101
+ end
102
+
103
+ def polling?
104
+ return false if @worker.nil?
105
+ puts "Expected CommunicationStyle 'TentacleActive' for Tentacle #{@name}, but got '#{@worker["Endpoint"]["CommunicationStyle"]}'" if (@worker["Endpoint"]["CommunicationStyle"] != "TentacleActive")
106
+ @worker["Endpoint"]["CommunicationStyle"] == "TentacleActive"
107
+ end
108
+
109
+ def exists?
110
+ ::File.exists?("c:\\program files\\Octopus Deploy\\Tentacle\\Tentacle.exe")
111
+ end
112
+ end
113
+
114
+ def octopus_deploy_worker(serverUrl, apiKey, instance)
115
+ OctopusDeployWorker.new(serverUrl, apiKey, instance)
116
+ end
117
+
118
+ private
119
+
120
+ def check_supports_spaces(serverUrl)
121
+ begin
122
+ resp = Net::HTTP.get_response(URI.parse("#{serverUrl}/api/"))
123
+ body = JSON.parse(resp.body)
124
+ version = body['Version']
125
+ return Gem::Version.new(version) > Gem::Version.new('2019.0.0')
126
+ rescue => e
127
+ puts "Unable to connect to #{serverUrl}: #{e}"
128
+ end
129
+
130
+ return false
131
+ end
132
+
133
+ def poll_until_worker_has_completed_healthcheck(serverUrl, apiKey, thumbprint)
134
+ worker = nil
135
+ url = "#{serverUrl}/api/#{@spaceFragment}workers/all?api-key=#{apiKey}"
136
+
137
+ now = Time.now
138
+ counter = 1
139
+ loop do
140
+ worker = get_worker_via_api(serverUrl, apiKey, thumbprint)
141
+
142
+ break if worker.nil?
143
+ break if counter > 10
144
+ break if !worker_healthcheck_outstanding(worker)
145
+ puts "Machine health check for #{worker["Name"]} has not yet completed. Waiting 5 seconds to try again."
146
+ counter += 1
147
+ sleep 5
148
+ end
149
+
150
+ worker
151
+ end
152
+
153
+ def worker_healthcheck_outstanding(worker)
154
+ worker["StatusSummary"] == "This machine was recently added. Please perform a health check."
155
+ end
156
+
157
+ def get_worker_via_api(serverUrl, apiKey, thumbprint)
158
+ worker = nil
159
+ url = "#{serverUrl}/api/#{@spaceFragment}workers/all?api-key=#{apiKey}"
160
+
161
+ begin
162
+ resp = Net::HTTP.get_response(URI.parse(url))
163
+ body = JSON.parse(resp.body)
164
+ worker = body.select {|e| e["Thumbprint"] == thumbprint}.first unless body.nil?
165
+ rescue => e
166
+ puts "Unable to connect to #{url}: #{e}"
167
+ end
168
+
169
+ worker
170
+ end
171
+ end
172
+
173
+ include Serverspec::Type
@@ -6,10 +6,16 @@ require 'json'
6
6
  module Serverspec::Type
7
7
  class OctopusDeployWorkerPool < Base
8
8
  @worker_pool = nil
9
+ @worker_pool_name = nil
9
10
  @serverUrl = nil
10
11
  @apiKey = nil
11
12
 
12
- def initialize(serverUrl, apiKey, worker_pool_name)
13
+ def initialize(*url_and_api_key, worker_pool_name)
14
+ serverUrl = get_octopus_url(url_and_api_key[0])
15
+ apiKey = get_octopus_api_key(url_and_api_key[1])
16
+
17
+ @worker_pool_name = worker_pool_name
18
+
13
19
  @name = "Octopus Deploy Worker Pool #{worker_pool_name}"
14
20
  @runner = Specinfra::Runner
15
21
  @serverUrl = serverUrl
@@ -28,12 +34,30 @@ module Serverspec::Type
28
34
  @worker_pool = get_worker_pool_via_api(serverUrl, apiKey, worker_pool_name)
29
35
  end
30
36
 
37
+ def in_space(space_name)
38
+ # allows us to tag .in_space() onto the end of the resource. as in
39
+ # describe octopus_worker_pool("account name").in_space("MyNewSpace") do
40
+ @spaceId = get_space_id?(space_name)
41
+ if @worker_pool_name.nil?
42
+ raise "'worker_pool_name' was not provided. Unable to connect to Octopus server to validate configuration."
43
+ end
44
+ self
45
+ end
46
+
31
47
  def exists?
32
48
  (!@worker_pool.nil?) && (@worker_pool != [])
33
49
  end
34
50
  end
35
51
 
36
- def octopus_deploy_worker_pool(serverUrl, apiKey, worker_pool_name)
52
+ def octopus_deploy_worker_pool(*url_and_api_key, worker_pool_name)
53
+ serverUrl, apiKey = get_octopus_url(url_and_api_key)
54
+
55
+ OctopusDeployWorkerPool.new(serverUrl, apiKey, worker_pool_name)
56
+ end
57
+
58
+ def octopus_worker_pool(*url_and_api_key, worker_pool_name)
59
+ serverUrl, apiKey = get_octopus_url(url_and_api_key)
60
+
37
61
  OctopusDeployWorkerPool.new(serverUrl, apiKey, worker_pool_name)
38
62
  end
39
63
 
@@ -41,7 +65,13 @@ module Serverspec::Type
41
65
 
42
66
  def get_worker_pool_via_api(serverUrl, apiKey, worker_pool_name)
43
67
  worker_pool = nil
44
- url = "#{serverUrl}/api/workerpools/all?api-key=#{apiKey}"
68
+
69
+ if @serverSupportsSpaces
70
+ # set the spaceId correctly
71
+ @spaceFragment = "#{@spaceId}/"
72
+ end
73
+
74
+ url = "#{serverUrl}/api/#{@spaceFragment}workerpools/all?api-key=#{apiKey}"
45
75
 
46
76
  begin
47
77
  resp = Net::HTTP.get_response(URI.parse(url))
@@ -1,3 +1,3 @@
1
1
  module OctopusServerSpecExtensions
2
- VERSION = "0.15.5"
2
+ VERSION = "0.17.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: octopus-serverspec-extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.5
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Richardson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-14 00:00:00.000000000 Z
11
+ date: 2019-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: serverspec
@@ -152,18 +152,39 @@ files:
152
152
  - Rakefile
153
153
  - bin/console
154
154
  - bin/setup
155
+ - docs/authentication.md
156
+ - docs/octopus_deploy_account.md
157
+ - docs/octopus_deploy_doc_template.md
158
+ - docs/octopus_deploy_environment.md
159
+ - docs/octopus_deploy_project_group.md
160
+ - docs/octopus_deploy_smtp_config.md
161
+ - docs/octopus_deploy_space.md
162
+ - docs/octopus_deploy_team.md
163
+ - docs/octopus_deploy_tentacle.md
164
+ - docs/octopus_deploy_upgrade_config.md
165
+ - docs/octopus_deploy_user.md
166
+ - docs/octopus_deploy_worker.md
167
+ - docs/octopus_deploy_worker_pool.md
155
168
  - lib/octopus_serverspec_extensions.rb
169
+ - lib/octopus_serverspec_extensions/matcher/allow_dynamic_infrastructure.rb
156
170
  - lib/octopus_serverspec_extensions/matcher/have_linux_line_endings.rb
157
171
  - lib/octopus_serverspec_extensions/matcher/have_version.rb
158
172
  - lib/octopus_serverspec_extensions/matcher/have_windows_line_endings.rb
159
173
  - lib/octopus_serverspec_extensions/matcher/run_under_account.rb
174
+ - lib/octopus_serverspec_extensions/matcher/use_guided_failure.rb
160
175
  - lib/octopus_serverspec_extensions/type/chocolatey_package.rb
161
176
  - lib/octopus_serverspec_extensions/type/java_property_file.rb
162
177
  - lib/octopus_serverspec_extensions/type/npm_package.rb
163
178
  - lib/octopus_serverspec_extensions/type/octopus_deploy_account.rb
164
179
  - lib/octopus_serverspec_extensions/type/octopus_deploy_environment.rb
165
180
  - lib/octopus_serverspec_extensions/type/octopus_deploy_project_group.rb
181
+ - lib/octopus_serverspec_extensions/type/octopus_deploy_smtp_config.rb
182
+ - lib/octopus_serverspec_extensions/type/octopus_deploy_space.rb
183
+ - lib/octopus_serverspec_extensions/type/octopus_deploy_team.rb
166
184
  - lib/octopus_serverspec_extensions/type/octopus_deploy_tentacle.rb
185
+ - lib/octopus_serverspec_extensions/type/octopus_deploy_upgrade_config.rb
186
+ - lib/octopus_serverspec_extensions/type/octopus_deploy_user.rb
187
+ - lib/octopus_serverspec_extensions/type/octopus_deploy_worker.rb
167
188
  - lib/octopus_serverspec_extensions/type/octopus_deploy_worker_pool.rb
168
189
  - lib/octopus_serverspec_extensions/type/windows_dsc.rb
169
190
  - lib/octopus_serverspec_extensions/type/windows_firewall.rb
@@ -189,7 +210,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
210
  - !ruby/object:Gem::Version
190
211
  version: '0'
191
212
  requirements: []
192
- rubygems_version: 3.0.1
213
+ rubyforge_project:
214
+ rubygems_version: 2.7.4
193
215
  signing_key:
194
216
  specification_version: 4
195
217
  summary: ServerSpec extensions for Octopus Deploy