Как при развёртывании контейнера докер запускать скрипт и копировать созданный им файл в другой образ?

Я пытаюсь написать надстройку над образом 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 шт):

Автор решения: aepot

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
→ Ссылка