wtorek, 7 marca 2023

Pobranie listy instancji Google Cloud Platform za pomocą PowerShella

 Google Cloud Platform Team przygotował moduł PoweShell za pomocą którego możemy w łatwy sposób zarządzać różnymi serwisami platformy chmurowej. 

W tym poście pokaże jak pobrać listę instancji oraz kawałek kodu bonusowego pokazujący jak pobrać po jednym hoście z każdego Manage Instance Group. 

Na początek instalujemy moduł GoogleCloud:

 Install-Module GoogleCloud  

Komenda pozwalająca wyświetlić wszystkie instancje dla naszego projektu w GCP:

 Get-GceInstance -Project "project_Id"  

Zostaną wyświetlone wszystkie wirtualne maszyny (Compute Engine) stworzone w naszym projekcie. 

Przypuśćmy, że nasz projekt składa się z wielu Manage Instance Groups (MIG). Naszym zadaniem jest sprawdzić na jednej maszynie z danego MIGu np. wersje dll-ki. Chcemy więc napisać kod, który zwróci nam po jednej maszynie dla każdego MIGa. Przyjmijmy dodatkowo że nasze maszyny mają formę nazewnictwa które pozwala w łatwy sposób przyporządkować ją do MIGa. 

Możemy dla przykładu przyjąć, że przykładowa lista maszyn to:

 envirsvrin-f6mf  
 envirsvrin-fgmf  
 envirsvrin-igmf  
 envirjsrul-femf  
 envirjsrul-fe1f  
 envirsvleo-ftmf  
 envirsvleo-f5mf  

Patrząc na to zestawienie od 5-tego znaku, 5 znaków jest unikalnych dla danego MIGa. 

Moja propozycja rozwiązania tego zadania, to użycie słownika i przechowanie jednej maszyny dla każdego z migów. Dodatkowo pobrane maszyny sortujemy po dacie stworzenia - tak więc mamy pewność, że maszyna działa od jakiegoś czasu i nie powinno być problemu z połączeniem do niej. Czy jest to najbardziej optymalne rozwiązanie? Zapewne nie - bardziej eleganckim rozwiązaniem byłoby pobranie wszystkich MIGów a następnie pobranie po jednej maszynie z każdego z nich. 

 $allVmsInProject = Get-GceInstance -Project "projectId" | Sort-Object -Property TimeCreated  
 #store one Vm for each mig  
 $vmDictionary = @{}   
 $(foreach ($vm in $allVmsInProject) {  
   $vmDictionary[$vm.Name.Substring(5, 5)] = $vm.Name  
 })  
 foreach ($vm in $vmDictionary.Values) {  
   Write-Host $vm  
 }  

Po wykonaniu skryptu otrzymamy następujący rezultat:

 envirjsrul-fe1f  
 envirsvleo-f5mf  
 envirsvrin-f6mf  

poniedziałek, 6 marca 2023

Serializacja Protobol Buffers (protobuf) danych przy pomocy biblioteki protobuf-net

 Protocol Buffers jest mechanizmem serializacji stworzonym przez Google. Założenie projektu opierało się na tym aby ilość danych wygenerowanych była jak najmniejsza, szybkość działania jak największa oraz nie były uzależnione od języka programowania czy użytej platformy. 

Można by powiedzieć, że jest to XML/JSON tylko w dużo lepszym wydaniu. Platforma .NET oferuje wiele narzędzi, które pozwalają tworzyć klasy na podstawie plików .proto. Na rynku istnieje także ciekawa biblioteka protobuf-net, która pozwala stworzyć kontrakt serializowanej klasy podobnie jak to ma się np. w przypadku XmlSerializera (za pomocą atrybutów).

Najłatwiej pokazać zasadę działania biblioteki na przykładzie. 

Na początek instalujemy protobuf-net:


Następnie definiujemy klasę którą chcemy zserializować: 

 [ProtoContract]  
 public class Person  
 {  
   [ProtoMember(1)]  
   public int Id { get; set; }  
   [ProtoMember(2)]  
   public string FirstName { get; set; }  
   [ProtoMember(3)]  
   public string LastName { get; set; }  
   [ProtoMember(4)]  
   public string Address { get; set; }  
   public override string ToString()  
   {  
     return $"{nameof(Id)}: {Id}, {nameof(FirstName)}: {FirstName}, {nameof(LastName)}: {LastName}, {nameof(Address)}: {Address}";  
   }  
 }  

Warto w tym miejscu wspomnieć o kilku aspektach:

  • ProtoContract - atrybut serializowanej klasy
  • ProtoMember(int i) - atrybut który przypisujemy poszczególnym właściwością klasy. Ważne aby numery nie powtarzały się dla tego samego typu (nawet w przypadku gdy dziedziczymy po tej klasie musimy pamiętać aby numery były unikalne). Unikalność jest na poziomie typu. Oznacza to, że dla kolejnego typu możemy rozpocząć znowu od 1. Nie warto używać dużych numerów początkowych. Im wyższy numer tym więcej danych do zapisu. 
Identyfikator (numer przypisany w atrybucie ProtoMember) jest jednym z najważniejszych elementów. Możemy zmienić nazwę pola, jednak dopóki jego numer pozostaje taki sam nie będzie problem z deserializacją danych. 

Teraz kawałek kodu który odpowiada za serializację / deserializację struktury danych. Dla przykładu dane zostaną zapisane do pliku:


 Person person = new()  
 {  
   Id = 1,  
   FirstName = "Mariusz",  
   LastName = "Kowalski",  
   Address = "Kraków, Malborska 10"  
 };  
 //1. Serializing data  
 var fileName = "person.bin";  
 using (var file = File.Create(fileName))  
 {  
   Serializer.Serialize(file, person);  
 }  
 //2. Deserializing data  
 using (var fileStream = File.OpenRead(fileName))  
 {  
   var deserializedPerson = Serializer.Deserialize<Person>(fileStream);  
   Console.WriteLine(deserializedPerson);  
 }  
 Console.ReadKey();  

Zarówno serializacja jak i deserializacja używa w tym przypadku klasy Serializer z biblioteki protobuf-net. Pracujemy na standardowych strumieniach danych - możemy je przekierować do pliku, przechować w pamięci czy też skonwertować do tablicy bajtów i przesłać np. do Pub/Suba.

czwartek, 2 marca 2023

Pobranie wersji pliku w PowerShell

 Za pomocą PowerShell możemy w łatwy sposób pobrać wersję pliku (biblioteki). Może się do przydać np. gdy chcemy sprawdzić czy na maszynie na pewno wrzucona jest poprawna wersja. Inną sytuację kiedy może się przydać ta wiedza jest sytuacja kiedy operujemy na wielu aplikacjach i chcemy sprawdzić jakiej wersji biblioteki używa dana aplikacja. 

Na początek oczywiście musimy znaleźć interesującą nas bibliotekę:

 $files = Get-ChildItem -Path "$directory\*.dll" -Recurse -Filter $dllName  

Powyższy kod zwróci wszystkie znalezione pliki. Teraz pozostaje już sprawdzenie właściwości VersionInfo.FileVersion:

 foreach ($file in $files) {            
   Write-Host $file.VersionInfo.FileVersion  
 }  

Używając komendy Invoke-Command możemy powyższy przykład przekształcić do skryptu który przeszukuje wiele hostów i zapisuje rezultat do pliku

 $hostsList = "host1", "host2", "host3"  
 $directoriesToSearch = "C:\directory1", "C:\directory2"  
 $dllNameVersionYouWantToLog = "Aspnet.dll"  
 $report = "c:\temp\report.txt"  
 New-Item -Path $report -ItemType File  
 foreach ($vmHost in $hostsList) {  
   $result = Invoke-Command -ComputerName $vmHost -ArgumentList ($vmHost, $dllNameVersionYouWantToLog, $directoriesToSearch) -ScriptBlock {  
     Param ($vmHost, $dllName, $directoriesToSearch)  
     Write-Host $vmHost  
     $pathsWithVersion = New-Object -TypeName "System.Text.StringBuilder";  
     foreach($directory in $directoriesToSearch) {  
       if (Test-Path -Path $directory){  
         $files = Get-ChildItem -Path "$directory\*.dll" -Recurse -Filter $dllName  
         foreach ($file in $files) {  
           [void]$pathsWithVersion.AppendLine("$($vmHost);$($file);$($file.VersionInfo.FileVersion)")  
         }  
       }  
     }  
     return $pathsWithVersion.ToString()  
   }  
   if($result) {  
     Add-Content $report -Value "$($result)"  
   }  
 }  

Oczywiście nic nie stoi na przeszkodzie aby parametry do skryptu przesłać z lini komend, bądź wczytać z pliku. Jedynym ograniczeniem jest wyobraźnia :)