Thursday, May 31, 2012

Dzil plugins: GitHub::Meta vs GithubMeta

"Dear lazyweb: how do I add a github repository link to my dist-zilla dist.ini?"

Curtis Poe (@ovidperl) via twitter


Ovid asked how to add git repo information to a dist.ini file. Both Dist::Zilla::Plugin::GitHub::Meta and Dist::Zilla::Plugin::GithubMeta were suggested, along with the old-school Dist::Zilla::Plugin::MetaResources.


I've used MetaResources to include repository information in my dist.ini files. Which plugin should I use now: GitHub::Meta GithubMeta?

My MetaResources usage is pretty simple: homepage + repository information. The GitHub::Meta and GithubMeta versions are nearly the same from the dist.ini side, both have the same format for manually overriding the default homepage:

#MetaResources Github example:
[MetaResources]
homepage        = http://lowlevelmanager.com/
repository.web  = http://github.com/spazm/app-pm-website
repository.url  = http://github.com/spazm/app-pm-website.git
repository.type = git

## GithubMeta version
[GithubMeta]
homepage = http://lowlevelmanager.com/

## GitHub::Meta version
[GitHub::Meta]
homepage = http://lowlevelmanager.com/

There are differences if we look inside the modules. GithubMeta makes a system call to git to get remote url(s). The plugin will only run if called from within a git repository and with git in $PATH. If it finds a github.com url, it uses that as the remote. This url is then parsed to get the github user name and project name.

Conversely, GitHub::Meta shells out to git to get the github.user entry from git config via git config github.user and then makes an API call to github to get the project name. It does have an interesting feature where it can check if the checkout is a fork and use the upstream information instead.

GitHub::Meta could be nice if you want to go all-in and auto-create the repository with GitHub::Create and push updates with GitHub::Update (actually, this just seems to change the homepage url?), all of them will require the github.user configuration set in git. The docs suggest using GitHub::Create with Git::Init. I must admit, I'm behind the DZil times, as I'm not even sure where to store the default plugin options to apply when running dzil new, to actually trigger these plugins.

GithubMeta does what you'd expect and does it with a minimum of fuss. No extra options to set in your git configuration, but you do have to have the github remote added to your repo by other means. This seems a much slimmer change for existing repositories.

Wednesday, May 23, 2012

Zsh: merge stdout and stderr with |&

|& provides a zsh-only shortcut for merging STDOUT and STDERR when piping. It is the same as 2>&1 | , just a lot shorter to type. I find this useful for commands that feel like dumping --help output to STDERR.

command --help |& less

Pro Tip: Don't confuse this with &| which backgrounds the final command of the pipeline.

Find out more in the "Simple commands & pipelines" section of zshmisc:

A pipeline is either a simple command, or a sequence of two or more simple commands where each command is separated from the next by `|' or `|&'. Where commands are separated by `|', the standard output of the first command is connected to the standard input of the next. `|&' is shorthand for `2>&1 |', which connects both the standard output and the standard error of the command to the standard input of the next.

Update: |& is borrowed from csh. I don't know if it originated in csh or prior.

The standard error output may be directed through a pipe with the standard output. Simply use the form |& instead of just |.

-- man csh(1)

Friday, May 11, 2012

Zsh brace expansion and inline for loops

Two tips for today: zsh brace expansions [1] [2] and short for loops.

Brace Expansion:
{x..y} will expand to the integers x,x+1,...y. Prepend the numbers with padding for padded output. This will work in bash as well.


echo {3..5}     # 3 4 5
echo {3..100}   # 3 4 5 ... 10 11 12 ... 98 99 100
echo {03..100}  # 03 04 05 ... 10 11 12 ... 98 99 100
echo {003..100} # 003 004 005 ... 010 011 012 ... 098 099 100

Up until now, I've used seq to create integer lists like this. Using seq in commands requires a subshell. Do you know how to zero-pad the output of seq?
echo $(seq 3 5)   # 3 4 5
echo $(seq -w 3 5) # 3 4 5
echo $(seq -w 8 10) # 08 09 10

But today is about moving beyond seq. Let's move on to for loops.

For Loops:

% for i in {3..5}; do echo $i; done
3
4
5

Zsh has two shortened forms of this standard for loop. The short forms only support a single command. If you really want multiple commands, you can put them in {}, but then you might as well use "do ... done" in that case. These won't work in bash (woo!).

I never remember this when I need it and can't find references to this zsh specific "single line forloop" -- but it's right there in zshmisc,"Alternate Forms For Complex Commands."

for ... in list; comand
for ... (list) command




# short form with "in" and ";"
% for i in {3..5}; echo "line $i"; date
line 3
line 4
line 5
Fri May 11 15:52:32 PDT 2012

# short form with parens:
% for i ({3..5}) echo "line $i"; date
line 3
line 4
line 5
Fri May 11 15:52:32 PDT 2012

# short form with multiple commands in {}
% for i in {3..5}; { echo "line $i"; date }
line 3
Fri May 11 15:52:43 PDT 2012
line 4
Fri May 11 15:52:43 PDT 2012
line 5
Fri May 11 15:52:43 PDT 2012

Complex Commands
for name ... [ in word ... ] term do list done

where term is at least one newline or ;. Expand the list of words, and set the parameter name to each of them in turn, executing list each time. If the in word is omitted, use the positional parameters instead of the words.

More than one parameter name can appear before the list of words. If N names are given, then on each execution of the loop the next N words are assigned to the corresponding parameters. If there are more names than remaining words, the remaining parameters are each set to the empty string. Execution of the loop ends when there is no remaining word to assign to the first name. It is only possible for in to appear as the first name in the list, else it will be treated as marking the end of the list.

Alternate Forms For Complex Commands

for name ... ( word ... ) sublist

A short form of for.

for name ... [ in word ... ] term sublist

where term is at least one newline or ;. Another short form of for.


Zsh history expansion

You can refer back to prior elements of your command history, modify them, and make new commands. With Zsh expansion, history expansions can be expanded in-line before executing! Start by trying to learn a couple entries and slowly expand while building up your memory of useful mappings.

!$ and !* to reference args from prior commands

I've been using !$ (bang-last) for a while now. It expands to the last arg of the previous command. Use this when you are taking multiple actions with the same argument:

    % touch newfile
    % chmod a+x !$
    % vim !$
I'm now working !* into my command-line-fu, which pulls all the args from the previous command rather than just the last.

History Expansion:

! initiates history expansion, pulling items from your previous commands. The most commonly known/used abbreviation being !! to repeat the last command in full. Similarly !n repeats item n from your history and !-n goes back n commands. !-1 is synonymous with !!. !str is the most recent command to start with str. !# is current command line up to this point.

"Word Designators" can be applied to the selected history item, as we do above with $ and * . 0 is the command, n is the nth argument, x-y is args x through y. x* is like x-$ while x- is like x-$ excluding the last item.

I haven't even gotten started with the modifiers, like G for global on ^search^replace, a and A for prepending the current dir and crazy others.

Examples:

!!                # last command
!$                # final word of prior command
!*                # all words of prior command (excluding the command itself)
!3                # third command from history
!-3:*             # all words from three commands back 
^foo^bar          #last command, with the first substring foo replaced by bar
!-2:s^foo^bar     #full command from 2 back, with the first foo replaced by bar
!-2:s^foo^bar^:G  #full command from 2 back, with all "foo"s replaced by "bar"s

man zshexpn for all the details.
History Expansion

History expansion allows you to use words from previous command lines in the command line you are typing. This simplifies spelling corrections and the repetition of complicated commands or arguments. Immediately before execution, each command is saved in the history list, the size of which is controlled by the HISTSIZE parameter. The one most recent command is always retained in any case. Each saved command in the history list is called a history event and is assigned a number, beginning with 1 (one) when the shell starts up. The history number that you may see in your prompt (see EXPANSION OF PROMPT SEQUENCES in zshmisc(1)) is the number that is to be assigned to the next command.

Overview

A history expansion begins with the first character of the histchars parameter, which is '!' by default, and may occur anywhere on the command line; history expansions do not nest. The '!' can be escaped with '\' or can be enclosed between a pair of single quotes ('') to suppress its special meaning. Double quotes will not work for this. Following this history character is an optional event designator (see the section 'Event Designators') and then an optional word designator (the section 'Word Designators'); if neither of these designators is present, no history expansion occurs.
Input lines containing history expansions are echoed after being expanded, but before any other expansions take place and before the command is executed. It is this expanded form that is recorded as the history event for later references.

By default, a history reference with no event designator refers to the same event as any preceding history reference on that command line; if it is the only history reference in a command, it refers to the previous command. However, if the option CSH_JUNKIE_HISTORY is set, then every history reference with no event specification always refers to the previous command.

For example, '!' is the event designator for the previous command, so '!!:1' always refers to the first word of the previous command, and '!!$' always refers to the last word of the previous command. With CSH_JUNKIE_HISTORY set, then '!:1' and '!$' function in the same manner as '!!:1' and '!!$', respectively. Conversely, if CSH_JUNKIE_HISTORY is unset, then '!:1' and '!$' refer to the first and last words, respectively, of the same event referenced by the nearest other history reference preceding them on the current command line, or to the previous command if there is no preceding reference.

The character sequence '^foo^bar' (where '^' is actually the second character of the histchars parameter) repeats the last command, replacing the string foo with bar. More precisely, the sequence '^foo^bar^' is synonymous with '!!:s^foo^bar^', hence other modifiers (see the section 'Modifiers') may follow the final '^'. In particular, '^foo^bar:G' performs a global substitution.

If the shell encounters the character sequence '!"' in the input, the history mechanism is temporarily disabled until the current list (see zshmisc(1)) is fully parsed. The '!"' is removed from the input, and any subsequent '!' characters have no special significance.

A less convenient but more comprehensible form of command history support is provided by the fc builtin.

Event Designators

An event designator is a reference to a command-line entry in the history list. In the list below, remember that the initial '!' in each item may be changed to another character by setting the histchars parameter.
!
Start a history expansion, except when followed by a blank, newline, '=' or '('. If followed immediately by a word designator (see the section 'Word Designators'), this forms a history reference with no event designator (see the section 'Overview').

!!

Refer to the previous command. By itself, this expansion repeats the previous command.

!n

Refer to command-line n.

!-n

Refer to the current command-line minus n.

!str

Refer to the most recent command starting with str.

!?str[?]
Refer to the most recent command containing str. The trailing '?' is necessary if this reference is to be followed by a modifier or followed by any text that is not to be considered part of str.
!#
Refer to the current command line typed in so far. The line is treated as if it were complete up to and including the word before the one with the '!#' reference.

!{...}

Insulate a history reference from adjacent characters (if necessary).

Word Designators

A word designator indicates which word or words of a given command line are to be included in a history reference. A ':' usually separates the event specification from the word designator. It may be omitted only if the word designator begins with a '^', '$', '*', '-' or '%'. Word designators include:
0
The first input word (command).

n

The nth argument.

^

The first argument. That is, 1.

$

The last argument.

%

The word matched by (the most recent) ?str search.

x-y

A range of words; x defaults to 0.

*

All the arguments, or a null value if there are none.

x*

Abbreviates 'x-$'.

x-

Like 'x*' but omitting word $.

Note that a '%' word designator works only when used in one of '!%', '!:%' or '!?str?:%', and only when used after a !? expansion (possibly in an earlier command). Anything else results in an error, although the error may not be the most obvious one.
Modifiers

After the optional word designator, you can add a sequence of one or more of the following modifiers, each preceded by a ':'. These modifiers also work on the result of filename generation and parameter expansion, except where noted.
a
Turn a file name into an absolute path: prepends the current directory, if necessary, and resolves any use of '..' and '.' in the path. Note that the transformation takes place even if the file or any intervening directories do not exist.

A

As 'a', but also resolve use of symbolic links where possible. Note that resolution of '..' occurs before resolution of symbolic links. This call is equivalent to a unless your system has the realpath system call (modern systems do).

c

Resolve a command name into an absolute path by searching the command path given by the PATH variable. This does not work for commands containing directory parts. Note also that this does not usually work as a glob qualifier unless a file of the same name is found in the current directory.

e

Remove all but the extension.

h

Remove a trailing pathname component, leaving the head. This works like 'dirname'.

l

Convert the words to all lowercase.

p

Print the new command but do not execute it. Only works with history expansion.

q

Quote the substituted words, escaping further substitutions. Works with history expansion and parameter expansion, though for parameters it is only useful if the resulting text is to be re-evaluated such as by eval.

Q

Remove one level of quotes from the substituted words.

r

Remove a filename extension of the form '.xxx', leaving the root name.

s/l/r[/]
Substitute r for l as described below. The substitution is done only for the first string that matches l. For arrays and for filename generation, this applies to each word of the expanded text. See below for further notes on substitutions.
The forms 'gs/l/r' and 's/l/r/:G' perform global substitution, i.e. substitute every occurrence of r for l. Note that the g or :G must appear in exactly the position shown.
&
Repeat the previous s substitution. Like s, may be preceded immediately by a g. In parameter expansion the & must appear inside braces, and in filename generation it must be quoted with a backslash.

t

Remove all leading pathname components, leaving the tail. This works like 'basename'.

u

Convert the words to all uppercase.

x

Like q, but break into words at whitespace. Does not work with parameter expansion.

Monday, May 7, 2012

debugging nagios remote nrpe commands

Nagios NRPE debugging steps:
  1. run the command manually on the target host
  2. enable debugging in nrpe.cfg and watch syslog
  3. dig in deeper with debug jobs.
Debugging nagios remote nrpe commands can feel very opaque. Normally I find my issue in step 1 of the debug escalation. Today I had to hit all three steps while debugging a test that wrapped around s3cmd. I eventually found that HOME is not set in the environment used to run nrpe commands.
check_nrpe -H 10.7.202.92 -c check_ui_s3_backup                                 
NRPE: Unable to read output
I run my check remotely and receive the dreaded general error "unable to read output." This means the script failed to run and didn't produce any output to STDOUT. STDERR seems to be ignored, even with logging enabled.

Step 1a: go to the server and verify the command being run by nrpe.

[andrew@ip-10-7-202-92]% grep check_ui_s3_backup /etc/nagios/nrpe.d/herbie.cfg
command[check_ui_s3_backup]=HOME=~postgres /usr/lib/nagios/plugins/herbie/check_ui_s3_backup
Step 1b: run the command manually. Here I find that the script fails if I don't have the config file:
[andrew@ip-10-7-202-92]% /usr/lib/nagios/plugins/herbie/check_ui_s3_backup
ERROR: /home/andrew/.s3cfg: No such file or directory
ERROR: Configuration file not available.
ERROR: Consider using --configure parameter to create one.
That should be a simple fix. Find the user running the nrpe command and give them a .s3cfg. Easy-Peasy.
cp .s3cfg ~nagios/
sudo -u nagios -H /usr/lib/nagios/plugins/herbie/check_ui_s3_backup
OK - Last backup 0 days ago.
Ok, it works locally. Recheck it remotely. It fails?!!?! This is where we start the gnashing of teeth and pulling of hair
[andrew@ip-10-7-203-10]% check_nrpe -H 10.7.202.92 -c check_ui_s3_backup
NRPE: Unable to read output
Step 2: Enable logging in nrpe.cfg, run the remote check and inspect the logs. Surprise, nothing useful.
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Connection from 10.7.203.10 port 15286
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Host address is in allowed_hosts
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Handling the connection...
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Host is asking for command 'check_ui_s3_backup' to be run...
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Running command: /usr/lib/nagios/plugins/herbie/check_ui_s3_backup
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Command completed with return code 3 and output: 
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Return Code: 3, Output: NRPE: Unable to read output
May  7 19:08:29 ip-10-7-202-92 nrpe[17159]: Connection from 10.7.203.10 closed.
Step 3: debug jobs (aka printf aka "hail marry" debugging). I create two new nrpe entries and restart nagios-nrpe-server. The first will show me the user running the command and the second will show the environment, using whoami and env respectively.
command[check_ui_test]=whoami
command[check_ui_test2]=env
[andrew@ip-10-7-203-10]% check_nrpe -H 10.7.202.92 -c check_ui_test
nagios
[andrew@ip-10-7-203-10]% check_nrpe -H 10.7.202.92 -c check_ui_test2
NRPE_PROGRAMVERSION=2.12
TERM=screen-256color-bce
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
LANG=en_US.UTF-8
NRPE_MULTILINESUPPORT=1
PWD=/
Yep, the tests are running as the expected user, nagios.

Holy Schmoly! Look at that environment! HOME is not set. Simple enough to fix for my check, but wow was I not expecting that. Also useful to note the minimal PATH and that the working directory is /.

Update check to explicitly set HOME in the environment and restart nrpe:

command[check_ui_s3_backup]=HOME=~nagios /usr/lib/nagios/plugins/herbie/check_ui_s3_backup
Restart nrpe:
[andrew@ip-10-7-202-92]% sudo service nagios-nrpe-server restart
 * Stopping nagios-nrpe nagios-nrpe
   ...done.
 * Starting nagios-nrpe nagios-nrpe
   ...done.
Check:
[andrew@ip-10-7-203-10]% check_nrpe -H 10.7.202.92 -c check_ui_s3_backup
OK - Last backup 0 days ago.