lux 0.9 → 1.0.2
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/README.md +105 -37
- data/lib/lux/docker_tasks.rb +106 -0
- data/lib/lux/version.rb +1 -1
- metadata +3 -3
- data/lib/lux/dockertasks.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 840ad1a2f55f5395e18d76976357486af1f860fa
|
4
|
+
data.tar.gz: 4b954b5fababffa6b24ead4841a9245f85308547
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 063f969054657465283c3407ecb61042cc9670d0ec8b6e71ae59fe53fb6dc073413cfd4226c867e295accc7a74db99023fa05a168986f59924f7584453ad8ba1
|
7
|
+
data.tar.gz: 1bced056319fa9ec7512b4bbe7d282d68c92bb4714e4f2eb616fc4aaabf3a791d55ee16c9b889c143092c3fba065b6253ea6d27c8a3b934d7fc7ee401470cbe5
|
data/README.md
CHANGED
@@ -9,6 +9,10 @@ Shining some light on setting up and running various Docker things.
|
|
9
9
|
Lux is both a command line tool (based on Thor) and a library of useful routines
|
10
10
|
that can be included in Rake tasks.
|
11
11
|
|
12
|
+
It uses the Docker API directly via the
|
13
|
+
[docker-api GEM](https://github.com/swipely/docker-api) and so does not require the docker
|
14
|
+
client.
|
15
|
+
|
12
16
|
## Installation
|
13
17
|
|
14
18
|
Add this line to your application's Gemfile:
|
@@ -27,6 +31,10 @@ Or install it yourself as:
|
|
27
31
|
|
28
32
|
## Usage
|
29
33
|
|
34
|
+
If using a Docker server across the network, make sure that you have exported the
|
35
|
+
environment variable `DOCKER_URL ` (which is a true URL). Note that this is different to
|
36
|
+
the Go docker client which uses `DOCKER_HOST`.
|
37
|
+
|
30
38
|
### Command Line Tool
|
31
39
|
|
32
40
|
The `lux` command simplifies a number of Docker command line operations and provides
|
@@ -38,62 +46,122 @@ Type: `lux help` to get started
|
|
38
46
|
### Rake Tasks
|
39
47
|
|
40
48
|
When trying to write Rakefiles that start Docker containers it is useful to have a Rake
|
41
|
-
task that represents a container image. Then you can make other tasks depend on them.
|
42
|
-
|
43
|
-
|
44
|
-
dependencies elegantly into the Rakefile.
|
49
|
+
task that represents a container image. Then you can make other tasks depend on them. If
|
50
|
+
the task (when invoked) checks the local Docker server for the image, and only executes
|
51
|
+
the task body if the image is not found, or if the listed dependencies are out-of-date,
|
52
|
+
then you can build Docker dependencies elegantly into the Rakefile.
|
45
53
|
|
46
|
-
|
47
|
-
|
54
|
+
Lux extends Rake with two new task types `dockerimage` and `container`. Both of them are
|
55
|
+
specializations of the `file` task. They use the Docker server pointed to by the
|
56
|
+
`DOCKER_URL` environment variable and do *not* query a remote registry.
|
48
57
|
|
49
|
-
|
50
|
-
require 'lux/dockertasks'
|
58
|
+
#### `dockerimage`
|
51
59
|
|
52
|
-
|
53
|
-
|
60
|
+
This checks for the existence of an image with the task name in the Docker server. It uses
|
61
|
+
the creation date found there as the timestamp and executes the associated action if it is
|
62
|
+
out-of-date with respect to it's dependencies. This means you can rebuild the image based
|
63
|
+
on other dependencies (such as the Dockerfile or content).
|
54
64
|
|
55
|
-
|
65
|
+
If you are using an image on a remote repository that you don't build locally then you can
|
66
|
+
either define it and make the action a `docker pull` command or omit it altogether. In the
|
67
|
+
former case you can then add this as a dependency to a container task to ensure that a
|
68
|
+
running container always has the most up-to-date image. The latter case works as the
|
69
|
+
Docker runtime will attempt to pull an image from a remote repository if it is not present
|
70
|
+
locally.
|
56
71
|
|
57
|
-
|
58
|
-
# The name of the task is 'ubuntu', the tag is 'trusty'
|
72
|
+
#### `container`
|
59
73
|
|
60
|
-
|
74
|
+
This checks for the existence of a running container for a particular docker image. The
|
75
|
+
task name is a compound name of the form `containername@imagename`. If the container
|
76
|
+
named is not running then the action block is executed, typically it will be a Docker run
|
77
|
+
command. If the container is already running then it's image is checked to see if that it
|
78
|
+
is the same as specified. If not the container is destroyed and the action block executed.
|
79
|
+
If the container image matches the requested one, then the creation date of the container
|
80
|
+
is used as the task timestamp. This allows you to specify a dockerimage dependency and the
|
81
|
+
container will then be restarted if the image is newer.
|
61
82
|
|
62
|
-
|
63
|
-
|
83
|
+
Note that you don't need a corresponding dockerimage task as the Docker runtime will
|
84
|
+
attempt to pull an image from a remote repository if it is not present locally.
|
64
85
|
|
65
|
-
|
86
|
+
#### Examples
|
66
87
|
|
67
|
-
|
68
|
-
|
69
|
-
# The task object yielded has the image and tag attributes
|
70
|
-
# as well as the usual name and description.
|
88
|
+
```ruby
|
89
|
+
require 'lux/docker_tasks'
|
71
90
|
|
72
|
-
|
73
|
-
|
74
|
-
|
91
|
+
desc "Build tool image if the image is not found locally"
|
92
|
+
dockerimage 'quay.io/rasputin/tools:1.0' do |t|
|
93
|
+
sh "docker build -t #{t.name} ."
|
75
94
|
end
|
76
95
|
|
77
|
-
desc "
|
78
|
-
|
79
|
-
sh "docker
|
96
|
+
desc "Build tool image if the image is not found or is older than the Dockerfile"
|
97
|
+
dockerimage 'quay.io/rasputin/tools:1.0' => 'Dockerfile' do |t|
|
98
|
+
sh "docker build -t #{t.name} ."
|
80
99
|
end
|
81
100
|
|
82
|
-
desc "
|
83
|
-
|
84
|
-
sh "docker run -
|
101
|
+
desc "Make sure we have a running tools container"
|
102
|
+
container 'tools@quay.io/rasputin/tools:1.0' do
|
103
|
+
sh "docker run --name tools -d quay.io/rasputin/tools:1.0"
|
85
104
|
end
|
86
105
|
|
87
|
-
desc "
|
88
|
-
|
89
|
-
sh "docker run -
|
106
|
+
desc "Make sure we have a running tools container with the most up-to-date image"
|
107
|
+
container 'tools@quay.io/rasputin/tools:1.0' => 'quay.io/rasputin/tools:1.0' do
|
108
|
+
sh "docker run --name tools -d quay.io/rasputin/tools:1.0"
|
90
109
|
end
|
91
110
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
111
|
+
# A set of tasks to ensure you always have the latest version of a container
|
112
|
+
# stored on a remote registry
|
113
|
+
|
114
|
+
require 'lux/docker_tasks'
|
115
|
+
|
116
|
+
desc "Always see if there is a new version of busybox"
|
117
|
+
task :getbusybox do
|
118
|
+
sh "docker pull busybox"
|
96
119
|
end
|
120
|
+
|
121
|
+
desc "Create a dockerimage task so we can depend on it"
|
122
|
+
dockerimage 'busybox' => :getbusybox
|
123
|
+
|
124
|
+
desc "Always run with the latest version"
|
125
|
+
container 'busy@busybox' => 'busybox' do
|
126
|
+
sh "docker run -it --name busy busybox"
|
127
|
+
end
|
128
|
+
|
129
|
+
```
|
130
|
+
#### Tracing
|
131
|
+
|
132
|
+
When the Rake trace option is enabled the destruction (or attempted destruction if it's a
|
133
|
+
dry-run) is recorded.
|
134
|
+
|
135
|
+
#### Namespaces
|
136
|
+
|
137
|
+
When using the `dockerimage` and `container` tasks with explicit tags (ie. containing a semi-colon), then
|
138
|
+
normal Rake namespace rules apply: The _repository_ name will be considered the namespace
|
139
|
+
and the _tag_ the taskname within the namespace. If this `dockerimage` task is declared in
|
140
|
+
the same execution alongside a matching explicit namespace and task declaration, then the
|
141
|
+
actions will accumulate.
|
142
|
+
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
task :default => 'busybox:greenest'
|
146
|
+
|
147
|
+
dockerimage 'busybox:greenest' do
|
148
|
+
puts "Build busybox:greenest Docker image"
|
149
|
+
end
|
150
|
+
|
151
|
+
namespace :busybox do
|
152
|
+
task :greenest do
|
153
|
+
puts "Do the greenest task in the busybox namespace"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
```
|
158
|
+
|
159
|
+
This prints:
|
160
|
+
|
161
|
+
```bash
|
162
|
+
$ rake -f Rakefile.demo
|
163
|
+
Build busybox:greenest Docker image
|
164
|
+
Do the greenest task in the busybox namespace
|
97
165
|
```
|
98
166
|
|
99
167
|
## Development
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Extend rake with file-like Docker tasks
|
2
|
+
#
|
3
|
+
require 'rake'
|
4
|
+
require 'docker'
|
5
|
+
|
6
|
+
module Rake
|
7
|
+
module DSL
|
8
|
+
def dockerimage(*args, &block)
|
9
|
+
Lux::DockerImageTask.define_task(*args, &block)
|
10
|
+
end
|
11
|
+
def container(*args, &block)
|
12
|
+
Lux::DockerContainerTask.define_task(*args, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Lux
|
18
|
+
class DockerImageTask < Rake::FileTask
|
19
|
+
# This task checks to see if the image is present in the local Docker Server
|
20
|
+
# If present it returns the creation date as the timestamp, if not then it
|
21
|
+
# returns Rake::Early. This allows dependencies to execute correctly
|
22
|
+
# The action block should build the container.
|
23
|
+
#
|
24
|
+
def initialize(task_name, app)
|
25
|
+
super(task_name, app)
|
26
|
+
@imagename = @name
|
27
|
+
@imagename += ':latest' unless @imagename.index(':')
|
28
|
+
@image = Docker::Image.all.select{|i| i.info['RepoTags'].include? @imagename}.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def needed?
|
32
|
+
! @image || out_of_date?(timestamp) || @application.options.build_all
|
33
|
+
end
|
34
|
+
|
35
|
+
def timestamp
|
36
|
+
if @image = Docker::Image.all.select{|i| i.info['RepoTags'].include? @imagename}.first
|
37
|
+
Time.at(@image.info['Created'])
|
38
|
+
else
|
39
|
+
Rake::EARLY
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class DockerContainerTask < Rake::FileTask
|
45
|
+
# This task checks whether a named container is running from a given image.
|
46
|
+
# The name of the task is <container-name>@<image-name>
|
47
|
+
# The container name must match /?[a-zA-Z0-9_-]+
|
48
|
+
#
|
49
|
+
# If a container is already running for this image, the creation time
|
50
|
+
# is checked against the current image modification date.
|
51
|
+
# If the container is not running or out-of-date it is restarted.
|
52
|
+
#
|
53
|
+
def initialize(task_name, app)
|
54
|
+
super(task_name, app)
|
55
|
+
@containername, sep, @imagename = task_name.partition('@')
|
56
|
+
raise "Task #{task_name} must be name@image" if @containername.empty? or @imagename.empty?
|
57
|
+
@container = Docker::Container.get(@containername) rescue nil
|
58
|
+
@imagename += ':latest' unless @imagename.index(':')
|
59
|
+
@image = Docker::Image.all.select{|i| i.info['RepoTags'].include? @imagename}.first
|
60
|
+
end
|
61
|
+
|
62
|
+
def needed?
|
63
|
+
not @container or
|
64
|
+
@container.info["Image"] != @image.id or
|
65
|
+
not @container.info["State"]["Running"] or
|
66
|
+
out_of_date?(timestamp) or
|
67
|
+
@application.options.build_all
|
68
|
+
end
|
69
|
+
|
70
|
+
# Before the task actions are done, remove the container
|
71
|
+
def execute(args)
|
72
|
+
destroy_container("it is out-of-date") { out_of_date?(timestamp) }
|
73
|
+
destroy_container("it uses the wrong image") { @container.info["Image"] != @image.id }
|
74
|
+
destroy_container("it is not running") { not @container.info["State"]["Running"] }
|
75
|
+
destroy_container("it exists!") { @container }
|
76
|
+
super args
|
77
|
+
end
|
78
|
+
|
79
|
+
# Destroy the container if the block yields true
|
80
|
+
def destroy_container msg, &block
|
81
|
+
return if !@container or (block_given? and not yield)
|
82
|
+
application.trace "** Prepare #{name} (destroying container: #{msg})" if application.options.trace
|
83
|
+
return if application.options.dryrun
|
84
|
+
# It may be hard to kill this container, try but if something goes wrong
|
85
|
+
# then print a message and proceed with the action block. The start command
|
86
|
+
# in there will fail in a more user-friendly way.
|
87
|
+
begin
|
88
|
+
@container.stop # @container.kill(signal: 'SIGTERM') better?
|
89
|
+
@container.wait(10)
|
90
|
+
@container.delete
|
91
|
+
rescue Exception => e
|
92
|
+
puts "Exception destroying container: #{e.to_s}"
|
93
|
+
end
|
94
|
+
@container = nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def timestamp
|
98
|
+
if @container = Docker::Container.get(@containername) rescue nil
|
99
|
+
Time.iso8601(@container.info['Created'])
|
100
|
+
else
|
101
|
+
Rake::EARLY
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/lib/lux/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lux
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Townsend
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -98,7 +98,7 @@ files:
|
|
98
98
|
- Rakefile.test
|
99
99
|
- bin/lux
|
100
100
|
- lib/lux.rb
|
101
|
-
- lib/lux/
|
101
|
+
- lib/lux/docker_tasks.rb
|
102
102
|
- lib/lux/version.rb
|
103
103
|
- lux.gemspec
|
104
104
|
homepage: https://github.com/townsen/lux
|
data/lib/lux/dockertasks.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'highline/import'
|
2
|
-
require 'rake/tasklib'
|
3
|
-
|
4
|
-
# A Task Generator for Docker images
|
5
|
-
#
|
6
|
-
# Example:
|
7
|
-
# require "lux/dockertasks"
|
8
|
-
#
|
9
|
-
# DockerImageTask.new('myimage') do
|
10
|
-
# puts "Here you can build the image"
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# task :run => 'myimage' do
|
14
|
-
# sh "docker run myimage"
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
class DockerImageTask < Rake::TaskLib
|
18
|
-
|
19
|
-
# The name of the task (defaults to the image)
|
20
|
-
attr_accessor :name
|
21
|
-
|
22
|
-
# The name of the image (if different to the task name)
|
23
|
-
attr_accessor :image
|
24
|
-
|
25
|
-
# Name of tag (default is latest)
|
26
|
-
attr_accessor :tag
|
27
|
-
|
28
|
-
# Task Description (default is 'Build Docker Image <imagename>')
|
29
|
-
attr_accessor :description
|
30
|
-
|
31
|
-
# The block to execute if this needs building
|
32
|
-
attr_accessor :build
|
33
|
-
|
34
|
-
# The image may include a tag, but the tag is never part of the name
|
35
|
-
# If the tag is omitted it defaults to 'latest'
|
36
|
-
# If a name is specified it becomes the task name.
|
37
|
-
#
|
38
|
-
def initialize(image, name=nil, &block)
|
39
|
-
@image, sep, @tag = image.partition(':')
|
40
|
-
@tag = 'latest' if @tag.empty?
|
41
|
-
@name = name || @image
|
42
|
-
@description = "Build Docker Image #{@image}:#{@tag}"
|
43
|
-
@build = block
|
44
|
-
define
|
45
|
-
end
|
46
|
-
|
47
|
-
def define
|
48
|
-
desc @description
|
49
|
-
task @name do
|
50
|
-
local_images = `docker images #{@image}`.split("\n")[1..-1].map{|l| l.split(/\s+/)}
|
51
|
-
local_images.select!{|i| i[1] == @tag}
|
52
|
-
if local_images.size == 0
|
53
|
-
if @build.nil?
|
54
|
-
HighLine.say "Build Docker Image <%=BOLD%>#{@image}:#{@tag}<%=CLEAR%> before proceeding"
|
55
|
-
exit 2
|
56
|
-
else
|
57
|
-
@build.call self
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
self
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|