elecksee 1.0.22 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +25 -0
- data/README.md +43 -6
- data/elecksee.gemspec +3 -3
- data/lib/elecksee/clone.rb +47 -16
- data/lib/elecksee/ephemeral.rb +65 -15
- data/lib/elecksee/helpers/copies.rb +36 -5
- data/lib/elecksee/helpers/options.rb +29 -3
- data/lib/elecksee/{helpers/base.rb → helpers.rb} +115 -56
- data/lib/elecksee/lxc.rb +204 -82
- data/lib/elecksee/lxc_file_config.rb +26 -0
- data/lib/elecksee/storage/overlay_directory.rb +68 -37
- data/lib/elecksee/storage/overlay_mount.rb +80 -45
- data/lib/elecksee/storage/virtual_device.rb +104 -60
- data/lib/elecksee/storage.rb +13 -0
- data/lib/elecksee/version.rb +2 -1
- data/lib/elecksee.rb +13 -0
- metadata +26 -33
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -22
- data/lib/elecksee/knife/config.rb +0 -37
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3d89dd64aa0c3a0ef232692735c75c415d0a994
|
4
|
+
data.tar.gz: 9b769a6d71ff3efa3320004e31f7bc18453ae66b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0eb4a7dc26256da9e8ea791721391cfb89fe0edd041831a798654e6385ea71290070f8a18f8b6ca442cf025ebfba758375d61293a10f49dc5e69e4a3a841e896
|
7
|
+
data.tar.gz: e22ebacc0cabf3108d3ab04365b01c683caf36162155530cf2bfeddcb12a190ff4e2e32b63bf788e767ef6bb8df24c5438385bb11f1193febe881ccbcebc5191
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## v1.1.0
|
2
|
+
* Update all documentation to yardoc
|
3
|
+
* Group classes into logical namespaces
|
4
|
+
* Define expected returns for methods
|
5
|
+
* Remove calls to lxc-shutdown (not always available)
|
6
|
+
* Always attempt in container halt prior to lxc-stop
|
7
|
+
* Use lxc-ls for container listing to prevent permission issues
|
8
|
+
* Use the Rye library under the hood for container connects
|
9
|
+
* Use ChildProcess for shelling out
|
10
|
+
|
1
11
|
## v1.0.22
|
2
12
|
* Update underlying implementation for execute
|
3
13
|
* Provide better info interpretation
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
## Branches
|
4
|
+
|
5
|
+
### `master` branch
|
6
|
+
|
7
|
+
The master branch is the current stable released version.
|
8
|
+
|
9
|
+
### `develop` branch
|
10
|
+
|
11
|
+
The develop branch is the current edge of development.
|
12
|
+
|
13
|
+
## Pull requests
|
14
|
+
|
15
|
+
* https://github.com/chrisroberts/elecksee/pulls
|
16
|
+
|
17
|
+
Please base all pull requests of the `develop` branch. Merges to
|
18
|
+
`master` only occur through the `develop` branch. Pull requests
|
19
|
+
based on `master` will likely be cherry picked.
|
20
|
+
|
21
|
+
## Issues
|
22
|
+
|
23
|
+
Need to report an issue? Use the github issues:
|
24
|
+
|
25
|
+
* https://github.com/chrisroberts/elecksee/issues
|
data/README.md
CHANGED
@@ -2,20 +2,57 @@
|
|
2
2
|
|
3
3
|
An LXC library for Ruby
|
4
4
|
|
5
|
-
##
|
5
|
+
## Basic usage
|
6
6
|
|
7
7
|
```ruby
|
8
|
-
require 'elecksee
|
8
|
+
require 'elecksee'
|
9
9
|
|
10
10
|
lxc = Lxc.new('container')
|
11
11
|
lxc.start unless lxc.running?
|
12
12
|
```
|
13
13
|
|
14
|
-
##
|
14
|
+
## Permissions
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
Root access is required for most operations. This library can
|
17
|
+
be utilized from a root user but is built to use sudo as required.
|
18
|
+
To enable sudo, use:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
Lxc.use_sudo = true
|
22
|
+
```
|
23
|
+
|
24
|
+
If you require a custom sudo for things like rvm, use:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
Lxc.use_sudo = 'rvmsudo'
|
28
|
+
```
|
29
|
+
|
30
|
+
## What's in the box
|
31
|
+
|
32
|
+
### Lxc
|
33
|
+
|
34
|
+
Container inspection and interaction. Will provide state
|
35
|
+
information about the container as well as providing an
|
36
|
+
interaction interface for running commands within the
|
37
|
+
container and changing its state (stop, start, freeze, thaw).
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
my_lxc = Lxc.new('my_container')
|
41
|
+
puts "Address: #{my_lxc.container_ip}"
|
42
|
+
puts "State: #{my_lxc.state}"
|
43
|
+
```
|
44
|
+
|
45
|
+
### Lxc::Ephemeral
|
46
|
+
|
47
|
+
Create ephemeral containers from existing stopped containers. Utilizes
|
48
|
+
an overlay filesystem to leave original container untouched. All ephemeral
|
49
|
+
resources are removed once the container is halted.
|
50
|
+
|
51
|
+
### Lxc::Clone
|
52
|
+
|
53
|
+
Make clones of existing stopped containers. Allows for utilizing optional
|
54
|
+
storage backends like full copies, overlay directories, virtual block
|
55
|
+
device or fs specific like btrfs snapshots.
|
19
56
|
|
20
57
|
### Notes
|
21
58
|
|
data/elecksee.gemspec
CHANGED
@@ -10,7 +10,7 @@ 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 '
|
14
|
-
s.add_dependency '
|
15
|
-
s.files = Dir['
|
13
|
+
s.add_dependency 'childprocess'
|
14
|
+
s.add_dependency 'rye'
|
15
|
+
s.files = Dir['{bin,lib}/**/**/*'] + %w(elecksee.gemspec README.md CHANGELOG.md LICENSE CONTRIBUTING.md)
|
16
16
|
end
|
data/lib/elecksee/clone.rb
CHANGED
@@ -1,12 +1,7 @@
|
|
1
|
-
|
2
|
-
helpers/base helpers/options helpers/copies lxc
|
3
|
-
storage/overlay_directory storage/overlay_mount
|
4
|
-
storage/virtual_device
|
5
|
-
).each do |path|
|
6
|
-
require "elecksee/#{path}"
|
7
|
-
end
|
1
|
+
require 'elecksee'
|
8
2
|
|
9
3
|
class Lxc
|
4
|
+
# Clone existing containers
|
10
5
|
class Clone
|
11
6
|
|
12
7
|
include Helpers
|
@@ -25,9 +20,14 @@ class Lxc
|
|
25
20
|
option :gateway, '-G', :string, :desc => 'Custom gateway'
|
26
21
|
option :netmask, '-N', :string, :default => '255.255.255.0', :desc => 'Custom netmask'
|
27
22
|
|
28
|
-
# Hash
|
23
|
+
# @return [Hash] original and new container instances
|
29
24
|
attr_reader :lxcs
|
30
25
|
|
26
|
+
# Create new instance
|
27
|
+
#
|
28
|
+
# @param args [Hash]
|
29
|
+
# @option args [String] :original existing container name
|
30
|
+
# @option args [String] :new new container name
|
31
31
|
def initialize(args={})
|
32
32
|
configure!(args)
|
33
33
|
@lxcs = {}
|
@@ -37,31 +37,40 @@ class Lxc
|
|
37
37
|
validate!
|
38
38
|
end
|
39
39
|
|
40
|
+
# Create the clone
|
41
|
+
#
|
42
|
+
# @return [Lxc] new clone
|
40
43
|
def clone!
|
41
44
|
begin
|
42
45
|
copy_original
|
43
46
|
update_naming(:no_config)
|
44
47
|
apply_custom_addressing if ipaddress
|
45
|
-
|
48
|
+
lxc
|
46
49
|
rescue Exception
|
47
50
|
@created.map(&:destroy)
|
48
51
|
raise
|
49
52
|
end
|
50
53
|
end
|
51
|
-
|
54
|
+
|
52
55
|
private
|
53
56
|
|
54
57
|
alias_method :name, :new_name
|
55
|
-
|
56
|
-
#
|
58
|
+
|
59
|
+
# @return [Lxc] new lxc instance
|
57
60
|
def lxc
|
58
61
|
lxcs[:new]
|
59
62
|
end
|
60
63
|
|
64
|
+
# Add to list of created items
|
65
|
+
#
|
66
|
+
# @param thing [Object] item created
|
61
67
|
def created(thing)
|
62
68
|
@created << thing
|
63
69
|
end
|
64
70
|
|
71
|
+
# Validate current state
|
72
|
+
#
|
73
|
+
# @return [TrueClass]
|
65
74
|
def validate!
|
66
75
|
unless(lxcs[:original].exists?)
|
67
76
|
raise "Requested `original` container does not exist (#{original})"
|
@@ -72,8 +81,12 @@ class Lxc
|
|
72
81
|
if(lxcs[:original].running?)
|
73
82
|
raise "Requested `original` container is current running (#{original})"
|
74
83
|
end
|
84
|
+
true
|
75
85
|
end
|
76
86
|
|
87
|
+
# Create copy of original container
|
88
|
+
#
|
89
|
+
# @return [TrueClass]
|
77
90
|
def copy_original
|
78
91
|
copy_init
|
79
92
|
if(device)
|
@@ -86,38 +99,56 @@ class Lxc
|
|
86
99
|
rootfs_dir = copy_fs
|
87
100
|
end
|
88
101
|
update_rootfs(rootfs_dir)
|
102
|
+
true
|
89
103
|
end
|
90
104
|
|
105
|
+
# Initialize container copy (base file copy)
|
106
|
+
#
|
107
|
+
# @return [TrueClass]
|
91
108
|
def copy_init
|
92
|
-
directory = CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
|
109
|
+
directory = Storage::CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
|
93
110
|
created(directory)
|
94
111
|
%w(config fstab).each do |file|
|
95
112
|
command("cp '#{lxcs[:original].path.join(file)}' '#{directory.target_path}'", :sudo => true)
|
96
113
|
end
|
114
|
+
true
|
97
115
|
end
|
98
116
|
|
117
|
+
# Copy into file system directory
|
118
|
+
#
|
119
|
+
# @return [String] path to new rootfs
|
99
120
|
def copy_fs
|
100
|
-
directory = CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
|
121
|
+
directory = Storage::CloneDirectory.new(lxcs[:new].name, :dir => File.dirname(lxcs[:original].path.to_s))
|
101
122
|
created(directory)
|
102
123
|
command("rsync -ax '#{lxcs[:original].rootfs}/' '#{File.join(directory.target_path, 'rootfs')}/'", :sudo => true)
|
103
124
|
File.join(directory.target_path, 'rootfs')
|
104
125
|
end
|
105
126
|
|
127
|
+
# Copy into new virtual block device
|
128
|
+
#
|
129
|
+
# @return [String] path to new rootfs
|
106
130
|
def copy_vbd
|
107
|
-
storage = VirtualDevice.new(lxcs[:new].name, :tmp_dir => '/opt/lxc-vbd')
|
131
|
+
storage = Storage::VirtualDevice.new(lxcs[:new].name, :tmp_dir => '/opt/lxc-vbd')
|
108
132
|
created(storage)
|
109
133
|
command("rsync -ax '#{lxcs[:original].rootfs}/' '#{storage.target_path}/'", :sudo => true)
|
110
134
|
storage.target_path
|
111
135
|
end
|
112
136
|
|
137
|
+
# Copy into new LVM partition
|
138
|
+
#
|
139
|
+
# @note not implemented
|
140
|
+
# @todo implement
|
113
141
|
def copy_lvm
|
114
142
|
raise 'Not implemented'
|
115
143
|
end
|
116
144
|
|
145
|
+
# Copy into new btrfs subvolume snapshot
|
146
|
+
#
|
147
|
+
# @return [String] path to new rootfs
|
148
|
+
# @todo remove on failure
|
117
149
|
def copy_btrfs
|
118
150
|
rootfs_path = lxcs[:new].path.join('rootfs')
|
119
151
|
command("btrfs subvolume snapshot '#{lxcs[:original].rootfs}' '#{rootfs_path}'", :sudo => true)
|
120
|
-
# TODO: Remove on failure
|
121
152
|
rootfs_path
|
122
153
|
end
|
123
154
|
end
|
data/lib/elecksee/ephemeral.rb
CHANGED
@@ -1,18 +1,11 @@
|
|
1
|
+
require 'elecksee'
|
1
2
|
require 'securerandom'
|
2
3
|
require 'fileutils'
|
3
4
|
require 'tmpdir'
|
4
5
|
require 'etc'
|
5
6
|
|
6
|
-
%w(
|
7
|
-
helpers/base helpers/options helpers/copies lxc
|
8
|
-
storage/overlay_directory storage/overlay_mount
|
9
|
-
storage/virtual_device
|
10
|
-
).each do |path|
|
11
|
-
require "elecksee/#{path}"
|
12
|
-
end
|
13
|
-
|
14
7
|
class Lxc
|
15
|
-
|
8
|
+
# Create ephemeral containers
|
16
9
|
class Ephemeral
|
17
10
|
|
18
11
|
include Helpers
|
@@ -34,15 +27,27 @@ class Lxc
|
|
34
27
|
option :tmp_dir, '-T', :string, :default => '/tmp/lxc/ephemerals', :aliases => 'tmp-dir', :desc => 'Directory of ephemeral temp files'
|
35
28
|
option :ephemeral_command, '-C', :string, :aliases => 'command'
|
36
29
|
|
30
|
+
# @return [String] name of container
|
37
31
|
attr_reader :name
|
32
|
+
# @return [TrueClass, FalseClass] enable CLI output
|
38
33
|
attr_reader :cli
|
34
|
+
# @return [String] hostname of container
|
39
35
|
attr_reader :hostname
|
36
|
+
# @return [String] path to container
|
40
37
|
attr_reader :path
|
38
|
+
# @return [Lxc] instance of ephemeral
|
41
39
|
attr_reader :lxc
|
40
|
+
# @return [Storage::OverlayDirectory, Storage::VirtualDevice]
|
42
41
|
attr_reader :ephemeral_device
|
42
|
+
# @return [Storage::OverlayMount]
|
43
43
|
attr_reader :ephemeral_overlay
|
44
|
+
# @return [Array<Storage::VirtualDevice]
|
44
45
|
attr_reader :ephemeral_binds
|
45
46
|
|
47
|
+
# Create new instance
|
48
|
+
#
|
49
|
+
# @param args [Hash]
|
50
|
+
# @option args [TrueClass, FalseClass] :cli enable CLI output
|
46
51
|
def initialize(args={})
|
47
52
|
configure!(args)
|
48
53
|
@cli = args[:cli]
|
@@ -54,19 +59,34 @@ class Lxc
|
|
54
59
|
@lxc = nil
|
55
60
|
end
|
56
61
|
|
62
|
+
# Trap signals to force cleanup
|
63
|
+
#
|
64
|
+
# @return [TrueClass]
|
57
65
|
def register_traps
|
58
66
|
%w(TERM INT QUIT).each do |sig|
|
59
67
|
Signal.trap(sig){ cleanup && raise }
|
60
68
|
end
|
69
|
+
true
|
61
70
|
end
|
62
71
|
|
72
|
+
# Write output to CLI
|
73
|
+
#
|
74
|
+
# @return [TrueClass, FalseClass]
|
63
75
|
def cli_output
|
64
76
|
if(cli)
|
65
77
|
puts "New ephemeral container started. (#{name})"
|
66
78
|
puts " - Connect using: sudo ssh -i #{ssh_key} root@#{lxc.container_ip(10)}"
|
79
|
+
true
|
80
|
+
else
|
81
|
+
false
|
67
82
|
end
|
68
83
|
end
|
69
84
|
|
85
|
+
# Start the ephemeral container
|
86
|
+
#
|
87
|
+
# @return [TrueClass]
|
88
|
+
# @note generally should not be called directly
|
89
|
+
# @see start!
|
70
90
|
def start_action
|
71
91
|
begin
|
72
92
|
lxc.start
|
@@ -83,10 +103,19 @@ class Lxc
|
|
83
103
|
true
|
84
104
|
end
|
85
105
|
|
106
|
+
# Create the ephemeral container
|
107
|
+
#
|
108
|
+
# @return [TrueClass]
|
86
109
|
def create!
|
87
110
|
setup
|
111
|
+
true
|
88
112
|
end
|
89
113
|
|
114
|
+
# Start the ephemeral container
|
115
|
+
#
|
116
|
+
# @param args [Symbol] argument list
|
117
|
+
# @return [TrueClass]
|
118
|
+
# @note use :fork to fork startup
|
90
119
|
def start!(*args)
|
91
120
|
register_traps
|
92
121
|
setup
|
@@ -102,8 +131,12 @@ class Lxc
|
|
102
131
|
else
|
103
132
|
start_action
|
104
133
|
end
|
134
|
+
true
|
105
135
|
end
|
106
136
|
|
137
|
+
# Stop container and cleanup ephemeral items
|
138
|
+
#
|
139
|
+
# @return [TrueClass, FalseClass]
|
107
140
|
def cleanup
|
108
141
|
lxc.stop
|
109
142
|
@ephemeral_overlay.unmount
|
@@ -120,22 +153,29 @@ class Lxc
|
|
120
153
|
|
121
154
|
private
|
122
155
|
|
156
|
+
# Setup the ephemeral container resources
|
157
|
+
#
|
158
|
+
# @return [TrueClass]
|
123
159
|
def setup
|
124
160
|
create
|
125
161
|
build_overlay
|
126
162
|
update_naming
|
127
163
|
discover_binds
|
128
164
|
apply_custom_networking if ipaddress
|
165
|
+
true
|
129
166
|
end
|
130
167
|
|
168
|
+
# Create the overlay
|
169
|
+
#
|
170
|
+
# @return [TrueClass, FalseClass]
|
131
171
|
def build_overlay
|
132
172
|
if(directory)
|
133
|
-
@ephemeral_device = OverlayDirectory.new(name, :tmp_dir => directory.is_a?(String) ? directory : tmp_dir)
|
173
|
+
@ephemeral_device = Storage::OverlayDirectory.new(name, :tmp_dir => directory.is_a?(String) ? directory : tmp_dir)
|
134
174
|
else
|
135
|
-
@ephemeral_device = VirtualDevice.new(name, :size => device, :tmp_fs => !device, :tmp_dir => tmp_dir)
|
175
|
+
@ephemeral_device = Storage::VirtualDevice.new(name, :size => device, :tmp_fs => !device, :tmp_dir => tmp_dir)
|
136
176
|
@ephemeral_device.mount
|
137
177
|
end
|
138
|
-
@ephemeral_overlay = OverlayMount.new(
|
178
|
+
@ephemeral_overlay = Storage::OverlayMount.new(
|
139
179
|
:base => Lxc.new(original).rootfs.to_path,
|
140
180
|
:overlay => ephemeral_device.target_path,
|
141
181
|
:target => lxc.path.join('rootfs').to_path,
|
@@ -144,6 +184,9 @@ class Lxc
|
|
144
184
|
@ephemeral_overlay.mount
|
145
185
|
end
|
146
186
|
|
187
|
+
# Create the container
|
188
|
+
#
|
189
|
+
# @return [TrueClass]
|
147
190
|
def create
|
148
191
|
Dir.glob(File.join(lxc_dir, original, '*')).each do |o_path|
|
149
192
|
next unless File.file?(o_path)
|
@@ -152,10 +195,15 @@ class Lxc
|
|
152
195
|
@lxc = Lxc.new(name)
|
153
196
|
command("mkdir -p #{lxc.path.join('rootfs')}", :sudo => true)
|
154
197
|
update_net_hwaddr
|
198
|
+
true
|
155
199
|
end
|
156
200
|
|
157
|
-
#
|
158
|
-
#
|
201
|
+
# Discover any bind mounts defined
|
202
|
+
#
|
203
|
+
# @return [TrueClass]
|
204
|
+
# @todo discovered binds for ephemeral are all tmpfs for
|
205
|
+
# now. should default to overlay mount, make virtual
|
206
|
+
# device and tmpfs optional
|
159
207
|
def discover_binds
|
160
208
|
contents = File.readlines(lxc.path.join('fstab')).each do |line|
|
161
209
|
parts = line.split(' ')
|
@@ -163,7 +211,7 @@ class Lxc
|
|
163
211
|
source = parts.first
|
164
212
|
target = parts[1].sub(%r{^.+rootfs/}, '')
|
165
213
|
container_target = lxc.rootfs.join(target).to_path
|
166
|
-
device = VirtualDevice.new(target.gsub('/', '_'), :tmp_fs => true)
|
214
|
+
device = Storage::VirtualDevice.new(target.gsub('/', '_'), :tmp_fs => true)
|
167
215
|
device.mount
|
168
216
|
FileUtils.mkdir_p(container_target)
|
169
217
|
ephemeral_binds << device
|
@@ -182,6 +230,8 @@ class Lxc
|
|
182
230
|
contents << "#{bind} #{lxc.rootfs.join(bind)} none bind 0 0\n"
|
183
231
|
end
|
184
232
|
write_file(lxc.path.join('fstab'), contents.join)
|
233
|
+
true
|
185
234
|
end
|
235
|
+
|
186
236
|
end
|
187
237
|
end
|
@@ -1,17 +1,24 @@
|
|
1
|
+
require 'elecksee'
|
1
2
|
require 'tempfile'
|
2
3
|
|
3
4
|
class Lxc
|
4
5
|
module Helpers
|
6
|
+
# Container related file copy helpers
|
5
7
|
module Copies
|
6
|
-
|
8
|
+
# Files requiring name updates
|
7
9
|
NAME_FILES = %w(fstab config)
|
10
|
+
# Files requiring hostname updates
|
8
11
|
HOSTNAME_FILES = %w(
|
9
12
|
rootfs/etc/hostname
|
10
13
|
rootfs/etc/hosts
|
11
14
|
rootfs/etc/sysconfig/network
|
12
15
|
rootfs/etc/sysconfig/network-scripts/ifcfg-eth0
|
13
16
|
)
|
14
|
-
|
17
|
+
|
18
|
+
# Update the rootfs
|
19
|
+
#
|
20
|
+
# @param rootfs_path [String] new rootfs path
|
21
|
+
# @return [TrueClass]
|
15
22
|
def update_rootfs(rootfs_path)
|
16
23
|
contents = File.readlines(lxc.config.to_s).map do |line|
|
17
24
|
if(line.start_with?('lxc.rootfs'))
|
@@ -21,8 +28,12 @@ class Lxc
|
|
21
28
|
end
|
22
29
|
end.join
|
23
30
|
write_file(lxc.config, contents)
|
31
|
+
true
|
24
32
|
end
|
25
33
|
|
34
|
+
# Update network hardware address
|
35
|
+
#
|
36
|
+
# @return [TrueClass]
|
26
37
|
def update_net_hwaddr
|
27
38
|
contents = File.readlines(lxc.config).map do |line|
|
28
39
|
if(line.start_with?('lxc.network.hwaddr'))
|
@@ -33,8 +44,14 @@ class Lxc
|
|
33
44
|
end
|
34
45
|
end.join
|
35
46
|
write_file(lxc.config, contents)
|
47
|
+
true
|
36
48
|
end
|
37
49
|
|
50
|
+
# Write file
|
51
|
+
#
|
52
|
+
# @param path [String]
|
53
|
+
# @param contents [String, Array<String>]
|
54
|
+
# @return [TrueClass]
|
38
55
|
def write_file(path, contents)
|
39
56
|
contents = contents.join if contents.is_a?(Array)
|
40
57
|
tmp = Tempfile.new('lxc-copy')
|
@@ -43,8 +60,14 @@ class Lxc
|
|
43
60
|
command("cp #{tmp.path} #{path}", :sudo => true)
|
44
61
|
tmp.unlink
|
45
62
|
command("chmod 0644 #{path}", :sudo => true)
|
63
|
+
true
|
46
64
|
end
|
47
|
-
|
65
|
+
|
66
|
+
# Update container names and host names
|
67
|
+
#
|
68
|
+
# @param args [Symbol] argument list
|
69
|
+
# @return [TrueClass]
|
70
|
+
# @note use :no_$FILE where $FILE is the basename to skip
|
48
71
|
def update_naming(*args)
|
49
72
|
NAME_FILES.each do |file|
|
50
73
|
next unless File.exists?(lxc.path.join(file))
|
@@ -54,16 +77,23 @@ class Lxc
|
|
54
77
|
end
|
55
78
|
HOSTNAME_FILES.each do |file|
|
56
79
|
next unless File.exists?(lxc.path.join(file))
|
57
|
-
next if args.include?("no_#{file}".to_sym)
|
80
|
+
next if args.include?("no_#{file.split('/').last}".to_sym)
|
58
81
|
contents = File.read(lxc.path.join(file)).gsub(original, name)
|
59
82
|
write_file(lxc.path.join(file), contents)
|
60
83
|
end
|
84
|
+
true
|
61
85
|
end
|
62
86
|
|
87
|
+
# Container is Enterprise linux (redhat)
|
88
|
+
#
|
89
|
+
# @return [TrueClass, FalseClass]
|
63
90
|
def el_platform?
|
64
91
|
lxc.rootfs.join('etc/redhat-release').exist?
|
65
92
|
end
|
66
|
-
|
93
|
+
|
94
|
+
# Apply custom networking files depending on platform
|
95
|
+
#
|
96
|
+
# @return [TrueClass]
|
67
97
|
def apply_custom_networking
|
68
98
|
if(el_platform?)
|
69
99
|
path = lxc.rootfs.join('etc/sysconfig/network-scripts/ifcfg-eth0')
|
@@ -100,6 +130,7 @@ gateway #{gateway}
|
|
100
130
|
EOF
|
101
131
|
write_file(path, content)
|
102
132
|
end
|
133
|
+
true
|
103
134
|
end
|
104
135
|
end
|
105
136
|
end
|
@@ -1,13 +1,27 @@
|
|
1
|
+
require 'elecksee'
|
2
|
+
|
1
3
|
class Lxc
|
2
4
|
module Helpers
|
3
|
-
|
5
|
+
# Helper methods for processing CLI options
|
4
6
|
module Options
|
5
7
|
class << self
|
8
|
+
# Load option helper into included class
|
9
|
+
#
|
10
|
+
# @param klass [Class]
|
11
|
+
# @return [TrueClass]
|
6
12
|
def included(klass)
|
7
13
|
klass.class_eval do
|
8
14
|
class << self
|
15
|
+
|
16
|
+
# @return [Hash] options
|
9
17
|
attr_reader :options
|
10
|
-
|
18
|
+
|
19
|
+
# Define option
|
20
|
+
#
|
21
|
+
# @param name [String] name of option
|
22
|
+
# @param short [String] short flag
|
23
|
+
# @param long [String] long flag
|
24
|
+
# @param args [Hash]
|
11
25
|
def option(name, short, type, args={})
|
12
26
|
@options ||= {}
|
13
27
|
@options[name] = args.merge(:short => short, :type => type)
|
@@ -22,6 +36,9 @@ class Lxc
|
|
22
36
|
|
23
37
|
private
|
24
38
|
|
39
|
+
# Configure instance and validate options
|
40
|
+
#
|
41
|
+
# @param args [Array]
|
25
42
|
def configure!(args)
|
26
43
|
self.class.options.each do |name, opts|
|
27
44
|
argv = args.detect{|k,v| (Array(opts[:aliases]) + Array(opts[:short]) + [name]).include?(k.to_sym)}
|
@@ -41,6 +58,12 @@ class Lxc
|
|
41
58
|
end
|
42
59
|
end
|
43
60
|
|
61
|
+
# Validate option type
|
62
|
+
#
|
63
|
+
# @param arg_name [String]
|
64
|
+
# @param val [Object]
|
65
|
+
# @param type [Symbol] expected type
|
66
|
+
# @return [TrueClass]
|
44
67
|
def check_type!(arg_name, val, type)
|
45
68
|
valid = false
|
46
69
|
case type
|
@@ -51,7 +74,10 @@ class Lxc
|
|
51
74
|
when :integer
|
52
75
|
valid = val.is_a?(Numeric)
|
53
76
|
end
|
54
|
-
|
77
|
+
unless(valid)
|
78
|
+
raise ArgumentError.new "Invalid type provided for #{arg_name}. Expecting value type of: #{type.inspect} Got: #{val.class} - #{val}"
|
79
|
+
end
|
80
|
+
true
|
55
81
|
end
|
56
82
|
end
|
57
83
|
end
|