Skip to content

Instantly share code, notes, and snippets.

@gcelet
Created April 6, 2018 20:25
Show Gist options
  • Save gcelet/ab12595ae5edfcddc3a092cc0f2cd006 to your computer and use it in GitHub Desktop.
Save gcelet/ab12595ae5edfcddc3a092cc0f2cd006 to your computer and use it in GitHub Desktop.
Evolve and Secrets Manager with Postgresql
{
"Evolve": {
"Command": "Migrate",
"Locations": [
"./sql/migrations/generic",
"./sql/migrations/postgresql",
"./sql/datasets/mandatory",
"./sql/datasets/samples"
],
"EraseDisabled": false,
"EraseOnValidationError": true,
"OutOfOrder": true
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
},
"Debug": {
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"LogLevel": {
"Default": "Debug"
}
}
}
}
{
"Evolve":{
"Command": "DoNothing",
"ConnectionString": "",
"Driver": "Npgsql",
"Locations": [
"sql/migrations/generic",
"sql/migrations/postgresql",
"sql/datasets/mandatory"
],
"EraseDisabled": true,
"CommandTimeout": 120,
"Schemas": "",
"StartVersion": 0,
"TargetVersion": null,
"OutOfOrder": false,
"MetadataTableSchema": "",
"MetadataTableName": "db_version",
"PlaceholderPrefix": "${",
"PlaceholderSuffix": "}",
"SqlMigrationPrefix": "v",
"EnableClusterMode": false
},
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
namespace Sample
{
using System.Collections.Generic;
using System.Linq;
using System.Text;
using global::Evolve.Configuration;
using global::Evolve.Migration;
/// <summary>
/// <para>
/// Evolve configuration for sql migrations.
/// </para>
/// <para>
/// Sql migrations have the following file name structure: prefixVERSIONseparatorDESCRIPTIONsuffix
/// Example: V1_3_1__Migration_description.sql
/// </para>
/// <para>
/// Placeholders are strings to replace in sql migrations.
/// Example: ${schema}
/// </para>
/// </summary>
public class EvolveConfiguration : IEvolveConfiguration
{
#region --- Constants ---
/// <summary>
/// Gets default for cluster mode
/// </summary>
public const bool DefaultClusterMode = true;
/// <summary>
/// The default base command for Evolve
/// </summary>
public const CommandOptions DefaultCommand = CommandOptions.DoNothing;
/// <summary>
/// Gets default path to scan recursively for migrations.
/// </summary>
public const string DefaultLocation = "Sql_Scripts";
/// <summary>
/// Gets the default metadata table name.
/// </summary>
public const string DefaultMetadataTableName = "changelog";
/// <summary>
/// Gets the default OutOfOrder settings
/// </summary>
public const bool DefaultOutOfOrder = false;
/// <summary>
/// Gets the default prefix of the placeholders.
/// </summary>
public const string DefaultPlaceholderPrefix = "${";
/// <summary>
/// Gets the default suffix of the placeholders.
/// </summary>
public const string DefaultPlaceholderSuffix = "}";
/// <summary>
/// Gets the default file name prefix for sql migrations.
/// </summary>
public const string DefaultSqlMigrationPrefix = "V";
/// <summary>
/// Gets the default file name separator for sql migrations.
/// </summary>
public const string DefaultSqlMigrationSeparator = "__";
/// <summary>
/// Gets the default file name suffix for sql migrations.
/// </summary>
public const string DefaultSqlMigrationSuffix = ".sql";
/// <summary>
/// Gets the default version used as a starting point for migrations.
/// </summary>
public const int DefaultStartVersion = 0;
/// <summary>
/// Gets the default target version to reach.
/// </summary>
public const long DefaultTargetVersion = long.MaxValue;
#endregion
#region --- Fields ---
private string _metadataTableSchema = null;
#endregion
#region --- Properties ---
/// <summary>
/// <para>
/// The base command for Evolve. (default: doNothing)
/// </para>
///
/// <para>
/// <see cref="CommandOptions.DoNothing"/> : Does nothing.
/// </para>
/// <para>
/// <see cref="CommandOptions.Migrate"/> : Migrates the database.
/// </para>
/// <para>
/// <see cref="CommandOptions.Erase"/> : Erases the database schemas listed in <see cref="Schemas"/>.
/// Only works if Evolve has created the schema at first or found it empty.
/// Otherwise Evolve won't do anything.
/// </para>
/// <para>
/// <see cref="CommandOptions.Repair"/> : Corrects checksums of the applied migrations in the metadata table,
/// with the ones from migration scripts.
/// </para>
/// </summary>
public CommandOptions Command { get; set; } = DefaultCommand;
/// <summary>
/// Gets or sets the wait time before terminating the attempt to execute
/// a migration and generating an error. (The default is 30 seconds.)
/// </summary>
public int? CommandTimeout { get; set; }
/// <summary>
/// <para>
/// Gets or sets the connection string to the database.
/// </para>
/// <para>
/// Must have the necessary privileges to execute ddl.
/// </para>
/// </summary>
public string ConnectionString { get; set; }
/// <summary>
/// Gets or sets the full name of the driver used to interact with the database.)
/// </summary>
public string Driver { get; set; }
/// <summary>
/// When true, Evolve will use a session level lock to coordinate the migrations on multiple nodes. (default: true)
/// </summary>
public bool EnableClusterMode { get; set; } = DefaultClusterMode;
/// <summary>
/// Gets or sets the encoding of Sql migrations. (default: UTF-8)
/// </summary>
public string Encoding { get; set; }
/// <summary>
/// <para>
/// When true, ensures that Evolve will never erase schemas. (default: false;)
/// </para>
/// <para>
/// Highly recommended in production !
/// </para>
/// </summary>
public bool IsEraseDisabled { get; set; }
/// <summary>
/// Gets or sets the paths (separated by semicolon) to scan recursively for migrations. (default: Sql_Scripts)
/// </summary>
public List<string> Locations { get; set; } = new List<string>();
/// <summary>
/// Gets or sets the metadata table name. (default: changelog)
/// </summary>
public string MetadataTableName { get; set; } = DefaultMetadataTableName;
/// <summary>
/// Gets or sets the schema containing the metadata table.
/// (default: If empty, the first schema defined in <see cref="Schemas"/> or the one of the datasource connection.)
/// </summary>
public string MetadataTableSchema
{
get => _metadataTableSchema.IsNullOrWhiteSpace() ? Schemas?.FirstOrDefault() : _metadataTableSchema;
set => _metadataTableSchema = value;
}
/// <summary>
/// <para>
/// When true, if incoherent migration checksums are found during validation phase,
/// Evolve will erase the database schemas and will re-execute migration scripts from scratch. (default: false)
/// </para>
/// <para>
/// Do not use in production !
/// </para>
/// <para>
/// Obviously useful during development.
/// </para>
/// </summary>
public bool MustEraseOnValidationError { get; set; }
/// <summary>
/// When true, allows migrations to be run "out of order".
/// If you already have versions 1 and 3 applied, and now a version 2 is found,
/// it will be applied too instead of being ignored. (default: false;)
/// </summary>
public bool OutOfOrder { get; set; } = DefaultOutOfOrder;
/// <summary>
/// Gets or sets the prefix of the placeholders. (default: ${)
/// </summary>
public string PlaceholderPrefix { get; set; } = DefaultPlaceholderPrefix;
/// <summary>
/// <para>
/// Gets or sets the list of supplied placeholders/values defined in the configuration file.
/// </para>
/// <para>
/// Placeholders are strings prefixed by: "Evolve.Placeholder." to replace in sql migrations.
/// </para>
/// <para>
/// Example: ${schema} will be replaced by the value defined by the property
/// Evolve.Placeholder.schema in the configuration file.
/// </para>
/// </summary>
public Dictionary<string, string> Placeholders { get; set; }
/// <summary>
/// Gets or sets the suffix of the placeholders. (default: })
/// </summary>
public string PlaceholderSuffix { get; set; } = DefaultPlaceholderSuffix;
/// <summary>
/// Gets or sets the semicolon separated list of schema managed by Evolve.
/// (default: If empty, the default schema for the datasource connection.)
/// </summary>
public List<string> Schemas { get; set; } = new List<string>();
/// <summary>
/// Gets or sets the file name prefix for sql migrations. (default: V)
/// </summary>
public string SqlMigrationPrefix { get; set; } = DefaultSqlMigrationPrefix;
/// <summary>
/// Gets or sets the file name separator for sql migrations. (default: __)
/// </summary>
public string SqlMigrationSeparator { get; set; } = DefaultSqlMigrationSeparator;
/// <summary>
/// Gets or sets the file name suffix for sql migrations. (default: .sql)
/// </summary>
public string SqlMigrationSuffix { get; set; } = DefaultSqlMigrationSuffix;
/// <summary>
/// Gets or sets the version used as a starting point for migrations. If null or empty it returns 0.
/// </summary>
public string StartVersion { get; set; }
/// <summary>
/// Gets or sets the target version to reach. If null or empty, it evolves all the way up.
/// </summary>
public string TargetVersion { get; set; }
/// <summary>
/// Gets or sets the encoding of Sql migrations. (default: UTF-8)
/// </summary>
Encoding IEvolveConfiguration.Encoding
{
get => !string.IsNullOrEmpty(Encoding)
? System.Text.Encoding.GetEncoding(Encoding)
: System.Text.Encoding.UTF8;
set => Encoding = value == null ? System.Text.Encoding.UTF8.EncodingName : value.EncodingName;
}
/// <summary>
/// Gets or sets the paths (separated by semicolon) to scan recursively for migrations. (default: Sql_Scripts)
/// </summary>
IEnumerable<string> IEvolveConfiguration.Locations
{
get => Locations;
set => Locations = value != null ? new List<string>(value) : null;
}
/// <summary>
/// Gets or sets the semicolon separated list of schema managed by Evolve.
/// (default: If empty, the default schema for the datasource connection.)
/// </summary>
IEnumerable<string> IEvolveConfiguration.Schemas
{
get => Schemas;
set => Schemas = value != null ? new List<string>(value) : null;
}
/// <summary>
/// Gets or sets the version used as a starting point for migrations. If null or empty it returns 0.
/// </summary>
MigrationVersion IEvolveConfiguration.StartVersion
{
get => !string.IsNullOrEmpty(StartVersion) ? new MigrationVersion(StartVersion) : null;
set => StartVersion = value != null ? value.Label : null;
}
MigrationVersion IEvolveConfiguration.TargetVersion
{
get => !string.IsNullOrEmpty(TargetVersion) ? new MigrationVersion(TargetVersion) : null;
set => TargetVersion = value != null ? value.Label : null;
}
#endregion
}
}
namespace Sample
{
using Evolve;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Npgsql;
public class Program
{
public static void Main(string[] args)
{
IWebHost webHost = BuildWebHost(args);
IEvolveConfiguration evolveConfiguration = webHost.Services.GetService<IEvolveConfiguration>();
ILoggerFactory loggerFactory = webHost.Services.GetService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger<Evolve>();
if (!TryCommand(evolveConfiguration, logger))
{
Environment.Exit(1);
return;
}
webHost.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
public static bool TryCommand(IEvolveConfiguration evolveConfiguration, ILogger logger)
{
try
{
var placeholders = (evolveConfiguration.Placeholders ?? new Dictionary<string, string>())
.ToDictionary(
kvp => $"{evolveConfiguration.PlaceholderPrefix ?? string.Empty}{kvp.Key}{evolveConfiguration.PlaceholderSuffix}",
kvp => kvp.Value
);
NpgsqlConnectionStringBuilder connectionStringBuilder = new NpgsqlConnectionStringBuilder(evolveConfiguration.ConnectionString);
using (var dbConnection = new NpgsqlConnection(connectionStringBuilder.ConnectionString))
{
Evolve evolve = new Evolve(dbConnection, msg => logger?.LogInformation(msg))
{
Command = evolveConfiguration.Command,
CommandTimeout = evolveConfiguration.CommandTimeout,
ConnectionString = evolveConfiguration.ConnectionString,
Driver = evolveConfiguration.Driver,
EnableClusterMode = evolveConfiguration.EnableClusterMode,
Encoding = evolveConfiguration.Encoding,
IsEraseDisabled = evolveConfiguration.IsEraseDisabled,
Locations = evolveConfiguration.Locations,
MetadataTableName = evolveConfiguration.MetadataTableName,
MetadataTableSchema = evolveConfiguration.MetadataTableSchema,
MustEraseOnValidationError = evolveConfiguration.MustEraseOnValidationError,
OutOfOrder = evolveConfiguration.OutOfOrder,
PlaceholderPrefix = evolveConfiguration.PlaceholderPrefix,
Placeholders = placeholders,
PlaceholderSuffix = evolveConfiguration.PlaceholderSuffix,
Schemas = evolveConfiguration.Schemas,
SqlMigrationPrefix = evolveConfiguration.SqlMigrationPrefix,
SqlMigrationSeparator = evolveConfiguration.SqlMigrationSeparator,
SqlMigrationSuffix = evolveConfiguration.SqlMigrationSuffix,
StartVersion = evolveConfiguration.StartVersion,
TargetVersion = evolveConfiguration.TargetVersion,
};
evolve.ExecuteCommand();
}
return true;
}
catch (Exception exception)
{
logger?.LogCritical(exception, "[EVOLVE MIGRATION] FATAL");
return false;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Evolve;
namespace Sample
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
EvolveConfiguration evolveConfigurationSettings = new EvolveConfiguration();
configuration.Bind("Evolve", evolveConfigurationSettings);
services.AddSingleton<IEvolveConfiguration>(evolveConfigurationSettings);
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment