birr 0.1

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.
@@ -0,0 +1,5 @@
1
+ require 'megam/version'
2
+ require 'megam/core/exceptions'
3
+ require 'megam/core/log'
4
+ require 'megam/core/text'
5
+
@@ -0,0 +1,190 @@
1
+ #
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'pp'
17
+ require 'megam/config'
18
+ require 'megam/birr'
19
+ require 'mixlib/cli'
20
+ require 'tmpdir'
21
+ require 'rbconfig'
22
+
23
+ class Megam::App
24
+ include Mixlib::CLI
25
+ #include(a_module) is called inside a class, and adds module methods as instance methods.
26
+ #extend(a_module) adds all module methods to the instance it is called on.
27
+
28
+ NO_COMMAND_GIVEN = "You need to pass a command with option (e.g., birr -i <install_path>)\n"
29
+
30
+ banner "Usage: birr (options)"
31
+
32
+ option :install_file,
33
+ :short => "-i INSTALL",
34
+ :long => "--install INSTALL",
35
+ :required => true,
36
+ :description => "The installation file path to use",
37
+ :proc => lambda { |path| File.expand_path(path, Dir.pwd) }
38
+
39
+ verbosity_level = 0
40
+ option :verbosity,
41
+ :short => '-V',
42
+ :long => '--verbose',
43
+ :description => "More verbose output. Use twice for max verbosity",
44
+ :proc => Proc.new { verbosity_level += 1},
45
+ :default => 0
46
+
47
+ option :help,
48
+ :short => "-h",
49
+ :long => "--help",
50
+ :description => "Show this message",
51
+ :on => :tail,
52
+ :boolean => true,
53
+ :show_options => true,
54
+ :exit => 0
55
+
56
+ option :yes,
57
+ :short => "-y",
58
+ :long => "--yes",
59
+ :description => "Say yes to all prompts for confirmation"
60
+
61
+ option :version,
62
+ :short => "-v",
63
+ :long => "--version",
64
+ :description => "Show birr version",
65
+ :boolean => true,
66
+ :proc => lambda {|v| puts "Birr: #{::Megam::VERSION}"},
67
+ :exit => 0
68
+
69
+ #attr_accessors are setters/getters in ruby. The arguments are filtered and available for use
70
+ #to subclasses.
71
+ attr_accessor :name_args
72
+
73
+ attr_accessor :text
74
+ def initialize
75
+ super # The super calls the mixlib cli.
76
+
77
+ ##Traps are being set for the following when an application starts.
78
+ ##SIGHUP 1 Term Hangup detected on controlling terminal
79
+ ## or death of controlling process
80
+ ##SIGINT 2 Term Interrupt from keyboard
81
+ ##SIGQUIT 3 Core Quit from keyboard
82
+ trap("TERM") do
83
+ Megam::App.fatal!("SIGTERM received, stopping", 1)
84
+ end
85
+
86
+ trap("INT") do
87
+ Megam::App.fatal!("SIGINT received, stopping", 2)
88
+ end
89
+
90
+ trap("QUIT") do
91
+ Megam::Log.info("SIGQUIT received, call stack:\n " + caller.join("\n "))
92
+ end
93
+
94
+ @text ||= Megam::Text.new(STDOUT, STDERR, STDIN, {})
95
+
96
+ end
97
+
98
+ # Run the "birr app". Let it roam and stay by our side.[Go birr..Does it remind of the Hutch adv.].
99
+ # The first thing run does is it parses the options. Once the first level of parsing is done,
100
+ # ie the help, no_command, sub_command entry is verified it proceeds to call
101
+ # Megam_Birr with the user entered options and arguments (ARGV)
102
+ def run
103
+ Mixlib::Log::Formatter.show_time = false
104
+ validate_and_parse_options
105
+ Megam::Birr.new.run(@named_args, config)
106
+ exit 0
107
+ end
108
+
109
+ def parse_options(args)
110
+ super
111
+ rescue OptionParser::InvalidOption => e
112
+ puts "Error: " + e.to_s
113
+ puts self.opt_parser
114
+ exit(1)
115
+ end
116
+
117
+ ##A few private helper methods being used by app itself.
118
+ ##If you run an application for ever, you might pool all the executions and gather the stacktrace.
119
+ private
120
+
121
+ # A check is performed to see if an option is entered, help or version
122
+ def validate_and_parse_options
123
+ # Checking ARGV validity *before* parse_options because parse_options
124
+ # mangles ARGV in some situations
125
+ if no_command_given?
126
+ print_help_and_exit(1, NO_COMMAND_GIVEN)
127
+ elsif (want_help? || want_version?)
128
+ print_help_and_exit
129
+ else
130
+ @named_args = parse_options(ARGV)
131
+ end
132
+
133
+ end
134
+
135
+ def no_subcommand_given?
136
+ ARGV[0] =~ /^-/
137
+ end
138
+
139
+ def no_command_given?
140
+ ARGV.empty?
141
+ end
142
+
143
+ def want_help?
144
+ ARGV[0] =~ /^(--help|-h)$/
145
+ end
146
+
147
+ def want_version?
148
+ ARGV[0] =~ /^(--version|-v)$/
149
+ end
150
+
151
+ # Print the help message with the exit code. If no command is given, then a fatal message is printed.
152
+ # The options are parsed by calling the parse_options present in the mixlib cli as extended by the app.
153
+ # A error gets caught and results in an ugly stack trace, which probably needs to be shown in an elegant way.
154
+ # The stacK should be logged using the debug_stacktrace method in the app class.
155
+ def print_help_and_exit(exitcode=1, fatal_message=nil)
156
+ Megam::Log.error(fatal_message) if fatal_message
157
+ parse_options(ARGV)
158
+ exit exitcode
159
+ end
160
+
161
+ class << self
162
+ #The exception in ruby carries the class, message and the trace.
163
+ #http://www.ruby-doc.org/core-2.0/Exception.html
164
+ def debug_stacktrace(e)
165
+ message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
166
+ megam_stacktrace_out = "Generated at #{Time.now.to_s}\n"
167
+ megam_stacktrace_out += message
168
+
169
+ #after the message is formulated in the variable megam_stack_trace_out, its
170
+ #stored in a file named megam-stacktrace.out
171
+ Megam::FileCache.store("megam-stacktrace.out", megam_stacktrace_out)
172
+
173
+ ##The same error is logged in the log file saying, go look at megam-stacktrace.out for error.
174
+ Megam::Log.fatal("Stacktrace dumped to #{Megam::FileCache.load("megam-stacktrace.out", false)}")
175
+ Megam::Log.debug(message)
176
+ true
177
+ end
178
+
179
+ # Log a fatal error message to both STDERR and the Logger, exit the application
180
+ def fatal!(msg, err = -1)
181
+ Megam::Log.fatal(msg)
182
+ Process.exit err
183
+ end
184
+
185
+ def exit!(msg, err = -1)
186
+ Megam::Log.debug(msg)
187
+ Process.exit err
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,176 @@
1
+ #
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+
16
+ require 'pp'
17
+ require 'megam/core/text'
18
+
19
+ #Main entry for the command line, after validating the class, run the install_file
20
+ class Megam::Birr
21
+
22
+ attr_accessor :text
23
+ attr_accessor :config
24
+ #text is used to print stuff in the terminal (message, log, info, warn etc.)
25
+ def self.text
26
+ @text ||= Megam::Text.new(STDOUT, STDERR, STDIN, {})
27
+ end
28
+
29
+ # I don't think we need this method. Will remove it later.
30
+ # We need to just call text.msg
31
+ def self.msg(msg="")
32
+ text.msg(msg)
33
+ end
34
+
35
+ # Create a new instance of the current class configured for the given
36
+ # arguments and options
37
+ def initialize()
38
+ end
39
+
40
+ # Run Birr for the given +args+ (ARGV), adding +options+ to the list of
41
+ # CLI options that the subcommand knows how to handle.
42
+ # ===Arguments
43
+ # args::: usually ARGV
44
+ # options::: A Mixlib::CLI option parser hash. These +options+ are how
45
+ # subcommands know about global Birr CLI options
46
+ def run(args=[], config={})
47
+ @config = config.dup
48
+ @text ||= Megam::Text.new(STDOUT, STDERR, STDIN, config)
49
+ # configure your Birr.
50
+ configure_birr
51
+ end
52
+
53
+ # configure meggy, to startwith locate the config file under .meggy/Birr.rb
54
+ # Once located, read the Birr.rb config file. parse them, and report any ruby syntax errors.
55
+ # if not merge then inside Meggy::Config object.
56
+ def configure_birr
57
+ # look for a .birr/birr.rb for configuration. Not used currently.
58
+ unless config[:install_file]
59
+ locate_install_file
60
+ end
61
+
62
+ # Now load the installation file provided as input {-i} parm.
63
+ if config[:install_file]
64
+ @text.info "Using #{config[:install_file]}"
65
+ apply_computed_config
66
+ read_config_file(config[:install_file])
67
+ else
68
+ text.warn("No birr configuration file found")
69
+ end
70
+
71
+ end
72
+
73
+ def locate_install_file
74
+ # Look for $HOME/.meggy/birr.rb, this aint' supported currently
75
+ # the idea is to allow configuration of the options used here.
76
+ if ENV['HOME']
77
+ user_config_file = File.expand_path(File.join(ENV['HOME'], '.birr', 'birr.rb'))
78
+ end
79
+
80
+ if File.exist?(user_config_file)
81
+ config[:install_file] = user_config_file
82
+ end
83
+ end
84
+
85
+ # Catch-all method that does any massaging needed for various config
86
+ # components, such as expanding file paths and converting verbosity level
87
+ # into log level.
88
+ def apply_computed_config
89
+
90
+ case config[:verbosity]
91
+ when 0, nil
92
+ config[:log_level] = :error
93
+ when 1
94
+ config[:log_level] = :info
95
+ else
96
+ config[:log_level] = :debug
97
+ end
98
+
99
+ Mixlib::Log::Formatter.show_time = false
100
+ Megam::Log.init(config[:log_location] || STDOUT)
101
+ Megam::Log.level(config[:log_level] || :error)
102
+
103
+ config.each do |key, val|
104
+ Megam::Config[key] = val
105
+ end
106
+
107
+ end
108
+
109
+ # load the content as provided in -i {installation_file}
110
+ # ruby errors get reported or else the file is executed.
111
+ def read_config_file(file)
112
+ self.instance_eval(IO.read(file), file, 1)
113
+ rescue SyntaxError => e
114
+ @text.error "You have invalid ruby syntax in your config file #{file}"
115
+ @text.info(text.color(e.message, :red))
116
+ if file_line = e.message[/#{Regexp.escape(file)}:[\d]+/]
117
+ line = file_line[/:([\d]+)$/, 1].to_i
118
+ highlight_config_error(file, line)
119
+ end
120
+ exit 1
121
+ rescue Exception => e
122
+ @text.error "You have an error in your config file #{file}"
123
+ @text.info "#{e.class.name}: #{e.message}"
124
+ filtered_trace = e.backtrace.grep(/#{Regexp.escape(file)}/)
125
+ filtered_trace.each {|line| text.msg(" " + text.color(line, :red))}
126
+ if !filtered_trace.empty?
127
+ line_nr = filtered_trace.first[/#{Regexp.escape(file)}:([\d]+)/, 1]
128
+ highlight_config_error(file, line_nr.to_i)
129
+ end
130
+
131
+ exit 1
132
+ end
133
+
134
+ #ERROR: You have invalid ruby syntax in your config file /home/ram/.meggy/Birr.rb
135
+ #/home/ram/.meggy/Birr.rb:9: syntax error, unexpected '='
136
+ #Birr[:username] > = "admin"
137
+ # ^
138
+ # # /home/ram/.meggy/Birr.rb
139
+ # 8: meggy_server_url 'http://localhost:6167'
140
+ # 9: Birr[:username] > = "admin"
141
+ # 10: Birr[:password] = "team4dog"
142
+ # Line 9 is marked in red, and the 3rd line where the error is show is highlighted in red.
143
+ #This is in case of a ruby parse error.
144
+ def highlight_config_error(file, line)
145
+ config_file_lines = []
146
+
147
+ # A file line is split into the line number (index) and the line content.
148
+ # The line number is converted to string (to_s), right justified 3 characters with a colon, and its trimmed (chomp)
149
+
150
+ IO.readlines(file).each_with_index {|l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}"}
151
+ # mark the appropriate line with a red color, if its just one line, then mark the zeroth line.
152
+ # if not get the range (deducting 2), and mark the second line.
153
+ if line == 1
154
+ lines = config_file_lines[0..3]
155
+ lines[0] = text.color(lines[0], :red)
156
+ else
157
+ lines = config_file_lines[Range.new(line - 2, line)]
158
+ lines[1] = text.color(lines[1], :red)
159
+ end
160
+ text.msg ""
161
+ # print the name of the file in white
162
+ text.msg text.color(" # #{file}", :white)
163
+ # print the rest of the line.
164
+ lines.each {|l| text.msg(l)}
165
+ text.msg ""
166
+ end
167
+
168
+
169
+
170
+ private
171
+
172
+ def self.working_directory
173
+ ENV['PWD'] || Dir.pwd
174
+ end
175
+
176
+ end
@@ -0,0 +1,59 @@
1
+ #The birr options class takes the block in the initialize method.
2
+ #it uses the custom attr_accessors, namely (setter, varargs_setters) to
3
+ #inject the value by defining methods with the dsl defined ones.
4
+ #
5
+ class Megam
6
+ class BirrOptions
7
+ def self.setter(*method_names)
8
+ method_names.each do |name|
9
+ send :define_method, name do |data|
10
+ instance_variable_set "@#{name}".to_sym, data
11
+ end
12
+ end
13
+ end
14
+
15
+ def self.varargs_setter(*method_names)
16
+ method_names.each do |name|
17
+ send :define_method, name do |*data|
18
+ instance_variable_set "@#{name}".to_sym, data
19
+ end
20
+ end
21
+ end
22
+
23
+ setter :sudo, :start_time, :tarball
24
+ varargs_setter :directory, :command
25
+ attr_reader :commands, :sudo
26
+
27
+ def initialize(&block)
28
+ # defaults
29
+ @tarball = nil
30
+ @sudo = false
31
+ @directory = []
32
+ @command = []
33
+ @commands = []
34
+ @start_time = Time.now
35
+ instance_eval(&block)
36
+
37
+ end
38
+
39
+ def commands
40
+ @commands = @command.flatten if @command
41
+ end
42
+
43
+ def sudo?
44
+ @sudo
45
+ end
46
+
47
+ def tarball_file
48
+ @tarball_file ||= @tarball
49
+ end
50
+
51
+ def to_s
52
+ tmps = ""
53
+ tmps << "sudo :" + @sudo.to_s + "\n"
54
+ tmps << "dir :" + @directory.to_s + "\n"
55
+ tmps << "cmd :" + commands.to_s + "\n"
56
+ tmps << "strt :" + @start_time.to_s
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,48 @@
1
+ #
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ require "megam/core/text"
16
+ require "megam/birr"
17
+
18
+ class Megam::CmdVerb
19
+
20
+ def initialize
21
+ end
22
+
23
+ # cp -r -s <from directory> <to directory>
24
+ def self.cp(opts={})
25
+ cp =""
26
+ cp << "sudo " if opts[:sudo]
27
+ cp << "cp"
28
+ cp << " -r " if opts[:recursive]
29
+ # cp << " -u " if opts[:copy_on_new] #copy only when the SOURCE file is newer than
30
+ cp << opts[:from_dir] if opts[:from_dir] or raise Megam::Exceptions::FileNotFound
31
+ cp << " "
32
+ cp << opts[:to_dir] if opts[:to_dir] or raise Megam::Exceptions::FileNotFound
33
+ cp
34
+ end
35
+
36
+ #gunzip -c foo.tar.gz | tar xvf -
37
+ def self.untar(opts={})
38
+ untar = "gunzip -c "
39
+ untar << opts[:tar_file] if opts[:tar_file]
40
+ untar << " | tar xvf - -C "
41
+ untar << opts[:to_dir] if opts[:to_dir]
42
+ untar
43
+ end
44
+
45
+ def to_s
46
+ "cmdverb -> [supports cp, untar *only]"
47
+ end
48
+ end