File Coverage

blib/lib/IO/K8s/Role/CertManaged.pm
Criterion Covered Total %
statement 76 79 96.2
branch 11 22 50.0
condition 26 43 60.4
subroutine 12 12 100.0
pod 0 10 0.0
total 125 166 75.3


line stmt bran cond sub pod time code
1             package IO::K8s::Role::CertManaged;
2             # ABSTRACT: Role for cert-manager certificate and issuer management
3             our $VERSION = '1.009';
4 3     3   2008 use Moo::Role;
  3         8  
  3         23  
5 3     3   1797 use Carp qw(croak);
  3         21  
  3         3452  
6              
7             # --- Certificate methods ---
8              
9             sub for_domains {
10 2     2 0 284511 my ($self, @domains) = @_;
11 2   50     49 my $spec = $self->spec // {};
12 2   50     33 my $existing = $spec->{dnsNames} // [];
13 2         6 push @$existing, @domains;
14 2         9 $spec->{dnsNames} = $existing;
15 2         27 $self->spec($spec);
16 2         50 return $self;
17             }
18              
19             sub with_issuer {
20 2     2 0 3736 my ($self, $name, %opts) = @_;
21 2   100     32 my $spec = $self->spec // {};
22             $spec->{issuerRef} = {
23             name => $name,
24             kind => $opts{kind} // 'Issuer',
25 2 50 50     26 $opts{group} ? (group => $opts{group}) : (group => 'cert-manager.io'),
26             };
27 2         25 $self->spec($spec);
28 2         36 return $self;
29             }
30              
31             sub store_in_secret {
32 2     2 0 4280 my ($self, $secret_name) = @_;
33 2   100     25 my $spec = $self->spec // {};
34 2         16 $spec->{secretName} = $secret_name;
35 2         23 $self->spec($spec);
36 2         32 return $self;
37             }
38              
39             sub add_ip_san {
40 2     2 0 4147 my ($self, @ips) = @_;
41 2         670 require IO::K8s::Types::Net;
42 2         8 for my $ip (@ips) {
43 3 100       933 croak "'$ip' is not a valid IP address"
44             unless IO::K8s::Types::Net::IPAddress()->check($ip);
45             }
46 1   50     529 my $spec = $self->spec // {};
47 1   50     19 my $existing = $spec->{ipAddresses} // [];
48 1         16 push @$existing, @ips;
49 1         4 $spec->{ipAddresses} = $existing;
50 1         17 $self->spec($spec);
51 1         49 return $self;
52             }
53              
54             sub renew_before {
55 2     2 0 4618 my ($self, %opts) = @_;
56 2   100     37 my $spec = $self->spec // {};
57 2 50       19 if ($opts{days}) {
    0          
58 2         9 $spec->{renewBefore} = ($opts{days} * 24) . 'h0m0s';
59             } elsif ($opts{hours}) {
60 0         0 $spec->{renewBefore} = $opts{hours} . 'h0m0s';
61             }
62 2         30 $self->spec($spec);
63 2         54 return $self;
64             }
65              
66             # --- Issuer/ClusterIssuer methods ---
67              
68             sub letsencrypt {
69 5     5 0 27070 my ($self, %opts) = @_;
70 5 50       26 my $email = $opts{email} or croak 'email is required for letsencrypt';
71 5   100     22 my $production = $opts{production} // 0;
72 5 100       18 my $server = $production
73             ? 'https://acme-v02.api.letsencrypt.org/directory'
74             : 'https://acme-staging-v02.api.letsencrypt.org/directory';
75              
76 5   50     166 my $spec = $self->spec // {};
77             $spec->{acme} = {
78             email => $email,
79             server => $server,
80 5   50     81 privateKeySecretRef => { name => $opts{secret} // 'letsencrypt-account-key' },
81             };
82 5         80 $self->spec($spec);
83 5         130 return $self;
84             }
85              
86             sub self_signed {
87 1     1 0 3348 my ($self) = @_;
88 1   50     15 my $spec = $self->spec // {};
89 1         13 $spec->{selfSigned} = {};
90 1         13 $self->spec($spec);
91 1         22 return $self;
92             }
93              
94             sub ca {
95 2     2 0 3960 my ($self, %opts) = @_;
96 2   50     40 my $spec = $self->spec // {};
97             $spec->{ca} = {
98 2   66     48 secretName => $opts{secret} // croak('secret is required for ca'),
99             };
100 1         12 $self->spec($spec);
101 1         19 return $self;
102             }
103              
104             sub add_http01_solver {
105 2     2 0 6 my ($self, %opts) = @_;
106 2   50     25 my $spec = $self->spec // {};
107 2   50     15 my $acme = $spec->{acme} //= {};
108 2   50     9 my $solvers = $acme->{solvers} //= [];
109 2         8 my $solver = { http01 => { ingress => {} } };
110 2 100       8 $solver->{http01}{ingress}{class} = $opts{class} if $opts{class};
111 2         5 push @$solvers, $solver;
112 2         25 $self->spec($spec);
113 2         30 return $self;
114             }
115              
116             sub add_dns01_solver {
117 1     1 0 4 my ($self, %opts) = @_;
118 1   50     12 my $spec = $self->spec // {};
119 1   50     17 my $acme = $spec->{acme} //= {};
120 1   50     5 my $solvers = $acme->{solvers} //= [];
121 1         2 my %dns01;
122 1 50       16 if ($opts{provider} eq 'cloudflare') {
    0          
123             $dns01{cloudflare} = {
124 1 50 50     15 $opts{secret} ? (apiTokenSecretRef => { name => $opts{secret}, key => $opts{key} // 'api-token' }) : (),
125             };
126             } elsif ($opts{provider} eq 'route53') {
127             $dns01{route53} = {
128 0 0       0 $opts{region} ? (region => $opts{region}) : (),
129             };
130             } else {
131 0         0 $dns01{$opts{provider}} = {};
132             }
133 1         4 push @$solvers, { dns01 => \%dns01 };
134 1         14 $self->spec($spec);
135 1         18 return $self;
136             }
137              
138             1;
139              
140             __END__