Как при развёртывании контейнера докер запускать скрипт и копировать созданный им файл в другой образ?
Я пытаюсь написать надстройку над образом swagger-ui, для того чтобы при развёртывании контейнера проходится по указанным адресам других контейнеров и агрегировать их swagger.json в один общий, который затем передавать в swagger-ui. На данный момент я написал скрипт на C# который успешно агрегирует json, но у меня никак не получается передать его в swagger. C#:
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Net;
using System.Text;
namespace SwaggerConfigGeneration
{
internal class Program
{
private const string startJSON = @"{
""openapi"": ""3.0.1"",
""info"": {
""title"": ""TestApi"",
""version"": ""1.0""
},
""paths"": {";
private const string endJSON = @"},
""components"": { }
}";
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder(
startJSON);
var swaggerServises = (Environment
.GetEnvironmentVariable("SWAGGER_SERVISES") ?? "localhost")
.Replace(" ", "")
.Replace("\n", "")
.Split(',');
try
{
foreach (var servis in swaggerServises)
{
var json = GetSwaggerServisJson(servis);
sb.Append(json["paths"].ToString()[1..^2]);
sb.Append(',');
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
sb.Remove(sb.Length-1,1);
sb.Append(endJSON);
using (FileStream fstream = new FileStream("swagger.json", FileMode.Create))
{
var buf = Encoding.Default.GetBytes(sb.ToString());
fstream.WriteAsync(buf, 0, buf.Length);
}
Console.WriteLine("Произведенно получение данных API");
}
private static JObject GetSwaggerServisJson(string swaggerServis)
{
using (WebClient wc = new WebClient())
{
var json = wc.DownloadString($"http://{swaggerServis}:8080/swagger/v1/swagger.json");
return JObject.Parse(json);
}
}
}
}
dockerfile:
FROM mcr.microsoft.com/dotnet/runtime:8.0 as builder
WORKDIR /app
ENV SWAGGER_SERVISES = localhost
COPY /bin/Debug/net8.0 /app
RUN dotnet SwaggerConfigGeneration.dll
FROM swaggerapi/swagger-ui
COPY --from=builder /app/swagger.json /app/swagger.json
docker-compose.yml
version: '3'
services:
swagger:
image: swagger-config-generation:latest
container_name: swagger
environment:
SWAGGER_SERVISES: ${SWAGGER_SERVISES}
depends_on:
- test-app
- test-app2
ports:
- 8081:8080
test-app:
image: testapi:latest
container_name: test-app
ports:
- 8080:8080
test-app2:
image: testapi2:latest
container_name: test-app2
ports:
- 8082:8080
.env:
SWAGGER_SERVISES = test-app2, test-app
Получившийся JSON:
{
"openapi": "3.0.1",
"info": {
"title": "TestApi",
"version": "1.0"
},
"paths": {
"/WeatherForecast": {
"get": {
"tags": [
"WeatherForecast"
],
"operationId": "GetWeatherForecast",
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
}
}
}
}
}
},
"/Logging/GetLogging": {
"get": {
"tags": [
"Logging"
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
},
"application/json": {
"schema": {
"type": "string"
}
},
"text/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
},
"/Logging/GetErrore": {
"get": {
"tags": [
"Logging"
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
},
"application/json": {
"schema": {
"type": "string"
}
},
"text/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}},
"components": { }
}
JSON который копируется в swagger:
{
"openapi": "3.0.1",
"info": {
"title": "TestApi",
"version": "1.0"
},
"paths": {},
"components": {}
}
Ответы (1 шт):
Dockerfile наверное должен выглядеть вот так
FROM mcr.microsoft.com/dotnet/runtime:8.0 as builder
WORKDIR /app
ARG SWAGGER_SERVICES
ENV SWAGGER_SERVICES $SWAGGER_SERVICES
COPY /bin/Debug/net8.0 /app
RUN dotnet SwaggerConfigGeneration.dll $SWAGGER_SERVICES
FROM swaggerapi/swagger-ui
COPY --from=builder /app/swagger.json /app/swagger.json
.env будет выглядеть вот так, через пробел (я исправил опечатку в названии переменной)
SWAGGER_SERVICES = test-app2 test-app
А скрипт получится примерно такой
internal class Program
{
private static readonly HttpClient client = new();
static async Task Main(string[] args)
{
string[] swaggerServices = args.Length > 0 ? args : ["localhost"];
JsonObject root = (JsonObject)JsonNode.Parse(JsonSerializer.Serialize(new {
openapi = "3.0.1",
info = new {
title = "TestApi",
version = "1.0"
},
paths = new { },
components = new { }
}));
try
{
JsonObject paths = root["paths"].AsObject();
foreach (var service in swaggerServices)
{
JsonObject servicePaths = await GetSwaggerServiceJson(service);
foreach (var pair in servicePaths)
{
paths.Add(pair.Key, pair.Value);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
File.WriteAllText("swagger.json", root.ToString());
Console.WriteLine("Произведенно получение данных API");
}
private static async Task<JsonObject> GetSwaggerServiceJson(string swaggerService)
{
string json = await client.GetStringAsync($"http://{swaggerService}:8080/swagger/v1/swagger.json");
return JsonNode.Parse(json).AsObject()["paths"].AsObject();
}
}
При этом пакет Newtonsoft.Json можно удалить из проекта.
Крайне не рекомендую работать с JSON как с текстом, можно наделать ошибок.
Чуть не забыл, ещё yaml поправить, docker-compose же должен знать, что образ надо собрать.
services:
swagger:
build:
context: ./ # путь к файлам сборки контейнера
dockerfile: ./Dockerfile # путь к файлу сборки
args:
SWAGGER_SERVICES: ${SWAGGER_SERVICES}
container_name: swagger
depends_on:
- test-app
- test-app2
ports:
- 8081:8080
test-app:
image: testapi:latest
container_name: test-app
ports:
- 8080:8080
test-app2:
image: testapi2:latest
container_name: test-app2
ports:
- 8082:8080