Skip to content

Commit

Permalink
[gui] guard against launching an instance with the name of a deleted …
Browse files Browse the repository at this point in the history
…instance
  • Loading branch information
andrei-toterman committed Oct 18, 2024
1 parent 2b46fba commit b32a37d
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 7 deletions.
11 changes: 9 additions & 2 deletions src/client/gui/lib/catalogue/launch_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class _LaunchFormState extends ConsumerState<LaunchForm> {
final imageInfo = ref.watch(launchingImageProvider);
final randomName = ref.watch(randomNameProvider);
final vmNames = ref.watch(vmNamesProvider);
final deletedVms = ref.watch(deletedVmsProvider);

final closeButton = IconButton(
icon: const Icon(Icons.close),
Expand All @@ -63,7 +64,7 @@ class _LaunchFormState extends ConsumerState<LaunchForm> {
autofocus: true,
helper: 'Names cannot be changed once an instance is created',
hint: randomName,
validator: nameValidator(vmNames),
validator: nameValidator(vmNames, deletedVms),
onSaved: (value) => launchRequest.instanceName =
value.isNullOrBlank ? randomName : value!,
width: 360,
Expand Down Expand Up @@ -312,7 +313,10 @@ class _LaunchFormState extends ConsumerState<LaunchForm> {
}
}

FormFieldValidator<String> nameValidator(Iterable<String> existingNames) {
FormFieldValidator<String> nameValidator(
Iterable<String> existingNames,
Iterable<String> deletedNames,
) {
return (String? value) {
if (value!.isEmpty) {
return null;
Expand All @@ -332,6 +336,9 @@ FormFieldValidator<String> nameValidator(Iterable<String> existingNames) {
if (existingNames.contains(value)) {
return 'Name is already in use';
}
if (deletedNames.contains(value)) {
return 'Name is already in use by a deleted instance';
}
return null;
};
}
16 changes: 11 additions & 5 deletions src/client/gui/lib/providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ final vmInfosStreamProvider = StreamProvider<List<VmInfo>>((ref) async* {
while (true) {
final timer = Future.delayed(1900.milliseconds);
try {
final infos = await grpcClient.info();
yield infos
.where((info) => info.instanceStatus.status != Status.DELETED)
.toList();
yield await grpcClient.info();
lastError = null;
} catch (error, stackTrace) {
if (error != lastError) {
Expand Down Expand Up @@ -78,7 +75,8 @@ final daemonAvailableProvider = Provider((ref) {
});

final vmInfosProvider = Provider((ref) {
return ref.watch(vmInfosStreamProvider).valueOrNull ?? const [];
final vmInfos = ref.watch(vmInfosStreamProvider).valueOrNull ?? const [];
return vmInfos.where((info) => info.instanceStatus.status != Status.DELETED);
});

final vmInfosMapProvider = Provider((ref) {
Expand Down Expand Up @@ -107,6 +105,14 @@ final vmNamesProvider = Provider((ref) {
return ref.watch(vmStatusesProvider).keys.toBuiltSet();
});

final deletedVmsProvider = Provider((ref) {
final vmInfos = ref.watch(vmInfosStreamProvider).valueOrNull ?? const [];
return vmInfos
.where((info) => info.instanceStatus.status == Status.DELETED)
.map((info) => info.name)
.toBuiltSet();
});

class ClientSettingNotifier extends AutoDisposeFamilyNotifier<String, String> {
final file = File(settingsFile());

Expand Down

0 comments on commit b32a37d

Please sign in to comment.