Skip to content

Commit

Permalink
fixed docker logs not working
Browse files Browse the repository at this point in the history
  • Loading branch information
reven committed May 10, 2023
1 parent 176edb9 commit f766b8f
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
<p>@lblSshDescription</p>
<InputText Page="GroupItem" @bind-Value="@Model.SshServer" Label="SshAddress" />
<InputText Page="GroupItem" @bind-Value="@Model.SshUserName" Label="SshUsername"/>
<InputText Page="GroupItem" @bind-Value="@Model.SshPassword" Label="SshPassword" />
<InputText Page="GroupItem" @bind-Value="@Model.SshPassword" Label="SshPassword" Password="true" />
</FenrusTab>

@if (DockerServers.Any())
Expand Down
15 changes: 12 additions & 3 deletions Components/SideEditors/GroupItemEditor/GroupItemEditor.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ protected override void OnInitialized()
Model.AppName = app.AppName;
Model.DockerContainer = app.DockerContainer;
Model.DockerUid = app.DockerUid;
Model.DockerCommand = app.DockerCommand;
Model.DockerCommand = app.DockerCommand?.EmptyAsNull() ?? "/bin/bash"; // default
Model.SshPasswordOriginal = app.SshPassword;
Model.SshPassword = string.IsNullOrEmpty(app.SshPassword) ? string.Empty : Globals.DUMMY_PASSWORD;
Model.SshServer = app.SshServer;
Expand Down Expand Up @@ -281,9 +281,18 @@ async Task Save()
app.Monitor = Model.Monitor;
app.ApiUrl = Model.ApiUrl;
app.AppName = Model.AppName;
app.DockerContainer = Model.DockerContainer;
app.DockerUid = Model.DockerUid == Guid.Empty ? null : Model.DockerUid;
app.DockerCommand = Model.DockerCommand;
if (app.DockerUid == null)
{
app.DockerContainer = string.Empty;
app.DockerCommand = string.Empty;
}
else
{
app.DockerCommand = Model.DockerCommand;
app.DockerContainer = Model.DockerContainer;
}

app.SshPassword = Model.SshPassword == Globals.DUMMY_PASSWORD
? Model.SshPasswordOriginal
: EncryptionHelper.Encrypt(Model.SshPassword);
Expand Down
59 changes: 58 additions & 1 deletion Controllers/TerminalController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public async Task Get([FromRoute] Guid uid, [FromQuery] int rows = 24, [FromQuer
new { uid = app }));
}

terminal = new DockerTerminal(ws, rows, cols, docker.Address, docker.Port, app.DockerContainer);
terminal = new DockerTerminal(ws, rows, cols, docker.Address, docker.Port, app.DockerContainer, app.DockerCommand);
}
else if (string.IsNullOrEmpty(app.SshServer) == false)
{
Expand All @@ -72,4 +72,61 @@ public async Task Get([FromRoute] Guid uid, [FromQuery] int rows = 24, [FromQuer

await terminal.Connect();
}


/// <summary>
/// Opens a terminal log
/// </summary>
/// <param name="uid">the Uid of the app container</param>
/// <param name="rows">the number of rows for the terminal</param>
/// <param name="cols">the number of columns for the terminal</param>
[HttpGet("log/{uid}")]
public async Task GetLog([FromRoute] Guid uid, [FromQuery] int rows = 24, [FromQuery] int cols = 24)
{
var settings = GetUserSettings();
if (settings == null)
throw new UnauthorizedAccessException();

var groups = new GroupService().GetAllForUser(settings.UserUid);

var item = groups?.SelectMany(x => x.Items)?.FirstOrDefault(x => x.Uid == uid);
if (item == null)
{
// try and find the item from the system groups
item = new GroupService().GetSystemGroups(enabledOnly: true)
.SelectMany(x => x.Items).FirstOrDefault(x => x.Uid == uid);
}

if (item == null)
{
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
return;
}

DockerTerminal? terminal = null;
using var ws = await HttpContext.WebSockets.AcceptWebSocketAsync();
if (item is AppItem app)
{
if (string.IsNullOrEmpty(app.DockerContainer) == false && app.DockerUid != null)
{
var docker = new DockerService().GetByUid(app.DockerUid.Value);
if (docker == null)
{
var translator = GetTranslator(settings);
throw new Exception(translator.Instant("ErrorMessages.DockerServerNotFound",
new { uid = app }));
}

terminal = new DockerTerminal(ws, rows, cols, docker.Address, docker.Port, app.DockerContainer, app.DockerCommand);
}
}

if(terminal == null)
{
HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
return;
}

await terminal.Log();
}
}
2 changes: 2 additions & 0 deletions Helpers/BackgroundHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static string[] GetBackgroundNames(Translator translator, bool hasCustomB
hasCustomBackground ? translator.Instant("Labels.CurrentImage") : null,
translator.Instant("Labels.ChooseImage"),
translator.Instant("Labels.Default"),
translator.Instant("Labels.DefaultStatic"),
translator.Instant("Labels.ColorBackground"),
"Vanta Birds",
"Vanta Cells",
Expand All @@ -41,6 +42,7 @@ public static string[] GetBackgroundValues(bool hasCustomBackground)
hasCustomBackground ? "image.js" : string.Empty,
"image-picker",
"default.js",
"default.static.js",
"color.js",
"vanta.birds.js",
"vanta.cells.js",
Expand Down
69 changes: 64 additions & 5 deletions Terminals/DockerTerminal.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net.WebSockets;
using System.Text;
using System.Text.RegularExpressions;
using Docker.DotNet;
using Docker.DotNet.Models;

Expand All @@ -22,6 +23,10 @@ public class DockerTerminal: Terminal
/// Gets the dockers container name
/// </summary>
public string ContainerName { get; init; }
/// <summary>
/// Gets the docker command to run
/// </summary>
public string Command { get; init; }

/// <summary>
/// Constructs a new terminal
Expand All @@ -32,24 +37,26 @@ public class DockerTerminal: Terminal
/// <param name="server">the docker server address</param>
/// <param name="port">the docker server port</param>
/// <param name="containerName">the dockers container name</param>
public DockerTerminal(WebSocket webSocket, int rows, int cols, string server, int port, string containerName) :
/// <param name="command">the docker command to run</param>
public DockerTerminal(WebSocket webSocket, int rows, int cols, string server, int port, string containerName, string command) :
base(webSocket, rows, cols)
{
this.Server = server;
this.Port = port < 1 || port > 65535 ? 2375 : port;
this.ContainerName = containerName;
this.Command = command?.EmptyAsNull() ?? "/bin/bash";
}

/// <summary>
/// Connects the terminal
/// </summary>
public override async Task Connect()
{
DockerClient client = new DockerClientConfiguration(new Uri($"http://{Server}:{(Port)}")).CreateClient();
using DockerClient client = new DockerClientConfiguration(new Uri($"http://{Server}:{(Port)}")).CreateClient();

var exec = await client.Exec.ExecCreateContainerAsync(ContainerName, new()
{
Cmd = new List<string>() {"/bin/bash"},
Cmd = new List<string>() { Command },
AttachStderr = true,
AttachStdin = true,
AttachStdout = true,
Expand All @@ -64,9 +71,56 @@ public override async Task Connect()
_ = ReadOutputAsync(this.Socket, multiplexedStream, source, token);

await ReadInputAsync(this.Socket, multiplexedStream, source, token);

await this.Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}

/// <summary>
/// Connects to the console log for a docker container
/// </summary>
public async Task Log()
{
using DockerClient client = new DockerClientConfiguration(new Uri($"http://{Server}:{(Port)}")).CreateClient();

var container = await client.Containers.InspectContainerAsync(ContainerName);
using var logs = await client.Containers.GetContainerLogsAsync(ContainerName, true, new ContainerLogsParameters()
{
Follow = true, // Follow the log output
Tail = "100",
Timestamps = true,
ShowStderr = true,
ShowStdout = true
});

using CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
var rgx = new Regex(@"(?<=(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.[\d]+Z\s)).*$", RegexOptions.Singleline);
_ = ReadOutputAsync(this.Socket, logs, source, token, fixer: (string input) =>
{
var match = rgx.Match(input);
return match.Success ? match.Value : input;
});

await WaitForLogClose();

await this.Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}

/// <summary>
/// Waits for the log to close
/// </summary>
private async Task WaitForLogClose()
{
var buffer = new byte[1024 * 4];
var receiveResult = await this.Socket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);

while (!receiveResult.CloseStatus.HasValue)
{
receiveResult = await this.Socket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
}


/// <summary>
Expand All @@ -76,11 +130,14 @@ public override async Task Connect()
/// <param name="multiplexedStream">the docker shell stream</param>
/// <param name="source">the cancellation token source</param>
/// <param name="cancellationToken">the cancellation token</param>
private async Task ReadOutputAsync(WebSocket webSocket, MultiplexedStream multiplexedStream, CancellationTokenSource source, CancellationToken cancellationToken = default)
/// <param name="fixer">{Optional] a action that takes in the string read from the docker stream and can be altered before sending it to the connecting stream</param>
private async Task ReadOutputAsync(WebSocket webSocket, MultiplexedStream multiplexedStream, CancellationTokenSource source, CancellationToken cancellationToken = default,
Func<string, string>? fixer = null)
{
var dockerBuffer = System.Buffers.ArrayPool<byte>.Shared.Rent(81920);
try
{
var nonPrintableRegex = new Regex(@"[^\x20-\x7E]");
while (true)
{
// Clear buffer
Expand All @@ -96,7 +153,9 @@ private async Task ReadOutputAsync(WebSocket webSocket, MultiplexedStream multip
if (dockerReadResult.Count > 0)
{
string str = Encoding.UTF8.GetString(dockerBuffer, 0, dockerReadResult.Count);
Console.WriteLine(str);
if (fixer != null)
str = fixer(str);

await SendMessage(str);
}
else
Expand Down
1 change: 1 addition & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"StatusIndicators": "Status Indicators",
"ShowSearch": "Show Search",
"Default": "Default",
"DefaultStatic": "Default (Static)",
"ChooseImage":"Choose Image...",
"CurrentImage": "Current Image",
"ColorBackground": "Solid Color",
Expand Down
1 change: 1 addition & 0 deletions wwwroot/backgrounds/default.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class DefaultBackground
{
dispose(){
console.log('disposing default backgorund');
let removeIfExists = function(id)
{
let ele = document.getElementById(id);
Expand Down
76 changes: 76 additions & 0 deletions wwwroot/backgrounds/default.static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class DefaultStaticBackground
{
dispose(){
let removeIfExists = function(id)
{
let ele = document.getElementById(id);
if(ele)
ele.remove();
}
removeIfExists('bg1-static');
removeIfExists('bg2-static');
removeIfExists('bg3-static');
removeIfExists('bkg-default-static-style');
}

init()
{
let createBackground = function(name)
{
if(document.getElementById(name))
return; // already exists
let div = document.createElement("div");
div.className = 'bg-static ' + name;
div.setAttribute('id', name);
document.body.insertBefore(div, document.body.firstChild);

}
createBackground('bg3-static');
createBackground('bg2-static');
createBackground('bg1-static');

let css = `
body
{
background-image: none;
--bg-base: rgb(10,10,10);
background:var(--bg-base, rgb(10,10,10));
}
.bg-static
{
background-image: linear-gradient(-60deg, var(--accent, #ff0090) 50%, var(--bg-base, rgb(10,10,10)) 50%);
bottom:0;
left:-50%;
opacity:.5;
position:fixed;
right:-30%;
top:0;
z-index:-1;
}
.bg1-static {
opacity:0.1 !important;
}
.bg2-static {
opacity:0.05 !important;
right:-80%;
}
.bg3-static {
opacity:0.03 !important;
right:-125%;
}
`
let styleEle = document.getElementById('bkg-default-static-style');
if(!styleEle){
styleEle = document.createElement('style');
styleEle.setAttribute('id', 'bkg-default-static-style');
document.body.insertBefore(styleEle, document.body.firstChild);
}
styleEle.innerHTML = css;
}
}
window.BackgroundType = DefaultStaticBackground;

if(document.querySelector('body.login-page, body.initial-config-page'))
new DefaultStaticBackground().init();
1 change: 1 addition & 0 deletions wwwroot/css/fdrive/views/grid.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wwwroot/css/fdrive/views/grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
img {
border-radius: 0.25rem;
object-fit: contain;
max-width: 120%;
}
i {
font-size:10rem;
Expand Down
Loading

0 comments on commit f766b8f

Please sign in to comment.