amoeba_deploy_tools 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a65c50aee5219d621d7c8b828cfb969e3f28983
4
- data.tar.gz: eb296c2a985c9e907ce16c9579363b21dbb12607
3
+ metadata.gz: 34b12c26a76d623203a1e6ec7b139d49c3296f9f
4
+ data.tar.gz: 9e27589f78df53ab436c5282a9e4660eca3acf9f
5
5
  SHA512:
6
- metadata.gz: 85f437f9e3c7b0f6e4de987fe7ba8f9910453c769fd23448059989463afa22685c2b042c02964660da88f8f58c627fef3b0fb7ffcddca79eb444b948cf497f5d
7
- data.tar.gz: ca7f024bebd8cbc71fc1dfd15242743ce56f6381aeb58496f402d3c2451b11d36a40be3412f8a95a6ab367e6cbd21f7e31ff5e019e362df6e8e6e2e3ed02c889
6
+ metadata.gz: 8065541a517c22f166f98f752d2f87af8629f2f9722da2b1f885be41a943607f38cb039d5bd7aae35ffb3a8125b37647cf712be5eecdb0443cbe5b2d5e7c7951
7
+ data.tar.gz: 02b8bed43ff7194127257da289b2b292130e6d2fee4d4636d6e9b35ae75038f1ae4e4d2cf9a58bc4ff4aeb9177d868bc3b3392642e4780b1756bc4209b4faae2
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ # Project files
21
+ .idea
22
+
23
+ # For librarian
24
+ /cookbooks
25
+ /tmp
26
+ *.swp
27
+
28
+ # RVM stuff we'll ignore for this gem for max flexibility
29
+ .ruby-version
30
+ .ruby-gemset
31
+ .rvmrc
32
+
33
+ # Ignore Gemfile.lock in this since it's a gem
34
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your dependencies in gemspec file
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Amoeba Consulting
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ Amoeba Deploy Tools
2
+ ===================
3
+
4
+ Amoeba Deploy Tools (ADT) is a ruby gem that enables rapid creation of servers using the Chef config
5
+ management system. Using Chef today there are many tools and best practices, and we have found that
6
+ often setting up a chef "kitchen" can be tedious. Additionally, we believe in supporting server-less
7
+ deploys. That is, we don't like to maintain and run a separate chef server just to manage our boxes.
8
+
9
+ ADT integrates a number of other tools (`chef-solo`, and `librarian`, and others) and we provide a
10
+ [skeleton Kitchen](http://github.com/AmoebaConsulting/amoeba-kitchen-skel), so you can fork our
11
+ kitchen, install this gem, and deploy a node in minutes.
12
+
13
+ ## Short introduction
14
+
15
+ First, you need ruby installed. Next, add `amoeba-deploy-tools` to your project Gemfile, or install
16
+ via the `gem` command:
17
+
18
+ > gem install amoeba-deploy-tools
19
+
20
+ If you are preparing a server to install an application, change to that project's directory.
21
+ Otherwise, switch to a dir where you plan on running ADT from. And run:
22
+
23
+ > amoeba init
24
+
25
+ The command will walk you through the process of initializing your kitchen. Now you've got a
26
+ kitchen all ready to go. We recommend you add it to version control (git). We do not recommend you
27
+ add the .amoeba.yml configuration file to git.
28
+
29
+ Next you must create a node definition, and run a deploy. A node definition sits in the kitchen's
30
+ `nodes/` directory. See this directory for a sample node you can copy / rename to make your own.
31
+
32
+ Once you have a node defined, just run:
33
+
34
+ > amoeba node bootstrap --node <node-name>
35
+
36
+ ... and the node will be provisioned. After provisioning, the node's metadata will be stored in the
37
+ `data_bags` folder. Our skeleton kitchen includes a basenode recipe that creates a deployment user
38
+ on the destination box, and disables root access. This user's name will be cached in the node's
39
+ databag and used for subsequent operations to the box. Now, as you make changes to the node, or any
40
+ cookbooks you create in `site-cookbooks`, you can push those changes by running:
41
+
42
+ > amoeba node push --node <node-name>
43
+
44
+ Finally to setup Capistrano, you need only add the following to your existing Capfile:
45
+
46
+ require 'amoeba_deploy_tools/capistrano'
47
+
48
+ For a full list of commends, run `amoeba help` or see below.
49
+
50
+ ## Detailed Information
51
+
52
+ In essence, ADT is controlled by a configuration file, `.amoeba.yml`, specifying where your kitchen
53
+ will be located, and a copy of the kitchen itself. In the future, we will support specifying
54
+ deployment-related configuration in this file (such as SSH information). So we recommend you keep it
55
+ gitignored if it contains sensitive information. The kitchen should be kept under version control,
56
+ and you can either fork it prior to running `amoeba init` or you can let `amoeba init` make a copy
57
+ of our skeleton which you can add to version control later.
58
+
59
+ ## Vagrant Testing
60
+
61
+ Using Vagrant to test your nodes is easy! Just install Vagrant, run `amoeba init` and modify the
62
+ kitchen's `Vagrantfile`, as necessary. Run `vagrant up` and watch as your VM comes alive.
63
+
64
+ Then, you can ensure the provider is setup correctly (see `data_bags/providers/vagrant.json`). You
65
+ must ensure the SSH port matches that in `Vagrantfile` (by default 2222), and you must point the
66
+ provider to the SSH key Vagrant uses (if you just installed Vagrant, this is by default correct,
67
+ `~/.vagrant.d/insecure_private_key`). You can, however, change the private key (for security
68
+ reasons if you plan on distributing the VM or using it in production) by specifying an alternative
69
+ one in the `Vagrantfile` configuration key `config.ssh.private_key_path` (see
70
+ [the following documentation](http://docs-v1.vagrantup.com/v1/docs/config/ssh/private_key_path.html)
71
+ on Vagrant for more information).
72
+
73
+ ## Commands
74
+
75
+ ### amoeba init [url] [--skeleton]
76
+
77
+ The URL is optional, but if specified will be used as the library's git repo. You can also specify
78
+ `--skeleton`, which will use the specified URL as a skeleton, and make a copy of it (useful if you
79
+ do not already have a Kitchen you wish to use in git, but want to start a new one based on the URL
80
+ specified).
81
+
82
+ The default URL is [Amoeba's skeleton](https://github.com/AmoebaConsulting/amoeba-kitchen-skel),
83
+ which will be copied (as if `--skeleton` was specified).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'amoeba_deploy_tools/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'amoeba_deploy_tools'
8
+ s.version = AmoebaDeployTools::VERSION
9
+ s.authors = ['Daniel Jabbour', 'Hike Danakian']
10
+ s.email = 'sayhi@amoe.ba'
11
+ s.summary = 'Easy Chef Solo / Knife operation with Amoeba Deploy Tools.'
12
+ s.description = 'Easy Chef Solo / Knife operation with Amoeba Deploy Tools.'
13
+ s.homepage = 'https://github.com/AmoebaConsulting/amoeba-deploy-tools'
14
+ s.license = 'MIT'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.test_files = `git ls-files -- spec/*`.split("\n")
19
+ s.require_paths = ['lib']
20
+
21
+ s.required_ruby_version = '>= 1.9.2'
22
+
23
+ s.add_development_dependency 'bundler', '~> 1.3'
24
+ s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'rspec', '~> 2.14.1'
26
+ s.add_development_dependency 'pry', '~> 0.9.12.4'
27
+
28
+ s.add_dependency 'hashie', '~> 2.0.5'
29
+ s.add_dependency 'thor', '~> 0.18.1'
30
+ s.add_dependency 'cocaine', '~> 0.5.3'
31
+
32
+ s.add_dependency 'chef', '~> 11.8.0'
33
+ s.add_dependency 'librarian-chef', '~> 0.0.2'
34
+ s.add_dependency 'knife-solo', '~> 0.4.0'
35
+ s.add_dependency 'knife-solo_data_bag', '~> 0.4.0'
36
+ end
@@ -0,0 +1,5 @@
1
+ def _cset(name, *args, &block)
2
+ unless exists?(name)
3
+ set(name, *args, &block)
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ require 'amoeba_deploy_tools/capistrano/common'
2
+
3
+ Capistrano::Configuration.instance(:must_exist).load do
4
+ _cset(:database_yml_path){ "#{shared_path}/config/database.yml" }
5
+
6
+ namespace :amoeba_deploy_tools do
7
+ desc 'Link the shared/config files into the current/config dir.'
8
+ task :symlink_configs, :roles => :app do
9
+ run [ "cd #{latest_release}",
10
+ "ln -nfs #{database_yml_path} config/"
11
+ ].join(' && ')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'capistrano/version'
2
+
3
+ if defined?(Capistrano::VERSION) && Capistrano::VERSION >= '3.0'
4
+ raise 'We do not yet support Capistrano v3.0. Please downgrade, send us a pull request, or symlink database.yml yourself.'
5
+ else
6
+ require 'amoeba_deploy_tools/capistrano/recipes'
7
+
8
+ Capistrano::Configuration.instance(:must_exist).load do
9
+ after 'deploy:finalize_update', 'amoeba_deploy_tools:symlink_configs'
10
+ end
11
+ end
@@ -0,0 +1,105 @@
1
+ require 'thor'
2
+ require 'hashie/mash'
3
+
4
+ module AmoebaDeployTools
5
+ class Command < Thor
6
+
7
+ include AmoebaDeployTools::Concerns::Hooks
8
+
9
+ option :node, desc: 'name of the node you wish to operate on (set default in .amoeba.yml)'
10
+ option :'log-level', desc: 'log level to output (DEBUG, INFO, WARN (default), or ERROR)'
11
+ option :dry, type: :boolean, default: false, desc: 'Don\'t actually execute any chef commands (just show what would be run)'
12
+
13
+ # Note that all subcommands will inherit this class. This means any setup done in here
14
+ # will be duplicated if it runs at initialization (since the main command and subcommand are
15
+ # both evaluated at runtime). Thus, it's important not to put anything in the constructor.
16
+ # If you wish to setup any global state, do so in the Amoeba class initializer.
17
+ def initialize(args=[], options={}, config={})
18
+ super
19
+ end
20
+
21
+ no_commands do
22
+ def config
23
+ return @amoebaConfig if @amoebaConfig
24
+
25
+ @amoebaConfig = Config.load('.amoeba.yml')
26
+ end
27
+
28
+ def kitchen_path
29
+ return @kitchen if @kitchen
30
+
31
+ if config.kitchen_.path
32
+ @kitchen = config.kitchen.path
33
+ else
34
+ @kitchen = '.'
35
+ logger.warn 'Using local dir as kitchen path, no `.amoeba.yml` config found. Consider running `amoeba init`'
36
+ end
37
+
38
+ say_fatal "ERROR: Could not find amoeba kitchen: #{@kitchen}" unless Dir.exists? @kitchen
39
+
40
+ @kitchen
41
+ end
42
+
43
+ def inside_kitchen(&block)
44
+ Bundler.with_clean_env do
45
+ Dir.chdir(kitchen_path) { block.call }
46
+ end
47
+ end
48
+
49
+ # The node must be specified unless you set a default one. You can specify it with the
50
+ # `--node [name]` option, or by setting `[:node][:default]` in your `.amoeba.yml``
51
+ def node
52
+ return @node if @node
53
+
54
+ node_name = options[:node] || config.node_.default_
55
+ say_fatal 'ERROR: must specify --node or have a default node in your config file' unless node_name
56
+
57
+ inside_kitchen do
58
+ node_filename = File.expand_path(File.join('nodes', "#{node_name}.json"))
59
+ if node_name.nil? || !File.exists?(node_filename)
60
+ say_fatal "ERROR: Could not find node JSON file: #{node_filename}"
61
+ end
62
+
63
+ @node = Config.load(node_filename, format: :json)
64
+ @node.tap {|n| n.filename = node_filename } if @node
65
+ end
66
+ end
67
+
68
+ def remote_node
69
+ data_bag(:nodes)[node.name]
70
+ end
71
+
72
+ def data_bag(name)
73
+ DataBag.new(name, kitchen_path)
74
+ end
75
+
76
+ def deployment
77
+ return @deployment if @deployment
78
+
79
+ @deployment = Hashie::Mash.new
80
+ @deployment.deep_merge!(node.deployment) if node.deployment
81
+
82
+ provider = data_bag(:providers)[node.deployment.provider] if node.deployment_.provider
83
+
84
+ @deployment.deep_merge!(provider) if provider
85
+ @deployment.deep_merge!(remote_node.deployment) if remote_node.deployment
86
+ @deployment.deep_merge!(node.deployment)
87
+
88
+ return @deployment
89
+ end
90
+
91
+ def logger
92
+ Logger.instance
93
+ end
94
+
95
+ def validate_chef_id!(name)
96
+ say_fatal "You must specify a key name for your data bag id." unless name
97
+ unless name =~ /^[a-zA-Z0-9\_\-]+$/
98
+ say_fatal "Your data bag name must only contain alphanums, dashes, and underscores. `#{name}` is invalid!"
99
+ end
100
+ return true
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,118 @@
1
+ module AmoebaDeployTools
2
+
3
+ DEFAULT_SKELETON_REPO = 'https://github.com/AmoebaConsulting/amoeba-kitchen-skel.git'
4
+
5
+ class Amoeba < Command
6
+
7
+ # Any "global" setup can be done here, as the "amoeba" command will always be initialized
8
+ def initialize(args=[], options={}, config={})
9
+ super
10
+ setup_logger
11
+ setup_cocaine
12
+
13
+ # Fix JSON so it doesn't try to dereference json_class in JSON responses
14
+ # See http://blog.defunct.ca/2013/02/01/query-chef-server-api-from-ruby-script/
15
+ JSON.create_id = ''
16
+ end
17
+
18
+ desc 'init (url optional)', 'Setup Amoeba Deploy Tools (either by creating a new kitchen or locating an existing one)'
19
+ method_options :skeleton => :boolean
20
+ def init(url=nil)
21
+ # Store the user-specified URL if it exists
22
+ user_url = url if url
23
+
24
+ # Check if we're in a git repo
25
+ if system('git rev-parse > /dev/null 2>&1')
26
+ project_dir = %x{git rev-parse --show-toplevel}
27
+
28
+ default_kitchen_dir = File.expand_path("#{File.basename(project_dir).chop}-kitchen",
29
+ File.expand_path('..', project_dir))
30
+ else
31
+ default_kitchen_dir = File.expand_path('.', 'kitchen')
32
+ end
33
+
34
+ kitchen_dir = ask "Where should the new kitchen be located? (default: #{default_kitchen_dir})"
35
+ kitchen_dir = default_kitchen_dir if kitchen_dir.empty?
36
+
37
+ if File.exist?(kitchen_dir)
38
+ say 'Existing kitchen found! Will not overwrite.', :yellow
39
+ else
40
+ # Copy (not clone) the repo if the URL isn't specified. If it is, obey the --skeleton param
41
+ copy = url ? options[:skeleton] : true
42
+
43
+ # If there was no specified URL, use default one
44
+ url ||= DEFAULT_SKELETON_REPO
45
+
46
+ git_opts = copy ? '--depth 1' : ''
47
+ if system("git clone #{git_opts} #{url} #{kitchen_dir}")
48
+ if copy
49
+ git_dir = File.expand_path('.git', kitchen_dir)
50
+ if File.directory?(git_dir)
51
+ FileUtils.rm_rf(git_dir)
52
+ end
53
+ say_bold "New kitchen created at: #{kitchen_dir}. Please add it to version control"
54
+ else
55
+ say_bold "Kitchen from #{url} has been 'git clone'-ed into your kitchen directory"
56
+ end
57
+ else
58
+ say_fatal "ERROR: Kitchen directory cannot be cloned from URL #{url}"
59
+ end
60
+ end
61
+
62
+ # Okay, the kitchen exists (one way or another)
63
+
64
+ config.kitchen!.url = user_url if user_url && !options[:skeleton]
65
+ config.kitchen!.path = kitchen_dir.to_s
66
+ config.save
67
+
68
+ say_bold 'Saving ./amoeba.yml config file. We suggest you `git ignore` this (contains local settings).'
69
+ end
70
+
71
+ desc 'sync OPTS', 'Not yet implemented.'
72
+ def sync
73
+ end
74
+
75
+ desc 'update OPTS', 'Not yet implemented.'
76
+ def update
77
+ end
78
+
79
+ desc 'app [COMMAND]', 'Manage the deployed application (see `amoeba app help`)'
80
+ subcommand 'app', AmoebaDeployTools::App
81
+
82
+ desc 'node [COMMAND]', 'Deploy and configure nodes (see `amoeba node help`)'
83
+ subcommand 'node', AmoebaDeployTools::Node
84
+
85
+ desc 'key [COMMAND]', 'Manage private keys (see `amoeba key help`)'
86
+ subcommand 'key', AmoebaDeployTools::Key
87
+
88
+ desc 'ssl [COMMAND]', 'Manage SSL certificates (see `amoeba ssl help`)'
89
+ subcommand 'ssl', AmoebaDeployTools::Ssl
90
+
91
+ no_commands do
92
+ def setup_logger
93
+ # Default logging level is warn. You can change this in your .amoeba.yml config
94
+ # by setting `logLevel` or by passing --logLevel option
95
+ level = 'WARN'
96
+ level = config.log_level if config.log_level
97
+ level = options[:'log-level'] if options[:'log-level']
98
+ begin
99
+ level = AmoebaDeployTools::Logger.const_get level.upcase
100
+ rescue NameError
101
+ say "WARNING: Invalid log level: #{level}. Defaulting to WARN.", :red
102
+ level = AmoebaDeployTools::Logger::WARN
103
+ end
104
+ AmoebaDeployTools::Logger.instance.level = level
105
+ end
106
+
107
+ def setup_cocaine
108
+ if options[:dry]
109
+ Cocaine::CommandLine.runner = Cocaine::CommandLine::FakeRunner.new
110
+ else
111
+ Cocaine::CommandLine.runner = AmoebaDeployTools::NoiseyCocaineRunner.new
112
+ end
113
+
114
+ Cocaine::CommandLine.logger = AmoebaDeployTools::Logger.instance
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,27 @@
1
+ module AmoebaDeployTools
2
+ class App < Command
3
+ desc 'deploy', 'Deploy the application (using capistrano)'
4
+ def deploy
5
+ cap :deploy
6
+ end
7
+
8
+ desc 'capfile', 'Generate Capfile for application'
9
+ def capfile
10
+ app = node.application
11
+ sudo(node.name, "cat ~#{app.name}/shared/config/Capfile")
12
+ end
13
+
14
+ desc 'exec CMD', "executes CMD on remote server in app's env"
15
+ def exec
16
+ end
17
+
18
+ desc 'ssh', 'SSHs to the application node, as the application user'
19
+ def ssh
20
+ end
21
+
22
+ no_commands do
23
+ def cap(cmd)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ module AmoebaDeployTools
2
+ module Concerns
3
+ module Hooks
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ ### Class methods
10
+ def before_hooks
11
+ @before_hooks ||= []
12
+ end
13
+
14
+ def after_hooks
15
+ @after_hooks ||= []
16
+ end
17
+
18
+ def before(&blk)
19
+ @before_hooks ||= []
20
+ @before_hooks << blk
21
+ end
22
+
23
+ def after(&blk)
24
+ @after_hooks ||= []
25
+ @after_hooks << blk
26
+ end
27
+ end
28
+
29
+ #### Instance methods
30
+
31
+ def invoke_command(command, *args)
32
+ # Ignore hooks on help commands
33
+ if command.name == 'help'
34
+ return super
35
+ end
36
+
37
+ self.class.before_hooks.each {|h| instance_eval &h }
38
+ retVal = super
39
+ self.class.after_hooks.each {|h| instance_eval &h }
40
+ return retVal
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,109 @@
1
+ module AmoebaDeployTools
2
+ module Concerns
3
+ module SSH
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ ### Class methods
10
+ end
11
+
12
+ ### Instance methods
13
+
14
+ # Outputs SSH options for connecting to this node (provide a map of deploy key to command
15
+ # line arg name).
16
+ def node_host_args(flag_map)
17
+ say_fatal 'ERROR: Missing deployment info for node.' unless deployment && deployment.host
18
+
19
+ host_arg = deployment.host
20
+ host_arg = "#{deployment.user}@#{host_arg}" if deployment.user
21
+
22
+ # Iterate through all the specified flags and check if they're defined in the deployment
23
+ # config, appending them to the output if they are.
24
+ flag_map.each do |field, argument_name|
25
+ host_arg << " #{argument_name} #{deployment[field]}" if deployment[field]
26
+ end
27
+
28
+ host_arg
29
+ end
30
+
31
+ # Run knife solo command on server
32
+ def knife_solo(cmd, options={})
33
+ say_fatal 'ERROR: Node must have a name defined' unless node.name
34
+
35
+ # Ensure json is a hashie
36
+ json = options.delete(:json) || Hashie::Mash.new
37
+
38
+ # If a block is specified, it means we have json in it, so let's resolve it
39
+ yield(json) if block_given?
40
+
41
+ # Ensure JSON for the node is set. It is either provided by the calling party, or we use the
42
+ # node file's definitions.
43
+ if json.empty?
44
+ json.deep_merge!(node)
45
+ end
46
+
47
+ exec = "bundle exec knife solo #{cmd.to_s} "
48
+
49
+ if options.delete(:ssh)
50
+ exec << node_host_args(port: '--ssh-port',
51
+ config: '--ssh-config-file',
52
+ ident: '--identity-file') << ' '
53
+ exec << "--no-host-key-verify --node-name #{node.name}"
54
+ end
55
+
56
+ if options.delete(:include_private_key)
57
+ private_key = node.private_key || 'default'
58
+ private_key = config.private_keys_[private_key]
59
+
60
+ # Stick the private key into a our custom JSON (to be appended to the node)
61
+ json.private_key_raw = private_key if private_key
62
+ end
63
+
64
+ opts = {}
65
+ opts[:runner] = AmoebaDeployTools::InteractiveCocaineRunner.new if options.delete(:interactive)
66
+
67
+ # Now go through all the options specified and append them to args
68
+ args = ''
69
+ options.each do |argument, value|
70
+ args << " --#{argument} #{value}"
71
+ end
72
+
73
+ inside_kitchen do
74
+ # JSON will be written to a temp file and used in place of the node JSON file
75
+ with_tmpfile(JSON.dump(json), name: ['node', '.json']) do |file_name|
76
+ knife_solo_cmd = Cocaine::CommandLine.new(exec, "#{args} #{file_name}", opts)
77
+ knife_solo_cmd.run
78
+ end
79
+ end
80
+ end
81
+
82
+ def ssh_run(cmd, options)
83
+ options = {
84
+ silent: false,
85
+ interactive: false
86
+ }.merge!(options)
87
+
88
+ opts = {}
89
+ opts[:runner] = Cocaine::CommandLine::BackticksRunner.new if options[:silent]
90
+ opts[:runner] = AmoebaDeployTools::InteractiveCocaineRunner.new if options[:interactive]
91
+
92
+ ssh_cmd = node_host_args(port: '-p', ident: '-i')
93
+
94
+ [ 'Compression=yes',
95
+ 'DSAAuthentication=yes',
96
+ 'LogLevel=FATAL',
97
+ 'StrictHostKeyChecking=no',
98
+ 'UserKnownHostsFile=/dev/null'
99
+ ].each do |opt|
100
+ ssh_cmd << " -o #{opt}"
101
+ end
102
+
103
+ ssh_cmd << " '#{cmd}'" if cmd && !cmd.empty?
104
+
105
+ Cocaine::CommandLine.new('ssh', ssh_cmd, opts).run
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,22 @@
1
+ module AmoebaDeployTools
2
+ class Key < Command
3
+
4
+ desc 'create', 'Create a private_key (used to encrypt secret data_bags like SSL certs)'
5
+ def create(name=nil)
6
+ validate_chef_id!(name)
7
+
8
+ key = Cocaine::CommandLine.new('openssl', "rand -base64 512 | tr -d '\\r\\n'",
9
+ runner: Cocaine::CommandLine::BackticksRunner.new).run
10
+ config.private_keys![name] = key
11
+
12
+ logger.debug "Saving key to `.amoeba.yml` config"
13
+
14
+ if config.new_file?
15
+ say_fatal "Cannot create new key, no .amoeba.yml file found! Please run `amoeba init`"
16
+ end
17
+
18
+ config.save
19
+ end
20
+
21
+ end
22
+ end