Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0296626
format
stevethomas Jan 22, 2025
5144071
wip it
stevethomas Jan 22, 2025
939604e
wip it
stevethomas Jan 22, 2025
a7aa013
wip
stevethomas Jan 22, 2025
e671e7a
sync some progress
stevethomas Jan 22, 2025
45d09d9
pull some improvements to CI syncing
stevethomas Jan 24, 2025
1614e6e
merge some safe changes from main
stevethomas Jan 29, 2025
d8e19e1
consolidate all web/queue/scheduler references to use ServerGroup enum
stevethomas Jan 29, 2025
47ce03a
merge several fixes from main
stevethomas Jan 29, 2025
8801193
wip
stevethomas Jan 29, 2025
2925d4f
wip it
stevethomas Jan 29, 2025
99b73b0
prefix more stubs with yolo-<environment>
stevethomas Jan 29, 2025
0a3296c
wip
stevethomas Jan 29, 2025
b89b25f
implement SteppedCommand on start and stop
stevethomas Jan 29, 2025
c2ae768
move namespaces
stevethomas Jan 30, 2025
28789ad
merge changes
stevethomas Jan 30, 2025
eef0228
wip
stevethomas Jan 30, 2025
b545b3f
merge various improvements from main
stevethomas Jan 30, 2025
0a7f240
wip
stevethomas Jan 31, 2025
0221c73
wip
stevethomas Jan 31, 2025
3346e79
wip
stevethomas Jan 31, 2025
038a4a6
sync fixes from main
stevethomas Feb 6, 2025
8e17223
wip it
stevethomas Sep 8, 2025
c31bd6c
wip
stevethomas Sep 8, 2025
3133c96
Merge branch 'main' into next
stevethomas Sep 8, 2025
06e2eb1
wip it
stevethomas Sep 8, 2025
a17b93b
wip it
stevethomas Sep 8, 2025
db5d567
wip it
stevethomas Sep 8, 2025
bcd3acd
wip it
stevethomas Sep 8, 2025
7ac39df
wip it
stevethomas Sep 8, 2025
40105e4
wip it
stevethomas Sep 8, 2025
935beb2
wip it
stevethomas Sep 8, 2025
dbf4053
re-add some missing files
stevethomas Sep 8, 2025
7355a64
remove slack webhooks
stevethomas Sep 8, 2025
e225da4
Merge branch 'main' into next
stevethomas Sep 15, 2025
4a8a7a2
wip it
stevethomas Sep 15, 2025
848dd48
Merge branch 'main' into next
stevethomas Sep 15, 2025
3616331
Merge branch 'main' into next
stevethomas Sep 15, 2025
20ca321
restore missing files
stevethomas Sep 15, 2025
d430835
Merge branch 'main' into next
stevethomas Sep 15, 2025
ddc4c93
fix duplicate method
stevethomas Sep 15, 2025
72029bc
wip
stevethomas Sep 15, 2025
b51f09d
wip
stevethomas Sep 15, 2025
666223f
Merge branch 'main' into next
stevethomas Sep 15, 2025
9093e06
Merge branch 'main' into next
stevethomas Sep 15, 2025
d60af07
wip it
stevethomas Sep 15, 2025
626ea13
Merge branch 'main' into next
stevethomas Sep 22, 2025
ebfea10
wip
stevethomas Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 16 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ rather than CloudFormation / Terraform / K8s / Elastic Beanstalk / <some-other-f
> [!IMPORTANT]
> While YOLO has been battle-tested on apps serving millions of requests per day, it is not supposed to be a
> set-and-forget solution for busy apps, but rather allows you to proactively manage, grow and adapt your infrastructure
> as requirements change over time.
> as requirements
> change over time.

It goes without saying, but use YOLO at your own risk.

Expand Down Expand Up @@ -86,7 +87,6 @@ The full list of available sync commands are:
- `yolo sync:tenant <environment>` prepares tenant resources (multitenancy apps only)
- `yolo sync:compute <environment>` prepares the compute resources
- `yolo sync:ci <environment>` prepares the continuous integration pipeline
- `yolo sync:iam <environment>` prepares necessary permissions

> [!TIP]
> All sync commands support a `--dry-run` argument; this is a great starting point to see what resources will be created
Expand All @@ -107,22 +107,13 @@ Run `yolo image:create <environment>` to generate a new AMI.

### b) Prepare the image for traffic

To prepare a new stage, run `yolo stage <environment>`.
To prepare the new image for traffic, run `yolo image:prepare <environment>`.

This interactive command walks you through updating or replacing the current stage configuration.
You will be prompted to select the AMI (the newest image will be at the top of the list).

New stages have the benefit of allowing testing before migrating production workloads over, however simply updating the
existing stage is recommended for minor changes.
After selecting which image to use, servers will be spun up, ready to receive app deployments.

| Situation | Recommended strategy |
|-----------------------------|----------------------|
| Update EC2 security group | update |
| Update EC2 type | update |
| Update EC2 instance profile | update |
| Update AMI | create |

When creating a new stage, the yolo.yml manifest will also be updated to point to the new autoscaling groups on the next
deployment.
The yolo.yml manifest will also be configured to point to the new autoscaling groups.

> [!NOTE]
> Rotating in a new image does not have any impact on existing traffic until the updated manifest is deployed - the
Expand Down Expand Up @@ -173,12 +164,12 @@ environments:
artefacts-bucket:
cloudfront:
alb:
security-group-id:
transcoder: false
autoscaling:
web:
queue:
scheduler:
combine: false
ec2:
instance-type: t3.small
queue-instance-type:
Expand All @@ -192,9 +183,11 @@ environments:
codedeploy:
strategy: without-load-balancing|with-load-balancing

asset-url: # defaults to aws.cloudfront
pulse-worker: false
mysqldump: false
build:
- composer install --no-cache --no-interaction --optimize-autoloader --no-progress --classmap-authoritative --no-dev
- npm ci
- npm run build
- rm -rf package-lock.json resources/js resources/css node_modules database/seeders database/factories resources/seeding

domain: example.com # standalone apps only
apex: # standalone apps only
Expand All @@ -206,22 +199,13 @@ environments:
fishing: # unique key for the tenant
domain: fishing-with-yolo.com

build:
- composer install --no-cache --no-interaction --optimize-autoloader --no-progress --classmap-authoritative --no-dev
- npm ci
- npm run build
- rm -rf package-lock.json resources/js resources/css node_modules database/seeders database/factories resources/seeding
pulse-worker: false
mysqldump: false

deploy: #runs on scheduler
deploy:
- php artisan migrate --force

deploy-queue: # runs on queue
-

deploy-web: # runs on web
-

deploy-all: # runs on all instances
deploy-all:
- php artisan optimize
```

Expand Down
4 changes: 2 additions & 2 deletions src/Commands/DeployCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class DeployCommand extends SteppedCommand
Steps\Deploy\PushAssetsToS3Step::class,
Steps\Deploy\UpdateCodeDeployDeploymentGroupStep::class,
Steps\Deploy\CreateCodeDeployDeploymentsStep::class,
// Steps\Deploy\SyncStandaloneRecordSetStep::class, // todo: temp
// Steps\Deploy\SyncMultitenancyRecordSetStep::class, // todo: temp
Steps\Deploy\SyncStandaloneRecordSetStep::class,
Steps\Deploy\SyncMultitenancyRecordSetStep::class,
Steps\Build\PurgeBuildStep::class,
];

Expand Down
8 changes: 4 additions & 4 deletions src/Commands/SyncMultitenancyTenantsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
class SyncMultitenancyTenantsCommand extends SteppedCommand
{
protected array $steps = [
// Steps\Tenant\SyncHostedZoneStep::class,
// Steps\Deploy\SyncMultitenancyRecordSetStep::class,
// Steps\Tenant\SyncSslCertificateStep::class,
// Steps\Tenant\AttachSslCertificateToLoadBalancerListenerStep::class,
Steps\Tenant\SyncHostedZoneStep::class,
Steps\Tenant\SyncRecordSetStep::class,
Steps\Tenant\SyncSslCertificateStep::class,
Steps\Tenant\AttachSslCertificateToLoadBalancerListenerStep::class,
Steps\Tenant\SyncQueueStep::class,
Steps\Tenant\SyncQueueAlarmStep::class,
];
Expand Down
24 changes: 24 additions & 0 deletions src/Concerns/UsesAutoscaling.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,38 @@

trait UsesAutoscaling
{
protected static array $asgWeb;

protected static array $asgQueue;

protected static array $asgScheduler;

protected static array $asgWebScalingPolicies;

public static function autoScalingGroupWeb(): array
{
if (isset(static::$asgWeb)) {
return static::$asgWeb;
}

return static::autoScalingGroup(Manifest::get('aws.autoscaling.web'));
}

public static function autoScalingGroupQueue(): array
{
if (isset(static::$asgQueue)) {
return static::$asgQueue;
}

return static::autoScalingGroup(Manifest::get('aws.autoscaling.queue'));
}

public static function autoScalingGroupScheduler(): array
{
if (isset(static::$asgScheduler)) {
return static::$asgScheduler;
}

return static::autoScalingGroup(Manifest::get('aws.autoscaling.scheduler'));
}

Expand Down Expand Up @@ -51,6 +71,10 @@ public static function autoScalingGroupWebScaleDownPolicy(): array

protected static function autoScalingGroupWebScalingPolicies(): array
{
if (isset(static::$asgWebScalingPolicies)) {
return static::$asgWebScalingPolicies;
}

return static::autoScalingGroupScalingPolicies(Manifest::get('aws.autoscaling.web'));
}

Expand Down
22 changes: 22 additions & 0 deletions src/Concerns/UsesCodeDeploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@

trait UsesCodeDeploy
{
protected static string $application;

protected static array $oneThirdAtATimeDeploymentConfig;

protected static array $webDeploymentGroup;

protected static array $queueDeploymentGroup;

protected static array $schedulerDeploymentGroup;

public static function applicationName(): string
{
return Helpers::keyedResourceName();
Expand Down Expand Up @@ -47,18 +57,30 @@ public static function OneThirdAtATimeDeploymentConfig(): array
/** @throws ResourceDoesNotExistException */
public static function webDeploymentGroup(): array
{
if (isset(static::$webDeploymentGroup)) {
return static::$webDeploymentGroup;
}

return static::deploymentGroup(Helpers::keyedResourceName(ServerGroup::WEB));
}

/** @throws ResourceDoesNotExistException */
public static function queueDeploymentGroup(): array
{
if (isset(static::$queueDeploymentGroup)) {
return static::$queueDeploymentGroup;
}

return static::deploymentGroup(Helpers::keyedResourceName(ServerGroup::QUEUE));
}

/** @throws ResourceDoesNotExistException */
public static function schedulerDeploymentGroup(): array
{
if (isset(static::$schedulerDeploymentGroup)) {
return static::$schedulerDeploymentGroup;
}

return static::deploymentGroup(Helpers::keyedResourceName(ServerGroup::SCHEDULER));
}

Expand Down
4 changes: 4 additions & 0 deletions src/Concerns/UsesElasticTranscoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

trait UsesElasticTranscoder
{
protected static array $elasticTranscoderPipeline;

protected static array $elasticTranscoderPreset;

public static function elasticTranscoderPipeline(): array
{
$name = Helpers::keyedResourceName();
Expand Down
8 changes: 8 additions & 0 deletions src/Exceptions/YoloException.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,12 @@ public function getSuggestion(): string
{
return $this->suggestion;
}

/**
* @throws self
*/
public function throw(): void
{
throw $this;
}
}
70 changes: 70 additions & 0 deletions src/Steps/Compute/SyncApplicationLoadBalancerStep.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Codinglabs\Yolo\Steps\Compute;

use Codinglabs\Yolo\Aws;
use Illuminate\Support\Arr;
use Codinglabs\Yolo\Helpers;
use Codinglabs\Yolo\AwsResources;
use Codinglabs\Yolo\Contracts\Step;
use Codinglabs\Yolo\Enums\StepResult;
use Codinglabs\Yolo\Exceptions\ResourceDoesNotExistException;

class SyncApplicationLoadBalancerStep implements Step
{
public function __invoke(array $options): StepResult
{
try {
AwsResources::loadBalancer();

return StepResult::SYNCED;
} catch (ResourceDoesNotExistException) {
if (! Arr::get($options, 'dry-run')) {
Aws::elasticLoadBalancingV2()->createLoadBalancer([
'Name' => Helpers::keyedResourceName(exclusive: false),
'SecurityGroups' => [AwsResources::loadBalancerSecurityGroup()['GroupId']],
'Subnets' => collect(AwsResources::subnets())
->pluck('SubnetId')
->toArray(),
...Aws::tags([
'Name' => Helpers::keyedResourceName(exclusive: false),
]),
]);

while (true) {
// wait for load balancer to provision
$loadBalancer = AwsResources::loadBalancer();

if ($loadBalancer['State']['Code'] === 'active') {
break;
}

sleep(3);
}

// todo: this is disabled due to issues dynamically generating the bucket policy
// Aws::elasticLoadBalancingV2()->modifyLoadBalancerAttributes([
// 'LoadBalancerArn' => AwsResources::loadBalancer()['LoadBalancerArn'],
// 'Attributes' => [
// [
// 'Key' => 'access_logs.s3.enabled',
// 'Value' => 'true',
// ],
// [
// 'Key' => 'access_logs.s3.bucket',
// 'Value' => Paths::s3ArtefactsBucket(),
// ],
// [
// 'Key' => 'access_logs.s3.prefix',
// 'Value' => 'logs',
// ],
// ],
// ]);

return StepResult::CREATED;
}

return StepResult::WOULD_CREATE;
}
}
}
50 changes: 50 additions & 0 deletions src/Steps/Compute/SyncListenerOnPort443Step.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Codinglabs\Yolo\Steps\Compute;

use Codinglabs\Yolo\Aws;
use Illuminate\Support\Arr;
use Codinglabs\Yolo\Helpers;
use Codinglabs\Yolo\Manifest;
use Codinglabs\Yolo\AwsResources;
use Codinglabs\Yolo\Enums\StepResult;
use Codinglabs\Yolo\Contracts\ExecutesDomainStep;
use Codinglabs\Yolo\Exceptions\ResourceDoesNotExistException;

class SyncListenerOnPort443Step implements ExecutesDomainStep
{
public function __invoke(array $options): StepResult
{
try {
AwsResources::loadBalancerListenerOnPort(443);

return StepResult::SYNCED;
} catch (ResourceDoesNotExistException) {
if (! Arr::get($options, 'dry-run')) {
Aws::elasticLoadBalancingV2()->createListener([
'LoadBalancerArn' => AwsResources::loadBalancer()['LoadBalancerArn'],
'Protocol' => 'HTTPS',
'Port' => 443,
'Certificates' => [
[
'CertificateArn' => AwsResources::certificate(Manifest::apex())['CertificateArn'],
],
],
'DefaultActions' => [
[
'Type' => 'forward',
'TargetGroupArn' => AwsResources::targetGroup()['TargetGroupArn'],
],
],
...Aws::tags([
'Name' => Helpers::keyedResourceName('https', exclusive: false),
]),
]);

return StepResult::CREATED;
}

return StepResult::WOULD_CREATE;
}
}
}
Loading