File Coverage

blib/lib/Net/Cloudflare/DNS/Teal.pm
Criterion Covered Total %
statement 11 70 15.7
branch 0 14 0.0
condition n/a
subroutine 4 11 36.3
pod 0 7 0.0
total 15 102 14.7


line stmt bran cond sub pod time code
1             package Net::Cloudflare::DNS::Teal;
2              
3             our $VERSION = '0.05';
4              
5 1     1   55102 use 5.006;
  1         3  
6 1     1   5 use strict;
  1         2  
  1         27  
7 1     1   5 use warnings;
  1         1  
  1         23  
8              
9 1     1   397 use Net::Cloudflare::DNS;
  1         165534  
  1         592  
10              
11             # Initialize from environment variables
12             sub new_from_env {
13 0     0 0   my $class = shift;
14              
15 0           my %args = ();
16              
17 0           my $fails = 0;
18 0           my @keys = qw(CLOUDFLARE_ZONE_ID CLOUDFLARE_API_TOKEN CLOUDFLARE_TEAL CLOUDFLARE_GREEN CLOUDFLARE_BLUE);
19 0           for my $key (@keys) {
20 0 0         if (not defined $ENV{$key}) { print "$key is not set\n"; $fails++; }
  0            
  0            
21             }
22 0 0         die "Missing environment variables" if $fails;
23              
24 0           $args{teal} = $ENV{CLOUDFLARE_TEAL};
25 0           $args{blue} = $ENV{CLOUDFLARE_BLUE};
26 0           $args{green} = $ENV{CLOUDFLARE_GREEN};
27 0           my $dns = Net::Cloudflare::DNS->new(api_token=>$ENV{CLOUDFLARE_API_TOKEN}, zone_id=>$ENV{CLOUDFLARE_ZONE_ID});
28 0           $args{dns} = $dns;
29 0           bless \%args, $class;
30             }
31              
32             # Initialize from parameters passed to new
33             sub new {
34 0     0 0   my $class = shift;
35              
36 0           my %args = @_;
37              
38 0           my $dns = Net::Cloudflare::DNS->new(api_token=>$args{api_token}, zone_id=>$args{zone_id});
39 0           $args{dns} = $dns;
40 0           bless \%args, $class;
41             }
42              
43             # Return live URL
44             # e.g. "blue.example.com"
45             sub get_live {
46 0     0 0   my $self = shift;
47              
48 0           my $teal = $self->{teal};
49 0           my $blue = $self->{blue};
50 0           my $green = $self->{green};
51 0           my $dns = $self->{dns};
52              
53 0           my $res = $dns->get_records(name=>$teal, type=>'CNAME');
54 0           my $live = $res->{result}->[0]->{content};
55              
56 0           return $live;
57             }
58              
59             # Return live color
60             # e.g. "blue"
61             sub get_live_color {
62 0     0 0   my $self = shift;
63              
64 0           my $blue = $self->{blue};
65 0           my $green = $self->{green};
66              
67 0           my $url = $self->get_live();
68 0 0         return "blue" if ($url eq $blue);
69 0 0         return "green" if ($url eq $green);
70             # Live is not blue nor green
71 0           return "";
72             }
73              
74             # Return dormant URL
75             # e.g. "green.example.com"
76             sub get_dormant {
77 0     0 0   my $self = shift;
78              
79 0           my $teal = $self->{teal};
80 0           my $blue = $self->{blue};
81 0           my $green = $self->{green};
82 0           my $dns = $self->{dns};
83              
84 0           my $res = $dns->get_records(name=>$teal, type=>'CNAME');
85 0           my $live = $res->{result}->[0]->{content};
86 0 0         my $dormant = $live eq $blue ? $green : $blue; # Actually, both seems dormants (correct init?)
87              
88 0           return $dormant;
89             }
90              
91             # Return dormant color
92             # e.g. "green"
93             sub get_dormant_color {
94 0     0 0   my $self = shift;
95              
96 0           my $blue = $self->{blue};
97 0           my $green = $self->{green};
98              
99 0           my $url = $self->get_dormant();
100 0 0         return "blue" if ($url eq $blue);
101 0 0         return "green" if ($url eq $green);
102             }
103              
104             # Change target of your entrypoint
105             # example.com IN CNAME blue.example.com (or blue.example.pages.dev in case of Cloudflare Pages)
106             # teal...
107             # example.com IN CNAME green.example.com (or green.example.pages.dev in case of Cloudflare Pages)
108             # blue and green stay unchanged, but live and dormant are!
109             # If your DNS records are proxied (high chance they are), the DNS changes is effective instantly
110             sub teal {
111 0     0 0   my $self = shift;
112              
113 0           my $teal = $self->{teal};
114 0           my $dns = $self->{dns};
115              
116 0           my $res = $dns->get_records(name=>$teal, type=>'CNAME');
117 0           my $erid = $res->{result}->[0]->{id};
118 0           print "live was " . $self->get_live_color() . " (" . $self->get_live() . ")\n";
119 0           $dns->update_record($erid, type=>"CNAME", name=>$teal, content=>$self->get_dormant(), proxied=>\1);
120 0           print "live is now " . $self->get_live_color() . " (" . $self->get_live() . ")\n";
121 0           print "(dormant is " . $self->get_dormant() . ")\n";
122             }
123              
124             =head1 NAME
125              
126             Net::Cloudflare::DNS::Teal - Makes your Blue/Green deployments in Cloudflare easy!
127              
128             This module is intended to be used for managing Blue/Green deployments in Cloudflare DNS.
129              
130             It's compatible with Cloudflare Pages, but can be used for wider range of use cases.
131             (but then think about using a Cloudflare Load Balancer)
132              
133             Typical use of this module is:
134              
135             First retrieve dormant to deploy new version of a website/service.
136              
137             Second, do your tests and/or some canary testing.
138              
139             Third, replace live version using C.
140              
141             This module comes with small scripts for ease of command line use (e.g. in CircleCI or GitHub Actions)
142              
143             When used with Cloudflare Pages, don't use custom domains as blue and green values (but directly the *.pages.dev,
144             e.g. blue.example.pages.dev) as pointing custom domains to another custom domains is not possible.
145              
146              
147             As of today, this module is NOT production ready.
148              
149              
150             =head1 VERSION
151              
152             Version 0.05
153              
154             =cut
155              
156              
157              
158             =head1 SYNOPSIS
159              
160             Net::Cloudflare::DNS::Teal makes your Blue/Green deployments in Cloudflare easy!
161              
162             Demo
163              
164             my $teal = Net::Cloudflare::DNS::Teal->new(zone_id => $ENV{CLOUDFLARE_ZONE_ID},
165             api_token => $ENV{CLOUDFLARE_API_TOKEN},
166             teal => $ENV{CLOUDFLARE_TEAL},
167             blue => $ENV{CLOUDFLARE_BLUE},
168             green => $ENV{CLOUDFLARE_GREEN}
169             );
170             # OR
171             my $teal = Net::Cloudflare::DNS::Teal->new_from_env(); # Will use environment variables
172              
173             my $live = $teal->get_live();
174             my $live_color = $teal->get_live_color();
175             my $dormant = $teal->get_dormant();
176             my $dormant_color = $teal->get_dormant_color();
177             $teal->teal();
178             ...
179              
180             =head1 AUTHOR
181              
182             Thibault DUPONCHELLE, C<< >>
183              
184              
185             =head1 ACKNOWLEDGEMENTS
186              
187              
188             =head1 LICENSE AND COPYRIGHT
189              
190             This software is Copyright (c) 2023 by Thibault DUPONCHELLE.
191              
192             This is free software, licensed under:
193              
194             The Artistic License 2.0 (GPL Compatible)
195              
196              
197             =cut
198              
199             1; # End of Net::Cloudflare::DNS::Teal