Search

Sara Trice

Just a programmin', bellydancin', cake bakin' kinda girl

Category

tech

Quickie: Creating a new EC2 instance with the AWS-SDK for Ruby version 2

Because the documentation for the version 2 SDK has made this incredibly unclear, here you go.

ec2 = Aws::EC2::Client.new(region: 'us-west-2')
res = Aws::EC2::Resource.new(client: ec2)

#remove dry_run: true from the parameters to actually create an instance
inst = res.create_instances(image_id:'ami-123456', min_count:1, max_count:1, instance_type: 't1.micro', dry_run: true)

Some helpful methods on the instance returned:
inst.public_ip_address
inst.state
inst.data
To stop the instance:
ec2.stop_instances(instance_ids:[inst.id])

To start the instance after stopping:
ec2.start_instances(instance_ids:[inst.id])

To terminate the instance:
ec2.terminate_instances(instance_ids:[inst.id])
Advertisements

Tech and feminism

Anyone who knows me would know that I am not particularly militant when it comes to feminism. I am not a man-hater. I like men. I have many menfriends. I married one. I think they’re neat.

But crap like this happens, and I have to say something: http://stoptechfeminism.tumblr.com/

Talking about acts of sexual assault at tech conferences (which is apparently what engendered the above tumblr) does not equal looking for a sacrificial lamb. It is addressing a very real problem in the culture, the culture many of us belong to. And people need to know that when acts of sexual assault or harassment happen in our culture, there are very real consequences, not only to the people involved, but to the community at large.

Many of us want our culture to expand and include all kinds of different people with new, amazing ideas and different ways of thinking. People do not join a community in which they feel threatened from the get-go. We do not need the talent pool to stagnate, people. Also? Comparing feminists to the Westboro Church is not doing you any favors.

Sidekiq on Engine Yard using a Redis utility instance

So I’ve been running Sidekiq on an Engine Yard instance for a while, but haven’t updated the setup since it was installed. I wanted to fire up a utility instance to run Redis on to take some of the load off the master application instance, so time to dig in.

First thing I did was to update my fork of the ey-cloud-recipes repo. The fabulous Jim Neath has updated the Sidekiq recipe and it’s much better than the old one.

Most of the instructions here will get you where you need to be:
https://github.com/engineyard/ey-cloud-recipes/tree/master/cookbooks/sidekiq

However, it won’t have you connecting to Redis on a utility instance. It’ll have you connecting to redis on localhost or maybe on your database instance, which is fine if your load isn’t heavy.

Let’s fire up a Redis utility instance (in other words, go to your app environment in EY, click “Add”, select utility instance, and name it “redis”). Note that if you want to run sidekiq on a utility instance as well instead of your application instance, fire up an additional utility instance named “sidekiq”.

In your cookbooks/main/recipes/default.rb cookbook, uncomment these lines:

include_recipe "sidekiq"
include_recipe "redis-yml"
include_recipe "redis"

In your cookbooks/redis-yml/recipes/default.rb file, change the first line to also add redis.yml on utility instances:

if ['app_master', 'app', 'util'].include?(node[:instance_role])

Add this to your before_restart.rb deploy hook:

on_app_servers_and_utilities do
  # link redis.yml in current/config to shared/config
  run "ln -nfs #{config.shared_path}/config/redis.yml #{config.release_path}/config/redis.yml"
end

Then, create a file in your config/initializers directory called sidekiq.rb that looks like this (replacing “<yournamespace>” with whatever you want your redis namespace to be, mine is “<appname>:<environment>”). This was pretty much taken wholesale from Add Redis To Your Application.

# check if the redis.yml file exists - if you are running your staging env as 
# a solo instance without a redis utility instance, it won't
if File.exists?(Rails.root + 'config/redis.yml')
  # Load the redis.yml configuration file
  redis_config = YAML.load_file(Rails.root + 'config/redis.yml')[Rails.env]

  if redis_config
    Sidekiq.configure_server do |config|
      config.redis = {
         namespace: "<yournamespace>",
         url: "redis://#{redis_config['host']}:#{redis_config['port']}"
      }
    end
    Sidekiq.configure_client do |config|
      config.redis = {
         namespace: "<yournamespace>",
         url: "redis://#{redis_config['host']}:#{redis_config['port']}"
      }
    end
  end

else

  Sidekiq.configure_server do |config|
    config.redis = {namespace: "<yournamespace>"}
  end
  Sidekiq.configure_client do |config|
    config.redis = {namespace: "<yournamespace>"}
  end

end

Upload and apply your recipes and deploy your application.

Finally, check your log directory on your app instance to make sure you are connecting to the right instance. in /data//current/log/sidekiq_0.log it should say something like:

2013-08-30T23:11:25Z 22052 TID-17e8eg INFO: Booting Sidekiq 2.6.0 with Redis at redis://(some internal ip):6379/0

It should NOT say:

2013-08-30T10:27:50Z 25034 TID-17uk98 INFO: Booting Sidekiq 2.6.0 with Redis at redis://localhost:6379/0

You can check to make sure you are connecting to the right instance by checking your node info when you SSH into the app instance:

sudo gem install jazor
sudo jazor /etc/chef/dna.json

Look for a node like this:

  "utility_instances": [
    {
      "name": "redis",
      "hostname": "(some internal ip)"
    }

This hostname IP should match the one in your sidekiq log. Ta da!

Checking your SSL Certificate setup

Had a problem recently where the SSL cert expired and I didn’t add the intermediate certs when putting the new one in an EC2 instance. Made for some annoying troubleshooting.

This service will check your setup and make sure everything is correct:

http://www.digicert.com/help/

Ruby on Rails and FTPS

If you’re unlucky enough to have to connect to an FTPS server (SFTP ends up being much easier), the Double Bag FTPS gem makes things much easier.

A few finer points to note:

  • “verify_mode: OpenSSL::SSL::VERIFY_NONE”¬†makes your life easier in development mode – not to mention some FTPS sites won’t even have one
  • Most servers don’t like active mode, so explicitly use passive mode. Your connection ends up hanging without it.

So usage is:

DoubleBagFTPS.open(host, user, password, nil, DoubleBagFTPS::EXPLICIT, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |ftps|
 ftps.passive = true
 ftps.put("myfile.txt", filename)
 end

Paperclip, Amazon S3 Storage, the Amazon AWS-SDK gem, and Error: “No such file or directory”

So at work, we switched from using the aws-s3 gem (unofficial) to the aws-sdk gem (official), since the new paperclip gem version required it. Added a new initializer file and made a few changes in how we were opening the S3 connection. No big deal.

Until we got to the part where we were opening a remote URL, reading its contents (an MP3), and trying to save it to a file on S3. (Before you get all riled up about piracy, it’s a voice generator service that we pay for.) So after switching gems, all we got when trying to grab the remote file was:

  No such file or directory - http://www.blahblahblah...

At first I thought it might be the service, but plugging in any other url didn’t work either. Arrgh! The code didn’t change in that respect! WTH?

Yeah well, apparently something did with the gem, because putting:

  require 'open-uri'

at the top of the controller fixed it all up.

Hope this saves you some time/hair pulling/frustration.

Indexing users that belong to groups with ancestry and thinking sphinx

So you’ve created a table of groups that are in a hierarchy with the gem ancestry, and you’ve created a table of users, and you’ve joined users to groups with a join table.

And now you want to use Thinking Sphinx, which is awesome for searching. So you think, hey, wouldn’t it be great if I could do a search for all users in a group’s subtree? Except that Thinking Sphinx cares not for your puny ancestry methods, and :subtree_ids doesn’t work in an index.

Fear not! I’ve done the heavy lifting for you.

in user.rb:
define_index do
  indexes last_name
  has groups(:id), :as => :direct_group_ids
  has "CONCAT_WS('/',groups.id,groups.ancestry)", :as => :group_ids, :type => :multi
end

The first attribute is so that the join is made from users to groups. The second actually creates the multi-value attribute that you can search on. So from there you can do:

User.search(:with_all => {:group_ids => [1]})

That will give you all the users that belong to the subtree, including the root group (in this case, the group with the id of “1”).

That being said, if you only want the users from sub-groups of the group you’re searching on (i.e. you never want users that are directly attached to the group you’re searching on), you can instead do this:

in user.rb:
define_index do
  indexes last_name
  has groups(:ancestry), :as => :group_ids, :type => :multi
end

So if you have group 1 which has group 2 and group 3 as children, the first example will give you all the users attached to all 3 groups; the second example will only give you the users attached to groups 2 and 3.

One last gotcha: if you’re running the search in console, remember to add “:per_page => 100” or however many entries you want back, or else by default you only get 20. Don’t want you to headdesk when you can’t figure out why it’s returning 20 users when it’s supposed to be returning 75.

Happy indexing!

Avoiding Ruby hash conditionals in Ruby on Rails

This gets really old:

if params[:teacher] && params[:teacher][:id] ...

so instead, do this:

if params[:teacher].try(:[], :id)

or do it a lot more:

name = params[:company][:owner][:name] if params[:company] and params[:company][:owner] and params[:company][:owner][:name]

turns into:

name = params.try(:[], :company).try(:[], :owner).try(:[], :name)

Yay for Stack Overflow!

Quickie: How to add a blank option to options_from_collection_for_select

<%= select_tag "some_select", ("<option></option>" + options_from_collection_for_select(@foo, "id", "item")).html_safe %>

Create a free website or blog at WordPress.com.

Up ↑