feat(digital-ocean): Implement endpoints for server creation and deletion

routes-refactor
NaiJi ✨ 2022-10-17 17:42:23 +00:00
parent f5a75e6eb5
commit d19531232c
4 changed files with 51 additions and 92 deletions

View File

@ -95,6 +95,9 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
final Response dbCreateResponse; final Response dbCreateResponse;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
final List<ServerVolume> volumes = await getVolumes();
await Future.delayed(const Duration(seconds: 6));
dbCreateResponse = await client.post( dbCreateResponse = await client.post(
'/volumes', '/volumes',
data: { data: {
@ -109,11 +112,12 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
final dbSize = dbCreateResponse.data['volume']['size_gigabytes']; final dbSize = dbCreateResponse.data['volume']['size_gigabytes'];
final dbName = dbCreateResponse.data['volume']['name']; final dbName = dbCreateResponse.data['volume']['name'];
volume = ServerVolume( volume = ServerVolume(
id: dbId, id: volumes.length,
name: dbName, name: dbName,
sizeByte: dbSize, sizeByte: dbSize,
serverId: null, serverId: null,
linuxDevice: null, linuxDevice: null,
uuid: dbId,
); );
} catch (e) { } catch (e) {
print(e); print(e);
@ -296,36 +300,9 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
required final String domainName, required final String domainName,
required final String serverType, required final String serverType,
}) async { }) async {
ServerHostingDetails? details; ServerHostingDetails? serverDetails;
final ServerVolume? newVolume = await createVolume();
if (newVolume == null) {
return details;
}
details = await createServerWithVolume(
dnsApiToken: dnsApiToken,
rootUser: rootUser,
domainName: domainName,
dataBase: newVolume,
serverType: serverType,
);
return details;
}
Future<ServerHostingDetails?> createServerWithVolume({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
required final ServerVolume dataBase,
required final String serverType,
}) async {
final Dio client = await getClient();
final String dbPassword = StringGenerators.dbPassword(); final String dbPassword = StringGenerators.dbPassword();
final int dbId = dataBase.id;
final String? dbUuid = dataBase.uuid;
final String apiToken = StringGenerators.apiToken(); final String apiToken = StringGenerators.apiToken();
final String hostname = getHostnameFromDomain(domainName); final String hostname = getHostnameFromDomain(domainName);
@ -334,58 +311,44 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
base64.encode(utf8.encode(rootUser.password ?? 'PASS')); base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
final String userdataString = final String userdataString =
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log"; "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=digital-ocean NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
print(userdataString); print(userdataString);
final Map<String, Object> data = { final Dio client = await getClient();
'name': hostname,
'server_type': serverType,
'start_after_create': false,
'image': 'ubuntu-20.04',
'volumes': dbUuid == null ? [dbId] : [dbUuid],
'networks': [],
'user_data': userdataString,
'labels': {},
'automount': true,
'location': 'fsn1'
};
print('Decoded data: $data');
ServerHostingDetails? serverDetails;
DioError? hetznerError;
bool success = false;
try { try {
final Map<String, Object> data = {
'name': hostname,
'size': serverType,
'image': 'ubuntu-20-04-x64',
'user_data': userdataString,
'region': region!,
};
print('Decoded data: $data');
final Response serverCreateResponse = await client.post( final Response serverCreateResponse = await client.post(
'/servers', '/droplets',
data: data, data: data,
); );
print(serverCreateResponse.data);
serverDetails = ServerHostingDetails( final int serverId = serverCreateResponse.data['server']['id'];
id: serverCreateResponse.data['server']['id'], final ServerVolume? newVolume = await createVolume();
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'], final bool attachedVolume =
createTime: DateTime.now(), await attachVolume(newVolume!.uuid!, serverId);
volume: dataBase,
apiToken: apiToken, if (attachedVolume) {
provider: ServerProvider.hetzner, serverDetails = ServerHostingDetails(
); id: serverId,
success = true; ip4: serverCreateResponse.data['droplet']['networks']['v4'][0],
} on DioError catch (e) { createTime: DateTime.now(),
print(e); volume: newVolume,
hetznerError = e; apiToken: apiToken,
provider: ServerProvider.digitalOcean,
);
}
} catch (e) { } catch (e) {
print(e); print(e);
} finally { } finally {
client.close(); close(client);
}
if (!success) {
await Future.delayed(const Duration(seconds: 10));
await deleteVolume(dbUuid ?? dbId.toString());
}
if (hetznerError != null) {
throw hetznerError;
} }
return serverDetails; return serverDetails;
@ -416,24 +379,26 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
final String hostname = getHostnameFromDomain(domainName); final String hostname = getHostnameFromDomain(domainName);
final Response serversReponse = await client.get('/servers'); final ServerBasicInfo serverToRemove = (await getServers()).firstWhere(
final List servers = serversReponse.data['servers']; (final el) => el.name == hostname,
final Map server = servers.firstWhere((final el) => el['name'] == hostname); );
final List volumes = server['volumes']; final ServerVolume volumeToRemove = (await getVolumes()).firstWhere(
(final el) => el.serverId == serverToRemove.id,
);
final List<Future> laterFutures = <Future>[]; final List<Future> laterFutures = <Future>[];
for (final volumeId in volumes) { await detachVolume(volumeToRemove.uuid!);
await client.post('/volumes/$volumeId/actions/detach');
}
await Future.delayed(const Duration(seconds: 10)); await Future.delayed(const Duration(seconds: 10));
for (final volumeId in volumes) { try {
laterFutures.add(client.delete('/volumes/$volumeId')); laterFutures.add(deleteVolume(volumeToRemove.uuid!));
laterFutures.add(client.delete('/droplets/$serverToRemove.id'));
await Future.wait(laterFutures);
} catch (e) {
print(e);
} finally {
close(client);
} }
laterFutures.add(client.delete('/servers/${server['id']}'));
await Future.wait(laterFutures);
close(client);
} }
@override @override
@ -530,7 +495,6 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
ip: server.publicNet.ipv4.ip, ip: server.publicNet.ipv4.ip,
reverseDns: server.publicNet.ipv4.reverseDns, reverseDns: server.publicNet.ipv4.reverseDns,
created: server.created, created: server.created,
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
), ),
) )
.toList(); .toList();

View File

@ -532,7 +532,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
ip: server.publicNet.ipv4.ip, ip: server.publicNet.ipv4.ip,
reverseDns: server.publicNet.ipv4.reverseDns, reverseDns: server.publicNet.ipv4.reverseDns,
created: server.created, created: server.created,
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
), ),
) )
.toList(); .toList();

View File

@ -588,7 +588,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
id: server.id, id: server.id,
createTime: server.created, createTime: server.created,
volume: ServerVolume( volume: ServerVolume(
id: server.volumeId, id: 0,
name: 'recovered_volume', name: 'recovered_volume',
sizeByte: 0, sizeByte: 0,
serverId: server.id, serverId: server.id,

View File

@ -5,14 +5,12 @@ class ServerBasicInfo {
required this.reverseDns, required this.reverseDns,
required this.ip, required this.ip,
required this.created, required this.created,
required this.volumeId,
}); });
final int id; final int id;
final String name; final String name;
final String reverseDns; final String reverseDns;
final String ip; final String ip;
final DateTime created; final DateTime created;
final int volumeId;
} }
class ServerBasicInfoWithValidators extends ServerBasicInfo { class ServerBasicInfoWithValidators extends ServerBasicInfo {
@ -26,7 +24,6 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
reverseDns: serverBasicInfo.reverseDns, reverseDns: serverBasicInfo.reverseDns,
ip: serverBasicInfo.ip, ip: serverBasicInfo.ip,
created: serverBasicInfo.created, created: serverBasicInfo.created,
volumeId: serverBasicInfo.volumeId,
isIpValid: isIpValid, isIpValid: isIpValid,
isReverseDnsValid: isReverseDnsValid, isReverseDnsValid: isReverseDnsValid,
); );
@ -37,7 +34,6 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
required final super.reverseDns, required final super.reverseDns,
required final super.ip, required final super.ip,
required final super.created, required final super.created,
required final super.volumeId,
required this.isIpValid, required this.isIpValid,
required this.isReverseDnsValid, required this.isReverseDnsValid,
}); });