Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 7 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max

calculation-build-push:
name: Calculation Build & Push Docker
analysis-build-push:
name: Analysis Build & Push Docker
needs: backend-test
runs-on: ubuntu-latest
permissions:
Expand All @@ -179,15 +179,15 @@ jobs:
id: meta
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

- name: Build and push Calculation Docker image
- name: Build and push Analysis Docker image
uses: docker/build-push-action@v7
with:
context: .
file: ./calculation.Dockerfile
file: ./analysis.Dockerfile
push: true
tags: |
dxworks/scriptbee/calculation:${{ steps.meta.outputs.version }}
dxworks/scriptbee/calculation:latest
dxworks/scriptbee/analysis:${{ steps.meta.outputs.version }}
dxworks/scriptbee/analysis:latest
cache-from: type=gha
cache-to: type=gha,mode=max

Expand All @@ -196,7 +196,7 @@ jobs:
# ------------------------------------------------------------------
create-release:
name: Create GitHub Release
needs: [ ui-build-push, gateway-build-push, calculation-build-push ]
needs: [ ui-build-push, gateway-build-push, analysis-build-push ]
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { rxResource } from '@angular/core/rxjs-interop';
import { of } from 'rxjs';
import { InstanceService } from '../../services/instances/instance.service';
import { ProjectStateService } from '../../services/projects/project-state.service';
import { CalculationInstanceStatus, InstanceInfo } from '../../types/instance';
import { InstanceStatus, InstanceInfo } from '../../types/instance';
import { ConfirmationDialogComponent } from '../dialogs/confirmation-dialog/confirmation-dialog.component';

@Component({
Expand Down Expand Up @@ -56,7 +56,7 @@ export class InstanceManagerComponent {
return this.instances().find((i) => i.id === id);
});

getStatusIcon(status: CalculationInstanceStatus): string {
getStatusIcon(status: InstanceStatus): string {
switch (status) {
case 'Running':
return 'check_circle';
Expand All @@ -71,7 +71,7 @@ export class InstanceManagerComponent {
}
}

getStatusClass(status: CalculationInstanceStatus): string {
getStatusClass(status: InstanceStatus): string {
return status.toLowerCase();
}

Expand Down
4 changes: 2 additions & 2 deletions ScriptBeeClient/src/app/types/instance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type CalculationInstanceStatus = 'Allocating' | 'Running' | 'Deallocating' | 'NotFound';
export type InstanceStatus = 'Allocating' | 'Running' | 'Deallocating' | 'NotFound';

export interface InstanceInfo {
id: string;
creationDate: string;
status: CalculationInstanceStatus;
status: InstanceStatus;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ScriptBee.Artifacts.Config;
namespace ScriptBee.Application.Model.Config;

public class UserFolderSettings
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace ScriptBee.Domain.Model.Instance;

public enum CalculationInstanceStatus
public enum AnalysisInstanceStatus
{
Allocating,
Running,
Expand Down
2 changes: 1 addition & 1 deletion ScriptBeeWebApp/src/Common/Model/Instance/InstanceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ public record InstanceInfo(
ProjectId ProjectId,
string Url,
DateTimeOffset CreationDate,
CalculationInstanceStatus Status
AnalysisInstanceStatus Status
);
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\Application.Model\Application.Model.csproj" />
<ProjectReference Include="..\..\..\Gateway\Application\Ports.Instance.Allocation\Ports.Instance.Allocation.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ScriptBee.Analysis.Instance.Docker.Config;
using ScriptBee.Application.Model.Config;
using ScriptBee.Domain.Model.Analysis;
using ScriptBee.Domain.Model.Instance;
using ScriptBee.Domain.Model.Project;
using ScriptBee.Ports.Instance.Allocation;

namespace ScriptBee.Analysis.Instance.Docker;

public class CalculationInstanceDockerAdapter(
IOptions<CalculationDockerConfig> config,
public class AnalysisInstanceDockerAdapter(
IOptions<AnalysisDockerConfig> config,
IOptions<UserFolderSettings> userFolderSettingsOptions,
IConfiguration configuration,
ILogger<CalculationInstanceDockerAdapter> logger,
ILogger<AnalysisInstanceDockerAdapter> logger,
IFreePortProvider freePortProvider
) : IAllocateInstance, IDeallocateInstance, IGetInstanceStatus
{
Expand All @@ -26,8 +28,8 @@ public async Task<string> Allocate(
CancellationToken cancellationToken = default
)
{
var calculationDockerConfig = config.Value;
using var client = CreateDockerClient(calculationDockerConfig);
var analysisDockerConfig = config.Value;
using var client = CreateDockerClient(analysisDockerConfig);

await PullImageIfNeeded(client, image.ImageName, cancellationToken);

Expand All @@ -36,36 +38,32 @@ public async Task<string> Allocate(
var portBindings = new Dictionary<string, IList<PortBinding>>
{
{
$"{calculationDockerConfig.Port}/tcp",
$"{analysisDockerConfig.Port}/tcp",
new List<PortBinding> { new() { HostPort = hostPort.ToString() } }
},
};

var mongoDbConnectionString =
calculationDockerConfig.MongoDbConnectionString
analysisDockerConfig.MongoDbConnectionString
?? configuration.GetConnectionString("mongodb");

var response = await client.Containers.CreateContainerAsync(
new CreateContainerParameters
{
Name = $"scriptbee-calculation-{instanceId}",
Name = $"scriptbee-analysis-{instanceId}",
Image = image.ImageName,
HostConfig = new HostConfig
{
NetworkMode = calculationDockerConfig.Network,
NetworkMode = analysisDockerConfig.Network,
PortBindings = portBindings,
Binds = GetBinds(),
},
ExposedPorts = new Dictionary<string, EmptyStruct>
{
{ $"{calculationDockerConfig.Port}/tcp", new EmptyStruct() },
},
Env = new List<string>
{
$"ScriptBee__InstanceId={instanceId}",
$"ScriptBee__ProjectId={projectDetails.Id}",
$"ScriptBee__ProjectName={projectDetails.Name}",
$"ConnectionStrings__mongodb={mongoDbConnectionString}",
{ $"{analysisDockerConfig.Port}/tcp", new EmptyStruct() },
},
Env = GetEnvironmentVariables(projectDetails, instanceId, mongoDbConnectionString),
Volumes = GetVolumes(),
},
cancellationToken
);
Expand All @@ -82,29 +80,19 @@ await client.Containers.StartContainerAsync(
return await GetContainerUrl(
client,
response.ID,
calculationDockerConfig.Network,
analysisDockerConfig.Network,
hostPort,
cancellationToken
);
}

private static DockerClient CreateDockerClient(CalculationDockerConfig calculationDockerConfig)
public async Task Deallocate(InstanceInfo instanceInfo, CancellationToken cancellationToken)
{
return new DockerClientConfiguration(
new Uri(calculationDockerConfig.DockerSocket)
).CreateClient();
}

public async Task Deallocate(
InstanceInfo calculationInstanceInfo,
CancellationToken cancellationToken
)
{
var containerName = $"scriptbee-calculation-{calculationInstanceInfo.Id}";
var containerName = $"scriptbee-analysis-{instanceInfo.Id}";
logger.LogInformation("Attempting to deallocate container: {Name}", containerName);

var calculationDockerConfig = config.Value;
using var client = CreateDockerClient(calculationDockerConfig);
var analysisDockerConfig = config.Value;
using var client = CreateDockerClient(analysisDockerConfig);

IList<ContainerListResponse> containers = await client.Containers.ListContainersAsync(
new ContainersListParameters
Expand Down Expand Up @@ -164,6 +152,107 @@ await client.Containers.RemoveContainerAsync(
}
}

public async Task<AnalysisInstanceStatus> GetStatus(
InstanceId instanceId,
CancellationToken cancellationToken
)
{
var containerName = $"scriptbee-analysis-{instanceId}";
var analysisDockerConfig = config.Value;
using var client = CreateDockerClient(analysisDockerConfig);

var containers = await client.Containers.ListContainersAsync(
new ContainersListParameters
{
All = true,
Filters = new Dictionary<string, IDictionary<string, bool>>
{
{
"name",
new Dictionary<string, bool> { { containerName, true } }
},
},
},
cancellationToken
);

var container = containers.FirstOrDefault();

if (container == null)
{
return AnalysisInstanceStatus.NotFound;
}

return container.State.ToLower() switch
{
"running" => AnalysisInstanceStatus.Running,
"created" or "restarting" => AnalysisInstanceStatus.Allocating,
"removing" => AnalysisInstanceStatus.Deallocating,
_ => AnalysisInstanceStatus.NotFound,
};
}

private List<string> GetEnvironmentVariables(
ProjectDetails projectDetails,
InstanceId instanceId,
string? mongoDbConnectionString
)
{
var environmentVariables = new List<string>
{
$"ScriptBee__InstanceId={instanceId}",
$"ScriptBee__ProjectId={projectDetails.Id}",
$"ScriptBee__ProjectName={projectDetails.Name}",
$"ConnectionStrings__mongodb={mongoDbConnectionString}",
};

if (
string.IsNullOrEmpty(userFolderSettingsOptions.Value.UserFolderPath)
&& string.IsNullOrEmpty(config.Value.UserFolderHostPath)
)
{
return environmentVariables;
}

environmentVariables.Add($"UserFolder__UserFolderPath={config.Value.UserFolderVolumePath}");

return environmentVariables;
}

private List<string> GetBinds()
{
var hostPath =
config.Value.UserFolderHostPath ?? userFolderSettingsOptions.Value.UserFolderPath;
return string.IsNullOrEmpty(hostPath)
? []
: [$"{hostPath}:{config.Value.UserFolderVolumePath}"];
}

private Dictionary<string, EmptyStruct> GetVolumes()
{
var hostPath =
config.Value.UserFolderHostPath ?? userFolderSettingsOptions.Value.UserFolderPath;
if (
string.IsNullOrEmpty(hostPath)
|| string.IsNullOrEmpty(config.Value.UserFolderVolumePath)
)
{
return new Dictionary<string, EmptyStruct>();
}

return new Dictionary<string, EmptyStruct>
{
{ config.Value.UserFolderVolumePath, new EmptyStruct() },
};
}

private static DockerClient CreateDockerClient(AnalysisDockerConfig analysisDockerConfig)
{
return new DockerClientConfiguration(
new Uri(analysisDockerConfig.DockerSocket)
).CreateClient();
}

private static async Task<string> GetContainerUrl(
DockerClient client,
string containerId,
Expand Down Expand Up @@ -195,7 +284,7 @@ private async Task PullImageIfNeeded(
{
var parts = imageName.Split(":");
var image = parts[0];
var tag = parts[1];
var tag = parts.Length > 1 ? parts[1] : "latest";

var images = await client.Images.ListImagesAsync(
new ImagesListParameters
Expand Down Expand Up @@ -228,44 +317,4 @@ await client.Images.CreateImageAsync(
logger.LogInformation("Image {Image}:{Tag} already exists", image, tag);
}
}

public async Task<CalculationInstanceStatus> GetStatus(
InstanceId instanceId,
CancellationToken cancellationToken
)
{
var containerName = $"scriptbee-calculation-{instanceId}";
var calculationDockerConfig = config.Value;
using var client = CreateDockerClient(calculationDockerConfig);

var containers = await client.Containers.ListContainersAsync(
new ContainersListParameters
{
All = true,
Filters = new Dictionary<string, IDictionary<string, bool>>
{
{
"name",
new Dictionary<string, bool> { { containerName, true } }
},
},
},
cancellationToken
);

var container = containers.FirstOrDefault();

if (container == null)
{
return CalculationInstanceStatus.NotFound;
}

return container.State.ToLower() switch
{
"running" => CalculationInstanceStatus.Running,
"created" or "restarting" => CalculationInstanceStatus.Allocating,
"removing" => CalculationInstanceStatus.Deallocating,
_ => CalculationInstanceStatus.NotFound,
};
}
}
Loading
Loading