aws-codedeploy-agent 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/aws-codedeploy-agent.gemspec +5 -5
  2. data/certs/host-agent-deployment-signer-ca-chain.pem +30 -0
  3. data/conf/codedeployagent.yml +0 -1
  4. data/lib/instance_agent.rb +1 -13
  5. data/lib/instance_agent/agent/base.rb +38 -12
  6. data/lib/instance_agent/agent/plugin.rb +21 -0
  7. data/lib/instance_agent/config.rb +2 -1
  8. data/lib/instance_agent/platform/linux_util.rb +4 -0
  9. data/lib/instance_agent/plugins/codedeploy/application_specification/ace_info.rb +133 -0
  10. data/lib/instance_agent/plugins/codedeploy/application_specification/acl_info.rb +163 -0
  11. data/lib/instance_agent/plugins/codedeploy/application_specification/application_specification.rb +143 -0
  12. data/lib/instance_agent/plugins/codedeploy/application_specification/context_info.rb +23 -0
  13. data/lib/instance_agent/plugins/codedeploy/application_specification/file_info.rb +23 -0
  14. data/lib/instance_agent/plugins/codedeploy/application_specification/linux_permission_info.rb +121 -0
  15. data/lib/instance_agent/plugins/codedeploy/application_specification/mode_info.rb +66 -0
  16. data/lib/instance_agent/plugins/codedeploy/application_specification/range_info.rb +134 -0
  17. data/lib/instance_agent/plugins/codedeploy/application_specification/script_info.rb +27 -0
  18. data/lib/instance_agent/plugins/codedeploy/codedeploy_control.rb +100 -0
  19. data/lib/instance_agent/plugins/codedeploy/command_executor.rb +359 -0
  20. data/lib/instance_agent/plugins/codedeploy/command_poller.rb +178 -0
  21. data/lib/instance_agent/plugins/codedeploy/deployment_specification.rb +161 -0
  22. data/lib/instance_agent/plugins/codedeploy/hook_executor.rb +226 -0
  23. data/lib/instance_agent/plugins/codedeploy/install_instruction.rb +389 -0
  24. data/lib/instance_agent/plugins/codedeploy/installer.rb +147 -0
  25. data/lib/instance_agent/plugins/codedeploy/onpremise_config.rb +42 -0
  26. data/lib/instance_agent/plugins/codedeploy/register_plugin.rb +17 -0
  27. data/lib/instance_agent/runner/child.rb +20 -5
  28. data/lib/instance_agent/runner/master.rb +2 -15
  29. data/lib/instance_metadata.rb +2 -2
  30. data/test/certificate_helper.rb +1 -1
  31. data/test/helpers/instance_agent_helper.rb +1 -0
  32. data/test/instance_agent/agent/base_test.rb +16 -3
  33. data/test/instance_agent/config_test.rb +2 -1
  34. data/test/instance_agent/plugins/codedeploy/application_specification_test.rb +1713 -0
  35. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/codedeploy_control_test.rb +1 -1
  36. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/command_executor_test.rb +32 -9
  37. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/command_poller_test.rb +13 -14
  38. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/deployment_specification_test.rb +98 -25
  39. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/hook_executor_test.rb +83 -15
  40. data/test/instance_agent/plugins/codedeploy/install_instruction_test.rb +568 -0
  41. data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/installer_test.rb +12 -9
  42. data/test/instance_agent/plugins/codedeploy/onpremise_config_test.rb +72 -0
  43. data/test/instance_agent/runner/child_test.rb +1 -1
  44. data/vendor/gems/.codedeploy-commands-1.0.0.created.rid +1 -1
  45. data/vendor/gems/codedeploy-commands/lib/aws/plugins/deploy_control_endpoint.rb +4 -0
  46. data/vendor/gems/jmespath-1.0.1/lib/jmespath.rb +41 -0
  47. data/vendor/gems/jmespath-1.0.1/lib/jmespath/caching_parser.rb +30 -0
  48. data/vendor/gems/jmespath-1.0.1/lib/jmespath/errors.rb +17 -0
  49. data/vendor/gems/jmespath-1.0.1/lib/jmespath/expr_node.rb +15 -0
  50. data/vendor/gems/jmespath-1.0.1/lib/jmespath/lexer.rb +116 -0
  51. data/vendor/gems/jmespath-1.0.1/lib/jmespath/parser.rb +347 -0
  52. data/vendor/gems/jmespath-1.0.1/lib/jmespath/runtime.rb +71 -0
  53. data/vendor/gems/jmespath-1.0.1/lib/jmespath/token.rb +41 -0
  54. data/vendor/gems/jmespath-1.0.1/lib/jmespath/token_stream.rb +60 -0
  55. data/vendor/gems/jmespath-1.0.1/lib/jmespath/tree_interpreter.rb +523 -0
  56. data/vendor/gems/jmespath-1.0.1/lib/jmespath/version.rb +3 -0
  57. data/vendor/gems/process_manager/lib/process_manager/master.rb +16 -5
  58. data/vendor/specifications/{aws-sdk-core-2.0.5.gemspec → aws-sdk-core-2.0.42.gemspec} +9 -11
  59. data/vendor/specifications/builder-3.2.2.gemspec +1 -1
  60. data/vendor/specifications/codedeploy-commands-1.0.0.gemspec +7 -6
  61. data/vendor/specifications/gli-2.5.6.gemspec +1 -1
  62. data/vendor/specifications/jmespath-1.0.1.gemspec +29 -0
  63. data/vendor/specifications/little-plugger-1.1.3.gemspec +1 -1
  64. data/vendor/specifications/logging-1.8.1.gemspec +1 -1
  65. data/vendor/specifications/multi_json-1.7.7.gemspec +1 -1
  66. data/vendor/specifications/multi_json-1.8.4.gemspec +1 -1
  67. data/vendor/specifications/multi_xml-0.5.5.gemspec +1 -1
  68. data/vendor/specifications/process_manager-0.0.13.gemspec +1 -1
  69. data/vendor/specifications/simple_pid-0.2.1.gemspec +1 -1
  70. metadata +76 -63
  71. data/lib/instance_agent/codedeploy_plugin/application_specification/ace_info.rb +0 -133
  72. data/lib/instance_agent/codedeploy_plugin/application_specification/acl_info.rb +0 -163
  73. data/lib/instance_agent/codedeploy_plugin/application_specification/application_specification.rb +0 -142
  74. data/lib/instance_agent/codedeploy_plugin/application_specification/context_info.rb +0 -23
  75. data/lib/instance_agent/codedeploy_plugin/application_specification/file_info.rb +0 -23
  76. data/lib/instance_agent/codedeploy_plugin/application_specification/linux_permission_info.rb +0 -121
  77. data/lib/instance_agent/codedeploy_plugin/application_specification/mode_info.rb +0 -66
  78. data/lib/instance_agent/codedeploy_plugin/application_specification/range_info.rb +0 -134
  79. data/lib/instance_agent/codedeploy_plugin/application_specification/script_info.rb +0 -27
  80. data/lib/instance_agent/codedeploy_plugin/codedeploy_control.rb +0 -72
  81. data/lib/instance_agent/codedeploy_plugin/command_executor.rb +0 -357
  82. data/lib/instance_agent/codedeploy_plugin/command_poller.rb +0 -170
  83. data/lib/instance_agent/codedeploy_plugin/deployment_specification.rb +0 -150
  84. data/lib/instance_agent/codedeploy_plugin/hook_executor.rb +0 -206
  85. data/lib/instance_agent/codedeploy_plugin/install_instruction.rb +0 -374
  86. data/lib/instance_agent/codedeploy_plugin/installer.rb +0 -143
  87. data/lib/instance_agent/codedeploy_plugin/request_helper.rb +0 -28
  88. data/test/instance_agent/codedeploy_plugin/application_specification_test.rb +0 -1710
  89. data/test/instance_agent/codedeploy_plugin/install_instruction_test.rb +0 -566
  90. data/test/instance_agent/codedeploy_plugin/request_helper_test.rb +0 -37
  91. data/vendor/specifications/jamespath-0.5.1.gemspec +0 -35
@@ -0,0 +1,42 @@
1
+ module InstanceAgent
2
+ module Plugins
3
+ module CodeDeployPlugin
4
+ class OnPremisesConfig
5
+ def self.configure
6
+ file_path = InstanceAgent::Config.config[:on_premises_config_file]
7
+ file_config = nil
8
+ if File.exists?(file_path) && File.readable?(file_path)
9
+ begin
10
+ file_config = YAML.load(File.read(file_path)).symbolize_keys
11
+ rescue
12
+ log(:error, "Invalid on premises config file")
13
+ raise "Invalid on premises config file"
14
+ end
15
+ else
16
+ log(:info, "On Premises config file does not exist or not readable")
17
+ end
18
+ if file_config
19
+ if file_config[:region]
20
+ ENV['AWS_REGION'] = file_config[:region]
21
+ end
22
+ if file_config[:aws_access_key_id]
23
+ ENV['AWS_ACCESS_KEY'] = file_config[:aws_access_key_id]
24
+ end
25
+ if file_config[:aws_secret_access_key]
26
+ ENV['AWS_SECRET_KEY'] = file_config[:aws_secret_access_key]
27
+ end
28
+ if file_config[:iam_user_arn]
29
+ ENV['AWS_HOST_IDENTIFIER'] = file_config[:iam_user_arn]
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.log(severity, message)
35
+ raise ArgumentError, "Unknown severity #{severity.inspect}" unless InstanceAgent::Log::SEVERITIES.include?(severity.to_s)
36
+ InstanceAgent::Log.send(severity.to_sym, "#{message}")
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'command_poller'
2
+ require_relative 'command_executor'
3
+ require_relative 'deployment_specification'
4
+ require_relative 'application_specification/application_specification'
5
+ require_relative 'application_specification/file_info'
6
+ require_relative 'application_specification/script_info'
7
+ require_relative 'application_specification/linux_permission_info'
8
+ require_relative 'application_specification/mode_info'
9
+ require_relative 'application_specification/acl_info'
10
+ require_relative 'application_specification/ace_info'
11
+ require_relative 'application_specification/context_info'
12
+ require_relative 'application_specification/range_info'
13
+ require_relative 'installer'
14
+ require_relative 'install_instruction'
15
+ require_relative 'codedeploy_control'
16
+ require_relative 'hook_executor'
17
+ require_relative 'onpremise_config'
@@ -4,16 +4,31 @@ require 'process_manager/child'
4
4
  module InstanceAgent
5
5
  module Runner
6
6
  class Child < ProcessManager::Daemon::Child
7
- AGENTS = {
8
- 0 => InstanceAgent::CodeDeployPlugin::CommandPoller
9
- }
10
7
 
11
8
  attr_accessor :runner
12
9
 
10
+ def load_plugins(plugins)
11
+ ProcessManager::Log.debug("Registering Plugins: #{plugins.inspect}.")
12
+ plugins.each do |plugin|
13
+ plugin_dir = File.expand_path("../plugins/#{plugin}/register_plugin", File.dirname(__FILE__))
14
+ ProcessManager::Log.debug("Loading plugin #{plugin} from #{plugin_dir}")
15
+ begin
16
+ require plugin_dir
17
+ rescue LoadError => e
18
+ ProcessManager::Log.error("Plugin #{plugin} could not be loaded: #{e.message}.")
19
+ raise
20
+ end
21
+ end
22
+ registered_plugins = InstanceAgent::Agent::Base.plugins
23
+ ProcessManager::Log.debug("Registered Plugins: #{registered_plugins.inspect}.")
24
+ Hash[registered_plugins.map.with_index { |value, index| [index, value] }]
25
+ end
26
+
13
27
  def prepare_run
28
+ @plugins ||= load_plugins(ProcessManager::Config.config[:plugins] || ["codedeploy"])
14
29
  validate_index
15
30
  with_error_handling do
16
- @runner = AGENTS[index].runner
31
+ @runner = @plugins[index].runner
17
32
  ProcessManager.set_program_name(description)
18
33
  end
19
34
  end
@@ -33,7 +48,7 @@ module InstanceAgent
33
48
  end
34
49
 
35
50
  def validate_index
36
- raise ArgumentError, "Invalid index #{index.inspect}: only 0-2 possible" unless AGENTS.keys.include?(index)
51
+ raise ArgumentError, "Invalid index #{index.inspect}" unless @plugins.keys.include?(index)
37
52
  end
38
53
 
39
54
  def with_error_handling
@@ -1,7 +1,5 @@
1
1
  # encoding: UTF-8
2
2
  require 'process_manager/master'
3
- require 'instance_agent/codedeploy_plugin/request_helper'
4
- require 'instance_agent/codedeploy_plugin/codedeploy_control'
5
3
  require 'instance_metadata'
6
4
 
7
5
  module InstanceAgent
@@ -22,18 +20,6 @@ module InstanceAgent
22
20
  ProcessManager::Config.config[:program_name]
23
21
  end
24
22
 
25
- def validate_ssl_config
26
- if !InstanceMetadata.instance_id.blank?
27
- region = InstanceMetadata.region
28
- request_helper = InstanceAgent::CodeDeployPlugin::RequestHelper.new(:deploy_control_client => InstanceAgent::CodeDeployPlugin::CodeDeployControl.new(:region => region))
29
-
30
- if (errors = request_helper.verify_clients_configuration)
31
- errors.each{|error| ProcessManager::Log.error("Stopping CodeDeploy agent. " + error)}
32
- end
33
- self.class.abort unless errors.empty?
34
- end
35
- end
36
-
37
23
  def self.log_file
38
24
  File.join(ProcessManager::Config.config[:log_dir], "#{ProcessManager::Config.config[:program_name]}.log")
39
25
  end
@@ -72,7 +58,7 @@ module InstanceAgent
72
58
  end
73
59
 
74
60
  def kill_children(sig)
75
- children.each do |index, child_pid|
61
+ children.each do |index, child_pid|
76
62
  begin
77
63
  Process.kill(sig, child_pid)
78
64
  rescue Errno::ESRCH
@@ -96,6 +82,7 @@ module InstanceAgent
96
82
  end
97
83
  end
98
84
  end
85
+
99
86
  end
100
87
 
101
88
  end
@@ -38,8 +38,8 @@ class InstanceMetadata
38
38
  Net::HTTP.start(IP_ADDRESS, PORT) do |http|
39
39
  response = http.get(path)
40
40
  if response.code.to_i != 200
41
- InstanceAgent::Log.send(:debug, "HTTP error from metadata service, code #{reponse.code}")
42
- raise "HTTP error from metadata service, code #{reponse.code}"
41
+ InstanceAgent::Log.send(:debug, "HTTP error from metadata service, code #{response.code}")
42
+ raise "HTTP error from metadata service, code #{response.code}"
43
43
  end
44
44
  return response.body
45
45
  end
@@ -10,7 +10,7 @@ class CertificateHelper
10
10
  ca_chain_file = generate_ca_chain()
11
11
 
12
12
  ENV['AWS_REGION'] = 'us-east-1'
13
- InstanceAgent::CodeDeployPlugin::DeploymentSpecification.init_cert_store(ca_chain_file)
13
+ InstanceAgent::Plugins::CodeDeployPlugin::DeploymentSpecification.init_cert_store(ca_chain_file)
14
14
  end
15
15
 
16
16
  def generate_root()
@@ -1,5 +1,6 @@
1
1
  # require the code
2
2
  require 'instance_agent'
3
+ require 'instance_agent/plugins/codedeploy/register_plugin'
3
4
 
4
5
  class InstanceAgentTestCase < Test::Unit::TestCase
5
6
  include ActiveSupport::Testing::Assertions
@@ -24,26 +24,39 @@ class InstanceAgentBaseTest < InstanceAgentTestCase
24
24
  end
25
25
 
26
26
  context 'rescues exceptions when running perform' do
27
+ setup do
28
+ @base.stubs(:log).with { |v1, v2| v1.eql?(:debug) }
29
+ end
30
+
27
31
  should 'rescue Aws::Errors::MissingCredentialsError' do
28
32
  @base.stubs(:perform).raises Aws::Errors::MissingCredentialsError
29
- @base.expects(:sleep).with 0
33
+ @base.expects(:sleep).with(9)
30
34
  @base.expects(:log).with(:error, "Missing credentials - please check if this instance was started with an IAM instance profile")
31
35
  assert_nothing_raised(Aws::Errors::MissingCredentialsError) { @base.run }
32
36
  end
33
37
 
34
38
  should 'rescue Aws::Errors::ServiceError' do
35
39
  @base.stubs(:perform).raises Aws::Errors::ServiceError.new(nil, "http error")
36
- @base.expects(:sleep).with(0)
40
+ @base.expects(:sleep).with(9)
37
41
  @base.expects(:log).with { |v1, v2| v1.eql?(:error) && v2 =~ /Cannot reach InstanceService/ }
38
42
  assert_nothing_raised(Aws::Errors::ServiceError) { @base.run }
39
43
  end
40
44
 
41
45
  should 'rescue all other types of exception' do
42
46
  @base.stubs(:perform).raises Exception
43
- @base.expects(:sleep).with(0)
47
+ @base.expects(:sleep).with(9)
44
48
  @base.expects(:log).with { |v1, v2| v1.eql?(:error) && v2 =~ /Error during perform/ }
45
49
  assert_nothing_raised(Exception) { @base.run }
46
50
  end
51
+
52
+ should 'back off on repeated exceptions' do
53
+ @base.stubs(:perform).raises Exception
54
+ @base.expects(:sleep).with(9)
55
+ @base.expects(:sleep).with(12)
56
+ @base.expects(:log).twice.with { |v1, v2| v1.eql?(:error) && v2 =~ /Error during perform/ }
57
+ assert_nothing_raised(Exception) { @base.run }
58
+ assert_nothing_raised(Exception) { @base.run }
59
+ end
47
60
  end
48
61
  end
49
62
  end
@@ -26,7 +26,8 @@ class InstanceAgentConfigTest < InstanceAgentTestCase
26
26
  :instance_service_port => nil,
27
27
  :wait_between_runs => 30,
28
28
  :wait_after_error => 30,
29
- :codedeploy_test_profile => 'prod'
29
+ :codedeploy_test_profile => 'prod',
30
+ :on_premises_config_file => '/etc/codedeploy-agent/conf/codedeploy.onpremises.yml'
30
31
  }, InstanceAgent::Config.config)
31
32
  end
32
33
 
@@ -0,0 +1,1713 @@
1
+ require 'test_helper'
2
+ require 'ostruct'
3
+ require 'yaml'
4
+
5
+ module InstanceAgent
6
+ module Plugins
7
+ module CodeDeployPlugin
8
+ module ApplicationSpecification
9
+ class ApplicationSpecificationTest < InstanceAgentTestCase
10
+ context 'The Application Specification' do
11
+ setup do
12
+ @test_revision_id = 'bar'
13
+ end
14
+
15
+ private
16
+
17
+ def make_app_spec
18
+ ApplicationSpecification.new(YAML.load(@app_spec_string), {:revision_id => @test_revision_id})
19
+ end
20
+
21
+ context "With missing version" do
22
+ setup do
23
+ @app_spec_string = <<-END
24
+ os: linux
25
+ END
26
+ end
27
+
28
+ should "raise an exception" do
29
+ assert_raised_with_message('unsupported version: ',AppSpecValidationException) do
30
+ make_app_spec()
31
+ end
32
+ end
33
+ end
34
+
35
+ context "With invalid version" do
36
+ setup do
37
+ @app_spec_string = <<-END
38
+ version: invalid
39
+ os: linux
40
+ END
41
+ end
42
+
43
+ should "raise an exception" do
44
+ assert_raised_with_message('unsupported version: invalid',AppSpecValidationException) do
45
+ make_app_spec()
46
+ end
47
+ end
48
+ end
49
+
50
+ context "With missing os" do
51
+ setup do
52
+ @app_spec_string = <<-END
53
+ version: 0.0
54
+ END
55
+ end
56
+
57
+ should "raise an exception" do
58
+ assert_raised_with_message('unsupported os: ',AppSpecValidationException) do
59
+ make_app_spec()
60
+ end
61
+ end
62
+ end
63
+
64
+ context "With invalid os" do
65
+ setup do
66
+ @app_spec_string = <<-END
67
+ version: 0.0
68
+ os: unsupported
69
+ END
70
+ end
71
+
72
+ should "raise an exception" do
73
+ assert_raised_with_message('unsupported os: unsupported',AppSpecValidationException) do
74
+ make_app_spec()
75
+ end
76
+ end
77
+ end
78
+
79
+ context "With no hooks or files" do
80
+ setup do
81
+ @app_spec_string = "version: 0.0\nos: linux\n"
82
+ end
83
+ should "Return an empty hooks hash" do
84
+ app_spec = make_app_spec
85
+ assert_equal({}, app_spec.hooks)
86
+ end
87
+ should "Return an empty files array" do
88
+ app_spec = make_app_spec
89
+ assert_equal([], app_spec.files)
90
+ end
91
+ end
92
+
93
+ context "With a single complete hook" do
94
+ setup do
95
+ #A single test script with all parameters
96
+ @app_spec_string = <<-END
97
+ version: 0.0
98
+ os: linux
99
+ hooks:
100
+ test_hook:
101
+ - location: test_location_1
102
+ runas: foo
103
+ timeout: 30
104
+ END
105
+ end
106
+ should "Return a collection containing test script 1" do
107
+ app_spec = make_app_spec
108
+ assert_not_equal nil, app_spec.hooks
109
+ assert_equal ['test_location_1'] , app_spec.hooks["test_hook"].map(&:location)
110
+ assert_equal ['foo'] , app_spec.hooks["test_hook"].map(&:runas)
111
+ assert_equal [30] , app_spec.hooks["test_hook"].map(&:timeout)
112
+ end
113
+ end
114
+
115
+ context "With two complete hooks" do
116
+ setup do
117
+ #A pair of test scripts with all parameters
118
+ @app_spec_string = <<-END
119
+ version: 0.0
120
+ os: linux
121
+ hooks:
122
+ test_hook:
123
+ - location: test_location_1
124
+ runas: foo
125
+ timeout: 30
126
+ - location: test_location_2
127
+ runas: foo2
128
+ timeout: 30
129
+ END
130
+ end
131
+
132
+ should "Return a collection containing test script 1 and test script 2" do
133
+ app_spec = make_app_spec
134
+ assert_not_equal nil, app_spec.hooks
135
+ assert_equal ['test_location_1', 'test_location_2'] , app_spec.hooks["test_hook"].map(&:location)
136
+ end
137
+ end
138
+
139
+ context "With partial hooks (just a runas)" do
140
+ setup do
141
+ #A test script with just a location
142
+ #A test script with location and runas
143
+ @app_spec_string = <<-END
144
+ version: 0.0
145
+ os: linux
146
+ hooks:
147
+ test_hook:
148
+ - location: test_location
149
+ - location: test_location_lr
150
+ runas: foo3
151
+ END
152
+ end
153
+
154
+ should "Return a collection containing the two scripts in order" do
155
+ app_spec = make_app_spec()
156
+ assert_not_equal nil, app_spec.hooks
157
+ assert_equal [nil, 'foo3'] , app_spec.hooks["test_hook"].map(&:runas)
158
+ end
159
+ end
160
+
161
+ context "With partial hooks (just a timeout)" do
162
+ setup do
163
+ #A test script with just a location
164
+ #A test script with location and timeout
165
+ @app_spec_string = <<-END
166
+ version: 0.0
167
+ os: linux
168
+ hooks:
169
+ test_hook:
170
+ - location: test_location
171
+ - location: test_location_lt
172
+ timeout: 30
173
+ END
174
+ end
175
+
176
+ should "Return a collection containing the two scripts in order" do
177
+ app_spec = make_app_spec()
178
+ assert_not_equal nil, app_spec.hooks
179
+ assert_equal [3600, 30] , app_spec.hooks["test_hook"].map(&:timeout)
180
+ end
181
+ end
182
+
183
+ context "With missing location data" do
184
+ setup do
185
+ #A test script without a location
186
+ @app_spec_string = <<-END
187
+ version: 0.0
188
+ os: linux
189
+ hooks:
190
+ test_hook:
191
+ - timeout: 30
192
+ END
193
+ end
194
+
195
+ should "raise an exception" do
196
+ assert_raised_with_message('script provided without a location value',AppSpecValidationException) do
197
+ make_app_spec()
198
+ end
199
+ end
200
+ end
201
+
202
+ context "With non numerical timeout data" do
203
+ setup do
204
+ #A test script with bad timeout data
205
+ @app_spec_string = <<-END
206
+ version: 0.0
207
+ os: linux
208
+ hooks:
209
+ test_hook:
210
+ - location: test_location
211
+ timeout: foo
212
+ END
213
+ end
214
+
215
+ should "raise an exception" do
216
+ assert_raised_with_message('Timeout needs to be an integer greater than 0',AppSpecValidationException) do
217
+ make_app_spec()
218
+ end
219
+ end
220
+ end
221
+
222
+ context "App spec has a file mapping" do
223
+ context "file map contains a single file" do
224
+ setup do
225
+ @app_spec_string = <<-END
226
+ version: 0.0
227
+ os: linux
228
+ files:
229
+ - source: test_source
230
+ destination: test_destination
231
+ END
232
+ end
233
+
234
+ should "return a hash containing the file mapping objects" do
235
+ app_spec = make_app_spec
236
+ assert_not_equal nil, app_spec.files
237
+ assert_equal ['test_source'] , app_spec.files.map(&:source)
238
+ assert_equal ['test_destination'] , app_spec.files.map(&:destination)
239
+ end
240
+ end
241
+
242
+ context "file map contains multiple files" do
243
+ setup do
244
+ @app_spec_string = <<-END
245
+ version: 0.0
246
+ os: linux
247
+ files:
248
+ - source: test_source
249
+ destination: test_destination
250
+ - source: test_source_2
251
+ destination: test_destination_2
252
+ END
253
+ end
254
+
255
+ should "return a hash containing the file mapping objects" do
256
+ app_spec = make_app_spec
257
+ assert_not_equal nil, app_spec.files
258
+ assert_equal ['test_source', 'test_source_2'] , app_spec.files.map(&:source)
259
+ assert_equal ['test_destination','test_destination_2'] , app_spec.files.map(&:destination)
260
+ end
261
+ end
262
+
263
+ context "file map is missing a destination" do
264
+ setup do
265
+ @app_spec_string = <<-END
266
+ version: 0.0
267
+ os: linux
268
+ files:
269
+ - source: test_source
270
+ END
271
+ end
272
+
273
+ should "raise and AppSpecValidationException" do
274
+ assert_raised_with_message('File needs to have a destination',AppSpecValidationException) do
275
+ make_app_spec()
276
+ end
277
+ end
278
+ end
279
+
280
+ context "file map is missing a source" do
281
+ setup do
282
+ @app_spec_string = <<-END
283
+ version: 0.0
284
+ os: linux
285
+ files:
286
+ - destination: test_destination
287
+ END
288
+ end
289
+
290
+ should "raise and AppSpecValidationException" do
291
+ assert_raised_with_message('File needs to have a source',AppSpecValidationException) do
292
+ make_app_spec()
293
+ end
294
+ end
295
+ end
296
+ end
297
+
298
+ context "With permission without object set" do
299
+ setup do
300
+ @app_spec_string = <<-END
301
+ version: 0.0
302
+ os: linux
303
+ permissions:
304
+ - pattern: test
305
+ END
306
+ end
307
+
308
+ should "raise an exception" do
309
+ assert_raised_with_message('permission provided without a object value',AppSpecValidationException) do
310
+ make_app_spec()
311
+ end
312
+ end
313
+ end
314
+
315
+ context "With permission pattern of **" do
316
+ setup do
317
+ @app_spec_string = <<-END
318
+ version: 0.0
319
+ os: linux
320
+ permissions:
321
+ - object: '/'
322
+ pattern: '**'
323
+ END
324
+ end
325
+
326
+ should "match all objects" do
327
+ app_spec = make_app_spec()
328
+ assert_equal '**', app_spec.permissions[0].pattern
329
+ end
330
+ end
331
+
332
+ context "With multiple permissions" do
333
+ setup do
334
+ @app_spec_string = <<-END
335
+ version: 0.0
336
+ os: linux
337
+ permissions:
338
+ - object: '/'
339
+ - object: test
340
+ - object: more
341
+ END
342
+ end
343
+
344
+ should "match all objects" do
345
+ app_spec = make_app_spec()
346
+ assert_equal 3, app_spec.permissions.length
347
+ assert_equal '/', app_spec.permissions[0].object
348
+ assert_equal "test", app_spec.permissions[1].object
349
+ assert_equal "more", app_spec.permissions[2].object
350
+ end
351
+ end
352
+
353
+ context "With permissions with pattern" do
354
+ setup do
355
+ @app_spec_string = <<-END
356
+ version: 0.0
357
+ os: linux
358
+ permissions:
359
+ - object: '/'
360
+ pattern: 'glob'
361
+ END
362
+ end
363
+
364
+ should "raise when validated as file permission" do
365
+ app_spec = make_app_spec()
366
+ assert_raised_with_message('Attempt to use pattern glob when assigning permissions to file /',AppSpecValidationException) do
367
+ app_spec.permissions[0].validate_file_permission
368
+ end
369
+ end
370
+ end
371
+
372
+ context "With permissions with except" do
373
+ setup do
374
+ @app_spec_string = <<-END
375
+ version: 0.0
376
+ os: linux
377
+ permissions:
378
+ - object: '/'
379
+ except:
380
+ - 'glob'
381
+ END
382
+ end
383
+
384
+ should "raise when validated as file permission" do
385
+ app_spec = make_app_spec()
386
+ assert_raised_with_message('Attempt to use except ["glob"] when assigning permissions to file /',AppSpecValidationException) do
387
+ app_spec.permissions[0].validate_file_permission
388
+ end
389
+ end
390
+ end
391
+
392
+ context "With permissions" do
393
+ setup do
394
+ @app_spec_string = <<-END
395
+ version: 0.0
396
+ os: linux
397
+ permissions:
398
+ - object: '/'
399
+ END
400
+ end
401
+
402
+ should "not raise when validated as file permission" do
403
+ app_spec = make_app_spec()
404
+ assert_nothing_raised do
405
+ app_spec.permissions[0].validate_file_permission
406
+ end
407
+ end
408
+ end
409
+
410
+ context "With permissions with pattern without file type" do
411
+ setup do
412
+ @app_spec_string = <<-END
413
+ version: 0.0
414
+ os: linux
415
+ permissions:
416
+ - object: '/'
417
+ pattern: 'glob'
418
+ type:
419
+ - "directory"
420
+ END
421
+ end
422
+
423
+ should "not raise when validated as file permission" do
424
+ app_spec = make_app_spec()
425
+ assert_nothing_raised do
426
+ app_spec.permissions[0].validate_file_permission
427
+ end
428
+ end
429
+ end
430
+
431
+ context "With permissions with acl without default ace" do
432
+ setup do
433
+ @app_spec_string = <<-END
434
+ version: 0.0
435
+ os: linux
436
+ permissions:
437
+ - object: '/'
438
+ acls:
439
+ - 'user:name:rwx'
440
+ END
441
+ end
442
+
443
+ should "be able to validate as a file acl" do
444
+ app_spec = make_app_spec()
445
+ assert_nothing_raised do
446
+ app_spec.permissions[0].validate_file_acl("test")
447
+ end
448
+ end
449
+ end
450
+
451
+ context "With permissions with acl with default ace" do
452
+ setup do
453
+ @app_spec_string = <<-END
454
+ version: 0.0
455
+ os: linux
456
+ permissions:
457
+ - object: '/'
458
+ acls:
459
+ - 'd:user:name:rwx'
460
+ END
461
+ end
462
+
463
+ should "be able to validate as a file acl" do
464
+ app_spec = make_app_spec()
465
+ assert_raised_with_message('Attempt to set default acl default:user:name:rwx on file test',RuntimeError) do
466
+ app_spec.permissions[0].validate_file_acl("test")
467
+ end
468
+ end
469
+ end
470
+
471
+ context "With valid permission object" do
472
+ setup do
473
+ @app_spec_string = <<-END
474
+ version: 0.0
475
+ os: linux
476
+ permissions:
477
+ - object: 'test'
478
+ pattern: 'prefix*'
479
+ except: ['*ReadMe', '*.tmp']
480
+ type: ['file']
481
+ owner: 'bob'
482
+ group: 'dev'
483
+ mode: 6763
484
+ acls:
485
+ - 'u:henry:7'
486
+ - 'd:g:manager:rw'
487
+ context:
488
+ name: 'user_u'
489
+ type: 'unconfined_t'
490
+ range: 's3-s5:c0.c7,c13'
491
+ END
492
+ end
493
+
494
+ should "match pattern when appropriate" do
495
+ app_spec = make_app_spec()
496
+ permission = app_spec.permissions[0]
497
+
498
+ assert permission.matches_pattern?(File.expand_path("test/prefix")), "Should match test/prefix for pattern"
499
+ assert permission.matches_pattern?(File.expand_path("test/prefix_matches")), "Should match test/prefix_matches for pattern"
500
+ assert !permission.matches_pattern?(File.expand_path("test/prefix/does_not_match")), "Should not match test/prefix/does_not_match for pattern"
501
+ assert !permission.matches_pattern?(File.expand_path("testprefix/")), "Should not match testprefix/ for pattern"
502
+ assert !permission.matches_pattern?(File.expand_path("tst/prefix")), "Should not match tst/prefix for pattern"
503
+ assert !permission.matches_pattern?(File.expand_path("test/not_prefix")), "Should not match test/not_prefix for pattern"
504
+ end
505
+
506
+ should "match except when appropriate" do
507
+ app_spec = make_app_spec()
508
+ permission = app_spec.permissions[0]
509
+
510
+ assert permission.matches_except?(File.expand_path("test/this.tmp")), "Should match test/this.tmp for except"
511
+ assert permission.matches_except?(File.expand_path("test/this_ReadMe")), "Should match test/this_ReadMe for except"
512
+ assert !permission.matches_except?(File.expand_path("test/prefix/does_not_match.tmp")), "Should not match test/prefix/does_not_match.tmp for except"
513
+ assert !permission.matches_except?(File.expand_path("testprefix/")), "Should not match testprefix/ for except"
514
+ assert !permission.matches_except?(File.expand_path("tst/prefix")), "Should not match tst/prefix for except"
515
+ assert !permission.matches_except?(File.expand_path("test/not_match")), "Should not match test/not_match for except"
516
+ end
517
+
518
+ should "set fields correctly" do
519
+ app_spec = make_app_spec()
520
+ permission = app_spec.permissions[0]
521
+ assert_equal 'test', permission.object
522
+ assert_equal 'prefix*', permission.pattern
523
+ assert_equal ['*ReadMe', '*.tmp'], permission.except
524
+ assert_equal ['file'], permission.type
525
+ assert_equal 'bob', permission.owner
526
+ assert_equal 'dev', permission.group
527
+
528
+ mode = permission.mode
529
+ assert_equal '6763', mode.mode
530
+ assert_equal '3', mode.world
531
+ assert_equal false, mode.world_readable
532
+ assert_equal true, mode.world_writable
533
+ assert_equal true, mode.world_executable
534
+ assert_equal '6', mode.group
535
+ assert_equal true, mode.group_readable
536
+ assert_equal true, mode.group_writable
537
+ assert_equal false, mode.group_executable
538
+ assert_equal '7', mode.owner
539
+ assert_equal true, mode.owner_readable
540
+ assert_equal true, mode.owner_writable
541
+ assert_equal true, mode.owner_executable
542
+ assert_equal true, mode.setuid
543
+ assert_equal true, mode.setgid
544
+ assert_equal false, mode.sticky
545
+
546
+ acl = permission.acls
547
+ assert_equal 2, acl.aces.length
548
+ ace = acl.aces[0]
549
+ assert_equal false, ace.default
550
+ assert_equal 'user', ace.type
551
+ assert_equal 'henry', ace.name
552
+ assert_equal true, ace.read
553
+ assert_equal true, ace.write
554
+ assert_equal true, ace.execute
555
+
556
+ ace = acl.aces[1]
557
+ assert_equal true, ace.default
558
+ assert_equal 'group', ace.type
559
+ assert_equal 'manager', ace.name
560
+ assert_equal true, ace.read
561
+ assert_equal true, ace.write
562
+ assert_equal false, ace.execute
563
+
564
+ context = permission.context
565
+ assert_equal 'user_u', context.user
566
+ assert_equal nil, context.role
567
+ assert_equal 'unconfined_t', context.type
568
+
569
+ range = context.range
570
+ assert_equal 3, range.low_sensitivity
571
+ assert_equal 5, range.high_sensitivity
572
+
573
+ categories = range.categories
574
+ assert_equal 9, categories.length
575
+ [(0..7).to_a,13].flatten!.each do |category|
576
+ assert_equal true, categories.include?(category), "Unable to find expected category #{category}"
577
+ end
578
+ end
579
+ end
580
+
581
+ context "With permission with acl with ace with too few parts" do
582
+ setup do
583
+ @app_spec_string = <<-END
584
+ version: 0.0
585
+ os: linux
586
+ permissions:
587
+ - object: 'test/'
588
+ acls:
589
+ - '7'
590
+ END
591
+ end
592
+
593
+ should "raise an exception" do
594
+ assert_raised_with_message('invalid acl entry 7',AppSpecValidationException) do
595
+ make_app_spec()
596
+ end
597
+ end
598
+ end
599
+
600
+ context "With permission with acl with ace with too many parts" do
601
+ setup do
602
+ @app_spec_string = <<-END
603
+ version: 0.0
604
+ os: linux
605
+ permissions:
606
+ - object: 'test/'
607
+ acls:
608
+ - 'd:u:bob:7:rwx'
609
+ END
610
+ end
611
+
612
+ should "raise an exception" do
613
+ assert_raised_with_message('invalid acl entry d:u:bob:7:rwx',AppSpecValidationException) do
614
+ make_app_spec()
615
+ end
616
+ end
617
+ end
618
+
619
+ context "With permission with acl with ace with invalid first part" do
620
+ setup do
621
+ @app_spec_string = <<-END
622
+ version: 0.0
623
+ os: linux
624
+ permissions:
625
+ - object: 'test/'
626
+ acls:
627
+ - 'invalid:u:bob:7:rwx'
628
+ END
629
+ end
630
+
631
+ should "raise an exception" do
632
+ assert_raised_with_message('invalid acl entry invalid:u:bob:7:rwx',AppSpecValidationException) do
633
+ make_app_spec()
634
+ end
635
+ end
636
+ end
637
+
638
+ context "With permission with acl with ace with invalid second part" do
639
+ setup do
640
+ @app_spec_string = <<-END
641
+ version: 0.0
642
+ os: linux
643
+ permissions:
644
+ - object: 'test/'
645
+ acls:
646
+ - 'd:invalid:bob:7:rwx'
647
+ END
648
+ end
649
+
650
+ should "raise an exception" do
651
+ assert_raised_with_message('invalid acl entry d:invalid:bob:7:rwx',AppSpecValidationException) do
652
+ make_app_spec()
653
+ end
654
+ end
655
+ end
656
+
657
+ context "With permission with acl with ace with default as first and second part" do
658
+ setup do
659
+ @app_spec_string = <<-END
660
+ version: 0.0
661
+ os: linux
662
+ permissions:
663
+ - object: 'test/'
664
+ acls:
665
+ - 'd:d:bob:7:rwx'
666
+ END
667
+ end
668
+
669
+ should "raise an exception" do
670
+ assert_raised_with_message('invalid acl entry d:d:bob:7:rwx',AppSpecValidationException) do
671
+ make_app_spec()
672
+ end
673
+ end
674
+ end
675
+
676
+ context "With permission with acl with ace with mask with name" do
677
+ setup do
678
+ @app_spec_string = <<-END
679
+ version: 0.0
680
+ os: linux
681
+ permissions:
682
+ - object: 'test/'
683
+ acls:
684
+ - 'mask:name:rwx'
685
+ END
686
+ end
687
+
688
+ should "raise an exception" do
689
+ assert_raised_with_message('invalid acl entry mask:name:rwx',AppSpecValidationException) do
690
+ make_app_spec()
691
+ end
692
+ end
693
+ end
694
+
695
+ context "With permission with acl with ace with other with name" do
696
+ setup do
697
+ @app_spec_string = <<-END
698
+ version: 0.0
699
+ os: linux
700
+ permissions:
701
+ - object: 'test/'
702
+ acls:
703
+ - 'd:other:name:rwx'
704
+ END
705
+ end
706
+
707
+ should "raise an exception" do
708
+ assert_raised_with_message('invalid acl entry d:other:name:rwx',AppSpecValidationException) do
709
+ make_app_spec()
710
+ end
711
+ end
712
+ end
713
+
714
+ context "With permission with acl with ace with invalid permission character" do
715
+ setup do
716
+ @app_spec_string = <<-END
717
+ version: 0.0
718
+ os: linux
719
+ permissions:
720
+ - object: 'test/'
721
+ acls:
722
+ - 'user:bob:rwxd'
723
+ END
724
+ end
725
+
726
+ should "raise an exception" do
727
+ assert_raised_with_message('unrecognized permission character d in user:bob:rwxd',AppSpecValidationException) do
728
+ make_app_spec()
729
+ end
730
+ end
731
+ end
732
+
733
+ context "With permission with acl with valid ace with 4 parts" do
734
+ setup do
735
+ @app_spec_string = <<-END
736
+ version: 0.0
737
+ os: linux
738
+ permissions:
739
+ - object: 'test/'
740
+ acls:
741
+ - 'd:u:bob:rwx'
742
+ - 'default:g:dev:rw'
743
+ END
744
+ end
745
+
746
+ should "generate correct fields" do
747
+ app_spec = make_app_spec()
748
+
749
+ acl = app_spec.permissions[0].acls
750
+ assert_equal 2, acl.aces.length
751
+
752
+ ace = acl.aces[0]
753
+ assert_equal true, ace.default
754
+ assert_equal 'user', ace.type
755
+ assert_equal 'bob', ace.name
756
+ assert_equal true, ace.read
757
+ assert_equal true, ace.write
758
+ assert_equal true, ace.execute
759
+
760
+ ace = acl.aces[1]
761
+ assert_equal true, ace.default
762
+ assert_equal 'group', ace.type
763
+ assert_equal 'dev', ace.name
764
+ assert_equal true, ace.read
765
+ assert_equal true, ace.write
766
+ assert_equal false, ace.execute
767
+ end
768
+ end
769
+
770
+ context "With permission with acl with valid ace with 3 parts" do
771
+ setup do
772
+ @app_spec_string = <<-END
773
+ version: 0.0
774
+ os: linux
775
+ permissions:
776
+ - object: 'test/'
777
+ acls:
778
+ - 'd:bob:rwx'
779
+ - 'default::rw'
780
+ - 'm::7'
781
+ - 'mask::7'
782
+ - 'g:dev:7'
783
+ - 'group:dev:7'
784
+ - 'u:bob:7'
785
+ - 'user:bob:7'
786
+ - 'u:mask:7'
787
+ - 'u:other:7'
788
+ END
789
+ end
790
+
791
+ should "generate correct fields" do
792
+ app_spec = make_app_spec()
793
+
794
+ acl = app_spec.permissions[0].acls
795
+ assert_equal 10, acl.aces.length
796
+
797
+ ace = acl.aces[0]
798
+ assert_equal true, ace.default
799
+ assert_equal 'user', ace.type
800
+ assert_equal 'bob', ace.name
801
+ assert_equal true, ace.read
802
+ assert_equal true, ace.write
803
+ assert_equal true, ace.execute
804
+
805
+ ace = acl.aces[1]
806
+ assert_equal true, ace.default
807
+ assert_equal 'user', ace.type
808
+ assert_equal '', ace.name
809
+ assert_equal true, ace.read
810
+ assert_equal true, ace.write
811
+ assert_equal false, ace.execute
812
+
813
+ ace = acl.aces[2]
814
+ assert_equal false, ace.default
815
+ assert_equal 'mask', ace.type
816
+ assert_equal '', ace.name
817
+ assert_equal true, ace.read
818
+ assert_equal true, ace.write
819
+ assert_equal true, ace.execute
820
+
821
+ ace = acl.aces[3]
822
+ assert_equal false, ace.default
823
+ assert_equal 'mask', ace.type
824
+ assert_equal '', ace.name
825
+ assert_equal true, ace.read
826
+ assert_equal true, ace.write
827
+ assert_equal true, ace.execute
828
+
829
+ ace = acl.aces[4]
830
+ assert_equal false, ace.default
831
+ assert_equal 'group', ace.type
832
+ assert_equal 'dev', ace.name
833
+ assert_equal true, ace.read
834
+ assert_equal true, ace.write
835
+ assert_equal true, ace.execute
836
+
837
+ ace = acl.aces[5]
838
+ assert_equal false, ace.default
839
+ assert_equal 'group', ace.type
840
+ assert_equal 'dev', ace.name
841
+ assert_equal true, ace.read
842
+ assert_equal true, ace.write
843
+ assert_equal true, ace.execute
844
+
845
+ ace = acl.aces[6]
846
+ assert_equal false, ace.default
847
+ assert_equal 'user', ace.type
848
+ assert_equal 'bob', ace.name
849
+ assert_equal true, ace.read
850
+ assert_equal true, ace.write
851
+ assert_equal true, ace.execute
852
+
853
+ ace = acl.aces[7]
854
+ assert_equal false, ace.default
855
+ assert_equal 'user', ace.type
856
+ assert_equal 'bob', ace.name
857
+ assert_equal true, ace.read
858
+ assert_equal true, ace.write
859
+ assert_equal true, ace.execute
860
+
861
+ ace = acl.aces[8]
862
+ assert_equal false, ace.default
863
+ assert_equal 'user', ace.type
864
+ assert_equal 'mask', ace.name
865
+ assert_equal true, ace.read
866
+ assert_equal true, ace.write
867
+ assert_equal true, ace.execute
868
+
869
+ ace = acl.aces[9]
870
+ assert_equal false, ace.default
871
+ assert_equal 'user', ace.type
872
+ assert_equal 'other', ace.name
873
+ assert_equal true, ace.read
874
+ assert_equal true, ace.write
875
+ assert_equal true, ace.execute
876
+ end
877
+ end
878
+
879
+ context "With permission with acl with valid ace with 2 parts" do
880
+ setup do
881
+ @app_spec_string = <<-END
882
+ version: 0.0
883
+ os: linux
884
+ permissions:
885
+ - object: 'test/'
886
+ acls:
887
+ - 'bob:0'
888
+ - 'm:7'
889
+ - 'mask:'
890
+ END
891
+ end
892
+
893
+ should "generate correct fields" do
894
+ app_spec = make_app_spec()
895
+
896
+ acl = app_spec.permissions[0].acls
897
+ assert_equal 3, acl.aces.length
898
+
899
+ ace = acl.aces[0]
900
+ assert_equal false, ace.default
901
+ assert_equal 'user', ace.type
902
+ assert_equal 'bob', ace.name
903
+ assert_equal false, ace.read
904
+ assert_equal false, ace.write
905
+ assert_equal false, ace.execute
906
+
907
+ ace = acl.aces[1]
908
+ assert_equal false, ace.default
909
+ assert_equal 'mask', ace.type
910
+ assert_equal '', ace.name
911
+ assert_equal true, ace.read
912
+ assert_equal true, ace.write
913
+ assert_equal true, ace.execute
914
+
915
+ ace = acl.aces[2]
916
+ assert_equal false, ace.default
917
+ assert_equal 'mask', ace.type
918
+ assert_equal '', ace.name
919
+ assert_equal false, ace.read
920
+ assert_equal false, ace.write
921
+ assert_equal false, ace.execute
922
+ end
923
+ end
924
+
925
+ context "With permission with context with invalid sensitivity range" do
926
+ setup do
927
+ @app_spec_string = <<-END
928
+ version: 0.0
929
+ os: linux
930
+ permissions:
931
+ - object: 'test/'
932
+ context:
933
+ name: 'name'
934
+ type: 'type'
935
+ range: 's3-s2:c0'
936
+ END
937
+ end
938
+
939
+ should "raise an exception" do
940
+ assert_raised_with_message('invalid sensitivity range in s3-s2:c0',AppSpecValidationException) do
941
+ make_app_spec()
942
+ end
943
+ end
944
+ end
945
+
946
+ context "With permission with context with missing sensitivity range part" do
947
+ setup do
948
+ @app_spec_string = <<-END
949
+ version: 0.0
950
+ os: linux
951
+ permissions:
952
+ - object: 'test/'
953
+ context:
954
+ name: 'name'
955
+ type: 'type'
956
+ range: 's3-:c0'
957
+ END
958
+ end
959
+
960
+ should "raise an exception" do
961
+ assert_raised_with_message('invalid range part s3-',AppSpecValidationException) do
962
+ make_app_spec()
963
+ end
964
+ end
965
+ end
966
+
967
+ context "With single sensitivity" do
968
+ setup do
969
+ @app_spec_string = <<-END
970
+ version: 0.0
971
+ os: linux
972
+ permissions:
973
+ - object: 'test/'
974
+ context:
975
+ type: 'type'
976
+ range: 's5'
977
+ END
978
+ end
979
+
980
+ should "generate correct fields" do
981
+ app_spec = make_app_spec()
982
+
983
+ assert_equal 5, app_spec.permissions[0].context.range.low_sensitivity
984
+ assert_equal 5, app_spec.permissions[0].context.range.high_sensitivity
985
+ assert_equal nil, app_spec.permissions[0].context.range.categories
986
+ end
987
+ end
988
+
989
+ context "With permission with context with missing sensitivity" do
990
+ setup do
991
+ @app_spec_string = <<-END
992
+ version: 0.0
993
+ os: linux
994
+ permissions:
995
+ - object: 'test/'
996
+ context:
997
+ name: 'name'
998
+ type: 'type'
999
+ range: ':c0'
1000
+ END
1001
+ end
1002
+
1003
+ should "raise an exception" do
1004
+ assert_raised_with_message('invalid range part :c0',AppSpecValidationException) do
1005
+ make_app_spec()
1006
+ end
1007
+ end
1008
+ end
1009
+
1010
+ context "With permission with context with missing sensitivity value" do
1011
+ setup do
1012
+ @app_spec_string = <<-END
1013
+ version: 0.0
1014
+ os: linux
1015
+ permissions:
1016
+ - object: 'test/'
1017
+ context:
1018
+ name: 'name'
1019
+ type: 'type'
1020
+ range: 's'
1021
+ END
1022
+ end
1023
+
1024
+ should "raise an exception" do
1025
+ assert_raised_with_message('invalid sensitivity s',AppSpecValidationException) do
1026
+ make_app_spec()
1027
+ end
1028
+ end
1029
+ end
1030
+
1031
+ context "With permission with context with negative sensitivity value" do
1032
+ setup do
1033
+ @app_spec_string = <<-END
1034
+ version: 0.0
1035
+ os: linux
1036
+ permissions:
1037
+ - object: 'test/'
1038
+ context:
1039
+ name: 'name'
1040
+ type: 'type'
1041
+ range: 's0-s-1'
1042
+ END
1043
+ end
1044
+
1045
+ should "raise an exception" do
1046
+ assert_raised_with_message('invalid sensitivity s-1',AppSpecValidationException) do
1047
+ make_app_spec()
1048
+ end
1049
+ end
1050
+ end
1051
+
1052
+ context "With permission with context with invalid sensitivity" do
1053
+ setup do
1054
+ @app_spec_string = <<-END
1055
+ version: 0.0
1056
+ os: linux
1057
+ permissions:
1058
+ - object: 'test/'
1059
+ context:
1060
+ name: 'name'
1061
+ type: 'type'
1062
+ range: 'sd3'
1063
+ END
1064
+ end
1065
+
1066
+ should "raise an exception" do
1067
+ assert_raised_with_message('invalid sensitivity sd3',AppSpecValidationException) do
1068
+ make_app_spec()
1069
+ end
1070
+ end
1071
+ end
1072
+
1073
+ context "With permission with context with invalid sensitivity 2" do
1074
+ setup do
1075
+ @app_spec_string = <<-END
1076
+ version: 0.0
1077
+ os: linux
1078
+ permissions:
1079
+ - object: 'test/'
1080
+ context:
1081
+ name: 'name'
1082
+ type: 'type'
1083
+ range: 'd3'
1084
+ END
1085
+ end
1086
+
1087
+ should "raise an exception" do
1088
+ assert_raised_with_message('invalid sensitivity d3',AppSpecValidationException) do
1089
+ make_app_spec()
1090
+ end
1091
+ end
1092
+ end
1093
+
1094
+ context "With permission with context with invalid category range" do
1095
+ setup do
1096
+ @app_spec_string = <<-END
1097
+ version: 0.0
1098
+ os: linux
1099
+ permissions:
1100
+ - object: 'test/'
1101
+ context:
1102
+ name: 'name'
1103
+ type: 'type'
1104
+ range: 's0:c2.c1'
1105
+ END
1106
+ end
1107
+
1108
+ should "raise an exception" do
1109
+ assert_raised_with_message('invalid category range c2.c1',AppSpecValidationException) do
1110
+ make_app_spec()
1111
+ end
1112
+ end
1113
+ end
1114
+
1115
+ context "With permission with context with missing category range part" do
1116
+ setup do
1117
+ @app_spec_string = <<-END
1118
+ version: 0.0
1119
+ os: linux
1120
+ permissions:
1121
+ - object: 'test/'
1122
+ context:
1123
+ name: 'name'
1124
+ type: 'type'
1125
+ range: 's0:c2.'
1126
+ END
1127
+ end
1128
+
1129
+ should "raise an exception" do
1130
+ assert_raised_with_message('invalid range part c2.',AppSpecValidationException) do
1131
+ make_app_spec()
1132
+ end
1133
+ end
1134
+ end
1135
+
1136
+ context "With valid category" do
1137
+ setup do
1138
+ @app_spec_string = <<-END
1139
+ version: 0.0
1140
+ os: linux
1141
+ permissions:
1142
+ - object: 'test/'
1143
+ context:
1144
+ type: 'type'
1145
+ range: 's0:c0.c1,c15,c7.c9'
1146
+ END
1147
+ end
1148
+
1149
+ should "generate correct fields" do
1150
+ app_spec = make_app_spec()
1151
+
1152
+ categories = app_spec.permissions[0].context.range.categories
1153
+ assert_equal 6, categories.length
1154
+ [(0..1).to_a, (7..9).to_a, 15].flatten!.each do |category|
1155
+ assert_equal true, categories.include?(category), "Unable to find expected category #{category}"
1156
+ end
1157
+ end
1158
+ end
1159
+
1160
+ context "With permission with context with missing category" do
1161
+ setup do
1162
+ @app_spec_string = <<-END
1163
+ version: 0.0
1164
+ os: linux
1165
+ permissions:
1166
+ - object: 'test/'
1167
+ context:
1168
+ name: 'name'
1169
+ type: 'type'
1170
+ range: 's0:'
1171
+ END
1172
+ end
1173
+
1174
+ should "raise an exception" do
1175
+ assert_raised_with_message('invalid range part s0:',AppSpecValidationException) do
1176
+ make_app_spec()
1177
+ end
1178
+ end
1179
+ end
1180
+
1181
+ context "With permission with context with missing category value" do
1182
+ setup do
1183
+ @app_spec_string = <<-END
1184
+ version: 0.0
1185
+ os: linux
1186
+ permissions:
1187
+ - object: 'test/'
1188
+ context:
1189
+ name: 'name'
1190
+ type: 'type'
1191
+ range: 's0:c'
1192
+ END
1193
+ end
1194
+
1195
+ should "raise an exception" do
1196
+ assert_raised_with_message('invalid category c',AppSpecValidationException) do
1197
+ make_app_spec()
1198
+ end
1199
+ end
1200
+ end
1201
+
1202
+ context "With permission with context with negative category value" do
1203
+ setup do
1204
+ @app_spec_string = <<-END
1205
+ version: 0.0
1206
+ os: linux
1207
+ permissions:
1208
+ - object: 'test/'
1209
+ context:
1210
+ name: 'name'
1211
+ type: 'type'
1212
+ range: 's0:c-1'
1213
+ END
1214
+ end
1215
+
1216
+ should "raise an exception" do
1217
+ assert_raised_with_message('invalid category c-1',AppSpecValidationException) do
1218
+ make_app_spec()
1219
+ end
1220
+ end
1221
+ end
1222
+
1223
+ context "With permission with context with category value above 1023" do
1224
+ setup do
1225
+ @app_spec_string = <<-END
1226
+ version: 0.0
1227
+ os: linux
1228
+ permissions:
1229
+ - object: 'test/'
1230
+ context:
1231
+ name: 'name'
1232
+ type: 'type'
1233
+ range: 's0:c1024'
1234
+ END
1235
+ end
1236
+
1237
+ should "raise an exception" do
1238
+ assert_raised_with_message('invalid category c1024',AppSpecValidationException) do
1239
+ make_app_spec()
1240
+ end
1241
+ end
1242
+ end
1243
+
1244
+ context "With permission with context without type" do
1245
+ setup do
1246
+ @app_spec_string = <<-END
1247
+ version: 0.0
1248
+ os: linux
1249
+ permissions:
1250
+ - object: 'test/'
1251
+ context:
1252
+ name: 'name'
1253
+ END
1254
+ end
1255
+
1256
+ should "raise an exception" do
1257
+ assert_raised_with_message("invalid context type required {\"name\"=>\"name\"}",AppSpecValidationException) do
1258
+ make_app_spec()
1259
+ end
1260
+ end
1261
+ end
1262
+
1263
+ context "With permission with context with invalid category" do
1264
+ setup do
1265
+ @app_spec_string = <<-END
1266
+ version: 0.0
1267
+ os: linux
1268
+ permissions:
1269
+ - object: 'test/'
1270
+ context:
1271
+ name: 'name'
1272
+ type: 'type'
1273
+ range: 's0:cd3'
1274
+ END
1275
+ end
1276
+
1277
+ should "raise an exception" do
1278
+ assert_raised_with_message('invalid category cd3',AppSpecValidationException) do
1279
+ make_app_spec()
1280
+ end
1281
+ end
1282
+ end
1283
+
1284
+ context "With permission with context with invalid category 2" do
1285
+ setup do
1286
+ @app_spec_string = <<-END
1287
+ version: 0.0
1288
+ os: linux
1289
+ permissions:
1290
+ - object: 'test/'
1291
+ context:
1292
+ name: 'name'
1293
+ type: 'type'
1294
+ range: 's0:d3'
1295
+ END
1296
+ end
1297
+
1298
+ should "raise an exception" do
1299
+ assert_raised_with_message('invalid category d3',AppSpecValidationException) do
1300
+ make_app_spec()
1301
+ end
1302
+ end
1303
+ end
1304
+
1305
+ context "With permission with context with duplicate categories" do
1306
+ setup do
1307
+ @app_spec_string = <<-END
1308
+ version: 0.0
1309
+ os: linux
1310
+ permissions:
1311
+ - object: 'test/'
1312
+ context:
1313
+ name: 'name'
1314
+ type: 'type'
1315
+ range: 's0:c0.c2,c1'
1316
+ END
1317
+ end
1318
+
1319
+ should "raise an exception" do
1320
+ assert_raised_with_message('duplicate categories',AppSpecValidationException) do
1321
+ make_app_spec()
1322
+ end
1323
+ end
1324
+ end
1325
+
1326
+ context "With permission with mode with 5 digits" do
1327
+ setup do
1328
+ @app_spec_string = <<-END
1329
+ version: 0.0
1330
+ os: linux
1331
+ permissions:
1332
+ - object: 'test/'
1333
+ mode: 12345
1334
+ END
1335
+ end
1336
+
1337
+ should "raise an exception" do
1338
+ assert_raised_with_message('permission mode length incorrect: 12345',AppSpecValidationException) do
1339
+ make_app_spec()
1340
+ end
1341
+ end
1342
+ end
1343
+
1344
+ context "With permission with mode with 2 digits" do
1345
+ setup do
1346
+ @app_spec_string = <<-END
1347
+ version: 0.0
1348
+ os: linux
1349
+ permissions:
1350
+ - object: 'test/'
1351
+ mode: 12
1352
+ END
1353
+ end
1354
+
1355
+ should "fill in needed zeros" do
1356
+ app_spec = make_app_spec()
1357
+
1358
+ mode = app_spec.permissions[0].mode
1359
+ assert_equal '012', mode.mode
1360
+ assert_equal '2', mode.world
1361
+ assert_equal false, mode.world_readable
1362
+ assert_equal true, mode.world_writable
1363
+ assert_equal false, mode.world_executable
1364
+ assert_equal '1', mode.group
1365
+ assert_equal false, mode.group_readable
1366
+ assert_equal false, mode.group_writable
1367
+ assert_equal true, mode.group_executable
1368
+ assert_equal '0', mode.owner
1369
+ assert_equal false, mode.owner_readable
1370
+ assert_equal false, mode.owner_writable
1371
+ assert_equal false, mode.owner_executable
1372
+ assert_equal false, mode.setuid
1373
+ assert_equal false, mode.setgid
1374
+ assert_equal false, mode.sticky
1375
+ end
1376
+ end
1377
+
1378
+ context "With permission with mode with invalid char" do
1379
+ setup do
1380
+ @app_spec_string = <<-END
1381
+ version: 0.0
1382
+ os: linux
1383
+ permissions:
1384
+ - object: 'test/'
1385
+ mode: 12a
1386
+ END
1387
+ end
1388
+
1389
+ should "raise an exception" do
1390
+ assert_raised_with_message('invalid character a in permission mode 12a',AppSpecValidationException) do
1391
+ make_app_spec()
1392
+ end
1393
+ end
1394
+ end
1395
+
1396
+ context "With permission with valid modes" do
1397
+ setup do
1398
+ @app_spec_string = <<-END
1399
+ version: 0.0
1400
+ os: linux
1401
+ permissions:
1402
+ - object: 'test/'
1403
+ mode: 7777
1404
+ - object: 'test1/'
1405
+ mode: 0000
1406
+ - object: 'test2/'
1407
+ mode: 777
1408
+ END
1409
+ end
1410
+
1411
+ should "generate correct fields" do
1412
+ app_spec = make_app_spec()
1413
+
1414
+ mode = app_spec.permissions[0].mode
1415
+ assert_equal '7777', mode.mode
1416
+ assert_equal '7', mode.world
1417
+ assert_equal true, mode.world_readable
1418
+ assert_equal true, mode.world_writable
1419
+ assert_equal true, mode.world_executable
1420
+ assert_equal '7', mode.group
1421
+ assert_equal true, mode.group_readable
1422
+ assert_equal true, mode.group_writable
1423
+ assert_equal true, mode.group_executable
1424
+ assert_equal '7', mode.owner
1425
+ assert_equal true, mode.owner_readable
1426
+ assert_equal true, mode.owner_writable
1427
+ assert_equal true, mode.owner_executable
1428
+ assert_equal true, mode.setuid
1429
+ assert_equal true, mode.setgid
1430
+ assert_equal true, mode.sticky
1431
+
1432
+ mode = app_spec.permissions[1].mode
1433
+ assert_equal '000', mode.mode
1434
+ assert_equal '0', mode.world
1435
+ assert_equal false, mode.world_readable
1436
+ assert_equal false, mode.world_writable
1437
+ assert_equal false, mode.world_executable
1438
+ assert_equal '0', mode.group
1439
+ assert_equal false, mode.group_readable
1440
+ assert_equal false, mode.group_writable
1441
+ assert_equal false, mode.group_executable
1442
+ assert_equal '0', mode.owner
1443
+ assert_equal false, mode.owner_readable
1444
+ assert_equal false, mode.owner_writable
1445
+ assert_equal false, mode.owner_executable
1446
+ assert_equal false, mode.setuid
1447
+ assert_equal false, mode.setgid
1448
+ assert_equal false, mode.sticky
1449
+
1450
+ mode = app_spec.permissions[2].mode
1451
+ assert_equal '777', mode.mode
1452
+ assert_equal '7', mode.world
1453
+ assert_equal true, mode.world_readable
1454
+ assert_equal true, mode.world_writable
1455
+ assert_equal true, mode.world_executable
1456
+ assert_equal '7', mode.group
1457
+ assert_equal true, mode.group_readable
1458
+ assert_equal true, mode.group_writable
1459
+ assert_equal true, mode.group_executable
1460
+ assert_equal '7', mode.owner
1461
+ assert_equal true, mode.owner_readable
1462
+ assert_equal true, mode.owner_writable
1463
+ assert_equal true, mode.owner_executable
1464
+ assert_equal false, mode.setuid
1465
+ assert_equal false, mode.setgid
1466
+ assert_equal false, mode.sticky
1467
+ end
1468
+ end
1469
+
1470
+ context "When acl is present" do
1471
+ setup do
1472
+ @app_spec_string = <<-END
1473
+ version: 0.0
1474
+ os: linux
1475
+ permissions:
1476
+ - object: 'test/'
1477
+ acls: []
1478
+ END
1479
+ app_spec = make_app_spec()
1480
+ @acl = app_spec.permissions[0].acls
1481
+ end
1482
+
1483
+ should "be able to add and clear additional aces" do
1484
+ @acl.add_ace("d:henry:4")
1485
+ @acl.clear_additional
1486
+ end
1487
+
1488
+ should "be able to get an empty acl" do
1489
+ assert_equal [], @acl.get_acl
1490
+ end
1491
+
1492
+ should "be able to get added aces in the acl" do
1493
+ @acl.add_ace("d:henry:4")
1494
+ assert_equal 1, @acl.get_acl.length
1495
+ @acl.clear_additional
1496
+ end
1497
+
1498
+ should "not be able to get a default ace" do
1499
+ assert_equal nil, @acl.get_default_ace
1500
+ end
1501
+
1502
+ should "be able to get an added default ace" do
1503
+ @acl.add_ace("d:henry:4")
1504
+ assert_not_nil @acl.get_default_ace
1505
+ @acl.clear_additional
1506
+ end
1507
+
1508
+ should "not be able to get a default group ace" do
1509
+ assert_equal nil, @acl.get_default_group_ace
1510
+ end
1511
+
1512
+ should "be able to get an added default group ace" do
1513
+ @acl.add_ace("d:g::4")
1514
+ assert_not_nil @acl.get_default_group_ace
1515
+ @acl.clear_additional
1516
+ end
1517
+
1518
+ should "not have a base named ace" do
1519
+ assert !@acl.has_base_named?
1520
+ end
1521
+
1522
+ should "have a base named ace when added" do
1523
+ @acl.add_ace("bob:4")
1524
+ assert @acl.has_base_named?
1525
+ @acl.clear_additional
1526
+ end
1527
+
1528
+ should "not have a base mask ace" do
1529
+ assert !@acl.has_base_mask?
1530
+ end
1531
+
1532
+ should "have a base mask ace when added" do
1533
+ @acl.add_ace("m:4")
1534
+ assert @acl.has_base_mask?
1535
+ @acl.clear_additional
1536
+ end
1537
+
1538
+ should "not have a default ace" do
1539
+ assert !@acl.has_default?
1540
+ end
1541
+
1542
+ should "have a default ace when added" do
1543
+ @acl.add_ace("d:bob:4")
1544
+ assert @acl.has_default?
1545
+ @acl.clear_additional
1546
+ end
1547
+
1548
+ should "not have a default user ace" do
1549
+ assert !@acl.has_default_user?
1550
+ end
1551
+
1552
+ should "have a default user ace when added" do
1553
+ @acl.add_ace("d::4")
1554
+ assert @acl.has_default_user?
1555
+ @acl.clear_additional
1556
+ end
1557
+
1558
+ should "not have a default group ace" do
1559
+ assert !@acl.has_default_group?
1560
+ end
1561
+
1562
+ should "have a default group ace when added" do
1563
+ @acl.add_ace("d:g::4")
1564
+ assert @acl.has_default_group?
1565
+ @acl.clear_additional
1566
+ end
1567
+
1568
+ should "not have a default other ace" do
1569
+ assert !@acl.has_default_other?
1570
+ end
1571
+
1572
+ should "have a default other ace when added" do
1573
+ @acl.add_ace("d:o:4")
1574
+ assert @acl.has_default_other?
1575
+ @acl.clear_additional
1576
+ end
1577
+
1578
+ should "not have a default named ace" do
1579
+ assert !@acl.has_default_named?
1580
+ end
1581
+
1582
+ should "have a default named ace when added" do
1583
+ @acl.add_ace("d:bob:4")
1584
+ assert @acl.has_default_named?
1585
+ @acl.clear_additional
1586
+ end
1587
+
1588
+ should "not have a default mask ace" do
1589
+ assert !@acl.has_default_mask?
1590
+ end
1591
+
1592
+ should "have a default mask ace when added" do
1593
+ @acl.add_ace("d:m:4")
1594
+ assert @acl.has_default_mask?
1595
+ @acl.clear_additional
1596
+ end
1597
+ end
1598
+
1599
+ context "When acl is present with existing aces" do
1600
+ setup do
1601
+ @app_spec_string = <<-END
1602
+ version: 0.0
1603
+ os: linux
1604
+ permissions:
1605
+ - object: 'test/'
1606
+ acls:
1607
+ - 'bob:6'
1608
+ - 'm:6'
1609
+ - 'd:bob:0'
1610
+ - 'd::3'
1611
+ - 'd:g::4'
1612
+ - 'd:o:3'
1613
+ - 'd:m:7'
1614
+ END
1615
+ app_spec = make_app_spec()
1616
+ @acl = app_spec.permissions[0].acls
1617
+ end
1618
+
1619
+ should "be able to get the acl" do
1620
+ assert_equal 7, @acl.get_acl.length
1621
+ end
1622
+
1623
+ should "be able to get default ace" do
1624
+ assert_not_nil @acl.get_default_ace
1625
+ end
1626
+
1627
+ should "be able to get default group ace" do
1628
+ assert_not_nil @acl.get_default_group_ace
1629
+ end
1630
+
1631
+ should "have base named ace" do
1632
+ assert_not_nil @acl.has_base_named?
1633
+ end
1634
+
1635
+ should "have base mask ace" do
1636
+ assert_not_nil @acl.has_base_mask?
1637
+ end
1638
+
1639
+ should "have default ace" do
1640
+ assert_not_nil @acl.has_default?
1641
+ end
1642
+
1643
+ should "have default user ace" do
1644
+ assert_not_nil @acl.has_default_user?
1645
+ end
1646
+
1647
+ should "have default group ace" do
1648
+ assert_not_nil @acl.has_default_group?
1649
+ end
1650
+
1651
+ should "have default other ace" do
1652
+ assert_not_nil @acl.has_default_other?
1653
+ end
1654
+
1655
+ should "have default named ace" do
1656
+ assert_not_nil @acl.has_default_named?
1657
+ end
1658
+
1659
+ should "have default mask ace" do
1660
+ assert_not_nil @acl.has_default_mask?
1661
+ end
1662
+ end
1663
+ end
1664
+
1665
+ context "With a ContextInfo" do
1666
+ should "with a simple range" do
1667
+ info = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new({"type"=>"type","range"=>"s3"})
1668
+ assert_equal "s3", info.range.get_range
1669
+ end
1670
+
1671
+ should "with a complex range" do
1672
+ info = InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new({"type"=>"type","range"=>"s3-s7:c5,c8.c10,c17"})
1673
+ assert_equal "s3-s7:c5,c8.c10,c17", info.range.get_range
1674
+ end
1675
+ end
1676
+
1677
+ context "With a ACEInfo" do
1678
+ should "not raise if made internal with base entries" do
1679
+ assert_nothing_raised do
1680
+ InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("u::7", true)
1681
+ InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("g::7", true)
1682
+ InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("o::7", true)
1683
+ end
1684
+ end
1685
+
1686
+ should "raise when not internal and has base user" do
1687
+ assert_raised_with_message("use mode to set the base acl entry u::7",AppSpecValidationException) do
1688
+ InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("u::7")
1689
+ end
1690
+ end
1691
+
1692
+ should "raise when not internal and has base group" do
1693
+ assert_raised_with_message("use mode to set the base acl entry g::7",AppSpecValidationException) do
1694
+ InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("g::7")
1695
+ end
1696
+ end
1697
+
1698
+ should "raise when not internal and has base other" do
1699
+ assert_raised_with_message("use mode to set the base acl entry o:7",AppSpecValidationException) do
1700
+ InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("o:7")
1701
+ end
1702
+ end
1703
+
1704
+ should "be able to get the ace" do
1705
+ assert_equal("default:user:bob:rwx", InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("d:u:bob:7").get_ace)
1706
+ assert_equal("mask::---", InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AceInfo.new("m:0").get_ace)
1707
+ end
1708
+ end
1709
+ end
1710
+ end
1711
+ end
1712
+ end
1713
+ end