monkinetic

Steve Ivy's weblog, XI Ed.

Docker, Openstack, policy-rc.d, mysqld

I'm working on building a Docker image that I can use with Jenkins to run tests on some code. That code interacts with an Openstack install in the container to run the tests.

I'm using a variation of this cloudgear install script to install Openstack, an approach that seems to work well on Vagrant VMs, but it's failing wildly in a Docker container.

The issue I'm on right now is that mysql-server won't start. I tried starting (and restarting) it manually in my Dockerfile with RUN service mysql start after mysql is installed but always get a policy-rc.d error.

invoke-rc.d: policy-rc.d denied execution of start.

I dug in and found that Docker's ubuntu includes a policy-rc.d file that simply returns 101 (not allowed) for any service that tries to start/restart after installation.

#!/bin/sh
exit 101

So... not sure why this is set up this way but I'll go with it. I changed my Dockerfile to rewrite the policy-rc.d file:

RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d

The 0 return code is basically a heard-coded "yes" for installed services to run. I tried re-building my image with this new file, and while I am no longer getting the "denied execution" errors mysql still is not starting (confirmed with a couple of test operations that run after the install):

RUN apt-get install mysql-server python-mysqldb mysql-client-5.5 -y
RUN ps aux | grep mysql
RUN mysql -uroot -p<pwd> -e "show databases;"

Which return:

Step 10 : RUN ps aux | grep mysql
---> Running in c68790f9b090
root        10  0.0  0.0   4408   320 ?        R    18:56   0:00 /bin/sh -c ps aux | grep mysql
 ---> 884cb443fb98
Removing intermediate container c68790f9b090
Step 11 : RUN mysql -uroot -p<pwd> -e "show databases;"
 ---> Running in 1631e5c2edee
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
2014/07/25 18:56:28 The command [/bin/sh -c mysql -uroot -p<pwd> -e "show databases;"] returned a non-zero code: 1

I'm pretty much out of ideas now. If you've got any, please ping me at steveivy@gmail.com or on Twitter @steveivy.

Another View of Privilege

(See the disclaimer)

Leslie Hawthorn's recent keynote at OSCON on privilege is unassuming and powerful.

Please watch it and pay attention to this tidbit: privilege can be viewed as operating in a system where all the default settings work for you. This stuck with me, even more than John Scalzi's (also excellent) gaming metaphor (I'm not a huge gamer).

Watch and think:

A gender/privilege topic disclaimer

For future reference:

Disclaimer: I've never written about gender and privilege topics - I'm a hetero white male who grew up in a suburban, anglo, Christan, two-parent family in the 70s and 80s when such things were (to me) normal. I didn't go through much of a rebellious stage (barring a couple of years of slightly-over-drinking, hair-coloring and ear-piercings that didn't offend, literally, anyone).

As John Scalzi would put it, my life has been basically lived on the lowest difficuly setting and I've been fortunate due to no particular effort on my own part. Please forgive statements made as fact that are obvious to you to be side-effects of this phenomenon, and please feel free to get in touch with any feedback.

Day One, Again

I'm really liking Day One. Picked it up for free yesterday and finding it fun and useful.

Neat trick: select a passage in the book I'm reading in iBooks, select "Share" > "Copy" and paste into Day One, complete with link to the book in the iBookstore.

Easy way to temporarily kill logging in Python

Ever want to just turn off that logger for a while in Python? Maybe while running tests or debugging? Even the CRITICAL messages?

Notice that the logging level contants (logging.DEBUG etc) actually represent numeric values:

Level       Numeric value
CRITICAL    50
ERROR       40
WARNING     30
INFO        20
DEBUG       10
NOTSET      0

On a whim I tried:

logging.getLogger('logger_to_turn_off').setLevel(100)

And bam, no logging, even the CRITICAL messages. Yes, most of you python nerds knew this, but I didn't, so I learned something at least!

Idea: LinkedIn Disclaimers

Fun little idea: a blog disclaimer generator based on your current LinkedIn profile.

"My opinions on [current industry] are my own and not those of [current employer]"

Markbox Public Beta, No Invite Required

Markbox is now in public beta, which means that no invite code is required to sign up!. (Do eet noowww!) In addition, after some thought we've removed the requirement that a credit card be provided to join the service. Just as before, there is a 30-day free trial, after which a valid card will be required to continue using it.

Have fun! Sign up for a free trial!

New Markbox invites have gone out

It's been a crazy road with more hair-pulling and teeth-gnashing than I had hoped, but I've scheduled the final set of invites for Markbox. Anyone who had signed up as of tonight to be notified will be sent an invite in the morning.

The hardest part of this for me is turning on Billing. Markbox has operating expenses that I need to recouop a little bit of so it can keep going, so Markbox is now $6/mo. through the end of the Beta. How much it will be thereafter remains to be seen. I have some ideas but I'm not going to commit to anything right now.

It's nearly midnight. I'm excited and exhausted. Those who say you can build and launch as web product on the side these days is technically, but not practically, correct. It's amazingly hard and after months and months working on this I'm still embarassed at some parts of it.

But I'm not going to let this stop me.

When in doubt...

...put it in a transaction.

This nerd advice brought to you by flask and MySQL.

p.s. Anyone remember the msql/mysql wars of the mid 90s?

SqlAlchemy Relationship Error: 'property of that name exists on mapper'

The Setup

I'm writing a web app in python with Flask and SqlAlchemy with MySQL.

the solution...

The Code

Starting with the following (slightly snipped for brevity) python/SqlAlchemy code in <project>/markbox/models.py:

class UserBlog(ModelBase):
    __tablename__ = 'user_blog'

    id = sa.Column(sa.Integer, primary_key=True)
    uid = sa.Column(strcol)
    # blog title
    name = sa.Column(strcol)

    # returns a query => blog.sync_stats.all()
    sync_stats = relationship(
        'models.SyncStats', backref='blog',
        lazy='dynamic')


class SyncStats(ModelBase):
    """
    Store various statistics about sync operations
    """
    __tablename__ = 'system_syncstats'

    id = sa.Column(sa.Integer, primary_key=True)
    uid = sa.Column(strcol, sa.ForeignKey('user_blogsettings.uid'))

And this code in <project>/markbox/tools/test.py:

import sys
import os

sys.path.append(os.path.abspath('.'))
sys.path.append(os.path.abspath('markbox'))
print sys.path

from markbox.models import UserBlog, SyncStats

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URI = "mysql://markbox_user:markbox@localhost/markbox"

engine = create_engine(SQLALCHEMY_DATABASE_URI, echo='debug')
Session = sessionmaker(bind=engine)
session = Session()

if __name__ == '__main__':
    blog = UserBlog.query.limit(1)

The Error

When run from the command line in <project>:

% python markbox/tools/test.py

I get:

['/Users/sivy/Projects/personal/markbox/markbox/tools', ..., '/Users/sivy/Projects/personal/markbox', '/Users/sivy/Projects/personal/markbox/markbox']
Traceback (most recent call last):
  File "markbox/tools/test.py", line 25, in <module>
    blog = UserBlog.query.limit(1)
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 131, in __get__
    mapper = class_mapper(owner)
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/util.py", line 1112, in class_mapper
    mapper = _inspect_mapped_class(class_, configure=configure)
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/util.py", line 1045, in _inspect_mapped_class
    mapperlib.configure_mappers()
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 2122, in configure_mappers
    mapper._post_configure_properties()
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 1244, in _post_configure_properties
    prop.init()
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/interfaces.py", line 231, in init
    self.do_init()
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/properties.py", line 1031, in do_init
    self._generate_backref()
  File "/Users/sivy/.virtualenvs/markbox/lib/python2.7/site-packages/sqlalchemy/orm/properties.py", line 1220, in _generate_backref
    self, m))
sqlalchemy.exc.ArgumentError: Error creating backref 'blog' on relationship 'UserBlog.sync_stats': property of that name exists on mapper 'Mapper|SyncStats|system_syncstats'

What I've Done

I've already confirmed that:

  • the SyncStats model has no "blog" property (it used to have a method, decorated with @property).
  • I've deleted any existing *.pyc files to make sure that the interpreter is not picking up pre-compiled code.

The solution

So, the problem turned out to be that the models were being imported from two paths:

from models import UserBlog

and later:

from markbox.models import UserBlog

This caused the intilization code to be run twice, and the resulting error.

Crowning Moment of Heart Warming

My kids, wife, and Mom went to the movies today (Turbo, well-liked all around) and before they went they stopped by the drugstore and bought movies snacks. Apparently our 8yo daughter, remembering Daddy's favorite candy, convinced Mom to buy a box of Reese's Pieces.

After the movie and a couple more errands, they came home, at which time our youngest called out something akin to "No Peeeeeking" and disappeared into her room. 20 minutes later she appeared in my office with a note that said (she's somewhat developmentally delayed) "Daddy <her name> romm for You Lov <her name>". She excitedly communicated that this was a hint and I was to accompany her to her room, where I got to search her room for a present.

When I found and opened the small gift bag under her pillow containing a wrapped-with-a-bow box of candy, my heart simply melted. How did I deserve such love and adoration?

Reeses Pieces

If monkinetic Then app.net

I'm testing out a basic IFTTT recipe that reposts monkinetic posts to my app.net account.

Update: Sorry I didn't realize when I made the recipe that it was going to grab the last n existing posts and repost them as well. Whoops. Thankfully it only reposted the last 3 posts.

Why is Adobe Flash Installing Google Chrome?

So, like a sheep, I install the latest Adobe Flash (bleagh) this morning so I can watch the Superb Owl Iron Man 3 commercial, since Adobe's being a snot and pigging backing on the Superb Owl's popularity to get more Flash installs out there, and I see this:

Adobe Flash installing Google Chrome

Since when does Flash install Chrome? /me Googles

Apparently, since at least August of 2012 accordion to this Adobe Forum post:

For regularly scheduled Flash Player releases, such as our 11.4 release, users are notified of new features included in the release and may opt to download the latest player via http://get.adobe.com/flashplayer. This workflow allows users to optionally download software from select Adobe partners along with Flash Player. Adobe offsets the ongoing development costs of Flash Player, which is made available for free, by offering users these options. Bundling Google Chrome and Toolbar???

Hm.

A Modern Proverb

Three things you should be wary of,
A new kid in his prime,
A man with all the answers,
And code that runs first time.