diff --git a/.aspire/settings.json b/.aspire/settings.json new file mode 100644 index 0000000..c195338 --- /dev/null +++ b/.aspire/settings.json @@ -0,0 +1,3 @@ +{ + "appHostPath": "../src/Aspire/src/AppHost/AppHost.csproj" +} \ No newline at end of file diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 776a0bf..70458c0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,6 +15,13 @@ "dotnet-ef" ], "rollForward": false + }, + "aspire.cli": { + "version": "9.4.0-preview.1.25378.8", + "commands": [ + "aspire" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 252ca96..b722565 100644 --- a/.editorconfig +++ b/.editorconfig @@ -455,6 +455,8 @@ dotnet_diagnostic.CA1707.severity = None dotnet_diagnostic.CA1716.severity = Suggestion # CA1032: Implement standard exception constructors dotnet_diagnostic.CA1032.severity = Suggestion +# AV1500: A method should not exceed a predefined number (60-100 lines) of lines +dotnet_diagnostic.AV1500.severity = none ################################################################################## # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ diff --git a/README.md b/README.md index 1c36c47..53ba69b 100644 --- a/README.md +++ b/README.md @@ -217,13 +217,13 @@ dotnet dev-certs https --trust ### Aspire -To run the application using the `ASPIRE App Host`, execute the following command from the solution root: +To run the application using the `Aspire App Host`, execute the following command from the solution root: ```bash -dotnet run --project ./src/Aspire/src/AppHost +aspire run ``` -> Note:The `ASPIRE dashboard` will be available at `http://localhost:18888` +> Note:The `Aspire dashboard` will be available at `http://localhost:18888` > ### Docker Compose diff --git a/src/Aspire/src/AppHost/AppHost.csproj b/src/Aspire/src/AppHost/AppHost.csproj index b78560f..3cb0678 100644 --- a/src/Aspire/src/AppHost/AppHost.csproj +++ b/src/Aspire/src/AppHost/AppHost.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Aspire/src/AppHost/Program.cs b/src/Aspire/src/AppHost/Program.cs index b391c07..959d6f7 100644 --- a/src/Aspire/src/AppHost/Program.cs +++ b/src/Aspire/src/AppHost/Program.cs @@ -2,39 +2,82 @@ using System.Net.Sockets; var builder = DistributedApplication.CreateBuilder(args); -// 1. Database Services -var username = builder.AddParameter("username", "postgres", secret: true); -var password = builder.AddParameter("password", "postgres", secret: true); +builder.AddDockerComposeEnvironment("docker-compose"); -var postgres = builder.AddPostgres("postgres", username, password) +// 1. Database Services +var pgUsername = builder.AddParameter("pg-username", "postgres", secret: true); +var pgPassword = builder.AddParameter("pg-password", "postgres", secret: true); + +var postgres = builder.AddPostgres("postgres", pgUsername, pgPassword) .WithImage("postgres:latest") - .WithEndpoint(port: 5432, targetPort: 5432, name: "postgres") + .WithEndpoint( + "tcp", + e => + { + e.Port = 5432; + e.TargetPort = 5432; + e.IsProxied = true; + e.IsExternal = false; + }) .WithArgs( - "-c", "wal_level=logical", - "-c", "max_prepared_transactions=10" - ) - .WithDataVolume("postgres-data") - .WithLifetime(ContainerLifetime.Persistent); + "-c", + "wal_level=logical", + "-c", + "max_prepared_transactions=10"); + +if (builder.ExecutionContext.IsPublishMode) +{ + postgres.WithDataVolume("postgres-data") + .WithLifetime(ContainerLifetime.Persistent); +} + var flightDb = postgres.AddDatabase("flight"); var passengerDb = postgres.AddDatabase("passenger"); var identityDb = postgres.AddDatabase("identity"); var persistMessageDb = postgres.AddDatabase("persist-message"); -var mongoUsername = builder.AddParameter("mongo-username", "root"); +var mongoUsername = builder.AddParameter("mongo-username", "root", secret: true); var mongoPassword = builder.AddParameter("mongo-password", "secret", secret: true); var mongo = builder.AddMongoDB("mongo", userName: mongoUsername, password: mongoPassword) - .WithImage("mongo:latest") - .WithEndpoint(port: 27017, targetPort: 27017, name: "mongo") - .WithDataVolume("mongo-data") - .WithLifetime(ContainerLifetime.Persistent); + .WithImage("mongo") + .WithImageTag("latest") + .WithEndpoint( + "tcp", + e => + { + e.Port = 27017; + e.TargetPort = 27017; + e.IsProxied = true; + e.IsExternal = false; + }); + +if (builder.ExecutionContext.IsPublishMode) +{ + mongo.WithDataVolume("mongo-data") + .WithLifetime(ContainerLifetime.Persistent); +} + var redis = builder.AddRedis("redis") .WithImage("redis:latest") - .WithEndpoint(port: 6379, targetPort: 6379, name: "redis") - .WithDataVolume("redis-data") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint( + "tcp", + e => + { + e.Port = 6379; + e.TargetPort = 6379; + e.IsProxied = true; + e.IsExternal = false; + }); + +if (builder.ExecutionContext.IsPublishMode) +{ + redis.WithDataVolume("redis-data") + .WithLifetime(ContainerLifetime.Persistent); +} + var eventstore = builder.AddEventStore("eventstore") .WithImage("eventstore/eventstore") @@ -43,41 +86,103 @@ var eventstore = builder.AddEventStore("eventstore") .WithEnvironment("EVENTSTORE_START_STANDARD_PROJECTIONS", "True") .WithEnvironment("EVENTSTORE_INSECURE", "True") .WithEnvironment("EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP", "True") - .WithHttpEndpoint(port: 2113, targetPort: 2113, name: "eventstore-http") - .WithDataVolume("eventstore-data") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint( + "http", + e => + { + e.TargetPort = 2113; + e.Port = 2113; + e.IsProxied = true; + e.IsExternal = true; + }) + .WithEndpoint( + port: 1113, + targetPort: 1113, + name: "tcp", + isProxied: true, + isExternal: false); + +if (builder.ExecutionContext.IsPublishMode) +{ + eventstore.WithDataVolume("eventstore-data") + .WithLifetime(ContainerLifetime.Persistent); +} // 2. Messaging Services -var rabbitmq = builder.AddRabbitMQ("rabbitmq") - .WithImage("rabbitmq:management") - .WithEndpoint(port: 5672, targetPort: 5672, name: "rabbitmq-amqp") - .WithEndpoint(port: 15672, targetPort: 15672, name: "rabbitmq-management") - .WithLifetime(ContainerLifetime.Persistent); +var rabbitmqUsername = builder.AddParameter("rabbitmq-username", "guest", secret: true); +var rabbitmqPassword = builder.AddParameter("rabbitmq-password", "guest", secret: true); -// 3. Observability Services +var rabbitmq = builder.AddRabbitMQ("rabbitmq", rabbitmqUsername, rabbitmqPassword) + .WithManagementPlugin() + .WithEndpoint( + "tcp", + e => + { + e.TargetPort = 5672; + e.Port = 5672; + e.IsProxied = true; + e.IsExternal = false; + }) + .WithEndpoint( + "management", + e => + { + e.TargetPort = 15672; + e.Port = 15672; + e.IsProxied = true; + e.IsExternal = true; + }); + +if (builder.ExecutionContext.IsPublishMode) +{ + rabbitmq.WithLifetime(ContainerLifetime.Persistent); +} + +// // 3. Observability Services var jaeger = builder.AddContainer("jaeger-all-in-one", "jaegertracing/all-in-one") - .WithEndpoint(port: 6831, targetPort: 6831, name: "jaeger-udp", protocol: ProtocolType.Udp) - .WithEndpoint(port: 16686, targetPort: 16686, name: "jaeger-ui") - .WithEndpoint(port: 14268, targetPort: 14268, name: "jaeger-api") - .WithEndpoint(port: 14317, targetPort: 4317, name: "jaeger-otlp-grpc") - .WithEndpoint(port: 14318, targetPort: 4318, name: "jaeger-otlp-http") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint( + port: 6831, + targetPort: 6831, + name: "agent", + protocol: ProtocolType.Udp, + isProxied: true, + isExternal: false) + .WithEndpoint(port: 16686, targetPort: 16686, name: "http", isProxied: true, isExternal: true) + .WithEndpoint(port: 14268, targetPort: 14268, name: "collector", isProxied: true, isExternal: false) + .WithEndpoint(port: 14317, targetPort: 4317, name: "otlp-grpc", isProxied: true, isExternal: false) + .WithEndpoint(port: 14318, targetPort: 4318, name: "otlp-http", isProxied: true, isExternal: false); + +if (builder.ExecutionContext.IsPublishMode) +{ + jaeger.WithLifetime(ContainerLifetime.Persistent); +} var zipkin = builder.AddContainer("zipkin-all-in-one", "openzipkin/zipkin") - .WithEndpoint(port: 9411, targetPort: 9411, name: "zipkin-api") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 9411, targetPort: 9411, name: "http", isProxied: true, isExternal: true); + +if (builder.ExecutionContext.IsPublishMode) +{ + zipkin.WithLifetime(ContainerLifetime.Persistent); +} var otelCollector = builder.AddContainer("otel-collector", "otel/opentelemetry-collector-contrib") - .WithBindMount("../../../../deployments/configs/otel-collector-config.yaml", "/etc/otelcol-contrib/config.yaml", isReadOnly: true) + .WithBindMount( + "../../../../deployments/configs/otel-collector-config.yaml", + "/etc/otelcol-contrib/config.yaml", + isReadOnly: true) .WithArgs("--config=/etc/otelcol-contrib/config.yaml") - .WithEndpoint(port: 11888, targetPort: 1888, name: "otel-pprof") - .WithEndpoint(port: 8888, targetPort: 8888, name: "otel-metrics") - .WithEndpoint(port: 8889, targetPort: 8889, name: "otel-exporter-metrics") - .WithEndpoint(port: 13133, targetPort: 13133, name: "otel-health") - .WithEndpoint(port: 4317, targetPort: 4317, name: "otel-grpc") - .WithEndpoint(port: 4318, targetPort: 4318, name: "otel-http") - .WithEndpoint(port: 55679, targetPort: 55679, name: "otel-zpages") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 11888, targetPort: 1888, name: "otel-pprof", isProxied: true, isExternal: true) + .WithEndpoint(port: 8888, targetPort: 8888, name: "otel-metrics", isProxied: true, isExternal: true) + .WithEndpoint(port: 8889, targetPort: 8889, name: "otel-exporter-metrics", isProxied: true, isExternal: true) + .WithEndpoint(port: 13133, targetPort: 13133, name: "otel-health", isProxied: true, isExternal: true) + .WithEndpoint(port: 4317, targetPort: 4317, name: "otel-grpc", isProxied: true, isExternal: true) + .WithEndpoint(port: 4318, targetPort: 4318, name: "otel-http", isProxied: true, isExternal: true) + .WithEndpoint(port: 55679, targetPort: 55679, name: "otel-zpages", isProxied: true, isExternal: true); + +if (builder.ExecutionContext.IsPublishMode) +{ + otelCollector.WithLifetime(ContainerLifetime.Persistent); +} var prometheus = builder.AddContainer("prometheus", "prom/prometheus") .WithBindMount("../../../../deployments/configs/prometheus.yaml", "/etc/prometheus/prometheus.yml") @@ -87,8 +192,12 @@ var prometheus = builder.AddContainer("prometheus", "prom/prometheus") "--web.console.libraries=/usr/share/prometheus/console_libraries", "--web.console.templates=/usr/share/prometheus/consoles", "--web.enable-remote-write-receiver") - .WithEndpoint(port: 9090, targetPort: 9090, name: "prometheus-web") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 9090, targetPort: 9090, name: "http", isProxied: true, isExternal: true); + +if (builder.ExecutionContext.IsPublishMode) +{ + prometheus.WithLifetime(ContainerLifetime.Persistent); +} var grafana = builder.AddContainer("grafana", "grafana/grafana") .WithEnvironment("GF_INSTALL_PLUGINS", "grafana-clock-panel,grafana-simple-json-datasource") @@ -97,8 +206,12 @@ var grafana = builder.AddContainer("grafana", "grafana/grafana") .WithEnvironment("GF_FEATURE_TOGGLES_ENABLE", "traceqlEditor") .WithBindMount("../../../../deployments/configs/grafana/provisioning", "/etc/grafana/provisioning") .WithBindMount("../../../../deployments/configs/grafana/dashboards", "/var/lib/grafana/dashboards") - .WithEndpoint(port: 3000, targetPort: 3000, name: "grafana-web") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 3000, targetPort: 3000, name: "http", isProxied: true, isExternal: true); + +if (builder.ExecutionContext.IsPublishMode) +{ + grafana.WithLifetime(ContainerLifetime.Persistent); +} var nodeExporter = builder.AddContainer("node-exporter", "prom/node-exporter") .WithBindMount("/proc", "/host/proc", isReadOnly: true) @@ -108,22 +221,36 @@ var nodeExporter = builder.AddContainer("node-exporter", "prom/node-exporter") "--path.procfs=/host/proc", "--path.rootfs=/rootfs", "--path.sysfs=/host/sys") - .WithEndpoint(port: 9101, targetPort: 9100, name: "node-exporter") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 9101, targetPort: 9100, name: "http", isProxied: true, isExternal: true); + +if (builder.ExecutionContext.IsPublishMode) +{ + nodeExporter.WithLifetime(ContainerLifetime.Persistent); +} var tempo = builder.AddContainer("tempo", "grafana/tempo") .WithBindMount("../../../../deployments/configs/tempo.yaml", "/etc/tempo.yaml", isReadOnly: true) .WithArgs("--config.file=/etc/tempo.yaml") - .WithEndpoint(port: 3200, targetPort: 3200, name: "tempo") - .WithEndpoint(port: 24317, targetPort: 4317, name: "tempo-otlp-grpc") - .WithEndpoint(port: 24318, targetPort: 4318, name: "tempo-otlp-http") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 3200, targetPort: 3200, name: "http", isProxied: true, isExternal: false) + .WithEndpoint(port: 9095, targetPort: 9095, name: "grpc", isProxied: true, isExternal: false) + .WithEndpoint(port: 4317, targetPort: 4317, name: "otlp-grpc", isProxied: true, isExternal: false) + .WithEndpoint(port: 4318, targetPort: 4318, name: "otlp-http", isProxied: true, isExternal: false); + +if (builder.ExecutionContext.IsPublishMode) +{ + tempo.WithLifetime(ContainerLifetime.Persistent); +} var loki = builder.AddContainer("loki", "grafana/loki") .WithBindMount("../../../../deployments/configs/loki-config.yaml", "/etc/loki/local-config.yaml", isReadOnly: true) .WithArgs("-config.file=/etc/loki/local-config.yaml") - .WithEndpoint(port: 3100, targetPort: 3100, name: "loki") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint(port: 3100, targetPort: 3100, name: "http", isProxied: true, isExternal: false) + .WithEndpoint(port: 9096, targetPort: 9096, name: "grpc", isProxied: true, isExternal: false); + +if (builder.ExecutionContext.IsPublishMode) +{ + loki.WithLifetime(ContainerLifetime.Persistent); +} var elasticsearch = builder.AddElasticsearch("elasticsearch") .WithImage("docker.elastic.co/elasticsearch/elasticsearch:8.17.0") @@ -139,18 +266,41 @@ var elasticsearch = builder.AddElasticsearch("elasticsearch") .WithEnvironment("transport.host", "localhost") .WithEnvironment("bootstrap.memory_lock", "true") .WithEnvironment("cluster.routing.allocation.disk.threshold_enabled", "false") - .WithEndpoint(port: 9200, targetPort: 9200, name: "elasticsearch-http") - .WithEndpoint(port: 9300, targetPort: 9300, name: "elasticsearch-transport") - .WithDataVolume("elastic-data") - .WithLifetime(ContainerLifetime.Persistent); + .WithEndpoint( + "http", + e => + { + e.TargetPort = 9200; + e.Port = 9200; + e.IsProxied = true; + e.IsExternal = false; + }) + .WithEndpoint( + "internal", + e => + { + e.TargetPort = 9300; + e.Port = 9300; + e.IsProxied = true; + e.IsExternal = false; + }) + .WithDataVolume("elastic-data"); + +if (builder.ExecutionContext.IsPublishMode) +{ + elasticsearch.WithLifetime(ContainerLifetime.Persistent); +} var kibana = builder.AddContainer("kibana", "docker.elastic.co/kibana/kibana:8.17.0") .WithEnvironment("ELASTICSEARCH_HOSTS", "http://elasticsearch:9200") - .WithEndpoint(port: 5601, targetPort: 5601, name: "kibana") + .WithEndpoint(port: 5601, targetPort: 5601, name: "http", isProxied: true, isExternal: true) .WithReference(elasticsearch) - .WaitFor(elasticsearch) - .WithLifetime(ContainerLifetime.Persistent); + .WaitFor(elasticsearch); +if (builder.ExecutionContext.IsPublishMode) +{ + kibana.WithLifetime(ContainerLifetime.Persistent); +} // 5. Application Services var identity = builder.AddProject("identity-service")