birr 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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