Permalink
Browse files

Perigrin's Obscenely Rude Re-factor : Step 1

* Move from Provision composing Roles to Provision using delegates

Using delegation over composition makes it easier to understand the model of what is going on. It also will make configuration easier in the long run as you can use Bread::Board or other pieces to assemble your application from a configuration file.

* Promote Configuration options to a First Class Object that is passed through

I'm not particularly happy with this one, but it makes passing through (and verifying) the configuration data much easier. The existing code looked like it had waffled between this style and using first-class methods on $self.

* Create a Role to define Provisioning Backend API

This allows us to keep the API we had set up before, where Oyster::Provision has create/delete/resize methods, but they are now delegated to backend objects rather composed directly into Oyster::Provision via Roles. This also means that creating a new backend class is simply a matter of writing to the API.

* Move Common backend logic into the API

The API had some common behaviors that came with properly initializing the object. Since this behavior was identical, and should be across all backends it makes sense to put it in a common location.

* Create EC2 specific tests

Time to start testing that we can actually provision something. I haven't actually run this test yet since doing so will require paying for AWS for an hour. I plan on doing this soon.
  • Loading branch information...
1 parent a9e65ce commit e42926883a7c46e144f405a3c89f501da22b801a @perigrin perigrin committed Jan 25, 2011
View
@@ -1,5 +1,7 @@
use strict;
use warnings;
+
package Oyster;
1;
+__END__
View
@@ -1,25 +1,30 @@
package Oyster::Provision;
-
use Moose;
-
-has 'api_username' => ( is => 'ro', isa => 'Str');
-has 'api_password' => ( is => 'ro', isa => 'Str');
-has 'name' => ( is => 'ro', isa => 'Str');
-has 'size' => ( is => 'ro', isa => 'Str');
-has 'image' => ( is => 'ro', isa => 'Str');
-has 'pub_ssh' => ( is => 'ro', isa => 'Str');
-has 'provision_backend' => (is => 'rw', isa => 'Str', required => 1, default => 'Oyster::Provision::Rackspace' );
-
-sub BUILD {
-
+use namespace::autoclean;
+
+has config => (
+ isa => 'HashRef',
+ is => 'ro',
+ required => 1,
+);
+
+has provision_class => (
+ isa => 'Str',
+ is => 'ro',
+ default => 'Oyster::Provision::Rackspace'
+);
+
+has 'provision_backend' => (
+ does => 'Oyster::Provision::API',
+ handles => 'Oyster::Provision::API',
+ lazy => 1,
+ builder => '_build_provision_backend',
+);
+
+sub _build_provision_backend {
my $self = shift;
-
- my $role = $self->provision_backend;
-
- eval "use $role";
- "$role"->meta->apply($self);
+ $self->provision_class->new( ${ $self->config } );
}
-
1;
__END__
@@ -31,10 +36,12 @@ Oyster::Provision - Provision an Oyster
=head1 SYNOPSIS
my $server = Oyster::Provision->new(
- name => 'Ostrica',
- size => '256',
- image => 'Meerkat',
- pub_ssh => "$ENV{HOME}/.ssh/id_rsa.pub",
+ config => {
+ name => 'Ostrica',
+ size => '256',
+ image => 'Meerkat',
+ pub_ssh => "$ENV{HOME}/.ssh/id_rsa.pub",
+ }
);
$server->create;
@@ -43,9 +50,9 @@ Oyster::Provision - Provision an Oyster
By default, the L<Oyster::Provision::Rackspace> backend
will be used.
-Each backend needs to accept at least the C<name>,
-C<size>, C<image> and C<pub_ssh> parameters. The meaning
-of these parameters may differ from one backend to another.
+Each backend needs to accept at least the C<name>, C<size>, C<image> and
+C<pub_ssh> configuration parameters. The meaning of these parameters may
+differ from one backend to another.
=head1 METHOS
@@ -0,0 +1,45 @@
+package Oyster::Provision::API;
+use Moose::Role;
+use namespace::autoclean;
+
+requires qw(
+ create
+ delete
+ resize
+ _build_api_username
+ _build_api_password
+);
+
+has config => (
+ isa => 'Oyster::Provision::Config',
+ is => 'ro',
+ required => 1,
+ coerce => 1,
+);
+
+has api_username => (
+ is => 'ro',
+ isa => 'Str',
+ lazy => 1,
+ builder => '_build_api_username',
+);
+
+sub _build_api_username {
+ return $ENV{CLOUDSERVERS_USER} if exists $ENV{CLOUDSERVERS_USER};
+ die "Need api_username or CLOUDSERVERS_USER in environment";
+}
+
+has api_password => (
+ is => 'ro',
+ isa => 'Str',
+ lazy => 1,
+ builder => '_build_api_password',
+);
+
+sub _build_api_password {
+ return $ENV{CLOUDSERVERS_KEY} if exists $ENV{CLOUDSERVERS_KEY};
+ die "Need api_password or CLOUDSERVERS_KEY in environment";
+}
+
+1;
+__END__
@@ -1,162 +0,0 @@
-package Oyster::Provision::AmazonEC2;
-use Carp;
-use Moose::Role;
-use Net::Amazon::EC2;
-
-has 'api_username' => ( is => 'ro', isa => 'Str', required => 1, lazy_build => 1);
-sub _build_api_username {
- my $self = shift;
- return $ENV{CLOUDSERVERS_USER} if exists $ENV{CLOUDSERVERS_USER};
- die "Need api_username or CLOUDSERVERS_USER in environment";
-}
-
-has 'api_password' => ( is => 'ro', isa => 'Str', required => 1, lazy_build => 1);
-sub _build_api_password {
- my $self = shift;
- return $ENV{CLOUDSERVERS_KEY} if exists $ENV{CLOUDSERVERS_KEY};
- die "Need api_password or CLOUDSERVERS_KEY in environment";
-}
-
-has ec2_oyster_key => (is => 'rw', isa => 'Str', default => "OysterDefault");
-
-sub ec2 {
- my $self = shift;
-
- my $ec2 = Net::Amazon::EC2->new(
- AWSAccessKeyId => $self->api_username,
- SecretAccessKey => $self->api_password,
- );
-
- my $key_pairs = $ec2->describe_key_pairs({ KeyName => $self->ec2_oyster_key });
-
- unless(defined($key_pairs)) {
-
- printf("Creating %s pair\n", $self->ec2_oyster_key);
- $ec2->create_key_pair({ KeyName => $self->ec2_oyster_key });
-
- }
-
- return $ec2;
-}
-
-sub create {
- my $self = shift;
-
- # Start 1 new instance from AMI: ami-XXXXXXXX
- my $instance = $self->ec2->run_instances(
- ImageId => $self->image() || "ami-be6e99d7",
- KeyName => $self->ec2_oyster_key,
- MinCount => 1,
- MaxCount => 1,
- );
-
-}
-
-sub delete {
- my $self = shift;
-
-}
-
-sub resize {
- my $self = shift;
-
- $self->config();
-}
-
-1;
-
-__END__
-
-=head1 NAME
-
-Oyster::Provision::AmazonEC2 -- Provision your Oyster on Amazon EC2
-
-=head1 SYNOPSIS
-
-Use the Amazon backend on your Oyster configuration file
-
-=head1 REQUIRED PARAMETERS
-
-The following are required to instantiate a backend:
-
-=over
-
-=item name
-
-The name of your new/existing rackspace server.
-
-pub_ssh
-
-This is a key name to pass to EC2
-
-=item size
-
-The size ID of the rackspace server you want to create.
-Use the following incantation to see them:
-
- perl -MNet::RackSpace::CloudServers -e'
- $r=Net::RackSpace::CloudServers->new(
- user=>$ENV{CLOUDSERVERS_USER},
- key=>$ENV{CLOUDSERVERS_KEY},
- );
- print map
- { "id $_->{id} ram $_->{ram} disk $_->{disk}\n" }
- $r->get_flavor_detail
- '
- id 1 ram 256 disk 10
- id 2 ram 512 disk 20
- id 3 ram 1024 disk 40
- id 4 ram 2048 disk 80
- id 5 ram 4096 disk 160
- id 6 ram 8192 disk 320
- id 7 ram 15872 disk 620
-
-=item image
-
-The image ID of the rackspace server you want to create.
-Use the following incantation to see them:
-
- perl -MNet::RackSpace::CloudServers -e'
- $r=Net::RackSpace::CloudServers->new(
- user=>$ENV{CLOUDSERVERS_USER},
- key=>$ENV{CLOUDSERVERS_KEY},
- );
- print map
- { "id $_->{id} name $_->{name}\n" }
- $r->get_image_detail
- '
- id 29 name Windows Server 2003 R2 SP2 x86
- id 69 name Ubuntu 10.10 (maverick)
- id 41 name Oracle EL JeOS Release 5 Update 3
- id 40 name Oracle EL Server Release 5 Update 4
- id 187811 name CentOS 5.4
- id 4 name Debian 5.0 (lenny)
- id 10 name Ubuntu 8.04.2 LTS (hardy)
- id 23 name Windows Server 2003 R2 SP2 x64
- id 24 name Windows Server 2008 SP2 x64
- id 49 name Ubuntu 10.04 LTS (lucid)
- id 14362 name Ubuntu 9.10 (karmic)
- id 62 name Red Hat Enterprise Linux 5.5
- id 53 name Fedora 13
- id 17 name Fedora 12
- id 71 name Fedora 14
- id 31 name Windows Server 2008 SP2 x86
- id 51 name CentOS 5.5
- id 14 name Red Hat Enterprise Linux 5.4
- id 19 name Gentoo 10.1
- id 28 name Windows Server 2008 R2 x64
- id 55 name Arch 2010.05
-
-Oyster only supports Linux images, specifically
-Ubuntu 10.10 (maverick).
-
-=item pub_ssh
-
-The public ssh key you would like copied to the
-new server's C</root/.ssh/authorized_keys> file
-to allow you to ssh in the box without providing
-a root password.
-
-=back
-
-=cut
Oops, something went wrong.

0 comments on commit e429268

Please sign in to comment.