File Coverage

blib/lib/Test/Docker/MySQL.pm
Criterion Covered Total %
statement 15 75 20.0
branch 0 14 0.0
condition n/a
subroutine 5 13 38.4
pod 3 6 50.0
total 23 108 21.3


line stmt bran cond sub pod time code
1             package Test::Docker::MySQL;
2             # ABSTRACT: Test::Docker::MySQL is a module to launch MySQL in docker containers.
3 1     1   23985 use strict;
  1         3  
  1         43  
4 1     1   4 use constant DEBUG => $ENV{DEBUG_TEST_DOCKER_MYSQL};
  1         2  
  1         97  
5             our $VERSION = '0.02';
6 1     1   2248 use DBI;
  1         19704  
  1         65  
7 1     1   1555 use IPC::Run ();
  1         56712  
  1         29  
8 1     1   1084 use Time::HiRes 'sleep';
  1         1876  
  1         6  
9              
10             sub WARN {
11 0     0 0   my $msg = join " ", @_;
12 0           chomp $msg;
13 0           warn sprintf "[%s %.5f] %s\n", __PACKAGE__, Time::HiRes::time, $msg;
14             }
15              
16             sub new {
17 0     0 1   my ($class, %args) = @_;
18              
19 0           my $tag = delete $args{tag};
20 0           my $ports = delete $args{ports};
21              
22 0 0         bless {
    0          
23             tag => defined $tag ? $tag : 'punytan/p5-test-docker-mysql',
24             ports => defined $ports ? $ports : [ 55500 .. 55555 ],
25             container_ids => [],
26             }, $class;
27             }
28              
29             sub get_port {
30 0     0 1   my $self = shift;
31 0           $self->docker('ps'); # guarantee docker connection
32 0           my $port = $self->find_port;
33 0           return $port;
34             }
35              
36             sub find_port {
37 0     0 0   my $self = shift;
38              
39 0           while (1) {
40 0           my $port = $self->{ports}[ int rand(scalar @{$self->{ports}}) ];
  0            
41 0           DEBUG && WARN "trying port $port";
42 0           my $container = eval { $self->docker(run => -p => "$port:3306", -d => $self->{tag}) };
  0            
43 0 0         if (my $e = $@) {
44 0           DEBUG && WARN "Failed to launch container: $e";
45 0           next;
46             } else {
47 0           push @{$self->{container_ids}}, $container;
  0            
48 0           eval { $self->_dbh($port) };
  0            
49 0 0         if (my $e = $@) {
50 0           DEBUG && WARN "Failed to get dbh: $e";
51 0           next;
52             } else {
53 0           return $port;
54             }
55             }
56             }
57              
58 0           die "Failed to allocate new container";
59             }
60              
61             sub _dbh {
62 0     0     my ($self, $port) = @_;
63              
64 0           my $dbh;
65              
66 0           while (not defined $dbh) {
67 0           my $dsn = "dbi:mysql:database=mysql;host=127.0.0.1;port=$port";
68              
69 0           DEBUG && WARN "Connecting dsn: $dsn";
70              
71 0           $dbh = eval { DBI->connect($dsn, 'root', '', { RaiseError => 1 }) };
  0            
72 0 0         if (my $e = $@) {
73 0           DEBUG && WARN "Failed to connect mysql server: $e";
74 0           sleep 0.2;
75             }
76             };
77              
78 0           DEBUG && WARN "Creating database: docker_mysql";
79 0           eval { $dbh->do("CREATE DATABASE docker_mysql") };
  0            
80 0 0         if (my $e = $@) {
81 0           DEBUG && WARN "Failed to get lock (create database): $e";
82             # Skip this container
83             }
84              
85 0           DEBUG && WARN "Created database: docker_mysql";
86             }
87              
88             sub docker {
89 0     0 1   my ($self, $cmd, @args) = @_;
90 0           $self->cmd(docker => $cmd, @args);
91             }
92              
93             sub cmd {
94 0     0 0   my ($self, @args) = @_;
95              
96 0           DEBUG && WARN sprintf "Run [ %s ]", join ' ', @args;
97 0           my $is_success = IPC::Run::run [ @args ], \my $stdin, \my $stdout, \my $stderr;
98 0 0         if ($is_success) {
99 0           chomp $stdout;
100 0           return $stdout;
101             } else {
102 0           die $stderr;
103             }
104             }
105              
106             sub DESTROY {
107 0     0     my $self = shift;
108 0           for my $container_id (@{$self->{container_ids}}) {
  0            
109 0           DEBUG && WARN "Destroying container: $container_id";
110 0           $self->docker(kill => $container_id);
111             }
112             }
113              
114             1;
115             __END__