From faff4b7e09e3c79ad1b3d407f35f0299d4312360 Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Wed, 9 Oct 2024 19:08:48 +0300 Subject: [PATCH 01/11] Implement multi-tenant db context for SQLite depending on the X-Tenant-Id Header --- NorthwindCRUD/Program.cs | 39 ++------- .../DbContextConfigurationProvider.cs | 81 +++++++++++++++++++ NorthwindCRUD/appsettings.json | 2 +- 3 files changed, 90 insertions(+), 32 deletions(-) create mode 100644 NorthwindCRUD/Providers/DbContextConfigurationProvider.cs diff --git a/NorthwindCRUD/Program.cs b/NorthwindCRUD/Program.cs index 2203523..e46786b 100644 --- a/NorthwindCRUD/Program.cs +++ b/NorthwindCRUD/Program.cs @@ -11,6 +11,7 @@ using Newtonsoft.Json.Converters; using NorthwindCRUD.Filters; using NorthwindCRUD.Helpers; +using NorthwindCRUD.Providers; using NorthwindCRUD.Services; namespace NorthwindCRUD @@ -74,36 +75,10 @@ public static void Main(string[] args) }); }); - var dbProvider = builder.Configuration.GetConnectionString("Provider"); - - if (dbProvider == "SQLite") - { - // For SQLite in memory to be shared across multiple EF calls, we need to maintain a separate open connection. - // see post https://stackoverflow.com/questions/56319638/entityframeworkcore-sqlite-in-memory-db-tables-are-not-created - var keepAliveConnection = new SqliteConnection(builder.Configuration.GetConnectionString("SQLiteConnectionString")); - keepAliveConnection.Open(); - } - - builder.Services.AddDbContext(options => + builder.Services.AddDbContext((serviceProvider, options) => { - if (dbProvider == "SqlServer") - { - options.UseSqlServer(builder.Configuration.GetConnectionString("SqlServerConnectionString")); - } - else if (dbProvider == "InMemory") - { - options.ConfigureWarnings(warnOpts => - { - // InMemory doesn't support transactions and we're ok with it - warnOpts.Ignore(InMemoryEventId.TransactionIgnoredWarning); - }); - - options.UseInMemoryDatabase(databaseName: builder.Configuration.GetConnectionString("InMemoryDBConnectionString")); - } - else if (dbProvider == "SQLite") - { - options.UseSqlite(builder.Configuration.GetConnectionString("SQLiteConnectionString")); - } + var configurationProvider = serviceProvider.GetRequiredService(); + configurationProvider.ConfigureOptions(options); }); var config = new MapperConfiguration(cfg => @@ -135,8 +110,10 @@ public static void Main(string[] args) }); builder.Services.AddAuthorization(); - + builder.Services.AddHttpContextAccessor(); + builder.Services.AddMemoryCache(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -150,7 +127,7 @@ public static void Main(string[] args) builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); - + var app = builder.Build(); // Necessary to detect if it's behind a load balancer, for example changing protocol, port or hostname diff --git a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs new file mode 100644 index 0000000..f0085b9 --- /dev/null +++ b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs @@ -0,0 +1,81 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using NorthwindCRUD.Helpers; + +namespace NorthwindCRUD.Providers +{ + public class DbContextConfigurationProvider + { + private const string DefaultTenantId = "default-tenant"; + private const string TenantHeaderKey = "X-Tenant-ID"; + private const string DatabaseConnectionCacheKey = "Data-Connection-{0}"; + + private readonly TimeSpan connectionKeySlidingExpiration = TimeSpan.FromHours(24); + + private readonly IHttpContextAccessor context; + private readonly IMemoryCache memoryCache; + private readonly IConfiguration configuration; + + public DbContextConfigurationProvider(IHttpContextAccessor context, IMemoryCache memoryCache, IConfiguration configuration) + { + this.context = context; + this.memoryCache = memoryCache; + this.configuration = configuration; + } + + public void ConfigureOptions(DbContextOptionsBuilder options) + { + var dbProvider = configuration.GetConnectionString("Provider"); + + if (dbProvider == "SqlServer") + { + options.UseSqlServer(configuration.GetConnectionString("SqlServerConnectionString")); + } + else if (dbProvider == "SQLite") + { + var tenantId = context.HttpContext?.Request.Headers[TenantHeaderKey].FirstOrDefault() ?? DefaultTenantId; + var connectionString = string.Format(configuration.GetConnectionString("SQLiteConnectionString"), tenantId); + + if (!memoryCache.TryGetValue(string.Format(DatabaseConnectionCacheKey, tenantId), out SqliteConnection connection)) + { + connection = new SqliteConnection(connectionString); + var cacheKey = string.Format(DatabaseConnectionCacheKey, tenantId); + memoryCache.Set(cacheKey, connection, GetCacheConnectionEntryOptions()); + } + + // For SQLite in memory to be shared across multiple EF calls, we need to maintain a separate open connection. + // see post https://stackoverflow.com/questions/56319638/entityframeworkcore-sqlite-in-memory-db-tables-are-not-created + connection.Open(); + + options.UseSqlite(connection).EnableSensitiveDataLogging(); + + SeedDb(options); + } + } + + private static void SeedDb(DbContextOptionsBuilder optionsBuilder) + { + using var dataContext = new DataContext(optionsBuilder.Options); + DBSeeder.Seed(dataContext); + } + + private static void CloseConnection(object key, object value, EvictionReason reason, object state) + { + //Used to clear datasource from memory. + (value as SqliteConnection)?.Close(); + } + + private MemoryCacheEntryOptions GetCacheConnectionEntryOptions() + { + var cacheEntryOptions = new MemoryCacheEntryOptions + { + SlidingExpiration = this.connectionKeySlidingExpiration, + }; + + cacheEntryOptions.RegisterPostEvictionCallback(CloseConnection); + + return cacheEntryOptions; + } + } +} diff --git a/NorthwindCRUD/appsettings.json b/NorthwindCRUD/appsettings.json index 2fe4d3c..f33f549 100644 --- a/NorthwindCRUD/appsettings.json +++ b/NorthwindCRUD/appsettings.json @@ -3,7 +3,7 @@ "Provider": "SQLite", //SqlServer or InMemory or SQLite "SqlServerConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Database=NorthwindCRUD;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=True", "InMemoryDBConnectionString": "NorthwindCRUD", - "SQLiteConnectionString": "DataSource=northwind-db;mode=memory;cache=shared" + "SQLiteConnectionString": "DataSource=northwind-db-{0};mode=memory;cache=shared" }, "Logging": { "LogLevel": { From 2ecd687c59834f088782bd7f07c921660afb17c8 Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Wed, 9 Oct 2024 19:10:23 +0300 Subject: [PATCH 02/11] Remove empty spaces --- NorthwindCRUD/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NorthwindCRUD/Program.cs b/NorthwindCRUD/Program.cs index e46786b..b2dbe25 100644 --- a/NorthwindCRUD/Program.cs +++ b/NorthwindCRUD/Program.cs @@ -127,7 +127,7 @@ public static void Main(string[] args) builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); - + var app = builder.Build(); // Necessary to detect if it's behind a load balancer, for example changing protocol, port or hostname From 5e81b63cc97af69f87abb411384694f9370d633a Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Thu, 10 Oct 2024 14:28:39 +0300 Subject: [PATCH 03/11] refactor, sanitize connection string, change to absolute expiration --- .../DbContextConfigurationProvider.cs | 30 ++++++++++++++----- NorthwindCRUD/appsettings.json | 3 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs index f0085b9..e0e88c3 100644 --- a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs +++ b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs @@ -11,8 +11,6 @@ public class DbContextConfigurationProvider private const string TenantHeaderKey = "X-Tenant-ID"; private const string DatabaseConnectionCacheKey = "Data-Connection-{0}"; - private readonly TimeSpan connectionKeySlidingExpiration = TimeSpan.FromHours(24); - private readonly IHttpContextAccessor context; private readonly IMemoryCache memoryCache; private readonly IConfiguration configuration; @@ -34,13 +32,15 @@ public void ConfigureOptions(DbContextOptionsBuilder options) } else if (dbProvider == "SQLite") { - var tenantId = context.HttpContext?.Request.Headers[TenantHeaderKey].FirstOrDefault() ?? DefaultTenantId; - var connectionString = string.Format(configuration.GetConnectionString("SQLiteConnectionString"), tenantId); + var tenantId = GetTenantId(); + + var connectionString = this.GetSqlLiteConnectionString(tenantId); + + var cacheKey = string.Format(DatabaseConnectionCacheKey, tenantId); - if (!memoryCache.TryGetValue(string.Format(DatabaseConnectionCacheKey, tenantId), out SqliteConnection connection)) + if (!memoryCache.TryGetValue(cacheKey, out SqliteConnection connection)) { connection = new SqliteConnection(connectionString); - var cacheKey = string.Format(DatabaseConnectionCacheKey, tenantId); memoryCache.Set(cacheKey, connection, GetCacheConnectionEntryOptions()); } @@ -68,14 +68,30 @@ private static void CloseConnection(object key, object value, EvictionReason rea private MemoryCacheEntryOptions GetCacheConnectionEntryOptions() { + var defaultAbsoluteCacheExpirationInHours = this.configuration.GetValue("DefaultAbsoluteCacheExpirationInHours"); var cacheEntryOptions = new MemoryCacheEntryOptions { - SlidingExpiration = this.connectionKeySlidingExpiration, + AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(defaultAbsoluteCacheExpirationInHours), }; cacheEntryOptions.RegisterPostEvictionCallback(CloseConnection); return cacheEntryOptions; } + + private string GetSqlLiteConnectionString(string tenantId) + { + var connectionStringTemplate = configuration.GetConnectionString("SQLiteConnectionString"); + var unsanitizedConntectionString = string.Format(connectionStringTemplate, tenantId); + var connectionStringBuilder = new SqliteConnectionStringBuilder(unsanitizedConntectionString); + var sanitizedConntectionString = connectionStringBuilder.ToString(); + + return sanitizedConntectionString; + } + + private string GetTenantId() + { + return context.HttpContext?.Request.Headers[TenantHeaderKey].FirstOrDefault() ?? DefaultTenantId; + } } } diff --git a/NorthwindCRUD/appsettings.json b/NorthwindCRUD/appsettings.json index f33f549..4faeb7c 100644 --- a/NorthwindCRUD/appsettings.json +++ b/NorthwindCRUD/appsettings.json @@ -3,8 +3,9 @@ "Provider": "SQLite", //SqlServer or InMemory or SQLite "SqlServerConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Database=NorthwindCRUD;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=True", "InMemoryDBConnectionString": "NorthwindCRUD", - "SQLiteConnectionString": "DataSource=northwind-db-{0};mode=memory;cache=shared" + "SQLiteConnectionString": "DataSource=northwind-db-{0};mode=memory;cache=shared;" }, + "DefaultAbsoluteCacheExpirationInHours": 24, "Logging": { "LogLevel": { "Default": "Information", From 1b4d8480f395e0e45ba7ba62474b47f4828e210a Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Thu, 10 Oct 2024 15:52:54 +0300 Subject: [PATCH 04/11] move variable --- NorthwindCRUD/Providers/DbContextConfigurationProvider.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs index e0e88c3..31170d0 100644 --- a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs +++ b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs @@ -34,12 +34,11 @@ public void ConfigureOptions(DbContextOptionsBuilder options) { var tenantId = GetTenantId(); - var connectionString = this.GetSqlLiteConnectionString(tenantId); - var cacheKey = string.Format(DatabaseConnectionCacheKey, tenantId); if (!memoryCache.TryGetValue(cacheKey, out SqliteConnection connection)) { + var connectionString = this.GetSqlLiteConnectionString(tenantId); connection = new SqliteConnection(connectionString); memoryCache.Set(cacheKey, connection, GetCacheConnectionEntryOptions()); } From 965cb0f92a700abf7b5bc50560d24431aab96663 Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Thu, 10 Oct 2024 16:46:27 +0300 Subject: [PATCH 05/11] fix compiler warnings --- NorthwindCRUD/Providers/DbContextConfigurationProvider.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs index 31170d0..6df8705 100644 --- a/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs +++ b/NorthwindCRUD/Providers/DbContextConfigurationProvider.cs @@ -1,4 +1,5 @@ -using Microsoft.Data.Sqlite; +using System.Globalization; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using NorthwindCRUD.Helpers; @@ -34,7 +35,7 @@ public void ConfigureOptions(DbContextOptionsBuilder options) { var tenantId = GetTenantId(); - var cacheKey = string.Format(DatabaseConnectionCacheKey, tenantId); + var cacheKey = string.Format(CultureInfo.InvariantCulture, DatabaseConnectionCacheKey, tenantId); if (!memoryCache.TryGetValue(cacheKey, out SqliteConnection connection)) { @@ -81,7 +82,7 @@ private MemoryCacheEntryOptions GetCacheConnectionEntryOptions() private string GetSqlLiteConnectionString(string tenantId) { var connectionStringTemplate = configuration.GetConnectionString("SQLiteConnectionString"); - var unsanitizedConntectionString = string.Format(connectionStringTemplate, tenantId); + var unsanitizedConntectionString = string.Format(CultureInfo.InvariantCulture, connectionStringTemplate, tenantId); var connectionStringBuilder = new SqliteConnectionStringBuilder(unsanitizedConntectionString); var sanitizedConntectionString = connectionStringBuilder.ToString(); From 4fa824a0749e98b61fe05daeeb38554991508101 Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Thu, 10 Oct 2024 19:39:20 +0300 Subject: [PATCH 06/11] Add tennant validation middleware --- .../TenantHeaderValidationMiddleware.cs | 35 +++++++++++++++++++ NorthwindCRUD/Program.cs | 3 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs diff --git a/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs b/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs new file mode 100644 index 0000000..3ccb708 --- /dev/null +++ b/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs @@ -0,0 +1,35 @@ +using System.Text.RegularExpressions; + +namespace NorthwindCRUD.Middlewares +{ + public class TenantHeaderValidationMiddleware + { + private const string TenantHeaderKey = "X-Tenant-ID"; + + private readonly RequestDelegate _next; + + public TenantHeaderValidationMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + var tenantHeader = context.Request.Headers[TenantHeaderKey].FirstOrDefault(); + + if (tenantHeader != null && !IsTenantValid(tenantHeader)) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsync($"Invalid format for Header {TenantHeaderKey}"); + return; + } + + await _next(context); + } + + private bool IsTenantValid(string tenantId) + { + return Regex.IsMatch(tenantId, "^[A-Za-z0-9-_]{0,40}$"); + } + } +} diff --git a/NorthwindCRUD/Program.cs b/NorthwindCRUD/Program.cs index b2dbe25..1b40840 100644 --- a/NorthwindCRUD/Program.cs +++ b/NorthwindCRUD/Program.cs @@ -11,6 +11,7 @@ using Newtonsoft.Json.Converters; using NorthwindCRUD.Filters; using NorthwindCRUD.Helpers; +using NorthwindCRUD.Middlewares; using NorthwindCRUD.Providers; using NorthwindCRUD.Services; @@ -132,7 +133,7 @@ public static void Main(string[] args) // Necessary to detect if it's behind a load balancer, for example changing protocol, port or hostname app.UseForwardedHeaders(); - + app.UseMiddleware(); app.UseHttpsRedirection(); app.UseDefaultFiles(); app.UseStaticFiles(); From cdab500ca1c8201d7c2a551882c1130511b37f1e Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Thu, 10 Oct 2024 19:45:32 +0300 Subject: [PATCH 07/11] fix compiler warnings --- .../Middlewares/TenantHeaderValidationMiddleware.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs b/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs index 3ccb708..b8389a1 100644 --- a/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs +++ b/NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs @@ -6,11 +6,11 @@ public class TenantHeaderValidationMiddleware { private const string TenantHeaderKey = "X-Tenant-ID"; - private readonly RequestDelegate _next; + private readonly RequestDelegate next; public TenantHeaderValidationMiddleware(RequestDelegate next) { - _next = next; + this.next = next; } public async Task InvokeAsync(HttpContext context) @@ -24,7 +24,7 @@ public async Task InvokeAsync(HttpContext context) return; } - await _next(context); + await next(context); } private bool IsTenantValid(string tenantId) From 727bd280b4a2ae7ba22b04d57253277849672f34 Mon Sep 17 00:00:00 2001 From: Deyvid Nenchev Date: Tue, 15 Oct 2024 15:12:25 +0300 Subject: [PATCH 08/11] Add AWS memory monitoring --- .ebextensions/cloudwatch.config | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .ebextensions/cloudwatch.config diff --git a/.ebextensions/cloudwatch.config b/.ebextensions/cloudwatch.config new file mode 100644 index 0000000..57d102b --- /dev/null +++ b/.ebextensions/cloudwatch.config @@ -0,0 +1,58 @@ +# https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-cw.html + +files: + "/opt/aws/amazon-cloudwatch-agent/bin/config.json": + mode: "000600" + owner: root + group: root + content: | + { + "agent":{ + "metrics_collection_interval":60, + "logfile":"/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log", + "run_as_user":"cwagent" + }, + "metrics":{ + "namespace":"CWAgent/AppBuilderData", + "append_dimensions":{ + "InstanceId":"${aws:InstanceId}", + "InstanceType":"${aws:InstanceType}", + "AutoScalingGroupName":"${aws:AutoScalingGroupName}" + }, + "aggregation_dimensions":[ + [ + "AutoScalingGroupName", "InstanceId" + ], + [ + + ] + ], + "metrics_collected":{ + "cpu":{ + "resources":[ + "*" + ], + "measurement":[ + "time_idle", + "time_iowait", + "time_system", + "time_user", + "usage_steal", + "usage_system", + "usage_user", + "usage_iowait" + ] + }, + "mem":{ + "measurement":[ + "used_percent", + "total", + "available_percent" + ] + } + } + } + } +container_commands: + start_cloudwatch_agent: + command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a append-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json \ No newline at end of file From a1c4283a321ebdfb606c71442bcb3f48470cbab0 Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 15 Oct 2024 13:08:20 -0300 Subject: [PATCH 09/11] Move .ebextensions to the app folder --- {.ebextensions => NorthwindCRUD/.ebextensions}/cloudwatch.config | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.ebextensions => NorthwindCRUD/.ebextensions}/cloudwatch.config (100%) diff --git a/.ebextensions/cloudwatch.config b/NorthwindCRUD/.ebextensions/cloudwatch.config similarity index 100% rename from .ebextensions/cloudwatch.config rename to NorthwindCRUD/.ebextensions/cloudwatch.config From 05ecc7b2593a7af98abe5aaa0eeba0ce90970021 Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 15 Oct 2024 13:24:24 -0300 Subject: [PATCH 10/11] Copy .ebextensions to publish --- NorthwindCRUD/NorthwindCRUD.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NorthwindCRUD/NorthwindCRUD.csproj b/NorthwindCRUD/NorthwindCRUD.csproj index faf2e0a..d8276db 100644 --- a/NorthwindCRUD/NorthwindCRUD.csproj +++ b/NorthwindCRUD/NorthwindCRUD.csproj @@ -47,4 +47,9 @@ + + + Always + + From 7870e416901e5d8a044914001c3a2ae9d5fd79b9 Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 15 Oct 2024 13:39:52 -0300 Subject: [PATCH 11/11] Format cloudwatch.config don't mix tabs and spaces --- NorthwindCRUD/.ebextensions/cloudwatch.config | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/NorthwindCRUD/.ebextensions/cloudwatch.config b/NorthwindCRUD/.ebextensions/cloudwatch.config index 57d102b..cc8f412 100644 --- a/NorthwindCRUD/.ebextensions/cloudwatch.config +++ b/NorthwindCRUD/.ebextensions/cloudwatch.config @@ -1,7 +1,7 @@ # https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-cw.html -files: - "/opt/aws/amazon-cloudwatch-agent/bin/config.json": +files: + "/opt/aws/amazon-cloudwatch-agent/bin/config.json": mode: "000600" owner: root group: root @@ -13,46 +13,43 @@ files: "run_as_user":"cwagent" }, "metrics":{ - "namespace":"CWAgent/AppBuilderData", - "append_dimensions":{ - "InstanceId":"${aws:InstanceId}", - "InstanceType":"${aws:InstanceType}", - "AutoScalingGroupName":"${aws:AutoScalingGroupName}" - }, - "aggregation_dimensions":[ - [ - "AutoScalingGroupName", "InstanceId" + "namespace":"CWAgent/AppBuilderData", + "append_dimensions":{ + "InstanceId":"${aws:InstanceId}", + "InstanceType":"${aws:InstanceType}", + "AutoScalingGroupName":"${aws:AutoScalingGroupName}" + }, + "aggregation_dimensions":[ + [ "AutoScalingGroupName", "InstanceId" ], + [ ] + ], + "metrics_collected":{ + "cpu":{ + "resources":[ + "*" ], - [ - + "measurement":[ + "time_idle", + "time_iowait", + "time_system", + "time_user", + "usage_steal", + "usage_system", + "usage_user", + "usage_iowait" ] - ], - "metrics_collected":{ - "cpu":{ - "resources":[ - "*" - ], - "measurement":[ - "time_idle", - "time_iowait", - "time_system", - "time_user", - "usage_steal", - "usage_system", - "usage_user", - "usage_iowait" - ] - }, - "mem":{ - "measurement":[ - "used_percent", - "total", - "available_percent" - ] - } - } + }, + "mem":{ + "measurement":[ + "used_percent", + "total", + "available_percent" + ] + } + } } - } + } + container_commands: - start_cloudwatch_agent: - command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a append-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json \ No newline at end of file + start_cloudwatch_agent: + command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a append-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json