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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a59ca3dfc3b9dc7b392ba2eccb2e03fdf762a345
4
- data.tar.gz: e64035da3da05c6d9ff4aafbe48d909e3bd09164
3
+ metadata.gz: 840ad1a2f55f5395e18d76976357486af1f860fa
4
+ data.tar.gz: 4b954b5fababffa6b24ead4841a9245f85308547
5
5
  SHA512:
6
- metadata.gz: 3da8015452b5ed70a742a1a69951c625d632846d14b6c9ac9156f7fa19e09d0490bd0bbde71e9230fca3ceef806add3283768d0c951792e99cdb00246ee9355f
7
- data.tar.gz: 0894977bcfef8ef4e769805cb1167358352b406ce3930b98389cd11dbfa2cd10b9a93d5b0b5a591f7e9e10f9b57d95a0f24a7f4df4ae83d90abd1005c5a07348
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
- If the task (when invoked) checks the local Docker server for the image, and
43
- only executes the task body if the image is not found, then you can build Docker
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
- The boilerplate to do this is included in Lux as a Rake Task Generator. Here are some
47
- examples:
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
- ```ruby
50
- require 'lux/dockertasks'
58
+ #### `dockerimage`
51
59
 
52
- # Define a simple task.
53
- # The tag will default to 'latest' and the name of the task is 'busybox'
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
- DockerImageTask.new('busybox')
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
- # Define a task with a tag.
58
- # The name of the task is 'ubuntu', the tag is 'trusty'
72
+ #### `container`
59
73
 
60
- DockerImageTask.new('ubuntu:trusty')
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
- # Define a task with a tag and a name.
63
- # The name of the task is 'reedy', the image is 'ubuntu' and the tag 'reedy'
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
- DockerImageTask.new('ubuntu:reedy', :reedy)
86
+ #### Examples
66
87
 
67
- # Define a task with a fully qualified image and a block.
68
- # The block will be executed if the image is not found locally.
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
- DockerImageTask.new('quay.io/rasputin/tools:1.0') do |t|
73
- puts "You want me to build the image #{t.image}:#{t.tag}?"
74
- fail "Not from here I can't..."
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 "Run Busybox"
78
- task :runb => 'busybox' do
79
- sh "docker run -it busybox"
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 "Run Ubuntu Trusty"
83
- task :runu => :ubuntu do
84
- sh "docker run -it ubuntu:trusty /bin/bash"
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 "Run Ubuntu Reedy"
88
- task :runr => :reedy do
89
- sh "docker run -it ubuntu:reedy /bin/bash"
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
- # Note how the task name does not include the tag
93
- desc "Run Tools"
94
- task :runt => 'quay.io/rasputin/tools' do
95
- sh "docker run -it quay.io/rasputin/tools:1.0 /bin/bash"
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
@@ -1,3 +1,3 @@
1
1
  module Lux
2
- VERSION = "0.9"
2
+ VERSION = "1.0.2"
3
3
  end
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: '0.9'
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-05 00:00:00.000000000 Z
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/dockertasks.rb
101
+ - lib/lux/docker_tasks.rb
102
102
  - lib/lux/version.rb
103
103
  - lux.gemspec
104
104
  homepage: https://github.com/townsen/lux
@@ -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