Selecting checked radio button with jQuery and IE8

Ever tried to select the checked radiobutton of a form using jQuery ? Well I did, and hundreds of time before, and never ran into any issues.

Today's the first time, and it involves my dear friend IE8. Seems like either jQuery or IE8 had trouble with my radio button selection.

I finally managed to get what I wanted but with a sightly different syntax for IE8.

First, the markup

<form id="myForm">
  <input name="data[Payment][value]" id="payment_1500" type="radio" value="1500" />
  <label for="payment_1500">1500</label><br />

  <input name="data[Payment][value]" id="payment_2000" type="radio" value="2000" />
  <label for="payment_2000">2000</label><br />

  <input name="data[Payment][value]" id="payment_5000" type="radio" value="5000" />
  <label for="payment_5000">5000</label><br />

  <button id="test">Select</button>
</form>

Pretty simple, isn't it ? I only have three radiobuttons, and I would like to get the selected value when pressing the Select button.

What should work everywhere

The following code is pretty straightforward and I expected it to just work.

var selected1 = $('#myForm input[name="data\\[Payment\\]\\[value\\]"]:checked');
var value1 = selected1.val();
console.log(value1);

Note that we have to double escape the [ and ] characters and wrap in quotes the namevalue. Nothing fancy, just classic string protection. This code works perfectly on Firefox and Chrome, and I deployed it in production for a few weeks.

Then, I got report of users that would have love to use the form, but got an error because no value was selected. I tested and tested it again without finding the cause. Then it occurs to me that all those reports came from user using IE8.

So I rebooted my VM, launched IE8 and was able to reproduce the bug on my first try.

What the heck is IE8 doing ?

Well, that's a deep question, and I've ask this myself countless times before. Once more, IE is doing things in its own weird way.

After some fiddling, I managed to make it work, by just slightly altering the syntax.

var selected2 = $('#myForm input[name="data\\[Payment\\]\\[value\\]"]').filter(':checked');
var value2 = selected2.val();
console.log(value2); 

Yep, that's right, I simply moved the :checked selector in its own filtercall and it worked. Took me a while to figure, but this finally turned out to be an easy fix.

You can test it yourself with this jsFiddle example. Don't forget to enable the log panel in IE8 by pressing F12 before running the code.

Enabling Flash calls to distant server when working locally

Default Flash security config prevents you from loading content from an external website when running a Flash file locally.

To prevent this, you have to go to this link and add the directory where your file is located.

Fortunatly, you can specify a top level directory and all subdirectories will inherit the authorization.

If you don't do this step, your Flash file will refuse to download external content.

First steps with puppet

I've never used puppet before this project. It in installed on our main server, monitoring the whole pool of servers used to host our game. It includes php hosts, sql data nodes and sql front servers as well as a couple of load balancer.

Puppet keeps a snapshot of the config that must be deployed on each server and keep them in sync. We just have to edit the puppet files, and the same config is replicated on each server.

I did not install puppet, our sysadmin did it, and I have no idea how he did it actually, I'm just fiddling with specific configurations here. But as it took me quite a bit to grasp, here is how it works, and some quirks to be aware of.

The puppet files

Puppet is installed in /etc/puppet. Our different modules are stored in/etc/puppet/modules. A module is a set of programs and config files that work together.

For example, we have one for the loadbalancers, one for the httppart, one for mysql, and even one for vim. I'm going to focus on the httpone, because it contains config files for memcache, lighttpd and php.

Modules are split in two parts : the manifest and the files. You can find each in /etc/puppet/modules/{modulename}/manifests and in /etc/puppet/modules/{modulename}/files.

As you can imagine, the manifest contains a list of directive telling puppet what programs need to be installed, what directories need to be present (with specific user and group) as well as what files need to be copied.

The files/ directory contains all the files that puppet might need for this module. It will copy them from this directory to the final servers, according to the manifest config.

Manifest syntax

The manifest uses a very simple syntax, using blocks of key => value to define config. I've mostly use the simple filedirectives, to copy files and create directories.

One can also use packagedirectories, to install things and handle dependencies, but I haven't messed with that (yet) so I won't talk about them here.

Here are a couple of simple file orders :

file { "/etc/lighttpd/scripts":
  ensure  => directory,
  owner   => root,
  group   => root,
  mode    => 550
}
file { "/etc/lighttpd/lighttpd.conf":
  owner   => root,
  group   => root,
  mode    => 644,
  source  => "puppet:///http/$target/lighttpd/lighttpd.conf",
  require => Package["lighttpd"],
  notify  => Service["lighttpd"],
}

The first part will create a/etc/lighttpd/scripts directory on the final server, with a user and group rootand 550 permissions mode.

The second part will copy the file stored in /etc/puppet/modules/http/{target}/lighttpd/lighttpd.conf on the puppet server to /etc/lighttpd/lighttpd.conf on the final server. I'm not quite sure what the requireand notifylines are for.

$target is a special variable. In my case it can take either the value valid or prod, allowing me to define custom config based on the environment. Valid and prod environment are supposed to have the exact same config, but separating them allow for testing a config change in one environment without potentially breaking the working prod environment. I'm not exactly sure how this $target var is set, this is one more thing our sysadmin did. Thanks to him.

Pulling changes

Puppet is configured to check its servers every 30mn. If some config files changed, or a directory isn't there anymore, it will recreate them, according to the manifest. But you can force the pulling of changes from the final servers themselves. Just log to one of the servers puppet is monitoring and type

$ sudo puppetd --test

The --test argument is a bit misleading. It actually does not seem to do any test at all, it simply pulls the puppet files from the puppet server and updates its files accordingly. It will notify you of any errors it might find, and even print a diffof the files changed.

One last thing

I've lost 30mn checking and re-checking my puppet file because my php.ini changes weren't updated. In fact, I just forgot to reload lighttpd...

PHP session on disk with lighttpd, and write access

On HappyLife, we used to use Memcache to handle the php sessions. We discovered a while back after much trouble that Memcache might not have been the best solution for sessions on a high traffic site. But this will be the subject of another post.

Anyway, I switched back to classical session, stored on disk.

Here's the relevant php.ini config

[Session]
session.save_handler = files
session.save_path = '/tmp/php'

/tmp/php should be writable by www-data, so

$ chown www-data:www-data /tmp/php

Working on Wednesday #11 : First steps with my Dreamplug

A while back, I ordered a Dreamplug. It's a micro computer more or less the size of a hard drive. It uses very little electricity, has ethernet, usb, sata and sd ports as well as bluetooth and wifi connectivity.

I wanted one to primarily work as a git and mercurial server to host my repos, as well as host a few websites I might be working on. As it's not the more powerful of device and will be using my home ADSL connection, it's also a good fit for testing web performance optimisations.

Actually, I have a lot of ideas for this plug (ssh tunnel, dropox-like server, etc).

First, let's establish a connection

To do any of that, you first have to log into your device. This first step might have been the one that caused me the much trouble.

I simply wanted to connect to the plug through SSH. I didn't want to plug it to my router because it would then have emit an open wifi network. Instead, I wanted to access it through a simple ethernet cable.

To do that I had to plug the Ethernet cable on the second slot (the one the closer to the lights) on the plug, then turn it on. I didn't manage to connect with the cable in the other plug nor if I plugged it after turning the device on.

What took me a while to figure is that the Dreamplug have its own ip address : 10.0.0.1. To enable transfer between your host and the plug, you have to set a fixed ip address to your ethernet (mine was eth0)

$ sudo ifconfig eth0 10.0.0.2

I chose 10.0.0.2 because it's close to the Dreamplug address and I will remember it better, but you can actually choose anything you want, it absolutely does not matter.

Connect through ssh

Once the first step is done, you are now able to connect to the device through SSH.

$ ssh root@10.0.0.1

The default password is nosoup4u. I chose to keep it for now, as I'll have to type it a lot, but I'll change it once everything is installed.

First thing you should do is create a new user. Using root for the day to day activities is a bad habit.

$ adduser pixelastic

You'll be prompted for password and other misc informations. Once done, we will give sudo power to this user.

On others Ubuntu machines, I usually do a adduser pixelastic admin or adduser pixelastic sudo, but this didn't work here. There wasn't even an admin group, and adding to sudo didn't seem to change anything.

Instead, I had to edit the /etc/sudoers file, using visudo.

If you've never used vi before, press i to enter edit mode, and vi will acts as a common editor. Press Esc when you've finished, then :x to save and exit

I added a new line just below the one starting with root and copied it, simply by changing the user name.

root    ALL=(ALL) ALL
pixelastic ALL=(ALL) ALL

Your new user should now have the sudo powers.

Unfortunatly, on my build one more thing was missing. It might not be the case on your plug, but we'd better check.

$ which sudo
 /usr/bin/sudo
$ ls -l /usr/bin/sudo
 -rwxr-xr-x 1 root root 114976 2011-02-14 08:08 /usr/bin/sudo

Check the permissions, if you have this output, then something is wrong. The sudo executable should have the s permissions, otherwise it won't work.

$ chmod u+s /usr/bin/sudo

Now, we can logout from the root session, and login back with our new user

$ ssh pixelastic@10.0.0.1
$ sudo pwd
 /home/pixelastic

What next ?

Now, one last sensible thing to do is to change the root password for something stronger, and even disable login as root through ssh. To do that, simply edit the /etc/ssh/sshd_config file and set PermitRootLogin to no.

You could also add your keys to the plug to avoid typing your password, install your softs, and link the plug to your router to make it accessible from the outside.

I plan to replace Ubuntu for Debian on my Dreamplug, I'll post about that later.