Skip to content

Commit c74bb8d

Browse files
committed
fix(docker): handle container conflict when creating
1 parent 89af5d5 commit c74bb8d

File tree

9 files changed

+81
-27
lines changed

9 files changed

+81
-27
lines changed

src/GZCTF/ClientApp/src/pages/About.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import classes from './About.module.css'
2020

2121
const About: FC = () => {
2222
const { config } = useConfig()
23-
const { repo, valid, rawTag: tag, sha, buildtime } = ValidatedRepoMeta()
23+
const { repo, valid, rawTag: tag, sha, buildTime } = ValidatedRepoMeta()
2424
const { t } = useTranslation()
2525
const theme = useMantineTheme()
2626

@@ -85,7 +85,7 @@ const About: FC = () => {
8585
<Group gap="xs">
8686
<Text size="xs" fw={500} c="dimmed" ff="monospace">
8787
{valid
88-
? `Built at ${buildtime.format('YYYY-MM-DDTHH:mm:ssZ')}`
88+
? `Built at ${buildTime.format('YYYY-MM-DDTHH:mm:ssZ')}`
8989
: 'This release is not officially built'}
9090
</Text>
9191
</Group>

src/GZCTF/ClientApp/src/utils/useConfig.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const RepoMeta = {
1818
sha: import.meta.env.VITE_APP_GIT_SHA ?? 'unknown',
1919
rawTag: import.meta.env.VITE_APP_GIT_NAME ?? 'unknown',
2020
timestamp: import.meta.env.VITE_APP_BUILD_TIMESTAMP ?? '',
21-
buildtime: import.meta.env.DEV ? dayjs() : dayjs(import.meta.env.VITE_APP_BUILD_TIMESTAMP),
21+
buildTime: import.meta.env.DEV ? dayjs() : dayjs(import.meta.env.VITE_APP_BUILD_TIMESTAMP),
2222
repo: 'https://github.com/GZTimeWalker/GZCTF',
2323
}
2424

@@ -59,7 +59,7 @@ export const useConfig = () => {
5959
}
6060

6161
export const ValidatedRepoMeta = () => {
62-
const { sha, rawTag, timestamp, buildtime } = RepoMeta
62+
const { sha, rawTag, timestamp, buildTime: buildtime } = RepoMeta
6363

6464
const tag = rawTag.replace(/-.*$/, '')
6565

@@ -73,13 +73,17 @@ export const ValidatedRepoMeta = () => {
7373
}
7474

7575
const showBanner = () => {
76-
const { sha, rawTag: tag, buildtime, repo, valid } = ValidatedRepoMeta()
76+
const { sha, rawTag: tag, buildTime, repo, valid } = ValidatedRepoMeta()
7777
const padding = ' '.repeat(45)
7878

7979
const bannerClr = ['color: #4ccaaa', 'color: unset']
8080
const textClr = ['font-weight: bold', 'font-weight: bold; color: #4ccaaa']
8181
const badClr = ['font-weight: bold', 'font-weight: bold; color: #fe3030']
8282

83+
// The GZCTF identifier is protected by the License.
84+
// DO NOT REMOVE OR MODIFY THE FOLLOWING LINE.
85+
// Please see LICENSE_ADDENDUM.txt for details.
86+
8387
const banner = `
8488
██████╗ ███████╗ ██████╗████████╗███████╗
8589
██╔════╝ ╚══███╔╝ %c ██╗██╗ %c ██╔════╝╚══██╔══╝██╔════╝
@@ -93,7 +97,7 @@ const showBanner = () => {
9397
9498
%cLicense : %cGNU Affero General Public License v3.0
9599
%cCommit : %c${valid ? sha : 'Unofficial build version'}
96-
%cBuilt at : %c${buildtime.format('YYYY-MM-DDTHH:mm:ssZ')}
100+
%cBuilt at : %c${buildTime.format('YYYY-MM-DDTHH:mm:ssZ')}
97101
%cIssues : %c${repo}/issues
98102
`
99103

src/GZCTF/Extensions/DatabaseSinkExtension.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ static LogModel ToLogModel(LogEvent logEvent)
6161
UserName = LogHelper.GetStringValue(userName, "Anonymous"),
6262
Logger = LogHelper.GetStringValue(sourceContext, "Unknown"),
6363
RemoteIP = LogHelper.GetStringValue(ip),
64-
Status = logEvent.Exception is null ?
65-
LogHelper.GetStringValue(status) : TaskStatus.Failed.ToString(),
64+
Status = logEvent.Exception is null ? LogHelper.GetStringValue(status) : TaskStatus.Failed.ToString(),
6665
Exception = logEvent.Exception?.ToString()
6766
};
6867
}

src/GZCTF/Extensions/SignalRSinkExtension.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ public void Emit(LogEvent logEvent)
4141
Msg = logEvent.RenderMessageWithExceptions(),
4242
UserName = LogHelper.GetStringValue(userName, "Anonymous"),
4343
IP = LogHelper.GetStringValue(ip),
44-
Status = logEvent.Exception is null ?
45-
LogHelper.GetStringValue(status) : TaskStatus.Failed.ToString(),
44+
Status = logEvent.Exception is null
45+
? LogHelper.GetStringValue(status)
46+
: TaskStatus.Failed.ToString(),
4647
}).Wait();
4748
}
4849
catch

src/GZCTF/Extensions/TelemetryExtension.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public static void UseTelemetry(this IApplicationBuilder app, TelemetryConfig? c
7070
if (config.Prometheus.Port is { } port)
7171
app.UseOpenTelemetryPrometheusScrapingEndpoint(context
7272
=> context.Connection.LocalPort == port
73-
&& string.Equals(
74-
context.Request.Path.ToString().TrimEnd('/'),
75-
"/metrics",
76-
StringComparison.OrdinalIgnoreCase));
73+
&& string.Equals(
74+
context.Request.Path.ToString().TrimEnd('/'),
75+
"/metrics",
76+
StringComparison.OrdinalIgnoreCase));
7777
else
7878
app.UseOpenTelemetryPrometheusScrapingEndpoint(context
7979
=> string.Equals(

src/GZCTF/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*
1212
* Modifications to these identifiers are prohibited as per the LICENSE_ADDENDUM.txt
1313
*/
14+
1415
global using GZCTF.Models.Data;
1516
global using GZCTF.Utils;
1617
global using AppDbContext = GZCTF.Models.AppDbContext;
@@ -408,6 +409,9 @@ static Program()
408409

409410
internal static void Banner()
410411
{
412+
// The GZCTF identifier is protected by the License.
413+
// DO NOT REMOVE OR MODIFY THE FOLLOWING LINE.
414+
// Please see LICENSE_ADDENDUM.txt for details.
411415
const string banner =
412416
"""
413417
___ ___ ___ ___

src/GZCTF/Services/Container/Manager/DockerManager.cs

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* This file is protected and may not be modified without permission.
33
* See LICENSE_ADDENDUM.txt for details.
44
*/
5+
56
using System.Net;
67
using Docker.DotNet;
78
using Docker.DotNet.Models;
@@ -95,9 +96,20 @@ await _client.Containers.RemoveContainerAsync(container.ContainerId,
9596
};
9697
}
9798

98-
CreateContainerResponse? containerRes = null;
99+
CreateContainerResponse? containerRes;
100+
var retry = 0;
101+
102+
CreateDockerContainer:
99103
try
100104
{
105+
if (retry++ >= 3)
106+
{
107+
_logger.SystemLog(
108+
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_ContainerCreationFailed),
109+
parameters.Name], TaskStatus.Failed, LogLevel.Information);
110+
return null;
111+
}
112+
101113
containerRes = await _client.Containers.CreateContainerAsync(parameters, token);
102114
}
103115
catch (DockerImageNotFoundException)
@@ -106,23 +118,45 @@ await _client.Containers.RemoveContainerAsync(container.ContainerId,
106118
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_PullContainerImage), config.Image],
107119
TaskStatus.Pending, LogLevel.Information);
108120

121+
// pull the image and retry
109122
await _client.Images.CreateImageAsync(new() { FromImage = config.Image }, _meta.Auth,
110123
new Progress<JSONMessage>(msg =>
111124
{
112125
Console.WriteLine($@"{msg.Status}|{msg.ProgressMessage}|{msg.ErrorMessage}");
113126
}), token);
127+
128+
goto CreateDockerContainer;
114129
}
115-
catch (Exception e)
130+
catch (DockerApiException e)
116131
{
117-
_logger.LogError(e,
118-
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_ContainerCreationFailed),
119-
parameters.Name]);
120-
return null;
121-
}
132+
if (e.StatusCode == HttpStatusCode.Conflict)
133+
{
134+
// the container already exists, remove it and retry
135+
try
136+
{
137+
await _client.Containers.RemoveContainerAsync(parameters.Name,
138+
new() { Force = true }, token);
139+
}
140+
catch (Exception ex)
141+
{
142+
_logger.LogError(ex,
143+
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_ContainerDeletionFailed),
144+
parameters.Name]);
145+
return null;
146+
}
122147

123-
try
124-
{
125-
containerRes ??= await _client.Containers.CreateContainerAsync(parameters, token);
148+
goto CreateDockerContainer;
149+
}
150+
151+
_logger.SystemLog(
152+
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_ContainerCreationFailedStatus),
153+
parameters.Name,
154+
e.StatusCode], TaskStatus.Failed, LogLevel.Warning);
155+
_logger.SystemLog(
156+
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_ContainerCreationFailedResponse),
157+
parameters.Name,
158+
e.ResponseBody], TaskStatus.Failed, LogLevel.Error);
159+
return null;
126160
}
127161
catch (Exception e)
128162
{
@@ -134,14 +168,13 @@ await _client.Images.CreateImageAsync(new() { FromImage = config.Image }, _meta.
134168

135169
var container = new Models.Data.Container { ContainerId = containerRes.ID, Image = config.Image };
136170

137-
var retry = 0;
171+
retry = 0;
138172
bool started;
139173

140174
do
141175
{
142176
started = await _client.Containers.StartContainerAsync(container.ContainerId, new(), token);
143-
retry++;
144-
if (retry == 3)
177+
if (retry++ >= 3)
145178
{
146179
_logger.SystemLog(
147180
Program.StaticLocalizer[nameof(Resources.Program.ContainerManager_ContainerInstanceStartFailed),
@@ -170,6 +203,8 @@ await _client.Images.CreateImageAsync(new() { FromImage = config.Image }, _meta.
170203
nameof(Resources.Program.ContainerManager_ContainerInstanceCreationFailedWithError),
171204
config.Image.Split("/").LastOrDefault() ?? "", info.State.Error],
172205
TaskStatus.Failed, LogLevel.Warning);
206+
207+
await DestroyContainerAsync(container, token);
173208
return null;
174209
}
175210

@@ -213,6 +248,9 @@ CreateContainerParameters GetCreateContainerParameters(ContainerConfig config) =
213248
["ChallengeId"] = config.ChallengeId.ToString()
214249
},
215250
Name = DockerMetadata.GetName(config),
251+
// The GZCTF identifier is protected by the License.
252+
// DO NOT REMOVE OR MODIFY THE FOLLOWING LINE.
253+
// Please see LICENSE_ADDENDUM.txt for details.
216254
Env = config.Flag is null
217255
? [$"GZCTF_TEAM_ID={config.TeamId}"]
218256
: [$"GZCTF_FLAG={config.Flag}", $"GZCTF_TEAM_ID={config.TeamId}"],

src/GZCTF/Services/Container/Manager/KubernetesManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* This file is protected and may not be modified without permission.
33
* See LICENSE_ADDENDUM.txt for details.
44
*/
5+
56
using System.Net;
67
using GZCTF.Models.Internal;
78
using GZCTF.Services.Container.Provider;
@@ -79,6 +80,9 @@ authSecretName is null
7980
Name = name,
8081
Image = config.Image,
8182
ImagePullPolicy = "Always",
83+
// The GZCTF identifier is protected by the License.
84+
// DO NOT REMOVE OR MODIFY THE FOLLOWING LINE.
85+
// Please see LICENSE_ADDENDUM.txt for details.
8286
Env =
8387
config.Flag is null
8488
? [new V1EnvVar("GZCTF_TEAM_ID", config.TeamId)]

src/GZCTF/Services/Container/Manager/SwarmManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* This file is protected and may not be modified without permission.
33
* See LICENSE_ADDENDUM.txt for details.
44
*/
5+
56
using System.Net;
67
using Docker.DotNet;
78
using Docker.DotNet.Models;
@@ -179,6 +180,9 @@ ServiceCreateParameters GetServiceCreateParameters(ContainerConfig config) =>
179180
new()
180181
{
181182
Image = config.Image,
183+
// The GZCTF identifier is protected by the License.
184+
// DO NOT REMOVE OR MODIFY THE FOLLOWING LINE.
185+
// Please see LICENSE_ADDENDUM.txt for details.
182186
Env =
183187
config.Flag is null
184188
? [$"GZCTF_TEAM_ID={config.TeamId}"]

0 commit comments

Comments
 (0)