elecksee 1.0.22 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|