doable 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f3b42f3e5a03262223ec145feb55b84a0c0d4d1a
4
- data.tar.gz: efe266d164608fa68728e7198b76aaae55f61c7f
3
+ metadata.gz: 8049e12988f84644e8ad77af4aa81c69586cd584
4
+ data.tar.gz: cdde8ea4ff303b1cdd1407ce2a45f4155fad67f4
5
5
  SHA512:
6
- metadata.gz: a8209389c4938a5ae05c0550d65301e7e9e194087933d2d8e07025ec54bad97d02f9335ebabee8096d28e06e7089591032a4f4f5ab6f2327a3442d361d96e817
7
- data.tar.gz: 665e86ed5616a54061eb333aef7e89260e9a11f4b657e2c5bc592aa0433a8396746616acaa15423f8da54208c1407eee5fd01a0f79d433b723ef7c3f7a73e792
6
+ metadata.gz: 8e5e57273ae3eb3a16aafa7992a2513bc7eefada445a1feac54d53e55a4633001eeed83b7a7967a3dbced96d7868a7fb041db469c5ed968763c4023a2a2f8af4
7
+ data.tar.gz: e13885ee5299ae731b16a9d1c83ec4868994a66cd2ed57e5bd0a8372b7758a3645e12c95e5ad4d0eab946e84d42c71af7da200b6d41caef7ccf20bebc9f56074
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Jonathan Gnagy <jonathan.gnagy@gmail.com>
1
+ Copyright (c) 2015 Jonathan Gnagy <jonathan.gnagy@gmail.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ Ruby Doable Framework
2
+ =====================
3
+
4
+ About
5
+ -----
6
+ This is a gem largely targeted and making the lives of DevOps / SysAdmin types easier. Whether it is installing complicated sets of software, writing maintenance scripts, or automating administrative tasks, `Doable` aims to simplify these common tasks, as well as make them more reliable and robust.
7
+
8
+ The core set of values for this framework are:
9
+
10
+ * Extensibility
11
+ * Modularity
12
+ * Simplicity
13
+ * Concurrency
14
+ * Flexibility
15
+ * Reliability
16
+ * Remaining lightweight
17
+
18
+ These goals are largely achieved through a small set of core features, with nearly all functionality broken out into plugins that can be enabled as required.
19
+
20
+ Building
21
+ -----
22
+
23
+ The eventual goal is for this gem to simply be available via a normal rubygems search, but until then, you must build and install the gem yourself. This can be done like so:
24
+
25
+ #!bash
26
+ # need Mercurial to clone the repo... or download it from https://bitbucket.org/jgnagy/doable/get/tip.zip
27
+ hg clone https://bitbucket.org/jgnagy/doable
28
+ cd doable
29
+ # highly recommend using RVM here, and Ruby 2.x or above
30
+ gem build doable.gemspec
31
+ # install what you just built
32
+ gem install ./doable-*.gem
33
+
34
+ Sometimes you might get lucky and there's a semi-recent version of the pre-built gem available on bitbucket [here](https://bitbucket.org/jgnagy/doable/downloads).
35
+
36
+ Usage
37
+ -----
38
+
39
+ Require the right gem(s):
40
+
41
+ #!ruby
42
+ require 'doable'
43
+
44
+ To create a simple job, define a plan:
45
+
46
+ #!ruby
47
+ job = Doable::Job.plan do |j|
48
+ j.before { log "Starting my awesome job" }
49
+ j.step { # do some stuff here }
50
+ j.attempt { # try to do some other stuff here }
51
+ j.after { log "Looks like we're all set" }
52
+ end
53
+
54
+ job.run # executes our job
55
+
56
+ This job is obviously pretty boring, but it shows some of the basics. Now let's continue and include some additional helpers in a new, more complex job:
57
+
58
+ #!ruby
59
+ require 'doable/helpers/password'
60
+
61
+ class JobWithPasswords < Doable::Job
62
+ include Doable::Helpers::Password
63
+ end
64
+
65
+ job2 = JobWithPasswords.plan do |j|
66
+ j.before { log "Here's a password for you..." }
67
+ j.step { puts "Password: " + generate_password(16) }
68
+ end
69
+
70
+ job2.run
71
+
72
+ After this job runs, you should see output that looks something like:
73
+
74
+ [2015/01/29 00:10:36] Here's a password for you...
75
+ Password: ZwyIVH8LjHGYYzDX
76
+ [2015/01/29 00:10:36] All Job steps completed successfully!
77
+
78
+ Hopefully this demonstrates how to get access to additional helper methods.
79
+
80
+ License
81
+ -------
82
+
83
+ Doable is distributed under the MIT License.
84
+
85
+ Contributing
86
+ ------------
87
+
88
+ I welcome pull-requests. I may or may not use your code, but I encourage the growth of others too. If this project inspires you to contribute, feel free to fork my code and submit a pull request. In most cases, I'll probably recommend you package additions in an extension gem rather than including new files, etc, directly into the core codebase.
@@ -1,6 +1,6 @@
1
1
  module Doable
2
2
  module Exceptions
3
- module FrameworkExceptions
3
+ module Framework
4
4
  class GenericFrameworkError < StandardError
5
5
  end
6
6
 
@@ -0,0 +1,23 @@
1
+ module Doable
2
+ module Exceptions
3
+ module OS
4
+ class OSException < GenericFrameworkError
5
+ end
6
+
7
+ class WrongUser < OSException
8
+ end
9
+
10
+ class MissingDirectory < OSException
11
+ end
12
+
13
+ class DirectoryExists < OSException
14
+ end
15
+
16
+ class InvalidHostname < OSException
17
+ end
18
+
19
+ class MissingFile < OSException
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,6 @@
1
1
  module Doable
2
2
  module Helpers
3
- module FrameworkHelpers
3
+ module Framework
4
4
  # raises a special skip exception and __must__ only be run in job steps
5
5
  # @raise [SkipStep] Do __not__ handle this exception!
6
6
  def skip(message = "Skipping Step")
@@ -1,10 +1,9 @@
1
1
  module Doable
2
+ # Create a mutex to manage logging to STDOUT
3
+ LOGGING_MUTEX = Mutex.new
4
+ LOGGING_MUTEX.freeze
2
5
  module Helpers
3
- module LoggingHelpers
4
- # Create a mutex to manage logging to STDOUT
5
- LOGGING_MUTEX = Mutex.new
6
- LOGGING_MUTEX.freeze
7
-
6
+ module Logging
8
7
  # Applies a color (with optional addition settings) to some text
9
8
  # @return [String]
10
9
  # @param text [String] The String to be colorized
@@ -0,0 +1,129 @@
1
+ require 'doable/exceptions/os'
2
+
3
+ module Doable
4
+ module Helpers
5
+ module OS
6
+ include Doable::Exceptions::OS
7
+ # Confirms the current script is running as a certain user
8
+ # @raise [WrongUser]
9
+ # @param user [String] User to confirm we're using
10
+ def must_run_as(user)
11
+ raise WrongUser, user unless user == Etc.getpwuid.name
12
+ log "Running as correct user"
13
+ end
14
+
15
+ # Ensure a directory exists
16
+ # @param directory [String] Directory to create or verify
17
+ # @return [String]
18
+ def find_or_create_directory(directory)
19
+ full_path = File.expand_path(directory)
20
+ FileUtils.mkdir_p(full_path) unless File.directory?(full_path)
21
+ return full_path
22
+ end
23
+
24
+ # Die if a directory doesn't exist
25
+ # @raise [MissingDirectory]
26
+ # @param directory [String] Directory to verify
27
+ # @return [String]
28
+ def find_directory(directory)
29
+ full_path = File.expand_path(directory)
30
+ raise MissingDirectory, directory unless File.directory?(full_path)
31
+ return full_path
32
+ end
33
+
34
+ # Die if a directory __does__ exist.
35
+ # Useful for ensuring software wasn't previously installed, etc.
36
+ # @raise [DirectoryExists]
37
+ # @param directory [String] Directory to verify does not exist
38
+ # @return [Boolean]
39
+ def die_if_dir_exists(directory)
40
+ full_path = File.expand_path(directory)
41
+ if File.directory?(full_path)
42
+ raise DirectoryExists, directory
43
+ else
44
+ return true
45
+ end
46
+ end
47
+
48
+ # Remove a file or directory
49
+ # @param file_or_directory [String] File or directory path to remove
50
+ def remove(file_or_directory)
51
+ FileUtils.rm_rf(File.expand_path(file_or_directory))
52
+ end
53
+
54
+ # Check that the OS can resolve a hostname
55
+ # @raise [InvalidHostname]
56
+ # @param hostname [String] Hostname to validate is resolveable
57
+ def validate_host(hostname)
58
+ raise InvalidHostname, hostname unless Resolv.getaddress(hostname)
59
+ end
60
+
61
+ # Ruby in-memory equivalent of `tee`
62
+ # Note that this command is dangerous for very long command output. Need to consider setting a buffer max size for this.
63
+ # @param command [String] Command to run
64
+ # @return [Fixnum, String] Returns the exit code of the command, along with any output
65
+ def tee(command)
66
+ command_output = ""
67
+ IO.popen(command) do |io|
68
+ while line = io.gets
69
+ command_output << line
70
+ LOGGING_MUTEX.synchronize { puts line }
71
+ end
72
+ end
73
+ return [$?.exitstatus, command_output]
74
+ end
75
+
76
+ # Used like Unix touch
77
+ # @param file_list [Array<String>,String] List of files to create or verify
78
+ def touch(file_list, options = {})
79
+ FileUtils.touch(file_list, options)
80
+ end
81
+
82
+ # Used for backwards compatibility (just a wrapper for touch)
83
+ # @param file [String] File to create or verify
84
+ def find_or_create_file(file)
85
+ unless File.exists?(File.expand_path(file))
86
+ log "File '#{file}' does not seem to exist... creating it...", :warn
87
+ touch File.expand_path(file)
88
+ end
89
+ return File.expand_path(file)
90
+ end
91
+
92
+ # Throws an exception if a file doesn't exist
93
+ # @raise [MissingFile]
94
+ # @param file [String] File or directory to verify
95
+ # @param message [String] Option message to log if file does not exist
96
+ # @return [String]
97
+ def find_file(file, message = "")
98
+ unless File.exists?(File.expand_path(file))
99
+ message.empty? ? log("Filename #{file} is invalid", :error) : log(message, :error)
100
+ raise MissingFile, file
101
+ end
102
+
103
+ return File.expand_path(file)
104
+ end
105
+
106
+ # A simple replacement for `sed`. This is far from a complete implementation and should be used sparingly. A much
107
+ # better approach is to use ERB files.
108
+ # @raise [InvalidInput]
109
+ # @param substitutions [Hash] Hash of Strings indexing Strings. Keys are replaced with their value.
110
+ # @param input_file [IO,String] IO object or String to use as the source
111
+ # @param output_file [IO,String] IO object or String to use as the destination
112
+ def sed(substitutions, input_file, output_file)
113
+ input_file = input_file.kind_of?(IO) ? input_file : File.open(input_file, "r")
114
+ output_file = output_file.kind_of?(IO) ? output_file : File.open(output_file, "w")
115
+ raise InvalidInput unless substitutions.kind_of?(Hash)
116
+
117
+ # Very fancy way to replace things in a file and write to a new file
118
+ output_file.puts(
119
+ input_file.read.gsub(Regexp.union(substitutions.keys)) do |match|
120
+ # Hideous way to work with regular expressions *and* string substitutions in a single pass over a large file
121
+ substitutions[substitutions.keys.keep_if {|s| substitutions[s].to_s if match == s or s.match(match) }.first].to_s
122
+ end
123
+ )
124
+ output_file.close
125
+ end
126
+
127
+ end
128
+ end
129
+ end
@@ -1,6 +1,6 @@
1
1
  module Doable
2
2
  module Helpers
3
- module PasswordHelpers
3
+ module Password
4
4
  # Generates a password
5
5
  # @param length [Fixnum] Length of the password
6
6
  # @param options [Hash] Options hash for specifying password details
data/lib/doable/job.rb CHANGED
@@ -4,8 +4,8 @@ module Doable
4
4
  # can also describe how to recover when things break and provides hooks and triggers to make more flexible
5
5
  # scripts for varying environments.
6
6
  class Job
7
- include Helpers::FrameworkHelpers
8
- include Helpers::LoggingHelpers
7
+ include Helpers::Framework
8
+ include Helpers::Logging
9
9
  attr_reader :steps, :hooks, :handlers, :threads
10
10
 
11
11
  def self.plan(&block)
@@ -141,12 +141,12 @@ module Doable
141
141
 
142
142
  unless step.successful?
143
143
  message = "\n\nUnhandled Exception in #{colorize("hooks##{hook}[#{index}]", :yellow)}: #{colorize("#{e.class}: (#{e.message})", :red)}\n\n"
144
- if @config.auto_rollback
145
- log message
146
- rollback!
147
- else
144
+ #if @config.auto_rollback
145
+ # log message
146
+ # rollback!
147
+ #else
148
148
  raise message
149
- end
149
+ #end
150
150
  end # unless
151
151
  end
152
152
  end # begin()
data/lib/doable.rb CHANGED
@@ -8,10 +8,12 @@ require 'erb'
8
8
  require 'thread'
9
9
  require 'pathname'
10
10
  require 'yaml'
11
+ require 'etc'
12
+ require 'resolv'
11
13
  # Framework requirements
12
- require 'doable/exceptions/framework_exceptions'
13
- include Doable::Exceptions::FrameworkExceptions
14
- require 'doable/helpers/logging_helpers'
15
- require 'doable/helpers/framework_helpers'
14
+ require 'doable/exceptions/framework'
15
+ include Doable::Exceptions::Framework
16
+ require 'doable/helpers/logging'
17
+ require 'doable/helpers/framework'
16
18
  require 'doable/step'
17
19
  require 'doable/job'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Gnagy
@@ -17,11 +17,14 @@ extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
19
  - LICENSE
20
+ - README.md
20
21
  - lib/doable.rb
21
- - lib/doable/exceptions/framework_exceptions.rb
22
- - lib/doable/helpers/framework_helpers.rb
23
- - lib/doable/helpers/logging_helpers.rb
24
- - lib/doable/helpers/password_helpers.rb
22
+ - lib/doable/exceptions/framework.rb
23
+ - lib/doable/exceptions/os.rb
24
+ - lib/doable/helpers/framework.rb
25
+ - lib/doable/helpers/logging.rb
26
+ - lib/doable/helpers/os.rb
27
+ - lib/doable/helpers/password.rb
25
28
  - lib/doable/job.rb
26
29
  - lib/doable/step.rb
27
30
  homepage: https://rubygems.org/gems/doable