fulmar 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b386982a3ab26556fd6efa59581e8431d768234
4
- data.tar.gz: 8c06a406efb3477836ceba7c40ed3d418949ae7f
3
+ metadata.gz: b5773cc97e0e9d8615d47d72ee31a9d9b0438e6b
4
+ data.tar.gz: 9f2702193748a1561335ef13eacad2ba07acd6d6
5
5
  SHA512:
6
- metadata.gz: 4d4fd4a803b9c83b4a74dd3042501567ce731c57ee6b414094205f8ebe6d5ae83c62395e4d788d680c54ed9218181ee73570f15c79cda5faa24513fed811f3b8
7
- data.tar.gz: cd186eb2339d4b36ea5dc171c849201b34f4cd7308454bb8ddf55de431d7e2614676d9413582d3344260fe5dc2dbcf9e2d13955e2575df88794c8da9215fbe66
6
+ metadata.gz: eff45b616da762576e948fe24d2c0ebc201a8f3cea1e134c687c5e6d5d77db2ee4e5bd7a224c82720d737eaf046f6caef7b463deafa4a330f8d0554661f9ad54
7
+ data.tar.gz: 0aeedda8a394b79420f082ebc3a91c2ad08ab4e20e2c9217ba32e3a0597bab662d2f243afb8ce4986690ffcfcef220e4f175edc1531e831fef60d9d5e67bafb4
data/.gitignore CHANGED
@@ -14,6 +14,8 @@
14
14
  mkmf.log
15
15
  /.idea/
16
16
  *.gem
17
+ *.bak
18
+ spec/fixtures/ssh_config/temp_*
17
19
  /.vagrant
18
20
  /Vagrantfile
19
21
  /vendor
data/fulmar.gemspec CHANGED
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'fulmar-shell', '~>1', '>=1.7.0'
25
25
  spec.add_runtime_dependency 'colorize', '~>0'
26
26
  spec.add_runtime_dependency 'activesupport', '~> 5.0'
27
+ spec.add_runtime_dependency 'diffy', '~> 3.2'
27
28
  end
@@ -94,22 +94,9 @@ module Fulmar
94
94
  @data[:hosts]
95
95
  end
96
96
 
97
- # Check for a feature
98
- # @todo Do we still need this? Maybe replace this with a "plugin?" method?
99
- def feature?(feature)
100
- return @data[:features].include? feature.to_s unless @data[:features].nil?
101
- case feature
102
- when :maria
103
- key? :maria
104
- else
105
- false
106
- end
107
- end
108
-
109
97
  # Checks if a configuration key exists in one of the targets
110
98
  def key?(key)
111
- each { |_env, _target, data| return true unless data[key].nil? }
112
- false
99
+ ready? ? @data[:environments][@environment][@target].key?(key) : false
113
100
  end
114
101
 
115
102
  # Merge another configuration into the currently active one
@@ -0,0 +1,21 @@
1
+ relevant_options = [:rsync, :local_path, :remote_path, :host, :hostname, :shared, :templates,
2
+ :ssh_config, :type]
3
+ relevant_options.each do |option|
4
+ target_test "empty configuration settings #{option}" do |config|
5
+ if config.key?(option) && config[option].nil?
6
+ next {
7
+ severity: :warning,
8
+ message: "config setting '#{option}' is set to nil/null, this might overwrite default settings"
9
+ }
10
+ end
11
+ end
12
+ end
13
+
14
+ target_test 'test if at least one shared directory exists' do |config|
15
+ if config[:type] == 'rsync_with_versions' && (config[:shared].nil? || config[:shared].empty?)
16
+ next {
17
+ severity: warning,
18
+ message: 'config does not contain a shared directory for versioning'
19
+ }
20
+ end
21
+ end
@@ -43,3 +43,17 @@ namespace :test do
43
43
  info "Feelin' fine." if error_count == 0
44
44
  end
45
45
  end
46
+
47
+ if File.exist?("#{Fulmar::Infrastructure::Service::SSHConfigService::DEFAULT_CONFIG_FILE}.bak")
48
+ namespace :revert do
49
+ task :ssh_config do
50
+ config = Fulmar::Infrastructure::Service::SSHConfigService::DEFAULT_CONFIG_FILE
51
+ backup = "#{config}.bak"
52
+ temp = "#{config}.tmp"
53
+ FileUtils.cp config, temp
54
+ FileUtils.cp backup, config
55
+ FileUtils.mv temp, backup
56
+ Fulmar::Infrastructure::Service::SSHConfigService.new(config).show_diff
57
+ end
58
+ end
59
+ end
@@ -190,6 +190,8 @@ module Fulmar
190
190
  # @return [true, false] success
191
191
  def add_shared
192
192
  commands = [] # Collect all remote commands first, then execute them in one step to avoid reconnecting very often
193
+ return true if @config[:shared].nil? || @config[:shared].empty?
194
+
193
195
  @config[:shared].each do |path|
194
196
  commands << "mkdir -p \"#{release_dir}/#{File.dirname(path)}\""
195
197
 
@@ -1,45 +1,81 @@
1
1
  require 'fulmar/shell'
2
+ require 'pp'
3
+ require 'diffy'
4
+ require 'active_support/core_ext/object/blank'
2
5
 
3
6
  module Fulmar
4
7
  module Infrastructure
5
8
  module Service
6
9
  # Adds entries to the ssh config and checks for existing ones
7
10
  class SSHConfigService
8
- CONFIG_FILE = "#{ENV['HOME']}/.ssh/config"
9
- KNOWN_HOST_FILE = "#{ENV['HOME']}/.ssh/known_hosts"
11
+ DEFAULT_CONFIG_FILE = "#{ENV['HOME']}/.ssh/config"
12
+ DEFAULT_KNOWN_HOSTS_FILE = "#{ENV['HOME']}/.ssh/known_hosts"
10
13
 
11
- def initialize(config)
14
+ attr_accessor :config_file, :known_host_file, :quiet
15
+
16
+ def initialize(config, config_file = DEFAULT_CONFIG_FILE, known_hosts_file = DEFAULT_KNOWN_HOSTS_FILE)
12
17
  @config = config
18
+ @config_file = config_file
19
+ @known_hosts_file = known_hosts_file
20
+ @quiet = false
21
+ end
22
+
23
+ def changed?
24
+ File.read(@config_file) != File.read("#{@config_file}.bak")
25
+ end
26
+
27
+ def diff(type = :color)
28
+ before = File.read("#{@config_file}.bak")
29
+ after = File.read(@config_file)
30
+ Diffy::Diff.new(before, after, context: 3).to_s(type)
31
+ end
32
+
33
+ def show_diff
34
+ return if @quiet || !changed?
35
+ puts 'You ssh host configuration changed: '
36
+ puts '--------------- DIFF ---------------'
37
+ puts diff
38
+ puts '--------------- /DIFF --------------'
39
+ puts 'You can revert these changes by running "fulmar revert:ssh_config"'
13
40
  end
14
41
 
15
42
  def add_hosts
43
+ backup_file
16
44
  @config.hosts.values.each do |data|
17
45
  unless config_valid?(data)
18
46
  puts "Skipping #{data[:hostname]}, config not sufficient." if @config[:debug]
19
47
  next
20
48
  end
21
- if host_exists?(data[:hostname])
22
- puts "Host #{data[:hostname]} exists, skipping..." if @config[:debug]
23
- else
24
- add_host(data[:hostname], data[:ssh_config])
25
- end
49
+ edit_host(data[:hostname], data[:ssh_config])
26
50
  end
51
+ show_diff
27
52
  end
28
53
 
29
54
  def remove_known_host(hostname)
30
- input_file = File.open(KNOWN_HOST_FILE, 'r')
31
- output_file = File.open(KNOWN_HOST_FILE + '.temp', 'w')
55
+ input_file = File.open(@known_hosts_file, 'r')
56
+ output_file = File.open(@known_hosts_file + '.temp', 'w')
32
57
  while (line = input_file.gets)
33
58
  output_file.puts(line) unless /^\[?#{hostname.gsub('.', '\\.')}(?:\]:\d+)?[ ,]/.match(line)
34
59
  end
35
60
  input_file.close
36
61
  output_file.close
37
- FileUtils.mv(KNOWN_HOST_FILE + '.temp', KNOWN_HOST_FILE)
62
+ FileUtils.mv(@known_hosts_file + '.temp', @known_hosts_file)
63
+ end
64
+
65
+ def edit_host(hostname, ssh_config)
66
+ data = read_file
67
+ new_data = block_before(data.clone, hostname) +
68
+ host_entry(hostname, ssh_config) +
69
+ block_after(data, hostname)
70
+
71
+ File.open(@config_file, 'w') do |file|
72
+ file.puts new_data.join("\n")
73
+ end
38
74
  end
39
75
 
40
76
  # Parses the users ssh config for an existing hostname
41
77
  def host_exists?(hostname)
42
- config_file = File.open(CONFIG_FILE, 'r')
78
+ config_file = File.open(@config_file, 'r')
43
79
  while (line = config_file.gets)
44
80
  if /\s*Host #{hostname.gsub('.', '\\.')}\s*$/.match(line)
45
81
  config_file.close
@@ -53,29 +89,108 @@ module Fulmar
53
89
  # Adds a host to the ssh config file
54
90
  def add_host(hostname, ssh_config = {})
55
91
  puts "Adding host #{hostname}..." if @config[:debug]
56
- config_file = File.open(CONFIG_FILE, 'a')
92
+ config_file = File.open(@config_file, 'a')
57
93
 
58
94
  unless ssh_config[:IdentityFile].blank? or ssh_config[:IdentityFile][0, 1] == '/'
59
95
  ssh_config[:IdentityFile] = @config.base_path + '/' + ssh_config[:IdentityFile]
60
96
  end
61
97
 
62
- config_file.puts "\n" # Add some space between this and the second last entry
63
- config_file.puts "# Automatically generated by fulmar for project '#{@config.project.description}'"
64
- config_file.puts "Host #{hostname}"
65
- ssh_config.keys.each do |key|
66
- value = ssh_config[key].to_s
67
- value = "\"#{value.gsub('"', '\\"')}\"" if value.include?(' ')
68
- config_file.puts " #{key} #{value}"
69
- end
98
+ config_file.puts host_entry(hostname, ssh_config)
70
99
 
71
100
  config_file.close
72
101
  end
73
102
 
74
- private
103
+ def backup_file
104
+ backup_filename = "#{@config_file}.bak"
105
+ FileUtils.cp @config_file, backup_filename
106
+ end
107
+
108
+ protected
109
+
110
+ def host_entry(hostname, ssh_config = {})
111
+ unless ssh_config[:IdentityFile].blank? or ssh_config[:IdentityFile][0, 1] == '/'
112
+ ssh_config[:IdentityFile] = @config.base_path + '/' + ssh_config[:IdentityFile]
113
+ end
114
+
115
+ entry = [
116
+ '', # Add some space between this and the second last entry
117
+ "# Automatically generated by fulmar for project '#{@config.project.description}'",
118
+ "Host #{hostname}"
119
+ ]
120
+ ssh_config.keys.each { |key| entry << " #{key} #{escape_value(ssh_config[key])}" }
121
+ entry << ''
122
+ entry
123
+ end
124
+
125
+ def escape_value(value)
126
+ value = value.to_s
127
+ value = "\"#{value.gsub('"', '\\"')}\"" if value.include?(' ')
128
+ value
129
+ end
130
+
131
+ def read_file
132
+ config_file_data = []
133
+ File.open(@config_file, 'r') do |file|
134
+ until file.eof?
135
+ config_file_data << file.gets.chomp
136
+ end
137
+ end
138
+ config_file_data
139
+ end
75
140
 
76
141
  def config_valid?(host_config)
77
142
  (!host_config[:hostname].blank? && !host_config[:ssh_config].nil?)
78
143
  end
144
+
145
+ def remove_trailing_newlines(data)
146
+ while !data.empty? && data.last.strip.empty?
147
+ data.pop
148
+ end
149
+ data
150
+ end
151
+
152
+ def block_before(data, hostname)
153
+ cache = []
154
+ before = []
155
+ data.each do |line|
156
+ if line.strip[0] == '#'
157
+ cache << line
158
+ else
159
+ if /^Host\s#{hostname}$/.match line.strip
160
+ return remove_trailing_newlines(before)
161
+ end
162
+ before += cache
163
+ cache = []
164
+ before << line
165
+ end
166
+ end
167
+ remove_trailing_newlines(before)
168
+ end
169
+
170
+ def block_after(data, hostname)
171
+ data = data.drop_while { |i| !/^Host\s#{hostname}$/.match(i.strip) }
172
+ return [] if data.empty?
173
+ data.shift
174
+
175
+ after = []
176
+ cache = []
177
+ write = false
178
+ data.each do |line|
179
+ if line.strip[0] == '#'
180
+ cache << line
181
+ else
182
+ if /^Host\s/.match line.strip
183
+ write = true
184
+ end
185
+ if write
186
+ after += cache
187
+ after << line
188
+ end
189
+ cache = []
190
+ end
191
+ end
192
+ remove_trailing_newlines(after)
193
+ end
79
194
  end
80
195
  end
81
196
  end
@@ -1,4 +1,4 @@
1
1
  # Provides a global version number
2
2
  module Fulmar
3
- VERSION = '2.0.2'
3
+ VERSION = '2.1.0'
4
4
  end
@@ -0,0 +1,16 @@
1
+ Host testhost examplehost foo-host
2
+ SendEnv FOO
3
+
4
+ Host testhost
5
+ Hostname testhost.example.om
6
+ Port 1234
7
+ User nobody
8
+
9
+ # trailing comment 4711
10
+
11
+ # host comment 0815
12
+ Host examplehost
13
+ Hostname example.com
14
+ IdentityFile /tmp/private.key
15
+ # ProxyCommand whoop whoop whoop
16
+ Port 4321
File without changes
@@ -0,0 +1,122 @@
1
+ require 'fulmar/infrastructure/service/ssh_config_service'
2
+
3
+ DEFAULT_CONFIG_FILE = "#{File.dirname(__FILE__)}/../../../fixtures/ssh_config/default_config.txt"
4
+ DEFAULT_KNOWN_HOSTS_FILE = "#{File.dirname(__FILE__)}/../../../fixtures/ssh_config/default_known_hosts.txt"
5
+ CONFIG_TEMP_FILE = "#{File.dirname(__FILE__)}/../../../fixtures/ssh_config/temp_config.txt"
6
+ KNOWN_HOSTS_TEMP_FILE = "#{File.dirname(__FILE__)}/../../../fixtures/ssh_config/temp_known_hosts.txt"
7
+
8
+ class MockProject
9
+ attr_accessor :name, :description
10
+ end
11
+
12
+ class MockConfig
13
+ def hosts
14
+ []
15
+ end
16
+
17
+ def project
18
+ MockProject.new
19
+ end
20
+ end
21
+
22
+ def new_service(config_file = DEFAULT_CONFIG_FILE, known_hosts_file = DEFAULT_KNOWN_HOSTS_FILE)
23
+ FileUtils.cp config_file, CONFIG_TEMP_FILE
24
+ FileUtils.cp known_hosts_file, KNOWN_HOSTS_TEMP_FILE
25
+ service = Fulmar::Infrastructure::Service::SSHConfigService.new(MockConfig.new, CONFIG_TEMP_FILE, KNOWN_HOSTS_TEMP_FILE)
26
+ service.backup_file
27
+ service
28
+ end
29
+
30
+ describe Fulmar::Infrastructure::Service::SSHConfigService do
31
+ after :each do
32
+ FileUtils.rm "#{CONFIG_TEMP_FILE}.bak"
33
+ end
34
+
35
+ describe '#edit_host' do
36
+ it 'should create a backup file' do
37
+ service = new_service
38
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
39
+ expect(File.exist?("#{CONFIG_TEMP_FILE}.bak")).to be true
40
+ end
41
+
42
+ it 'should add a host' do
43
+ service = new_service
44
+ service.edit_host('test_new', {'Hostname' => '123.example.com'})
45
+ expect(File.read(CONFIG_TEMP_FILE)).to include('testhost')
46
+ expect(File.read(CONFIG_TEMP_FILE)).to include('test_new')
47
+ end
48
+
49
+ it 'should replace an existing host' do
50
+ service = new_service
51
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
52
+ expect(File.read(CONFIG_TEMP_FILE)).not_to include('1234')
53
+ end
54
+
55
+ it 'should keep a trailing comment above the edited host entry' do
56
+ service = new_service
57
+ service.edit_host('examplehost', {'Hostname' => '123.example.com'})
58
+ expect(File.read(CONFIG_TEMP_FILE)).to include('trailing comment 4711')
59
+ end
60
+
61
+ it 'should keep replace the host comment above the edited host entry' do
62
+ service = new_service
63
+ service.edit_host('examplehost', {'Hostname' => '123.example.com'})
64
+ expect(File.read(CONFIG_TEMP_FILE)).not_to include('host comment 0815')
65
+ end
66
+
67
+ it 'should keep the number of hosts when replacing one' do
68
+ service = new_service
69
+ service.edit_host('examplehost', {'Hostname' => '123.example.com'})
70
+ before = File.read("#{CONFIG_TEMP_FILE}.bak").scan(/Host /).size
71
+ expect(File.read(CONFIG_TEMP_FILE).scan(/Host /).size).to eql(before)
72
+ end
73
+
74
+ it 'should increase the number of hosts when adding one' do
75
+ service = new_service
76
+ service.edit_host('examplehost2', {'Hostname' => '123.example.com'})
77
+ before = File.read("#{CONFIG_TEMP_FILE}.bak").scan(/Host /).size
78
+ expect(File.read(CONFIG_TEMP_FILE).scan(/Host /).size).to eql(before + 1)
79
+ end
80
+
81
+ it 'should keep comments within unaffected host blocks' do
82
+ service = new_service
83
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
84
+ expect(File.read(CONFIG_TEMP_FILE)).to include('whoop whoop whoop')
85
+ end
86
+
87
+ it 'should not add more new lines with every call' do
88
+ service = new_service
89
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
90
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
91
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
92
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
93
+ expect(File.read(CONFIG_TEMP_FILE)).not_to include("\n\n\n")
94
+ end
95
+
96
+ it 'should not change the file on a second run' do
97
+ service = new_service
98
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
99
+ service.backup_file
100
+ service.edit_host('testhost', {'Hostname' => '123.example.com'})
101
+ before = File.read("#{CONFIG_TEMP_FILE}.bak")
102
+ after = File.read(CONFIG_TEMP_FILE)
103
+ expect(after).to eql(before)
104
+ end
105
+ end
106
+
107
+ describe '#remove_trailing_newlines' do
108
+ it 'should remove multiple newlines from an array' do
109
+ service = new_service
110
+ test_array = ['test', '', '', '']
111
+ result = service.send(:remove_trailing_newlines, test_array)
112
+ expect(result).to eql(['test'])
113
+ end
114
+
115
+ it 'should not touch non-empty lines' do
116
+ service = new_service
117
+ test_array = %w[test foo bar]
118
+ result = service.send(:remove_trailing_newlines, test_array)
119
+ expect(result).to eql(test_array)
120
+ end
121
+ end
122
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fulmar
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Siegl
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-05-31 00:00:00.000000000 Z
12
+ date: 2017-07-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -87,6 +87,20 @@ dependencies:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '5.0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: diffy
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.2'
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.2'
90
104
  description: Fulmar is a task manager for deployments.
91
105
  email:
92
106
  - j.siegl@core4.de
@@ -113,6 +127,7 @@ files:
113
127
  - lib/fulmar/domain/service/config_test_service.rb
114
128
  - lib/fulmar/domain/service/config_tests/compatibility.rb
115
129
  - lib/fulmar/domain/service/config_tests/hosts.rb
130
+ - lib/fulmar/domain/service/config_tests/misc.rb
116
131
  - lib/fulmar/domain/service/config_tests/paths.rb
117
132
  - lib/fulmar/domain/service/config_tests/project.rb
118
133
  - lib/fulmar/domain/service/configuration_service.rb
@@ -139,9 +154,12 @@ files:
139
154
  - lib/fulmar/service/helper_service.rb
140
155
  - lib/fulmar/task_manager.rb
141
156
  - lib/fulmar/version.rb
157
+ - spec/fixtures/ssh_config/default_config.txt
158
+ - spec/fixtures/ssh_config/default_known_hosts.txt
142
159
  - spec/lib/fulmar/model/configuration_spec.rb
143
160
  - spec/lib/fulmar/service/helper_service_spec.rb
144
161
  - spec/lib/fulmar/service/plugin_service_spec.rb
162
+ - spec/lib/fulmar/service/ssh_config_service_spec.rb
145
163
  - spec/spec_helper.rb
146
164
  homepage: https://github.com/CORE4/fulmar
147
165
  licenses:
@@ -168,7 +186,10 @@ signing_key:
168
186
  specification_version: 4
169
187
  summary: A deployment task manager.
170
188
  test_files:
189
+ - spec/fixtures/ssh_config/default_config.txt
190
+ - spec/fixtures/ssh_config/default_known_hosts.txt
171
191
  - spec/lib/fulmar/model/configuration_spec.rb
172
192
  - spec/lib/fulmar/service/helper_service_spec.rb
173
193
  - spec/lib/fulmar/service/plugin_service_spec.rb
194
+ - spec/lib/fulmar/service/ssh_config_service_spec.rb
174
195
  - spec/spec_helper.rb