Team City w mackach ośmiornicy cz. I

Ostatnio jednym z zadań, którego postanowiłem się podjąć, było wprowadzenie do projektu prostego rodzaju Continuous Integration oraz Delivery.

Do wymagań należało wprowadzenie kilku środowisk, każde miało posiadać inne ustawienia, a co najważniejsze możliwość deploy’u on demand, czyli na kliknięcie przycisku, bez żadnego ręcznego kopiowania. Po 2 dniach ciężkiej pracy i czarowania z pomocą PowerShell’a udało się, wszystko zaczęło działać jak powinno.

Powiodło się przy pomocy TeamCity oraz bezpośredniego publikowania projektu do folderu, który był hostowany przez IIS. No i tu pojawia się jedno ale. Moim zdaniem nie było to optymalne rozwiązanie. Na pewno można to było rozwiązać lepiej, bardziej przystępnie, bez choćby jednej linijki napisanej w Powershell’u.

Zainteresowałem się tym tematem i tak trafiłem na Octopus’a, narzędzie służące do zarządzania nawet najbardziej skomplikowanymi deploy’ami.

Poniżej przedstawię  schemat działania dla aplikacji .NET’owej, jednak Octopus nie jest ograniczony tylko do Microsoft’owej technologii.

Na stronie Octopus’a można trafić na bardzo trafny obrazek, obrazujący cały pipeline:

pipeline

Na potrzeby tego wpisu ograniczę się tylko do pokazania sposobu zautomatyzowania deploy’u na jedno środowisko.

Wymagania początkowe

Niezbędne będzie nam zainstalowane TeamCity oraz IIS.

Do rozpoczęcia pracy z .NET Core’m oraz Octopusem w TeamCity będą potrzebne dwa pluginy: .NET Core, Octopus. Na koniec instrukcja jak zainstalować pluginy.

Podstawowa konfiguracja TeamCity

Zaczynamy od utworzenia projektu TeamCity. Będziemy w nim używać repozytorium GIT znajdującego się na Bitbucket’ie. Jest to oczywiście tylko jedna z wielu możliwości.

repo

Nazywamy projekt oraz nadajemy nazwę naszej pierwszej konfiguracji.

poject

Zabieramy się do stworzenia podstawowych build step’ów.

 Podstawowe Build Steps

Zaczniemy od restore’u wszystkich paczek z Nuget’a w naszym projekcie:

restore

Następnym krokiem będzie budowanie naszej aplikacji, w zaawansowanych ustawieniach można ustawić konfigurację build’u.

build

Przechodzimy do publish’u naszej aplikacji do wybranego folderu

publish

Konfiguracją naszej ośmiorniczki zajmiemy się w kolejnym poście.

Do przeczytania już niedługo 🙂

Advertisements

.NET Core 2.0

W minionym tygodniu miałem przyjemność migracji z .NET Core’a 1.1 do mającego kilka dni wcześniej finalny release .NET Core’a 2.0. Po krótkim obeznaniu się z tematem miałem mieszane uczucia, połowa wpisów opisywała to zadanie jako mission impossible, druga jako przysłowiową bułkę z masłem. Na szczęście, w naszym przypadku obyło się bez większej ilości problemów. Oprócz instalacji nowej wersji SDK, aktualizacji Visual Studio oraz przejścia przez podstawowy poradnik Microsoftu, kosmetycznej modyfikacji wymagał jedynie mechanizm autentykacji JWT.

Wszystko zakończyło się pomyślnie. Projekt buduje się, odpala i działa jak przed migracją. Ucieszyłem się, że poszło tak łatwo i wszystko zgodnie z tutorialem zamieszczonym na stronie Microsoftu.

Jeśli zastanawiałeś się nad migracją, nie myśl więcej tylko to zrób!

Co nam to tak naprawdę daje?

Przede wszystkim .NET Core 2.0 jest kompatybilny z równolegle opublikowaną pełną specyfikacją .NET Standard 2.0. Daje nam to możliwość używania o ponad 20 tysięcy paczek nugetow’ych więcej w porównaniu z poprzednią wersją, aktualnie dostępnych jest około 70% wszystkich paczek na nuget.org.

.NET Standard pozwala nam także na reużywanie kodu w każdym innym projekcie kompatybilnym z jego obecnie najwyższą wersją, co oznacza np. stosowanie wcześniej napisanych klas w aplikacji webowej podczas pisania aplikacji mobilnej w Xamarinie. Wszystkie technologie kompatybilne z konkretną wersją .NET Standard’u opierają się na dokładnie tym samym zbiorze API’s.

Zgodnie z ogłoszeniami Microsoft’u, ASP .NET Core 2.0 jest nawet 20% szybszy od swojego poprzednika, co czyni go jednym z najszybszym i najbardziej wydajnych webowych frameworków świata. Używając serwera Node.js, jesteśmy w stanie obsłużyć 1000 requestów na sekundę, co jest uważane jako bardzo wydajne oraz szybkie. Natomiast używając serwera tej samej wielkości z .NET Core’m 2.0 ta wartość wzrasta, aż 20-krotnie, do 20 tysięcy zapytań na sekundę!

W .NET Core 2.0 “wyczyszczone” zostały także pliki .csproj. Zamiast dodawania wielu malutkich paczek, teraz dodajemy paczkę Microsoft.AspNetCore.All, która posiada wszystko czego potrzeba ASP.NET Core oraz Entity Framework Core do prawidłowego działania.

Dzięki ogromnemu wsparciu całego community programistycznego .NET może rozwijać się w tak szybkim tempie. Z niecierpliwością czekam na dalszy rozwój sytuacji, nawet Microsoft podczas prezentacji .NET Core’a 2.0 powiedział, że jest to dopiero pierwsza pełnoprawna wersja.

Zapraszam Was na wirtualną konferencję związaną z technologiami Microsoft’u: http://www.dotnetconf.net

Internetowy SWAGGER

Chciałbym podzielić się dziś z wami wiedzą o Swagger’ze, świetnym narzędziu do prostego, bezbolesnego, a co najważniejsze szybkiego i skutecznego dokumentowania API.

Swagger wykorzystuje pliki JSON do tworzenia dokumentacji. Oczywiście możemy ręcznie rzeźbić JSON’y (przykładowy plik: http://petstore.swagger.io/v2/swagger.json, prawda, że mało zachęcający i przydatny?), ale zaprezentuje zdecydowanie wygodniejszy sposób: automatyczną generację pliku prosto z naszej aplikacji oraz prezentację za pomocą SwaggerUI.

Podstawowa konfiguracja jest bardzo, bardzo prosta.

Tworzymy podstawowy projekt ASP.NET CORE WEB API. Instalujemy paczkę Swashbuckle.AspNetCore przez Nuget’a, Cli lub Package Manager Console.

Następnie przechodzimy do pliku Startup.cs. Dodajemy do naszych serwisów Swagger’a w metodze ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddSwaggerGen(c => 
    {
        c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
    });
    services.AddMvc();
}

Możemy wyspecyfikować nazwę naszej strony z dokumentacją, wersję naszego API, mnóstwo innych rzeczy między innymi kontakt do autorów lub informacje o licencji.
Następnie w metodzie Configure dajemy znać, że będziemy używac Swagger’a oraz SwaggerUI. Ustawiamy endpoint do naszego pliku swagger.json, który będzie się automatycznie generował i zawierał dokumentację, specyfikujemy także nazwę.

public void Configure(IApplicationBuilder app, IHostingEnvironment env,
                      ILoggerFactory loggerFactory)
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    });
    app.UseMvc();
}

Pod adresem /swagger/v1/swagger.json możemy obejrzeć ‘surową’ wersję naszej dokumentacji.
I już 😊
Po odpaleniu naszej aplikacji i przejściu pod adres /swagger (polecam ustawić jako strona startowa, po włączeniu aplikacji) ukazuje nam się:

swagger

Możemy podejrzeć jakie mamy możliwe endpoint’y, są one pogrupowane kontrolerami.  Swagger UI ułatwia nam także manualne przetestowanie funkcjonalności. Pozwala obejrzeć przykładową odpowiedz serwera, po udekorowaniu naszej akcji kontrolera atrybutem:


[ProducesResponseType(typeof(MyCustomType), (int)HttpStatusCode.OK)]

Oczywiście OK możemy zamienić dowolnym innym status kodem.

Widoczne są także HTTP verbs, typy parametrów oraz ich pochodzenie, możliwe zwracane statusy HTTP.

Pełny podgląd przykładowej akcji:

swagger1

To tylko część z dostępnych możliwości. Pozostałe z nich to na przykład:

  • Możliwość dodawania custom’ych header’ów do zapytań, przydatne np. gdy chcemy przekazać access token.
  • Dodatkowy opis naszych metod i parametrów przekazywany za pomocą komentarzy XML
  • Prawie dowolna personalizacja naszej strony z dokumentacją.

Pokażę pierwszą z możliwości, właśnie na przykładzie dodawania header’a z access key.

Musimy zacząć od dostarczenia implementacji IOperationFilter

public class AddCustomParameters: IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        IParameter param = new NonBodyParameter
        {
            Name = "authorization",
            In = "header",
            Description = "access token",
            Required = true,
            Default = "bearer "
         };
         if (operation.Parameters == null)
         {
             operation.Parameters = new List<IParameter>();
         }
         operation.Parameters.Add(param);
   }
}

W metodzie Apply tworzymy parametr, który chcemy dodać m.in: jego nazwę, gdzie ma się znajdować, opis, czy jest wymagany oraz początkową wartość.

Następnie modyfikujemy dodawanie Swagger’a do serwisów:

services.AddSwaggerGen(c =&gt;
{
    c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
    c.OperationFilter<AddCustomParameters>();
});

I dodawanie własnych header’ów gotowe. Po ponownym włączeniu aplikacji ukazuje nam się gotowe pole, do którego musimy tylko dokleić nasz wygenerowany podczas logowania token.

swagger2

Pomimo kontraktów jakie specyfikowane są pomiędzy osobami piszącymi frontend oraz backend danej funkcjonalności Swagger może być bardzo przydatnym narzędziem, które rozwieje wszelkie możliwe wątpliwości dotyczące typu przekazywanego parametru, pisowni lub możliwych status kodów.

In-memory caching..

Ostatnio w projekcie tworzonym podczas praktyk spotkaliśmy się z wymaganiem, aby po trzech nieudanych próbach logowania pojawiała się reCaptcha, której poprawne wypełnienie umożliwia przeprowadzenie kolejnej próby. Po krótkim research’u
na temat sposobu rozpoznawania użytkownika wybór padł na przypisywaniu
do IP ilości niepoprawnych prób logowania. Z kilku możliwych sposobów przechowywania takiej informacji, postanowiliśmy wybrać in-memory caching.

In-memory caching jest najprostszą wersją cache’u oferowaną przez ASP.NET Core. Reprezentuje ona dane przechowywane w pamięci serwera internetowego.
In-memory cache w implementacji Microsoftowej jest całkowicie bezpieczny podczas używania wielowątkowego, co bardzo nas ucieszyło i upewniło w przekonaniu, że właśnie tego sposobu chcemy użyć.

Rozpoczęcie pracy z cache’m jest bardzo proste, jego API jest opakowane serwisem, który możemy wstrzykiwać za pomocą dependency injection.

Tworzymy podstawowy projekt .NET CORE MVC.

Na początku musimy dodać implementację IMemoryCache do naszej kolekcji serwisów w pliku Startup w metodzie ConfigureService.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddMvc();
}

I już! Jesteśmy gotowi do użycia!

Kolejnym krokiem jest wstrzyknięcie IMemoryCache w konstruktorze kontrolera lub dowolnej innej klasy.

private readonly IMemoryCache _memoryCache;

public CacheController(IMemoryCache memoryCache)
{
    _memoryCache = memoryCache;
}

API reprezentujące cache jest bardzo proste, podobne do tego znanego
z Dictionary, więc nie powinno nikomu sprawić wielkich problemów.

Na przykład, aby zapisać dane w pamięci możemy posłużyć się poniższym zapisem:

_memoryCache.Set("date", DateTime.Now);

Będziemy teraz pod kluczem ‘date’ mieć zapisaną datę obecną w chwili wykonania operacji.
Aby teraz pobrać zapisane dane z pamięci możemy użyć metody Get.

var cachedDate =  _memoryCache.Get("date");

Po stworzeniu instancji klasy MemoryCacheEntryOptions możemy dowolnie konfigurować ustawienia.

var memoryCacheEntryOptions = new MemoryCacheEntryOptions();

Na przykład:

memoryCacheEntryOptions.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));

Dzięki temu nasze dane zostaną usunięte po upływie 30 minut od dodania.

memoryCacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(30));

Za pomocą metody SetSlidingExpiration, możemy ustawić, aby dane były usuwane po 30 minutach od momentu, kiedy były ostatnio wykorzystywane.

Aby zaaplikować ustawienia do naszych zapisywanych danych możemy użyć metody Set z inna sygnaturą:

_memoryCache.Set("date", memoryCacheEntryOptions, DateTime.Now);

Istnieje jeszcze mnóstwo innych możliwości konfiguracji używania in-memory caching, między innymi ustalenie zależności pomiędzy danymi lub callback’ów wykonywanych np. po usunięciu konkretnej danej.

Oto moje przykładowe użycie cache’a do zapisywania w pamięci daty w momencie wykonywania operacji oraz następnie pobierania danej z cache’a oraz zwrócenie jej w raz z obecną datą w celu porównania.
Sposób zapisu oraz kodowania jaki jest tutaj widoczny, jest użyty tylko i wyłącznie na potrzeby bloga.

[HttpPut]
public IActionResult Put()
{
    _memoryCache.Set("date",new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMinutes(30)), DateTime.Now);
    return NoContent();
}

[HttpGet]
public IActionResult Get()
{
    if (_memoryCache.TryGetValue("date", out DateTime cachedTime))
    {
        return Ok(new
        {
            CachedTime = cachedTime.ToString("G"),
            CurrentTime = DateTime.Now.ToString("G")
        });
    }
    return BadRequest("No cached time!");
}

Hello Blog

Stworzyłem tego bloga, aby utrwalać swoją nabywaną wiedzę i upewniać się, że sam dobrze rozumiem temat. Mam nadzieję, że komuś udzieli się moja pasja dotycząca programowania 🙂

Jeżeli nie potrafisz czegoś prosto wyjaśnić – to znaczy, że niewystarczająco to rozumiesz.
~Albert Einstein