kitchen-lxd_cli 0.1.0.beta → 0.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 +4 -4
- data/.gitignore +1 -0
- data/.kitchen/logs/kitchen.log +29 -0
- data/:w +176 -0
- data/README.md +125 -29
- data/kitchen-lxd_cli.gemspec +2 -2
- data/lib/kitchen/driver/lxd_cli.rb +186 -44
- data/lib/kitchen/driver/lxd_cli_version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69c637b90cb10b7f5e7fbff0b2efd389f1d09c82
|
4
|
+
data.tar.gz: 2baf35c80ecd9804cb328b7ff2e0222a2a408a56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1a64dedb9835ac100b511e177f365d2d6276d657d3688bb50a551a3b138acce19ce1e8cbff63efcbcda12dcad299aa756d8012708bd1d9224007d881a01b5b1
|
7
|
+
data.tar.gz: 0f8665e9c1bfe62bf50021fa2520097618393764197619b0ffe8af0a94c44e25fb194ac434dfd716720587dca927072fc590442a21860d74af3553bbd6c3f833
|
data/.gitignore
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
I, [2015-11-01T00:01:20.462600 #7769] INFO -- Kitchen: -----> Starting Kitchen (v1.4.2)
|
2
|
+
E, [2015-11-01T00:01:20.462938 #7769] ERROR -- Kitchen: ------Exception-------
|
3
|
+
E, [2015-11-01T00:01:20.462972 #7769] ERROR -- Kitchen: Class: Kitchen::UserError
|
4
|
+
E, [2015-11-01T00:01:20.462994 #7769] ERROR -- Kitchen: Message: Kitchen YAML file /home/braden/kitchen-lxd_cli/.kitchen.yml does not exist.
|
5
|
+
E, [2015-11-01T00:01:20.463013 #7769] ERROR -- Kitchen: ------Backtrace-------
|
6
|
+
E, [2015-11-01T00:01:20.463031 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/loader/yaml.rb:74:in `read'
|
7
|
+
E, [2015-11-01T00:01:20.463049 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/config.rb:145:in `data'
|
8
|
+
E, [2015-11-01T00:01:20.463068 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/config.rb:124:in `suites'
|
9
|
+
E, [2015-11-01T00:01:20.463086 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/config.rb:175:in `filter_instances'
|
10
|
+
E, [2015-11-01T00:01:20.463103 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/config.rb:134:in `build_instances'
|
11
|
+
E, [2015-11-01T00:01:20.463124 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/config.rb:110:in `instances'
|
12
|
+
E, [2015-11-01T00:01:20.463142 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/command.rb:115:in `filtered_instances'
|
13
|
+
E, [2015-11-01T00:01:20.463160 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/command.rb:145:in `parse_subcommand'
|
14
|
+
E, [2015-11-01T00:01:20.463202 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/command/action.rb:38:in `block in call'
|
15
|
+
E, [2015-11-01T00:01:20.463232 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/2.1.0/benchmark.rb:279:in `measure'
|
16
|
+
E, [2015-11-01T00:01:20.463250 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/command/action.rb:37:in `call'
|
17
|
+
E, [2015-11-01T00:01:20.463279 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/cli.rb:56:in `perform'
|
18
|
+
E, [2015-11-01T00:01:20.463298 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/cli.rb:171:in `block (2 levels) in <class:CLI>'
|
19
|
+
E, [2015-11-01T00:01:20.463317 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
|
20
|
+
E, [2015-11-01T00:01:20.463336 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
|
21
|
+
E, [2015-11-01T00:01:20.463366 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/cli.rb:308:in `invoke_task'
|
22
|
+
E, [2015-11-01T00:01:20.463387 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor.rb:359:in `dispatch'
|
23
|
+
E, [2015-11-01T00:01:20.463407 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
|
24
|
+
E, [2015-11-01T00:01:20.463427 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/bin/kitchen:13:in `block in <top (required)>'
|
25
|
+
E, [2015-11-01T00:01:20.463447 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/lib/kitchen/errors.rb:154:in `with_friendly_errors'
|
26
|
+
E, [2015-11-01T00:01:20.463466 #7769] ERROR -- Kitchen: /opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/test-kitchen-1.4.2/bin/kitchen:13:in `<top (required)>'
|
27
|
+
E, [2015-11-01T00:01:20.463485 #7769] ERROR -- Kitchen: /opt/chefdk/bin/kitchen:14:in `load'
|
28
|
+
E, [2015-11-01T00:01:20.463523 #7769] ERROR -- Kitchen: /opt/chefdk/bin/kitchen:14:in `<main>'
|
29
|
+
E, [2015-11-01T00:01:20.463543 #7769] ERROR -- Kitchen: ----------------------
|
data/:w
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Braden Wright (<braden.m.wright@gmail.com>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2015, Braden Wright
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'kitchen'
|
20
|
+
|
21
|
+
module Kitchen
|
22
|
+
|
23
|
+
module Driver
|
24
|
+
|
25
|
+
# LxdCli driver for Kitchen.
|
26
|
+
#
|
27
|
+
# @author Braden Wright <braden.m.wright@gmail.com>
|
28
|
+
# class LxdCli < Kitchen::Driver::SSHBase
|
29
|
+
class LxdCli < Kitchen::Driver::Base
|
30
|
+
kitchen_driver_api_version 2
|
31
|
+
|
32
|
+
default_config :public_key_path do
|
33
|
+
[
|
34
|
+
File.expand_path('~/.ssh/id_rsa.pub'),
|
35
|
+
File.expand_path('~/.ssh/id_dsa.pub'),
|
36
|
+
File.expand_path('~/.ssh/identity.pub'),
|
37
|
+
File.expand_path('~/.ssh/id_ecdsa.pub')
|
38
|
+
].find { |path| File.exist?(path) }
|
39
|
+
end
|
40
|
+
|
41
|
+
required_config :public_key_path
|
42
|
+
|
43
|
+
def create(state)
|
44
|
+
if exists?
|
45
|
+
if running?
|
46
|
+
debug("#{instance.name} already exists, and is already running. Nothing to do")
|
47
|
+
else
|
48
|
+
debug("#{instance.name} already exists, starting instead")
|
49
|
+
run_lxc_command("start #{instance.name}")
|
50
|
+
end
|
51
|
+
else
|
52
|
+
image_name = create_image_if_missing
|
53
|
+
profile = "-p #{config[:profile]}" if config[:profile]
|
54
|
+
lxc_config = "-c #{config[:config]}" if config[:config]
|
55
|
+
run_lxc_command("launch #{image_name} #{instance.name} #{profile} #{lxc_config}")
|
56
|
+
end
|
57
|
+
ip_address(state)
|
58
|
+
setup_ssh_access
|
59
|
+
|
60
|
+
conn(state).execute("touch testing")
|
61
|
+
# instance.transport.connection(state).login_command #fails
|
62
|
+
end
|
63
|
+
|
64
|
+
def destroy(state)
|
65
|
+
if exists?
|
66
|
+
if running?
|
67
|
+
run_lxc_command("stop #{instance.name}")
|
68
|
+
else
|
69
|
+
debug("#{instance.name} isn't running, just destroying instead")
|
70
|
+
end
|
71
|
+
run_lxc_command("delete #{instance.name}")
|
72
|
+
else
|
73
|
+
debug("#{instance.name} doesn't exist. Nothing to do")
|
74
|
+
end
|
75
|
+
state.delete(:hostname)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def exists?
|
80
|
+
status = `lxc info #{instance.name} > /dev/null 2>&1 && echo $?`.chomp
|
81
|
+
if "#{status}" == "0"
|
82
|
+
debug("#{instance.name} exists")
|
83
|
+
return true
|
84
|
+
else
|
85
|
+
debug("#{instance.name} doesn't exist")
|
86
|
+
return false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def running?
|
91
|
+
status = `lxc info #{instance.name}`.match(/Status: ([a-zA-Z]+)[\n]/).captures[0].upcase
|
92
|
+
if status == "RUNNING"
|
93
|
+
debug("#{instance.name} is running")
|
94
|
+
return true
|
95
|
+
else
|
96
|
+
debug("#{instance.name} isn't running")
|
97
|
+
return false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_image_if_missing
|
102
|
+
image_name = config[:image_name] ||= instance.platform.name
|
103
|
+
status = `lxc image show #{image_name} > /dev/null 2>&1 && echo $?`.chomp
|
104
|
+
if "#{status}" == "0"
|
105
|
+
debug("Image #{image_name} exists")
|
106
|
+
else
|
107
|
+
debug("Image #{image_name} doesn't exist, creating now.")
|
108
|
+
image = get_ubuntu_image_info
|
109
|
+
image_os = config[:image_os] ||= image[:os]
|
110
|
+
image_release = config[:image_release] ||= image[:release]
|
111
|
+
debug("lxd-images import #{image_os} #{image_release} --alias #{image_name}")
|
112
|
+
run_local_command("lxd-images import #{image_os} #{image_release} --alias #{image_name}")
|
113
|
+
end
|
114
|
+
return image_name
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_ubuntu_image_info
|
118
|
+
platform, release = instance.platform.name.split('-')
|
119
|
+
if platform.downcase == "ubuntu"
|
120
|
+
case release.downcase
|
121
|
+
when "14.04", "1404", "trusty", "", nil
|
122
|
+
image = { :os => platform, :release => "trusty" }
|
123
|
+
when "14.10", "1410", "utopic"
|
124
|
+
image = { :os => platform, :release => "utopic" }
|
125
|
+
when "15.04", "1504", "vivid"
|
126
|
+
image = { :os => platform, :release => "vivid" }
|
127
|
+
when "15.10", "1510", "wily"
|
128
|
+
image = { :os => platform, :release => "wily" }
|
129
|
+
when "16.04", "1604", "xenial"
|
130
|
+
image = { :os => platform, :release => "xenial" }
|
131
|
+
else
|
132
|
+
image = { :os => platform, :release => release }
|
133
|
+
end
|
134
|
+
else
|
135
|
+
image = { :os => platform, :release => release }
|
136
|
+
end
|
137
|
+
return image
|
138
|
+
end
|
139
|
+
|
140
|
+
def ip_address(state)
|
141
|
+
debug("start ip")
|
142
|
+
begin
|
143
|
+
lxc_info = `lxc info #{instance.name}`
|
144
|
+
end while (!lxc_info.match(/eth0:[\t]IPV[46][\t]([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*[\n]/))
|
145
|
+
conn(state).execute("-- ifconfig eth0 #{config[:ipv4]} up") if config[:ipv4]
|
146
|
+
# run_local_command("lxc exec #{instance.name} -- ifconfig eth0 #{config[:ipv4]} up") if config[:ipv4]
|
147
|
+
lxc_ip = config[:ipv4] ||= lxc_info.match(/eth0:[\t]IPV[46][\t]([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*[\n]/).captures[0].to_s
|
148
|
+
debug("found ip #{lxc_ip}")
|
149
|
+
state[:hostname] = lxc_ip
|
150
|
+
return lxc_ip
|
151
|
+
end
|
152
|
+
|
153
|
+
def setup_ssh_access
|
154
|
+
info("Copying public key from #{config[:public_key_path]} to #{instance.name}")
|
155
|
+
begin
|
156
|
+
sleep 0.3
|
157
|
+
status = `lxc file push #{config[:public_key_path]} #{instance.name}/root/.ssh/authorized_keys 2> /dev/null && echo $?`.chomp
|
158
|
+
end while ("#{status}" != "0")
|
159
|
+
end
|
160
|
+
|
161
|
+
def run_lxc_command(cmd)
|
162
|
+
# should be moved to transport and run_command used
|
163
|
+
#conn(state).run_lxc_command("stop #{instance.name}")
|
164
|
+
`lxc #{cmd}` if cmd
|
165
|
+
end
|
166
|
+
|
167
|
+
def run_local_command(cmd)
|
168
|
+
`#{cmd}` if cmd
|
169
|
+
end
|
170
|
+
|
171
|
+
def conn(state)
|
172
|
+
return instance.transport.connection(state)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
A Test Kitchen Driver for LxdCli.
|
4
4
|
|
5
|
+
At this point would NOT recommend using in Production.
|
6
|
+
|
5
7
|
## <a name="overview"></a> Overview
|
6
8
|
|
7
9
|
This is a test-kitchen driver for lxd, which controls lxc. I named it LxdCli because this is my first plugin and I wanted to leave LXD driver name in case a more extensive project is put together.
|
@@ -12,16 +14,43 @@ I started the project because I really like the idea of developing containers, b
|
|
12
14
|
|
13
15
|
I AM VERY OPEN TO SUGGESTIONS/HELP. As I mentioned I haven't written a kitchen driver or published any ruby gems before so I was hesitant to even release it.
|
14
16
|
|
17
|
+
Minimal .kitchen.yml
|
15
18
|
```yaml
|
16
19
|
---
|
17
20
|
driver:
|
18
21
|
name: lxd_cli
|
19
|
-
# optional: searches ~/.ssh/ by default for public key
|
20
|
-
# public_key_path: "~/.ssh/id_rsa.pub"
|
21
22
|
|
22
23
|
provisioner:
|
23
24
|
name: chef_zero
|
24
|
-
|
25
|
+
|
26
|
+
platforms:
|
27
|
+
- name: ubuntu-14.04
|
28
|
+
|
29
|
+
suites:
|
30
|
+
- name: default
|
31
|
+
run_list:
|
32
|
+
attributes:
|
33
|
+
```
|
34
|
+
|
35
|
+
All Options Shown .kitchen.yml
|
36
|
+
```yaml
|
37
|
+
---
|
38
|
+
driver:
|
39
|
+
name: lxd_cli
|
40
|
+
public_key_path: "/my/path/id_rsa.pub"
|
41
|
+
image_name: my-ubuntu-image
|
42
|
+
image_os: ubuntu
|
43
|
+
image_release: trusty
|
44
|
+
profile: my_lxc_profile
|
45
|
+
config: limits.memory=2G
|
46
|
+
domain_name: localdomain
|
47
|
+
dns_servers: ["8.8.8.8","8.8.4.4"]
|
48
|
+
ipv4: 10.0.3.99/24
|
49
|
+
ip_gateway: 10.0.3.1
|
50
|
+
stop_instead_of_destroy: true
|
51
|
+
|
52
|
+
provisioner:
|
53
|
+
name: chef_zero
|
25
54
|
|
26
55
|
platforms:
|
27
56
|
# Following 3 will all use the same image (LTS), but it will be named accordingly
|
@@ -32,28 +61,20 @@ platforms:
|
|
32
61
|
- name: ubuntu-15.10
|
33
62
|
- name: ubuntu-1510
|
34
63
|
- name: ubuntu-wily
|
35
|
-
```
|
36
64
|
|
37
|
-
|
65
|
+
suites:
|
66
|
+
- name: web
|
67
|
+
driver_config:
|
68
|
+
ipv4: 10.0.3.31/24
|
69
|
+
run_list:
|
70
|
+
- name: db
|
71
|
+
driver_config:
|
72
|
+
ipv4: 10.0.3.32/24
|
73
|
+
run_list:
|
38
74
|
|
39
|
-
|
40
|
-
|
41
|
-
1) Creates image if its missing. If a platform is specified LXD will check if an image with the same name exists, if not it will parse the platform name, and install the appropriate image. If the image exists it will use that image.
|
42
|
-
|
43
|
-
2) If container does not exists, creates container. Using the instance name as the container name, and using the image from previous step.
|
44
|
-
|
45
|
-
3) If container exists, starts it
|
46
|
-
|
47
|
-
4) copies public key from ~/.ssh/ to container /root/.ssh/
|
48
|
-
|
49
|
-
|
50
|
-
Note: if you have troubles with the public key, you can always manually setup public key on container, and publish it to an image. If the image already exists it will not be download via test-kitchen.
|
51
|
-
|
52
|
-
Kitchen Destroy
|
53
|
-
|
54
|
-
1) Stops container if its running
|
75
|
+
```
|
55
76
|
|
56
|
-
|
77
|
+
As of now if you install lxd, and have a public key setup in ~/.ssh/ then you should be able to use plugin without manual intervention. I have only tested with Ubuntu containers. Good chance most things will work in other Platforms, but some networking config is Ubuntu/Debian specific (dns_servers, domain_name).
|
57
78
|
|
58
79
|
LXD Links:
|
59
80
|
|
@@ -63,17 +84,91 @@ LXD Links:
|
|
63
84
|
|
64
85
|
## <a name="installation"></a> Installation and Setup
|
65
86
|
|
87
|
+
Must have LXD installed on OS, I've only tested on Ubuntu 15.10
|
88
|
+
|
89
|
+
Install on command line:
|
90
|
+
```
|
91
|
+
gem install kitchen-lxd_cli
|
92
|
+
```
|
93
|
+
or use bundler:
|
94
|
+
```
|
95
|
+
gem "kitchen-lxd_cli"
|
96
|
+
|
97
|
+
gem "kitchen-lxd_cli", :github => "bradenwright/kitchen-lxd_cli"
|
98
|
+
|
99
|
+
gem "kitchen-lxd_cli", :path => "~/kitchen-lxd_cli"
|
100
|
+
```
|
66
101
|
Please read the [Driver usage][driver_usage] page for more details.
|
67
102
|
|
68
103
|
## <a name="config"></a> Configuration
|
69
104
|
|
70
|
-
|
105
|
+
Current config options:
|
106
|
+
|
107
|
+
* image_name
|
108
|
+
* image_os
|
109
|
+
* image_release
|
110
|
+
* profile
|
111
|
+
* config
|
112
|
+
* domain_name
|
113
|
+
* dns_servers
|
114
|
+
* ipv4
|
115
|
+
* ip_gateway
|
116
|
+
* stop_instead_of_destroy
|
117
|
+
|
118
|
+
public_key_path: /my/path/public_key_file
|
119
|
+
|
120
|
+
can be manual set otherwise is derived by default based
|
71
121
|
~/.ssh/ directory, specifically the setting is derived by searching for:
|
72
122
|
- `~/.ssh/id_rsa.pub`
|
73
123
|
- `~/.ssh/id_dsa.pub`
|
74
124
|
- `~/.ssh/identity.pub`
|
75
125
|
- `~/.ssh/id_ecdsa.pub`
|
76
126
|
|
127
|
+
The public key at this location is copied to /root/.ssh in the lxc container to allow password less login.
|
128
|
+
|
129
|
+
image_name:
|
130
|
+
|
131
|
+
Defaults to platform.name. Also note that if the image exists it will be used and will not be created. This allows a container to be published (image manually created).
|
132
|
+
|
133
|
+
image_os:
|
134
|
+
|
135
|
+
By default platform.name is split on "-" and the first element is used. E.G. platform.name = ubuntu-14.04 then image_os = ubuntu
|
136
|
+
|
137
|
+
image_release:
|
138
|
+
|
139
|
+
By default platform.name is split on "-" and the second element is used. E.G. platform.name = ubuntu-14.04 the image_release = 14.04 For ubuntu 14.04 is changed to release name, E.G. trusty, when image is being downloaded. For Ubuntu trusty, 14.04, 1404 will all result in the same image being used, it will just be named different depending. It may work for OS's other than ubuntu but have not tested
|
140
|
+
|
141
|
+
profile:
|
142
|
+
|
143
|
+
Default is Nil. See LXC documentation but a lxc profile can be specified. Which will be passed to "lxc init" command
|
144
|
+
|
145
|
+
config:
|
146
|
+
|
147
|
+
Default is Nil. See LXC documentation but a lxc config container key/value can be specified. [LXC Container Config Options](https://github.com/lxc/lxd/blob/master/specs/configuration.md#keyvalue-configuration-1) Again option is passed to "lxc launch" command.
|
148
|
+
|
149
|
+
domain_name:
|
150
|
+
|
151
|
+
Default is nil.
|
152
|
+
|
153
|
+
dns_server:
|
154
|
+
|
155
|
+
Default is nil. Which is used for dhcp, if a static ip is setup then dns servers need to be configured for chef to work. If a static ip is supplied and no dns_server is specified it will try to use the default gateway, google dns (e.g. 10.0.3.1, 8.8.8.8, 8.8.4.4). If a default gateway is not specified or can't be located then only google dns (8.8.8.8, 8.8.4.4) will be used. A hash of dns_servers may be specified instead.
|
156
|
+
|
157
|
+
LXC NETWORKING OPTIONS: LXC by default uses 10.0.3.2/24 with a gateway of 10.0.3.1. You may use any ip space you wish, but LXD/LXC install sets up an ethernet bridge on 10.0.3.0/24 so make sure whatever network you choose to use is configured and accessible. In Ubuntu 15.10, LXD/LXC 0.20 configuration for networking is located at /etc/default/lxc-net, another option other than static ips is to setup dhcp host/ip mappings to reserve ips (described in /etc/default/lxc-net file comments) but it didn't seem to be working for me. You can also configure DHCP scope, etc. I've been using static ips from 10.0.3.0/24 and those ips have worked without needing to make changes to LXD/LXC configuration, although I did change the DHCP scope to allow space for static ip addresses (just to make sure there wasn't accidentally overlap)
|
158
|
+
|
159
|
+
ipv4:
|
160
|
+
|
161
|
+
Allows for Static IP/CIDR to be set, currently netmask is not supported. E.g. 10.0.3.100/24
|
162
|
+
|
163
|
+
ip_gateway:
|
164
|
+
|
165
|
+
Allows for a default gateway to be set. If ipv4 is used ip_gateway default value is 10.0.3.1, if dhcp is used then default gateway is given via dhcp. Default gateway is also used as a dns_server if static ips are used and no dns server is specified.
|
166
|
+
|
167
|
+
|
168
|
+
stop_instead_of_destroy:
|
169
|
+
|
170
|
+
Default is false. Can be useful sometimes to keep machines intact. It allows kitchen destroy to stop container, kitchen create can be issued to start boxes if they are not running.
|
171
|
+
|
77
172
|
### <a name="config-require-chef-omnibus"></a> require\_chef\_omnibus
|
78
173
|
|
79
174
|
Determines whether or not a Chef [Omnibus package][chef_omnibus_dl] will be
|
@@ -93,11 +188,12 @@ The default value is unset, or `nil`.
|
|
93
188
|
## <a name="roadmap"></a> Roadmap
|
94
189
|
* Update/Clean README
|
95
190
|
* Config option for remote host, remote user, etc. So lxc can be launched on a remote server, not just locally
|
96
|
-
* Config option for
|
191
|
+
* Config option to add remote for lxc, so images can be downloaded from other locations. Currently would have to manually be done in LXD/LXC
|
97
192
|
* Config option to publish container on verify
|
98
|
-
* Config options for static ip, cpu, mem
|
99
193
|
* Config option to install upstart (not used by default in containers)
|
100
194
|
* Config option for proxy/cache
|
195
|
+
* Ability to set more than 1 config (key/value)
|
196
|
+
* Example chef cookbook which uses this driver to setup a multi-node web application
|
101
197
|
|
102
198
|
## <a name="development"></a> Development
|
103
199
|
|
@@ -123,9 +219,9 @@ Created and maintained by [Braden Wright][author] (<braden.m.wright@gmail.com>)
|
|
123
219
|
Apache 2.0 (see [LICENSE][license])
|
124
220
|
|
125
221
|
|
126
|
-
[author]: https://github.com/
|
127
|
-
[issues]: https://github.com/
|
128
|
-
[license]: https://github.com/
|
129
|
-
[repo]: https://github.com/
|
222
|
+
[author]: https://github.com/bradenwright
|
223
|
+
[issues]: https://github.com/bradenwright/kitchen-lxd_cli/issues
|
224
|
+
[license]: https://github.com/bradenwright/kitchen-lxd_cli/blob/master/LICENSE
|
225
|
+
[repo]: https://github.com/bradenwright/kitchen-lxd_cli
|
130
226
|
[driver_usage]: http://docs.kitchen-ci.org/drivers/usage
|
131
227
|
[chef_omnibus_dl]: http://www.getchef.com/chef/install/
|
data/kitchen-lxd_cli.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Kitchen::Driver::LXD_CLI_VERSION
|
9
9
|
spec.authors = ['Braden Wright']
|
10
10
|
spec.email = ['braden.m.wright@gmail.com']
|
11
|
-
spec.description = %q{A Test Kitchen Driver for
|
11
|
+
spec.description = %q{A Test Kitchen Driver for LXD}
|
12
12
|
spec.summary = spec.description
|
13
|
-
spec.homepage = ''
|
13
|
+
spec.homepage = 'https://github.com/bradenwright/kitchen-lxd_cli'
|
14
14
|
spec.license = 'Apache 2.0'
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -25,7 +25,9 @@ module Kitchen
|
|
25
25
|
# LxdCli driver for Kitchen.
|
26
26
|
#
|
27
27
|
# @author Braden Wright <braden.m.wright@gmail.com>
|
28
|
-
class LxdCli < Kitchen::Driver::
|
28
|
+
class LxdCli < Kitchen::Driver::Base
|
29
|
+
kitchen_driver_api_version 2
|
30
|
+
|
29
31
|
default_config :public_key_path do
|
30
32
|
[
|
31
33
|
File.expand_path('~/.ssh/id_rsa.pub'),
|
@@ -34,45 +36,46 @@ module Kitchen
|
|
34
36
|
File.expand_path('~/.ssh/id_ecdsa.pub')
|
35
37
|
].find { |path| File.exist?(path) }
|
36
38
|
end
|
39
|
+
default_config :stop_instead_of_destroy, false
|
40
|
+
|
41
|
+
required_config :public_key_path
|
37
42
|
|
38
43
|
def create(state)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
else
|
47
|
-
create_image_if_missing
|
48
|
-
run_command("lxc launch #{instance.platform.name} #{instance.name}")
|
44
|
+
unless exists?
|
45
|
+
image_name = create_image_if_missing
|
46
|
+
profile = "-p #{config[:profile]}" if config[:profile]
|
47
|
+
lxc_config = "-c #{config[:config]}" if config[:config]
|
48
|
+
info("Initializing container #{instance.name}")
|
49
|
+
run_lxc_command("init #{image_name} #{instance.name} #{profile} #{lxc_config}")
|
49
50
|
end
|
50
|
-
|
51
|
+
|
52
|
+
config_and_start_container unless running?
|
53
|
+
configure_dns
|
54
|
+
lxc_ip = wait_for_ip_address
|
55
|
+
state[:hostname] = lxc_ip
|
51
56
|
setup_ssh_access
|
52
57
|
end
|
53
58
|
|
54
59
|
def destroy(state)
|
55
60
|
if exists?
|
56
61
|
if running?
|
57
|
-
|
58
|
-
|
59
|
-
debug("#{instance.name} isn't running, just destroying instead")
|
62
|
+
info("Stopping container #{instance.name}")
|
63
|
+
run_lxc_command("stop #{instance.name}")
|
60
64
|
end
|
61
|
-
|
62
|
-
|
63
|
-
debug("#{instance.name} doesn't exist. Nothing to do")
|
65
|
+
info("Deleting container #{instance.name}")
|
66
|
+
run_lxc_command("delete #{instance.name}") unless config[:stop_instead_of_destroy]
|
64
67
|
end
|
65
68
|
state.delete(:hostname)
|
66
69
|
end
|
67
70
|
|
68
71
|
private
|
69
72
|
def exists?
|
70
|
-
|
71
|
-
if
|
72
|
-
debug("#{instance.name} exists")
|
73
|
+
`lxc info #{instance.name} > /dev/null 2>&1`
|
74
|
+
if $?.to_i == 0
|
75
|
+
debug("Container #{instance.name} exists")
|
73
76
|
return true
|
74
77
|
else
|
75
|
-
debug("#{instance.name} doesn't exist")
|
78
|
+
debug("Container #{instance.name} doesn't exist")
|
76
79
|
return false
|
77
80
|
end
|
78
81
|
end
|
@@ -80,29 +83,30 @@ module Kitchen
|
|
80
83
|
def running?
|
81
84
|
status = `lxc info #{instance.name}`.match(/Status: ([a-zA-Z]+)[\n]/).captures[0].upcase
|
82
85
|
if status == "RUNNING"
|
83
|
-
debug("#{instance.name} is running")
|
86
|
+
debug("Container #{instance.name} is running")
|
84
87
|
return true
|
85
88
|
else
|
86
|
-
debug("#{instance.name} isn't running")
|
89
|
+
debug("Container #{instance.name} isn't running")
|
87
90
|
return false
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
91
94
|
def create_image_if_missing
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
95
|
+
image_name = config[:image_name] ||= instance.platform.name
|
96
|
+
`lxc image show #{image_name} > /dev/null 2>&1`
|
97
|
+
if $?.to_i == 0
|
98
|
+
debug("Image #{image_name} exists")
|
96
99
|
else
|
97
|
-
|
98
|
-
image =
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
info("Image #{image_name} doesn't exist, creating now. May take a few minutes.")
|
101
|
+
image = get_image_info
|
102
|
+
image_os = config[:image_os] ||= image[:os]
|
103
|
+
image_release = config[:image_release] ||= image[:release]
|
104
|
+
run_local_command("lxd-images import #{image_os} #{image_release} --alias #{image_name}")
|
102
105
|
end
|
106
|
+
return image_name
|
103
107
|
end
|
104
108
|
|
105
|
-
def
|
109
|
+
def get_image_info
|
106
110
|
platform, release = instance.platform.name.split('-')
|
107
111
|
if platform.downcase == "ubuntu"
|
108
112
|
case release.downcase
|
@@ -119,25 +123,163 @@ module Kitchen
|
|
119
123
|
else
|
120
124
|
image = { :os => platform, :release => release }
|
121
125
|
end
|
122
|
-
|
126
|
+
else
|
127
|
+
image = { :os => platform, :release => release }
|
128
|
+
end
|
129
|
+
return image
|
130
|
+
end
|
131
|
+
|
132
|
+
def config_and_start_container
|
133
|
+
config[:ip_gateway] ||= "auto"
|
134
|
+
arg_disable_dhcp = ""
|
135
|
+
if config[:ipv4]
|
136
|
+
IO.popen("bash", "r+") do |p|
|
137
|
+
p.puts("echo -e \"lxc.network.type = veth\nlxc.network.name = eth0\nlxc.network.link = lxcbr0\nlxc.network.ipv4 = #{config[:ipv4]}\nlxc.network.ipv4.gateway = #{config[:ip_gateway]}\nlxc.network.flags = up\" | lxc config set #{instance.name} raw.lxc -")
|
138
|
+
p.puts("exit")
|
139
|
+
end
|
140
|
+
arg_disable_dhcp = "&& lxc exec #{instance.name} -- sed -i 's/dhcp/manual/g' /etc/network/interfaces.d/eth0.cfg"
|
141
|
+
end
|
142
|
+
# TODO: loop over/run all lxc config settings passed in or figure out how to use multiple with lxc init
|
143
|
+
info("Starting container #{instance.name}")
|
144
|
+
run_lxc_command("start #{instance.name} #{arg_disable_dhcp}")
|
145
|
+
end
|
146
|
+
|
147
|
+
def configure_dns
|
148
|
+
IO.popen("lxc exec #{instance.name} bash", "r+") do |p|
|
149
|
+
dns_servers = ""
|
150
|
+
config[:dns_servers].each do |dns_server|
|
151
|
+
dns_servers += "nameserver #{dns_server}\n"
|
152
|
+
end if config[:dns_servers]
|
153
|
+
|
154
|
+
case config[:ip_gateway]
|
155
|
+
when "auto", ""
|
156
|
+
dns_servers = "nameserver 8.8.8.8\nnameserver 8.8.4.4"
|
157
|
+
dns_servers = "nameserver 8.8.8.8\nnameserver 8.8.4.4"
|
158
|
+
else
|
159
|
+
dns_servers = "nameserver #{config[:ip_gateway]}\nnameserver 8.8.8.8\nnameserver 8.8.4.4"
|
160
|
+
end if config[:ipv4] && dns_servers.length == 0
|
161
|
+
|
162
|
+
if dns_servers.length > 0
|
163
|
+
wait_for_path("/etc/resolvconf/resolv.conf.d/base")
|
164
|
+
debug("Setting up the following dns servers via /etc/resolvconf/resolv.conf.d/base:")
|
165
|
+
debug(dns_servers.gsub("\n", ' '))
|
166
|
+
p.puts(" echo \"#{dns_servers.chomp}\" > /etc/resolvconf/resolv.conf.d/base")
|
167
|
+
p.puts("resolvconf -u")
|
168
|
+
end
|
169
|
+
|
170
|
+
debug("Setting up /etc/hosts")
|
171
|
+
if config[:domain_name]
|
172
|
+
# p.puts("echo -e \" dns-search #{config[:domain_name]}\" >> /etc/network/interfaces.d/eth0.cfg")
|
173
|
+
args_host = "#{instance.name}.#{config[:domain_name]} #{instance.name}"
|
174
|
+
end
|
175
|
+
args_host ||= "#{instance.name}"
|
176
|
+
wait_for_path("/etc/hosts")
|
177
|
+
p.puts("if grep -iq '127.0.1.1' /etc/hosts; then")
|
178
|
+
p.puts("sed -i 's/^127.0.1.1.*$/127.0.1.1\t#{args_host}/' /etc/hosts")
|
179
|
+
p.puts("else echo '#***** Setup by Kitchen-LxdCli driver *****#' >> /etc/hosts")
|
180
|
+
p.puts("echo -e '127.0.1.1\t#{args_host}' >> /etc/hosts; fi")
|
181
|
+
p.puts("exit")
|
123
182
|
end
|
124
183
|
end
|
184
|
+
=begin
|
185
|
+
def configure_ip_via_lxc_restart
|
186
|
+
debug("Configuring new ip address on eth0")
|
187
|
+
|
188
|
+
IO.popen("lxc exec #{instance.name} bash", "r+") do |p|
|
189
|
+
p.puts('echo -e "#############################################" > /etc/network/interfaces.d/eth0.cfg')
|
190
|
+
p.puts('echo -e "# DO NOT EDIT CONTROLLED BY KITCHEN-LXC_CLI #" >> /etc/network/interfaces.d/eth0.cfg')
|
191
|
+
p.puts('echo -e "#############################################" >> /etc/network/interfaces.d/eth0.cfg')
|
192
|
+
p.puts('echo -e "auto eth0" >> /etc/network/interfaces.d/eth0.cfg')
|
193
|
+
if config[:ipv4]
|
194
|
+
config[:ip_gateway] ||= "10.0.3.1"
|
195
|
+
config[:dns_servers] ||= [ "8.8.8.8", "8.8.4.4" ]
|
196
|
+
p.puts('echo -e " iface eth0 inet static" >> /etc/network/interfaces.d/eth0.cfg')
|
197
|
+
p.puts("echo -e \" address #{config[:ipv4]}\" >> /etc/network/interfaces.d/eth0.cfg")
|
198
|
+
else
|
199
|
+
p.puts('echo -e " iface eth0 inet dhcp" >> /etc/network/interfaces.d/eth0.cfg')
|
200
|
+
end
|
201
|
+
p.puts("echo -e \" gateway #{config[:ip_gateway]}\" >> /etc/network/interfaces.d/eth0.cfg") if config[:ip_gateway]
|
202
|
+
config[:dns_servers].each do |dns_server|
|
203
|
+
p.puts("echo -e \" dns-nameserver #{dns_server}\" >> /etc/network/interfaces.d/eth0.cfg")
|
204
|
+
end if config[:dns_servers]
|
205
|
+
if config[:domain_name]
|
206
|
+
p.puts("echo -e \" dns-search #{config[:domain_name]}\" >> /etc/network/interfaces.d/eth0.cfg")
|
207
|
+
end
|
208
|
+
p.puts("exit")
|
209
|
+
end
|
210
|
+
debug("Finished configuring new ip address, restarting #{instance.name} for settings to take effect")
|
211
|
+
debug_note_about_configuring_ip
|
212
|
+
wait_for_ip_address
|
213
|
+
sleep 3 # Was hanging more often than not whenever I lowered the sleep
|
214
|
+
debug("Restarting #{instance.name}")
|
215
|
+
run_lxc_command("restart #{instance.name}")
|
216
|
+
debug("Finished restarting #{instance.name} ip address should be configured")
|
217
|
+
end
|
218
|
+
=end
|
219
|
+
|
220
|
+
def setup_ssh_access
|
221
|
+
info("Setting up public key #{config[:public_key_path]} on #{instance.name}")
|
222
|
+
wait_for_path("/root/.ssh")
|
223
|
+
|
224
|
+
begin
|
225
|
+
debug("Uploading public key...")
|
226
|
+
`lxc file push #{config[:public_key_path]} #{instance.name}/root/.ssh/authorized_keys 2> /dev/null`
|
227
|
+
break if $?.to_i == 0
|
228
|
+
sleep 0.3
|
229
|
+
end while true
|
230
|
+
|
231
|
+
debug("Finished Copying public key from #{config[:public_key_path]} to #{instance.name}")
|
232
|
+
end
|
125
233
|
|
126
|
-
def
|
234
|
+
def wait_for_ip_address
|
235
|
+
info("Waiting for network to become ready")
|
127
236
|
begin
|
128
|
-
lxc_info = `lxc info #{instance.name}
|
129
|
-
|
130
|
-
|
131
|
-
|
237
|
+
lxc_info = `lxc info #{instance.name}`.match(/^[ ]+eth0:[\t]IPV4[\t]([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*$/)
|
238
|
+
debug("Still waiting for IP Address...")
|
239
|
+
lxc_ip = lxc_info.captures[0].to_s if lxc_info && lxc_info.captures
|
240
|
+
break if lxc_ip && lxc_ip.length > 7
|
241
|
+
sleep 0.3
|
242
|
+
end while true
|
243
|
+
debug("Found Ip Address #{lxc_ip}")
|
132
244
|
return lxc_ip
|
133
245
|
end
|
134
246
|
|
247
|
+
def wait_for_path(path)
|
248
|
+
begin
|
249
|
+
debug("Waiting for #{path} to become available...")
|
250
|
+
run_lxc_command("exec #{instance.name} -- ls #{path}")
|
251
|
+
break if $?.to_i == 0
|
252
|
+
sleep 0.3
|
253
|
+
end while true
|
254
|
+
debug("Found #{path}")
|
255
|
+
end
|
256
|
+
|
135
257
|
def setup_ssh_access
|
136
|
-
info("
|
258
|
+
info("Setting up public key #{config[:public_key_path]} on #{instance.name}")
|
259
|
+
wait_for_path("/root/.ssh")
|
260
|
+
|
137
261
|
begin
|
138
|
-
|
139
|
-
|
140
|
-
|
262
|
+
debug("Uploading public key...")
|
263
|
+
`lxc file push #{config[:public_key_path]} #{instance.name}/root/.ssh/authorized_keys 2> /dev/null`
|
264
|
+
break if $?.to_i == 0
|
265
|
+
sleep 0.3
|
266
|
+
end while true
|
267
|
+
|
268
|
+
debug("Finished Copying public key from #{config[:public_key_path]} to #{instance.name}")
|
269
|
+
end
|
270
|
+
|
271
|
+
def run_lxc_command(cmd)
|
272
|
+
run_local_command("lxc #{cmd}") if cmd
|
273
|
+
end
|
274
|
+
|
275
|
+
def run_local_command(cmd)
|
276
|
+
debug("run_local_command ran: #{cmd}")
|
277
|
+
`#{cmd}` if cmd
|
278
|
+
debug("Command finished: #{$?.to_s}")
|
279
|
+
end
|
280
|
+
|
281
|
+
def debug_note_about_configuring_ip
|
282
|
+
debug("NOTE: Restarting seemed to be the only way I could get things to work. Tried lxc profiles, config options. Tried restart networking service but it didn't work, also tried passing command like ifconfig 10.0.3.x/24 eth0 up. Which set the ip but after container ran for a while, it would reset to dhcp address that had been assigned. Restarting container seems to be working, and is really fast. Open to better alternatives.")
|
141
283
|
end
|
142
284
|
end
|
143
285
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-lxd_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Braden Wright
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-kitchen
|
@@ -94,7 +94,7 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description: A Test Kitchen Driver for
|
97
|
+
description: A Test Kitchen Driver for LXD
|
98
98
|
email:
|
99
99
|
- braden.m.wright@gmail.com
|
100
100
|
executables: []
|
@@ -103,8 +103,10 @@ extra_rdoc_files: []
|
|
103
103
|
files:
|
104
104
|
- ".cane"
|
105
105
|
- ".gitignore"
|
106
|
+
- ".kitchen/logs/kitchen.log"
|
106
107
|
- ".tailor"
|
107
108
|
- ".travis.yml"
|
109
|
+
- ":w"
|
108
110
|
- CHANGELOG.md
|
109
111
|
- Gemfile
|
110
112
|
- LICENSE
|
@@ -113,7 +115,7 @@ files:
|
|
113
115
|
- kitchen-lxd_cli.gemspec
|
114
116
|
- lib/kitchen/driver/lxd_cli.rb
|
115
117
|
- lib/kitchen/driver/lxd_cli_version.rb
|
116
|
-
homepage:
|
118
|
+
homepage: https://github.com/bradenwright/kitchen-lxd_cli
|
117
119
|
licenses:
|
118
120
|
- Apache 2.0
|
119
121
|
metadata: {}
|
@@ -128,14 +130,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
130
|
version: '0'
|
129
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
132
|
requirements:
|
131
|
-
- - "
|
133
|
+
- - ">="
|
132
134
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
135
|
+
version: '0'
|
134
136
|
requirements: []
|
135
137
|
rubyforge_project:
|
136
138
|
rubygems_version: 2.4.8
|
137
139
|
signing_key:
|
138
140
|
specification_version: 4
|
139
|
-
summary: A Test Kitchen Driver for
|
141
|
+
summary: A Test Kitchen Driver for LXD
|
140
142
|
test_files: []
|
141
143
|
has_rdoc:
|