machine_configure 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1ccd8db9cf4911d7c39b8a1a60e2aef90f38798437bcb1e6f463c9977fcc369f
4
+ data.tar.gz: 073d2219e21ec95877111f79cc430da279dd1fb8551e94f82f072d3478fc8d6e
5
+ SHA512:
6
+ metadata.gz: 6a9ef77ddcef9d115d2e796fe0493a4f1623ac65adb5e46dac1f7f138a23bc5c8b24e66060c682041b11414d3aea6236d8eff298b89b66332dbf5ec5dc82fc9a
7
+ data.tar.gz: 3ea90114839e8c543de8ee78678a0e97d8e8e3384cdc5970ea636a1ad90888775b52c8d46a588b791c48cd4214374c48657e0af51d0b885cf0abd0ce1335af3c
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ .byebug_history
11
+ .virb/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.2
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gem 'argument_parser', git: 'https://github.com/Noah2610/ArgumentParser'
6
+
7
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,33 @@
1
+ GIT
2
+ remote: https://github.com/Noah2610/ArgumentParser
3
+ revision: 3ac5b0083fd42ae8740d1d22d988f56c74a29470
4
+ specs:
5
+ argument_parser (1.0.0)
6
+
7
+ PATH
8
+ remote: .
9
+ specs:
10
+ machine_configure (0.0.1)
11
+ rubyzip (~> 1.2)
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ minitest (5.11.3)
17
+ rake (10.5.0)
18
+ rdoc (6.0.4)
19
+ rubyzip (1.2.1)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ argument_parser!
26
+ bundler (~> 1.16)
27
+ machine_configure!
28
+ minitest (~> 5.0)
29
+ rake (~> 10.0)
30
+ rdoc
31
+
32
+ BUNDLED WITH
33
+ 1.16.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Noah Rosenzweig
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # MachineConfigure
2
+ Export or import your docker-machine's configuration files,
3
+ and share them with others.
4
+
5
+ ## Table of Contents _(Generated)_
6
+ - [Description](#description)
7
+ - [Installation](#installation)
8
+ - [Install manually](#install-manually)
9
+ - [Usage](#usage)
10
+ - [Exporting](#exporting)
11
+ - [Example](#example)
12
+ - [Importing](#importing)
13
+ - [Example](#example)
14
+ - [TODO](#todo)
15
+ - [License](#license)
16
+
17
+ ## Description
18
+ This gem is heavily inspired by [machine-share][machine-share-site],
19
+ a nodejs package, which does basically the same thing as this gem.
20
+ I just had some minor inconveniences with it,
21
+ so I decided I would write my own version in Ruby.
22
+
23
+ If you use [docker-machine][docker-machine-site], this might come in handy,
24
+ if you ever want to share your remote docker-machine instance
25
+ with a collegue or across multiple machines.
26
+
27
+ With this gem, you can export your configuration files for a specific
28
+ docker-machine instance into a zip file. You can then share the created
29
+ zip file with others / other computers and import it again.
30
+
31
+ ## Installation
32
+ __ATTENTION:
33
+ The gem is not yet available on rubygems.org, this is still a todo.__
34
+
35
+ Install from [rubygems][rubygems-site] with ...
36
+
37
+ ```
38
+ $ gem install machine_configure
39
+ ```
40
+
41
+ ### Install manually
42
+ If you want to install the gem manually from this repository directly,
43
+ you'll need to:
44
+
45
+ - Clone the repository,
46
+ - install all dependencies,
47
+ - build the gem, and
48
+ - install it.
49
+
50
+ Here's a command to copy/paste, it does all of the above ...
51
+
52
+ ```
53
+ git clone https://github.com/Noah2610/MachineConfigure.git && \
54
+ cd MachineConfigure && \
55
+ bundle install --with development && \
56
+ rake build && \
57
+ gems=($(ls pkg/machine_configure-*)); \
58
+ gem install "${gems[-1]}"; \
59
+ unset gems
60
+ ```
61
+
62
+ Once this completes, you should have the gem installed.
63
+ You can try using it by executing `maccon`.
64
+
65
+ ## Usage
66
+ ### Exporting
67
+ To export the configurations of an existing docker-machine instance,
68
+ use the `export` keyword, pass the machine's name,
69
+ and optionally, the output zip file.
70
+ #### Example
71
+
72
+ ```
73
+ $ maccon export my_machine # Creates my_machine.zip
74
+ $ maccon export my_machine my_machine_configs.zip # Creates my_machine_configs.zip
75
+ ```
76
+
77
+ ### Importing
78
+ To import a docker-machine's configuration files from a zip file,
79
+ use the `import` keyword, and pass the target zip file.
80
+ #### Example
81
+
82
+ ```
83
+ $ maccon import my_machine_configs.zip
84
+ ```
85
+
86
+ For all command-line options, see `--help`.
87
+
88
+ ---
89
+
90
+ ## TODO
91
+ See the [Trello Board][trello-site] for open tasks.
92
+
93
+ ## License
94
+ The gem is available as open source under the terms of the [MIT License][mit-site].
95
+
96
+ [machine-share-site]: https://github.com/bhurlow/machine-share
97
+ [docker-machine-site]: https://docs.docker.com/machine
98
+ [rubygems-site]: https://rubygems.org/gems/machine_configure
99
+ [trello-site]: https://trello.com/b/ZVdArdrk
100
+ [mit-site]: https://opensource.org/licenses/MIT
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'machine_configure'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/rdoc ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ # Generate rdoc documentation
3
+
4
+ root="$( dirname "$0" )/.."
5
+ dir="${root}/lib"
6
+ main="${dir}/machine_configure.rb"
7
+ output_dir="${root}/doc"
8
+
9
+ rdoc --main "$main" --op "$output_dir" $dir
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/maccon ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'machine_configure'
5
+
6
+ cli = MachineConfigure::CLI.new
7
+ cli.run
@@ -0,0 +1,92 @@
1
+ module MachineConfigure
2
+ class CLI
3
+ include Helpers::Message
4
+ include CLIConstants
5
+
6
+ def initialize
7
+ Helpers::Message.error_no_stack_trace!
8
+ @arguments = nil
9
+ end
10
+
11
+ # Parses given command-line arguments,
12
+ # and sets proper settings.
13
+ def run
14
+ @arguments = ArgumentParser.get_arguments VALID_ARGUMENTS
15
+ print_help if (@arguments[:options][:help])
16
+ print_version if (@arguments[:options][:version])
17
+ if (@arguments[:keywords][:export])
18
+ handle_export
19
+ elsif (@arguments[:keywords][:import])
20
+ handle_import
21
+ else
22
+ print_help
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def print_help
29
+ puts HELP_TEXT
30
+ exit
31
+ end
32
+
33
+ def print_version
34
+ puts "#{GEM_NAME} v#{VERSION}"
35
+ exit
36
+ end
37
+
38
+ def handle_export
39
+ verify_keywords_export
40
+ dm_name = @arguments[:keywords][:export][1]
41
+ zipfile = @arguments[:keywords][:export][2]
42
+ zipfile = get_zipfile_from_name dm_name unless (zipfile)
43
+ exporter = Exporter.new dm_name
44
+ exporter.export_to zipfile
45
+ end
46
+
47
+ def handle_import
48
+ verify_keywords_import
49
+ zipfile = @arguments[:keywords][:import][1]
50
+ importer = Importer.new
51
+ importer.import_from zipfile
52
+ end
53
+
54
+ def verify_options *option_names
55
+ option_names.flatten.each do |option_name|
56
+ error(
57
+ "Option `--#{VALID_ARGUMENTS[:double][option_name].first.first}' must be given."
58
+ ) unless (@arguments[:options][option_name])
59
+ end
60
+ end
61
+
62
+ def verify_keywords_export
63
+ error(
64
+ "Missing argument DOCKER_MACHINE_NAME for #{VALID_ARGUMENTS[:keywords][:export].first.first}."
65
+ ) unless (@arguments[:keywords][:export][1])
66
+ verify_keyword_size_for :export
67
+ end
68
+
69
+ def verify_keywords_import
70
+ error(
71
+ "Missing argument ZIP_FILE for #{VALID_ARGUMENTS[:keywords][:import].first.first}.",
72
+ "See --help for more information."
73
+ ) unless (@arguments[:keywords][:import][1])
74
+ verify_keyword_size_for :import
75
+ end
76
+
77
+ def verify_keyword_size_for keyword_name
78
+ target_size = VALID_ARGUMENTS[:keywords][keyword_name].size
79
+ given_size = @arguments[:keywords][keyword_name].size
80
+ if (given_size > target_size)
81
+ extra_arguments = @arguments[:keywords][keyword_name][target_size .. -1]
82
+ error(
83
+ "Invalid argument#{extra_arguments.size > 1 ? ?s : ''} `#{extra_arguments.join(', ')}'"
84
+ )
85
+ end
86
+ end
87
+
88
+ def get_zipfile_from_name dm_name
89
+ return "#{dm_name}.zip"
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,81 @@
1
+ module MachineConfigure
2
+ module CLIConstants
3
+ # Hash containing all valid arguments,
4
+ # which may be passed to the CLI.
5
+ VALID_ARGUMENTS = {
6
+ single: {
7
+ help: [
8
+ [?h],
9
+ false
10
+ ],
11
+ version: [
12
+ [?v],
13
+ false
14
+ ],
15
+ name: [
16
+ [?n],
17
+ true
18
+ ],
19
+ zipfile: [
20
+ [?z],
21
+ true
22
+ ]
23
+ },
24
+ double: {
25
+ help: [
26
+ ['help'],
27
+ false
28
+ ],
29
+ version: [
30
+ ['version'],
31
+ false
32
+ ],
33
+ name: [
34
+ ['name'],
35
+ true
36
+ ],
37
+ zipfile: [
38
+ ['zip'],
39
+ true
40
+ ]
41
+ },
42
+ keywords: {
43
+ export: [['export', ?e], :INPUT, :INPUTS],
44
+ import: [['import', ?i], :INPUTS]
45
+ }
46
+ }
47
+
48
+ # The <tt>--help</tt> text.
49
+ cli_name = DIR[:caller].basename
50
+ HELP_TEXT = <<-END_HELP_TEXT
51
+ USAGE
52
+ $ #{cli_name} [--help|--version]
53
+ $ #{cli_name} export DOCKER_MACHINE_NAME [ZIP_FILE]
54
+ $ #{cli_name} import ZIP_FILE
55
+
56
+ KEYWORDS
57
+ export
58
+ Export existing configuration files from
59
+ the docker-machine instance DOCKER_MACHINE_NAME.
60
+ Optionally, add a ZIP_FILE name.
61
+ import
62
+ Import an exported ZIP_FILE.
63
+
64
+ OPTIONS
65
+ --help -h
66
+ Print this text and exit.
67
+ --version -v
68
+ Print the current version number and exit.
69
+
70
+ EXAMPLES
71
+ Export an existing docker-machine instance with the name "my_machine",
72
+ to a new zip file "my_machine.zip":
73
+ $ #{cli_name} export my_machine
74
+ The same as above, but specify the output zip file name:
75
+ $ #{cli_name} export my_machine my_machine_configs
76
+ Import a new docker-machine instance from
77
+ the zip file "my_machine_configs.zip":
78
+ $ #{cli_name} import my_machine_configs.zip
79
+ END_HELP_TEXT
80
+ end
81
+ end
@@ -0,0 +1,84 @@
1
+ module MachineConfigure
2
+ # The Exporter is responsible for finding all
3
+ # necessary certificates for a given docker-machine,
4
+ # and bundle them all into a single zip archive.
5
+ class Exporter
6
+ include Helpers::Message
7
+ include Helpers::Shared
8
+
9
+ # Initialize with a docker-machine <tt>name</tt>.
10
+ def initialize name
11
+ @machine_name = name
12
+ VALIDATOR.validate_machine_name name
13
+ @dir = {
14
+ machine: DM_MACHINES_PATH.join(@machine_name),
15
+ certs: DM_CERTS_PATH.join(@machine_name)
16
+ }
17
+ VALIDATOR.validate_directories @dir[:machine]
18
+ @contents = nil
19
+ end
20
+
21
+ # Export certificates for the machine.
22
+ def export_to zip_file
23
+ zip_file = Pathname.new "#{zip_file.to_s}#{zip_file.to_s.match(/\.zip\z/i) ? '' : '.zip'}"
24
+ VALIDATOR.validate_zip_file_export zip_file
25
+ files = get_files
26
+ @contents = get_contents_from_files(*files)
27
+ config_json_path = get_config_json_path
28
+ @contents[config_json_path] = remove_home_in @contents[config_json_path]
29
+ @contents[MACHINE_NAME_FILENAME] = @machine_name
30
+ write_zip_file_to zip_file
31
+ message(
32
+ "Successfully created zip archive",
33
+ " `#{zip_file.to_s}'",
34
+ "with the keys and certificates from docker-machine",
35
+ " `#{@machine_name}'"
36
+ )
37
+ end
38
+
39
+ private
40
+
41
+ # Returns all necessary filepaths.
42
+ def get_files
43
+ return @dir.values.map do |dir|
44
+ next get_files_recursively_from dir
45
+ end .reject { |x| !x } .flatten
46
+ end
47
+
48
+ # Returns all filepaths from <tt>directory</tt>, recursively.
49
+ def get_files_recursively_from directory
50
+ dir = Pathname.new directory
51
+ return nil unless (dir.directory?)
52
+ return dir.each_child.map do |file|
53
+ next file.realpath.to_path if (file.file?)
54
+ next get_files_recursively_from file if (file.directory?)
55
+ end .flatten
56
+ end
57
+
58
+ # Reads contents from <tt>files</tt>, and returns
59
+ # a Hash with the file's filepath as the key and
60
+ # the file's content as the value.
61
+ # The filepath key is the absolute path to the file,
62
+ # but _without_ the DM_STORAGE_PATH.
63
+ def get_contents_from_files *files
64
+ return files.flatten.map do |filename|
65
+ file = Pathname.new filename
66
+ content = file.read
67
+ path = remove_storage_path_from file.to_path
68
+ next [path, content]
69
+ end .to_h
70
+ end
71
+
72
+ # Write the processed <tt>@contents</tt> to
73
+ # a zip archive using Zip.
74
+ def write_zip_file_to zip_file
75
+ Zip::File.open(zip_file, Zip::File::CREATE) do |zip|
76
+ @contents.each do |filepath, content|
77
+ zip.get_output_stream(filepath) do |zipfile|
78
+ zipfile.write content
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,77 @@
1
+ module MachineConfigure
2
+ module Helpers
3
+ # This helper provides useful error methods,
4
+ # which may abort the script with an error message
5
+ # and a stack traceback.
6
+ module Message
7
+ MESSAGE_PADDING = ' '
8
+ STACK_TRACE_SIZE = 20
9
+ STACK_TRACE_PADDING = 1
10
+ @@_error_stack_trace = true
11
+
12
+ class << self
13
+ # Disable stack trace output on #error method.
14
+ def error_no_stack_trace!
15
+ @@_error_stack_trace = false
16
+ end
17
+
18
+ # Enable stack trace output on #error method,
19
+ # if it was disabled.
20
+ def error_yes_stack_trace!
21
+ @@_error_stack_trace = true
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def error *messages #:doc:
28
+ message = messages.flatten.join(?\n).gsub(/^/, MESSAGE_PADDING)
29
+ output = [
30
+ "#{_get_message_header} ERROR:",
31
+ message,
32
+ "#{MESSAGE_PADDING}Exiting."
33
+ ]
34
+ if (@@_error_stack_trace)
35
+ stack_trace = caller[STACK_TRACE_PADDING ... (STACK_TRACE_SIZE + STACK_TRACE_PADDING)].map do |line|
36
+ next "#{MESSAGE_PADDING}#{line}"
37
+ end .reverse
38
+ output.unshift([
39
+ "Stack traceback (most recent call last):",
40
+ stack_trace,
41
+ ])
42
+ end
43
+ abort output.flatten.join(?\n)
44
+ end
45
+
46
+ def warning *messages #:doc:
47
+ message = _get_warning_message_from(*messages.flatten)
48
+ puts message
49
+ end
50
+
51
+ def warning_print *messages #:doc:
52
+ message = _get_warning_message_from(*messages.flatten)
53
+ print message
54
+ end
55
+
56
+ def message *messages #:doc:
57
+ message = messages.flatten.join(?\n).gsub(/^/, MESSAGE_PADDING)
58
+ puts([
59
+ "#{_get_message_header}",
60
+ message
61
+ ].flatten.join(?\n))
62
+ end
63
+
64
+ def _get_warning_message_from *messages
65
+ message = messages.flatten.join(?\n).gsub(/^/, MESSAGE_PADDING)
66
+ return [
67
+ "#{_get_message_header} WARNING:",
68
+ message
69
+ ].flatten.join(?\n)
70
+ end
71
+
72
+ def _get_message_header
73
+ return DIR[:caller].basename
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,31 @@
1
+ module MachineConfigure
2
+ module Helpers
3
+ module Shared
4
+ private
5
+
6
+ # Returns the path to the docker-machine's <tt>'config.json'</tt> file,
7
+ # but without the prefixed path leading to the DM_MACHINES_PATH.
8
+ def get_config_json_path
9
+ return nil unless (@machine_name)
10
+ config_json_path = remove_storage_path_from DM_MACHINES_PATH.join(@machine_name, 'config.json').to_path
11
+ end
12
+
13
+ # Returns the <tt>filepath</tt> without DM_STORAGE_PATH.
14
+ def remove_storage_path_from filepath
15
+ return filepath.to_s.sub("#{DM_STORAGE_PATH.to_path}/", '')
16
+ end
17
+
18
+ # Replaces any occurences of the user's
19
+ # home directory path with HOME_REPLACE_STRING.
20
+ def remove_home_in string
21
+ return string.gsub(HOME, HOME_REPLACE_STRING)
22
+ end
23
+
24
+ # Replaces any occurences of HOME_REPLACE_STRING
25
+ # with the user's home directory path.
26
+ def insert_home_in string
27
+ return string.gsub(HOME_REPLACE_STRING, HOME)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ module MachineConfigure
2
+ # The Importer takes a zip file,
3
+ # processes the files,
4
+ # and extracts them into the proper directories.
5
+ class Importer
6
+ include Helpers::Message
7
+ include Helpers::Shared
8
+
9
+ def initialize
10
+ @contents = nil
11
+ @machine_name = nil
12
+ end
13
+
14
+ # Import given <tt>zip_file</tt>
15
+ # as a new docker-machine.
16
+ def import_from zip_file
17
+ VALIDATOR.validate_zip_file_import zip_file
18
+ @contents = get_contents_from_zip zip_file
19
+ @machine_name = @contents[MACHINE_NAME_FILENAME]
20
+ @contents.delete MACHINE_NAME_FILENAME
21
+ @dir = {
22
+ machine: DM_MACHINES_PATH.join(@machine_name),
23
+ certs: DM_CERTS_PATH.join(@machine_name)
24
+ }
25
+ #VALIDATOR.validate_directories_dont_exist *@dir.values
26
+ VALIDATOR.validate_no_machine_name @machine_name
27
+ config_json_path = get_config_json_path
28
+ @contents[config_json_path] = insert_home_in @contents[config_json_path]
29
+ write_contents
30
+ # Finally, check that the newly imported machine is recognized by docker-machine.
31
+ VALIDATOR.validate_machine_name @machine_name
32
+ message(
33
+ "Successfully imported docker-machine configuration files from archive",
34
+ " `#{zip_file.to_s}'",
35
+ "for docker-machine",
36
+ " `#{@machine_name}'"
37
+ )
38
+ end
39
+
40
+ private
41
+
42
+ # Reads the <tt>zip_file</tt> and returns
43
+ # a Hash with each file's path as the key
44
+ # and the file's content as the value.
45
+ def get_contents_from_zip zip_file
46
+ contents = {}
47
+ Zip::File.open(zip_file) do |zip|
48
+ zip.each_entry do |entry|
49
+ entry.get_input_stream do |entryfile|
50
+ contents[entry.name] = entryfile.read
51
+ end
52
+ end
53
+ end
54
+ return contents
55
+ end
56
+
57
+ # Write <tt>@contents</tt> to proper paths.
58
+ def write_contents
59
+ @contents.each do |relative_filepath, content|
60
+ filepath = DM_STORAGE_PATH.join relative_filepath
61
+ filedir = filepath.dirname
62
+ filedir.mkpath unless (filedir.directory?)
63
+ permission = 0644
64
+ permission = 0600 if (filepath.basename.to_path == 'id_rsa')
65
+ file = File.open filepath.to_path, ?w, permission
66
+ file.write content
67
+ file.close
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,4 @@
1
+ module MachineConfigure
2
+ GEM_NAME = 'machine_configure'
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,220 @@
1
+ module MachineConfigure
2
+ # This class should validate that dependencies are installed (docker-machine).
3
+ class Validator
4
+ include Helpers::Message
5
+ # Default command-line apps,
6
+ # which need to be available.
7
+ BASE_APPS = [
8
+ 'docker-machine'
9
+ ]
10
+
11
+ def initialize
12
+ @validated_apps = []
13
+ end
14
+
15
+ # Calls #validate_apps for BASE_APPS.
16
+ def validate_base_apps
17
+ validate_apps *BASE_APPS
18
+ end
19
+
20
+ # Check if given <tt>apps</tt> (or default BASE_APPS),
21
+ # are available from the command-line.
22
+ # Throw an error and exit if any aren't available.
23
+ def validate_apps *apps
24
+ apps.flatten.each do |appname|
25
+ validate_app appname
26
+ end
27
+ end
28
+
29
+ # Check that the given <tt>name</tt>
30
+ # exists for docker-machine.
31
+ def validate_machine_name name
32
+ validate_app 'docker-machine'
33
+ error(
34
+ "Docker machine `#{name}' is not available."
35
+ ) unless (docker_machine_exists? name)
36
+ end
37
+
38
+ # Check that the given <tt>name</tt>
39
+ # does _not_ exist for docker-machine.
40
+ def validate_no_machine_name name
41
+ validate_app 'docker-machine'
42
+ prompt_to_replace_docker_machine name if (docker_machine_exists? name)
43
+ end
44
+
45
+ # Check that the given <tt>directories</tt> exist,
46
+ # and are directories.
47
+ def validate_directories *directories
48
+ directories.flatten.each do |directory|
49
+ validate_directory directory
50
+ end
51
+ end
52
+
53
+ # Check that the given <tt>directories</tt> do *NOT* exist.
54
+ def validate_directories_dont_exist *directories
55
+ directories.flatten.each do |directory|
56
+ validate_directory_doesnt_exist directory
57
+ end
58
+ end
59
+
60
+ # Check that the given <tt>zip_file</tt>
61
+ # doesn't exist already but that the path leading
62
+ # to the file does exist.
63
+ def validate_zip_file_export zip_file
64
+ path = File.dirname zip_file
65
+ error(
66
+ "The path to the zip file `#{path.to_path}' doesn't exist."
67
+ ) unless (is_directory? path)
68
+ prompt_to_replace_file zip_file if (is_file? zip_file)
69
+ end
70
+
71
+ # Similar to #validate_zip_file_export,
72
+ # but don't prompt for overwriting, etc.
73
+ # The zip file _has_ to exist in this case.
74
+ def validate_zip_file_import zip_file
75
+ error(
76
+ "The zip file `#{zip_file.to_s}' doesn't exist or is a directory."
77
+ ) unless (is_file? zip_file)
78
+ end
79
+
80
+ private
81
+
82
+ def validate_app appname
83
+ return if (@validated_apps.include? appname)
84
+ if (app_available? appname)
85
+ @validated_apps << appname
86
+ return
87
+ end
88
+ error(
89
+ "`#{appname}' is not available.",
90
+ "Please make sure you have it installed."
91
+ )
92
+ end
93
+
94
+ def app_available? name
95
+ return system("which #{name} &> /dev/null")
96
+ end
97
+
98
+ def docker_machine_exists? name
99
+ return system("docker-machine inspect #{name} &> /dev/null")
100
+ end
101
+
102
+ def validate_directory directory
103
+ return if (is_directory? directory)
104
+ error(
105
+ "Directory `#{directory.to_s}' does not exist or is a file."
106
+ )
107
+ end
108
+
109
+ def validate_directory_doesnt_exist directory
110
+ error(
111
+ "Directory `#{directory.to_s}' already exists."
112
+ ) if (is_directory?(directory) || is_file?(directory))
113
+ end
114
+
115
+ def is_directory? directory
116
+ return File.directory? directory
117
+ end
118
+
119
+ def is_file? file
120
+ return File.file? file
121
+ end
122
+
123
+ def prompt_to_replace_file file
124
+ options = {
125
+ overwrite: ?o,
126
+ append: ?a,
127
+ nothing: ?N
128
+ }
129
+ warning_print(
130
+ "File `#{file.to_s}' already exists. What do you want to do?",
131
+ " Overwrite file? (Remove existing and create new archive.) [#{options[:overwrite]}]",
132
+ " Append content to existing archive? [#{options[:append]}]",
133
+ " Do nothing, abort. [#{options[:nothing]}]",
134
+ "[#{options.values.join(?/)}] "
135
+ )
136
+ answer = STDIN.gets[0].strip.downcase
137
+ case answer
138
+ when options[:overwrite].downcase
139
+ message "Overwriting archive `#{file.to_s}'."
140
+ File.delete file
141
+ when options[:append].downcase
142
+ message "Appending to archive `#{file.to_s}'."
143
+ when options[:nothing].downcase, ''
144
+ message "Exiting."
145
+ abort
146
+ else
147
+ prompt_to_replace_file file
148
+ return
149
+ end
150
+ end
151
+
152
+ def prompt_to_replace_docker_machine name
153
+ options = {
154
+ backup: ?B,
155
+ overwrite: ?o,
156
+ nothing: ?n
157
+ }
158
+ warning_print(
159
+ "Docker machine `#{name}' already exists. What do you want to do?",
160
+ " Backup current configurations and create new one from import? [#{options[:backup]}]",
161
+ " (Backup to #{DM_BACKUP_PATH})",
162
+ " Overwrite existing files? [#{options[:overwrite]}]",
163
+ " Do nothing, abort. [#{options[:nothing]}]",
164
+ "[#{options.values.join(?/)}] "
165
+ )
166
+ answer = STDIN.gets[0].strip.downcase
167
+ case answer
168
+ when options[:overwrite].downcase
169
+ message "Overwriting existing configuration files for `#{name}'."
170
+ when options[:backup].downcase, ''
171
+ backup_docker_machine name
172
+ when options[:nothing].downcase
173
+ message "Exiting."
174
+ abort
175
+ else
176
+ prompt_to_replace_docker_machine name
177
+ return
178
+ end
179
+ end
180
+
181
+ def backup_docker_machine name
182
+ backup_name_date = "#{name}.#{Time.now.strftime('%Y-%m-%d')}"
183
+ mk_backup_directories
184
+ machine_path = DM_MACHINES_PATH.join name
185
+ cert_path = DM_CERTS_PATH.join name
186
+
187
+ #backup_machine_path = DM_BACKUP_MACHINES_PATH.join backup_name_date
188
+ #backup_cert_path = DM_BACKUP_CERTS_PATH.join backup_name_date
189
+
190
+ backup_machine_path = backup_cert_path = nil
191
+ backup_machine_path = get_backup_directory_for DM_BACKUP_MACHINES_PATH.join(backup_name_date) if (machine_path.directory?)
192
+ backup_cert_path = get_backup_directory_for DM_BACKUP_CERTS_PATH.join(backup_name_date) if (cert_path.directory?)
193
+
194
+ msg = [ "Backing-up configuration files for `#{name}' to" ]
195
+ msg << " `#{backup_machine_path}'" if (backup_machine_path)
196
+ msg[-1] += ', and' if (backup_machine_path && backup_cert_path)
197
+ msg << " `#{backup_cert_path}'" if (backup_cert_path)
198
+ message msg
199
+ FileUtils.mv machine_path, backup_machine_path if (backup_machine_path)
200
+ FileUtils.mv cert_path, backup_cert_path if (backup_cert_path)
201
+ end
202
+
203
+ def mk_backup_directories
204
+ DM_BACKUP_MACHINES_PATH.mkpath unless (DM_BACKUP_MACHINES_PATH.directory?)
205
+ DM_BACKUP_CERTS_PATH.mkpath unless (DM_BACKUP_CERTS_PATH.directory?)
206
+ end
207
+
208
+ def get_backup_directory_for base_backup_directory
209
+ base_backup_directory = Pathname.new base_backup_directory unless (base_backup_directory.is_a? Pathname)
210
+ return base_backup_directory unless (base_backup_directory.directory?)
211
+ base_backup_directory_tmp = base_backup_directory.dup
212
+ counter = 0
213
+ while (base_backup_directory_tmp.directory?)
214
+ counter += 1
215
+ base_backup_directory_tmp = Pathname.new "#{base_backup_directory.to_path}_#{counter}"
216
+ end
217
+ return base_backup_directory_tmp
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,56 @@
1
+ require 'bundler/setup'
2
+ require 'argument_parser'
3
+ require 'fileutils'
4
+ require 'pathname'
5
+ require 'zip'
6
+
7
+ module MachineConfigure
8
+ entry_file = Pathname.new(__FILE__).realpath
9
+ root = entry_file.dirname
10
+
11
+ HOME = Dir.home.chomp ?/
12
+ DIR = {
13
+ caller: Pathname.new($0).realpath,
14
+ entry: entry_file,
15
+ root: root,
16
+ src: root.join('machine_configure'),
17
+ helpers: root.join('machine_configure/helpers')
18
+ }
19
+
20
+ # This constant will replace any occurences of
21
+ # the user's home directory path in the
22
+ # docker-machine's config.json file.
23
+ HOME_REPLACE_STRING = '<REPLACE_WITH_HOME>'
24
+
25
+ # This string will be used as the filename for an
26
+ # additional file, which will only have the machine name in it.
27
+ MACHINE_NAME_FILENAME = 'MACHINE_NAME'
28
+
29
+ # The path to the docker-machine storage directory.
30
+ # <tt>$MACHINE_STORAGE_PATH</tt> or <tt>'~/.docker/machine'</tt>.
31
+ DM_STORAGE_PATH = (ENV['MACHINE_STORAGE_PATH'] ? (
32
+ Pathname.new(ENV['MACHINE_STORAGE_PATH'].chomp(?/)).realpath
33
+ ) : (
34
+ Pathname.new(File.join(HOME, '.docker/machine'))
35
+ ))
36
+ # The path to the docker-machine <tt>'machines'</tt> directory.
37
+ DM_MACHINES_PATH = DM_STORAGE_PATH.join 'machines'
38
+ # The path to the docker-machine <tt>'certs'</tt> directory.
39
+ DM_CERTS_PATH = DM_STORAGE_PATH.join 'certs'
40
+ # The paths for the machines backup directories <em>(Created by this script.)</em>.
41
+ DM_BACKUP_PATH = DM_STORAGE_PATH.join("#{DIR[:caller].basename}.backup")
42
+ DM_BACKUP_MACHINES_PATH = DM_BACKUP_PATH.join('machines')
43
+ DM_BACKUP_CERTS_PATH = DM_BACKUP_PATH.join('certs')
44
+
45
+ require DIR[:src].join 'meta'
46
+ require DIR[:helpers].join 'shared'
47
+ require DIR[:helpers].join 'message'
48
+ require DIR[:src].join 'validator'
49
+ require DIR[:src].join 'exporter'
50
+ require DIR[:src].join 'importer'
51
+ require DIR[:src].join 'cli_constants'
52
+ require DIR[:src].join 'cli'
53
+
54
+ VALIDATOR = Validator.new
55
+ VALIDATOR.validate_base_apps
56
+ end
@@ -0,0 +1,36 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'machine_configure/meta'
4
+ github_url = 'https://github.com/Noah2610/MachineConfigure'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = MachineConfigure::GEM_NAME
8
+ spec.version = MachineConfigure::VERSION
9
+ spec.authors = ['Noah Rosenzweig']
10
+ spec.email = ['rosenzweig.noah@gmail.com']
11
+ spec.summary = 'Manage your docker-machines\' configuration files.'
12
+ spec.description = <<-END
13
+ This gem can import or export the necessary configuration files
14
+ for your docker-machine configuration.
15
+ Use it to share your machine instances with others.
16
+ END
17
+ spec.homepage = github_url
18
+ spec.license = 'MIT'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ `git ls-files -z`.split(?\x0).reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.16'
30
+ #spec.add_development_dependency 'byebug'
31
+ spec.add_development_dependency 'minitest', '~> 5.0'
32
+ spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'rdoc'
34
+
35
+ spec.add_dependency 'rubyzip', '~> 1.2'
36
+ end
data/vimrc ADDED
@@ -0,0 +1 @@
1
+ nmap <leader>rd :!bin/rdoc<CR>
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: machine_configure
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Noah Rosenzweig
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.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: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubyzip
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ description: |2
84
+ This gem can import or export the necessary configuration files
85
+ for your docker-machine configuration.
86
+ Use it to share your machine instances with others.
87
+ email:
88
+ - rosenzweig.noah@gmail.com
89
+ executables:
90
+ - maccon
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - ".gitignore"
95
+ - ".travis.yml"
96
+ - Gemfile
97
+ - Gemfile.lock
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/console
102
+ - bin/rdoc
103
+ - bin/setup
104
+ - exe/maccon
105
+ - lib/machine_configure.rb
106
+ - lib/machine_configure/cli.rb
107
+ - lib/machine_configure/cli_constants.rb
108
+ - lib/machine_configure/exporter.rb
109
+ - lib/machine_configure/helpers/message.rb
110
+ - lib/machine_configure/helpers/shared.rb
111
+ - lib/machine_configure/importer.rb
112
+ - lib/machine_configure/meta.rb
113
+ - lib/machine_configure/validator.rb
114
+ - machine_configure.gemspec
115
+ - vimrc
116
+ homepage: https://github.com/Noah2610/MachineConfigure
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.7.7
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Manage your docker-machines' configuration files.
140
+ test_files: []