docker-compose-api 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2.0
5
+
6
+ sudo: required
7
+
8
+ dist: trusty
9
+
10
+ services:
11
+ - docker
12
+
13
+ install:
14
+ - bundle install
15
+
16
+ script:
17
+ - bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docker-compose-api.gemspec
4
+ gemspec
5
+
6
+ # Code climate coverage support
7
+ gem 'codeclimate-test-reporter', group: :test, require: nil
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Mauricio S. Klein
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ [![Build Status](https://travis-ci.org/mauricioklein/docker-compose-api.svg?branch=develop)](https://travis-ci.org/mauricioklein/docker-compose-api)
2
+ [![Code Climate](https://codeclimate.com/github/mauricioklein/docker-compose-api/badges/gpa.svg)](https://codeclimate.com/github/mauricioklein/docker-compose-api)
3
+ [![Test Coverage](https://codeclimate.com/github/mauricioklein/docker-compose-api/badges/coverage.svg)](https://codeclimate.com/github/mauricioklein/docker-compose-api/coverage)
4
+
5
+ # Docker Compose Api
6
+
7
+ Docker Compose API provides an easy way to parse docker compose files and lift the whole environment.
8
+
9
+ ## Instalation
10
+
11
+ ```ruby
12
+ # Add the line below on your Gemfile...
13
+ gem 'docker-compose-api', git: 'https://github.com/mauricioklein/docker-compose-api'
14
+
15
+ # ... and run bundle install
16
+ bundle install
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'docker-compose'
23
+
24
+ # Docker compose is simply a layer running over Docker client (https://github.com/swipely/docker-api).
25
+ # So, all Docker specific configurations, such URL, authentication, SSL, etc, must be made directly on
26
+ # Docker client.
27
+ #
28
+ # Docker compose provides an easy way to access this client:
29
+ DockerCompose.docker_client
30
+
31
+ # Gem version
32
+ DockerCompose.version
33
+
34
+ # Loading a compose file
35
+ compose = DockerCompose.load('[path to docker compose file]')
36
+
37
+ # Accessing containers
38
+ compose.containers # access all containers
39
+ compose.containers['[container name]'] # access a specific container
40
+
41
+ # Starting containers (and their dependencies)
42
+ compose.start # start all containers
43
+ compose.start(['container1', 'container2', ...]) # start a list of specific containers
44
+
45
+ # Stopping containers
46
+ # (ps: container dependencies will keep running)
47
+ compose.stop # stop all containers
48
+ compose.stop(['container1', 'container2', ...]) # stop a list of specific containers
49
+
50
+ # Killing containers
51
+ # (ps: container dependencies will keep running)
52
+ compose.kill # kill all containers
53
+ compose.kill(['container1', 'container2', ...]) # kill a list of specific containers
54
+
55
+ # Checking if a container is running or not
56
+ a_container = compose.containers['a_container']
57
+ a_container.running?
58
+
59
+ # Accessing container informations
60
+ a_container.stats
61
+ ```
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it ( https://github.com/mauricioklein/docker-compose-api/fork )
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 5. Create a new Pull Request
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "docker-compose-api"
8
+ spec.version = DockerCompose.version
9
+ spec.authors = ["Mauricio S. Klein"]
10
+ spec.email = ["mauricio.klein.msk@gmail.com"]
11
+ spec.summary = %q{A simple ruby client for docker-compose api}
12
+ spec.description = %q{A simple ruby client for docker-compose api}
13
+ spec.homepage = "https://github.com/mauricioklein/docker-compose-api"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "docker-api", "~> 1.22.2"
22
+
23
+ spec.add_development_dependency "bundler"
24
+ spec.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,53 @@
1
+ require_relative 'docker-compose/models/compose'
2
+ require_relative 'docker-compose/models/compose_container'
3
+ require_relative 'version'
4
+
5
+ require 'yaml'
6
+ require 'docker'
7
+
8
+ module DockerCompose
9
+ #
10
+ # Get Docker client object
11
+ #
12
+ def self.docker_client
13
+ Docker
14
+ end
15
+
16
+ #
17
+ # Load a given docker-compose file.
18
+ # Returns a new Compose object
19
+ #
20
+ def self.load(filepath)
21
+ unless File.exist?(filepath)
22
+ puts("Compose file doesn't exists")
23
+ raise ENOENT
24
+ end
25
+
26
+ compose = Compose.new
27
+
28
+ _compose_entries = YAML.load_file(filepath)
29
+ _compose_entries.each do |entry|
30
+ compose.add_container(create_container(entry))
31
+ end
32
+
33
+ # Perform containers linkage
34
+ compose.link_containers
35
+
36
+ compose
37
+ end
38
+
39
+ def self.create_container(attributes)
40
+ ComposeContainer.new({
41
+ label: attributes[0],
42
+ image: attributes[1]['image'],
43
+ build: attributes[1]['build'],
44
+ links: attributes[1]['links'],
45
+ ports: attributes[1]['ports'],
46
+ volumes: attributes[1]['volumes'],
47
+ command: attributes[1]['command'],
48
+ environment: attributes[1]['environment']
49
+ })
50
+ end
51
+
52
+ private_class_method :create_container
53
+ end
@@ -0,0 +1,86 @@
1
+ require 'docker'
2
+ require_relative 'compose_port'
3
+ require_relative '../utils/compose_utils'
4
+
5
+ class Compose
6
+ attr_reader :containers
7
+
8
+ def initialize
9
+ @containers = {}
10
+ end
11
+
12
+ public
13
+
14
+ #
15
+ # Add a new container to compose
16
+ #
17
+ def add_container(container)
18
+ @containers[container.attributes[:label]] = container
19
+ true
20
+ end
21
+
22
+ #
23
+ # Create link relations among containers
24
+ #
25
+ def link_containers
26
+ @containers.each_value do |container|
27
+ links = container.attributes[:links]
28
+
29
+ next if links.nil?
30
+
31
+ links.each do |link|
32
+ dependency_container = @containers[link]
33
+ container.dependencies << dependency_container
34
+ end
35
+ end
36
+ end
37
+
38
+ #
39
+ # Start a container
40
+ #
41
+ # This method accepts an array of labels.
42
+ # If labels is informed, only those containers with label present in array will be started.
43
+ # Otherwise, all containers are started
44
+ #
45
+ def start(labels = [])
46
+ call_container_method(:start, labels)
47
+ end
48
+
49
+ #
50
+ # Stop a container
51
+ #
52
+ # This method accepts an array of labels.
53
+ # If labels is informed, only those containers with label present in array will be stopped.
54
+ # Otherwise, all containers are stopped
55
+ #
56
+ def stop(labels = [])
57
+ call_container_method(:stop, labels)
58
+ end
59
+
60
+ #
61
+ # Stop a container
62
+ #
63
+ # This method accepts an array of labels.
64
+ # If labels is informed, only those containers with label present in array will be stopped.
65
+ # Otherwise, all containers are stopped
66
+ #
67
+ def kill(labels = [])
68
+ call_container_method(:kill, labels)
69
+ end
70
+
71
+ private
72
+
73
+ def call_container_method(method, labels = [])
74
+ labels = @containers.keys if labels.empty?
75
+
76
+ containers = @containers.select { |key, value|
77
+ labels.include?(key)
78
+ }
79
+
80
+ containers.values.each do |entry|
81
+ entry.send(method)
82
+ end
83
+
84
+ true
85
+ end
86
+ end
@@ -0,0 +1,175 @@
1
+ require 'docker'
2
+ require_relative 'compose_port'
3
+ require_relative '../utils/compose_utils'
4
+
5
+ class ComposeContainer
6
+ attr_reader :attributes, :dependencies
7
+
8
+ def initialize(hash_attributes)
9
+ @attributes = {
10
+ label: hash_attributes[:label],
11
+ image: ComposeUtils.format_image(hash_attributes[:image]),
12
+ build: hash_attributes[:build],
13
+ links: hash_attributes[:links],
14
+ ports: prepare_ports(hash_attributes[:ports]),
15
+ volumes: hash_attributes[:volumes],
16
+ command: ComposeUtils.format_command(hash_attributes[:command]),
17
+ environment: hash_attributes[:environment]
18
+ }.reject{ |key, value| value.nil? }
19
+
20
+ # Docker client variables
21
+ @container = nil
22
+ @dependencies = []
23
+ end
24
+
25
+ private
26
+
27
+ #
28
+ # Download or build an image
29
+ #
30
+ def prepare_image
31
+ has_image_or_build_arg = @attributes.key?(:image) || @attributes.key?(:build)
32
+
33
+ raise ArgumentError.new('No Image or Build command provided') unless has_image_or_build_arg
34
+
35
+ # Build or pull image
36
+ if @attributes.key?(:image)
37
+ if image_exists
38
+ Docker::Image.get(@attributes[:image])
39
+ else
40
+ Docker::Image.create('fromImage' => @attributes[:image])
41
+ end
42
+ elsif @attributes.key?(:build)
43
+ Docker::Image.build_from_dir(@attributes[:build])
44
+ end
45
+ end
46
+
47
+ #
48
+ # Start a new container with parameters informed in object construction
49
+ # (TODO: start container from a Dockerfile)
50
+ #
51
+ def prepare_container
52
+ exposed_ports = {}
53
+ port_bindings = {}
54
+ links = []
55
+
56
+ # Build expose and port binding parameters
57
+ if !@attributes[:ports].nil?
58
+ @attributes[:ports].each do |port|
59
+ exposed_ports["#{port.container_port}/tcp"] = {}
60
+ port_bindings["#{port.container_port}/tcp"] = [{
61
+ "HostIp" => port.host_ip || '',
62
+ "HostPort" => port.host_port || ''
63
+ }]
64
+ end
65
+ end
66
+
67
+ # Build link parameters
68
+ @dependencies.each do |dependency|
69
+ links << "#{dependency.stats['Id']}:#{dependency.attributes[:label]}"
70
+ end
71
+
72
+ container_config = {
73
+ Image: @attributes[:image],
74
+ Cmd: @attributes[:command],
75
+ Env: @attributes[:environment],
76
+ Volumes: @attributes[:volumes],
77
+ ExposedPorts: exposed_ports,
78
+ HostConfig: {
79
+ Links: links,
80
+ PortBindings: port_bindings
81
+ }
82
+ }
83
+
84
+ @container = Docker::Container.create(container_config)
85
+ end
86
+
87
+ #
88
+ # Process each port entry in docker compose file and
89
+ # create structure recognized by docker client
90
+ #
91
+ def prepare_ports(port_entries)
92
+ ports = []
93
+
94
+ if port_entries.nil?
95
+ return nil
96
+ end
97
+
98
+ port_entries.each do |port_entry|
99
+ ports.push(ComposeUtils.format_port(port_entry))
100
+ end
101
+
102
+ ports
103
+ end
104
+
105
+ #
106
+ # Check if a given image already exists in host
107
+ #
108
+ def image_exists
109
+ Docker::Image.exist?(@attributes[:image])
110
+ end
111
+
112
+ public
113
+
114
+ #
115
+ # Start the container and its dependencies
116
+ #
117
+ def start
118
+ # Start dependencies
119
+ @dependencies.each do |dependency|
120
+ dependency.start unless dependency.running?
121
+ end
122
+
123
+ # Create a container object
124
+ if @container.nil?
125
+ prepare_image
126
+ prepare_container
127
+ end
128
+
129
+ @container.start unless @container.nil?
130
+ end
131
+
132
+ #
133
+ # Stop the container
134
+ #
135
+ def stop
136
+ @container.kill unless @container.nil?
137
+ end
138
+
139
+ #
140
+ # Kill the container
141
+ #
142
+ def kill
143
+ @container.kill unless @container.nil?
144
+ end
145
+
146
+ #
147
+ # Delete the container
148
+ #
149
+ def delete
150
+ @container.delete(:force => true) unless @container.nil?
151
+ @container = nil
152
+ end
153
+
154
+ #
155
+ # Add a dependency to this container
156
+ # (i.e. a container that must be started before this one)
157
+ #
158
+ def add_dependency(dependency)
159
+ @dependencies << dependency
160
+ end
161
+
162
+ #
163
+ # Return container statistics
164
+ #
165
+ def stats
166
+ @container.json
167
+ end
168
+
169
+ #
170
+ # Check if a container is already running or not
171
+ #
172
+ def running?
173
+ @container.nil? ? false : self.stats['State']['Running']
174
+ end
175
+ end
@@ -0,0 +1,9 @@
1
+ class ComposePort
2
+ attr_reader :container_port, :host_ip, :host_port
3
+
4
+ def initialize(container_port, host_port = nil, host_ip = nil)
5
+ @container_port = container_port
6
+ @host_ip = host_ip
7
+ @host_port = host_port
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ module ComposeUtils
2
+ #
3
+ # Format a given docker image in a complete structure (base image + tag)
4
+ #
5
+ def self.format_image(image)
6
+ base_image = nil
7
+ tag = nil
8
+
9
+ if image.nil?
10
+ return nil
11
+ end
12
+
13
+ if image.index(':').nil?
14
+ base_image = image
15
+ tag = 'latest'
16
+ else
17
+ image_split = image.split(':')
18
+ base_image = image_split[0]
19
+ tag = image_split[1]
20
+ end
21
+
22
+ "#{base_image}:#{tag}"
23
+ end
24
+
25
+ #
26
+ # Transform docker command from string to an array of commands
27
+ #
28
+ def self.format_command(command)
29
+ command.nil? ? nil : command.split(' ')
30
+ end
31
+
32
+ #
33
+ # Read a port specification in string format
34
+ # and create a compose port structure
35
+ #
36
+ def self.format_port(port_entry)
37
+ compose_port = nil
38
+ container_port = nil
39
+ host_port = nil
40
+ host_ip = nil
41
+
42
+ port_parts = port_entry.split(':')
43
+
44
+ case port_parts.length
45
+ # [container port]
46
+ when 1
47
+ compose_port = ComposePort.new(port_parts[0])
48
+
49
+ # [host port]:[container port]
50
+ when 2
51
+ compose_port = ComposePort.new(port_parts[1], port_parts[0])
52
+
53
+ # [host ip]:[host port]:[container port]
54
+ when 3
55
+ compose_port = ComposePort.new(port_parts[2], port_parts[1], port_parts[0])
56
+ end
57
+
58
+ compose_port
59
+ end
60
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ module DockerCompose
2
+ def self.version
3
+ "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe DockerCompose do
4
+ before(:all) do
5
+ @compose = DockerCompose.load(File.expand_path('spec/docker-compose/fixtures/sample1.yaml'))
6
+ end
7
+
8
+ it 'should be able to access gem version' do
9
+ expect(DockerCompose.version).to_not be_nil
10
+ end
11
+
12
+ it 'should be able to access Docker client' do
13
+ expect(DockerCompose.docker_client).to_not be_nil
14
+ end
15
+
16
+ it 'should read a YAML file correctly' do
17
+ expect(@compose.containers.length).to eq(2)
18
+ end
19
+
20
+ context 'All containers' do
21
+ it 'should start/stop all containers' do
22
+ # Start containers to test Stop
23
+ @compose.start
24
+ @compose.containers.values.each do |container|
25
+ expect(container.running?).to be true
26
+ end
27
+
28
+ # Stop containers
29
+ @compose.stop
30
+ @compose.containers.values.each do |container|
31
+ expect(container.running?).to be false
32
+ end
33
+ end
34
+
35
+ it 'should start/kill all containers' do
36
+ # Start containers to test Kill
37
+ @compose.start
38
+ @compose.containers.values.each do |container|
39
+ expect(container.running?).to be true
40
+ end
41
+
42
+ # Kill containers
43
+ @compose.kill
44
+ @compose.containers.values.each do |container|
45
+ expect(container.running?).to be false
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'Single container' do
51
+ context 'Without dependencies' do
52
+ it 'should start/stop a single container' do
53
+ ubuntu = @compose.containers.values.first.attributes[:label]
54
+ redis = @compose.containers.values.last.attributes[:label]
55
+
56
+ # Should start Redis only, since it hasn't dependencies
57
+ @compose.start([redis])
58
+ expect(@compose.containers[ubuntu].running?).to be false
59
+ expect(@compose.containers[redis].running?).to be true
60
+
61
+ # Stop Redis
62
+ @compose.stop([redis])
63
+ expect(@compose.containers[ubuntu].running?).to be false
64
+ expect(@compose.containers[redis].running?).to be false
65
+ end
66
+
67
+ it 'should start/kill a single container' do
68
+ ubuntu = @compose.containers.values.first.attributes[:label]
69
+ redis = @compose.containers.values.last.attributes[:label]
70
+
71
+ # Should start Redis only, since it hasn't dependencies
72
+ @compose.start([redis])
73
+ expect(@compose.containers[ubuntu].running?).to be false
74
+ expect(@compose.containers[redis].running?).to be true
75
+
76
+ # Stop Redis
77
+ @compose.kill([redis])
78
+ expect(@compose.containers[ubuntu].running?).to be false
79
+ expect(@compose.containers[redis].running?).to be false
80
+ end
81
+ end # context 'Without dependencies'
82
+
83
+ context 'With dependencies' do
84
+ it 'should start/stop a single container' do
85
+ ubuntu = @compose.containers.values.first.attributes[:label]
86
+ redis = @compose.containers.values.last.attributes[:label]
87
+
88
+ # Should start Ubuntu and Redis, since Ubuntu depends on Redis
89
+ @compose.start([ubuntu])
90
+ expect(@compose.containers[ubuntu].running?).to be true
91
+ expect(@compose.containers[redis].running?).to be true
92
+
93
+ # Stop Ubuntu (Redis keeps running)
94
+ @compose.stop([ubuntu])
95
+ expect(@compose.containers[ubuntu].running?).to be false
96
+ expect(@compose.containers[redis].running?).to be true
97
+
98
+ # Stop Redis
99
+ @compose.stop([redis])
100
+ expect(@compose.containers[redis].running?).to be false
101
+ end
102
+
103
+ it 'should start/kill a single container' do
104
+ ubuntu = @compose.containers.values.first.attributes[:label]
105
+ redis = @compose.containers.values.last.attributes[:label]
106
+
107
+ # Should start Ubuntu and Redis, since Ubuntu depends on Redis
108
+ @compose.start([ubuntu])
109
+ expect(@compose.containers[ubuntu].running?).to be true
110
+ expect(@compose.containers[redis].running?).to be true
111
+
112
+ # Kill Ubuntu (Redis keeps running)
113
+ @compose.kill([ubuntu])
114
+ expect(@compose.containers[ubuntu].running?).to be false
115
+ expect(@compose.containers[redis].running?).to be true
116
+
117
+ # Kill Redis
118
+ @compose.kill([redis])
119
+ expect(@compose.containers[redis].running?).to be false
120
+ end
121
+ end # context 'with dependencies'
122
+ end # context 'Single container'
123
+
124
+ it 'should assign ports' do
125
+ ubuntu = @compose.containers.values.first
126
+
127
+ # Start container
128
+ ubuntu.start
129
+
130
+ port_bindings = ubuntu.stats['HostConfig']['PortBindings']
131
+ exposed_ports = ubuntu.stats['Config']['ExposedPorts']
132
+
133
+ # Check port bindings
134
+ expect(port_bindings.length).to eq(3)
135
+ expect(port_bindings.key?('3000/tcp')).to be true
136
+ expect(port_bindings.key?('8000/tcp')).to be true
137
+ expect(port_bindings.key?('8001/tcp')).to be true
138
+
139
+ # Check exposed ports
140
+ expect(exposed_ports.key?('3000/tcp')).to be true
141
+ expect(exposed_ports.key?('8000/tcp')).to be true
142
+ expect(exposed_ports.key?('8001/tcp')).to be true
143
+
144
+ # Stop container
145
+ ubuntu.stop
146
+ end
147
+
148
+ it 'should link containers' do
149
+ ubuntu = @compose.containers.values.first
150
+
151
+ # Start container
152
+ ubuntu.start
153
+
154
+ # Ubuntu should be linked to Redis
155
+ links = ubuntu.stats['HostConfig']['Links']
156
+ expect(links.length).to eq(1)
157
+
158
+ # Stop container
159
+ ubuntu.stop
160
+ end
161
+
162
+ after(:all) do
163
+ @compose.containers.values.each do |entry|
164
+ entry.delete
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,16 @@
1
+ ubuntu:
2
+ image: ubuntu
3
+ ports:
4
+ - "3000"
5
+ - "8000:8000"
6
+ - "127.0.0.1:8001:8001"
7
+ expose:
8
+ - "5000"
9
+ links:
10
+ - redis
11
+ command: /bin/sleep 60
12
+
13
+ redis:
14
+ image: redis
15
+ expose:
16
+ - "6000"
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ describe ComposeContainer do
4
+ context 'Object creation' do
5
+ it 'should prepare the attributes correctly' do
6
+ attributes = {
7
+ image: 'ubuntu:latest',
8
+ links: ['links:links'],
9
+ ports: ['3000', '8000:8000', '127.0.0.1:8001:8001'],
10
+ volumes: {'/tmp' => {}},
11
+ command: 'ps aux',
12
+ environment: ['ENVIRONMENT']
13
+ }
14
+
15
+ entry = ComposeContainer.new(attributes)
16
+
17
+ expect(entry.attributes[:image]).to eq(attributes[:image])
18
+ expect(entry.attributes[:links]).to eq(attributes[:links])
19
+ expect(entry.attributes[:volumes]).to eq(attributes[:volumes])
20
+ expect(entry.attributes[:command]).to eq(attributes[:command].split(' '))
21
+ expect(entry.attributes[:environment]).to eq(attributes[:environment])
22
+
23
+ # Check ports structure
24
+ expect(entry.attributes[:ports].length).to eq(attributes[:ports].length)
25
+
26
+ # Port 1: '3000'
27
+ port_entry = entry.attributes[:ports][0]
28
+ expect(port_entry.container_port).to eq('3000')
29
+ expect(port_entry.host_ip).to eq(nil)
30
+ expect(port_entry.host_port).to eq(nil)
31
+
32
+ # Port 2: '8000:8000'
33
+ port_entry = entry.attributes[:ports][1]
34
+ expect(port_entry.container_port).to eq('8000')
35
+ expect(port_entry.host_ip).to eq(nil)
36
+ expect(port_entry.host_port).to eq('8000')
37
+
38
+ # Port 3: '127.0.0.1:8001:8001'
39
+ port_entry = entry.attributes[:ports][2]
40
+ expect(port_entry.container_port).to eq('8001')
41
+ expect(port_entry.host_ip).to eq('127.0.0.1')
42
+ expect(port_entry.host_port).to eq('8001')
43
+ end
44
+ end
45
+
46
+ context 'Start container' do
47
+ it 'should start/stop a container' do
48
+ attributes = {
49
+ image: 'ubuntu:latest',
50
+ links: ['links:links'],
51
+ volumes: {'/tmp' => {}},
52
+ command: 'ps aux',
53
+ environment: ['ENVIRONMENT']
54
+ }
55
+
56
+ entry = ComposeContainer.new(attributes)
57
+
58
+ #Start container
59
+ entry.start
60
+ expect(entry.running?).to be true
61
+
62
+ # Stop container
63
+ entry.stop
64
+ expect(entry.running?).to be false
65
+ end
66
+
67
+ it 'should not start a container without either image and build commands' do
68
+ attributes = {
69
+ links: ['links:links'],
70
+ volumes: {'/tmp' => {}},
71
+ command: 'ps aux',
72
+ environment: ['ENVIRONMENT']
73
+ }
74
+
75
+ entry = ComposeContainer.new(attributes)
76
+ expect{entry.start}.to raise_error(ArgumentError)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe ComposeUtils do
4
+ context 'Format image' do
5
+ it 'should return nil when image is nil' do
6
+ expect(ComposeUtils.format_image(nil)).to be_nil
7
+ end
8
+
9
+ it 'should assign latest tag when no tag is provided' do
10
+ expect(ComposeUtils.format_image('ubuntu')).to eq('ubuntu:latest')
11
+ end
12
+
13
+ it 'should assign base image and tag when both are provided' do
14
+ expect(ComposeUtils.format_image('ubuntu:11')).to eq('ubuntu:11')
15
+ end
16
+ end
17
+
18
+ context 'Format command' do
19
+ it 'should return nil when command is nil' do
20
+ expect(ComposeUtils.format_command(nil)).to be_nil
21
+ end
22
+
23
+ it 'should return original command as array when command has no whitespaces' do
24
+ expect(ComposeUtils.format_command('top')).to eq(['top'])
25
+ end
26
+
27
+ it 'should split command in array on whitespaces' do
28
+ expect(ComposeUtils.format_command('ls -lh')).to eq(['ls', '-lh'])
29
+ end
30
+ end
31
+
32
+ context 'Format port' do
33
+ it 'should recognize pattern "[container port]"' do
34
+ compose_port = ComposeUtils.format_port('8080')
35
+ expect(compose_port.container_port).to eq('8080')
36
+ expect(compose_port.host_port).to eq(nil)
37
+ expect(compose_port.host_ip).to eq(nil)
38
+ end
39
+
40
+ it 'should recognize pattern "[host port]:[container port]"' do
41
+ compose_port = ComposeUtils.format_port('8080:7777')
42
+ expect(compose_port.container_port).to eq('7777')
43
+ expect(compose_port.host_port).to eq('8080')
44
+ expect(compose_port.host_ip).to eq(nil)
45
+ end
46
+
47
+ it 'should recognize pattern "[host ip]:[host port]:[container port]' do
48
+ compose_port = ComposeUtils.format_port('127.0.0.1:8080:7777')
49
+ expect(compose_port.container_port).to eq('7777')
50
+ expect(compose_port.host_port).to eq('8080')
51
+ expect(compose_port.host_ip).to eq('127.0.0.1')
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'codeclimate-test-reporter'
4
+ CodeClimate::TestReporter.start
5
+
6
+ require 'docker-compose'
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docker-compose-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mauricio S. Klein
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-10-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: docker-api
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.22.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.22.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A simple ruby client for docker-compose api
63
+ email:
64
+ - mauricio.klein.msk@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - .travis.yml
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - docker-compose-api.gemspec
76
+ - lib/docker-compose.rb
77
+ - lib/docker-compose/models/compose.rb
78
+ - lib/docker-compose/models/compose_container.rb
79
+ - lib/docker-compose/models/compose_port.rb
80
+ - lib/docker-compose/utils/compose_utils.rb
81
+ - lib/version.rb
82
+ - spec/docker-compose/docker_compose_spec.rb
83
+ - spec/docker-compose/fixtures/sample1.yaml
84
+ - spec/docker-compose/models/compose_container_spec.rb
85
+ - spec/docker-compose/utils/compose_utils_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: https://github.com/mauricioklein/docker-compose-api
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: A simple ruby client for docker-compose api
112
+ test_files:
113
+ - spec/docker-compose/docker_compose_spec.rb
114
+ - spec/docker-compose/fixtures/sample1.yaml
115
+ - spec/docker-compose/models/compose_container_spec.rb
116
+ - spec/docker-compose/utils/compose_utils_spec.rb
117
+ - spec/spec_helper.rb