octopus-serverspec-extensions 0.17.1 → 0.19.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.
- checksums.yaml +4 -4
- data/.gitignore +14 -14
- data/.rspec +2 -2
- data/.travis.yml +5 -5
- data/Gemfile +4 -4
- data/LICENSE.txt +12 -12
- data/README.md +39 -39
- data/Rakefile +6 -6
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/docs/authentication.md +45 -45
- data/docs/octopus_deploy_account.md +37 -37
- data/docs/octopus_deploy_doc_template.md +16 -16
- data/docs/octopus_deploy_environment.md +32 -32
- data/docs/octopus_deploy_project_group.md +30 -30
- data/docs/octopus_deploy_smtp_config.md +39 -39
- data/docs/octopus_deploy_space.md +32 -32
- data/docs/octopus_deploy_team.md +25 -25
- data/docs/octopus_deploy_tentacle.md +40 -40
- data/docs/octopus_deploy_upgrade_config.md +33 -33
- data/docs/octopus_deploy_user.md +33 -33
- data/docs/octopus_deploy_worker.md +38 -38
- data/docs/octopus_deploy_worker_pool.md +25 -25
- data/lib/octopus_serverspec_extensions.rb +85 -85
- data/lib/octopus_serverspec_extensions/matcher/allow_dynamic_infrastructure.rb +12 -12
- data/lib/octopus_serverspec_extensions/matcher/have_linux_line_endings.rb +13 -13
- data/lib/octopus_serverspec_extensions/matcher/have_version.rb +36 -36
- data/lib/octopus_serverspec_extensions/matcher/have_windows_line_endings.rb +13 -13
- data/lib/octopus_serverspec_extensions/matcher/run_under_account.rb +17 -17
- data/lib/octopus_serverspec_extensions/matcher/use_guided_failure.rb +12 -12
- data/lib/octopus_serverspec_extensions/type/chocolatey_package.rb +33 -33
- data/lib/octopus_serverspec_extensions/type/java_property_file.rb +28 -28
- data/lib/octopus_serverspec_extensions/type/npm_package.rb +36 -36
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_account.rb +176 -176
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_environment.rb +117 -117
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_project_group.rb +127 -127
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_smtp_config.rb +109 -109
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_space.rb +92 -92
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_team.rb +81 -81
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_tentacle.rb +208 -208
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_upgrade_config.rb +112 -112
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_user.rb +110 -110
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_worker.rb +173 -173
- data/lib/octopus_serverspec_extensions/type/octopus_deploy_worker_pool.rb +88 -88
- data/lib/octopus_serverspec_extensions/type/windows_dsc.rb +37 -32
- data/lib/octopus_serverspec_extensions/type/windows_firewall.rb +32 -32
- data/lib/octopus_serverspec_extensions/type/windows_scheduled_task.rb +33 -33
- data/lib/octopus_serverspec_extensions/version.rb +3 -3
- data/octopus-serverspec-extensions.gemspec +34 -34
- metadata +17 -18
@@ -1,111 +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
|
-
|
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
111
|
include Serverspec::Type
|
@@ -1,173 +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
|
-
|
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_worker?
|
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_worker?
|
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
|
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
|
+
puts "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_worker?
|
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_worker?
|
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
|