docker-compose 0.0.0 → 0.1.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.
@@ -0,0 +1,165 @@
1
+ require 'open3'
2
+
3
+ module Docker::Compose
4
+ # An easy-to-use interface for invoking commands and capturing their output.
5
+ # Instances of Shell can be interactive, which prints the command's output
6
+ # to the terminal and also allows the user to interact with the command.
7
+ class Shell
8
+ # If true, commands run in the shell will have their stdio streams tied
9
+ # to the parent process so the user can view their output and send input
10
+ # to them. Commands' stdout is still captured normally when they are
11
+ # interactive.
12
+ #
13
+ # Note that interactivity doesn't work very well because we use popen,
14
+ # which uses pipes to communicate with the child process and pipes have
15
+ # a fixed buffer size; the displayed output tends to "lag" behind the
16
+ # actual program, and bytes sent to stdin may not arrive until you send
17
+ # a lot of them!
18
+ #
19
+ # TODO: solve pipe buffering issues, perhaps with a pty...
20
+ #
21
+ # @return [Boolean]
22
+ attr_accessor :interactive
23
+
24
+ # Convert Ruby keyword arguments into CLI parameters that are compatible
25
+ # with the syntax of golang's flags package.
26
+ #
27
+ # Options are translated to CLI parameters using the following convention:
28
+ # 1) Snake-case symbols are hyphenated, e.g. :no_foo => "--no-foo"
29
+ # 2) boolean values indicate a CLI flag; true includes the flag, false or nil omits it
30
+ # 3) other values indicate a CLI option that has a value.
31
+ # 4) single character values are passed as short options e.g. "-X V"
32
+ # 5) multi-character values are passed as long options e.g. "--XXX=V"
33
+ #
34
+ def self.options(**opts)
35
+ flags = []
36
+
37
+ # Transform opts into golang flags-style command line parameters;
38
+ # append them to the command.
39
+ opts.each do |kw, arg|
40
+ if kw.length == 1
41
+ if arg == true
42
+ # true: boolean flag
43
+ flags << "-#{kw}"
44
+ elsif arg
45
+ # truthey: option that has a value
46
+ flags << "-#{kw}" << arg.to_s
47
+ else
48
+ # falsey: omit boolean flag
49
+ end
50
+ else
51
+ kw = kw.to_s.gsub('_','-')
52
+ if arg == true
53
+ # true: boolean flag
54
+ flags << "--#{kw}"
55
+ elsif arg
56
+ # truthey: option that has a value
57
+ flags << "--#{kw}=#{arg}"
58
+ else
59
+ # falsey: omit boolean flag
60
+ end
61
+ end
62
+ end
63
+
64
+ flags
65
+ end
66
+
67
+ # Create an instance of Shell.
68
+ def initialize
69
+ @interactive = false
70
+ end
71
+
72
+ # Run a shell command whose arguments and flags are expressed using some
73
+ # Rubyish sugar. This method accepts an arbitrary number of positional
74
+ # parameters; each parameter can be a Hash, an array, or a simple Object.
75
+ # Arrays and simple objects are appended to argv as "bare" words; Hashes
76
+ # are translated to golang flags and then appended to argv.
77
+ #
78
+ # @example Run docker-compose with complex parameters
79
+ # command('docker-compose', {file: 'joe.yml'}, 'up', {d:true}, 'mysvc')
80
+ #
81
+ # @see #options for information on Hash-to-flag translation
82
+ def command(*cmd)
83
+ argv = []
84
+
85
+ cmd.each do |item|
86
+ case item
87
+ when Array
88
+ # list of words to append to argv
89
+ argv.concat(item.map { |e| e.to_s })
90
+ when Hash
91
+ # list of options to convert to CLI parameters
92
+ argv.concat(self.class.options(item))
93
+ else
94
+ # single word to append to argv
95
+ argv << item.to_s
96
+ end
97
+ end
98
+
99
+ run(argv)
100
+ end
101
+
102
+ # Run a shell command. Perform no translation or substitution. Return
103
+ # the program's exit status and stdout.
104
+ #
105
+ # @param [Array] argv command to run; argv[0] is program name and the
106
+ # remaining elements are parameters and flags
107
+ # @return [Array] a pair of Integer exitstatus and String output
108
+ private def run(argv)
109
+ stdin, stdout, stderr, thr = Open3.popen3(*argv)
110
+
111
+ streams = [stdout, stderr]
112
+
113
+ if @interactive
114
+ streams << STDIN
115
+ else
116
+ stdin.close
117
+ end
118
+
119
+ output = String.new.force_encoding(Encoding::BINARY)
120
+
121
+ until streams.empty? || (streams.length == 1 && streams.first == STDIN)
122
+ ready, _, _ = IO.select(streams, [], [], 1)
123
+
124
+ if ready && ready.include?(STDIN)
125
+ input = STDIN.readpartial(1_024) rescue nil
126
+ if input
127
+ stdin.write(input)
128
+ else
129
+ # our own STDIN got closed; proxy to child's stdin
130
+ stdin.close
131
+ end
132
+ end
133
+
134
+ if ready && ready.include?(stderr)
135
+ data = stderr.readpartial(1_024) rescue nil
136
+ if data
137
+ STDERR.write(data) if @interactive
138
+ else
139
+ streams.delete(stderr)
140
+ end
141
+ end
142
+
143
+ if ready && ready.include?(stdout)
144
+ data = stdout.readpartial(1_024) rescue nil
145
+ if data
146
+ output << data
147
+ STDOUT.write(data) if @interactive
148
+ else
149
+ streams.delete(stdout)
150
+ end
151
+ end
152
+ end
153
+
154
+ # This blocks until the process exits (which probably already happened,
155
+ # given that we have received EOF on its output streams).
156
+ status = thr.value.exitstatus
157
+
158
+ [status, output]
159
+ rescue Interrupt
160
+ # Proxy Ctrl+C to our child process
161
+ Process.kill('INT', thr.pid) rescue nil
162
+ raise
163
+ end
164
+ end
165
+ end
@@ -1,6 +1,5 @@
1
- # encoding: utf-8
2
1
  module Docker
3
2
  module Compose
4
- VERSION = '0.0.0'
3
+ VERSION = "0.1.0"
5
4
  end
6
5
  end
@@ -1,17 +1,12 @@
1
- # encoding: utf-8
2
- require 'net/http'
3
-
4
1
  require_relative 'compose/version'
5
- require_relative 'compose/error'
6
- require_relative 'compose/container'
7
- require_relative 'compose/collection'
2
+ require_relative 'compose/shell'
8
3
  require_relative 'compose/session'
9
4
  require_relative 'compose/net_info'
10
5
  require_relative 'compose/mapper'
11
6
 
12
7
  module Docker
13
8
  module Compose
14
- # Create a new session with default options.
9
+ # Create a new session.
15
10
  def self.new
16
11
  Session.new
17
12
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-compose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Spataro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-01 00:00:00.000000000 Z
11
+ date: 2015-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: backticks
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: bundler
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: '2.3'
19
+ version: '1.10'
34
20
  type: :development
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: '2.3'
26
+ version: '1.10'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rake
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -74,14 +60,10 @@ executables: []
74
60
  extensions: []
75
61
  extra_rdoc_files: []
76
62
  files:
77
- - ".coveralls.yml"
78
- - ".github/workflows/publish.yml"
79
63
  - ".gitignore"
80
64
  - ".rspec"
81
- - ".rubocop.yml"
82
65
  - ".ruby-version"
83
66
  - ".travis.yml"
84
- - CHANGELOG.md
85
67
  - CODE_OF_CONDUCT.md
86
68
  - Gemfile
87
69
  - LICENSE.txt
@@ -90,18 +72,13 @@ files:
90
72
  - bin/console
91
73
  - bin/setup
92
74
  - docker-compose.gemspec
93
- - docker-compose.yml
94
75
  - lib/docker/compose.rb
95
- - lib/docker/compose/collection.rb
96
- - lib/docker/compose/container.rb
97
- - lib/docker/compose/error.rb
76
+ - lib/docker/compose/future/session.rb
98
77
  - lib/docker/compose/mapper.rb
99
78
  - lib/docker/compose/net_info.rb
100
79
  - lib/docker/compose/rake_tasks.rb
101
80
  - lib/docker/compose/session.rb
102
- - lib/docker/compose/shell_printer.rb
103
- - lib/docker/compose/shell_printer/fish.rb
104
- - lib/docker/compose/shell_printer/posix.rb
81
+ - lib/docker/compose/shell.rb
105
82
  - lib/docker/compose/version.rb
106
83
  homepage: https://github.com/xeger/docker-compose
107
84
  licenses:
@@ -115,17 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
92
  requirements:
116
93
  - - ">="
117
94
  - !ruby/object:Gem::Version
118
- version: '2.0'
119
- - - "<"
120
- - !ruby/object:Gem::Version
121
- version: '4.0'
95
+ version: '0'
122
96
  required_rubygems_version: !ruby/object:Gem::Requirement
123
97
  requirements:
124
98
  - - ">="
125
99
  - !ruby/object:Gem::Version
126
100
  version: '0'
127
101
  requirements: []
128
- rubygems_version: 3.2.3
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.5
129
104
  signing_key:
130
105
  specification_version: 4
131
106
  summary: Wrapper docker-compose with added Rake smarts.
data/.coveralls.yml DELETED
@@ -1 +0,0 @@
1
- repo_token: hMWX6XPNvG4Gfy9gCDEvrxhQVKdyN5e2m
@@ -1,42 +0,0 @@
1
- name: publish
2
-
3
- on:
4
- push:
5
- branches: [actions]
6
-
7
- jobs:
8
- publish:
9
- name: publish
10
- runs-on: ubuntu-latest
11
- permissions:
12
- contents: read
13
- packages: write
14
-
15
- steps:
16
- - uses: actions/checkout@v3
17
- - uses: ruby/setup-ruby@v1
18
- with:
19
- ruby-version: 3.0.0
20
- bundler-cache: true
21
- - name: build
22
- run: |
23
- release='v0.0.0'
24
- version=`echo $release | cut -b2-`
25
- if ! echo $release | grep -q '^v[0-9]\+\.[0-9]\+\.[0-9]\+$'; then
26
- echo "Release name must be in the format of 'vX.Y.Z', got '$release'"
27
- exit 1
28
- fi
29
- sed -i -r "s/VERSION = '.+'/VERSION = '$version'/" lib/docker/compose/version.rb
30
- bundle exec rake build
31
- - name: push
32
- run: |
33
- mkdir -p $HOME/.gem
34
- touch $HOME/.gem/credentials
35
- chmod 0600 $HOME/.gem/credentials
36
- printf -- "---\n:rubygems_api_key: ${RUBYGEMS_TOKEN}\n:github: Bearer ${GITHUB_TOKEN}\n" > $HOME/.gem/credentials
37
- gem push pkg/*.gem
38
- gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} pkg/*.gem
39
- env:
40
- GITHUB_TOKEN: "Bearer ${{ secrets.GITHUB_TOKEN }}"
41
- OWNER: ${{ github.repository_owner }}
42
- RUBYGEMS_TOKEN: "${{ secrets.RUBYGEMS_TOKEN }}"
data/.rubocop.yml DELETED
@@ -1,38 +0,0 @@
1
- # CODING CONVENTIONS
2
- # - any cop that can be auto-corrected with `rubocop -a` is mandatory
3
- # - other cops may be enabled if committer fixes all issues
4
- AllCops:
5
- NewCops: enable
6
- Exclude:
7
- - features/**/*
8
- - spec/**/*
9
- Style/Encoding:
10
- Enabled: true
11
- Style/FormatString:
12
- Enabled: false
13
- Style/GuardClause:
14
- Enabled: false
15
- Layout/LineLength:
16
- Enabled: false
17
- Lint/AmbiguousRegexpLiteral:
18
- Enabled: false
19
- Metrics/AbcSize:
20
- Enabled: false
21
- Metrics/BlockLength:
22
- Enabled: false
23
- Metrics/ClassLength:
24
- Enabled: false
25
- Metrics/MethodLength:
26
- Enabled: false
27
- Metrics/CyclomaticComplexity:
28
- Enabled: false
29
- Metrics/PerceivedComplexity:
30
- Enabled: false
31
- Style/ClassAndModuleChildren:
32
- Enabled: false
33
- Style/Documentation:
34
- Enabled: false
35
- Style/RaiseArgs:
36
- Enabled: false
37
- Style/Semicolon:
38
- Enabled: false
data/CHANGELOG.md DELETED
@@ -1,32 +0,0 @@
1
- 1.1
2
- ---
3
-
4
- #### New features
5
-
6
- Add `scale` command to Session methods.
7
-
8
-
9
- 1.0
10
- ---
11
-
12
- No significant changes; the 1.0 increment is to indicate that Docker::Compose now has test coverage, and that we intend to maintain a stable API until 2.0.
13
-
14
- 0.6
15
- ---
16
-
17
- #### Interface-breaking changes
18
-
19
- - Replaced `docker:compose:server` Rake task with more general `docker:compose:host`
20
- - Replaced `server_env` option with `host_env`
21
- - Replaced `extra_server_env` option with `extra_host_env`
22
- - Stopped mapping values in `extra_host_env`; they are now exported verbatim
23
-
24
- #### New features
25
-
26
- Produce `docker:compose:env` output that is compatible with user's login shell.
27
-
28
- 0.5
29
- ---
30
-
31
- Initial public release of prototype. Features work well, but there is no test
32
- coverage.
data/docker-compose.yml DELETED
@@ -1,11 +0,0 @@
1
- # basic smoke test for the gem.
2
- # TODO: write cucumber features for comprehensive functional tests!
3
- apple:
4
- image: busybox
5
- command: /bin/sh -c 'sleep 3600'
6
- ports:
7
- - 123
8
- - 456
9
- orange:
10
- image: busybox
11
- command: /bin/sh -c 'sleep 3600'
@@ -1,13 +0,0 @@
1
- module Docker::Compose
2
- class Collection < Array
3
- # @example find containers that are up
4
- # who_is_up = coll.where { |c| c.up? }
5
- # @example count space taken by all containers
6
- # coll.map { |c| c.size }.inject(0) { |a, x| a + x }
7
- def where
8
- hits = Collection.new
9
- self.each { |c| hits << c if yield(c) }
10
- hits
11
- end
12
- end
13
- end
@@ -1,80 +0,0 @@
1
- module Docker::Compose
2
- class Container
3
- # Format string for `docker ps`
4
- PS_FMT = '({{.ID}}) ({{.Image}}) ({{.Size}}) ({{.Status}}) ({{.Names}}) ({{.Labels}}) ({{.Ports}})'
5
- # Number of template substitutions in PS_FMT
6
- PS_FMT_LEN = PS_FMT.count('.')
7
- # Pattern that parses human-readable values from ps .Status
8
- PS_STATUS = /^([A-Za-z]+) ?\(?([0-9]*)\)? ?(.*)$/i
9
- # Pattern that parses FIRST occurrence of container size from a string,
10
- # along with its units.
11
- PS_SIZE = /^([0-9.]+)\s*([kmgt]?B)/i
12
-
13
- attr_reader :id, :image, :size, :status, :exitstatus
14
- attr_reader :names, :labels, :ports
15
-
16
- # @param [String] id
17
- # @param [String] image
18
- # @param [String,Integer] size e.g. "0B (virtual 100MB)"
19
- # @param [String,#map] status e.g. ['Exited', '0', '3 minutes ago']
20
- # @param [String,Array] names list of container names (CSV)
21
- # @param [String,Array] labels list of container labels (CSV)
22
- # @param [String,Array] ports list of exposed ports (CSV)
23
- def initialize(id, image, size, status, names, labels, ports)
24
- if size.is_a?(String) && (m = PS_SIZE.match(size))
25
- scalar, units = m[1], m[2]
26
- scalar = scalar.to_f # lazy: invalid --> 0.0
27
- mult = case units.downcase
28
- when 'b' then 1
29
- when 'kb' then 1_024
30
- when 'mb' then 1_024**2
31
- when 'gb' then 1_024**3
32
- when 'tb' then 1_024**4
33
- else
34
- raise Error.new('Service#new', units, 'Impossibly large unit')
35
- end
36
- size = scalar * mult
37
- end
38
-
39
- if status.is_a?(String)
40
- status = PS_STATUS.match(status)
41
- raise Error.new('Service#new', status, 'Unrecognized status') unless status
42
- end
43
-
44
- names = names.split(',').map{ |x| x.strip } if names.is_a?(String)
45
- labels = labels.split(',').map{ |x| x.strip } if labels.is_a?(String)
46
- ports = ports.split(',').map{ |x| x.strip } if ports.is_a?(String)
47
-
48
- @id = id
49
- @image = image
50
- @size = size
51
- @status = status[1].downcase.to_sym
52
-
53
- @exitstatus = case @status
54
- when :up
55
- nil
56
- else
57
- status[2].to_i
58
- end
59
-
60
- @names = names
61
- @labels = labels
62
- @ports = ports
63
- end
64
-
65
- # static sanity checking ftw!
66
- unless ( initarity = instance_method(:initialize).arity ) == PS_FMT_LEN
67
- raise LoadError.new("#{__FILE__}:#{__LINE__} - arity(\#initialize) != PS_FMT_LEN; #{initarity} != #{PS_FMT_LEN}")
68
- end
69
-
70
- # @return [String]
71
- def name
72
- names.first
73
- end
74
-
75
- # @return [Boolean]
76
- def up?
77
- self.status == :up
78
- end
79
- end
80
- end
@@ -1,26 +0,0 @@
1
- # encoding: utf-8
2
- module Docker::Compose
3
- class Error < RuntimeError
4
- attr_reader :status, :detail
5
-
6
- # @param [String] cmd
7
- # @param [Integer] status
8
- # @param [String] detail
9
- def initialize(cmd, status, detail)
10
- @status = status
11
- @detail = detail
12
-
13
- brief = detail.split(/[\r\n]+/).select { |l| !l.empty? }.first || '(no output)'
14
-
15
- case status
16
- when Numeric
17
- status = status.to_s
18
- else
19
- status = "'#{status}'"
20
- end
21
-
22
- message = format("'%s' failed with status %s: %s", cmd, status, brief)
23
- super(message)
24
- end
25
- end
26
- end
@@ -1,17 +0,0 @@
1
- # encoding: utf-8
2
- module Docker::Compose::ShellPrinter
3
- # Printer that works with the Friendly Interactive Shell (fish).
4
- class Fish < Posix
5
- def eval_output(command)
6
- format('eval (%s)', command)
7
- end
8
-
9
- def export(name, value)
10
- format('set -gx %s %s; ', name, single_quoted_escaped(value))
11
- end
12
-
13
- def unset(name)
14
- format('set -ex %s; ', name)
15
- end
16
- end
17
- end
@@ -1,33 +0,0 @@
1
- # encoding: utf-8
2
- module Docker::Compose::ShellPrinter
3
- # Printer that works with any POSIX-compliant shell e.g. sh, bash, zsh
4
- class Posix
5
- def comment(value)
6
- format('# %s', value)
7
- end
8
-
9
- def eval_output(command)
10
- format('eval "$(%s)"', command)
11
- end
12
-
13
- def export(name, value)
14
- format('export %s=%s', name, single_quoted_escaped(value))
15
- end
16
-
17
- def unset(name)
18
- format('unset %s', name)
19
- end
20
-
21
- protected def single_quoted_escaped(value)
22
- # "escape" any occurrences of ' in value by closing the single-quoted
23
- # string, concatenating a single backslash-escaped ' character, and reopening
24
- # the single-quoted string.
25
- #
26
- # This relies on the shell's willingness to concatenate adjacent string
27
- # literals. Tested under sh, bash and zsh; should work elsewhere.
28
- escaped = value.gsub("'") { "'\\''" }
29
-
30
- "'#{escaped}'"
31
- end
32
- end
33
- end
@@ -1,26 +0,0 @@
1
- # encoding: utf-8
2
- require 'etc'
3
-
4
- module Docker::Compose
5
- module ShellPrinter
6
- def self.new
7
- shell = Etc.getpwuid(Process.uid).shell
8
- basename = File.basename(shell)
9
-
10
- # Crappy titleize (bash -> Bash)
11
- basename[0] = basename[0].upcase
12
-
13
- # Find adapter class named after shell; default to posix if nothing found
14
- klass = begin
15
- const_get(basename.to_sym)
16
- rescue
17
- Posix
18
- end
19
-
20
- klass.new
21
- end
22
- end
23
- end
24
-
25
- require_relative 'shell_printer/posix'
26
- require_relative 'shell_printer/fish'