elecksee 1.1.8 → 2.0.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: 3c76cb776b49ae4be0b00557d87abbe6d82a9da7
4
- data.tar.gz: bc2bc7f0e9442eafde3541317b757411b81621ca
3
+ metadata.gz: 1fa238c10437677255df26d118e761553342b5a6
4
+ data.tar.gz: f22d8b320fde7d3a531728bf77b3613883d2e6d1
5
5
  SHA512:
6
- metadata.gz: 7a39c2bde1d990e87956fc80ca5681a018a0e81e139435cda4aa66dce4095c2dc2be68873a410fef276733dfe76efc2f4a5146c1b9523357549538df9bec9c15
7
- data.tar.gz: 272f78213b4d9c2b1ec5d447f57b16e1e6e79c66a302428e2ea037e4f847f578096347a54fb9a777bace9d7c72039f45c2b967b14f99e9e93333909e831fb098
6
+ metadata.gz: de48a9a73b02e227bffbc8f8094355157acdd54bdb1214f3e2b281009827de15ac4261cb084749a840f5b30a1090796eb036eace96badb3ef91d1237eee6d2d0
7
+ data.tar.gz: 2cd3efa4127547d4ecb1f5f09abf93dfad46313bd38a0baac1686fe29506908c9405d22398fdd4e72423862e34c0f7c262cca99ec2865e6186d487747f14d620
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## v2.0.0
2
+ * [fix] Use sudo helper when cloning
3
+ * [enhancement] Disable retry on ephemeral command
4
+ * [enhancement] Dynamic parsing/generation of configuration files
5
+ * [fix] Do not register traps when executing inline
6
+ * [fix] Synchronize childprocess access to prevent race
7
+
8
+ _WARNING: Updated configuration file handling may cause breakage_
9
+
1
10
  ## v1.1.8
2
11
  * Add `include` support for file config
3
12
 
data/elecksee.gemspec CHANGED
@@ -10,6 +10,8 @@ Gem::Specification.new do |s|
10
10
  s.description = 'LXC helpers'
11
11
  s.require_path = 'lib'
12
12
  s.executables = %w(lxc-awesome-ephemeral)
13
+ s.add_dependency 'bogo'
14
+ s.add_dependency 'attribute_struct'
13
15
  s.add_dependency 'childprocess'
14
16
  s.add_dependency 'rye'
15
17
  s.files = Dir['{bin,lib}/**/**/*'] + %w(elecksee.gemspec README.md CHANGELOG.md LICENSE CONTRIBUTING.md)
data/lib/elecksee.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'elecksee/version'
2
+ require 'bogo'
2
3
 
3
4
  # LXC interface
4
5
  class Lxc
@@ -93,7 +93,7 @@ class Lxc
93
93
  lxc.start
94
94
  if(ephemeral_command)
95
95
  lxc.wait_for_state(:running)
96
- lxc.container_command(ephemeral_command)
96
+ lxc.container_command(ephemeral_command, 0) # no retries on ephemeral commands
97
97
  else
98
98
  cli_output
99
99
  lxc.wait_for_state(:stopped)
@@ -137,7 +137,6 @@ class Lxc
137
137
  start_action
138
138
  end
139
139
  else
140
- register_traps
141
140
  start_action
142
141
  end
143
142
  true
@@ -194,7 +193,9 @@ class Lxc
194
193
  content << 'trap scrub SIGTERM SIGINT SIGQUIT'
195
194
  content << "lxc-start -n #{lxc.name} -d"
196
195
  content << 'sleep 1'
197
- content << "lxc-wait -n #{lxc.name} -s STOPPED"
196
+ content << 'until [ `lxc-wait -n #{lxc.name} -s STOPPED -t 2` ]'
197
+ content << 'do'
198
+ content << 'done'
198
199
  content << 'scrub'
199
200
  tmp = Tempfile.new('elecksee')
200
201
  tmp.chmod(0700)
@@ -4,6 +4,10 @@ class Lxc
4
4
  # Helper modules
5
5
  module Helpers
6
6
 
7
+ class << self
8
+ attr_accessor :child_process_lock
9
+ end
10
+
7
11
  autoload :Copies, 'elecksee/helpers/copies'
8
12
  autoload :Options, 'elecksee/helpers/options'
9
13
 
@@ -56,11 +60,14 @@ class Lxc
56
60
  s_err = Tempfile.new('stderr')
57
61
  s_out.sync
58
62
  s_err.sync
59
- c_proc = ChildProcess.build(*Shellwords.split(cmd))
60
- c_proc.environment.merge('HOME' => detect_home)
61
- c_proc.io.stdout = s_out
62
- c_proc.io.stderr = s_err
63
- c_proc.start
63
+ c_proc = nil
64
+ Lxc::Helpers.child_process_lock.synchronize do
65
+ c_proc = ChildProcess.build(*Shellwords.split(cmd))
66
+ c_proc.environment.merge('HOME' => detect_home)
67
+ c_proc.io.stdout = s_out
68
+ c_proc.io.stderr = s_err
69
+ c_proc.start
70
+ end
64
71
  begin
65
72
  c_proc.poll_for_exit(args[:timeout] || 1200)
66
73
  rescue ChildProcess::TimeoutError
@@ -228,3 +235,5 @@ class Lxc
228
235
 
229
236
  end
230
237
  end
238
+
239
+ Lxc::Helpers.child_process_lock = Mutex.new
@@ -1,145 +1,101 @@
1
1
  require 'elecksee'
2
+ require 'attribute_struct'
2
3
 
3
- class Lxc
4
- # Configuration file interface
5
- class FileConfig
4
+ class LxcStruct < AttributeStruct
6
5
 
7
- # @return [Array]
8
- attr_reader :network
9
- # @return [String] path to configuration file
10
- attr_reader :base
11
-
12
- class << self
6
+ def network(*args, &block)
7
+ unless(self[:network])
8
+ set!(:network, ::AttributeStruct::CollapseArray.new)
9
+ self[:network].push(_klass_new)
10
+ end
11
+ if(self[:network].last._data[:type].is_a?(::AttributeStruct::CollapseArray))
12
+ val = self[:network].last._data[:type].pop
13
+ self[:network].push(_klass_new)
14
+ self[:network].last.set!(:type, val)
15
+ end
16
+ self[:network].last
17
+ end
13
18
 
14
- # Convert object to Hash if possible
15
- #
16
- # @param thing [Object]
17
- # @return [Hash]
18
- # @note used mostly for the lxc resource within chef
19
- def convert_to_hash(thing)
20
- if(defined?(Chef) && thing.is_a?(Chef::Resource))
21
- result = Hash[*(
22
- (thing.methods - Chef::Resource.instance_methods).map{ |key|
23
- unless(key.to_s.start_with?('_') || thing.send(key).nil?)
24
- [key, thing.send(key)]
25
- end
26
- }.compact.flatten(1)
27
- )]
28
- else
29
- unless(thing.is_a?(Hash))
30
- result = defined?(Mash) ? Mash.new : {}
31
- thing.to_hash.each do |k,v|
32
- result[k] = v.respond_to?(:keys) && v.respond_to?(:values) ? convert_to_hash(v) : v
33
- end
34
- end
35
- end
36
- result || thing
37
- end
19
+ def _klass
20
+ ::LxcStruct
21
+ end
38
22
 
39
- # Symbolize keys within hash
40
- #
41
- # @param thing [Hashish]
42
- # @return [Hash]
43
- def symbolize_hash(thing)
44
- if(defined?(Mash))
45
- Mash.new(thing)
46
- else
47
- result = {}
48
- thing.each do |k,v|
49
- result[k.to_sym] = v.is_a?(Hash) ? symbolize_hash(v) : v
50
- end
51
- result
52
- end
53
- end
23
+ end
54
24
 
55
- # Generate configuration file contents
56
- #
57
- # @param resource [Hashish]
58
- # @return [String]
59
- def generate_config(resource)
60
- resource = symbolize_hash(convert_to_hash(resource))
61
- config = []
62
- config << "lxc.utsname = #{resource[:utsname]}"
63
- if(resource[:aa_profile])
64
- config << "lxc.aa_profile = #{resource[:aa_profile]}"
65
- end
66
- [resource[:network]].flatten.each do |net_hash|
67
- nhsh = Mash.new(net_hash)
68
- flags = nhsh.delete(:flags)
69
- %w(type link).each do |k|
70
- config << "lxc.network.#{k} = #{nhsh.delete(k)}" if nhsh[k]
71
- end
72
- nhsh.each_pair do |k,v|
73
- config << "lxc.network.#{k} = #{v}"
74
- end
75
- if(flags)
76
- config << "lxc.network.flags = #{flags}"
77
- end
78
- end
79
- if(resource[:cap_drop])
80
- config << "lxc.cap.drop = #{Array(resource[:cap_drop]).join(' ')}"
81
- end
82
- %w(include pts tty arch devttydir mount mount_entry rootfs rootfs_mount pivotdir).each do |k|
83
- config << "lxc.#{k.sub('_', '.')} = #{resource[k.to_sym]}" if resource[k.to_sym]
84
- end
85
- prefix = 'lxc.cgroup'
86
- resource[:cgroup].each_pair do |key, value|
87
- if(value.is_a?(Array))
88
- value.each do |val|
89
- config << "#{prefix}.#{key} = #{val}"
90
- end
91
- else
92
- config << "#{prefix}.#{key} = #{value}"
93
- end
94
- end
95
- config.join("\n") + "\n"
96
- end
25
+ class Lxc
26
+ # Configuration file interface
27
+ class FileConfig
97
28
 
98
- end
29
+ # @return [String] path to config file
30
+ attr_reader :path
31
+ # @return [AttrubuteStruct] config file contents
32
+ attr_accessor :state
99
33
 
100
34
  # Create new instance
101
35
  #
102
36
  # @param path [String]
103
37
  def initialize(path)
104
- raise 'LXC config file not found' unless File.exists?(path)
105
38
  @path = path
106
- @network = []
107
- @base = defined?(Mash) ? Mash.new : {}
108
- parse!
39
+ if(File.exists?(path))
40
+ parse!
41
+ else
42
+ @state = LxcStruct.new
43
+ end
44
+ end
45
+
46
+ # @return [Smash] hash like dump of state
47
+ def state_hash
48
+ state._dump.to_smash
49
+ end
50
+
51
+ # Overwrite the config file
52
+ #
53
+ # @return [Integer]
54
+ def write!
55
+ File.write(path, generate_content)
56
+ end
57
+
58
+ # Generate config file content from current value of state
59
+ #
60
+ # @return [String]
61
+ def generate_content
62
+ process_item(state_hash).flatten.join("\n")
109
63
  end
110
64
 
111
65
  private
112
66
 
67
+ # Convert item to configuration file line
68
+ #
69
+ # @param item [Object]
70
+ # @param parents [Array<String>] parent hash keys
71
+ # @return [Array<String>]
72
+ def process_item(item, parents=[])
73
+ case item
74
+ when Hash
75
+ item.map do |k,v|
76
+ process_item(v, parents + [k])
77
+ end
78
+ when Array
79
+ item.map do |v|
80
+ process_item(v, parents)
81
+ end
82
+ else
83
+ ["#{parents.join('.')} = #{item}"]
84
+ end
85
+ end
86
+
113
87
  # Parse the configuration file
114
88
  #
115
- # @return [TrueClass]
89
+ # @return [AttributeStruct]
116
90
  def parse!
117
- cur_net = nil
118
- File.readlines(@path).each do |line|
119
- if(line.start_with?('lxc.network'))
120
- parts = line.split('=')
121
- name = parts.first.split('.').last.strip
122
- if(name.to_sym == :type)
123
- @network << cur_net if cur_net
124
- cur_net = Mash.new
125
- end
126
- if(cur_net)
127
- cur_net[name] = parts.last.strip
128
- else
129
- raise "Expecting 'lxc.network.type' to start network config block. Found: 'lxc.network.#{name}'"
130
- end
131
- else
132
- parts = line.split('=')
133
- name = parts.first.sub('lxc.', '').strip
134
- if(@base[name])
135
- @base[name] = [@base[name], parts.last.strip].flatten
136
- else
137
- @base[name] = parts.last
138
- end
139
- end
91
+ struct = LxcStruct.new
92
+ struct._set_state(:value_collapse => true)
93
+ File.read(path).split("\n").each do |line|
94
+ parts = line.split('=').map(&:strip)
95
+ parts.last.replace("'#{parts.last}'")
96
+ struct.instance_eval(parts.join(' = '))
140
97
  end
141
- @network << cur_net if cur_net
142
- true
98
+ @state = struct
143
99
  end
144
100
 
145
101
  end
@@ -34,7 +34,7 @@ class Lxc
34
34
  # @return [TrueClass, FalseClass]
35
35
  def create
36
36
  unless(File.directory?(overlay_path))
37
- FileUtils.mkdir_p(overlay_path)
37
+ command("mkdir -p #{overlay_path}", :sudo => true)
38
38
  true
39
39
  else
40
40
  false
@@ -1,4 +1,4 @@
1
1
  module Elecksee
2
2
  # Current library version
3
- VERSION = Gem::Version.new('1.1.8')
3
+ VERSION = Gem::Version.new('2.0.0')
4
4
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elecksee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.8
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-08 00:00:00.000000000 Z
11
+ date: 2015-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bogo
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: attribute_struct
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: childprocess
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -83,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
111
  version: '0'
84
112
  requirements: []
85
113
  rubyforge_project:
86
- rubygems_version: 2.2.2
114
+ rubygems_version: 2.4.8
87
115
  signing_key:
88
116
  specification_version: 4
89
117
  summary: LXC helpers