danarchy_deploy 0.1.0

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
+ SHA1:
3
+ metadata.gz: bc77046b77757e8d847424bced87d9b0a78131a9
4
+ data.tar.gz: f07bf23008b48a3dd3f84b35d1bd87cb9e0a379a
5
+ SHA512:
6
+ metadata.gz: d017636ab2096321579edeb48973913374dd23680328fafada853012394af638c80e9e107b21cfaad81b10a6bc51e93f937e5c81b2368bcfe0a0b3430a399d9a
7
+ data.tar.gz: 91523adf0a963049251406efe47516c91689f89f0a1b423870c71ddb56a65f3fddeefc6541e2da26a62afd0f5b190e1e5a53190bcee332ee2d2b6439473c55e7
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
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.8
5
+ before_install: gem install bundler -v 1.16.0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in danarchy_deploy.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ danarchy_deploy (0.1.0)
5
+ danarchy_couchdb (~> 0.1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ danarchy_couchdb (0.1.2)
11
+ diff-lcs (1.3)
12
+ rake (10.5.0)
13
+ rspec (3.7.0)
14
+ rspec-core (~> 3.7.0)
15
+ rspec-expectations (~> 3.7.0)
16
+ rspec-mocks (~> 3.7.0)
17
+ rspec-core (3.7.1)
18
+ rspec-support (~> 3.7.0)
19
+ rspec-expectations (3.7.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.7.0)
22
+ rspec-mocks (3.7.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.7.0)
25
+ rspec-support (3.7.1)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.16)
32
+ danarchy_deploy!
33
+ rake (~> 10.0)
34
+ rspec (~> 3.0)
35
+
36
+ BUNDLED WITH
37
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Dan Heneise
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,46 @@
1
+ # DanarchyDeploy
2
+
3
+ dAnarchy Deploy is a template-driven Ruby gem to deploy locally or remotely to Gentoo systems (and debian/ubuntu, more being added). This can take a .JSON or .YAML input file, or a CouchDB connection as a deployment template and install necessary packages, add users and groups, write ERB templates, and decompress tar/zip archives. More documentation incoming.
4
+
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'danarchy_deploy'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install danarchy_deploy
21
+
22
+ !! Since dAnarchy Deploy takes several actions that require root/sudo access, I install the gem with sudo as any remote deployment will on the target host:
23
+
24
+ $ sudo gem install danarchy_deploy
25
+
26
+
27
+ ## Usage
28
+
29
+ dAnarchy Deploy usage info can be read with -h/--help:
30
+ ```ruby
31
+ $ danarchy_deploy -h
32
+ Usage: sudo /usr/local/bin/danarchy_deploy (local|remote) --json /path/to/deployment.json [options]
33
+ -j, --json=file Read configuration from JSON file.
34
+ -y, --yaml=file Read configuration from YAML file.
35
+ -p, --pretend Pretend run: Don't take any action.
36
+ -f, --first-run First Run: Run as a first run causing services to run all init actions.
37
+ -d, --deploy-dir Deployment directory. Defaults to '/danarchy/deploy'.
38
+ --ssh-verbose Verbose SSH stdout/stderr output.
39
+ --version Print /usr/local/bin/danarchy_deploy version.
40
+ -h, --help Print this help info.
41
+
42
+ DanarchyDeploy: 0.1.0
43
+ Exiting! Must be run with sudo!
44
+ ```
45
+
46
+ More documentation incoming...
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/danarchy_deploy'
3
+ require 'danarchy_couchdb'
4
+ require 'date'
5
+ require 'optparse'
6
+
7
+ deployment = nil
8
+ options = { couchdb: "/home/#{ENV['SUDO_USER']}/.danarchy/danarchy_deploy/danarchy_deploy.json",
9
+ deploy_dir: '/danarchy/deploy',
10
+ deploy_file: nil,
11
+ pretend: false,
12
+ ssh_verbose: false,
13
+ first_run: false }
14
+
15
+ ARGV.push('--help') if ARGV.empty?
16
+ optparse = OptionParser.new do |opts|
17
+ opts.banner = "Usage: sudo #{$PROGRAM_NAME} (local|remote) --json /path/to/deployment.json [options]"
18
+
19
+ opts.on('-j=file', '--json=file', 'Read configuration from JSON file.') do |file|
20
+ require 'json'
21
+ options[:deploy_file] = file
22
+ deployment = JSON.parse(File.read(file), symbolize_names: true)
23
+ end
24
+
25
+ opts.on('-y=file', '--yaml=file', 'Read configuration from YAML file.') do |file|
26
+ require 'yaml'
27
+ options[:deploy_file] = file
28
+ deployment = YAML.load_file(file)
29
+ end
30
+
31
+ opts.on('-p', '--pretend', 'Pretend run: Don\'t take any action.') do |val|
32
+ options[:pretend] = true
33
+ end
34
+
35
+ opts.on('-f', '--first-run', 'First Run: Run as a first run causing services to run all init actions.') do |val|
36
+ options[:first_run] = true
37
+ end
38
+
39
+ opts.on('-d', '--deploy-dir', "Deployment directory. Defaults to '/danarchy/deploy'.") do |val|
40
+ options[:deploy_dir] = val.gsub(/\/$/, '')
41
+ end
42
+
43
+ opts.on('--ssh-verbose', "Verbose SSH stdout/stderr output.") do |val|
44
+ options[:ssh_verbose] = true
45
+ end
46
+
47
+ opts.on('--version', "Print #{$PROGRAM_NAME} version.") do |val|
48
+ puts "DanarchyDeploy: #{DanarchyDeploy::VERSION}"
49
+ end
50
+
51
+ opts.on('-h', '--help', 'Print this help info.') do |val|
52
+ puts opts, ''
53
+ end
54
+ end.parse!
55
+
56
+ puts "DanarchyDeploy: #{DanarchyDeploy::VERSION}"
57
+ abort('Exiting! Must be run with sudo!') if Process.uid != 0
58
+
59
+ location = ARGV.delete('remote') || ARGV.delete('local') || abort("ERROR: Need an option 'local' or 'remote' to know what to do!")
60
+ cdb_config = File.exist?(options[:couchdb]) ? JSON.parse(File.read(options[:couchdb]), symbolize_names: true)[:couchdb] : nil
61
+ cdb = DanarchyCouchDB::Connection.new(cdb_config) if cdb_config
62
+
63
+ if !deployment && !options[:deploy_file] && cdb
64
+ deployment_name = ARGV.shift
65
+ abort("Need a deployment name!") if !deployment_name
66
+ puts "DanarchyCouchDB: #{DanarchyCouchDB::VERSION}"
67
+ puts "CouchDB connection found! Loading deployment for #{cdb_config[:database]}:#{deployment_name}"
68
+ deployment = cdb.get(cdb_config[:database], deployment_name)
69
+ abort("ERROR: Deployment #{deployment_name} => #{deployment[:reason]}") if deployment[:error]
70
+ puts "Found deployment: #{deployment[:_id]} | rev => #{deployment[:_rev]}"
71
+ options[:deploy_file] = "#{options[:deploy_dir]}/#{deployment_name}/#{deployment_name}.json"
72
+ puts "Temp JSON path: #{options[:deploy_file]}"
73
+ File.write(options[:deploy_file], deployment.to_json)
74
+ end
75
+
76
+ if !deployment[:last_deploy] || deployment[:last_deploy].empty?
77
+ puts "This looks like a first-time run since a last_deploy time wasn't found."
78
+ options[:first_run] = true
79
+ end
80
+
81
+ if location == 'remote'
82
+ deployment = DanarchyDeploy::RemoteDeploy.new(deployment, options)
83
+ elsif location == 'local'
84
+ puts "Deploying #{deployment[:hostname]} locally to #{`hostname`.chomp}."
85
+
86
+ if !options[:pretend]
87
+ puts ' ! Ctrl-c out if this is not what you want to do!'
88
+ 10.downto(0) do |i|
89
+ trap('SIGINT') { abort("\nExiting!") }
90
+ print "\rDeploying in #{i} seconds"
91
+ sleep(1)
92
+ end
93
+
94
+ puts "\nDeploying!"
95
+ end
96
+
97
+ deployment = DanarchyDeploy::LocalDeploy.new(deployment, options)
98
+ end
99
+
100
+ if deployment && cdb && !options[:pretend]
101
+ puts "Saving deployment to CouchDB."
102
+ old_rev = cdb.get(cdb_config[:database], deployment_name)[:_rev]
103
+ save = cdb.put(cdb_config[:database], deployment[:_id], deployment)
104
+
105
+ if save[:ok] == true
106
+ puts ' |+ Saved deployment to CouchDB!'
107
+ puts " |_id: #{save[:id]}"
108
+ puts " |_rev: #{save[:rev]}\n\n"
109
+ else
110
+ puts ' ! Unable to save deployment to CouchDB'
111
+ puts " |_error: #{save[:error]}"
112
+ puts " |_rev: #{old_rev[:rev]}\n\n"
113
+ end
114
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "danarchy_deploy"
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/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
@@ -0,0 +1,36 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "danarchy_deploy/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "danarchy_deploy"
8
+ spec.version = DanarchyDeploy::VERSION
9
+ spec.authors = ["Dan James"]
10
+ spec.email = ["dan@danarchy.me"]
11
+
12
+ spec.summary = %q{Pushes deployments locally or remotely based on a JSON/YAML/CouchDB template.}
13
+ spec.description = %q{DanarchyDeploy intends to simplify Gentoo Linux (and other distro) deployments down to a single template from an input JSON or YAML file, or from a CouchDB file.}
14
+ spec.homepage = "https://github.com/danarchy85/danarchy_deploy"
15
+ spec.license = "MIT"
16
+
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
19
+ else
20
+ raise "RubyGems 2.0 or newer is required to protect against " \
21
+ "public gem pushes."
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(%r{^(test|spec|features)/})
26
+ end
27
+ spec.bindir = "bin"
28
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency "danarchy_couchdb", "~> 0.1.0"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.16"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ end
@@ -0,0 +1,130 @@
1
+ require_relative 'danarchy_deploy/version'
2
+
3
+ module DanarchyDeploy
4
+ require_relative './danarchy_deploy/archiver'
5
+ require_relative './danarchy_deploy/groups'
6
+ require_relative './danarchy_deploy/helpers'
7
+ require_relative './danarchy_deploy/installer'
8
+ require_relative './danarchy_deploy/users'
9
+ require_relative './danarchy_deploy/services'
10
+ require_relative './danarchy_deploy/templater'
11
+
12
+ class LocalDeploy
13
+ def self.new(deployment, options)
14
+ puts "\n" + self.name
15
+ puts "Pretend run! Not making any changes." if options[:pretend]
16
+
17
+ puts 'Begining Deployment:'
18
+ printf("%12s %0s\n", 'Hostname:', deployment[:hostname])
19
+ printf("%12s %0s\n", 'OS:', deployment[:os])
20
+ printf("%12s %0s\n", 'Packages:', deployment[:packages].join(', ')) if deployment[:packages]
21
+
22
+ deployment = DanarchyDeploy::Installer.new(deployment, options)
23
+ deployment = DanarchyDeploy::Services.new(deployment, options) if deployment[:services]
24
+ deployment = DanarchyDeploy::Groups.new(deployment, options) if deployment[:groups]
25
+ deployment = DanarchyDeploy::Users.new(deployment, options) if deployment[:users]
26
+ deployment = DanarchyDeploy::Services.init(deployment, options) if deployment[:services]
27
+
28
+ deployment[:last_deploy] = DateTime.now.strftime("%Y/%m/%d %H:%M:%S")
29
+ puts "\nFinished Local Deployment at #{deployment[:last_deploy]}!"
30
+ File.write(options[:deploy_file], deployment.to_json) if options[:deploy_file].end_with?('.json')
31
+ File.write(options[:deploy_file], deployment.to_yaml) if options[:deploy_file].end_with?('.yaml')
32
+ deployment
33
+ end
34
+ end
35
+
36
+ class RemoteDeploy
37
+ def self.new(deployment, options)
38
+ puts "\n" + self.name
39
+ options[:working_dir] = options[:deploy_dir] + '/' + deployment[:hostname]
40
+ connector = { hostname: deployment[:hostname],
41
+ ipv4: deployment[:ipv4],
42
+ ssh_user: deployment[:ssh_user],
43
+ ssh_key: deployment[:ssh_key] }
44
+
45
+ install_gem(connector, options)
46
+ push_deployment(connector, options)
47
+ deploy_result = remote_LocalDeploy(connector, options)
48
+
49
+ if deploy_result[:stderr]
50
+ puts ' ! Deployment failed!'
51
+ abort("STDERR:\n#{deploy_result[:stderr]}")
52
+ else
53
+ puts deploy_result[:stdout]
54
+ end
55
+
56
+ pull_deployment(connector, options)
57
+ remote_Cleanup(connector, options)
58
+
59
+ puts "\nRemote deployment complete!"
60
+ deployment = JSON.parse(File.read(options[:deploy_file]), symbolize_names: true) if options[:deploy_file].end_with?('.json')
61
+ deployment = YAML.load_file(options[:deploy_file]) if options[:deploy_file].end_with?('.yaml')
62
+ deployment
63
+ end
64
+
65
+ private
66
+ def self.install_gem(connector, options)
67
+ puts "\n > Installing danarchy_deploy on #{connector[:hostname]}"
68
+ install_result = DanarchyDeploy::Helpers.run_command("ssh -i #{connector[:ssh_key]} #{connector[:ssh_user]}@#{connector[:ipv4]} -o ConnectTimeout=30 'sudo gem install -f danarchy_deploy && test -d #{options[:working_dir]} || sudo mkdir -vp #{options[:working_dir]} && sudo chown -Rc #{connector[:ssh_user]}:#{connector[:ssh_user]} #{options[:working_dir]}'", options)
69
+
70
+ if install_result[:stderr]
71
+ puts ' ! Gem install failed!'
72
+ abort("STDERR:\n#{install_result[:stderr]}")
73
+ else
74
+ puts " |+ Gem installed!"
75
+ end
76
+ end
77
+
78
+ # Add ssh-keygen function, then update
79
+ def self.push_deployment(connector, options)
80
+ puts "\n > Pushing deployment: #{options[:deploy_file]}"
81
+ push_result = DanarchyDeploy::Helpers.run_command("rsync --rsh 'ssh -i #{connector[:ssh_key]}' -Havzu --delete #{options[:working_dir]}/ #{connector[:ssh_user]}@#{connector[:ipv4]}:#{options[:working_dir]}/", options)
82
+ # push_result = DanarchyDeploy::Helpers.run_command("scp -i #{connector[:ssh_key]} #{options[:deploy_file]} #{connector[:ssh_user]}@#{connector[:ipv4]}:#{options[:deploy_dir]}/#{connector[:hostname]}/", options)
83
+
84
+ if push_result[:stderr]
85
+ puts ' ! Deployment push failed!'
86
+ abort("STDERR:\n#{push_result[:stderr]}")
87
+ else
88
+ puts " |+ Deployment pushed!"
89
+ end
90
+ end
91
+
92
+ def self.remote_LocalDeploy(connector, options)
93
+ puts "\n > Running LocalDeploy on #{connector[:hostname]}\n\tOutput will return at the end of deployment."
94
+
95
+ deployment = "ssh -i #{connector[:ssh_key]} #{connector[:ssh_user]}@#{connector[:ipv4]} 'sudo danarchy_deploy local "
96
+ deployment += '--first-run ' if options[:first_run]
97
+ deployment += '--ssh-verbose ' if options[:ssh_verbose]
98
+ deployment += '--pretend ' if options[:pretend]
99
+ deployment += '--json ' if options[:deploy_file].end_with?('.json')
100
+ deployment += '--yaml ' if options[:deploy_file].end_with?('.yaml')
101
+ deployment += options[:deploy_file] + '\''
102
+
103
+ DanarchyDeploy::Helpers.run_command(deployment, options)
104
+ end
105
+
106
+ def self.pull_deployment(connector, options)
107
+ puts "\n > Pulling deployment: #{options[:deploy_file]}"
108
+ pull_result = DanarchyDeploy::Helpers.run_command("scp -i #{connector[:ssh_key]} #{connector[:ssh_user]}@#{connector[:ipv4]}:#{options[:deploy_file]} #{options[:deploy_file]}", options)
109
+
110
+ if pull_result[:stderr]
111
+ puts ' ! Deployment pull failed!'
112
+ abort("STDERR:\n#{pull_result[:stderr]}")
113
+ else
114
+ puts " |+ Deployment pulled!"
115
+ end
116
+ end
117
+
118
+ def self.remote_Cleanup(connector, options)
119
+ puts "\n > Cleaning up: #{options[:deploy_dir]}"
120
+ cleanup_result = DanarchyDeploy::Helpers.run_command("ssh -i #{connector[:ssh_key]} #{connector[:ssh_user]}@#{connector[:ipv4]} 'sudo rm -rfv #{options[:working_dir]}'", options)
121
+
122
+ if cleanup_result[:stderr]
123
+ puts ' ! Deployment cleanup failed!'
124
+ abort("STDERR:\n#{cleanup_result[:stderr]}")
125
+ else
126
+ puts " |+ Deployment cleaned up!"
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,70 @@
1
+
2
+ module DanarchyDeploy
3
+ class Archiver
4
+ def self.new(archives, options)
5
+ puts "\n" + self.name
6
+
7
+ archives.each do |archive|
8
+ abort("No target destination set for archive: #{archive[:source]}!") if !archive[:target]
9
+
10
+ tmparchive = false
11
+ if !archive[:source]
12
+ archive[:source] = options[:deploy_dir] + "/.tmp_archive_#{DateTime.now.strftime("%Y%m%d_%H%M%S")}"
13
+ tmparchive = true
14
+ end
15
+
16
+ if archive[:data]
17
+ data = DanarchyDeploy::Helpers.decode_base64(archive[:data])
18
+ write_tmp_archive(archive[:source], data)
19
+ end
20
+
21
+ puts " > Extracting #{archive[:source]} to #{archive[:target]}"
22
+
23
+ if !File.exist?(archive[:source])
24
+ puts " ! Source file not found!: #{archive[:source]}"
25
+ return false
26
+ end
27
+
28
+ Dir.exist?(archive[:target]) || FileUtils.mkdir_p(archive[:target])
29
+ command = prep_extraction(archive, options)
30
+ archive_result = DanarchyDeploy::Helpers.run_command(command, options)
31
+
32
+ if archive_result[:stderr]
33
+ puts ' ! Archive extraction failed!'
34
+ abort("STDERR:\n#{archive_result[:stderr]}")
35
+ else
36
+ puts " |+ Archive extracted: #{archive[:source]}\n"
37
+ end
38
+
39
+ cleanup_source_archive(archive[:source]) if tmparchive
40
+ end
41
+ end
42
+
43
+ private
44
+ def self.prep_extraction(archive, options)
45
+ file_type = `file #{archive[:source]}`
46
+ command = 'tar xvf ' if file_type.include?('POSIX tar archive')
47
+ command = 'tar xvfj ' if file_type.include?('bzip2 compressed data')
48
+ command = 'tar xvfz ' if file_type.include?('gzip compressed data')
49
+ command = 'unzip ' if file_type.include?('Zip archive data')
50
+
51
+ if options[:pretend]
52
+ command = command.gsub(/x/, 't') if command.start_with?('tar')
53
+ command += '-t ' if command.start_with?('unzip')
54
+ end
55
+
56
+ command += archive[:source]
57
+ command += " -C #{archive[:target]}" if command.start_with?('tar')
58
+ command += " -d #{archive[:target]}" if command.start_with?('unzip')
59
+ command
60
+ end
61
+
62
+ def self.write_tmp_archive(source, data)
63
+ File.write(source, data)
64
+ end
65
+
66
+ def self.cleanup_source_archive(source)
67
+ File.delete(source)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module DanarchyDeploy
3
+ class Groups
4
+ def self.new(deployment, options)
5
+ puts "\n" + self.name
6
+ (groupadd_result, groupdel_result) = nil
7
+
8
+ deployment[:groups].each do |group|
9
+ puts " > Checking if group '#{group[:groupname]}' already exists."
10
+ groupcheck_result = groupcheck(group, options)
11
+
12
+ if groupcheck_result[:stdout]
13
+ puts " - Group: #{group[:groupname]} already exists!"
14
+ else
15
+ puts " |+ Adding group: #{group[:groupname]}"
16
+ groupadd_result = groupadd(group, options)
17
+ end
18
+ end
19
+
20
+ # [groupadd_result, groupdel_result]
21
+ deployment
22
+ end
23
+
24
+ private
25
+ def self.groupadd(group, options)
26
+ groupadd_cmd = "groupadd #{group[:groupname]} "
27
+ groupadd_cmd += "--gid #{group[:gid]} " if group[:gid]
28
+ groupadd_cmd += "--system " if group[:system]
29
+ if options[:pretend]
30
+ puts "\tFake run: #{groupadd_cmd}"
31
+ else
32
+ DanarchyDeploy::Helpers.run_command(groupadd_cmd, options)
33
+ end
34
+ end
35
+
36
+ def self.groupdel(group, options)
37
+ groupdel_cmd = "groupdel #{group[:groupname]}"
38
+ if options[:pretend]
39
+ puts "\tFake run: #{groupdel_cmd}"
40
+ else
41
+ DanarchyDeploy::Helpers.run_command(groupdel_cmd, options)
42
+ end
43
+ end
44
+
45
+ def self.groupcheck(group, options)
46
+ DanarchyDeploy::Helpers.run_command("/usr/bin/getent group #{group[:groupname]}", options)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,28 @@
1
+ require 'open3'
2
+
3
+ module DanarchyDeploy
4
+ class Helpers
5
+ def self.run_command(command, options)
6
+ pid, stdout, stderr = nil
7
+ printf("%14s %0s\n", 'Running:', "#{command}")
8
+ Open3.popen3(command) do |i, o, e, t|
9
+ pid = t.pid
10
+ (out, err) = o.read, e.read
11
+ stdout = !out.empty? ? out : nil
12
+ stderr = !err.empty? ? err : nil
13
+ end
14
+
15
+ if options[:ssh_verbose]
16
+ puts "------\nSTDOUT: ", stdout, '------' if stdout
17
+ puts "------\nSTDERR: ", stderr, '------' if stderr
18
+ end
19
+
20
+ { pid: pid, stdout: stdout, stderr: stderr }
21
+ end
22
+
23
+ def self.decode_base64(data)
24
+ require 'base64'
25
+ Base64.decode64(data)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,86 @@
1
+
2
+ module DanarchyDeploy
3
+ class Installer
4
+ def self.new(deployment, options)
5
+ # available opts: { pretend: true|false }
6
+ abort('Operating System not defined! Exiting!') if !deployment[:os]
7
+ puts "\n" + self.name
8
+
9
+ os = deployment[:os]
10
+ packages = deployment[:packages]
11
+ installer, updater, cleaner, packages = prep_operating_system(os, packages, options)
12
+ install_result = nil
13
+ if packages
14
+ install_result = DanarchyDeploy::Helpers.run_command("#{installer} #{packages}", options)
15
+ else
16
+ puts 'No packages to install.'
17
+ end
18
+
19
+ if !options[:pretend]
20
+ puts "\nRunning system updates..."
21
+ updater_result = DanarchyDeploy::Helpers.run_command(updater, options)
22
+ puts updater_result[:stdout] if updater_result[:stdout]
23
+ puts "\nCleaning up unused packages..."
24
+ cleanup_result = DanarchyDeploy::Helpers.run_command(cleaner, options)
25
+ puts cleanup_result[:stdout] if cleanup_result[:stdout]
26
+ end
27
+
28
+ # [install_result, cleanup_result, updater_result]
29
+ deployment
30
+ end
31
+
32
+ private
33
+ def self.prep_operating_system(os, packages, options)
34
+ (installer, updater, cleaner) = nil
35
+ if os.downcase == 'gentoo'
36
+ puts "#{os.capitalize} detected! Using emerge."
37
+
38
+ if packages
39
+ installer = 'emerge --usepkg --quiet --noreplace '
40
+ installer += '--pretend ' if options[:pretend]
41
+
42
+ packages.each do |pkg|
43
+ IO.popen("qlist -I #{pkg}") do |o|
44
+ packages.delete(pkg) if !o.read.empty?
45
+ end
46
+ end
47
+ end
48
+
49
+ cleaner = 'emerge --depclean --quiet '
50
+ cleaner += '--pretend ' if options[:pretend]
51
+
52
+ updater = 'emerge --update --deep --newuse --quiet --with-bdeps=y @world'
53
+ updater += ' --pretend' if options[:pretend]
54
+ elsif %w[debian ubuntu].include?(os.downcase)
55
+ puts "#{os.capitalize} detected! Using apt."
56
+ installer = 'export DEBIAN_FRONTEND=noninteractive ; apt install -y -qq '
57
+ installer += '--dry-run ' if options[:pretend]
58
+ updater = 'apt-get upgrade -y -qq'
59
+ cleaner = 'apt-get autoclean -y -qq'
60
+ elsif os.downcase == 'opensuse'
61
+ puts "#{os.capitalize} detected! Using zypper."
62
+ installer = 'zypper install '
63
+ updater = nil
64
+ cleaner = nil
65
+ # Needs package checking & testing
66
+ elsif %w[centos redhat].include?(os.downcase)
67
+ # needs more testing
68
+ puts "#{os.capitalize} detected! Using yum."
69
+ if packages
70
+ installer = 'yum install -y '
71
+
72
+ packages.each do |pkg|
73
+ IO.popen("rpm -q #{pkg}") do |o|
74
+ packages.delete(pkg) if !o.read.include?('not installed')
75
+ end
76
+ end
77
+ end
78
+
79
+ updater = nil
80
+ cleaner = nil
81
+ end
82
+
83
+ [installer, updater, cleaner, packages.join(' ')]
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,51 @@
1
+
2
+ module DanarchyDeploy
3
+ class Services
4
+ def self.new(deployment, options)
5
+ puts "\n" + self.name
6
+
7
+ deployment[:services].each do |service, params|
8
+ puts "Configuring service: #{service}"
9
+
10
+ if params[:templates] && !params[:templates].empty?
11
+ puts " > COnfiguring templates for #{service}"
12
+ DanarchyDeploy::Templater.new(params[:templates], options)
13
+ end
14
+
15
+ if params[:archives] && !params[:archives].empty?
16
+ puts "\n" + self.name
17
+ puts " > Deploying archives for #{service}"
18
+ DanarchyDeploy::Archiver.new(params[:archives], options)
19
+ end
20
+ end
21
+
22
+ deployment
23
+ end
24
+
25
+ private
26
+ def self.init(deployment, options)
27
+ puts "\n" + self.name
28
+
29
+ deployment[:services].each do |service, params|
30
+ next if !params[:init]
31
+ if options[:first_run] == false
32
+ puts " ! Not a first-time run! Setting actions to 'reload'.\n\tUse --first-run to run actions: #{params[:init].join(' ,')}\n"
33
+ params[:init] = ['reload']
34
+ end
35
+
36
+ params[:init].each do |action|
37
+ puts " > Taking action: #{action} on #{service}"
38
+ command = "systemctl #{action} #{service}"
39
+
40
+ if options[:pretend]
41
+ puts " Fake run: #{command}\n"
42
+ else
43
+ DanarchyDeploy::Helpers.run_command(command, options)
44
+ end
45
+ end
46
+ end
47
+
48
+ deployment
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,164 @@
1
+ require 'erb'
2
+ require 'fileutils'
3
+
4
+ module DanarchyDeploy
5
+ class Templater
6
+ def self.new(templates, options)
7
+ puts "\n" + self.name
8
+
9
+ templates.each do |template|
10
+ abort("No target destination set for template: #{template[:source]}!") if !template[:target]
11
+ abort("No source or data for template: #{template[:target]}") if !template[:source] && !template[:data]
12
+
13
+ target = template[:target]
14
+ source = template[:source] || '-- encoded data --'
15
+ dir_perms = template[:dir_perms]
16
+ file_perms = template[:file_perms]
17
+ @variables = template[:variables] || nil
18
+ puts "\n > Checking: #{target}"
19
+ puts " Source: #{source}"
20
+ puts " |> Dir Permissions: #{dir_perms || '--undefined--'}"
21
+ puts " |> File Permissions: #{file_perms || '--undefined--'}"
22
+ puts " |> Variables: #{@variables}" if @variables
23
+
24
+ targetdir = File.dirname(target)
25
+ tmpdir = options[:deploy_dir] + '/' + File.basename(File.dirname(target))
26
+ p tmpdir
27
+ Dir.exist?(targetdir) || FileUtils.mkdir_p(targetdir, mode: 0755)
28
+ Dir.exist?(tmpdir) || FileUtils.mkdir_p(tmpdir, mode: 0755)
29
+ tmpfile = tmpdir + '/' + File.basename(target) + '.tmp'
30
+
31
+ if source == '-- encoded data --'
32
+ data = DanarchyDeploy::Helpers.decode_base64(template[:data])
33
+ source = tmpfile + '.erb'
34
+ write_tmpfile(source, data)
35
+ end
36
+
37
+ File.open(tmpfile, 'w') do |f|
38
+ result = ERB.new(File.read(source)).result(binding)
39
+ f.write(result)
40
+ f.close
41
+ end
42
+
43
+ result = { write_erb: [], verify_permissions: {} }
44
+ if options[:pretend]
45
+ diff(target, tmpfile)
46
+ puts "\n - Fake Run: Not changing '#{target}'."
47
+ result[:verify_permissions][File.dirname(tmpfile)] = verify_permissions(File.dirname(tmpfile), dir_perms, options)
48
+ result[:verify_permissions][tmpfile] = verify_permissions(tmpfile, file_perms, options)
49
+ elsif md5sum(target,tmpfile) == true
50
+ puts "\n - No change in '#{target}': Nothing to update here."
51
+ result[:verify_permissions][File.dirname(target)] = verify_permissions(File.dirname(target), dir_perms, options)
52
+ result[:verify_permissions][target] = verify_permissions(target, file_perms, options)
53
+ else
54
+ diff(target, tmpfile)
55
+ result[:write_erb] = enable_erb(target, tmpfile)
56
+ puts " => #{target} was updated!"
57
+ result[:verify_permissions][File.dirname(target)] = verify_permissions(File.dirname(target), dir_perms, options)
58
+ result[:verify_permissions][target] = verify_permissions(target, file_perms, options)
59
+ end
60
+
61
+ FileUtils.rm_rf(File.dirname(tmpfile))
62
+ result
63
+ end
64
+ end
65
+
66
+ private
67
+ def self.verify_permissions(target, perms, options)
68
+ (owner, group, mode) = nil
69
+ chmod = nil
70
+ puts "\n > Verifying ownership and permissions for '#{target}'"
71
+ if perms
72
+ (owner, group, mode) = perms[:owner], perms[:group], perms[:mode]
73
+ else
74
+ if File.stat(target).mode & 07777 == '0777'.to_i(8)
75
+ puts " ! '#{target}' has 0777 permissions! Setting those to something more sane."
76
+ if File.ftype(target) == 'directory'
77
+ puts " |+ Setting file mode to: 0775"
78
+ chmod = File.chmod(0775, target) ? true : false if !options[:pretend]
79
+ elsif File.ftype(target) == 'file'
80
+ puts " |+ Setting file mode to: 0644"
81
+ chmod = File.chmod(0644, target) ? true : false if !options[:pretend]
82
+ end
83
+ else
84
+ puts " - Permissions were not defined for '#{target}'! Leaving them alone."
85
+ end
86
+
87
+ return [chmod]
88
+ end
89
+
90
+ (owner, uid, group, gid) = check_user_group(owner, group)
91
+
92
+ updated = []
93
+ file_stat = File.stat(target)
94
+ if file_stat.uid != uid || file_stat.gid != gid
95
+ puts " |+ Setting ownership to: #{owner}:#{group}"
96
+ chown = File.chown(uid, gid, target) ? true : false if !options[:pretend]
97
+ updated.push(chown)
98
+ end
99
+
100
+ if file_stat.mode & 07777 != mode.to_i(8)
101
+ puts " |+ Setting file mode to: #{mode}"
102
+ chmod = File.chmod(mode.to_i(8), target) ? true : false if !options[:pretend]
103
+ updated.push(chmod)
104
+ end
105
+
106
+ updated
107
+ end
108
+
109
+ def self.check_user_group(owner, group)
110
+ (uid, gid) = nil
111
+
112
+ IO.popen("id -u #{owner} 2>/dev/null") do |id|
113
+ uid = id.gets
114
+ if uid == nil
115
+ puts " ! User: #{owner} not found! Using: 'root'"
116
+ owner = 'root'
117
+ uid = 0
118
+ end
119
+ uid = uid.chomp.to_i if uid.class == String
120
+ end
121
+
122
+ IO.popen("id -g #{group} 2>/dev/null") do |id|
123
+ gid = id.gets
124
+ if gid == nil
125
+ puts " ! Group: #{group} not found! Using: 'root'"
126
+ group = 'root'
127
+ gid = 0
128
+ end
129
+ gid = gid.chomp.to_i if gid.class == String
130
+ end
131
+
132
+ [owner, uid, group, gid]
133
+ end
134
+
135
+ def self.md5sum(target, tmpfile)
136
+ if File.exist?(target) && File.exist?(tmpfile)
137
+ FileUtils.identical?(target, tmpfile)
138
+ elsif File.exist?(target) && !File.exist?(tmpfile)
139
+ return false
140
+ end
141
+ end
142
+
143
+ def self.diff(target, tmpfile)
144
+ if File.exist?(target) && File.exist?(tmpfile)
145
+ puts "\n !! Diff between #{target} <=> #{tmpfile}"
146
+ IO.popen("diff -Naur #{target} #{tmpfile}") do |o|
147
+ puts o.read
148
+ end
149
+ puts "\n !! End Diff \n\n"
150
+ elsif File.exist?(target) && !File.exist?(tmpfile)
151
+ return false
152
+ end
153
+ end
154
+
155
+ def self.enable_erb(target, tmpfile)
156
+ puts "\n |+ Moving #{tmpfile} => #{target}"
157
+ system("mv #{tmpfile} #{target}")
158
+ end
159
+
160
+ def self.write_tmpfile(source, data)
161
+ File.write(source, data)
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,138 @@
1
+
2
+ module DanarchyDeploy
3
+ class Users
4
+ def self.new(deployment, options)
5
+ puts "\n" + self.name
6
+ (useradd_result, userdel_result, archives_result) = nil
7
+
8
+ deployment[:users].each do |user|
9
+ puts " > Checking if user '#{user[:username]}' already exists."
10
+ usercheck_result = usercheck(user, options)
11
+
12
+ if usercheck_result[:stdout]
13
+ puts " - User: #{user[:username]} already exists!"
14
+ else
15
+ group = { groupname: user[:username] }
16
+ group[:gid] = user[:gid] ? user[:gid] : nil
17
+ group[:system] = user[:system] ? user[:system] : nil
18
+
19
+ groupcheck_result = DanarchyDeploy::Groups.groupcheck(group, options)
20
+ if !groupcheck_result[:stdout] && group[:gid]
21
+ puts " |+ Adding group: #{group[:groupname]}"
22
+ DanarchyDeploy::Groups.groupadd(group, options)
23
+ end
24
+
25
+ puts " |+ Adding user: #{user[:username]}"
26
+ useradd_result = useradd(user, options)
27
+ end
28
+
29
+ if !options[:pretend]
30
+ puts "\n > Checking groups for user: #{user[:username]}"
31
+ if user[:groups] && checkgroups(usercheck_result, user, options) == false
32
+ updategroups(user, options)
33
+ puts " |+ Updated groups: #{user[:groups].join(',')}"
34
+ else
35
+ puts " - No change to groups needed."
36
+ end
37
+
38
+ if user[:authorized_keys]
39
+ puts "\n > Checking on #{user[:authorized_keys].count} authorized_keys for user: #{user[:username]}"
40
+ authorized_keys(user)
41
+ end
42
+
43
+ if user[:sudoer]
44
+ puts "\n > Checking sudo rules for user: #{user[:username]}"
45
+ sudoer(user)
46
+ end
47
+ end
48
+
49
+ if user[:archives] && !user[:archives].empty?
50
+ puts " > Deploying archives for #{user[:username]}"
51
+ DanarchyDeploy::Archiver.new(user[:archives], options)
52
+ end
53
+ end
54
+
55
+ # [useradd_result, userdel_result]
56
+ deployment
57
+ end
58
+
59
+ private
60
+ def self.useradd(user, options)
61
+ useradd_cmd = "useradd #{user[:username]} "
62
+ useradd_cmd += "--home-dir #{user[:home]} " if user[:home]
63
+ useradd_cmd += "--uid #{user[:uid]} " if user[:uid]
64
+ useradd_cmd += "--gid #{user[:gid]} " if user[:gid]
65
+ useradd_cmd += "--groups #{user[:groups].join(',')} " if user[:groups]
66
+ useradd_cmd += "--shell /sbin/nologin " if user[:nologin]
67
+ useradd_cmd += "--system " if user[:system]
68
+ if options[:pretend]
69
+ puts "\tFake run: #{useradd_cmd}"
70
+ else
71
+ DanarchyDeploy::Helpers.run_command(useradd_cmd, options)
72
+ end
73
+ end
74
+
75
+ def self.userdel(user, options)
76
+ userdel_cmd = "userdel --remove #{user[:username]}"
77
+ if options[:pretend]
78
+ puts "\tFake run: #{userdel_cmd}"
79
+ else
80
+ DanarchyDeploy::Helpers.run_command(userdel_cmd, options)
81
+ end
82
+ end
83
+
84
+ def self.usercheck(user, options)
85
+ DanarchyDeploy::Helpers.run_command("id #{user[:username]}", options)
86
+ end
87
+
88
+ def self.checkgroups(usercheck_result, user, options)
89
+ return nil if !usercheck_result[:stdout]
90
+ livegroups = usercheck_result[:stdout].split(/\s+/).last.split('=').last.gsub(/\(([^)]*)\)/, '').split(',').map(&:to_i)
91
+ livegroups.delete(user[:gid])
92
+ livegroups.sort == user[:groups].sort
93
+ end
94
+
95
+ def self.updategroups(user, options)
96
+ groups = user[:groups].join(',')
97
+ groupupdate_cmd = "usermod #{user[:username]} --groups #{groups}"
98
+ if options[:pretend]
99
+ puts "\tFake run: #{groupupdate_cmd}"
100
+ else
101
+ DanarchyDeploy::Helpers.run_command(groupupdate_cmd, options)
102
+ end
103
+ end
104
+
105
+ def self.authorized_keys(user)
106
+ ssh_path = user[:home] + '/.ssh'
107
+ authkeys = ssh_path + '/authorized_keys'
108
+
109
+ Dir.exist?(ssh_path) || Dir.mkdir(ssh_path)
110
+ File.open(authkeys, 'a+') do |f|
111
+ user[:authorized_keys].each do |authkey|
112
+ if !f.read.include?(authkey)
113
+ puts " + Adding authorized_key: #{authkey}"
114
+ f.puts authkey
115
+ else
116
+ puts ' - No change needed'
117
+ end
118
+ end
119
+
120
+ f.close
121
+ end
122
+ end
123
+
124
+ def self.sudoer(user)
125
+ sudoer_file = '/etc/sudoers.d/danarchy_deploy-' + user[:username]
126
+ File.open(sudoer_file, 'r+') do |f|
127
+ if !f.read.include?(user[:sudoer])
128
+ puts " |+ Added: '#{user[:sudoer]}'"
129
+ f.puts user[:sudoer]
130
+ else
131
+ puts ' - No change needed'
132
+ end
133
+
134
+ f.close
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,3 @@
1
+ module DanarchyDeploy
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,98 @@
1
+ {
2
+ "hostname": "hostname",
3
+ "os": "gentoo|debian|ubuntu",
4
+ "ipv4": "IPv4 to use for deployment",
5
+ "ssh_user": "deploy-user",
6
+ "ssh_key": "/home/path/to/deploy-user/ssh_key.",
7
+ "packages": [
8
+ "package1",
9
+ "package2"
10
+ ],
11
+ "users": [
12
+ {
13
+ "username": "username",
14
+ "home": "/home/username",
15
+ "uid": int,
16
+ "gid": int,
17
+ "sudoer": "username ALL = NOPASSWD: ALL",
18
+ "ssh-authorized_keys": [
19
+ "ssh-ed25519 it0C5o6GHC8lxqctpexakfdA5o7LeSe+QbMhIl+GYtZ2OCMFliLsODDrrazR+u2y user@hostname",
20
+ "ssh-rsa K0APeEvotGunpBrl/LvSAG/gLUldCnOrL60v47QYjuqoGJmM3Fk8V29+8jZPp9Dl user@hostname"
21
+ ],
22
+ "groups": [
23
+ int,
24
+ int
25
+ ],
26
+ "archives": [
27
+ {
28
+ "target": "/path/to/extract/to/",
29
+ "source": "/path/to/tarball.(tar.{gz,bz2}|zip)"
30
+ },
31
+ {
32
+ "target": "/path/to/extract/to/",
33
+ "data": "couchdb:base64_encoded_data"
34
+ }
35
+ ]
36
+ }
37
+ ],
38
+ "groups": [
39
+ {
40
+ "groupname": "groupname",
41
+ "gid": int,
42
+ }
43
+ ],
44
+ "services": {
45
+ "service_name": {
46
+ "init": {
47
+ "service": [
48
+ "enable",
49
+ "restart"
50
+ ]
51
+ }
52
+ "archives": [
53
+ {
54
+ "target": "/path/to/extract/to/",
55
+ "source": "/path/to/tarball.(tar.{gz,bz2}|zip)"
56
+ },
57
+ {
58
+ "target": "/path/to/extract/to/",
59
+ "data": "couchdb:base64_encoded_data"
60
+ }
61
+ ],
62
+ "templates": [
63
+ {
64
+ "target": "/path/to/target/file",
65
+ "source": "/path/to/source/erb",
66
+ "dir_perms": {
67
+ "owner": "username",
68
+ "group": "groupname",
69
+ "mode": "0755"
70
+ },
71
+ "file_perms": {
72
+ "owner": "username",
73
+ "group": "groupname",
74
+ "mode": "0644"
75
+ },
76
+ "variables": {
77
+ "var1": "value",
78
+ "var2": "value"
79
+ }
80
+ },
81
+ {
82
+ "target": "/path/to/target/file",
83
+ "data": "couchdb:base64_encoded_erb",
84
+ "dir_perms": {
85
+ "owner": "username",
86
+ "group": "groupname",
87
+ "mode": "0755"
88
+ },
89
+ "file_perms": {
90
+ "owner": "username",
91
+ "group": "groupname",
92
+ "mode": "0644"
93
+ }
94
+ }
95
+ ]
96
+ }
97
+ }
98
+ }
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: danarchy_deploy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dan James
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: danarchy_couchdb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.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: 0.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: 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: rspec
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
+ description: DanarchyDeploy intends to simplify Gentoo Linux (and other distro) deployments
70
+ down to a single template from an input JSON or YAML file, or from a CouchDB file.
71
+ email:
72
+ - dan@danarchy.me
73
+ executables:
74
+ - danarchy_deploy
75
+ - danarchy_deploy-console
76
+ - setup
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - ".gitignore"
81
+ - ".rspec"
82
+ - ".travis.yml"
83
+ - Gemfile
84
+ - Gemfile.lock
85
+ - LICENSE.txt
86
+ - README.md
87
+ - Rakefile
88
+ - bin/danarchy_deploy
89
+ - bin/danarchy_deploy-console
90
+ - bin/setup
91
+ - danarchy_deploy.gemspec
92
+ - lib/danarchy_deploy.rb
93
+ - lib/danarchy_deploy/archiver.rb
94
+ - lib/danarchy_deploy/groups.rb
95
+ - lib/danarchy_deploy/helpers.rb
96
+ - lib/danarchy_deploy/installer.rb
97
+ - lib/danarchy_deploy/services.rb
98
+ - lib/danarchy_deploy/templater.rb
99
+ - lib/danarchy_deploy/users.rb
100
+ - lib/danarchy_deploy/version.rb
101
+ - templates/deploy_template.json
102
+ homepage: https://github.com/danarchy85/danarchy_deploy
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ allowed_push_host: https://rubygems.org
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.6.14
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Pushes deployments locally or remotely based on a JSON/YAML/CouchDB template.
127
+ test_files: []