docker-compose-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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