File Coverage

blib/lib/Testcontainers/ContainerRequest.pm
Criterion Covered Total %
statement 59 68 86.7
branch 18 22 81.8
condition n/a
subroutine 6 6 100.0
pod 0 1 0.0
total 83 97 85.5


line stmt bran cond sub pod time code
1             package Testcontainers::ContainerRequest;
2             # ABSTRACT: Container configuration request
3              
4 4     4   101774 use strict;
  4         8  
  4         169  
5 4     4   19 use warnings;
  4         8  
  4         181  
6 4     4   491 use Moo;
  4         6717  
  4         22  
7 4     4   2409 use Carp qw( croak );
  4         9  
  4         238  
8 4         3462 use Testcontainers::Labels qw(
9             default_labels merge_custom_labels
10 4     4   1798 );
  4         9  
11              
12             our $VERSION = '0.001';
13              
14             =head1 DESCRIPTION
15              
16             Represents the configuration for creating a Docker container, similar to
17             Go's C. Built internally by L
18             from the options passed to it.
19              
20             =cut
21              
22             has image => (
23             is => 'ro',
24             required => 1,
25             );
26              
27             has exposed_ports => (
28             is => 'ro',
29             default => sub { [] },
30             );
31              
32             has env => (
33             is => 'ro',
34             default => sub { {} },
35             );
36              
37             has labels => (
38             is => 'ro',
39             default => sub { {} },
40             );
41              
42             has _internal_labels => (
43             is => 'ro',
44             default => sub { {} },
45             );
46              
47             has cmd => (
48             is => 'ro',
49             default => sub { [] },
50             );
51              
52             has entrypoint => (
53             is => 'ro',
54             default => sub { [] },
55             );
56              
57             has name => (
58             is => 'ro',
59             default => undef,
60             );
61              
62             has session_id => (
63             is => 'ro',
64             lazy => 1,
65             default => sub { Testcontainers::Labels::session_id() },
66             );
67              
68             has wait_for => (
69             is => 'ro',
70             default => undef,
71             );
72              
73             has tmpfs => (
74             is => 'ro',
75             default => sub { {} },
76             );
77              
78             has startup_timeout => (
79             is => 'ro',
80             default => 60,
81             );
82              
83             has privileged => (
84             is => 'ro',
85             default => 0,
86             );
87              
88             has network_mode => (
89             is => 'ro',
90             default => undef,
91             );
92              
93             has networks => (
94             is => 'ro',
95             default => sub { [] },
96             );
97              
98             sub to_docker_config {
99 5     5 0 35 my ($self) = @_;
100              
101 5         16 my $config = {
102             Image => $self->image,
103             };
104              
105             # Exposed ports (Docker API format: { "80/tcp": {} })
106 5 100       6 if (@{$self->exposed_ports}) {
  5         17  
107 2         3 my %exposed;
108 2         2 for my $port (@{$self->exposed_ports}) {
  2         5  
109 3 100       10 my $normalized = $port =~ m{/} ? $port : "$port/tcp";
110 3         7 $exposed{$normalized} = {};
111             }
112 2         5 $config->{ExposedPorts} = \%exposed;
113             }
114              
115             # Environment variables
116 5 100       18 if (%{$self->env}) {
  5         14  
117 1         1 $config->{Env} = [ map { "$_=$self->{env}{$_}" } sort keys %{$self->env} ];
  2         6  
  1         4  
118             }
119              
120             # Labels — merge standard Testcontainers labels with user-supplied ones.
121             # User labels starting with 'org.testcontainers' are rejected.
122             # Internal (framework) labels bypass the prefix check.
123 5         59 my %defaults = default_labels($self->session_id);
124 5         15 my %merged = merge_custom_labels(\%defaults, $self->labels);
125             # Layer in framework-internal labels (e.g. org.testcontainers.module)
126 4         7 for my $k (keys %{$self->_internal_labels}) {
  4         9  
127 0         0 $merged{$k} = $self->_internal_labels->{$k};
128             }
129 4         9 $config->{Labels} = \%merged;
130              
131             # Command
132 4 100       4 if (@{$self->cmd}) {
  4         8  
133 1         3 $config->{Cmd} = $self->cmd;
134             }
135              
136             # Entrypoint
137 4 50       4 if (@{$self->entrypoint}) {
  4         38  
138 0         0 $config->{Entrypoint} = $self->entrypoint;
139             }
140              
141             # HostConfig
142 4         7 my $host_config = {};
143              
144             # Port bindings - publish all exposed ports
145 4 100       4 if (@{$self->exposed_ports}) {
  4         8  
146 2         2 my %port_bindings;
147 2         3 for my $port (@{$self->exposed_ports}) {
  2         4  
148 3 100       8 my $normalized = $port =~ m{/} ? $port : "$port/tcp";
149 3         22 $port_bindings{$normalized} = [{ HostIp => '', HostPort => '' }];
150             }
151 2         4 $host_config->{PortBindings} = \%port_bindings;
152 2         5 $host_config->{PublishAllPorts} = \1;
153             }
154              
155             # Tmpfs mounts
156 4 100       4 if (%{$self->tmpfs}) {
  4         10  
157 1         2 $host_config->{Tmpfs} = $self->tmpfs;
158             }
159              
160             # Privileged mode
161 4 50       23 if ($self->privileged) {
162 0         0 $host_config->{Privileged} = \1;
163             }
164              
165             # Network mode
166 4 50       8 if ($self->network_mode) {
167 0         0 $host_config->{NetworkMode} = $self->network_mode;
168             }
169              
170 4         6 $config->{HostConfig} = $host_config;
171              
172             # NetworkingConfig for named networks
173 4 50       5 if (@{$self->networks}) {
  4         9  
174 0         0 my %endpoints;
175 0         0 for my $net (@{$self->networks}) {
  0         0  
176 0         0 $endpoints{$net} = {};
177             }
178             $config->{NetworkingConfig} = {
179 0         0 EndpointsConfig => \%endpoints,
180             };
181             }
182              
183 4         10 return $config;
184             }
185              
186             =method to_docker_config
187              
188             Converts the request into a Docker API compatible configuration hashref
189             suitable for container creation.
190              
191             =cut
192              
193             1;