Skip to content

Commit e171ba7

Browse files
committed
feat: implement fly reconciliation
Signed-off-by: Chris Goller <[email protected]>
1 parent 14f17d6 commit e171ba7

File tree

11 files changed

+723
-5
lines changed

11 files changed

+723
-5
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
config/
22
dist/
33
node_modules/
4+
.git

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"date-fns": "^2.30.0",
2525
"dotenv": "^16.3.1",
2626
"execa": "^7.2.0",
27-
"fast-json-patch": "^3.1.1"
27+
"fast-json-patch": "^3.1.1",
28+
"undici": "^6.14.1"
2829
},
2930
"devDependencies": {
3031
"@bufbuild/protoc-gen-connect-es": "^0.12.0",

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/depot/cloud/v2/cloud.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ message GetDesiredStateResponse {
4343
SecurityGroup security_group = 6;
4444
RootVolume root_volume = 7;
4545
optional string user_data = 8;
46+
optional string volume_id = 9;
4647
}
4748

4849
message NewVolume {
@@ -147,23 +148,34 @@ message GetActiveAgentVersionResponse {
147148
message CloudState {
148149
oneof state {
149150
Aws aws = 1;
151+
Fly fly = 2;
150152
}
151153

152154
message Aws {
153155
string availability_zone = 1;
154156
string state = 2;
155157
}
158+
159+
message Fly {
160+
string region = 1;
161+
string state = 2;
162+
}
156163
}
157164

158165
message CloudStatePatch {
159166
int32 generation = 1;
160167
oneof patch {
161168
Aws aws = 2;
169+
Fly fly = 3;
162170
}
163171

164172
message Aws {
165173
string patch = 1;
166174
}
175+
176+
message Fly {
177+
string patch = 1;
178+
}
167179
}
168180

169181
message ReconcileVolumesRequest {}

src/proto/depot/cloud/v2/cloud_pb.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,11 @@ export class GetDesiredStateResponse_NewMachine extends Message<GetDesiredStateR
402402
*/
403403
userData?: string
404404

405+
/**
406+
* @generated from field: optional string volume_id = 9;
407+
*/
408+
volumeId?: string
409+
405410
constructor(data?: PartialMessage<GetDesiredStateResponse_NewMachine>) {
406411
super()
407412
proto3.util.initPartial(data, this)
@@ -417,6 +422,7 @@ export class GetDesiredStateResponse_NewMachine extends Message<GetDesiredStateR
417422
{no: 6, name: 'security_group', kind: 'enum', T: proto3.getEnumType(GetDesiredStateResponse_SecurityGroup)},
418423
{no: 7, name: 'root_volume', kind: 'message', T: GetDesiredStateResponse_RootVolume},
419424
{no: 8, name: 'user_data', kind: 'scalar', T: 9 /* ScalarType.STRING */, opt: true},
425+
{no: 9, name: 'volume_id', kind: 'scalar', T: 9 /* ScalarType.STRING */, opt: true},
420426
])
421427

422428
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetDesiredStateResponse_NewMachine {
@@ -1002,6 +1008,13 @@ export class CloudState extends Message<CloudState> {
10021008
value: CloudState_Aws
10031009
case: 'aws'
10041010
}
1011+
| {
1012+
/**
1013+
* @generated from field: depot.cloud.v2.CloudState.Fly fly = 2;
1014+
*/
1015+
value: CloudState_Fly
1016+
case: 'fly'
1017+
}
10051018
| {case: undefined; value?: undefined} = {case: undefined}
10061019

10071020
constructor(data?: PartialMessage<CloudState>) {
@@ -1013,6 +1026,7 @@ export class CloudState extends Message<CloudState> {
10131026
static readonly typeName = 'depot.cloud.v2.CloudState'
10141027
static readonly fields: FieldList = proto3.util.newFieldList(() => [
10151028
{no: 1, name: 'aws', kind: 'message', T: CloudState_Aws, oneof: 'state'},
1029+
{no: 2, name: 'fly', kind: 'message', T: CloudState_Fly, oneof: 'state'},
10161030
])
10171031

10181032
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CloudState {
@@ -1081,6 +1095,52 @@ export class CloudState_Aws extends Message<CloudState_Aws> {
10811095
}
10821096
}
10831097

1098+
/**
1099+
* @generated from message depot.cloud.v2.CloudState.Fly
1100+
*/
1101+
export class CloudState_Fly extends Message<CloudState_Fly> {
1102+
/**
1103+
* @generated from field: string region = 1;
1104+
*/
1105+
region = ''
1106+
1107+
/**
1108+
* @generated from field: string state = 2;
1109+
*/
1110+
state = ''
1111+
1112+
constructor(data?: PartialMessage<CloudState_Fly>) {
1113+
super()
1114+
proto3.util.initPartial(data, this)
1115+
}
1116+
1117+
static readonly runtime: typeof proto3 = proto3
1118+
static readonly typeName = 'depot.cloud.v2.CloudState.Fly'
1119+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
1120+
{no: 1, name: 'region', kind: 'scalar', T: 9 /* ScalarType.STRING */},
1121+
{no: 2, name: 'state', kind: 'scalar', T: 9 /* ScalarType.STRING */},
1122+
])
1123+
1124+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CloudState_Fly {
1125+
return new CloudState_Fly().fromBinary(bytes, options)
1126+
}
1127+
1128+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CloudState_Fly {
1129+
return new CloudState_Fly().fromJson(jsonValue, options)
1130+
}
1131+
1132+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CloudState_Fly {
1133+
return new CloudState_Fly().fromJsonString(jsonString, options)
1134+
}
1135+
1136+
static equals(
1137+
a: CloudState_Fly | PlainMessage<CloudState_Fly> | undefined,
1138+
b: CloudState_Fly | PlainMessage<CloudState_Fly> | undefined,
1139+
): boolean {
1140+
return proto3.util.equals(CloudState_Fly, a, b)
1141+
}
1142+
}
1143+
10841144
/**
10851145
* @generated from message depot.cloud.v2.CloudStatePatch
10861146
*/
@@ -1101,6 +1161,13 @@ export class CloudStatePatch extends Message<CloudStatePatch> {
11011161
value: CloudStatePatch_Aws
11021162
case: 'aws'
11031163
}
1164+
| {
1165+
/**
1166+
* @generated from field: depot.cloud.v2.CloudStatePatch.Fly fly = 3;
1167+
*/
1168+
value: CloudStatePatch_Fly
1169+
case: 'fly'
1170+
}
11041171
| {case: undefined; value?: undefined} = {case: undefined}
11051172

11061173
constructor(data?: PartialMessage<CloudStatePatch>) {
@@ -1113,6 +1180,7 @@ export class CloudStatePatch extends Message<CloudStatePatch> {
11131180
static readonly fields: FieldList = proto3.util.newFieldList(() => [
11141181
{no: 1, name: 'generation', kind: 'scalar', T: 5 /* ScalarType.INT32 */},
11151182
{no: 2, name: 'aws', kind: 'message', T: CloudStatePatch_Aws, oneof: 'patch'},
1183+
{no: 3, name: 'fly', kind: 'message', T: CloudStatePatch_Fly, oneof: 'patch'},
11161184
])
11171185

11181186
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CloudStatePatch {
@@ -1175,6 +1243,46 @@ export class CloudStatePatch_Aws extends Message<CloudStatePatch_Aws> {
11751243
}
11761244
}
11771245

1246+
/**
1247+
* @generated from message depot.cloud.v2.CloudStatePatch.Fly
1248+
*/
1249+
export class CloudStatePatch_Fly extends Message<CloudStatePatch_Fly> {
1250+
/**
1251+
* @generated from field: string patch = 1;
1252+
*/
1253+
patch = ''
1254+
1255+
constructor(data?: PartialMessage<CloudStatePatch_Fly>) {
1256+
super()
1257+
proto3.util.initPartial(data, this)
1258+
}
1259+
1260+
static readonly runtime: typeof proto3 = proto3
1261+
static readonly typeName = 'depot.cloud.v2.CloudStatePatch.Fly'
1262+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
1263+
{no: 1, name: 'patch', kind: 'scalar', T: 9 /* ScalarType.STRING */},
1264+
])
1265+
1266+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CloudStatePatch_Fly {
1267+
return new CloudStatePatch_Fly().fromBinary(bytes, options)
1268+
}
1269+
1270+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CloudStatePatch_Fly {
1271+
return new CloudStatePatch_Fly().fromJson(jsonValue, options)
1272+
}
1273+
1274+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CloudStatePatch_Fly {
1275+
return new CloudStatePatch_Fly().fromJsonString(jsonString, options)
1276+
}
1277+
1278+
static equals(
1279+
a: CloudStatePatch_Fly | PlainMessage<CloudStatePatch_Fly> | undefined,
1280+
b: CloudStatePatch_Fly | PlainMessage<CloudStatePatch_Fly> | undefined,
1281+
): boolean {
1282+
return proto3.util.equals(CloudStatePatch_Fly, a, b)
1283+
}
1284+
}
1285+
11781286
/**
11791287
* @generated from message depot.cloud.v2.ReconcileVolumesRequest
11801288
*/

src/utils/aws.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
CLOUD_AGENT_CONNECTION_ID,
3636
additionalSubnetIDs,
3737
} from './env'
38+
import {toPlainObject} from './plain'
3839

3940
const client = new EC2Client({})
4041

@@ -378,7 +379,3 @@ async function reconcileMachine(state: Record<string, Instance>, machine: GetDes
378379
}
379380
}
380381
}
381-
382-
function toPlainObject<T>(obj: T): T {
383-
return JSON.parse(JSON.stringify(obj))
384-
}

src/utils/fly/buildkit.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {V1Machine, Volume, createVolume, launchMachine} from './client'
2+
3+
export interface BuildkitMachineRequest {
4+
depotID: string
5+
region: string
6+
volumeID: string
7+
image: string
8+
env: Record<string, string>
9+
buildkitToml: string
10+
}
11+
12+
export async function launchBuildkitMachine(buildkit: BuildkitMachineRequest): Promise<V1Machine> {
13+
const {depotID, region, volumeID, image, env, buildkitToml} = buildkit
14+
const machine = await launchMachine({
15+
name: depotID,
16+
region,
17+
config: {
18+
guest: {
19+
cpu_kind: 'shared',
20+
cpus: 16,
21+
memory_mb: 1024 * 32,
22+
},
23+
files: [
24+
{
25+
guest_path: '/etc/buildkit/buildkitd.toml',
26+
raw_value: Buffer.from(buildkitToml).toString('base64'),
27+
},
28+
],
29+
init: {
30+
entryPoint: ['/usr/bin/buildkitd'],
31+
},
32+
env,
33+
image,
34+
mounts: [
35+
{
36+
encrypted: false,
37+
path: '/var/lib/buildkit',
38+
volume: volumeID,
39+
},
40+
],
41+
auto_destroy: false,
42+
restart: {policy: 'no'},
43+
dns: {},
44+
},
45+
})
46+
return machine
47+
}
48+
49+
export interface BuildkitVolumeRequest {
50+
depotID: string
51+
region: string
52+
sizeGB: number
53+
}
54+
55+
export async function createBuildkitVolume(req: BuildkitVolumeRequest): Promise<Volume> {
56+
const {depotID, region, sizeGB} = req
57+
const volume = await createVolume({
58+
name: depotID,
59+
region,
60+
size_gb: sizeGB,
61+
snapshot_retention: 5, // 5 is fly's minimum value.
62+
encrypted: false,
63+
fstype: 'ext4',
64+
})
65+
return volume
66+
}

0 commit comments

Comments
 (0)