/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Amazon.Extensions.Configuration.SystemsManager.AppConfig; using Amazon.Extensions.Configuration.SystemsManager.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; namespace Amazon.Extensions.Configuration.SystemsManager { /// /// /// An AWS Systems Manager based . /// public class SystemsManagerConfigurationProvider : ConfigurationProvider { private ISystemsManagerConfigurationSource Source { get; } private ISystemsManagerProcessor SystemsManagerProcessor { get; } private ManualResetEvent ReloadTaskEvent { get; } = new ManualResetEvent(true); /// /// /// Initializes a new instance with the specified source. /// /// The used to retrieve values from AWS Systems Manager Parameter Store public SystemsManagerConfigurationProvider(SystemsManagerConfigurationSource source) : this(source, new SystemsManagerProcessor(source)) { } /// /// /// Initializes a new instance with the specified source. /// /// The used to retrieve values from AWS Systems Manager AppConfig public SystemsManagerConfigurationProvider(AppConfigConfigurationSource source) : this(source, new AppConfigProcessor(source)) { } /// /// /// Initializes a new instance with the specified source. /// /// The used to retrieve values from AWS Systems Manager /// The used to retrieve values from AWS Systems Manager public SystemsManagerConfigurationProvider(ISystemsManagerConfigurationSource source, ISystemsManagerProcessor systemsManagerProcessor) { Source = source ?? throw new ArgumentNullException(nameof(source)); SystemsManagerProcessor = systemsManagerProcessor ?? throw new ArgumentNullException(nameof(systemsManagerProcessor)); if (source.ReloadAfter != null) { ChangeToken.OnChange(() => { var cancellationTokenSource = new CancellationTokenSource(source.ReloadAfter.Value); var cancellationChangeToken = new CancellationChangeToken(cancellationTokenSource.Token); return cancellationChangeToken; }, async () => { ReloadTaskEvent.Reset(); try { await LoadAsync(true).ConfigureAwait(false); } finally { ReloadTaskEvent.Set(); } }); } } /// /// If this configuration provider is currently performing a reload of the config data this method will block until /// the reload is called. /// /// This method is not meant for general use. It is exposed so a AWS Lambda function can wait for the reload to complete /// before completing the event causing the AWS Lambda compute environment to be frozen. /// public void WaitForReloadToComplete(TimeSpan timeout) { ReloadTaskEvent.WaitOne(timeout); } /// /// /// Loads the AWS Systems Manager Parameters. /// public override void Load() => LoadAsync(false).ConfigureAwait(false).GetAwaiter().GetResult(); // If 1) reload flag is set to true and 2) OnLoadException handler is not set, // all exceptions raised during OnReload() will be ignored. private async Task LoadAsync(bool reload) { try { var newData = await SystemsManagerProcessor.GetDataAsync().ConfigureAwait(false) ?? new Dictionary(); if (!Data.EquivalentTo(newData)) { Data = newData; OnReload(); } } catch (Exception ex) { if (Source.Optional) return; var ignoreException = reload; if (Source.OnLoadException != null) { var exceptionContext = new SystemsManagerExceptionContext { Provider = this, Exception = ex, Reload = reload }; Source.OnLoadException(exceptionContext); ignoreException = exceptionContext.Ignore; } if (!ignoreException) throw; } } } }