supervisord supervisorctl cheat sheet
On my new server I made the switch from daemontools to supervisor. As if to make daemontools users feel at home the documentation for supervisorctl use is a bit thin :)
So far my biggest gotcha was that after adding a new .conf file in your includes (or to the main file itself, but you wouldn't do that would you?) is that you need to do *both* of the following:
> supervisorctl reread
new.app.conf: available
> supervisorctl update
new.app.conf: added process group
> supervisorctl
new.app RUNNING ...
haproxy
Unfortunately supervisor doesn't offer a way to provide a custom restart command, so doing an uninterrupted haproxy restart via supervisor isn't possible. Instead make sure you have autorestart set to unexpected (the default) rather than true (which is what I usually do) - that way if you want a clean haproxy restart you can just do it yourself at the commandline using the -sf option as normal.
11:14 PM, 25 Mar 2012 by Mark Aufflick Permalink | Comments (0)
SAPI Cocoa SDK
01:13 AM, 24 Mar 2012 by Mark Aufflick Permalink | Comments (0)
Stunnel + Thin + Rails for the world's easiest ssl hosting
These days I do most quick and dirty web stuff with Ruby on Rails, and any open source web stuff I install I prefer Rails based code for easy hacking. Every so often you want to host something for internal or other small-volume usage, but it has to be SSL. Traditionally this is a pain in the backside, especially with Rails. Back in the day I did everything with OpenACS or plain AOLServer. This meant I had to install a bunch of stuff by hand (although debian packages are now available), but once it was done it was a simple config tweak to change the number of threads and/or add ssl etc. With Rails it's kind of the opposite. Getting things up and running is incredibly easy, but scaling it or adding ssl usually means Apache, or Nginx, or some other (relatively) heavy server plus other stuff. Doing this all as a non-super user is even more painful.
For a client I wanted to setup a Redmine instance for a wiki and browsing internal git repositories. It's a Rails 2.x app. Initial setup is easy. Using the latest trunk version of Redmine (which supports Ruby 1.9 and uses bundler) it's a simple matter of the following steps (which assume pretty much nothing is installed except base libraries like sqlite3):
- checking out the svn trunk
svn co svn co http://redmine.rubyforge.org/svn/trunk redmine
cd redmine
- installing rvm, setting up the right ruby & gem set
bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
. ~/.bashrc
rvm install ruby-1.9.3
rvm use ruby-1.9.3@redmine --create
rvm install bundler
- installing bundler and using it to prepare all the ruby gems (I am using sqlite and don't have mysql libraries installed)
gem install bundler
bundle install --without=mysql
- follow the main redmine installation steps from step 3, but make the database adapter sqlite3 and the database db/production.sqlite
http://www.redmine.org/projects/redmine/wiki/RedmineInstall
You now have Redmine up and running, via the test webrick server, on port 3000. But that's no good for two reasons, one we obviously don't have ssl, and two webrick is a single process (and ruby is always a single processor thread) so it's not very scalable.
A nice simple, and yet fast, way to scale Ruby http servers is Thin. It's Ruby and C. It's only http though, so we need to put ssl in front some how. A great way of doing this is stunnel, which can tunnel just about anything to anything when it comes to ssl. So we run a couple of Thin instances, and stunner to proxy it to ssl. The great thing is we can arrange for the communication between the two to be via Unix sockets so we don't even need to reserve localhost TCP ports–so we can scale the number of instances without checking with anyone else.
Here are my steps for Thin and stunnel:
- Install Thin:
gem install thin
- Install stunnel:
# get the latest version of stunner from http://www.stunnel.org/?page=downloads
tar zxf stunnel-4.xx.tar.gz
cd stunnel-4.xx
mkdir ~/usr
./configure --prefix=~/usr
make
make install
- You need a .pem certificate for stunnel. If you don't have a real certificate, you can create a self-signed one like so:
openssl req -new -x509 -days 365 -nodes -config stunnel.cnf -out stunnel.pem -keyout my_stunnel.pem
- where the stunnel.cnf file is in the source distribution. Answer the questions appropriately.
- stunnel needs a config file, something like this (in this case for three thin instances)
pid =
cert = /path/to/my_stunnel.pem
[redmine]
accept = 12345
connect = /path/to/my_rails_app/tmp/thin.0.sockconnect = /path/to/my_rails_app/tmp/thin.1.sock
connect = /path/to/my_rails_app/tmp/thin.2.sock
- start up stunnel
~/usr/bin/stunnel /path/to/my/stunnel.conf
- start up your rails app with Thin
thin start -s3 --socket /path/to/my_rails_app/tmp/thin.sock -e production
And that's it, your site running on https port 12345! All this has not required root access at any point. If you want to listen on a low port, eg. 443, the only thing that needs to run as root is stunnel.
But don't break out the champagne yet - we have a problem. Your rails app knows the hostname and port thanks to the Host: header, but it has no idea that it's running behind stunnel and is therefore ssl. This is only a problem in the occasions where a full url is needed, and the primary case is for http redirect headers. I take care of this by rewriting the Location header in application_controller.rb:
after_filter :force_https_for_redirects
def force_https_for_redirects
if response.headers.has_key?("Location")
response.headers["Location"].sub!(/^http:/, 'https:')
end
end
You may have other cases that need fixing, like constructing urls for emails. Note that I wouldn't recommend mucking about with the X-Forwarded-Proto patches for stunnel. I just don't trust third party patches for security software, and in any case I'm not sure it would work with using Unix sockets for the intermediary transport.
Update: Here's how to monkey patch ActionController::UrlWriter::url_for() so your emails will have correct links. In Rails 2 (in my case for redmine), I did it in environment.rb - for Rails 3 I think you could do it in application.rb, but it doesn't really matter where you do it as long as it gets compiled after ActionController::UrlWriter is loaded.
module ActionController
module UrlWriter
alias :original_url_for:url_for
def url_for(options = {})
options[:host] = 'efserv.com'unless options[:host]
options[:protocol] = 'https' unless options[:protocol]
options[:port] = 7022 unless options[:port]
original_url_for(options)
end
end
end
01:52 AM, 20 Mar 2012 by Mark Aufflick Permalink | Comments (0)
PMPKVObservation – Yet Another KVO-improvement project
If you’re reading this then you’re probably as frustrated by seemingly random KVO crashes and/or the pain of huge if/else blocks in your observers. I’ve never been happy with the other KVO-improvement classes I’ve seen, and never having had any bright insights myself I kept doing things the normal way. This becomes especially painful with things like view based NSTableViews when you are re-using objects and so need to observe a new object, being very careful to un-observe the prior object (unless it’s been released, which you need to either track yourself if you can, or retain it, which has its own problems).
It was clear that the dealloc swizzling approach of Mike Ash’s MAKVONotificationCenter was unavoidable, but I didn’t like the complexity. Recently Gwynne Raskind posted a somewhat updated MAKVONotificationCenter which sparked some discussion, including a comment discussing SPKVONotificationCenter by Joachim Bengtsson. Joachim’s brainwave was that observations should be modelled as standalone objects and simply managing the lifecycle of that object appropriately should remove the observation. Clean and simple.
Except it’s not quite that simple because you still need to swizzle the dealloc of the observed object since it can go away at any time. And as much as I love a good macro as much as the next hacker, Joachim’s $depends() macro looks about as much fun as a turing complete makefile.
Enter PMPKVObservation!
Include PMPKVObservation.m and .h in your project, then do something like:
#import"PMPKVObservation.h"
@interface PMPKVOTests ()
@property (retain) PMPKVObservation * kvo;
@end
@implementation PMPKVOTests
@synthesizekvo = _kvo;
- (void)observationFired:(PMPKVObservation *)observationchanges:(NSDictionary *)changes;
{
NSLog(@"observation fired for object: %@ keyPath: %@ changes: %@",
observation.observee,
observation.keyPath,
changes);
}
- (void)selectorObservation
{
self.kvo = [PMPKVObservation observe:anObjectToObserve
observer:self
selector:@selector(observationFired:changes:)
keyPath:@"observeMe"
options:0];
}
- (void)blockObservation
{
self.kvo = [PMPKVObservation observe:anObjectToObserve
keyPath:@"observeMe"
options:0
callback:^(PMPKVObservation *observation, NSDictionary *changeDictionary) {
NSLog(@"observation fired for object: %@ keyPath: %@ changes: %@",
observation.observee,
observation.keyPath,
changes);
}];
}
- (void)dealloc
{
[ _kvo release];
[super dealloc];
}
@end
The options and change dictionary are as per normal KVO. You need to be wary of the block causing retain cycles of self as usual.
Unlike MAKVONotificationCenter and others, you are responsible for managing the lifecycle of the kvo object – when it is dealloc-ed the observation will be removed. Like MAKVONotificationCenter, a minimum of magic is used to swizzle the dealloc method of the observed object and remove the observation if it still exists (tracked via an associated NSHashTable). If for some reason you want to find out if the kvo object you hold has been released early you can check the isValid accessor (which you can observe with KVO).
Caveats
- While I am using this in a client project, that project has not yet shipped so I can’t promise wide-spread testing. Take a look at the tests in the test app and if you are using scenarios that you don’t thing are tested pull requests are welcome!
- I haven’t tried this under ARC, but I think I’ve made PMPKVObservation.m so it will compile and work as-is (the same can’t be said for the test app). Please let me know how you go.
- No attempt is made to stop you changing the observed object etc. after creation, but doing so isn’t going to be very effective…
Repository: https://github.com/aufflick/PMPKVObservation
06:49 PM, 13 Mar 2012 by Mark Aufflick Permalink | Comments (0)
Apple Museum
Your eyes don't deceive you, that is a 5 1/4 inch drive - the processor direct slot has an Apple // card in it (which was designed for the LC, which my parents bought it with, but it works great in the Colour Classic).
09:10 PM, 22 Feb 2012 by Mark Aufflick Permalink | Comments (0)
Emacs Programmed Completion
Subtitle - how (completing-read) stole my afternoon.
I thought I'd post my experiences since completing-read is so poorly understood based on the forum and blog posts out there. When you write an interactive Elisp command, many will know that you can easily read a value from the user. Eg: this will ask you how you feel and log it:
(defun mood(mood)
"Logs your mood"
(interactive "sHow are you feeling? ")
(message mood))
But the string form of argument to interactive is just a shortcut. You can easily supply some code instead, with a static completion list:
(defun mood(mood)
"Logs your mood"
(interactive (list (completing-read "How are you feeling? " '("happy""sad")))
(insert arg))
(message mood))
If you want something more complex though, instead of supplying a completion list, you can supply a function and that function receives as one of it's arguments the string to be completed. But it's not as simple as that function just returning a list unfortunately, and that's where people come unstuck. Your function is now embedded in the guts of the Emacs completion mechanism and as such it has to answer a whole range of questions about completions. It is reasonably well documented (if a bit terse) in the Programmed Completion section of the Emacs manual. There is a second argument which contains an optional predicate function (I'm ignoring that), but the important bit is the third argument which effectively sets the mode in which your function is being called.
In fact, your function will usually be called multiple times for each completion tab press. Essentially, your function may be asked to provide the best completion string possible, if there string is an exact match, if there are no matches, or to provide a list of possible matches. There is one final thing that will be asked, and that is the substring your completion is for. So in the example you're about to see I am completing directories, so the completion being offered at any given time only applies to the part after the last forward slash.
An example is as good as a thousand or so words, so here's my interactive function cdsrc. Basically it's a convenience function to quickly change directory into one of my client projects, which may be one or two levels deep in my main company source directory. The hard work is done by cdsrc-completions and you can see in the (cond) at the end where it responds to the different modes discussed briefly above and in detail in Programmed Completion.
(setq cdsrc-completions-prefix "~/src/_Pumptheory/")
(defun safe-fill-common-string-prefix (s1 s2)
(if (and s1 s2)
(fill-common-string-prefix s1 s2) nil))
(defun cdsrc-completions (str pred mode)
(let ((dirname (replace-regexp-in-string "[^/]+$""" str))
(filename (replace-regexp-in-string "^.*/""" str))
(slashpos (or (string-match "/.*$" str) 0)))
(let ((completions (all-completions filename (delq nil
(mapcar (lambda (x)
(and (file-directory-p (concat cdsrc-completions-prefix dirname x))
(not (string-match "^\\." x))
(concat x "/")))
(directory-files (concat cdsrc-completions-prefix dirname)))))))
;; return differently based on what mode we were called in ;; see http://www.gnu.org/software/emacs/manual/html_node/elisp/Programmed-Completion.html
(cond
((not mode) (cond
((and (eq 0 (length completions)) (eq 0 (length filename))) 't);; in our case, only report exact match when no nested dirs left
((> (length completions) 1)
(concat dirname (or (reduce 'safe-fill-common-string-prefix completions) filename)))
((eq (length completions) 1)
(concat dirname (car completions)))
('t nil)))
((eq mode 't) completions)
((eq mode 'lambda) (member (concat filename "/") completions))
('t (cons (list 'boundaries slashpos) (length filename))));; let completion know our completions only apply after the last / )))
(defun cdsrc (path)
"Changes to a subdir of cdsrc-completions-prefix in the current active interactive shell buffer"
(interactive (list
(let ((completion-ignore-case 't))
(completing-read
(concat "Enter subdir of " cdsrc-completions-prefix ": ")
'cdsrc-completions))))
(let ((path2 (concat cdsrc-completions-prefix path)))
(cd path2)
(if (string-match mode-name "Shell")
(progn (end-of-buffer)
(comint-kill-input)
(comint-send-string (current-buffer) (concat "cd " path2))
(comint-send-input)))))
This is out of my emacs.d, specifically aufflick-shell-hooks.el.
05:36 AM, 21 Feb 2012 by Mark Aufflick Permalink | Comments (0)
Comments are broken
Update: Comments are now fixed, both for registered and anonymous persons.
01:44 AM, 10 Feb 2012 by Mark Aufflick Permalink | Comments (1)
New Server
BTW, as well as the same hardware running for over 10 years, the same Linux installation (with security patches which eventually had to be backported after it stopped being supported) has been running that whole time. If I remember right, the maximum uptime was around 700 days - just short of two years. Unfortunately it had to be shut down then to move it to another power rail.
I felt a certain nostalgia poking around it's directory structure looking for anything I wanted to keep. It's probably the last server I've had with /usr/local/playground in a nod to russm. And certainly the last server I will ever run qmail on - it was so awesome, but the massively scalable queue based mail handling just doesn't work with today's spam probes.
07:09 AM, 09 Feb 2012 by Mark Aufflick Permalink | Comments (1)
Archive
| January 2012 | ||||||
| S | M | T | W | T | F | S |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 | ||||
February 2012
November 2011
October 2011
April 2011
March 2011
January 2011
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
March 2008
February 2008
January 2008
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
February 2004
January 2004
December 2003
November 2003
October 2003
September 2003
August 2003
Blog Categories
software (40)..cocoa (21)
..heads up 'tunes (5)
..ruby (6)
..lisp (4)
..perl (4)
..openacs (1)
mac (21)
embedded (2)
..microprocessor (2)
..avr (1)
electronics (3)
design (1)
photography (26)
..black and white (6)
..A day in Sydney (18)
..The Daily Shoot (6)
food (2)
Book Review (2)
Notifications
Request notifications







