Translate

jueves, 9 de marzo de 2023

Gleam, un lenguaje funcional que corre en la Vm de Erlang.


Gleam es un lenguaje funcional que corre sobre la maquina virtual de Erlang. Es de tipado estatico, funcional y sintaxis similar a Erlang y Elixir.   

Gleam se ejecuta en la máquina virtual Erlang que impulsa sistemas a escala planetaria como WhatsApp y Ericsson, está listo para cargas de trabajo de cualquier tamaño. Gracias a un sistema de simultaneidad basado en múltiples núcleos que puede ejecutar millones de tareas simultáneas, estructuras de datos rápidas e inmutables y un recolector de basura simultáneo, su servicio puede escalar y mantenerse con facilidad.

Gleam viene con compilador, herramienta de compilación, formateador, integraciones de editor y administrador de paquetes, todo integrado, por lo que crear un proyecto Gleam es simplemente ejecutar gleam new.

Como parte del ecosistema BEAM más amplio, los programas Gleam pueden usar miles de paquetes publicados, ya sea que estén escritos en Gleam, Erlang o Elixir.

Sin valores nulos, sin excepciones, mensajes de error claros y un sistema de tipo práctico. Ya sea que esté escribiendo código nuevo o manteniendo código antiguo, Gleam está diseñado para hacer que su trabajo sea lo más divertido y libre de estrés posible.

Gleam también puede compilar en JavaScript, lo que le permite usar su código en el navegador o en cualquier otro lugar donde se pueda ejecutar JavaScript. También genera definiciones de TypeScript, por lo que puede interactuar con su código Gleam con confianza, incluso desde el exterior.


Veamos un pequeño hola mundo hecho en Gleam:

import gleam/io


pub fn main() {

  io.println("hello, friend!")

}


Por ejemplo veamos código asincrono: 


fn spawn_task(i) {

  task.async(fn() {

    let n = int.to_string(i)

    io.println("Hello from " <> n)

  })

}


pub fn main() {

  // Run a million threads, no problem

    list.range(0, 1_000_000)

  |> list.map(spawn_task)

  |> list.each(task.await_forever)

}

Dejo link: https://gleam.run/

martes, 7 de marzo de 2023

¿Tu empresa está lista para dejar que los datos hagan el trabajo pesado?

 

domingo, 5 de marzo de 2023

Primeros pasos con Phoenix


Antes de empezar de manera muy rápida aclaremos que Phoenix es un framework web o para hacer API en Elixir. 

Phoenix está escrito en Elixir, y nuestro código de aplicación también estará escrito en Elixir. Por ende el primer paso es instalar Elixir , en mi caso lo voy a instalar con asdf

asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git

asdf install elixir 1.14.3-otp-25

El código de Elixir se compila en el código de bytes de Erlang para ejecutarse en la máquina virtual de Erlang. Sin Erlang, el código de Elixir no tiene una máquina virtual para ejecutarse, por lo que también debemos instalar Erlang. 

asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git

asdf install erlang 25.0.3

Y ahora vamos a setear las versiones: 

asdf global erlang 25.0.3

asdf global elixir 1.14.3-otp-25

Pero pueden ver como instalar en su equipo en el siguiente link : https://elixir-lang.org/install.html

Cuando instalamos Elixir siguiendo las instrucciones de la página de instalación de Elixir, normalmente también obtendremos Erlang. Para checkear esto hacemos : 

emanuel@crespo:~$ elixir --version

Erlang/OTP 25 [erts-13.1.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]


Elixir 1.14.3 (compiled with Erlang/OTP 25)


También podemos escribir erl que es el comando para la relp de Erlang. 

Si acabamos de instalar Elixir por primera vez, también necesitaremos instalar el administrador de paquetes Hex. Hex es necesario para ejecutar una aplicación de Phoenix (mediante la instalación de dependencias) y para instalar cualquier dependencia adicional que podamos necesitar en el camino. En mi caso es : 

mix local.hex


Ahora debemos instalar phoenix, en mi caso voy a instalar la ultima versión

mix archive.install hex phx_new 

Por lo visto, ya esta todo instalado, ahora vamos a crear nuestro primer proyecto: 

mix phx.new hello_world --module HelloWorld

Y si todo salio bien vamos a ejecutarlo: 

cd hello_world/
mix phx.server

Si vamos a http://localhost:4000/ nos aparecerá una pagina. 

Van a ver que tira como 5000 errores porque no configuramos la base de datos, pero eso lo vamos hacer en el proximo post. 

Dejo link; https://www.phoenixframework.org/

viernes, 3 de marzo de 2023

Asdf, multiples sdks en una sola aplicación

 


Asdf, es como sdkman pero para multiples plataformas y lenguajes. Con Asdf puedo instalar node, elixir, python, etc... 

Para instalarlo en linux tenemos que tener git y curl instalados : 


apt install curl git


Usamos git para bajar le código: 

git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.2


Luego ejecutamos lo siguiente: 

. "$HOME/.asdf/asdf.sh"

. "$HOME/.asdf/completions/asdf.bash"


Y ya esta!! 


Ahora instalemos nodejs por ejemplo, primero instalamos el plugin para la plataforma: 

asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git


y instalamos node : 

$ asdf install nodejs latest

Trying to update node-build... ok

Downloading node-v19.7.0-linux-x64.tar.gz...

-> https://nodejs.org/dist/v19.7.0/node-v19.7.0-linux-x64.tar.gz

Installing node-v19.7.0-linux-x64...

Installed node-v19.7.0-linux-x64 to /home/emanuel/.asdf/installs/nodejs/19.7.0

En mi caso instalo la versión 19.7.0, ahora tengo que setearla : 

asdf local nodejs 19.7.0

con "asdf list nodejs" puedo saber las versiones que tengo instaladas. 

Y listo!


Dejo link: 



miércoles, 1 de marzo de 2023

The best Golang Learning Resources on the Web


Quiero recomentarles el sitio golang resources que contiene un conjunto de recursos clasificados por nivel de conocimiento. Es muy visual y muy completa. Sin más....

Dejo link: https://golangresources.com/

lunes, 27 de febrero de 2023

Inyección de dependencia en Go usando Fx parte 2

 


Seguimos con fx, ahora vamos a hacer otro ejemplo con fx y Go basados en este ejemplo hecho con spring y java.

Primero vamos a hacer una interfaz que nos permita obtener un saludo : 

type Saludor interface {

GetSaludo() string

}

Luego vamos a implementarla en español e ingles : 


type SaludorEs struct {

}

func (saludoEs SaludorEs) GetSaludo() string {

return "Hola !!"

}


type SaludorEn struct {
}

func (saludoEn SaludorEn) GetSaludo() string {
return "Hi !!"
}

Y vamos a hacer métodos que nos permitan crear estas implementaciones : 

func CrearSaludadorEs() (Saludor, error) {
return SaludorEs{}, nil
}

func CrearSaludadorEn() (Saludor, error) {
return SaludorEn{}, nil
}

Y ahora vamos a hacer un método que sea polimorfico que imprima el saludo : 


func Saludar(saludor Saludor) {
fmt.Println(saludor.GetSaludo())
}

Como ven referencia a la interfaz por lo tanto se puede usar con el SaludadorEs y el SaludadorEn porque implementan esta interfaz. 

Y por ultimo vamos hacer 2 app de fx una en español y la otra en ingles : 


func main() {
appEs := fx.New(

fx.Provide(
CrearSaludadorEs,
),

fx.Invoke(Saludar),
)

ctxEs, cancelEs := context.WithCancel(context.Background())
defer cancelEs()
if err := appEs.Start(ctxEs); err != nil {
log.Fatal(err)
}

appEn := fx.New(

fx.Provide(
CrearSaludadorEn,
),

fx.Invoke(Saludar),
)

ctxEn, cancelEn := context.WithCancel(context.Background())
defer cancelEn()
if err := appEn.Start(ctxEn); err != nil {
log.Fatal(err)
}
}

y la salida es la siguiente : 

[Fx] PROVIDE    main.Saludor <= main.CrearSaludadorEs()
[Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE             main.Saludar()
Hola !!
[Fx] RUNNING
[Fx] PROVIDE    main.Saludor <= main.CrearSaludadorEn()
[Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE             main.Saludar()
Hi !!
[Fx] RUNNING

Y listo!!



Un Ejemplo de polimorfismo en golang

 


Si seguís el blog tenes que saber más o menos que es el polimorfismo. En golang no tenemos clases pero tenemos interfaces, por lo tanto podemos utilizar el polimorfismo. 


Supongamos que tenemos que imprimir diferentes cosas documentos de texto, planillas, etc. Y queremos que una impresora pueda imprimir cualquier documento. Por lo tanto la impresora va a tomar un objeto imprimible y lo imprimira. 

Entonces vamos a hacer la interfaz imprimible. 


type Imprimible interface {

to_string() string

}


Y ahora vamos a hacer estructuras que implementen la interfaz : 


type Doc struct {

texto string

}


type Datos struct {

dato1 string

dato2 string

}


func (doc Doc) to_string() string {

return doc.texto

}


func (datos Datos) to_string() string {

return datos.dato1 + " " + datos.dato2

}


Y ahora vamos a hacer la función imprimir que para que funcione el ejemplo va a imprimir por pantalla: 


func imprimir(imprimible Imprimible) {

fmt.Println(imprimible.to_string())

}


Y por ultimo vamos a utilizar esta función: 



func main() {

var doc = Doc{texto: "Hola!! Este es Doc"}

var datos = Datos{dato1: "dato1", dato2: "dato2"}

imprimir(doc)

imprimir(datos)

}


Y listo!!! 

Inyección de dependencia en Go usando Fx


Vamos por parte, que es la inyección de dependencias?

Cualquier aplicación no trivial está formada por dos o más objetos que colaboran entre sí para realizar alguna lógica. Tradicionalmente cada objeto se hacía cargo de obtener sus propias referencias a los objetos a los cuales colaboraba (sus dependencias). Esto lleva a código acoplado y difícil de probar. Y para solucionar esto esta la inyección de dependencias. El contexto ingresa las dependencias de un objeto. Para mayor información pueden ir a : https://emanuelpeg.blogspot.com/2009/07/inyeccion-de-dependencias.html


Y que es Fx? Según la documentación oficial publicada por Uber, Fx es un framework para Go que:

  • Facilita la inyección de dependencia.
  • Elimina la necesidad de estado global y func init().

Fx usa el patrón de inyección de constructor, tratemos de entender exactamente cómo hace que la inyección de dependencia sea fácil en Go.

Para usar Fx debemos: 

El primer paso es instalar la biblioteca a través de go get go.uber.org/fx.

Y luego podemos hacer esto: 

package main


import (

"context"

"go.uber.org/fx"

"go.uber.org/fx/fxevent"

"log"

"net"

"net/http"

"os"

"time"

)


func NewLogger() *log.Logger {

logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)

logger.Print("Create NewLogger.")

return logger

}


func NewHandler(logger *log.Logger) (http.Handler, error) {

logger.Print("Create NewHandler.")

return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {

logger.Print("Got a request.")

}), nil

}


func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {

logger.Print("Create NewMux.")

mux := http.NewServeMux()

server := &http.Server{

Addr:    "127.0.0.1:8080",

Handler: mux,

}

lc.Append(fx.Hook{

OnStart: func(context.Context) error {

logger.Print("Starting HTTP server.")

ln, err := net.Listen("tcp", server.Addr)

if err != nil {

return err

}

go server.Serve(ln)

return nil

},

OnStop: func(ctx context.Context) error {

logger.Print("Stopping HTTP server.")

return server.Shutdown(ctx)

},

})


return mux

}


func Register(mux *http.ServeMux, h http.Handler) {

mux.Handle("/", h)

}


func main() {

app := fx.New(


fx.Provide(

NewLogger,

NewHandler,

NewMux,

),


fx.Invoke(Register),


fx.WithLogger(

func() fxevent.Logger {

return fxevent.NopLogger

},

),

)


startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)

defer cancel()

if err := app.Start(startCtx); err != nil {

log.Fatal(err)

}


if _, err := http.Get("http://localhost:8080/"); err != nil {

log.Fatal(err)

}


stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)

defer cancel()

if err := app.Stop(stopCtx); err != nil {

log.Fatal(err)

}


}


En el ejemplo queremos costruir varias entidades, un *log.Logger,  un http.Handler, un servidor *http.ServeMux y queremos llamar a una request. Y fx, nos construye estos objetos. 

Si ejecutamos esta aplicación el resultado será : 

Create NewLogger.
Create NewMux.
Create NewHandler.
Starting HTTP server.
Got a request.
Stopping HTTP server.


Dejo link: 


viernes, 24 de febrero de 2023

Programación poliglota con GraalVM parte 2


Seguimos con GraalVM 

Veamos un ejemplo queremos ejecutar una aplicación javascript o node que dentro llama a codigo ruby, por ejemplo. 

Vamos a tener que crear un archivo .js que lo llamaré ejemplo.js que contenga lo siguiente: 


var array = Polyglot.eval("ruby", "[1,2,42,4]")

console.log(array[2]);


Y lo ejecutamos con : 


js --polyglot --jvm ejemplo.js

42

node --polyglot --jvm ejemplo.js

42


Así de fácil o queremos ejecutar en una aplicación R, javascript :

array <- eval.polyglot("js", "[1,2,42,4]")
print(array[3L])

Y lo corremos con : 

Rscript --polyglot --jvm ejemplo.R
[1] 42

Y puedo seguir con ejemplos pero creo que se entiende :D


Dejo link : https://www.graalvm.org/reference-manual/polyglot-programming/#running-polyglot-applications

martes, 21 de febrero de 2023

Probar código asíncrono con cats

Cómo simplificar las pruebas unitarias para código asincrónico haciéndolas sincrónicas con Cats? Esta es la pregunta que vamos a intantar responder. 

Supongamos que estamos midiendo el tiempo de actividad en un conjunto de servidores.  Habrá dos componentes. El primero es un UptimeClient que sondea servidores remotos para conocer su tiempo de actividad:


import scala.concurrent.Future

trait UptimeClient {

    def getUptime(hostname: String): Future[Int]

}


También tendremos un UptimeService que mantiene una lista de servidores y permite al usuario sondearlos por su tiempo de actividad total:


import cats.instances.future._ // for Applicative

import cats.instances.list._// for Traverse

import cats.syntax.traverse._// for traverse

import scala.concurrent.ExecutionContext.Implicits.global


class UptimeService(client: UptimeClient) {

    def getTotalUptime(hostnames: List[String]): Future[Int] =

        hostnames.traverse(client.getUptime).map(_.sum)

}

Hemos modelado UptimeClient como un trait porque vamos a querer probarlo en pruebas unitarias. Por ejemplo, podemos escribir un cliente de prueba que nos permita proporcionar datos ficticios en lugar de llamar a servidores reales:


class TestUptimeClient(hosts: Map[String, Int]) extends UptimeClient {

    def getUptime(hostname: String): Future[Int] =

        Future.successful(hosts.getOrElse(hostname, 0))

}


Ahora, supongamos que estamos escribiendo pruebas unitarias para UptimeService. Queremos probar su capacidad para sumar valores, independientemente de dónde los obtenga. Aquí hay un ejemplo:


def testTotalUptime() = {

   val hosts= Map("host1" -> 10, "host2" -> 6)

   val client= new TestUptimeClient(hosts)

   val service= new UptimeService(client)

   val actual= service.getTotalUptime(hosts.keys.toList)

   val expected = hosts.values.sum

   assert(actual == expected)

}


El código no compila porque hemos cometido un error clásico. Olvidamos que nuestro código de aplicación es asíncrono. Nuestro resultado real es de tipo Future[Int] y nuestro resultado esperado es de tipo Int. ¡No podemos compararlos directamente!

Hay un par de maneras de resolver este problema. Podríamos modificar nuestro código de prueba para acomodar la asincronía. Sin embargo, existe otra alternativa. ¡Hagamos que nuestro código de servicio sea sincrónico para que nuestra prueba funcione sin modificaciones!

Necesitamos implementar dos versiones de UptimeClient: una asíncrona para usar en producción y una síncrona para usar en nuestras pruebas unitarias:


trait RealUptimeClient extends UptimeClient {

    def getUptime(hostname: String): Future[Int]

}

trait TestUptimeClient extends UptimeClient {

    def getUptime(hostname: String): Int

}


La pregunta es: ¿qué tipo de resultado debemos dar al método abstracto en UptimeClient? Future[Int] o Int:


trait UptimeClient {

    def getUptime(hostname: String): ???

}


Al principio esto puede parecer difícil. Pero afortunadamente, Cats brinda una solución en términos del tipo de identidad, Id. Id nos permite "envolver" tipos en un constructor de tipos sin cambiar su significado:


package cats

type Id[A] = A


Id nos permite abstraernos sobre los tipos de devolución en UptimeClient. Implementa esto ahora:


import cats.Id

trait UptimeClient[F[_]] {

    def getUptime(hostname: String): F[Int]

}

trait RealUptimeClient extends UptimeClient[Future] {

   def getUptime(hostname: String): Future[Int]

}

trait TestUptimeClient extends UptimeClient[Id] {

   def getUptime(hostname: String): Id[Int]

}


Ahora deberíamos poder desarrollar una definición de TestUptimeClient en una clase completa basada en Map[String, Int].


Dirijamos nuestra atención a UptimeService. Necesitamos reescribirlo para abstraer los dos tipos de UptimeClient:

class UptimeService[F[_]](client: UptimeClient[F]) {

    def getTotalUptime(hostnames: List[String]): F[Int] =

        hostnames.traverse(client.getUptime).map(_.sum)

}

Ahora descomente el cuerpo de getTotalUptime. Debería obtener un error de compilación similar al siguiente:


// <console>:28: error: could not find implicit value for

//evidence parameter of type cats.Applicative[F]

//hostnames.traverse(client.getUptime).map(_.sum)

//                              ^


El problema aquí es que traverse solo funciona en secuencias de valores que tienen un Aplicativo. En nuestro código original estábamos atravesando una Lista[Futuro[Int]]. Hay un aplicativo para Future, así que estuvo bien.

En esta versión estamos atravesando una Lista[F[Int]]. Necesitamos demostrarle al compilador que F tiene un Aplicativo. Haga esto agregando un parámetro de constructor implícito a UptimeService.


import cats.Applicative

import cats.syntax.functor._ // for map


class UptimeService[F[_]](client: UptimeClient[F]) (implicit a: Applicative[F]) {

    def getTotalUptime(hostnames: List[String]): F[Int] =

        hostnames.traverse(client.getUptime).map(_.sum)

}


Finalmente, dirijamos nuestra atención a nuestras pruebas unitarias. Nuestro código de prueba ahora funciona según lo previsto sin ninguna modificación. Creamos una instancia de TestUptimeClient y la envolvemos en un UptimeService. Esto une efectivamente F a Id, lo que permite que el resto del código funcione sincrónicamente sin preocuparse por las mónadas o los aplicativos:


def testTotalUptime() = {

    val hosts= Map("host1" -> 10, "host2" -> 6)

    val client= new TestUptimeClient(hosts)

    val service= new UptimeService(client)

    val actual= service.getTotalUptime(hosts.keys.toList)

    val expected = hosts.values.sum

    assert(actual == expected)

}

testTotalUptime()


Este estudio de caso proporciona un ejemplo de cómo Cats puede ayudarnos a abstraernos en diferentes escenarios computacionales. Usamos la clase de tipo Applicative para abstraer sobre código asíncrono y síncrono. Apoyarnos en una abstracción funcional nos permite especificar la secuencia de cálculos que queremos realizar sin preocuparnos por los detalles de la implementación.

Las clases de tipo como Functor, Applicative, Monad y Traverse proporcionan implementaciones abstractas de patrones como mapeo, compresión, secuenciación e iteración. Las leyes matemáticas de esos tipos aseguran que funcionen junto con un conjunto consistente de semántica.

Usamos Applicative en este caso de estudio porque era la clase de tipos menos poderosa que hacía lo que necesitábamos. Si hubiéramos requerido flatMap, podríamos haber cambiado Applicative por Monad. Si hubiéramos necesitado abstraernos sobre diferentes tipos de secuencias, podríamos haber usado Traverse. También hay clases de tipos como ApplicativeError y MonadError que ayudan a modelar fallas y cálculos exitosos.

lunes, 20 de febrero de 2023

Tutoriales de kubernetes


Este post es una pavada, pero bueno quiero compartirlo igual. Encontré un conjunto de tutoriales de kubernete en español en la pagina de kubernetes. No es una super novedad pero me sirvieron mucho. 

Dejo link : https://kubernetes.io/es/docs/tutorials/

jueves, 16 de febrero de 2023

¿Cuál es la diferencia entre "is not null" y "!= null" en C# ?



La principal diferencia entre e != null y is not null es la forma en que el compilador ejecuta la comparación.

El compilador garantiza que no se invoque ningún operador de igualdad == sobrecargado por el usuario cuando se evalúa la expresión x es nula.

Es decir si sobre escriben mal == estamos muertos, por eso esta bueno siempre usar "is not null" o "is null" 

Veamos un ejemplo: 

public class TestObject

{

  public string Test { get; set; }


  // attempt to allow TestObject to be testable against a string

  public static bool operator ==(TestObject a, object b)

  {

    if(b == null)

      return false;

    

    if(b is string)

      return a.Test == (string)b;


    if(b is TestObject)

      return a.Test == ((TestObject)b).Test;


    return false;

  }


  public static bool operator !=(TestObject a, object b)

  {

    if(b == null)

      return false;

    

    if(b is string)

      return a.Test != (string)b;


    if(b is TestObject)

      return a.Test != ((TestObject)b).Test;


    return false;

  }

}


Como vemos la sobrecarga de == y != cuando b es nulo, retorna false y eso esta mal. Por lo tanto, si ejecutamos el siguiente código:


TestObject e = null;

if(e == null)

  Console.WriteLine("e == null");

if(e is null)

  Console.WriteLine("e is null");


El Output va ser : e is null

Y si hacemos : 


TestObject e = new TestObject();

if(e != null)

  Console.WriteLine("e != null");

if(e is not null)

  Console.WriteLine("e is not null");


El Output va ser: e is not null

Ninguno de los operadores sobrecargados se implementa "correctamente", por lo que la consola nunca genera e == null o e != null.

martes, 14 de febrero de 2023

Cual es el lenguaje de programación más verde?

Cual es el lenguaje de programación más verde o más ecológico? 

Cuando elegimos lenguajes de programación, generalmente lo hacemos en función de su sintaxis, rendimiento o incluso curva de aprendizaje. Sin embargo, hace unos años, un grupo de investigadores portugueses investigó el consumo de energía de 27 lenguajes fueron medidos por su consumo de energía. 

El resumen del artículo dice lo siguiente:

“Este documento presenta un estudio del tiempo de ejecución, el uso de la memoria y el consumo de energía de veintisiete lenguajes de software bien conocidos. Supervisamos el rendimiento de dichos lenguajes utilizando diez problemas de programación diferentes, expresados en cada uno de los lenguajes. Nuestros resultados muestran hallazgos interesantes, como lenguaje más lentos o más rápidos que consumen menos o más energía, y cómo el uso de la memoria influye en el consumo de energía. Mostramos cómo usar nuestros resultados para brindar apoyo a los ingenieros de software para decidir qué lenguaje usar cuando la eficiencia energética es una preocupación”.

Los resultados son los siguientes:




jueves, 9 de febrero de 2023

Mastering API Architecture

 

EBOOK
[eBook] Mastering API Architecture
Hi Emanuel,

Designing and evolving your API architecture is a continuous journey. While you cannot predict how technology and business requirements will change, you can be sure APIs are key to a successful digital strategy. This O’Reilly eBook gives you practical tips on building, managing, and securing APIs, illustrated with a case study about moving a legacy application to the cloud.

In this eBook you will learn:

  • How to transition a legacy application to the cloud, illustrated through a case study
  • API fundamentals such as standards, schema, and specification
  • About API gateways, service mesh, and Kubernetes and when to use each
  • How to mitigate the most common API security risks in the OWASP API Top Ten

martes, 7 de febrero de 2023

Hacer un endpoit para saber el estado del host con echo en Golang parte 2


Todo muy lindo en el post anterior peeeeroo nos falto saber el estado del server, para ello vamos a utilizar la librería : "github.com/shirou/gopsutil". La idea es saber si tiene memoria libre, si el micro esta a full, etc. (estado de los recursos) 

Vamos a necesitar una estructura que contenga toda la información a mostrar : 


type StatusInfo struct {

RuntimeOS            string `json:"runtimeOS"`

TotalMemory          string `json:"total_memory"`

FreeMemory           string `json:"free_memory"`

PercentageUsedMemory string `json:"percentage_used_memory"`


TotalDiskSpace           string `json:"total_disk_space"`

UsedDiskSpace            string `json:"used_disk_space"`

FreeDiskSpace            string `json:"free_disk_space"`

PercentageDiskSpaceUsage string `json:"percentage_disk_space_usage"`


CpuIndexNumber string `json:"cpu_index_number"`

VendorId       string `json:"vendorId"`

Family         string `json:"family"`

NumberOfCores  string `json:"number_of_cores"`

ModelName      string `json:"model_name"`

Speed          string `json:"speed"`


Hostname                 string `json:"hostname"`

Uptime                   string `json:"uptime"`

NumberOfProcessesRunning string `json:"number_of_processes_running"`


Os       string `json:"os"`

Platform string `json:"platform"`


HostId string `json:"host_id"`

}

Y con la librería gopsutil podemos llenar el struct: 


func status(c echo.Context) error {

runtimeOS := runtime.GOOS

// memory

vmStat, err := mem.VirtualMemory()

dealwithErr(err)


diskStat, err := disk.Usage("/")

dealwithErr(err)


// cpu - get CPU number of cores and speed

cpuStat, err := cpu.Info()

dealwithErr(err)


// host or machine kernel, uptime, platform Info

hostStat, err := host.Info()

dealwithErr(err)


var sInfo StatusInfo


sInfo.RuntimeOS = runtimeOS

sInfo.TotalMemory = strconv.FormatUint(vmStat.Total, 10) + " bytes "

sInfo.FreeMemory = strconv.FormatUint(vmStat.Free, 10) + " bytes"

sInfo.PercentageUsedMemory = strconv.FormatFloat(vmStat.UsedPercent, 'f', 2, 64) + "%"


sInfo.TotalDiskSpace = strconv.FormatInt(int64(diskStat.Total), 10) + " bytes "

sInfo.UsedDiskSpace = strconv.FormatInt(int64(diskStat.Used), 10) + " bytes"

sInfo.FreeDiskSpace = strconv.FormatInt(int64(diskStat.Free), 10) + " bytes"

sInfo.PercentageDiskSpaceUsage = strconv.FormatFloat(diskStat.UsedPercent, 'f', 2, 64) + "%"


sInfo.CpuIndexNumber = strconv.FormatInt(int64(cpuStat[0].CPU), 10)

sInfo.VendorId = cpuStat[0].VendorID

sInfo.Family = cpuStat[0].Family

sInfo.NumberOfCores = strconv.FormatInt(int64(cpuStat[0].Cores), 10)

sInfo.ModelName = cpuStat[0].ModelName

sInfo.Speed = strconv.FormatFloat(cpuStat[0].Mhz, 'f', 2, 64)


sInfo.Hostname = hostStat.Hostname

sInfo.Uptime = strconv.FormatUint(hostStat.Uptime, 10)

sInfo.NumberOfProcessesRunning = strconv.FormatUint(hostStat.Procs, 10)


sInfo.Os = hostStat.OS

sInfo.Platform = hostStat.Platform


sInfo.HostId = hostStat.HostID


return c.JSON(http.StatusOK, sInfo)

}


Uso una función para mostrar el error : 


func dealwithErr(err error) {

if err != nil {

fmt.Println(err)

}

}



Y por último agregamos un endpoint en nuestro main: 


func main() {

e := echo.New()

        ....

e.GET("/status", func(c echo.Context) error {

return status(c)

})

e.Logger.Fatal(e.Start(":1323"))

}

Si vamos a localhost:1323/status podemos obtenere una salida como esta : 

{"runtimeOS":"linux","total_memory":"20769558528 bytes ","free_memory":"2220945408 bytes","percentage_used_memory":"75.97%","total_disk_space":"745407905792 bytes ","used_disk_space":"7737049088 bytes","free_disk_space":"737670856704 bytes","percentage_disk_space_usage":"1.04%","cpu_index_number":"0","vendorId":"GenuineIntel","family":"6","number_of_cores":"1","model_name":"11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz","speed":"4700.00","hostname":"crespo","uptime":"262837","number_of_processes_running":"462","os":"linux","platform":"ubuntu","host_id":"c47f72aa-2ce5-414b-97ad-d51759c9a406"}