diff --git a/docs/modules/Conch::Command::fix_usernames.md b/docs/modules/Conch::Command::fix_usernames.md new file mode 100644 index 000000000..2767682a9 --- /dev/null +++ b/docs/modules/Conch::Command::fix_usernames.md @@ -0,0 +1,13 @@ +# NAME + +fix\_usernames - fixes old usernames in the database + +# SYNOPSIS + +``` +bin/conch fix_usernames [long options...] + + -n --dry-run dry-run (no changes are made) + + --help print usage message and exit +``` diff --git a/docs/modules/Conch::Command::workspace_to_build.md b/docs/modules/Conch::Command::workspace_to_build.md new file mode 100644 index 000000000..d7f15fd7e --- /dev/null +++ b/docs/modules/Conch::Command::workspace_to_build.md @@ -0,0 +1,13 @@ +# NAME + +workspace\_to\_build - convert workspace content to a build + +# SYNOPSIS + +``` +bin/conch workspace_to_build [long options...] [workspace name] ... + + -n --dry-run dry-run (no changes are made) + + --help print usage message and exit +``` diff --git a/docs/modules/index.md b/docs/modules/index.md index ec44f14a4..f8afbeef3 100644 --- a/docs/modules/index.md +++ b/docs/modules/index.md @@ -7,9 +7,11 @@ * [Conch::Command::clean_roles](../modules/Conch::Command::clean_roles) * [Conch::Command::create_token](../modules/Conch::Command::create_token) * [Conch::Command::create_user](../modules/Conch::Command::create_user) +* [Conch::Command::fix_usernames](../modules/Conch::Command::fix_usernames) * [Conch::Command::merge_validation_results](../modules/Conch::Command::merge_validation_results) * [Conch::Command::thin_device_reports](../modules/Conch::Command::thin_device_reports) * [Conch::Command::update_validation_plans](../modules/Conch::Command::update_validation_plans) +* [Conch::Command::workspace_to_build](../modules/Conch::Command::workspace_to_build) * [Conch::Command::workspaces](../modules/Conch::Command::workspaces) * [Conch::Controller::Build](../modules/Conch::Controller::Build) * [Conch::Controller::Datacenter](../modules/Conch::Controller::Datacenter) diff --git a/docs/scripts/conch.md b/docs/scripts/conch.md index 08e762359..fa5ac732f 100644 --- a/docs/scripts/conch.md +++ b/docs/scripts/conch.md @@ -38,6 +38,10 @@ Conch-specific commands are: Create a new user +- [fix\_usernames](../modules/Conch::Command::fix_usernames) + + fixes Joyent usernames so they are not the same as the email + - [merge\_validation\_results](../modules/Conch::Command::merge_validation_results) Collapse duplicate validation\_result rows together @@ -50,6 +54,10 @@ Conch-specific commands are: bring validation\_plans up to date with new versions of all validations +- [workspace\_to\_build](../modules/Conch::Command::workspace_to_build) + + convert workspace content to a build + - [workspaces](../modules/Conch::Command::workspaces) View all workspaces in their heirarchical order diff --git a/lib/Conch/Command/fix_usernames.pm b/lib/Conch/Command/fix_usernames.pm new file mode 100644 index 000000000..90c5bb326 --- /dev/null +++ b/lib/Conch/Command/fix_usernames.pm @@ -0,0 +1,64 @@ +package Conch::Command::fix_usernames; + +=pod + +=head1 NAME + +fix_usernames - fixes old usernames in the database + +=head1 SYNOPSIS + + bin/conch fix_usernames [long options...] + + -n --dry-run dry-run (no changes are made) + + --help print usage message and exit + +=cut + +use Mojo::Base 'Mojolicious::Command', -signatures; +use Getopt::Long::Descriptive; + +has description => 'fixes Joyent usernames so they are not the same as the email'; + +has usage => sub { shift->extract_usage }; # extracts from SYNOPSIS + +sub run ($self, @opts) { + local @ARGV = @opts; + my ($opt, $usage) = describe_options( + # the descriptions aren't actually used anymore (mojo uses the synopsis instead)... but + # the 'usage' text block can be accessed with $usage->text + 'clean_roles %o', + [ 'dry-run|n', 'dry-run (no changes are made)' ], + [], + [ 'help', 'print usage message and exit', { shortcircuit => 1 } ], + ); + + $self->app->schema->txn_do(sub { + my $user_rs = $self->app->db_user_accounts + ->active + ->search({ name => { '=' => \'email' } }) + ->search({ email => { -like => '%@joyent.com%' } }); + + print STDERR '# examining '.$user_rs->count.' users...'."\n"; + + while (my $user = $user_rs->next) { + print STDERR '# considering name='.$user->name.', email='.$user->email."\n"; + my ($userid, $host) = split(/@/, $user->name, 2); + next if not $userid; + next if $userid !~ /\./; + next if $userid =~ /\+/; + + my ($first, $last) = split(/\./, $userid, 2); + $first = ucfirst $first; + $last = ucfirst $last; + + print '# name will become '.$first.' '.$last."\n"; + next if $opt->dry_run; + + $user->update({ name => $first.' '.$last }); + } + }); +} + +1; diff --git a/lib/Conch/Command/workspace_to_build.pm b/lib/Conch/Command/workspace_to_build.pm new file mode 100644 index 000000000..8d116d169 --- /dev/null +++ b/lib/Conch/Command/workspace_to_build.pm @@ -0,0 +1,100 @@ +package Conch::Command::workspace_to_build; + +=pod + +=head1 NAME + +workspace_to_build - convert workspace content to a build + +=head1 SYNOPSIS + + bin/conch workspace_to_build [long options...] [workspace name] ... + + -n --dry-run dry-run (no changes are made) + + --help print usage message and exit + +=cut + +use Mojo::Base 'Mojolicious::Command', -signatures; +use Getopt::Long::Descriptive; +use List::Util 'minstr'; + +has description => 'convert workspace content to a build'; + +has usage => sub { shift->extract_usage }; # extracts from SYNOPSIS + +sub run ($self, @opts) { + local @ARGV = @opts; + my ($opt, $usage) = describe_options( + # the descriptions aren't actually used anymore (mojo uses the synopsis instead)... but + # the 'usage' text block can be accessed with $usage->text + 'clean_roles %o', + [ 'dry-run|n', 'dry-run (no changes are made)' ], + [], + [ 'help', 'print usage message and exit', { shortcircuit => 1 } ], + ); + + my @workspace_names = @ARGV; + + foreach my $workspace_name (@workspace_names) { + $self->app->schema->txn_do(sub { + my $workspace = $self->app->db_workspaces->find({ name => $workspace_name }); + my $build = $self->app->db_builds->find({ name => $workspace_name }); + + if (not $build) { + # find the earliest create date of each rack and device in the workspace and + # use that as build.started + my $device_created_rs = $workspace + ->related_resultset('workspace_racks') + ->related_resultset('rack') + ->related_resultset('device_locations') + ->related_resultset('device') + ->order_by('created') + ->rows(1) + ->hri + ->get_column('created'); + + my $rack_created_rs = $workspace + ->related_resultset('workspace_racks') + ->related_resultset('rack') + ->order_by('created') + ->rows(1) + ->hri + ->get_column('created'); + + $build = $self->app->db_builds->create({ + name => $workspace_name, + description => $workspace->description, + started => minstr($device_created_rs->single, $rack_created_rs->single), + }); + } + + $build->find_or_create_related('user_build_roles', { + user_id => { '=' => $self->app->db_user_accounts->search({ email => 'ether@joyent.com' })->columns('id')->as_query }, + role => 'admin', + }); + + $build->find_or_create_related('organization_build_roles', { + organization_id => { '=' => $self->app->db_organizations->search({ name => 'joyent' })->columns('id')->as_query }, + role => 'ro', + }); + + # now put all of the workspace's racks into the build, if they weren't already in + # another build + $self->app->db_racks->search( + { 'rack.build_id' => undef, 'workspace.name' => $workspace_name }, + { join => { workspace_racks => 'workspace' } }, + ) ->update({ build_id => $build->id }); + + # now put all the racks's devices into the build, if they weren't already in + # another build + $self->app->db_devices->search( + { 'device.build_id' => undef, 'workspace.name' => $workspace_name }, + { join => { device_location => { rack => { workspace_racks => 'workspace' } } } }, + )->update({ build_id => $build->id }); + }); + } +} + +1;