-
Notifications
You must be signed in to change notification settings - Fork 16
/
ConfigureLeaderCommand.php
151 lines (129 loc) · 6.03 KB
/
ConfigureLeaderCommand.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<?php
namespace FoxxMD\LaravelElasticBeanstalkCron\Console\AWS;
use Aws\Ec2\Ec2Client;
use Functional as F;
use Illuminate\Console\Command;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
class ConfigureLeaderCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'aws:configure:leader';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Configure leader ec2 instance';
/**
* @var ConfigRepository
*/
protected $config;
public function __construct(ConfigRepository $config)
{
parent::__construct();
$this->config = $config;
}
public function handle()
{
$client = new Ec2Client([
'credentials' => [
'key' => $this->config->get('elasticbeanstalkcron.key', ''),
'secret' => $this->config->get('elasticbeanstalkcron.secret', ''),
],
'region' => $this->config->get('elasticbeanstalkcron.region', 'us-east-1'),
'version' => 'latest',
]);
$this->info('Initializing Leader Selection...');
// Only do cron setup if environment is configured to use it (This way we don't accidentally run on workers)
if ((bool) $this->config->get('elasticbeanstalkcron.enable', false)) {
// AL2 is using IMDSv2 which use session token
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
// get token first, check to see if we are in an instance
$ch = curl_init('http://169.254.169.254/latest/api/token'); //magic ip from AWS
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-aws-ec2-metadata-token-ttl-seconds: 21600']);
if ($token = curl_exec($ch)) {
$ch = curl_init('http://169.254.169.254/latest/meta-data/instance-id');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-aws-ec2-metadata-token: ' . $token]);
$instanceId = curl_exec($ch);
$this->info('Instance ID: ' . $instanceId);
// Get this instance metadata so we can find the environment it's running in
$tags = $info = $client->describeInstances([
'Filters' => [
[
'Name' => 'instance-id',
'Values' => [$instanceId],
],
],
])->get('Reservations')[0]['Instances'][0]['Tags'];
// Get environment name
$environmentName = F\first($tags, function ($tagArray) {
return $tagArray['Key'] == 'elasticbeanstalk:environment-name';
})['Value'];
$this->info('Environment: ' . $environmentName);
$this->info('Getting Instances with Environment: ' . $environmentName);
// Get instances that have this environment tagged
$info = $client->describeInstances([
'Filters' => [
[
'Name' => 'tag-value',
'Values' => [$environmentName],
],
],
]);
$instances = F\map($info->get('Reservations'), function ($i) {
return current($i['Instances']);
});
$this->info('Getting potential instances...');
// Only want instances that are running
$candidateInstances = F\select($instances, function ($instanceMeta) {
return $instanceMeta['State']['Code'] == 16;
});
$leader = false;
if (!empty($candidateInstances)) { //there are instances running
if (count($candidateInstances) > 1) {
// if there is more than one we sort by launch time and get the oldest
$this->info('More than one instance running, finding the oldest...');
$oldestInstance = F\sort($candidateInstances, function ($left, $right) {
return $left['LaunchTime'] <=> $right['LaunchTime'];
})[0];
} else {
$this->info('Only one instance running...');
$oldestInstance = reset($candidateInstances);
}
if ($oldestInstance['InstanceId'] == $instanceId) {
// if this instance is the oldest instance it's the leader
$leader = true;
}
} else {
$this->info('No candidate instances found. \'O Brave New World!');
$leader = true;
}
// No leader is running so we'll setup this one as the leader
// and create a cron entry to run the scheduler
if ($leader) {
$this->info('We are the Leader! Initiating Cron Setup');
$this->call('system:start:cron');
} else {
// Instance was found, don't do any cron stuff
$this->info('We are not a leader instance :( Maybe next time...');
$this->info('Leader should be running on Instance ' . $oldestInstance['InstanceId']);
}
$this->info('Leader Selection Done!');
} else {
// Probably be run from your local machine
$this->error('Did not detect an ec2 environment. Exiting.');
}
} else {
$this->info('USE_CRON env var not set. Exiting.');
}
}
}