dockersitter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -0
  3. data/.gitignore +4 -0
  4. data/.rspec +1 -0
  5. data/COPYING.txt +674 -0
  6. data/ChangeLog.rdoc +4 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.rdoc +31 -0
  10. data/Rakefile +36 -0
  11. data/bin/dockersitter +6 -0
  12. data/docker_mgr.gemspec +60 -0
  13. data/gemspec.yml +17 -0
  14. data/lib/command_router.rb +25 -0
  15. data/lib/commands/backup_app.rb +51 -0
  16. data/lib/commands/config.rb +9 -0
  17. data/lib/commands/create.rb +83 -0
  18. data/lib/commands/delete.rb +23 -0
  19. data/lib/commands/generate_backup_scripts.rb +33 -0
  20. data/lib/commands/init.rb +20 -0
  21. data/lib/commands/restore_app.rb +56 -0
  22. data/lib/commands/start.rb +30 -0
  23. data/lib/docker_mgr/version.rb +4 -0
  24. data/lib/docker_mgr.rb +1 -0
  25. data/lib/templates/Dockerfile.erb +5 -0
  26. data/lib/templates/admin/examples/postgres_backup +5 -0
  27. data/lib/templates/admin/examples/postgres_restore +4 -0
  28. data/lib/templates/admin/installation_scripts/install_derby.sh +2 -0
  29. data/lib/templates/admin/installation_scripts/install_git.sh +2 -0
  30. data/lib/templates/admin/installation_scripts/install_glassfish.sh +2 -0
  31. data/lib/templates/admin/installation_scripts/install_java.sh +6 -0
  32. data/lib/templates/admin/installation_scripts/install_node.sh +12 -0
  33. data/lib/templates/admin/installation_scripts/install_rust.sh +3 -0
  34. data/lib/templates/admin/installation_scripts/scriptrunner.sh +19 -0
  35. data/lib/templates/admin/routines/backup_routine +2 -0
  36. data/lib/templates/after_all.erb +3 -0
  37. data/lib/templates/after_all_restore.erb +3 -0
  38. data/lib/templates/backup.erb +8 -0
  39. data/lib/templates/before_all.erb +3 -0
  40. data/lib/templates/before_all_restore.erb +3 -0
  41. data/lib/templates/docker-compose.yml.erb +15 -0
  42. data/lib/templates/restore.erb +3 -0
  43. data/lib/util.rb +120 -0
  44. data/spec/docker_mgr_spec.rb +8 -0
  45. data/spec/spec_helper.rb +4 -0
  46. metadata +176 -0
@@ -0,0 +1,56 @@
1
+ require 'fileutils'
2
+ require 'open3'
3
+ require 'thor/group'
4
+ require 'thor'
5
+
6
+
7
+ class RestoreApp < Thor::Group
8
+ include Thor::Actions
9
+ include DockerMgr::Util
10
+
11
+
12
+ argument :app_name
13
+ def restore
14
+
15
+ app_backup_dir = "#{backup_dir}/#{app_name}"
16
+ app_backup_tmp_dir = "#{app_backup_dir}/tmp"
17
+
18
+ reverse_sorted_entries = Dir.entries(app_backup_dir)
19
+ .select {|entry| entry != "." && entry != ".."}
20
+ .sort {| a,b | extract_date(b) <=> extract_date(a)}
21
+
22
+ puts "please select your backup"
23
+ reverse_sorted_entries.each_with_index{|entry,i| puts "#{i+1} #{entry}"}
24
+ puts "(a) abort"
25
+
26
+ choice = STDIN.gets.chomp
27
+
28
+ abort "aborting" unless %w(1 2 3).include? choice
29
+
30
+ chosen_backup = reverse_sorted_entries[choice.to_i - 1]
31
+
32
+ choice = yes?("do you want to restore the data-container back to #{Time.at(extract_date(chosen_backup).to_i).strftime '%d.%m.%Y-%H:%M:%S'} (y,N)")
33
+
34
+ abort "aborting" unless choice
35
+
36
+ puts `tar -zxf #{app_backup_dir}/#{chosen_backup} -C #{app_backup_dir}`
37
+
38
+ FileUtils.cd "#{apps_dir}/#{@app_name}" do
39
+ puts 'executing before_all hook'
40
+ puts exec_hook(@app_name,"restore","before_all")
41
+ service_hooks_for(@app_name,"restore").each do |service|
42
+ script_path = "#{apps_dir}/#{@app_name}/administration/hooks/restore.d/#{service}"
43
+ puts "executing #{script_path}"
44
+ Open3.popen3("#{script_path} #{app_backup_tmp_dir}/#{service} 2>&1") do |i,o,e,th|
45
+ while line = o.gets do
46
+ puts line
47
+ end
48
+ end
49
+ end
50
+ puts 'executing after_all hook'
51
+ puts exec_hook(@app_name,"restore","after_all")
52
+ end
53
+
54
+ `sudo rm -rf #{app_backup_tmp_dir}`
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ require 'util'
2
+ require 'fileutils'
3
+ require 'thor/group'
4
+
5
+ class Start < Thor::Group
6
+ include DockerMgr::Util
7
+
8
+ argument :app_name,
9
+ :required => false
10
+
11
+ def start
12
+ if @app_name
13
+ start_app(@app_name)
14
+ else
15
+ Dir.entries(apps_dir)
16
+ .select{|e| e != '.' && e != '..'}
17
+ .each(&method(:start_app))
18
+ end
19
+ end
20
+
21
+ no_tasks do
22
+ def start_app(app_name)
23
+ app_path = "#{apps_dir}/#{app_name}"
24
+ FileUtils.cd app_path do
25
+ puts `sudo docker-compose up -d`
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,4 @@
1
+ module DockerMgr
2
+ # docker_mgr version
3
+ VERSION = "0.1.0"
4
+ end
data/lib/docker_mgr.rb ADDED
@@ -0,0 +1 @@
1
+ require 'docker_mgr/version'
@@ -0,0 +1,5 @@
1
+ FROM <%=options[:image]%>
2
+ MAINTAINER <%=@user_name%> <<%=@user_email%>>
3
+ ADD administration/installation /tmp/installation_scripts
4
+ ADD administration/scriptrunner.sh /tmp/scriptrunner.sh
5
+ RUN chmod +x /tmp/scriptrunner.sh && /bin/bash /tmp/scriptrunner.sh /tmp/installation_scripts
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ PGPASSWORD="mysecretpassword"
4
+
5
+ sudo docker run -it --link postgrestest_app_1:postgres --rm -e PGPASSWORD=$PGPASSWORD postgres /bin/bash -c 'pg_dumpall -h "$POSTGRES_PORT_5432_TCP_ADDR" -p "$POSTGRES_PORT_5432_TCP_PORT" -U postgres' > $1/backup.sql
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+
3
+ PGPASSWORD="mysecretpassword"
4
+ sudo docker run -it --link postgrestest_app_1:postgres --rm -e PGPASSWORD=$PGPASSWORD -v $1:/backup postgres /bin/bash -c 'psql -h "$POSTGRES_PORT_5432_TCP_ADDR" -p "$POSTGRES_PORT_5432_TCP_PORT" -U postgres -f /backup/backup.sql'
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ mkdir /opt/derby && wget -P /tmp http://psg.mtu.edu/pub/apache//db/derby/db-derby-10.10.2.0/db-derby-10.10.2.0-bin.zip && unzip /tmp/db-derby-10.10.2.0-bin.zip -d /opt/derby && rm /tmp/db-derby-10.10.2.0-bin.zip
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ sudo apt-get install -y git
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ mkdir /opt/glassfish && wget -P /tmp http://download.java.net/glassfish/4.0/release/glassfish-4.0.zip && unzip /tmp/glassfish-4.0.zip -d /opt/glassfish && rm /tmp/glassfish-4.0.zip
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+ apt-get install -y software-properties-common
3
+ add-apt-repository -y ppa:webupd8team/java
4
+ apt-get update
5
+ echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections
6
+ apt-get install -y oracle-java8-installer unzip
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+ VERSION="v0.11.16"
3
+ git clone https://github.com/creationix/nvm.git /.nvm
4
+ echo ". /.nvm/nvm.sh" >> /etc/bash.bashrc
5
+ /bin/bash -c ". /.nvm/nvm.sh && nvm install $VERSION && nvm use $VERSION && nvm alias default $VERSION && ln -s /.nvm/$VERSION/bin/node /usr/bin/node && ln -s /.nvm/$VERSION/bin/npm /usr/bin/npm"
6
+ groupadd nodegrp
7
+ usermod -a -G nodegrp developer
8
+
9
+ chgrp -R nodegrp /.nvm/$VERSION/*
10
+ chmod -R 775 /.nvm/$VERSION/*
11
+ chgrp nodegrp `which npm`
12
+ chgrp nodegrp `which node`
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ curl -sf -L https://static.rust-lang.org/rustup.sh | sh
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+ if [ $# == 0 ] || [ $# -gt 1 ]; then
4
+ echo wrong number of arguments;
5
+ exit 1;
6
+ fi
7
+
8
+ DIR=$1
9
+
10
+ LAST_CHAR=${DIR: -1}
11
+
12
+ if [ $LAST_CHAR != "/" ]; then
13
+ DIR=$DIR/;
14
+ fi
15
+
16
+ for f in $DIR*
17
+ do
18
+ sh $f;
19
+ done
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ sudo docker-compose up -d
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ sudo docker-compose up -d
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ <%volumes(app_name,@service).each_with_index do |v,i|%>
4
+ mkdir -p $1/<%="#{@service}/#{@service}_#{i}"%>
5
+ <%end%>
6
+
7
+ data_container_id=$(sudo docker-compose ps -q <%=@service%>)
8
+ sudo docker run --rm --volumes-from $data_container_id <%=volumes(app_name,@service).each_with_index.map{|volume,i|"-v $1/#{@service}/#{@service}_#{i}:/backup/#{@service}_#{i}"}.join(" ")%> ubuntu:14.04 /bin/bash -c '<%=volumes(app_name,@service).each_with_index.map{|v,i|"echo \"/#{v}\" > /backup/#{@service}_#{i}/backup.descriptor && tar -czpf /backup/#{@service}_#{i}/#{@service}_#{i}.tar.gz /#{v}"}.join(" && ")%>'
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ sudo docker-compose stop <%=services(@app_name).join(" ")%> && echo y | sudo docker-compose rm <%=services(@app_name).join(" ")%>
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ sudo docker-compose stop && echo y | sudo docker-compose rm
@@ -0,0 +1,15 @@
1
+ app:
2
+ <%if options[:image]%>image: <%=options[:image]%> <%else%>build: .<%end%>
3
+ volumes_from:
4
+ - appdata
5
+ environment:
6
+ VIRTUAL_HOST: <%=@app_name%>.rene-richter.de
7
+ <%options[:env].each do | var |%><%=var.gsub('=',': ')%>
8
+ <%end%>
9
+ appdata:
10
+ <%if options[:image]%>image: <%=options[:image]%> <%else%>build: .<%end%>
11
+ volumes:
12
+ <%options[:volumes].each do |volume|-%>
13
+ - <%=volume%>
14
+ <%end-%>
15
+ command: /bin/echo datacontainer
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ data_container_id=$(sudo docker-compose ps -q <%=@service%>)
3
+ sudo docker run --rm --volumes-from $data_container_id <%=volumes(@app_name,@service).each_with_index.map{|volume,i|"-v $1/#{@service}/#{@service}_#{i}:/backup/#{@service}_#{i}"}.join(" ")%> ubuntu:14.04 /bin/bash -c '<%=volumes(@app_name,@service).each_with_index.map{|v,i|"tar -zxf /backup/#{@service}_#{i}/#{@service}_#{i}.tar.gz -C /backup/#{@service}_#{i}; DIR=$(cat /backup/#{@service}_#{i}/backup.descriptor); rm -rf $DIR/* && cp -r /backup/#{@service}_#{i}/$DIR/* $DIR"}.join(" && ")%>'
data/lib/util.rb ADDED
@@ -0,0 +1,120 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ module DockerMgr
5
+
6
+ module Util
7
+
8
+ def root_dir
9
+ return @root_dir if @root_dir
10
+ error_message = "not within project tree"
11
+ curr_dir = Dir.pwd
12
+ return curr_dir if curr_dir.end_with? "/docker"
13
+ dir_parts = curr_dir.split "/docker/"
14
+ raise error_message if dir_parts == 1
15
+ possible_root = "#{dir_parts[0]}/docker"
16
+ raise error_message unless (Dir.entries(possible_root) && %w{admin backup apps}).length == 3
17
+ @root_dir = possible_root
18
+ end
19
+
20
+
21
+ def backup_dir
22
+ "#{root_dir}/backup"
23
+ end
24
+
25
+ def base_images_dir
26
+ "#{root_dir}/base_images"
27
+ end
28
+
29
+
30
+ def attic_dir
31
+ "#{root_dir}/attic"
32
+ end
33
+
34
+ def apps_dir
35
+ "#{root_dir}/apps"
36
+ end
37
+
38
+ def admin_dir
39
+ "#{root_dir}/admin"
40
+ end
41
+
42
+ def routine_dir
43
+ "#{admin_dir}/routines"
44
+ end
45
+
46
+ def install_dir
47
+ "#{admin_dir}/installation_scripts"
48
+ end
49
+
50
+ def extract_date(entry)
51
+ /_\d+\./.match(entry).to_s.chop[1..-1].to_i
52
+ end
53
+
54
+ def service_hooks_for(app_name,type)
55
+ Dir.entries("#{apps_dir}/#{app_name}/administration/hooks/#{type}.d")
56
+ .select {| entry | !entry.start_with?(".") && entry != "before_all" && entry != "after_all" }
57
+ end
58
+
59
+ def services(app_name)
60
+ YAML.load(File.read("#{apps_dir}/#{app_name}/docker-compose.yml"))
61
+ .each_key
62
+ .select {|k| !k.end_with?("data")}
63
+ end
64
+
65
+ def data_services(app_name)
66
+ YAML.load(File.read("#{apps_dir}/#{app_name}/docker-compose.yml")).each_key
67
+ .select {|k| k.end_with?("data")}
68
+ end
69
+
70
+ def volumes(app_name,service_name)
71
+ raw_volumes = YAML.load(File.read("#{apps_dir}/#{app_name}/docker-compose.yml"))["#{service_name}"]['volumes']
72
+ raw_volumes.map do |volume|
73
+ volume.chop! if volume.end_with? '/'
74
+ volume[0] = '' if volume.start_with? '/'
75
+ volume
76
+ end
77
+ end
78
+
79
+ def exec_hook(app_name,type,hook_name)
80
+ if File.exist? "#{apps_dir}/#{app_name}/administration/hooks/#{type}.d/#{hook_name}"
81
+ `#{apps_dir}/#{app_name}/administration/hooks/#{type}.d/#{hook_name}`
82
+ end
83
+ end
84
+
85
+
86
+ def remove_line_from_routine(routine,filter_line)
87
+ File.open "#{routine_dir}/tmp","w" do | output_file |
88
+ File.foreach "#{routine_dir}/#{routine}" do | line |
89
+ output_file.write line unless line == "#{filter_line}\n"
90
+ end
91
+ end
92
+ FileUtils.mv "#{routine_dir}/tmp", "#{routine_dir}/#{routine}"
93
+ end
94
+
95
+ def add_line_to_routine(routine,line)
96
+ File.open("#{routine_dir}/#{routine}",'a') { | file | file.write("#{line}\n") }
97
+ end
98
+
99
+
100
+ def extract_git_variable(name)
101
+ config = `git config --list`
102
+ result = config.lines.grep(/#{Regexp.quote(name)}/).map{|e| e.split('=')[1].chomp }.first
103
+ unless result
104
+ puts "please enter your #{name.split('.')[1]}"
105
+ result = STDIN.gets.chomp
106
+ `git config --global #{name} #{result}`
107
+ end
108
+ result
109
+ end
110
+
111
+ def extract_email
112
+ extract_git_variable("user.email")
113
+ end
114
+
115
+ def extract_name
116
+ extract_git_variable("user.name")
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'docker_mgr'
3
+
4
+ describe DockerMgr do
5
+ it "should have a VERSION constant" do
6
+ subject.const_get('VERSION').should_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require 'rspec'
2
+ require 'docker_mgr/version'
3
+
4
+ include DockerMgr
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dockersitter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rene Richter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-17 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.19'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.19'
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.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.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: '2.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubygems-tasks
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.2'
97
+ description: generates scripts and projectstructure for docker
98
+ email: Richterrettich@gmail.com
99
+ executables:
100
+ - dockersitter
101
+ extensions: []
102
+ extra_rdoc_files:
103
+ - COPYING.txt
104
+ - ChangeLog.rdoc
105
+ - LICENSE.txt
106
+ - README.rdoc
107
+ files:
108
+ - ".document"
109
+ - ".gitignore"
110
+ - ".rspec"
111
+ - COPYING.txt
112
+ - ChangeLog.rdoc
113
+ - Gemfile
114
+ - LICENSE.txt
115
+ - README.rdoc
116
+ - Rakefile
117
+ - bin/dockersitter
118
+ - docker_mgr.gemspec
119
+ - gemspec.yml
120
+ - lib/command_router.rb
121
+ - lib/commands/backup_app.rb
122
+ - lib/commands/config.rb
123
+ - lib/commands/create.rb
124
+ - lib/commands/delete.rb
125
+ - lib/commands/generate_backup_scripts.rb
126
+ - lib/commands/init.rb
127
+ - lib/commands/restore_app.rb
128
+ - lib/commands/start.rb
129
+ - lib/docker_mgr.rb
130
+ - lib/docker_mgr/version.rb
131
+ - lib/templates/Dockerfile.erb
132
+ - lib/templates/admin/examples/postgres_backup
133
+ - lib/templates/admin/examples/postgres_restore
134
+ - lib/templates/admin/installation_scripts/install_derby.sh
135
+ - lib/templates/admin/installation_scripts/install_git.sh
136
+ - lib/templates/admin/installation_scripts/install_glassfish.sh
137
+ - lib/templates/admin/installation_scripts/install_java.sh
138
+ - lib/templates/admin/installation_scripts/install_node.sh
139
+ - lib/templates/admin/installation_scripts/install_rust.sh
140
+ - lib/templates/admin/installation_scripts/scriptrunner.sh
141
+ - lib/templates/admin/routines/backup_routine
142
+ - lib/templates/after_all.erb
143
+ - lib/templates/after_all_restore.erb
144
+ - lib/templates/backup.erb
145
+ - lib/templates/before_all.erb
146
+ - lib/templates/before_all_restore.erb
147
+ - lib/templates/docker-compose.yml.erb
148
+ - lib/templates/restore.erb
149
+ - lib/util.rb
150
+ - spec/docker_mgr_spec.rb
151
+ - spec/spec_helper.rb
152
+ homepage: https://rubygems.org/gems/docker-mgr
153
+ licenses:
154
+ - GPL
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.4.8
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: a management tool for docker
176
+ test_files: []