Wednesday, March 13, 2013

Short commit SHA in Jenkins jobs' build name

I always forget this, and spend way too much time looking it up.  This is the part I'm looking for:
${GIT_REVISION,length=8}
Hi future me.

If you're not me, you probably already have the GitHub Plugin installed, which pulls in the base Git Plugin.  And if you started reading on the wiki page for the git plugin you may have noticed a reference to a environment variable available as GIT_COMMIT.  After some bit of searching you bump into the Build Name Setter Plugin, and try adding some stuff in the "Build Name" field on the job configuration maybe like:
${GIT_COMMIT}-${BUILD_NUMBER}
Which doesn't work, so you go back to more important stuff.

Or maybe you get lucky and realize the Token Macro Plugin is involved and stumble across the example there.  Which is hidden in-between some Java you can barely look at, is the small blurb at the end of a paragraph which mentions "${GIT_REVISION,length=8}".

What was interesting there for me was that the build name setter relies completely on the token macro plugin for the string expansion, and pulling strings out of the ENV is not it's primary purpose.  There's a special syntax even:
${ENV,var="GIT_COMMIT"}
Which if you discovered, probably frustrated you, since can't seem to use the ENV vars you export during the build script, or even do basic bash variable manipulation like ${GIT_COMMIT:0:8}.

At this point you're realizing the fact that the token macro expansion only looks like bash variable expansion to confuse you.

Jenkins plugins can and do internally in their java guts define and export sometimes parameterized "tokens" which are exported for the specific purpose of being available to the token macro plugin which the build name setter users.

I honestly had no idea how anyone was supposed to know how this stuff worked together short of trolling through the git plugin's source like I did and reading GitRevisionTokenMacro.  But when I looked at the commit, my eyes were opened!


Jenkin's plugin system allows authors to write these html snippets which will be inlined into the job configuration page!?  This is why I can never seem to find "the docs" for a plugin - they're just added in all over the place whenever you install them - wherever it seems relevant to the author who wrote them!

So my new plan is to "always click the ? first" - why wasn't that my old plan?

Friday, August 17, 2012

Default Devstack Cirros Image "root" password

If you forget to send in an ssh key for injection when you boot the devstack default "cirros" image you can still log-in with password auth.

username: cirros
password: cubswin:)

that's "cubswin" followed by a "colon" and "close-paren" (smily face)

e.g.

clayg@devstack:~$ ssh cirros@10.0.0.2
Warning: Permanently added '10.0.0.2' (RSA) to the list of known hosts.
cirros@10.0.0.2's password: cubswin:)

Just as an aside, the dropbear ssh server on the cirros image allows root login by default, but the root user's password on the image is basically disabled:

 root:!$1$LJwQnqlv$DK6oKqcTq9Rf2ClC.kMa3/:10933:0:99999:7:::
cirros:$1$LJwQnqlv$DK6oKqcTq9Rf2ClC.kMa3/:10933:0:99999:7:::

... that leading "!" in the encrypted password field for "root" in /etc/shadow makes all the difference.  If you remove it, you can see it's the same hash as the cirros user and the "cubswin:)" password works for root too.

If you're just having trouble getting into ssh/port 22, a bit of security groups should square you away:

nova secgroup-add-rule default tcp 22 22 0.0.0.0/0

Monday, August 2, 2010

Commits to Redisco

So this is kinda cool, my first pull request on github was accepted:
test, fix

Redisco, is a neat project:


Redisco allows you to store objects in Redis. It is inspired by the Ruby library Ohm and its design and code are loosely based on Ohm and the Django ORM. It is built on top of redis-py. It includes container classes that allow easier access to Redis sets, lists, and sorted sets.
I'm excited about using redisco in a project I'm working on for work that has a django/jQuery front end and celery/redis-server back end.  Redisco itself is a pretty new project (initial commit May 13, 2010 ) but I think it serves and important niche, allowing a lower barrier of entry to projects that want to move away from a RDMS data-store and get their first taste of NoSQL.  Plus, the author was quick to help me fix problems that were effecting ME.

In fact, you can check it out right now - my bug fixes are already available on PyPI:
$ pip install redisco
Downloading/unpacking redisco
  Downloading redisco-0.1.1.tar.gz
  Running setup.py egg_info for package redisco
Installing collected packages: redisco
  Running setup.py install for redisco
Successfully installed redisco  

Thursday, July 1, 2010

Py3K - PEP 355

So, we still don't get object oriented path manipulation in Py3K

$ python3
Python 3.1.1+ (r311:74480, Nov  2 2009, 14:49:22) 
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import path
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named path
>>> 

Is there still hope?
OK. Pronouncement: PEP 355 is dead. The authors (or the PEP editor)
can update the PEP.

I'm looking forward to a new PEP.

--Guido
PEP 355 died out years ago, but the reference implementation still exists, and IMHO is still garbage.  Will there ever be another attempt at a stdlib PathClass?

Monday, May 24, 2010

Monday, March 1, 2010

How vain am I?

This vain:
http://code.djangoproject.com/ticket/5786

changed by jacob 

resolution set to fixed

Thanks to alextreme, lbruno, and clayg.



Yeah, that's MY bug fix in your django 1.2 (currently in beta, get your official copy March 9th!)

The really sad part is, it's not even really MY code change, not REALLY.  I just re-compiled some other guys patch as a top level diff and updated the tests... but I'm still TOTALLY jazzed about being the last submitter before the final commit.  Wait, is that vain?  Or just totally lame?

Tuesday, October 6, 2009

Python over-riding options with ConfigParser and OptionParser

It looks like it may be woth looking into argparse (non-standard library)

"""
The basic idea is that config information can be pulled out of

* Global config file
* User local config file
* environment variables
* command line

Higher priority comes later in the list. 
"""

But here's an implementation of cascading config file/command line options with ConfigParser and OptionParser - without subclassing.

Basically you just...

  • Set global defaults in at the top of the module in the constants section
  • Use those to set defaults in the config object
  • Read in the config file possibly over-riding defaults
  • Read out the values in the config object to a dictionary
  • Use THOSE values to set the command line defaults

Then you can get any option you need out of the command line options argument

It's not very flexible, but it uses standard libraries and should be fairly  obvious.


#! /usr/bin/env python
__doc__ = """
example of cascading options with ConfigParser and OptionParser
"""

# imports
import sys
from ConfigParser import ConfigParser
from optparse import OptionParser
# constants
default_option = 'default_value'
default_toggle = True

# classes
# internal functions

def main():
    # create a config parser
    # config parser objects store all options as strings
    config = ConfigParser({'option':str(default_option),
                           'toggle':str(default_toggle),
                          })
    config.read('example.cfg')
    
    # create command line option parser
    parser = OptionParser("%prog [options]" + __doc__.rstrip())
    
    # configure command line options
    parser.add_option("-o", "--option", action="store", dest="option", help="set option")
    parser.add_option("-t", "--toggleOff", action="store_false", dest="toggle", help="set toggle off")
    parser.add_option("-T", "--toggleOn", action="store_true", dest="toggle", help="set toggle on")
    
    # read config objects defaults section into a dictionary
    config_options = config.defaults()
    # config_options is dictionary of strings, over-ride toggle to bool
    config_options['toggle'] = config.getboolean('DEFAULT', 'toggle')
    # feed dictionary of defaults into parser object
    parser.set_defaults(**config_options)

    # parse command line options
    (options, args) = parser.parse_args()

    print "option: %s" % options.option
    if options.toggle:
        print "toggle is ON"
    else:
        print "toggle is OFF"

    return 0;

if __name__ == '__main__':
    status = main()
    sys.exit(status)