Feature toggle z użyciem usługi Azure App Configuration
Pracuję właśnie nad swoją nową aplikacją. I nadszedł moment kiedy postanowiłem, że czas zacząć testować ją w środowisku serwerowym i przy okazji skonfigurować cały proces. Dzięki temu, jak już będę chciał „iść na produkcję” to większość podstawowych spraw będzie gotowa. W tym celu, skoro aplikacja powstaje w ASP.NET Core, postanowiłem wykorzystać usługi Azure.
Tylko, że skoro udostępniam na świat niedokończoną aplikację, która niekoniecznie jest w stanie zabezpieczyć się przed chociażby tworzeniem dziesiątek tysięcy kont, to chciałem jakoś zapewnić sobie względny spokój. W tym celu uznałem, że najprostsza będzie możliwość wyłączenia możliwości zakładania nowych kont.
Jest i nagle znika
Plan był taki, żebym mógł w prosty sposób wyłączyć konkretny fragment widoku i zablokować akcje w kontrolerze, które odpowiadały za obsługę rejestracji (Pamiętaj: to, że coś ukryjesz na frontendzie w żaden sposób nie zabezpiecza Cię przed tym, że ktoś będzie próbował wykonać konkretne akcje!)
Ogólnie taki sposób włączania lub wyłączania jakiegoś elementu aplikacji, za pomocą ustawionej wartości logicznej, znany jest jaki feature toggle – przełącznik funkcjonalności. Można taką wartość trzymać chociażby w plikach konfiguracyjnych. Ważne jest to, żebym mógł ten przełącznik przełączyć bez zmian w kodzie projektu – maksimum na co można się zgodzić to zmiana wartości w pliku i zrestartowanie aplikacji.
Rozwiązanie wygodne
W pracy do takich zabaw korzystaliśmy z LaunchDarkly, a potem, kiedy mocno zmienili cennik, przeszliśmy na ConfigCat. Jednak oba rozwiązania mają jedną wspólną cechę – dodatkowy koszt.
Dlatego postanowiłem, że skoro korzystam z platformy Azure to poszukam rozwiązania, które być może na niej jest dostępne. I okazało się, że znalazłem je bardzo szybko. Tym rozwiązaniem jest Azure App Configuration. Nazwa właściwie mówi wszystko – ta usługa pozwala przechowywać konfigurację, którą potem możemy użyć w aplikacji.
Warto jednak wspomnieć, że ta konfiguracja dzieli się na dwa rodzaje – wszelkiego typu wartości, np. liczba powtórzeń jakieś operacji, adres email supportu itd. oraz właśnie „flagi funkcji”. I to właśnie ten drugi rodzaj mnie dzisiaj interesuje.
Skonfigurowałem sobie wszystko, dodałem dwie flagi – do włączania logowania i rejestracji – i mogłem wrzucać aplikację na serwer. I faktycznie, za pomocą jednego przełącznika mam kontrolę nad tym czy możliwość rejestrowania się w moim systemie jest dostępna, czy nie. Co prawda nie natychmiast (o tym później) ale działa dokładnie tak jak oczekiwałem. Dlatego tutaj przedstawię Wam pokrótce jak można skorzystać z Azure App Configuration właśnie w celu włączania funkcji w Waszym projekcie.
Azure App Configuration w praktyce
Przejdźmy w kilku krokach przez konfigurację. Zakładam, że posiadasz konto w chmurze Azure. Sama usługa Azure App Configuration jest darmowa dla limitu 2000 zapytań dziennie – do celów developersko-testowych całkowicie wystarczy.
Konfiguracja po stronie Azure
Żeby dodać konfigurację z feature togglami w Azure zaloguj się do panelu i znajdź po prostu „App Configuration”. Potem standardowo wystarczy wybrać do jakiej subskrypcji ma być podpięte czy jaką ma mieć nazwę i zaczekać aż uruchomią serwis.
Teraz przejdź do usługi i znajdź w menu, w sekcji „Operacje” pozycję „Menedżer funkcji”:
Teraz można już dodawać swoje flagi. W oknie dodawania podajesz nazwę i opcjonalny opis. Można też od razu zaznaczyć, żeby flaga była włączona.
W swoim projekcie mam dodane dwie flagi – tak jak wspominałem, do włączania logowania i rejestracji:
Ostatnia sprawa, która Cię tutaj interesuje to connection string, który znajdziesz w menu pod pozycją „Klucze dostępu”:
Skopiuj zaznaczoną pozycję i dodają ją do connection stringów w swoim projekcie. Zaraz przejdziemy do tego, w którym miejscu będziemy z tego korzystać.
Konfiguracja w aplikacji ASP.NET Core 5 MVC
Przede wszystkim trzeba w projekcie zainstalować dwie biblioteki z NuGeta:
Microsoft.Azure.AppConfiguration.AspNetCore
Microsoft.FeatureManagement.AspNetCore
Dodaj wspomniany wcześniej connection string do sekcji connection stringów, które masz w swojej aplikacji, np. do połączenia z bazą danych – czy będzie to appsettings, czy user-secrets, albo zmienne środowiskowe, to bez znaczenia.
Przejdźmy teraz do pliku Program.cs i w funkcji CreateHostBuilder()
dodaj połączenie z Azure App Configuration. Ja mam umieszczony connection string w sekcji connectionStrings, pod nazwą AppConfig. Jeżeli u Ciebie jest inaczej to zmień linijkę, gdzie pobierane jest połączenie:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
// TUTAJ ZACZYNA SIĘ KONFIGURACJA APP CONFIGURATION
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.ConfigureAppConfiguration(config =>
{
var settings = config.Build();
var connection = settings.GetConnectionString("AppConfig");
if (!string.IsNullOrEmpty(connection))
{
config.AddAzureAppConfiguration(options =>
options.Connect(connection).UseFeatureFlags(options =>
{
options.CacheExpirationInterval = TimeSpan.FromMinutes(5);
}));
}
}).UseStartup<Startup>());
Za pomocą linijki
options.CacheExpirationInterval = TimeSpan.FromMinutes(5);
ustawiam, że cache dla pobieranych wartości odświeża się co 5 minut. Dzięki temu aplikacja nie będzie pytać usługi o aktualną wartość za każdym razem jak odświeżysz stronę. Pozwala to oszczędzać dostępną pulę zapytań. Jedynym mankamentem jest to, że nowe ustawienia pojawią się dopiero po 5 minutach. Domyślnie jest to ustawione na 30 sekund. Możesz ustawić wartość, która najlepiej pasuje do Twojego projektu.
Kiedy to zrobisz to przejdź do pliku Startup.cs i w metodzie ConfigureServices()
dodaj te dwie linijki:
services.AddAzureAppConfiguration();
services.AddFeatureManagement();
Ostatnia sprawa związana z konfiguracją to dodanie w metodzie Configure(), również w pliku Startup.cs, takiej linijki:
if (!env.IsDevelopment())
{
app.UseAzureAppConfiguration();
}
Dlaczego dodałem tutaj sprawdzanie czy nie działam w środowisku deweloperskim? Bo lokalnie mogę bez problemu korzystać z plików konfiguracyjnych, a ponowne uruchomienie aplikacji jest w miarę szybkie. Więc uznałem, że nie będę marnował dostępnej puli zapytań, żeby testować aplikację na swoim komputerze.
Ostatni element, który dodam, zanim przejdę do korzystania z flag, to enum z flagami, które obsługuję. Dzięki temu nie będę musiał wpisywać stringów kiedy będę używał wybranej flagi, ale to zobaczysz później. W moim projekcie znalazł się więc plik z taką zawartością:
public enum FeatureFlags
{
LoginEnabled,
RegistrationEnabled
}
Jak widzisz, wartości odpowiadają nazwom flag, które miałem wcześniej dodane w usłudze.
Korzystanie z flag
Ponieważ w tym tekście mówię o flagach w kontekście wyłączania części funkcjonalności dostępnych bezpośrednio dla użytkownika, to tutaj skupię się na dwóch rzeczach – warunkowym włączaniu akcji kontrolera i warunkowym wyświetleniu treści na stronie.
Pierwsza sprawa to włączanie akcji kontrolera. Robi się to przez dodanie jednego atrybutu do akcji:
[HttpGet]
[FeatureGate(FeatureFlags.LoginEnabled)]
public IActionResult Login()
{
return View();
}
Sam atrybut znajduje się w namespace Microsoft.FeatureManagement.Mvc
. Jak widzisz używam tutaj enuma, którego wcześniej dodawałem.
W ten sposób, jeżeli flaga będzie wyłączona, to ta akcja nie będzie działa. Dzięki temu użytkownik nie będzie mógł wykonać kodu, który np. nie jest gotowy.
Drugi element, który Ci pokażę, będzie korzystał ze specjalnie przygotowanego na tę okazję znacznika. Dlatego najpierw dodaj poniższą linijkę w pliku _ViewImports.cshtml:
@addTagHelper *, Microsoft.FeatureManagement.AspNetCore
Od teraz, jeżeli chcesz, żeby dodana flaga kontrolowała też to, czy jakiś fragment strony się wyświetla, możesz otoczyć go znacznikiem <feature>
:
<feature name="RegistrationEnabled">
<a href="/account/register" class="user"><i class="fa fa-user"></i></a>
<a href="/account/register" class="site-btn">Zarejestruj się</a>
</feature>
W tym miejscu podałem nazwę flagi jako string. Jednak możesz też skorzystać z enuma, jeżeli uznasz to za lepsze rozwiązanie:
<feature name="@FeatureFlags.RegistrationEnabled.ToString()">
I w taki właśnie sposób, kiedy będziesz ustawiać wartość true lub false dla wybranej flagi, elementy Twojej aplikacji będą włączone lub nie. Dzięki temu możesz wyłączać we wdrożonej aplikacji fragmenty, nad którymi nadal pracujesz. Dzięki temu możesz chociażby próbować wprowadzać continuous deployment w swoim procesie.
Leave a Comment