doable 0.0.2 → 0.0.3

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: 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