dip 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6a9ad2527f29216fd1a230c2c8acc9032f08cd1eea686f89df433f8c59be50fb
4
+ data.tar.gz: d84a3f71b392136dd0d275391ec0c58424b91a00e3ae0a1ea93233ec3a718026
5
+ SHA512:
6
+ metadata.gz: 99e059244669e8aa9a7ab9393aaad1461231667ef66ce0073ad417462bae8996c9819794e8fba8006ea4fabf6296426b006ae5ff485afb0b92aefaafd5719bf2
7
+ data.tar.gz: 5497a34f27a83e88f91eadca30dbaf3732f38da9b70c5d7bbfbe994ab3b0293156966bb187c3394120ab35a992995d3ea8b7d1e6e4b7d4384b55e141badfd28e
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 bibendi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,206 @@
1
+ # DIP [![Build Status](https://travis-ci.org/bibendi/dip.svg?branch=master)](https://travis-ci.org/bibendi/dip)
2
+
3
+ Docker Interaction Process
4
+
5
+ CLI utility for straightforward provisioning and interacting with an application configured by docker-compose.
6
+
7
+ DIP also contains commands for running support containers such as ssh-agent and DNS server.
8
+
9
+ ## Installation
10
+
11
+ ```sh
12
+ gem install dip
13
+ ```
14
+
15
+ ## Changelog
16
+
17
+ https://github.com/bibendi/dip/releases
18
+
19
+
20
+ ## Docker installation
21
+
22
+ - [Ubuntu](docs/docker-ubuntu-install.md)
23
+ - [Mac OS](docs/docker-for-mac-install.md)
24
+
25
+ ## Usage
26
+
27
+ ```sh
28
+ dip --help
29
+ dip SUBCOMMAND --help
30
+ ```
31
+
32
+ ### dip.yml
33
+
34
+ ```yml
35
+ version: '2'
36
+
37
+ environment:
38
+ COMPOSE_EXT: development
39
+ RAILS_ENV: development
40
+
41
+ compose:
42
+ files:
43
+ - docker/docker-compose.yml
44
+ - docker/docker-compose.$COMPOSE_EXT.yml
45
+ - docker/docker-compose.$DIP_OS.yml
46
+ project_name: bear-$RAILS_ENV
47
+
48
+ interaction:
49
+ sh:
50
+ service: foo-app
51
+ compose_run_options: [no-deps]
52
+
53
+ bundle:
54
+ service: foo-app
55
+ command: bundle
56
+
57
+ rake:
58
+ service: foo-app
59
+ command: bundle exec rake
60
+
61
+ rspec:
62
+ service: foo-app
63
+ environment:
64
+ RAILS_ENV: test
65
+ command: bundle exec rspec
66
+
67
+ rails:
68
+ service: foo-app
69
+ command: bundle exec rails
70
+ subcommands:
71
+ s:
72
+ service: foo-web
73
+ compose_method: up
74
+
75
+ psql:
76
+ service: foo-app
77
+ command: psql -h pg -U postgres
78
+
79
+ provision:
80
+ - dip compose up -d foo-pg foo-redis
81
+ - dip bundle install
82
+ - dip rake db:migrate
83
+ ```
84
+
85
+ ### dip run
86
+
87
+ Run commands defined within `interaction` section of dip.yml
88
+
89
+ ```sh
90
+ dip run rails c
91
+ dip run rake db:migrate
92
+ ```
93
+
94
+ `run` argument can be ommited
95
+
96
+ ```sh
97
+ dip rake db:migrate
98
+ dip VERSION=12352452 rake db:rollback
99
+ ```
100
+
101
+ ### dip provision
102
+
103
+ Run commands each by each from `provision` section of dip.yml
104
+
105
+ ### dip compose
106
+
107
+ Run docker-compose commands that are configured according with application dip.yml
108
+
109
+ ```sh
110
+ dip compose COMMAND [OPTIONS]
111
+
112
+ dip compose up -d redis
113
+ ```
114
+
115
+ ### dip ssh
116
+
117
+ Runs ssh-agent container based on https://github.com/whilp/ssh-agent with your ~/.ssh/id_rsa.
118
+ It creates a named volume `ssh_data` with ssh socket.
119
+ An application's docker-compose.yml should contains environment variable `SSH_AUTH_SOCK=/ssh/auth/sock` and connects to external volume `ssh_data`.
120
+
121
+ ```sh
122
+ dip ssh up
123
+ ```
124
+
125
+ docker-compose.yml
126
+
127
+ ```yml
128
+ services:
129
+ web:
130
+ environment:
131
+ - SSH_AUTH_SOCK=/ssh/auth/sock
132
+ volumes:
133
+ - ssh-data:/ssh:ro
134
+
135
+ volumes:
136
+ ssh-data:
137
+ external:
138
+ name: ssh_data
139
+ ```
140
+
141
+ ### dip nginx
142
+
143
+ Runs Nginx server container based on [bibendi/nginx-proxy](https://github.com/bibendi/nginx-proxy) image. An application's docker-compose.yml should contains environment variable `VIRTUAL_HOST` and `VIRTUAL_PATH` and connects to external network `frontend`.
144
+
145
+ foo-project/docker-compose.yml
146
+
147
+ ```yml
148
+ version: '2'
149
+
150
+ services:
151
+ foo-web:
152
+ image: company/foo_image
153
+ environment:
154
+ - VIRTUAL_HOST=*.bar-app.docker
155
+ - VIRTUAL_PATH=/
156
+ networks:
157
+ - default
158
+ - frontend
159
+ dns: $DIP_DNS
160
+
161
+ networks:
162
+ frontend:
163
+ external:
164
+ name: frontend
165
+ ```
166
+
167
+ baz-project/docker-compose.yml
168
+
169
+ ```yml
170
+ version: '2'
171
+
172
+ services:
173
+ baz-web:
174
+ image: company/baz_image
175
+ environment:
176
+ - VIRTUAL_HOST=*.bar-app.docker
177
+ - VIRTUAL_PATH=/api/v1/baz_service,/api/v2/baz_service
178
+ networks:
179
+ - default
180
+ - frontend
181
+ dns: $DIP_DNS
182
+
183
+ networks:
184
+ frontend:
185
+ external:
186
+ name: frontend
187
+ ```
188
+
189
+ ```sh
190
+ dip nginx up
191
+ cd foo-project && dip compose up
192
+ cd baz-project && dip compose up
193
+ curl www.bar-app.docker/api/v1/quz
194
+ curl www.bar-app.docker/api/v1/baz_service/qzz
195
+ ```
196
+
197
+ ### dip dns
198
+
199
+ Runs DNS server container based on https://github.com/aacebedo/dnsdock It used for container to container requests through nginx. An application's docker-compose.yml should define `dns` configuration with environment variable `$DIP_DNS` and connect to external network `frontend`. `$DIP_DNS` will be automatically assigned by dip.
200
+
201
+ ```sh
202
+ dip dns up
203
+
204
+ cd foo-project
205
+ dip compose exec foo-web curl http://www.bar-app.docker/api/v1/baz_service
206
+ ```
data/exe/dip ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib_path = File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
+
7
+ require 'dip'
8
+ require 'dip/cli'
9
+ require 'pry-byebug' if Dip.debug?
10
+
11
+ Signal.trap('INT') do
12
+ warn("\n#{caller.join("\n")}: interrupted")
13
+ exit(1)
14
+ end
15
+
16
+ begin
17
+ run_vars = []
18
+
19
+ ARGV.each do |arg|
20
+ if !run_vars.frozen? && arg.include?("=")
21
+ key, val = arg.split("=", 2)
22
+ run_vars << "#{'--x-dip-run-vars=' if run_vars.empty?}#{key}:#{val}"
23
+ else
24
+ run_vars.freeze
25
+ end
26
+ end
27
+
28
+ unless run_vars.empty?
29
+ ARGV.shift(run_vars.size)
30
+ ARGV.push(*run_vars)
31
+ end
32
+
33
+ Dip::CLI.start
34
+ rescue Dip::Error => err
35
+ puts "ERROR: #{err.message}"
36
+ exit 1
37
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dip/version"
4
+ require "dip/config"
5
+ require "dip/environment"
6
+
7
+ module Dip
8
+ Error = Class.new(StandardError)
9
+
10
+ class << self
11
+ def config_path
12
+ ENV["DIP_FILE"] || "./dip.yml"
13
+ end
14
+
15
+ def config
16
+ @config ||= Dip::Config.new(config_path)
17
+ end
18
+
19
+ def env
20
+ @env ||= Dip::Environment.new(config.environment)
21
+ end
22
+
23
+ def test?
24
+ ENV["DIP_ENV"] == "test"
25
+ end
26
+
27
+ def debug?
28
+ ENV["DIP_ENV"] == "debug"
29
+ end
30
+
31
+ def reset!
32
+ @config = nil
33
+ @env = nil
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Dip
6
+ class CLI < Thor
7
+ class << self
8
+ def retrieve_command_name(args)
9
+ meth = args.first.to_sym unless args.empty?
10
+ args.unshift("run") if meth && ::Dip.config.interaction.key?(meth.to_sym)
11
+
12
+ super(args)
13
+ end
14
+
15
+ # Hackery. Take the run method away from Thor so that we can redefine it.
16
+ def is_thor_reserved_word?(word, type)
17
+ return false if word == "run"
18
+
19
+ super
20
+ end
21
+ end
22
+
23
+ desc 'version', 'dip version'
24
+ def version
25
+ require_relative 'version'
26
+ puts Dip::VERSION
27
+ end
28
+ map %w(--version -v) => :version
29
+
30
+ desc 'compose CMD [OPTIONS]', 'Run docker-compose commands'
31
+ method_option :help, aliases: '-h', type: :boolean,
32
+ desc: 'Display usage information'
33
+ def compose(cmd, *argv)
34
+ if options[:help]
35
+ invoke :help, ['compose']
36
+ else
37
+ require_relative 'commands/compose'
38
+ Dip::Commands::Compose.new(cmd, argv).execute
39
+ end
40
+ end
41
+
42
+ desc 'CMD or dip run CMD [OPTIONS]', 'Run configured command in a docker-compose service'
43
+ method_option :help, aliases: '-h', type: :boolean,
44
+ desc: 'Display usage information'
45
+ method_option :x_dip_run_vars,
46
+ type: :hash,
47
+ desc: "Enforce environment variables into container, recommended run like 'dip FOO=bar cmd'"
48
+ def run(cmd, subcmd = nil, *argv)
49
+ if options[:help]
50
+ invoke :help, ['run']
51
+ else
52
+ require_relative 'commands/run'
53
+ Dip::Commands::Run.
54
+ new(cmd, subcmd, argv,
55
+ run_vars: options[:x_dip_run_vars]).
56
+ execute
57
+ end
58
+ end
59
+
60
+ desc "provision", "Execute commands within provision section"
61
+ method_option :help, aliases: '-h', type: :boolean,
62
+ desc: 'Display usage information'
63
+ def provision
64
+ if options[:help]
65
+ invoke :help, ['provision']
66
+ else
67
+ require_relative 'commands/provision'
68
+ Dip::Commands::Provision.new.execute
69
+ end
70
+ end
71
+
72
+ desc "ssh", "ssh-agent container commands"
73
+ def ssh(*args)
74
+ require_relative 'cli/ssh'
75
+ Dip::CLI::SSH.start(args)
76
+ end
77
+
78
+ desc "dns", "DNS server for automatic docker container discovery"
79
+ def dns(*args)
80
+ require_relative 'cli/dns'
81
+ Dip::CLI::DNS.start(args)
82
+ end
83
+
84
+ desc "nginx", "Nginx reverse proxy server"
85
+ def nginx(*args)
86
+ require_relative 'cli/nginx'
87
+ Dip::CLI::Nginx.start(args)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require_relative "../commands/dns"
5
+
6
+ module Dip
7
+ class CLI
8
+ # See more https://github.com/aacebedo/dnsdock
9
+ class DNS < Thor
10
+ desc "dns up", "Run dnsdock container"
11
+ method_option :help, aliases: '-h', type: :boolean,
12
+ desc: 'Display usage information'
13
+ method_option :name, aliases: '-n', type: :string, default: "dnsdock",
14
+ desc: 'Container name'
15
+ method_option :socket, aliases: '-s', type: :string, default: "/var/run/docker.sock",
16
+ desc: 'Path to docker socket'
17
+ method_option :net, aliases: '-t', type: :string, default: "frontend",
18
+ desc: 'Container network name'
19
+ method_option :publish, aliases: '-p', type: :string, default: "53/udp",
20
+ desc: 'Container port'
21
+ method_option :image, aliases: '-i', type: :string, default: "aacebedo/dnsdock:latest-amd64",
22
+ desc: 'Docker image name'
23
+ method_option :domain, aliases: '-d', type: :string, default: "docker",
24
+ desc: 'Top level domain'
25
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
26
+ def up
27
+ if options[:help]
28
+ invoke :help, ['up']
29
+ else
30
+ Dip::Commands::DNS::Up.new(
31
+ name: options.fetch(:name),
32
+ socket: options.fetch(:socket),
33
+ net: options.fetch(:net),
34
+ publish: options.fetch(:publish),
35
+ image: options.fetch(:image),
36
+ domain: options.fetch(:domain)
37
+ ).execute
38
+ end
39
+ end
40
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
41
+
42
+ desc "dns down", "Stop dnsdock container"
43
+ method_option :help, aliases: '-h', type: :boolean,
44
+ desc: 'Display usage information'
45
+ method_option :name, aliases: '-n', type: :string, default: "dnsdock",
46
+ desc: 'Container name'
47
+ def down
48
+ if options[:help]
49
+ invoke :help, ['down']
50
+ else
51
+ Dip::Commands::DNS::Down.new(
52
+ name: options.fetch(:name)
53
+ ).execute
54
+ end
55
+ end
56
+
57
+ desc "dns restart", "Stop and start dnsdock container"
58
+ method_option :help, aliases: '-h', type: :boolean,
59
+ desc: 'Display usage information'
60
+ def restart(*args)
61
+ if options[:help]
62
+ invoke :help, ['restart']
63
+ else
64
+ Dip::CLI::DNS.start(["down"] + args)
65
+ sleep 1
66
+ Dip::CLI::DNS.start(["up"] + args)
67
+ end
68
+ end
69
+
70
+ desc "dns ip", "Get ip address of dnsdock container"
71
+ method_option :help, aliases: '-h', type: :boolean,
72
+ desc: 'Display usage information'
73
+ method_option :name, aliases: '-n', type: :string, default: "dnsdock",
74
+ desc: 'Container name'
75
+ method_option :net, aliases: '-t', type: :string, default: "frontend",
76
+ desc: 'Container network name'
77
+ def ip
78
+ if options[:help]
79
+ invoke :help, ['status']
80
+ else
81
+ Dip::Commands::DNS::IP.new(
82
+ name: options.fetch(:name),
83
+ net: options.fetch(:net)
84
+ ).execute
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require_relative "../commands/nginx"
5
+
6
+ module Dip
7
+ class CLI
8
+ # See more https://github.com/bibendi/nginx-proxy
9
+ class Nginx < Thor
10
+ desc "nginx up", "Run nginx container"
11
+ method_option :help, aliases: '-h', type: :boolean,
12
+ desc: 'Display usage information'
13
+ method_option :name, aliases: '-n', type: :string, default: "nginx",
14
+ desc: 'Container name'
15
+ method_option :socket, aliases: '-s', type: :string, default: "/var/run/docker.sock",
16
+ desc: 'Path to docker socket'
17
+ method_option :net, aliases: '-t', type: :string, default: "frontend",
18
+ desc: 'Container network name'
19
+ method_option :publish, aliases: '-p', type: :string, default: "80:80",
20
+ desc: 'Container port'
21
+ method_option :image, aliases: '-i', type: :string, default: "bibendi/nginx-proxy:latest",
22
+ desc: 'Docker image name'
23
+ method_option :domain, aliases: '-d', type: :string, default: "docker",
24
+ desc: 'Top level domain'
25
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
26
+ def up
27
+ if options[:help]
28
+ invoke :help, ['up']
29
+ else
30
+ Dip::Commands::Nginx::Up.new(
31
+ name: options.fetch(:name),
32
+ socket: options.fetch(:socket),
33
+ net: options.fetch(:net),
34
+ publish: options.fetch(:publish),
35
+ image: options.fetch(:image),
36
+ domain: options.fetch(:domain)
37
+ ).execute
38
+ end
39
+ end
40
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
41
+
42
+ desc "nginx down", "Stop nginx container"
43
+ method_option :help, aliases: '-h', type: :boolean,
44
+ desc: 'Display usage information'
45
+ method_option :name, aliases: '-n', type: :string, default: "nginx",
46
+ desc: 'Container name'
47
+ def down
48
+ if options[:help]
49
+ invoke :help, ['down']
50
+ else
51
+ Dip::Commands::Nginx::Down.new(
52
+ name: options.fetch(:name)
53
+ ).execute
54
+ end
55
+ end
56
+
57
+ desc "nginx restart", "Stop and start nginx container"
58
+ method_option :help, aliases: '-h', type: :boolean,
59
+ desc: 'Display usage information'
60
+ def restart(*args)
61
+ if options[:help]
62
+ invoke :help, ['restart']
63
+ else
64
+ Dip::CLI::Nginx.start(["down"] + args)
65
+ sleep 1
66
+ Dip::CLI::Nginx.start(["up"] + args)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require_relative "../commands/ssh"
5
+
6
+ module Dip
7
+ class CLI
8
+ class SSH < Thor
9
+ desc "ssh up", "Run ssh-agent container"
10
+ method_option :help, aliases: '-h', type: :boolean,
11
+ desc: 'Display usage information'
12
+ method_option :key, aliases: '-k', type: :string, default: "$HOME/.ssh/id_rsa",
13
+ desc: 'Path to ssh key'
14
+ method_option :volume, aliases: '-v', type: :string, default: "$HOME",
15
+ desc: 'Mounted docker volume'
16
+ method_option :interactive, aliases: '-t', type: :boolean, default: true,
17
+ desc: 'Run in interactive mode'
18
+ # Backward compatibility
19
+ method_option :nonteractive, aliases: '-T', type: :boolean,
20
+ desc: 'Run in noninteractive mode'
21
+ def up
22
+ if options[:help]
23
+ invoke :help, ['up']
24
+ else
25
+ Dip::Commands::SSH::Up.new(
26
+ key: options.fetch(:key),
27
+ volume: options.fetch(:volume),
28
+ interactive: options.nonteractive? ? false : options.interactive?
29
+ ).execute
30
+ end
31
+ end
32
+
33
+ map add: :up
34
+
35
+ desc "ssh down", "Stop ssh-agent container"
36
+ method_option :help, aliases: '-h', type: :boolean,
37
+ desc: 'Display usage information'
38
+ def down
39
+ if options[:help]
40
+ invoke :help, ['down']
41
+ else
42
+ Dip::Commands::SSH::Down.new.execute
43
+ end
44
+ end
45
+
46
+ desc "ssh restart", "Stop and start ssh-agent container"
47
+ method_option :help, aliases: '-h', type: :boolean,
48
+ desc: 'Display usage information'
49
+ def restart(*args)
50
+ if options[:help]
51
+ invoke :help, ['restart']
52
+ else
53
+ Dip::CLI::SSH.start(["down"] + args)
54
+ sleep 1
55
+ Dip::CLI::SSH.start(["up"] + args)
56
+ end
57
+ end
58
+
59
+ desc "ssh status", "Show status of ssh-agent container"
60
+ method_option :help, aliases: '-h', type: :boolean,
61
+ desc: 'Display usage information'
62
+ def status
63
+ if options[:help]
64
+ invoke :help, ['status']
65
+ else
66
+ Dip::Commands::SSH::Status.new.execute
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Dip
6
+ class Command
7
+ extend Forwardable
8
+
9
+ def_delegators self, :shell, :subshell
10
+
11
+ class ExecRunner
12
+ def self.call(cmd, argv, env: {}, **options)
13
+ ::Process.exec(env, cmd, *argv, options)
14
+ end
15
+ end
16
+
17
+ class SubshellRunner
18
+ def self.call(cmd, argv, env: {}, panic: true, **options)
19
+ return if ::Kernel.system(env, cmd, *argv, options)
20
+ raise Dip::Error, "Command '#{([cmd] + argv).join(' ')}' executed with error." if panic
21
+ end
22
+ end
23
+
24
+ class << self
25
+ def shell(cmd, argv = [], subshell: false, **options)
26
+ cmd = Dip.env.interpolate(cmd)
27
+ argv = argv.map { |arg| Dip.env.interpolate(arg) }
28
+
29
+ puts [Dip.env.vars, cmd, argv].inspect if Dip.debug?
30
+
31
+ runner = subshell ? SubshellRunner : ExecRunner
32
+ runner.call(cmd, argv, env: Dip.env.vars, **options)
33
+ end
34
+
35
+ def subshell(*args, **kwargs)
36
+ kwargs[:subshell] = true
37
+ shell(*args, **kwargs)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative 'dns'
5
+
6
+ module Dip
7
+ module Commands
8
+ class Compose < Dip::Command
9
+ DOCKER_EMBEDDED_DNS = "127.0.0.11"
10
+
11
+ def initialize(cmd, argv = [])
12
+ @cmd = cmd
13
+ @argv = argv
14
+ @config = ::Dip.config.compose
15
+ end
16
+
17
+ def execute
18
+ compose_argv = Array(find_files) + Array(find_project_name)
19
+ compose_argv << @cmd
20
+ compose_argv += @argv
21
+
22
+ Dip.env["DIP_DNS"] ||= find_dns
23
+
24
+ shell("docker-compose", compose_argv)
25
+ end
26
+
27
+ private
28
+
29
+ def find_files
30
+ return unless (files = @config[:files])
31
+
32
+ if files.is_a?(Array)
33
+ files.each_with_object([]) do |file_name, memo|
34
+ file_name = ::Dip.env.interpolate(file_name)
35
+ next unless File.exist?(file_name)
36
+
37
+ memo << "--file"
38
+ memo << file_name
39
+ end
40
+ end
41
+ end
42
+
43
+ def find_project_name
44
+ return unless (project_name = @config[:project_name])
45
+
46
+ if project_name.is_a?(String)
47
+ project_name = ::Dip.env.interpolate(project_name)
48
+ ["--project-name", project_name]
49
+ end
50
+ end
51
+
52
+ def find_dns
53
+ name = Dip.env["DNSDOCK_CONTAINER"] || "dnsdock"
54
+ net = Dip.env["FRONTEND_NETWORK"] || "frontend"
55
+
56
+ IO.pipe do |r, w|
57
+ Dip::Commands::DNS::IP.
58
+ new(name: name, net: net).
59
+ execute(out: w, err: File::NULL, panic: false)
60
+
61
+ w.close_write
62
+ ip = r.readlines[0].to_s.strip
63
+ ip.empty? ? DOCKER_EMBEDDED_DNS : ip
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require_relative '../command'
5
+
6
+ module Dip
7
+ module Commands
8
+ module DNS
9
+ class Up < Dip::Command
10
+ def initialize(name:, socket:, net:, publish:, image:, domain:)
11
+ @name = name
12
+ @socket = socket
13
+ @net = net
14
+ @publish = publish
15
+ @image = image
16
+ @domain = domain
17
+ end
18
+
19
+ def execute
20
+ subshell("docker", "network create #{@net}".shellsplit, panic: false, err: File::NULL)
21
+ subshell("docker", "run #{container_args} #{@image} --domain=#{@domain}".shellsplit)
22
+ end
23
+
24
+ private
25
+
26
+ def container_args
27
+ result = %w(--detach)
28
+ result << "--volume #{@socket}:/var/run/docker.sock:ro"
29
+ result << "--restart always"
30
+ result << "--publish #{@publish}"
31
+ result << "--net #{@net}"
32
+ result << "--name #{@name}"
33
+ result.join(' ')
34
+ end
35
+ end
36
+
37
+ class Down < Dip::Command
38
+ def initialize(name:)
39
+ @name = name
40
+ end
41
+
42
+ def execute
43
+ subshell("docker", "stop #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
44
+ subshell("docker", "rm -v #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
45
+ end
46
+ end
47
+
48
+ class IP < Dip::Command
49
+ def initialize(name:, net:)
50
+ @name = name
51
+ @net = net
52
+ end
53
+
54
+ def execute(**options)
55
+ subshell("docker",
56
+ "inspect " \
57
+ "--format '{{ .NetworkSettings.Networks.#{@net}.IPAddress }}' " \
58
+ "#{@name}".shellsplit,
59
+ **options)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require_relative '../command'
5
+
6
+ module Dip
7
+ module Commands
8
+ module Nginx
9
+ class Up < Dip::Command
10
+ def initialize(name:, socket:, net:, publish:, image:, domain:)
11
+ @name = name
12
+ @socket = socket
13
+ @net = net
14
+ @publish = publish
15
+ @image = image
16
+ @domain = domain
17
+ end
18
+
19
+ def execute
20
+ subshell("docker", "network create #{@net}".shellsplit, panic: false, err: File::NULL)
21
+ subshell("docker", "run #{container_args} #{@image}".shellsplit)
22
+ end
23
+
24
+ private
25
+
26
+ def container_args
27
+ result = %w(--detach)
28
+ result << "--volume #{@socket}:/tmp/docker.sock:ro"
29
+ result << "--restart always"
30
+ result << "--publish #{@publish}"
31
+ result << "--net #{@net}"
32
+ result << "--name #{@name}"
33
+ result << "--label com.dnsdock.alias=#{@domain}"
34
+ result.join(' ')
35
+ end
36
+ end
37
+
38
+ class Down < Dip::Command
39
+ def initialize(name:)
40
+ @name = name
41
+ end
42
+
43
+ def execute
44
+ subshell("docker", "stop #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
45
+ subshell("docker", "rm -v #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+
5
+ module Dip
6
+ module Commands
7
+ class Provision < Dip::Command
8
+ def execute
9
+ Dip.config.provision.each do |command|
10
+ subshell(command)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require_relative '../command'
5
+ require_relative 'compose'
6
+
7
+ module Dip
8
+ module Commands
9
+ class Run < Dip::Command
10
+ def initialize(cmd, subcmd = nil, argv = [], run_vars: nil)
11
+ @cmd = cmd.to_sym
12
+ @subcmd = subcmd.to_sym if subcmd
13
+ @argv = argv
14
+ @run_vars = run_vars
15
+ @config = ::Dip.config.interaction
16
+ end
17
+
18
+ # TODO: Refactor
19
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
20
+ def execute
21
+ command = @config.fetch(@cmd)
22
+ command[:subcommands] ||= {}
23
+
24
+ if (subcommand = command[:subcommands].fetch(@subcmd, {})).any?
25
+ subcommand[:command] ||= nil
26
+ command.merge!(subcommand)
27
+ elsif @subcmd
28
+ @argv.unshift(@subcmd.to_s)
29
+ end
30
+
31
+ Dip.env.merge(command[:environment]) if command[:environment]
32
+
33
+ compose_method = command.fetch(:compose_method, "run").to_s
34
+
35
+ compose_argv = []
36
+ compose_argv.concat(prepare_compose_run_options(command[:compose_run_options]))
37
+ compose_argv.concat(run_vars)
38
+ compose_argv << "--rm" if compose_method == "run"
39
+ compose_argv << command.fetch(:service).to_s
40
+ compose_argv += command[:command].to_s.shellsplit
41
+ compose_argv.concat(@argv)
42
+
43
+ Dip::Commands::Compose.new(compose_method, compose_argv).execute
44
+ end
45
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
46
+
47
+ def prepare_compose_run_options(value)
48
+ return [] unless value
49
+
50
+ value.map do |o|
51
+ o = o.start_with?("-") ? o : "--#{o}"
52
+ o.shellsplit
53
+ end.flatten
54
+ end
55
+
56
+ def run_vars
57
+ return [] unless @run_vars
58
+
59
+ @run_vars.map { |k, v| ["-e", "#{k}=#{v}"] }.flatten
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require_relative '../command'
5
+
6
+ module Dip
7
+ module Commands
8
+ module SSH
9
+ class Up < Dip::Command
10
+ def initialize(key:, volume:, interactive:)
11
+ @key = key
12
+ @volume = volume
13
+ @interactive = interactive
14
+ end
15
+
16
+ def execute
17
+ subshell("docker", "volume create --name ssh_data".shellsplit, out: File::NULL, err: File::NULL)
18
+
19
+ subshell("docker", "run --detach --volume ssh_data:/ssh --name=ssh-agent whilp/ssh-agent".shellsplit)
20
+
21
+ key = Dip.env.interpolate(@key)
22
+ subshell("docker", "run #{container_args} whilp/ssh-agent ssh-add #{key}".shellsplit)
23
+ end
24
+
25
+ private
26
+
27
+ def container_args
28
+ result = %w(--rm)
29
+ volume = Dip.env.interpolate(@volume)
30
+ result << "--volume ssh_data:/ssh"
31
+ result << "--volume #{volume}:#{volume}"
32
+ result << "--interactive --tty" if @interactive
33
+ result.join(' ')
34
+ end
35
+ end
36
+
37
+ class Down < Dip::Command
38
+ def execute
39
+ subshell("docker", "stop ssh-agent".shellsplit, panic: false, out: File::NULL, err: File::NULL)
40
+ subshell("docker", "rm -v ssh-agent".shellsplit, panic: false, out: File::NULL, err: File::NULL)
41
+ subshell("docker", "volume rm ssh_data".shellsplit, panic: false, out: File::NULL, err: File::NULL)
42
+ end
43
+ end
44
+
45
+ class Status < Dip::Command
46
+ def execute
47
+ subshell("docker", "inspect --format '{{.State.Status}}' ssh-agent".shellsplit)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "erb"
5
+
6
+ module Dip
7
+ class Config
8
+ def initialize(config_path)
9
+ load_or_default(config_path)
10
+ end
11
+
12
+ def merge(config)
13
+ @config.merge!(config)
14
+ end
15
+
16
+ def environment
17
+ @config.fetch(:environment, {})
18
+ end
19
+
20
+ def compose
21
+ @config.fetch(:compose, {})
22
+ end
23
+
24
+ def interaction
25
+ @config.fetch(:interaction, {})
26
+ end
27
+
28
+ def provision
29
+ @config.fetch(:provision, [])
30
+ end
31
+
32
+ private
33
+
34
+ def load_or_default(config_path)
35
+ @config ||= if File.exist?(config_path) && !Dip.test?
36
+ YAML.safe_load(
37
+ ERB.new(File.read(config_path)).result,
38
+ [], [], true,
39
+ symbolize_names: true
40
+ )
41
+ else
42
+ {}
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dip
4
+ class Environment
5
+ VAR_REGEX = /\$[\{]?(?<var_name>[a-zA-Z_][a-zA-Z0-9_]*)[\}]?/
6
+ SPECIAL_VARS = {"DIP_OS" => :find_dip_os}.freeze
7
+
8
+ attr_reader :vars
9
+
10
+ def initialize(default_vars)
11
+ @vars = {}
12
+
13
+ merge(default_vars)
14
+ end
15
+
16
+ def merge(new_vars)
17
+ new_vars.each do |key, value|
18
+ key = key.to_s
19
+ @vars[key] = ENV.fetch(key) { interpolate(value.to_s) }
20
+ end
21
+ end
22
+
23
+ def [](name)
24
+ vars.fetch(name) { ENV[name] }
25
+ end
26
+
27
+ def []=(key, value)
28
+ @vars[key] = value
29
+ end
30
+
31
+ def interpolate(value)
32
+ value.gsub(VAR_REGEX) do
33
+ var_name = Regexp.last_match[:var_name]
34
+
35
+ if SPECIAL_VARS.key?(var_name)
36
+ self[var_name] || send(SPECIAL_VARS[var_name])
37
+ else
38
+ self[var_name]
39
+ end
40
+ end
41
+ end
42
+
43
+ alias replace interpolate
44
+
45
+ private
46
+
47
+ def find_dip_os
48
+ @dip_os ||= Gem::Platform.local.os
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dip
4
+ VERSION = "3.0.0"
5
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dip
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ platform: ruby
6
+ authors:
7
+ - bibendi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-09-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.20.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.20.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.59'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.59'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.16'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.16'
111
+ - !ruby/object:Gem::Dependency
112
+ name: test-unit
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3'
125
+ description: DIP - Docker Interaction Process.CLI tool for better development experience
126
+ when interacting with docker and docker-compose.
127
+ email:
128
+ - merkushin.m.s@gmail.com
129
+ executables:
130
+ - dip
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - LICENSE.txt
135
+ - README.md
136
+ - exe/dip
137
+ - lib/dip.rb
138
+ - lib/dip/cli.rb
139
+ - lib/dip/cli/dns.rb
140
+ - lib/dip/cli/nginx.rb
141
+ - lib/dip/cli/ssh.rb
142
+ - lib/dip/command.rb
143
+ - lib/dip/commands/compose.rb
144
+ - lib/dip/commands/dns.rb
145
+ - lib/dip/commands/nginx.rb
146
+ - lib/dip/commands/provision.rb
147
+ - lib/dip/commands/run.rb
148
+ - lib/dip/commands/ssh.rb
149
+ - lib/dip/config.rb
150
+ - lib/dip/environment.rb
151
+ - lib/dip/version.rb
152
+ homepage: https://github.com/bibendi/dip
153
+ licenses:
154
+ - MIT
155
+ metadata:
156
+ allowed_push_host: https://rubygems.org/
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 2.7.6
174
+ signing_key:
175
+ specification_version: 4
176
+ summary: Ruby gem CLI tool for better interacting docker-compose files.
177
+ test_files: []