Weblogs Código

Fixed Buffer

Como instalar un servicio Net Core en Linux

mayo 21, 2019 08:00

Banner de la entrada "Como instalar un servicio NetCore en Linux"

Hace un par de semanas hablamos de como poder crear un servicio Net Core que fuese multiplataforma, y probamos a instalarlo en Windows. Hoy vamos a continuar con el proceso y vamos a instalarlo en Linux para comprobar que realmente es multiplataforma (y de paso ver lo realmente fácil que es).

Para poder hacerlo, solo necesitamos 2 cosas:

  • Una máquina Linux con .Net Core.
  • Una aplicación .Net Core programada como un servicio.

Para la primera de las dos, te dejo un enlace a mi entrada hablando sobre «Como instalar .Net Core en Linux» , para la segunda, vamos a reutilizar el código que vimos en la última entrada. Para ello, desde nuestra terminal (asumo que no todo el mundo usa git, por lo que voy a utilizar la descarga http), vamos a escribir los comandos:

wget https://codeload.github.com/FixedBuffer/ServicioNetCore/zip/master
unzip -a master

Una vez descomprimido, vamos a navegar hasta el .csproj con:

cd ServicioNetCore-master
cd PostServicioNetCore

Y una vez dentro, simplemente vamos a publicarlo:

dotnet publish --configuration Release

Con estos pasos, ya tenemos un binario listo para registrar como servicio. ¡Vamos a ello!

Instalando nuestro servicio Net Core en Linux

En primer lugar, la ruta donde hemos generado los binarios es poco amigable (además de ser poco práctica), así que vamos a mover la carpeta a una ruta más apropiada:

sudo mv /home/jorturfer/ServicioNetCore-master/PostServicioNetCore/bin/Release//netcoreapp2.2/publish /usr/local/NetCoreService

*Estamos moviendo la carpeta publish entera a la ruta «/usr/local/», que es una ruta mucho más apropiada para nuestro servicio, aunque no es obligatorio.

Una vez que tenemos todo puesto en su sitio vamos a instalar un servicio NetCore en Linux, para ello, solo hay que crear su fichero de definición en «/etc/systemd/system/» y registrarlo. Para crear la definición, vamos a escribir:

sudo nano /etc/systemd/system/netcoreservice.service

Y dentro del fichero, vamos a rellenar el template:

[Unit]
Description=DESCRIPCION DEL SERVICIO

[Service]
WorkingDirectory= RUTA DONDE ESTAN LOS BINARIOS
ExecStart=/usr/bin/dotnet RUTA HASTA LA DLL
Restart=always
RestartSec=10
SyslogIdentifier=IDENTIFICADOR DEL SISTEMA
User=USUARIO QUE LO VA A EJECUTAR
Environment=VARIABLES DE ENTORNO QUE VA A RECIBIR

[Install]
WantedBy=multi-user.target

Después de rellenar los datos, nos quedará algo como esto:

[Unit]
Description=Servicio para la entrada de "Como instalar un servicio NetCore en Linux"
[Service]
WorkingDirectory= /usr/local/NetCoreService
ExecStart=/usr/bin/dotnet /usr/local/NetCoreService/PostServicioNetCore.dll
Restart=always
RestartSec=10
SyslogIdentifier=dotnet-postservice
User=jorturfer
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

Por último, vamos a registrarlo con el comando:

sudo systemctl enable netcoreservice.service

Después de esto, ya podemos manejar nuestro servicio Net Core en Linux como cualquier otro servicio con los comandos:

sudo service nombreservicio start/stop/status/restart

Vamos a comprobar que funciona bien, ejecutando:

sudo service netcoreservice start

Después de eso, deberíamos encontrar un fichero llamado «PostServicioCore.txt» junto al binario con un registro por cada minuto que ha estado arrancado. Además, gracias a SSH, vamos a poder asociarnos al proceso y depurarlo como si estuviese en nuestra maquina. (Incluso con Visual Studio Code gracias al último anuncio de Microsoft sobre depuración remota en la MSBuild2019)

Como hemos podido comprobar, el servicio que hicimos multiplataforma, realmente lo es, e instalar un servicio Net Core en Linux no tiene ningún misterio.

**La entrada Como instalar un servicio Net Core en Linux se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Variable not found

Ámbito local implícito en sentencias using de C# 8

mayo 21, 2019 06:22

.NET CoreLa palabra clave using, utilizada tanto en forma de directiva como de instrucción, es una de las más sobrecargadas del lenguaje C#. Es útil para bastantes cosas, como la importación de espacios de nombres, definición de alias de namespaces o tipos, simplificar el acceso a miembros de tipos estáticos, o para especificar bloques o ámbitos de uso de recursos (objetos IDisposable) que deben ser liberados automáticamente.

Centrándonos en este último caso de uso, seguro que en muchas ocasiones habéis escrito código como el siguiente, donde vamos anidando objetos IDisposable para asegurar que al finalizar la ejecución de cada bloque los recursos sean liberados de forma automática:
void DoSomething()
{
using(var conn = new SqlConnection(...))
{
connection.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "...";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// ...
}
}
}
}
}
Al final, lo que encontramos es código con un nivel de indentación muy alto, y que resulta muy extenso, básicamente porque una gran parte de las líneas las dedicamos sólo a abrir y cerrar llaves. A la postre, esto sólo hace que nuestro código crezca a lo ancho, lo cual no es bueno desde el punto de vista de la simplicidad y facilidad de lectura.

Pues bien, C# 8 introduce una pequeña mejora destinada a simplificar la codificación de este tipo de ámbitos de uso de recursos: el ámbito local implícito. A partir de esta versión del lenguaje, podremos escribir un código como el anterior de la siguiente manera:
void DoSomething()
{
using var conn = new SqlConnection(...);
connection.Open();

using var cmd = conn.CreateCommand();
cmd.CommandText = "...";

using var reader = cmd.ExecuteReader();
while (reader.Read())
{
// ...
}
}
Utilizando la construcción using var ... que vemos en el código anterior, estamos indicando al compilador que la liberación del recurso declarado debe realizarse de forma automática cuando finalice el ámbito en el que nos encontramos. En el ejemplo anterior, la llamada al método Dispose() se realizará al finalizar la ejecución de `DoSomething()'.
Recuerda que a día de hoy ya puedes probar C# 8 en Visual Studio 2019 o directamente desde la interfaz de línea de comandos de .NET Core.
Por supuesto, esto aplica a cualquier tipo de ámbito, por lo que podríamos utilizar esta sintaxis en cualquier tipo de bloque, como un if u otros de los permitidos por el lenguaje:
if (expr)
{
using var fileStream = File.Create(...);
...
// Aquí se liberará fileStream automáticamente
}
Como detalle de implementación, pero que tiene su sentido, la liberación se realizará en orden inverso a como han sido declarados, de forma que el resultado sería muy similar a si hubiéramos optado por utilizar bloques using tradicionales.

Mmmm... no sé, no sé....

La verdad es que tengo sensaciones raras respecto a esta novedad del lenguaje. Por una parte es cierto que todo lo que nos ahorre teclear más es una ventaja y que evitar la verbosidad hace que todo sea más simple. Desde este punto de vista, la idea me convence :)

Sin embargo, el hecho de eliminar los bloques explícitos hace menos visible la intención de liberar los recursos utilizados... y sobre todo, no sé si podría hacer más difícil detectar cuándo se nos ha olvidado hacerlo.

Por ejemplo, echando un vistazo al código siguiente, no es fácil detectar que se nos olvidó añadir el using en una de las declaraciones:
void DoSomething()
{
using var conn = new SqlConnection(...);
connection.Open();

var cmd = conn.CreateCommand();
cmd.CommandText = "...";

using var reader = cmd.ExecuteReader();
while (reader.Read())
{
// ...
}
... // Aquí se liberarán conn, cmd y reader... ¿o no?
}
También, aunque entiendo que la excesiva indentación que podría darse por la utilización de bloques using convencionales puede ser perjudicial para facilitar la comprensión y legibilidad de el código, es fácilmente salvable extrayendo bloques a métodos, funciones locales o incluso otros componentes. Incluso en muchos casos estaríamos diseñando mejor nuestros componentes en términos de aplicación de niveles de abstracción correctos en cada lugar.

En fin, supongo que iré teniendo una opinión más formada cuando empiece a ver la utilización que le damos a esta característica y los problemas o beneficios que nos vaya trayendo su uso :)

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 362

mayo 20, 2019 06:10

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Blog Bitix

Combinaciones de teclado en HTML, JavaScript y páginas web

mayo 19, 2019 07:00

En las páginas y aplicaciones web también es posible utilizar combinaciones de teclas para proporcionar acceso rápido a funciones usadas frecuentemente. Utilizando la propiedad global accesskey para los campos de texto la acción es ubicar el foco en el campo y para los botones realizar la acción de clic. Los listeners como onkeydown permiten conocer que teclas modificadoras como Ctrl, Shift y Alt se han pulsado al mismo tiempo.

HTML
JavaScript

En las aplicaciones de escritorio es habitual usar atajos de teclado o combinaciones de teclas para realizar acciones sin necesidad del ratón y sin requerir levantar las manos del teclado. Estos atajos de teclado permiten ahorrar tiempo al realizar acciones habituales ya que conociendo y usando la combinación de teclas es mucho más rápido que usar el ratón. Los usuarios que trabajan con una aplicación gran cantidad de tiempo les facilita la tarea. En las aplicaciones web ya no es tan habitual encontrar asignaciones de combinación de teclas pero también se pueden emplear y son igual de útiles.

La forma de utilizar combinaciones de teclas en una página web es haciendo uso de los eventos onKeyDown y onKeyUp o el atributo global accesskey que puede ser empleado en cualquier elemento aunque dependiendo del tipo de elemento el comportamiento puede ser distinto. El listener del evento recibe como argumento un objeto que representa el evento, entre la información que contiene está la tecla pulsada y las teclas de control adicionales pulsadas al mismo tiempo.

En este ejemplo se captura la combinación de teclas Ctrl + Shift + s a nivel de página que muestra una ventana emergente utilizando la función alert() de JavaScript. El objeto del evento que se recibe como parámetro del listener posee las propiedades ctrlKey, shiftKey y altKey con las que determinar además de la tecla pulsada las teclas modificadoras adicionales pulsadas al mismo tiempo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<head>
<title>Key combinations test</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
Press Ctrl + Shift + s<br><br>
<input type="text" accesskey="t"><br><br>
<input id ="button" type="button" accesskey="b" value="Button">
<script type="text/javascript">
$('body').on("keydown", function(e) {
if (e.ctrlKey && e.shiftKey && e.which === 83) {
alert("You pressed Ctrl + Shift + s");
e.preventDefault();
}
});
$("#button").on("click", function(e) {
alert("You clicked button");
});
</script>
</body>
</html>
Ejemplo combinaciones de teclas

Los usos que se les pueden dar a estas combinaciones de teclas van desde posicionar el foco en un determinado campo de entrada o realizar alguna acción como enviar los datos de un formulario una vez están rellenados y son válidos o cualquiera otra acción mediante JavaScript. Haciendo uso del método preventDefault() del objeto evento se evita que el evento dispare la acción por defecto si tiene una asignada.

La propiedad global accesskey puede utilizarse en cualquier elemento de HTML, en el ejemplo en un elemento de formulario input y en un botón. En Firefox y GNU/Linux la combinación de teclas es Alt + Shift + [key], para el campo de texto la combinación es Alt + Shift + t y para el botón Alt + Shift + b, en el botón la acción es ubicar el foco de entrada en el campo de texto y para el botón realizar la acción clic.

Acciones con combinaciones de teclas

» Leer más, comentarios, etc...

Blog Bitix

Lanzar excepciones checked como si fueran unchecked en Java

mayo 17, 2019 02:30

Java

No es una buena práctica al igual que al no recomendado antipatrón de inicialización de variables con dobles llaves pero en el uso de streams que aceptan lambdas es un rodeo a la limitación de no poder lanzar excepciones checked por no estar definida en su API.

En Java existen dos tipos de excepciones las checked que son de obligada captura o ser lanzadas y las unchecked que no son de obligada captura ni ser declaradas. Al usar streams y algunas interfaces funcionales de Java como Consumer que no lanzan excepciones el compilador generará un error de compilación si la implementación lanza una excepción.

En el siguiente código el compilador producirá un error de compilación ya que intenta lanzar una excepción pero la interfaz funcional que implementa no lo permite.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import java.util.List;
public class Main1 {
public static void main(String[] args) {
List<String> list = List.of("a", "b", "c");
list.stream().forEach(i -> {
if (i.equals("d")) {
throw new Exception();
}
});
}
}
1
2
3
4
5
6
$ java Main1.java
Main1.java:9: error: unreported exception Exception; must be caught or declared to be thrown
throw new Exception();
^
1 error
error: compilation failed

Aunque en Java existen las excepciones checked y estas han de ser declaradas no es una limitación a nivel de la máquina virtual, se puede lanzar una excepción checked aunque no esté declarada. El siguiente código compila sin errores y se ejecutan, lanzándose la excepción aunque el método main() no la declare. Esto es debido a que en el método sneakyThrow() T es inferido como del tipo RuntimeException.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Main2 {
@SuppressWarnings("unchecked")
static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
throw (T) t;
}
static <T extends Throwable> void nonSneakyThrow(T t) throws T {
throw t;
}
public static void main(String[] args) {
Exception e = new Exception();
sneakyThrow(e); // No problems here
 //nonSneakyThrow(e); // Error: Unhandled exception: java.lang.Exception
 }
}
1
2
3
$ java Main2.java
Exception in thread "main" java.lang.Exception
at Main2.main(Main2.java:15)

Con la clase Unsafe interna del JDK (que tampoco es recomendable usar porque en el futuro será eliminada) también es posible lanzar una excepción checked sin declararla, aunque Main3.getUnsafe().throwException(e) lanza una excepción el método main() no la declara.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class Main3 {
private static Unsafe getUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
Exception e = new Exception();
Main.getUnsafe().throwException(e);
}
}
1
2
3
$ java Main3.java
Exception in thread "main" java.lang.Exception
at Main3.main(Main3.java:18)

Es posible lanzar excepciones checked como si fuesen uncheked, no es una buena práctica ya que no permite al compilador cumplir con la tarea a la que está destinada que es detectar errores en tiempo de compilación potenciales problemas además de no indicar en la API que un método lanza una excepción que debería se tratada. En la librería Vavr con la clase Try se puede usar un método que lanza una excepción, tratarla si se produce y convertir el método en uno que no lanza excepciones adecuado para el uso en los streams.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.util.List;
import io.vavr.control.Try;
public class Main4 {
public static void action(String string) throws Exception {
if (i.equals("d")) {
throw new Exception();
}
}
public static void main(String[] args) {
List<String> list = List.of("a", "b", "c");
list.stream().forEach(i -> {
Try.of(() -> { action(i); });
});
}
}

La opción más recomendable es crear una clase como Try o usar la de la librería Vavr en vez de una de las posibilidades no recomendadas anteriores.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 361

mayo 16, 2019 02:46

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, con bastantes perlas procedentes del evento Build 2019 que ha tenido lugar en Seattle días atrás. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Metodologías ágiles. De lo racional a la inspiración.

Open Space, para un roto o para un descosido

mayo 15, 2019 12:24

NOTA: Estoy escribiendo la guia de facilitación del Open Space. Hemos hablado varias veces aquí ya de los Open Space como un formato increible para la organización y facilitación de conferencias. En Agile-Spain hemos organizado ya tres a nivel nacional, y han surgido multitud de pequeños "opens" para tratar muchos temas alrededor del agilismo. Si no sabes qué es un Open Space, echa un ojo a esta

» Leer más, comentarios, etc...

Picando Código

Cómics: Morgan’s Organs – La aventura anatómicamente incorrecta

mayo 14, 2019 10:12

Vuelve el cómic que se describe como «Inside Out» de Pixar pero para adultos: Morgan’s Organs, un cómic de humor sobre las aventuras de Morgan y los órganos que viven dentro de su cuerpo. Si quieren saber más de qué trata, pueden leer mi reseña en: Cómics: Morgan’s Organs

Daniel Brodie, su autor, lanzó la campaña en Kickstarter para publicar el tercer título en la colección:

En el primer libro, las dos «cabezas» del cuerpo de Morgan se enfrentan para controlar la toma de sus decisiones, mientras Morgan lucha con la presión de una temporada sexual complicada. En el segundo libro, Morgan se enfrenta al dolor de haber tragado algo que no debía, y las consecuencias para sus organautas mientras se mueve a través de su sistema digestivo. En esta tercera aventura, veremos una historia con alcohol, arte fálico y un secreto que Morgan desea nunca haber conocido. El estilo narrativo va a ser fuera de lo convencional con una estructura similar a la de la película de Christopher Nolan, Memento.

Además del tercer libro, en esta campaña se pueden obtener también los primeros dos, tanto en versión impresa como en PDF. Y se agregaron recompensas como muñecos de peluche y de resina de varios de los órganos además de pósters, pins y más.

A pocos días de haber empezado la campaña, ya está cerca de alcanzar la meta de financiación. Faltando 32 días al momento de escribir esto, no tengo dudas de que llegue. Se estima que el cómic esté listo y sea enviado a quienes apoyamos la campaña en julio de este año. Es el tercer Kickstarter del equipo y los dos anteriores fueron -en mi experiencia- de las campañas que mejor cumplieron con los tiempos prometidos. Visiten la campaña en Kickstarter para ver un preview del nuevo título y más información:

» Leer más, comentarios, etc...

Variable not found

Constructores con parámetros en entidades EF Core

mayo 14, 2019 06:05

Entity Framework CoreSeguimos viendo características novedosas de Entity Framework Core, y ahora llega el turno a otro detalle interesante si lo que buscamos es proteger nuestras entidades de modificaciones descontroladas y así asegurar en todo momento su consistencia: los constructores parametrizados.

En versiones anteriores a EF Core 2.1 y todos sus antecesores "clásicos", un problema que era difícilmente salvable era la necesidad de que en las entidades existiera un constructor público sin parámetros.

Esto tenía sentido, pues debíamos proporcionar al framework una vía para crear las instancias al materializarlas desde el almacén de datos. De hecho, al materializar, el marco de trabajo usaba dicho constructor para crear la instancia, y luego iba poblando las propiedades una a una utilizando sus setters (o estableciendo el valor de sus backing fields, como vimos en un post anterior)

Pues bien, a partir de EF Core 2.1, tanto es posible utilizar constructores privados o con cualquier otro tipo de visibilidad, como constructores parametrizados, siempre que dichos parámetros presenten nombres idénticos a los de las propiedades de la entidad (salvo en que utilizarán camel-casing), cuyos valores serán suministrados en tiempo de ejecución.
De momento, la configuración y selección de estos constructores es totalmente guiada por convenciones, aunque se prevé que podrá ser configurable en versiones futuras de EF.
Así, la siguiente entidad sería materializada usando el constructor definido en la misma, al que llegarán los valores de id y name obtenidos desde el almacén:
public class Friend
{
public int Id { get; private set; }
public string Name { get; private set; }

private Friend(int id, string name)
{
Id = id;
Name = name;
}
}
Es importante tener en cuenta que no es necesario que existan parámetros en el constructor para todas las propiedades de la entidad. Las propiedades que no estén reflejadas en ellos serán cargadas de la forma tradicional, a través del setter; por tanto, en el siguiente código también será utilizado el constructor definido en la clase, aunque la propiedad Name será poblada directamente por EF:
public class Friend
{
public int Id { get; private set; } // Poblada en el constructor
public string Name { get; private set; } // Poblada por EF

private Friend(int id)
{
Id = id;
}
}
Sin embargo, mucho ojo, porque el constructor no será utilizado si recibe algún parámetro cuyo nombre y tipo no coincida con el de alguna propiedad de la entidad, como en los siguientes ejemplos:
public class Friend
{
public int Id { get; private set; }
public string Name { get; private set; }

// Descartado: 'fullName' no es una propiedad
private Friend(int id, string fullName) { ... }

// Descartado: 'name' no es de tipo string
private Friend(int id, object name) { ... }
}

¿Se pueden inyectar otro tipo de valores al constructor, aparte de los valores de los campos?

Sí se puede, aunque de momento el soporte es muy limitado, permitiéndose únicamente inyectar parámetros de tipos específicamente reconocidos por Entity Framework Core. En la documentación oficial se citan los siguientes tipos:
  • DbContext, que recibirán el contexto de datos actual.
  • ILazyLoader, el servicio que gestiona la carga diferida de propiedades.
  • Action<object, string>, un delegado para gestionar el lazy loading de la entidad.
  • IEntityType, que contiene metadatos sobre el tipo de la entidad actual.
En caso de especificar parámetros de alguno de estos tipos podremos darle cualquier nombre, pues EF sabrá que no nos estamos refiriendo a una propiedad de la entidad:
public class Friend
{
public int Id { get; private set; }
public string Name { get; private set; }

private Friend(int id, string name, IEntityType entityType)
{
Id = id;
Name = name as string;
... // Hacer algo con entityType aquí
}
}
Por ejemplo, el siguiente código sería totalmente válido. Observad que aprovechamos el constructor para obtener una referencia al contexto de datos, que luego utilizamos en un método de lo que podría ser su lógica de negocio:
public class Friend
{
private readonly MyContext _context;
public int Id { get; private set; }
public string Name { get; private set; }

private Friend(int id, string name, MyContext context)
{
Id = id;
Name = name as string;
_context = context;
}

public async Task LogActionAsync(string what)
{
var action = new Action() {Action = what, Who = Name};
_context.Actions.Add(action);
await _context.SaveChangesAsync();
}
}
Para futuras versiones de EF Core se está considerando la posibilidad de recibir también servicios de aplicación en el constructor.

¿Y puedo tener más de un constructor en mi entidad?

Pues sí, podrías tener por ejemplo un constructor privado para el uso interno de EF Core, y otros constructores públicos que permitan crear instancias de la entidad desde otros puntos del código.

Lo único a tener en cuenta aquí es la prioridad que EF Core dará a dichos constructores.
Según la implementación actual, que desconozco si podrá variar en un futuro, el constructor utilizado en el momento de la materialización será aquél que reciba mayor número de servicios y el menor número de parámetros asociados a propiedades escalares (ver en GitHub).

Por ejemplo, en la siguiente entidad, el constructor utilizado al materializarla sería el constructor #3, puesto que recibe más servicios que #1 y #2, pero el #4 es descartado al recibir un parámetro cuyo nombre no coincide con el de una propiedad:
public class Friend
{
public int Id { get; set; }
public string Name { get; set; }

// Constructor #1
public Friend() { }

// Constructor #2
public Friend(int id, string name)
{
Id = id;
Name = name;
}

// Constructor #3
public Friend(int id, string name, MyContext ctx)
{
Id = id;
Name = name;
}

// Constructor #4
public Friend(int id, string fullName, MyContext ctx, IEntityType entityType)
{
Id = id;
Name = fullName;
}
}
¡Y hasta aquí hemos llegado! Espero que esta característica os resulte interesante para sacarle mayor partido a Entity Framework Core en vuestros proyectos :)

Publicado en Variable not found.

» Leer más, comentarios, etc...

Blog Bitix

Rediseño de Blog Bitix con contenido centrado, más grande horizontalmente y publicidad lateral sticky

mayo 13, 2019 06:00

He cambiado ligeramente el diseño del blog con la intención de mejorar la experiencia de usuario haciendo que el contenido quede centrado en la pantalla en vez de estar desplazado un poco a la izquierda por un panel lateral. Pero más espacio horizontal para el contenido podría afectar negativamente al rendmiento de los banners de publicidad laterales si tenía que quitarlos con lo que he tenido que buscar una solución para ambos requerimientos. También he experimentado con la disposición de la publicidad, incluido un billboard y publicidad lateral sticky para que permanezca más tiempo visible y quizá mejorar su rendmiento.

Hugo

En el último diseño significativo que hice en el 2015 cambié a un tema más claro, la posición de varios banners de publicidad además de quitar algunas secciones, otros cambios de menor importancia han sido quitar el justificado del texto o la posición del título en la cabecera, además de generar de forma estática el contenido del blog con Hugo en vez de Octopress.

Hacía tiempo que como mejora para el diseño del blog quería que el contenido de los artículos estuviese centrado en la pantalla y tuviese más espacio horizontal, ambas cosas porque considero que mejoraría un poco la experiencia de lectura de los usuarios. Un panel a la derecha que ocupaba cierto espacio hacía que el contenido del artículo no estuviese centrado y quedase escorado un poco hacia la izquierda, además en ciertos artículos donde incluyo código por el espacio ocupaban requería de una barra de desplazamiento horizontal para ver la parte derecha de los listados.

Este deseo me obligaba a quitar el panel lateral donde tenía un banner de publicidad fijo estilo large-skycraper de 300px de ancho por 600px de alto junto a otro debajo large-rectangle de 336px por 280px, una imagen con un enlace de Yo apoyo al software libre, tu también con los artículos de donaciones que he hecho y un enlace al Archivo y hemeroteca. Pero perder esos banners de publicidad para dejar más espacio al contenido posiblemente afectaría al rendimiento de los anuncios.

Querer que el contenido estuviese centrado y ocupase más espacio horizontal y querer publicidad lateral competían entre sí así que tenía que buscar una solución que cumpliese ambos.

Diseño de Blog Bitix antes de hacer cambios

Hacer que el contenido estuviese centrado y ocupase más espacio horizontal obligaba poner la publicidad lateral más a la derecha, fuera del espacio del contenido central. Como mejora para esa publicidad lateral también quería que permaneciese visible aún haciendo desplazamiento vertical, ya que antes la publicidad siempre permanecía en la misma posición al inicio del artículo y se dejaba de ver en la parte inferior del artículo posiblemente quedando desaprovechada alguna oportunidad. Para hacer que la publicidad se desplace verticalmente he usado el posicionamiento sticky que ya soportan los navegadores. Con los primeros cambios el diseño queda como deseaba.

Primer diseño de Blog Bitix después de hacer algunos cambios

Sin embargo, hacer que el contenido ocupe todo el espacio horizontal de lo que tenía anteriormente y poner la publicidad más a la derecha requiere que los usuarios tengan una pantalla con suficientemente resolución para que quepa todo. Yo tengo una pantalla de resolución 2560x1440 y lo veía todo bien pero Google Analytics me indicaba que un porcentaje importante del los usuarios, un 32%, tienen una resolución habitual en los portátiles de 1366x768 píxeles. Con esa resolución los 1140 píxeles requeridos para el contenido más los 300 del banner horizontal en la parte derecha no entraba. También agravado si para algunas páginas deseaba en la parte lateral izquierda otro panel sticky de publicidad.

Mantener la publicidad lateral sticky hace que no entre un skycraper y un large-rectangle verticalmente en una resolución de 768px de alto de modo que he dejado solo un espacio para publicidad. Como idea para el futuro quizá haga que al llegar a cierto desplazamiento vertical se cambier el espacio de publicidad por otro.

Resolución de pantalla de los usuarios

Ocupando 1140 píxeles el contenido poco espacio queda en los laterales en una resolución de 1366 de ancho. La solución que he aplicado para poder poner publicidad en ambos laterales es reducir un poco el ancho para el contenido central, a 1080px, y que la publicidad lateral se adapte al espacio que queda, no entran banners de 300px en los laterales pero si dos skycraper de 120px de ancho al menos, con la que en buena medida la experiencia de usuario se mantiene igual que en resoluciones mayores.

La página es ahora un poco menos ancha que antes 1080px frente a 1140px pero el contenido ocupa más espacio horizontal, 1080px frente a unos 840px. Haciendo que la publicidad lateral se adapte al espacio restante según la resolución que quede me ha permitido cumplir los dos objetivos de hacer que el contenido quede centrado y con más espacio horizontal sin perder la publicidad lateral y haciendo que esta permanezca visible en la pantalla aún con desplazamiento vertical y en ambos laterales en algunas páginas.

En resoluciones menores de 1366 he optado por quitar completamente la publicidad lateral ya que aún aplicando un diseño adaptativo en cualquier caso no aparecería en los laterales porque físicamente no hay espacio suficiente.

Así queda en las resoluciones habituales de 2560 píxeles, 1920, 1600, 1440, 1366 y 1200.

Diseño después de los cambios a diferentes resoluciones (2560, 1920, 1600, 1440, 1366 y 1200)

Padría hacer algún experimento o test a/b de que diseño resulta mejor si el anterior o el contenido más ancho y medirlo por el porcentaje de rebote, tiempo medio de permanencia en la página o retorno de usuarios pero dudo que esas métricas fuesen suficientemente buenas como para determinar que un diseño sea mejor que otro, es dedicar un tiempo y esfuerzo a medir algo que considero claramente es mejor. En lo que si podría hacer algún experimento es medir que disposición de anuncios, en ubicación y tamaño da mejor resultado pero prefiero dedicar el tiempo a escribir artículos, no creo que los cambios que he hecho en los anuncios impacten muy negativamente o quiza se compense con la posición sticky de los banners laterales.

También he introducirdo un banner billboard en la cabecera de la página a ver que tal resultado da, el leaderboard del inicio de los artículos tenía buen rendimiento aún siendo bastante pequeño, lo he mantenido en los artículos que incluyo un resumen. En los que no tienen un resumen lo he sustituido por el billboard ya que mantener ambos me resutlaba un exceso de publicidad. Dentro de unos meses compararé con los anteriores o del año pasado si los cambios que he hecho dan buen resultado.

» Leer más, comentarios, etc...

Koalite

No corrijas ese bug

mayo 13, 2019 05:06

Hace casi 20 años Joel Spolsky escribía lo que se conoce como el test de Joel, 12 preguntas que (supuestamente) deberías aspirar a responder afirmativamente para mejorar tu proceso de desarrollo. Para tener 20 años han aguantado relativamente bien, y el hecho de que Stack Overflow lo use en las ofertas de trabajo (seguro que el hecho de que Joel sea fundador no tiene nada que ver) hace que sean conocidillas.

La quinta de ellas dice algo así como:

¿Arreglas los bugs antes de escribir código nuevo?

Suena a perogrullo. ¿Cómo no vas a arreglar los bugs cuando los encuentras? ¿Qué vas a hacer si no, dejarlos sin resolver? ¿Permitir que se acumulen hasta que destruyan tu precioso software? Además, todo el mundo sabe que resolver un bug suele ser más fácil (y por tanto más barato) cuanto menos tiempo pasa desde su introducción, así que el mejor momento para corregirlo es tan pronto como se detecta; idealmente durante el propio proceso de desarrollo.

Si estuviera todo tan claro no estaría escribiendo este post. Ya me conocéis.

No solo no siempre es necesario corregir los bugs según se encuentran, sino que a veces no merece la pena corregir ciertos bugs.

No todos los bugs iguales

Estás trabajando en una nueva funcionalidad. Por ser originales, el típico carrito de la compra de una tienda online. Mientras la implementas descubres que al sumar el precio de las productos se te ha olvidado tener en cuenta la cantidad, así que si alguien compra varias unidades de un mismo producto el cálculo del total falla. ¿Lo corriges ahora o esperas? Ahora, sin dudarlo.

Llegas a la siguiente funcionalidad. Generar un PDF con la factura del carrito de antes. Mientras haces pruebas descubres que la librería que usas para generar el PDF falla cuando el nombre del cliente tiene caracteres coreanos. Y tú no has vendido a un coreano en tu vida, Hulio. ¿Lo corriges ahora o esperas? Pues si corregirlo implica cambiar la librería para generar PDFs y ajustar todo tu código, lo más razonable es pasar del tema, apuntar el bug en algún sitio para que soporte conozca esa limitación, y esperar a que el señor 동현 se queje.

Resulta que algunos clientes se están quejando porque en su resumen de facturación el desglose de impuestos no siempre es correcto. ¿Lo corriges o esperas? Teniendo en cuenta las implicaciones legales, parece necesario corregirlo rápidamente.

Te llega un correo de un cliente que no ha recibido su mercancía. Tras analizarlo, descubres que una terrible coincidencia hizo que una caída de un proveedor de servicios externos, unida a un fallo en el envío de un email, y la ausencia por vacaciones del responsable de almacén se encuentran detrás del problema. ¿Montas un sistema resistente a este tipo de coincidencias o lo dejas como está? Si contruir un sistema tolerante a fallos require cambiar la arquitectura de toda la aplicación y esto sólo te ha pasado una vez en 500.000 pedidos, es probable que puedas asumir el fallo y resolverlo manualmente cuando se produzca.

Mientras revisas otra parte del código encuentras un caso extraño en el que se puede producir un error que impediría servir una determinada página generando un error 500 en el servidor. Compruebas los logs del servidor y ves que en los 2 años que lleva ese código en producción nunca se ha producido. ¿Merece la pena corregirlo? Depende. Técnicamente es un bug, pero si un bug no afecta a nadie, ¿realmente es un bug?

Triaje de bugs

En medicina de emergencias se utiliza la palabra triaje para referirse a la selección y clasificación de los pacientes en función de las prioridades de atención, privilegiando la posibilidad de supervivencia, de acuerdo con las necesidades terapéuticas y los recursos disponibles. Es decir, que cuando no puedes atender a todos los pacientes de forma inmediata y óptima, debes intentar salvar el máximo posible teniendo en cuenta la gravedad de sus lesiones y los recursos disponibles.

Cuando desarrollamos software disponemos de un recurso limitado, el tiempo, y debemos decidir de qué manera hemos de emplearlo para maximizar su valor. Esto implica que a veces será mejor dedicar el tiempo a implementar nuevas funcionalidades, a veces a corregir bugs existentes, a veces a pagar deuda técnica, a veces a investigar nuevas tecnologías, y otras veces a mil cosas más.

Decidir cómo repartir los recursos disponibles entre todas estas actividades es toda una ciencia (o arte, según se mire) y se escapa del objetivo de este post, pero podemos intentar ver algunas aspectos que nos permitan realizar un mejor triaje de bugs para decidir si los corregimos y cuándo los corregimos.

El coste de un bug

Cada bug que existe en un sistema tiene un coste asociado. Puede ser un coste derivado de los clientes que dejan de serlo por la existencia del bug, del tiempo dedicado por las personas de soporte para atenderlo cada vez que se produce, o del coste asociado a las acciones compensatorias que se realizan cuando ocurre, por ejempo, realizar un descuento a los clientes que, por culpa de un bug, han sufrido un retraso en su pedido, o hacer el seguimiento telefónico del envío de un pedido porque no se puede consultar en una página web.

Para comprender (e idealmente mesurar) el coste real de un bug la clave es conocer su impacto de negocio. Para ti, para tus clientes y para tus usuarios (que pueden no ser los mismos que tus clientes, nunca lo olvides).

Una primera dimensión que podemos considerar es la criticidad intrínseca del bug. No es lo mismo un bug que hace que un usuario pierda los datos que lleva introduciendo varias horas en un procesador de texto, que un bug que hace que se pierda el envío de un tweet si se pierde la conexión con el servidor de twitter. No es igual un bug que hace un usuario presente mal su declaración de impuestos y reciba una sanción administrativa por ello, que un bug que hace que las fotos de sus vacaciones aparezcan desordenadas cuando están sacadas en el mismo minuto. Esta criticidad siempre va ligada al valor de negocio de la funcionalidad afectada, por lo que sin comprender éste, es imposible valorarla.

También podemos considerar el número de usuarios afectados y la importancia relativa de esos usuarios para nuestro negocio. Como veíamos antes, no es igual un bug que afecta a todos los usuarios, que un bug que sólo afecta a aquellos usuarios con nombres coreanos si no vendemos en corea, o a los que usan cierta conjunción de funcionalidades y operativas de nuestra aplicación que sólo se da en 1 de cada 10.000 casos.

Además influye la frecuencia con que se reproduce el problema. Un bug de poca criticidad que se produce muchas veces al día y genera muchas llamadas de soporte puede suponer un coste mayor que un bug mucho más crítico que sólo se produce cuando hay eclipse de sol coincidiendo con el paso de Saturno por Sagitario.

El coste de su solución

En cuanto a lo que nos cuesta corregir el bug, hay factores más claros y otros que es más fácil pasar por alto a simple vista.

Lo primero que nos viene a la mente al pensar en el coste de corregir un bug es el tiempo que nos va a llegar. Nada sorprendente. Cuanto más tiempo de desarrollo requiera, mayor será el coste de la resolución y menos tiempo podremos destinar a otras tareas.

Otro factor a considerar es la posibilidad de introducir nuevos bugs con los cambios usados para corregir el primero. Casi puedo oir el nerviosismo de los auténticos crafters: ¡para eso están los tests! ¡Usa TDD para construir una red de seguridad contra regresiones!.

Sí, es verdad. Cuanto mejor sea tu proceso de pruebas y control de calidad (lo que suele implicar tener automatizado buena parte del mismo), menos riesgo corres de introducir bugs al cambiar código para corregir otros. Desgraciadamente, no todas las aplicaciones cuentan con tests de la calidad que nos gustará, y no todas las áreas de la aplicación se pueden cubrir con tests fiables. Por ejemplo, el interfaz de usuario o la integración con dispositivos externos son partes en las que, por muchos tests que tengas, es difícil estar seguro de que un cambio no rompe nada sin hacer pruebas manuales, y según se van complicando las operativas existentes en una aplicación, saber qué pruebas manuales hay que hacer y realizarlas correctamente no es fácil.

Existe otro factor que se tiende a despreciar: la complejidad que introduce la corrección. Aun asumiendo que puedas corregir el bug y que estés razonablemente seguro de que no rompes nada al hacerlo, ¿cuánto se va a complicar el mantenimiento posterior de la aplicación?

Hay ocasiones en que para corregir un bug necesitas hacer cambios importantes en el diseño o en la arquitectura del sistema. Esos cambios pueden convertir un diseño simple en algo mucho más complejo capaz de cubrir los escenarios en los que se produce el bug, y al realizarlos estamos comprometiendo el desarrollo futuro introduciendo fragilidad en el sistema e incrementando el coste de la evolución.

Hacer una valoración cuantitativa de todos estos factores no es trivial, y menos en un momento en que se tiene cierta aversión a las estimaciones y parece que nadie quiere mojarse en nada. Aun así, es importante analizar cada caso y usar una política de decisión algo mejor que una cola FIFO o cuál es el cliente o comercial que más chilla.

Una cuestión económica

Al final, como en casi todas las actividades humanas, decidir si hay que corregir o no un bug es una cuestión puramente económica. Y por económica no quiero decir necesariamente de euros, sino de relación coste/beneficio.

A muchos desarrolladores nos cuesta asumir cuando algo es suficientemente bueno. Tendemos a buscar la «perfección» de nuestro código y nuestras aplicaciones (a veces sólo de las partes que a las que hemos decidido darles más importancia porque nos parecen más divertidas). Nos pone nerviosos asumir deuda técnica.

Sin embargo, hay ocasiones en que lo mejor que podemos hacer con un bug es anotarlo para saber que existe y poder monitorizar su incidencia a lo largo del tiempo y olvidarnos de él por el momento. Igual que alcanzar un 100% de cobertura de código con los tests no suele ser rentable, mantener el sistema 100% libre de bugs tampoco suele serlo. Claro, que esto no vale hacerlo para todos los bugs y que nuestros clientes descubran qué es lo peor de desarrollar software.

No hay posts relacionados.

» Leer más, comentarios, etc...

Israel Perales

Sonarqube

mayo 11, 2019 11:29

Sonarqube

Después de escuchar mucho sobre la agilidad y medir la calidad del código en muchos blogs, comencé a preguntar a amigos como revisaban sus proyectos.

La mayoría no lo hacían por que en sus empresas tienen un departamento de 'QA', a lo que yo les recomendé usar FindBugs , un plugin sencillo para Eclipse que conocí allí por el 2014 y que me salvo de muchas.

Al instalarlo y correrlo por primera vez me di cuenta de la falta que me hacia, ¿solo se media la calidad por los bugs?, ¿no había otra cosa que medir? , cuanto mas ví el termino deuda técnica en los blogs, sentí que algo estaba mal en mi código.

Creo que fue en este post donde encontré algo llamado Sonar.

Su nombre completo es Sonarqube, es un Software Open Source que te ayuda a medir la calidad del código, donde combina FindBugs con otros proyectos para medir prácticamente TODO y te lo da en una amigable interfaz web a modo de reportes.

¿Como se instala?

Al ser una aplicación cliente/servidor se necesita una base de datos SQL Server / Oracle / PostgreSQL y debemos blablabla ...
Puff! las instalaciones son tediosas, puedes irte a la antigua instalando base de datos desde 0 , pero en estos años, ¿quien no tiene Docker instalado en su maquina ?.

Instala Docker y docker-compose

Una vez hecho esto, debemos crear un directorio llamado Sonarqube y el archivo docker-compose.yml

version: "2"

services:

  sonarqube:
    image: sonarqube:latest
    ports:
      - "9000:9000"
    networks:
      - sonarnett
    environment:
      - SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar
    volumes:
      - sonarqube_conf:/opt/sonarqube/conf
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_bundled-plugins:/opt/sonarqube/lib/bundled-plugins

  db:
    image: postgres:9.6
    mem_limit: 256m
    networks:
      - sonarnett
    environment:
      - POSTGRES_USER=sonar
      - POSTGRES_PASSWORD=sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data

networks:
  sonarnett:
    driver: bridge
  default:
    driver: bridge

volumes:
  sonarqube_conf:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_bundled-plugins:
  postgresql:
  postgresql_data:

Así se ve la salida de tree:

perales@linux-h4ne:~/Escritorio> tree sonarqube/
sonarqube/
└── docker-compose.yml

0 directories, 1 file
perales@linux-h4ne:~/Escritorio> 

Y ejecutamos docker-compose up

Veremos algo así:

Creating network "sonarqube_sonarnett" with driver "bridge"
Creating volume "sonarqube_sonarqube_conf" with default driver
Creating volume "sonarqube_sonarqube_data" with default driver
Creating volume "sonarqube_sonarqube_extensions" with default driver
Creating volume "sonarqube_sonarqube_bundled-plugins" with default driver
Creating volume "sonarqube_postgresql" with default driver
Creating volume "sonarqube_postgresql_data" with default driver
Pulling sonarqube (sonarqube:latest)...
latest: Pulling from library/sonarqube
e79bb959ec00: Pull complete
d4b7902036fe: Pull complete
1b2a72d4e030: Pull complete
d54db43011fd: Pull complete
1a97c78dad71: Pull complete
6dcb79eeeda4: Pull complete
bd56246cf4fd: Pull complete
88cea60f56c5: Pull complete
7ed4501359c8: Pull complete
4bbf191118e2: Pull complete
11032ed7b930: Pull complete
c59744850b29: Pull complete
Digest: sha256:7e3086215d86430bb1bde555d755605e351f59ed7fa721aaff0d7478b665086a
Status: Downloaded newer image for sonarqube:latest
Pulling db (postgres:9.6)...
9.6: Pulling from library/postgres
27833a3ba0a5: Already exists
ed00742830a6: Already exists
dc611c2aceba: Already exists
a61becab5279: Already exists
8dcff41e7aea: Already exists
820bf1bbf0d7: Already exists
050804429905: Already exists
782c81275334: Already exists
89de6f9e489b: Pull complete
6fd7948190c1: Pull complete
2b248dfe0f9f: Pull complete
2bb82f5734a9: Pull complete
6165fd6c366d: Pull complete
b91f3b4e0a77: Pull complete
Digest: sha256:ee723e25ac72edaede1c7209c33f295b56875bdee62fa08f913117c2fbca9287
Status: Downloaded newer image for postgres:9.6
Creating sonarqube_db_1        ... done
Creating sonarqube_sonarqube_1 ... done
Attaching to sonarqube_db_1, sonarqube_sonarqube_1

Al terminar el log de la consola entramos a la liga http://localhost:9000 y podremos ver la interfaz web.

Para loguearnos usamos admin, admin.

Sonarqube

Sonarqube

Sonarqube

¿Como se usa?

Sonar esta vació, ¿como lo usamos?, la dinámica a seguir es la siguiente:

  • Tenemos un código fuente.

  • Agregamos un archivo de configuración de sonar(sonar-project.properties).

  • Generamos un token de autentificación y lo agregamos a la configuración.

  • Ejecutamos el analizador de código de nuestra preferencia.

Analicemos un proyecto de ejemplo:

Clonamos el repo

git clone https://github.com/ripper2hl/fuego.git

Agregamos el archivo sonar-project.properties

cd fuego/

touch sonar-project.properties

Aquí la documentación de como configurar lo

https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner

Así debería de quedarnos:

sonar.projectName=fuego
sonar.projectKey=com.perales:fuego
sonar.host.url=http://localhost:9000
sonar.sources=src/
sonar.sourceEncoding=UTF-8

Para generar el token debemos ir a My Account que se encuentra en la parte superior derecha y después a la pestaña de Security, esta es la liga http://localhost:9000/account/security/ .

El token se agrega al archivo de propiedades como sonar.login

sonar.projectName=fuego
sonar.projectKey=com.perales:fuego
sonar.host.url=http://localhost:9000
sonar.sources=src/
sonar.sourceEncoding=UTF-8
sonar.login=1d5a5466cec2a33ead727a2082454e25b9cdef49

Ahora debemos ejecutar un Sonar Scanner , de los cuales existen diferentes tipos, para java existe uno que se combina con maven , pero nosotros usaremos un binario de propósito general, el cual funciona para cualquier lenguaje de programación.

En esta liga puedes descargar los binarios, GNU/Linux, Mac OS o Windows

https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner

En GNU/Linux creamos un enlace simbólico para poder ejecutarlo desde terminal

ln -s ~/Descargas/sonar-scanner-3.3.0.1492-linux/bin/sonar-scanner ~/.local/bin/sonar-scanner

Si ejecutamos sonar-scanner --version nos debe mostrar esto:

sonar-scanner --version
INFO: Scanner configuration file: /home/perales/Descargas/sonar-scanner-3.3.0.1492-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: NONE
INFO: SonarQube Scanner 3.3.0.1492
INFO: Java 1.8.0_121 Oracle Corporation (64-bit)
INFO: Linux 5.0.11-1-default amd64

Para analizar nuestro proyecto , solamente ejecutamos sonar-scanner en la raíz del proyecto y la terminal mostrara lo siguiente:

INFO: Scanner configuration file: /home/perales/Descargas/sonar-scanner-3.3.0.1492-linux/conf/sonar-scanner.properties
INFO: Project root configuration file: /home/perales/Documentos/git/fuego/sonar-project.properties
INFO: SonarQube Scanner 3.3.0.1492
INFO: Java 1.8.0_121 Oracle Corporation (64-bit)
INFO: Linux 5.0.11-1-default amd64
INFO: User cache: /home/perales/.sonar/cache
INFO: SonarQube server 7.7.0
INFO: Default locale: "es_MX", source code encoding: "UTF-8"
INFO: Load global settings
INFO: Load global settings (done) | time=275ms
INFO: Server id: 243B8A4D-AWqQ27WJuNHxbkg6tnXs
INFO: User cache: /home/perales/.sonar/cache
INFO: Load/download plugins
INFO: Load plugins index
INFO: Load plugins index (done) | time=125ms
INFO: Load/download plugins (done) | time=4077ms
INFO: Process project properties
INFO: Execute project builders
INFO: Execute project builders (done) | time=34ms
INFO: Project key: com.perales:fuego
INFO: Base dir: /home/perales/Documentos/git/fuego
INFO: Working dir: /home/perales/Documentos/git/fuego/.scannerwork
INFO: Load project settings for component key: 'com.perales:fuego'
INFO: Load project repositories
INFO: Load project repositories (done) | time=32ms
INFO: Load quality profiles
INFO: Load quality profiles (done) | time=305ms
INFO: Load active rules
INFO: Load active rules (done) | time=6580ms
INFO: Indexing files...
INFO: Project configuration:
INFO: 6 files indexed
INFO: 0 files ignored because of scm ignore settings
INFO: Quality profile for js: Sonar way
INFO: Quality profile for web: Sonar way
INFO: ------------- Run sensors on module fuego
INFO: Load metrics repository
INFO: Load metrics repository (done) | time=200ms
INFO: Sensor JaCoCo XML Report Importer [jacoco]
INFO: Sensor JaCoCo XML Report Importer [jacoco] (done) | time=11ms
INFO: Sensor SonarJS [javascript]
INFO: 3 source files to be analyzed
INFO: 3/3 source files have been analyzed
INFO: Sensor SonarJS [javascript] (done) | time=2646ms
INFO: Sensor ESLint-based SonarJS [javascript]
INFO: 3 source files to be analyzed
INFO: Sensor ESLint-based SonarJS [javascript] (done) | time=9792ms
INFO: Sensor JavaXmlSensor [java]
INFO: 3/3 source files have been analyzed
INFO: Sensor JavaXmlSensor [java] (done) | time=20ms
INFO: Sensor HTML [web]
INFO: Sensor HTML [web] (done) | time=395ms
INFO: ------------- Run sensors on project
INFO: Sensor Zero Coverage Sensor
INFO: Sensor Zero Coverage Sensor (done) | time=115ms
INFO: SCM provider for this project is: git
INFO: 4 files to be analyzed
INFO: 4/4 files analyzed
INFO: Calculating CPD for 4 files
INFO: CPD calculation finished
INFO: Analysis report generated in 451ms, dir size=91 KB
INFO: Analysis report compressed in 165ms, zip size=21 KB
INFO: Analysis report uploaded in 1700ms
INFO: ANALYSIS SUCCESSFUL, you can browse http://localhost:9000/dashboard?id=com.perales%3Afuego
INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report
INFO: More about the report processing at http://localhost:9000/api/ce/task?id=AWqie1fuYKZYNF3HzZ-O
INFO: Analysis total time: 36.800 s
INFO: ------------------------------------------------------------------------
INFO: EXECUTION SUCCESS
INFO: ------------------------------------------------------------------------
INFO: Total time: 44.311s
INFO: Final Memory: 18M/291M
INFO: ------------------------------------------------------------------------

Si vamos a la interfaz web, podremos ver los reportes generados.

Sonarqube

Sonarqube

Sonarqube

Sonarqube

Fuentes:

» Leer más, comentarios, etc...

Blog Bitix

Crear de forma sencilla y rápida máquinas virtuales de VirtualBox con Vagrant

mayo 10, 2019 03:00

Vagrant
HashiCorp

VirtualBox es una de las herramientas que permiten virtualizar un sistema operativo completo y sus aplicaciones dentro de otra máquina. Como es un sistema operativo completo requiere que el sistema que la alberga tenga RAM suficiente para sí mismo y RAM suficiente para el sistema virtualizado, se puede configurar la cantidad de RAM y almacenamiento persistente de la máquina virtual. En el proceso de virtualización se pierde algo de rendimiento por la sobrecarga que añade virtualizar un sistema operativo completo, los procesadores modernos ofrecen soporte para que el rendimiento sea lo mayor posible pero no es igual a ejecutar el sistema de forma nativa en el sistema, sobre todo en el aspecto de interfaces gráficas y aceleración 2D y 3D.

La virtualización es una buena forma de probar una distribución GNU/Linux para evaluarla o ejecutar Windows en un Linux. Hay otras herramientas de virtualización como QEMU y KVM pero la virtud de VirtualBox es que es muy sencilla y está disponible para Windows, GNU/Linux y macOS.

VirtualBox

Para tener una máquina virtual el proceso se puede hacer desde el principio desde el medio de instalación ofrecido siguiendo los de su instalador. Pero para hacer alguna prueba de desarrollo y si se necesitan virtualizar varias máquinas el proceso manual es incómodo además de repetitivo. Vagrant es una de las herramientas ofrecidas por HashiCorp que permite automatizar la creación y aprovisionamiento de máquinas virtuales en VirtualBox mediante la especificación de un archivo de configuración. Permite replicar entornos y crear un cluster de máquinas que resulta muy útil al desarrollar o probar cierto software.

En este ejemplo se configura una máquina virtual usando como sistema operativo base Ubuntu 18.04, y se aprovisiona configurando ella Docker. El aprovisionamiento se realiza mediante una serie de comandos y archivos que se añaden del host al sistema virtualizado tal como se hace en un sistema Ubuntu desde su estado de instalación inicial. Entre las opciones de configuración permitidas están el nombre de la máquina virtual, su sistema operativo, la cantidad de memoria que se le asigna, propiedades de red, asignar direcciones IP estáticas, …

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.define "ubuntu-docker" do |instance|
instance.vm.box = "ubuntu/bionic64"
instance.vm.provider :virtualbox do |vb|
vb.name = "Ubuntu Docker (Vagrant)"
end
instance.vm.network "private_network", ip: "192.168.33.10"
instance.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
instance.vm.provision "file", source: "docker-compose.yml", destination: "/home/vagrant/docker-compose.yml"
instance.vm.provision "shell", inline: $docker_role_script
end
end
$docker_role_script = <<-SCRIPT
echo "Updating..."
sudo apt-get update
sudo apt-get upgrade
echo "Installing docker..."
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
 sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker vagrant
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
echo "Starting Docker service..."
sudo systemctl daemon-reload
sudo systemctl enable docker.service
sudo systemctl start docker.service
SCRIPT
end

Se puede crear un archivo inicia con comentarios para empezar a configurar la máquina virtual.

1
2
$ vagrant init ubuntu/bionic64

Definido el archivo de configuración para Vagrant se inician las máquina virtual con un comando. Y se detienen con otro. Si hay necesidad en el mismo archivo se pueden definir varias máquinas virtuales.

1
2
$ vagrant up
$ vagrant halt
VirtualBox Vagrant VM

Una vez iniciada la máquina virtual Vagrant configura SSH para tener acceso a su terminal, hay que especificar el nombre de la máquina virtual.

1
2
$ vagrant ssh ubuntu-docker
Vagrant SSH

La máquina en el ejemplo ha sido aprovisionada con Docker mediante un script con los comandos para instalarlo y un archivo de Docker Compose con un servicio del servidor web nginx. Desde la terminal de la máquina virtual se inicia el servicio con Docker que queda accesible tanto desde la pripia máquina virtual como desde el host indicando la dirección IP que se le ha asignado.

docker-compose up y curl desde la MV y desde el host

Vagrant tiene un repositorio de imágenes entre las que elegir para el sistema, están las más populares como Ubuntu, Fedora, Debian y CentOS. Es un repositorio en donde los usuarios pueden subir sus propias imágenes aunque por defecto es mejor usar las oficiales de cada sistema.

Posee varias páginas de documentación bastante completas donde conocer los todos los detalles de uso de Vagrant.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando vagrant up.

» Leer más, comentarios, etc...

Coding Potions

Tutorial SASS. Todo sobre el preprocesador CSS

mayo 09, 2019 12:00

Introducción ¿Alguna vez has sentido que escribes demasiado código CSS? Pues estás de suerte, porque con sass te vas a ahorrar mucho código. Sass lo que hace es compilar lo que escribes a código css que pueda entender el navegador. Es cierto que cada vez están implementando nuevas funcionalidades a css, pero antiguamente no tenía tantas y LESS y posteriormente SASS vinieron para resolver este problema. Incluso en los tiempos que corren SASS sigue siendo una buena ida para usar junto a cualquier framework.

» Leer más, comentarios, etc...

Variable not found

Triscaidecafílicos del mundo, regocijaos: ¡13 años de Variable Not Found!

mayo 08, 2019 07:52

La triscaidecafobia es el miedo irracional al número trece, muy asociado a supersticiones y creencias arraigadas en distintas épocas y culturas, como la egipcia, vikinga, el propio cristianismo, o incluso las órdenes templarias. Por contraposición, la triscaidecafilia sería justamente lo contrario: el gusto excesivo por todo lo relacionado con dicho número.

Y hasta aquí la píldora cultural del día, ahora vamos a lo importante :D

Hoy Variable not found cumple trece años, una auténtica barbaridad. Por supuesto, mucho más de lo que habría podido esperar en aquél remoto 2006, cuando gritaba al maquinista de este tren llamado blogosfera (suena viejuno, ¿eh?) para que me dejara subir a él. Quizás en ese momento, desde el andén, no habría apostado demasiado por un viaje de más allá del par de años.

Y aquí seguimos: más de 1.100 entradas, algo más de 1.000 comentarios y, según Blogger, acercándonos a los cuatro millones de páginas servidas. Pero lo mejor, que aún seguimos con muchas, muchas ganas de seguir aportando granitos de arena a la comunidad, compartiendo lo que vamos aprendiendo cada día.

La salud del blog: cómo fue el año pasado

Pues como suele decirse, no nos podemos quejar, aunque con nuestros achaques ;D

Ya el año anterior noté un paulatino descenso del número de visitas, cosa que atribuí a penalizaciones por la ausencia de HTTPS en el blog, y durante gran parte del pasado año hemos seguido en la misma tónica. Afortunadamente, parece que desde primeros de 2019 han debido apretar algún tornillo en Google y la cosa lleva meses empezado a retomar cifras de antes de ese tropezón.

En cualquier caso, dado que soy un absoluto ignorante en temas de SEO, posicionamiento y similares, tampoco es algo que me preocupe más de la cuenta. Obviamente me alegra saber que el tiempo que dedico a esto es de utilidad para alguien, y cuantos más mejor, pero no es algo que me obsesione.

A pesar de lo dicho anteriormente, creo que aún seguimos teniendo cifras bastante buenas, para tratarse de un blog tan especializado. Desde mayo de 2018 han pasado por aquí más de 110.000 personas, visitando un total de 200.000 páginas durante un promedio de cerca de 4 minutos. La página de Facebook supera los 900 seguidores, y a través de Twitter nos siguen cerca de 2.500 amigos (sumando @jmaguilar y @variablnotfound). Muchas gracias a todos, porque sois los que le dais sentido a esta historia :)

DeveloperGoogle, que todo lo sabe, dice que la mayoría de vosotros sois hombres (80%). Curiosamente, esta cifra ha ido disminuyendo durante los últimos años en favor del sexo femenino (por ejemplo, en 2015 este dato ascendía al 90%), lo cual quiero creer que indica que la monopolización masculina en nuestro sector tiende a desaparecer, o al menos a reducirse, la larga.

La mayoría de vosotros os encontráis en un rango de edad entre 25 y 34 años, subiendo ligeramente las visitas de desarrolladores algo mayores. En cualquier caso, el 90% de las visitas tenéis menos de 45 años. Juventud, divino tesoro ;)

Como no podía ser de otra forma, arrasáis los lectores que Google clasifica como tecnófilos, profesionales del desarrollo de software, amantes del cine y videojuegos. Cerca del 80% utilizáis Chrome, seguido de lejos por un 11% que prefiere Firefox, y ya con menos del 5% encontramos Internet Explorer (¿¡todavía anda eso por ahí!?), Edge, Opera, Safari y otros.

Por goleada, la principal vía de acceso al blog es a través de búsquedas, con un 88% (obviamente, casi todo procedente de Google), un 11% entradas directas, y el resto repartidas entre redes sociales y enlaces entrantes.

Distribución por paísesRespecto a la ubicación geográfica, los amigos de México continúan su ascenso, llegando a establecerse en algo más del 25% del total de visitantes. España queda relegado al segundo puesto, con el 17%, y le siguen Colombia (10.24%), Perú (8.35%), Argentina (6.87%) y varios países más con una diferencia entre ellos del 1% (Chile, Estados Unidos, Ecuador, Costa Rica, y los hermanos de Venezuela).

Como siempre digo, y se pone claramente de manifiesto viendo los números, este espacio sigue siendo un blog humilde y con pocas pretensiones, pero eso sí,con un ambicioso objetivo: que aprendamos juntos, y pasarlo lo mejor posible durante el transcurso.

Una vez más, muchas gracias a todos por cada visita, comentario, sugerencia o contacto, porque sin vosotros esto no tendría sentido alguno.

Así que, si os parece, nos seguimos viendo por aquí un año más, buscando la variable :)

Publicado en: www.variablenotfound.com.

» Leer más, comentarios, etc...

Fixed Buffer

Cómo crear un servicio Net Core multiplataforma

mayo 07, 2019 08:00

Imagen para el post de crear servicios para .Net Core

Después de varias semanas hablando sobre las maravillas de Terraform o de Integración y Despliegue continuos (CI/CD), hoy vengo a hablaros de un problema con el que me he encontrado con un proyecto que tengo entre manos.

Por necesidades del proyecto, quiero hacer un servicio .Net Core, y poder instalarlo en cualquier plataforma. Esto no es ningún problema en Linux por ejemplo, pero para poder correr un servicio en Windows hay que cumplir con ciertos requisitos. Para .Net Framework existen múltiples herramientas que nos lo permiten, como puede ser un proyecto de instalador de servicio o utilizar Topself, pero para .Net Core ya es otra cosa…

Para que un servicio se pueda instalar y funcione en Windows, tiene que heredar de «ServiceBase«, lo que difiere un poco de cómo funciona en otras plataformas, y hace que crear un servicio multiplataforma no sea algo directo. Esto se puede solucionar fácilmente con un poco de código.

Creando nuestro servicio Net Core

Para empezar, vamos a crear una aplicación de consola .Net Core:

La imagen muestra la creación de un proyecto de consola net core

Para poder hospedar un servicio cualquiera, vamos a necesitar añadir el paquete NuGet «Microsoft.Extensions.Hosting«, y además, para poder correrlo cuando funcione en Windows, vamos a necesitar también el paquete NuGet «System.ServiceProcess.ServiceController«. Una vez que los hemos añadido, vamos a crear nuestro IHost:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace PostServicioNetCore
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var host = new HostBuilder()
                 .ConfigureHostConfiguration(configHost =>
                 {
                     //Configuración del host
                 })
                 .ConfigureAppConfiguration((hostContext, configApp) =>
                 {
                     //Configuración de la aplicacion

                 })
                .ConfigureServices((hostContext, services) =>
                {
                    //Configuración de los servicios
                    services.AddHostedService<LifetimeHostedService>();
                });
            if (!Debugger.IsAttached) //En caso de no haber debugger, lanzamos como servicio
            {
                await host.RunServiceAsync();
            }
            else
            {
                await host.RunConsoleAsync();
            }
        }
    }
}

Al igual que haríamos en un proyecto ASP NET Core en el Startup.cs y en Program.cs, podemos configurar las diferentes partes de nuestro servicio Net Core mediante los métodos:

  • «ConfigureHostConfiguration» (en Program.cs)
  • «ConfigureAppConfiguration» («Configure» en Startup.cs)
  • «ConfigureServices» (en Startup.cs)

En este caso, estamos levantando un IHost genérico al cual le vamos a inyectar la dependencia del servicio/servicios que queremos ejecutar registrándolos mediante «AddHostedService».

Para poder probar, el servicio que hemos registrado es este (tiene que implementar IHostedService):

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace PostServicioNetCore
{
    public class LifetimeHostedService : IHostedService, IDisposable
    {
        ILogger<LifetimeHostedService> logger;
        private Timer _timer;
        private string _path;
        public LifetimeHostedService(ILogger<LifetimeHostedService> logger, IHostingEnvironment hostingEnvironment)
        {
            this.logger = logger;
            _path = $"{hostingEnvironment.ContentRootPath}PostServicioCore.txt";
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            try
            {
                WriteToFile("Inicia el servicio");
                _timer = new Timer((e) => WriteTimeToFile(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
            }
            catch (Exception ex)
            {
                logger.LogError($"{ex.Message},{ex.StackTrace}");
            }
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            WriteToFile("Finaliza el servicio");
            return Task.CompletedTask;
        }

        private void WriteTimeToFile()
        {
            WriteToFile(DateTime.Now.ToString());
        }

        private void WriteToFile(string message)
        {
            if (!File.Exists(_path))
            {
                using (var sw = File.CreateText(_path))
                {
                    sw.WriteLine(message);
                }
            }
            else
            {
                using (var sw = File.AppendText(_path))
                {
                    sw.WriteLine(message);
                }
            }
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}

En él, simplemente se añaden mensajes a un fichero que se encuentra junto al binario para indicar cuando empieza, cuando finaliza y cada minuto mientras esté activo, pero aquí sería donde vamos a poner que es lo que hace nuestro servicio (consultar una cola de mensajes, lecturas a una Db, etc).

Hasta aquí, este es el mismo concepto que utilizamos en ASP NET Core para crear servicios que corran en segundo plano en una web. Creamos el servicio heredando de «IHostedService» y lo registramos desde «ConfigureServices». El problema como decía al principio, es que los servicios en Windows no funcionan así, tienen que heredar de «ServiceBase».

Preparando nuestro servicio para Windows

En primer lugar, vamos a necesitar crear nuestra clase heredada:

using Microsoft.Extensions.Hosting;
using System;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;

namespace PostServicioNetCore
{
    public class ServiceBaseLifetime : ServiceBase, IHostLifetime
    {
        private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();

        public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
        {
            ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
        }

        private IApplicationLifetime ApplicationLifetime { get; }

        public Task WaitForStartAsync(CancellationToken cancellationToken)
        {
            cancellationToken.Register(() => _delayStart.TrySetCanceled());
            ApplicationLifetime.ApplicationStopping.Register(Stop);

            new Thread(Run).Start(); //Ejecutamos la tarea en un hilo para bloquear y prevenir que IHost.StartAsync termine.
            return _delayStart.Task;
        }

        private void Run()
        {
            try
            {
                Run(this); // Bloqueamos la ejecución hasta que el servicio termine.
                _delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
            }
            catch (Exception ex)
            {
                _delayStart.TrySetException(ex);
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            Stop();
            return Task.CompletedTask;
        }

        // Se llama desde base.Run() cuando el servicio esta listo para funcionar.
        protected override void OnStart(string[] args)
        {
            _delayStart.TrySetResult(null);
            base.OnStart(args);
        }

        protected override void OnStop()
        {
            ApplicationLifetime.StopApplication();
            base.OnStop();
        }
    }
}

Sin ánimo de entrar muy en profundidad, ya que esta clase es una copia del ejemplo que ofrece Microsoft navegando entre los repositorios en GitHub, en ella básicamente se implementan los métodos propios de un servicio Windows y de la interfaz «IHostLifetime«. De este modo, la aplicación puede correr como un servicio en Windows y ser usada desde «IHost».

Esta clase tiene que estar en el contenedor de inyección de dependencias siempre que el servicio corra sobre Windows, así que vamos a añadir unos métodos de extensión que nos facilite incluirla si es necesario:

using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;

namespace PostServicioNetCore
{
    public static class ServiceBaseLifetimeHostExtensions
    {
        public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
        {
            return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
        }

        public static Task RunServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
        {
            //Si es Windows, añadimos el ServiceBaseLifetime a la inyeccion de dependencias
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(cancellationToken);
            }
            else //Sino, ejecutamos normalmente
            {
                return hostBuilder.Build().RunAsync(cancellationToken);
            }
        }
    }
}

Gracias a la condición que pusimos al arrancar el IHost para detectar si estábamos en sin el debugger, y con la condición que añadimos dentro de la extensión RunAsServiceAsync, vamos a poder ejecutar nuestro programa como un servicio para Windows, para Linux o como un programa de consola (que es ideal para depurarlo).

Por último, si queremos instalar el servicio en Windows, primero vamos a publicarlo de manera que generemos un .exe indicándole al publicar que queremos que el «Tiempo de ejecución de destino» sea una versión de Windows»

La imagen muestra como indicarle el destino de la publicación

Una vez que hemos publicado nuestro .exe, vamos a tirar de «Service Controller Commands» para instalarlo. Desde la terminal:

sc create "Servicio Core" binpath= "C:\Users\jorge\source\repos\PostServicioNetCore\PostServicioNetCore\bin\Release\netcoreapp2.2\publish\PostServicioNetCore.exe"

Por el contrario, si queremos que el servicio sea para Linux, basta con que no generemos un .exe al publicar y lo registremos normalmente (como veremos en la próxima entrada aprovechando este código).

Con esto, ya tenemos nuestro servicio Net Core multiplataforma listo para ser instalado y ejecutado tanto en Windows como en Linux, y al que solo tenemos que ir añadiéndole la funcionalidad que queramos mediante nuevas implementaciones de «IHostedService».

Como siempre, dejo el código fuente en GitHub para que puedas descargártelo y probar.

**La entrada Cómo crear un servicio Net Core multiplataforma se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 359

mayo 07, 2019 06:35

Enlaces interesantesAhí va una edición bastante cargadita, con enlaces recopilados durante las dos últimas semanas. Como siempre, espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en: www.variablenotfound.com.

» Leer más, comentarios, etc...

Blog Bitix

De un móvil Samsung Galaxy S3 a un Samsung Galaxy S6

mayo 05, 2019 09:00

He estado muy contento con un Samsung Galaxy S3 durante unos 3 o 4 años más al instalarle LineageOS 7.1 y en ningún momento no ha habido nada de lo que he necesitado hacer que no puediera por no tener capacidad, aún teniendo solo 1GB de memoria. Al abrir aplicaciones o alguna opción era un poco lento pero aceptable según mi criterio. Ahora he pasado a un Samsung Galaxy S6 que hoy día es un móvil ya viejo pero para mi suficiente. En los días que llevo usándolo estoy más que satisfecho, la mejora de rendmiento es apreciable respecto al S3. Si con el S3 ya estaba contento y tenía suficiente, con el S6 creo que será otro móvil del que guardaré un grato recuerdo.

Samsung
Android

El móvil es posiblemente el primer contacto de mucha gente nacida sobre los años 1970 en una época que su juventud la pasó sin la tecnología actual como internet, los ordenadores personales y los citados móviles que también pueden ser donde realizan la mayor parte de su actividad tecnológica. Los móviles primero llegaron como sustitutos de los teléfonos fijos para enviar y recibir llamadas incorporando la funcionalidad de mensajes de texto y de forma muy limitada conexión servicios por red no diría internet.

Sobre la década de los años 2000 se desarrollaron los primeros teléfonos inteligentes o smartphones con mayores capacidades, teclados físicos y aún pantallas pequeñas, también adquieren otras funciones como la incorporación de cámaras fotográficas o conectividad con WiFi y conexión a internet través de redes móviles. La siguiente tendencia ha sido incorporar pantallas táctiles cada vez más grandes, mejores cámaras fotográficas y otros sensores como GPS para ubicación, NFC para pagos por proximidad o de huellas. En realidad se han convertido en un dispositivo muy versátil con una capacidad de cómputo y memoria significativa que rivaliza con algunos portátiles, tanto que incluso para muchas personas hace innecesario tener un ordenador personal portátil o de escritorio y para otras personas puede haberlo incluso sustituido.

En mi caso el móvil me sigue pareciendo un dispositivo incómodo para trabajar sobre todo para escribir texto y sigo utilizando en mucha mayor medida un ordenador de escritorio. Aunque hay algunos intentos como el Samsung Galaxy DeX u otro aún no se ha masificado el baticinio que hacía en La siguiente disrupción tecnológica, la convergencia en el 2016 como elemento que puede convertir el móvil en un ordenador personal. Mis necesidades no son tales como para que el móvil pueda sustituir el ordenador personal pero ya he pasado por varios móviles y hoy ha llegado el momento de buscar un susituto al Samsung Galaxy S3 que me ha servido perfectamente y con el que he estado muy contento más aún después de instalarle LineageOS 14.1 para tener Android 7.1 (Nougat) desde su original 4.0 (Ice Cream Sandwich) actualizado a 4.3 (Jelly Bean) y 4.4 (KitKat).

He tenido varios móviles empezando por un Motorola C350 y pasando por Nokia 6630, HTC Wildfire CDMA, Motorola Defy, el citado Samsung Galaxy S3 lanzado en el 2012 hasta que he vuelto a cambiar. Realmente el único móvil que he comprado fue el primero el resto los he ido heredando de los descartes de otras personas y el Samsung Galaxy S3 es un móvil con el que he hecho todo lo que he necesitado. Principalmente usar GMail, Twitter, mensajería con WhatsApp, ocasionalmente Firefox para el móvil y algún juego como Clash Royale. En buenas condiciones de luz realizando un enfoque adecuado con sus 8 megapixeles de cámara principal es capaz de tomar buenas fotografías y sacar buenos vídeos.

Móviles que he tenido

Pero el Galaxy S3 también ya me estaba dando algunos síntomas de agotamiento, en modo espera me aguanta un día pero con un uso algo más intensivo la batería baja rápidamente y requiere alguna carga más de una carga al día. Algunas aplicaciones como GMail y Twitter se nota que el procesador Exynos 4412 quad-core que tiene cumple pero no va sobrado para la época actual. En total el S3 habrá tenido 7 años de vida, menos lo que no sería mucho si no lo hubiese dado un segundo uso y un tercero con LineageOS.

Como opciones candidatas he estado barajando un Samsung Galaxy A50, Samsung Galaxy A70, Xiaomi Mi 9 y Xiaomi Mi 9 SE. Cualquiera de ellos unos pedazo móviles en el rango de los 300-450€ que no tienen mucho que envidiar a otros de gama alta pero que cuestan el doble o más.

Sin embargo, en vez de comprar uno de estos anteriores nuevos como en ocasiones anteriores he tenido la posibilidad de heredar otro móvil, en este caso un Samsung Galaxy S6 lanzado en el 2015 que para haber pasado 4 años a día de hoy y por las especificaciones que tiene ha resistido el paso del tiempo bastante bien creo para los móviles del 2019 de gama media-baja. Y es que es un móvil de gama alta de su época al igual que lo fue el S3 en su momento. En primer contacto con él se nota una apreciable fluidez al abrir aplicaciones comparado con el Samsung Galaxy S3.

  • Actualizable a Android 7.0 (Nougat).
  • Pantalla 5.1”, Super AMOLED, 1440 x 2560 pixels, 577 ppi.
  • Procesador Exynos 7420 Octa (14 nm), CPU Octa-core (4x2.1 GHz Cortex-A57 & 4x1.5 GHz Cortex-A53), GPU Mali-T760MP8.
  • Memoria 3 GiB RAM, 32 GiB interna, sin microSD.
  • Cámara principal 16 MP, f/1.9, cámara delantera 5 MP, f/1.9.
  • Comunicaciones Wi-Fi 802.11 a/b/g/n/ac, Bluetooth 4.1, GPS, NFC, infrarrojos.
  • Sensores huellas en el botón de inicio, acelerómetro, giroscopio, proximidad, brújula, barómetro, frecuencia cardiaca.
  • Batería 2550mAh no extraíble, carga rápida de 15W, carga inalámbrica.
Nuevo móvil, Samsung Galaxy S6

Tiene marcos grandes, no tiene un sensor de huellas integrado en la pantalla, sin triple cámara con gran angular, ni zoom óptico y efecto desenfoque como están incorporando los móviles nuevos como los anteriores y echo de menos una tarjeta microSD para ampliarle la capacidad de almacenamiento sobre todo para añadirle mi colección de música. Pero suficiente para mi.

La tecnología avanza muy rápido cada año hay un nuevo modelo de móvil que supera al anterior, pero suelen ser mejoras graduales que hasta pasado varios años más un móvil como el Samsung Galaxy S6 después de 4 años sigue siendo un gran móvil al que le daré una segunda vida. Quizá dentro de unos años herede un Samsung Galaxy S8 o S9 y así estoy tirando millas sin necesidad real de tener que comprar un móvil. Mejor que reciclar es reutilizar.

» Leer más, comentarios, etc...

Blog Bitix

Autenticación mediante clave pública y privada con SSH

mayo 03, 2019 06:00

GNU
Linux

Una de las formas más comunes de autenticar a un usuario, comprobar que un usuario es quien dice ser, es mediante un usuario y contraseña. Contraseña que (en principio) solo conoce el usuario por lo que si este proporciona la correcta se determina que es quien dice ser. Sin embargo, las contraseñas son débiles si no incluyen letras en mayúsculas y minúsculas, números y símbolos pero también difíciles de recordar agravado porque en cada servicio se debería usar una diferente para impedir que ante el descubrimiento de una no sea posible acceder a todos los otros servicios donde se usase la misma.

Para generar contraseñas fuertes se puede usar Strong Password Generator y para almacenarlas el programa KeePassXC.

Con OpenSSH también se puede iniciar sesión de línea de comandos en otro sistema proporcionando un usuario y una contraseña que se solicitará. O mejor aún con una clave pública y privada sin necesidad de introducir una contraseña.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ ssh ubuntu@192.168.33.10
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-48-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Fri May 3 18:04:48 UTC 2019
System load: 0.0 Processes: 102
Usage of /: 10.0% of 9.63GB Users logged in: 1
Memory usage: 6% IP address for enp0s3: 10.0.2.15
Swap usage: 0% IP address for enp0s8: 192.168.33.10
0 packages can be updated.
0 updates are security updates.
Last login: Fri May 3 18:04:00 2019 from 192.168.33.1
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
ubuntu@ubuntu-bionic:~$

Usar SSH es más seguro y más cómodo que usar contraseñas para realizar la autenticación, es más seguro ya que una clave pública y privada son más largas y es más cómodo ya que no se solicita contraseña constantemente que de otra manera al cabo de un tiempo se convierte en un paso tedioso y molesto. Lo primero que hay que hacer es generar una clave SSH pública y privada propia. Una clave de 2048 bits ya se considera segura pero como cuesta lo mismo generar una de más bits se puede generar una de 8192 bits para más seguridad.

1
2
$ openssl genrsa -out ubuntu 8192
$ ssh-keygen -y -f ubuntu > ubuntu.pub

En el servidor SSH se deben modificar algunas propiedades de configuración del archivo de configuración /etc/ssh/sshd_config para permitir la autenticación con usuario y contraseña para poder copiar la clave pública y una vez copiada la clave para mayor seguridad no permitir la autenticación mediante usuario y contraseña.

1
PasswordAuthentication yes

Una vez generado el par de claves hay que copiar la clave pública al servidor donde se desee iniciar sesión. Manualmente concatenando la clave pública al archivo _.ssh/authorizedkeys del directorio home del usuario con el que se quiere iniciar sesión o también se puede copiar la clave pública usando el comando ssh-copy-id. Para revocar el acceso mediante esa clave basta con eliminar su linea del archivo de claves autorizadas.

1
$ ssh-copy-id -i ~/.ssh/ubuntu ubuntu@192.168.33.10
1
$ sudo passwd ubuntu

Si se poseen varios pares de claves públicas y privadas se puede especificar que clave privada usar para cada máquina a la que se desee conectar en el archivo ~/.ssh/config.

1
2
3
4
Host 192.168.33.10
HostName 192.168.33.10
User ubuntu
IdentityFile ~/.ssh/ubuntu

La clave privada debe tener permisos restringidos sino se muestra una advertencia e impide el inicio de sesión.

1
$ chmod 600 ubuntu

Para probar la autenticación con SSH se puede utilizar una máquina virtual de VirtualBox creada con Vagrant. En el caso de utilizar el usuario ubuntu hay que asignarle una clave con el comando passwd para ejecutar comando ssh-copy-id ya que se solicita su contraseña en este paso, una vez realizado se puede desactivar la autenticación mediante usuario y contraseña cambiando el valor de la configuración PasswordAuthentication a no.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/bionic64"
config.vm.provider :virtualbox do |vb|
vb.name = "Ubuntu 18.04 (Vagrant)"
end
config.vm.network "private_network", ip: "192.168.33.10"
config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
end
end
1
$ vagrant up

» Leer más, comentarios, etc...

Israel Perales

Mi experiencia con ICEWM

mayo 03, 2019 06:55

Mi experiencia con ICEWM

Actualmente me encuentro trabajando bajo mucha presión y por esta razón necesito que los recursos de mi PC estén 100% enfocados en el desarrollo.

Tener arriba los servicios de Docker, IntelliJ y Firefox hacen que GNOME Shell se sienta pesado, cuando instale Open SUSE Tumbleweed me dí cuenta que tenia otros escritorios además de GNOME con Wayland y GNOME con Xorg, uno de ellos es ICEWM.

ICEWM es un entorno de escritorio a la Windows, muy ligero y me permite trabajar con fluidez, tiene los 4 escritorios acomodados de forma horizontal, un botón de inicio, una barra de tareas no personalizable y un monitor de recursos sencillo aun lado del reloj, lo único que necesito.

Este minimalismo, como siempre llega a ser un problema, ya que ahora faltan opciones que en GNOME son fundamentales, como lo es la selección de redes WIFI o cableadas, para la cual uno debe correr el siguiente comando por terminal.

nm-applet &

Listo aparecerá un icono para seleccionar la red que necesites.

Después puede que se cambie el teclado y por mas que busques no encontraras nada en el menú para poder cambiar la distribución del teclado.

Para cambiar la distribución a español, debes ejecutar el siguiente comando.

setxkbmap -option grp:switch,grp:alt_shift_toggle,grp_led:­scroll es

Listo teclado en Español.

Si queremos cuidar nuestra vista con el filtro de luz azul corremos el comando :

redshift -O 3200k

Existen algunas otras cosas molestas de este muy ligero entorno de escritorio, como que al presionar ctrl + alt + t en lugar de abrir Gnome Terminal , termina abriendo xterm, no se puede personalizar el toolbar de inicio y pequeños detalles que al inicio te hacen extrañar GNOME, pero que conforme estas trabajando, ya ni te importan, el rendimiento lo compensa.

Fuentes

» Leer más, comentarios, etc...

Israel Perales

Terminé el curso de agilidad y lean de Javier Garzás

mayo 03, 2019 06:43

Terminé el curso de agilidad y lean de Javier Garzás

El nombre completo del curso se llama Agilidad y Lean. Gestionando los proyectos y negocios del siglo. XXI (7ª. edición) , este curso esta alojado en la plataforma Miriada X, en la cual ya había hecho anteriormente un curso de aplicaciones web para Firefox OS.

No se si la persona que lo creo tenga en cuenta que México es el país que mas horas trabaja de la OCDE o que sólo el 1.7% de las personas gana más de 20 mil pesos al mes(unos 875 Euros).

Lo anterior va por que a lo largo del curso se estudia los principios del manifiesto ágil, uno de ellos es Individuos e Interacciones sobre procesos y herramientas y vemos temas como el people ware, no trabajar mas de 40 horas por semana, niko niko para 'medir' la felicidad de los equipos, Kudo box para incentivar la motivación, auto organización, equipos pequeños, transferencia del conocimiento por medio de la programación en pareja y lo que le sigue a la programación en pareja llamado Mob Programming.

Se comienza con una animación sin sonido de un equipo de 3-4 personas que están por utilizar SCRUM para desarrollar un proyecto.

En la primera lección se hace referencia que debemos estar conscientes de que hacer software no es lo mismo que construir casas o carros, por que nuestro trabajo la mayoría de las veces es intangible, lleva un proceso de construcción intelectual, es una industria relativamente joven, el software es difícil de predecir, la agilidad no es para todos los proyectos, aunque debería estar presente en mas de los que esta hoy y se debe evitar tener un rambo(persona que es la única que puede modificar ese Stored Procedure de 20k de lineas) en el equipo.

En las lecciones 3( El “Product Owner” y las historias de usuario
), 4(SCRUM) y 5(La planificación ágil) se tratan temas muy ligados a SCRUM y a lo que se le podría llamar agilidad "clásica" , te enseña lo que es, las practicas y técnicas para estimar(Planning poker), organizar (Board, historias de usuario, juntas) y como medir el rendimiento de un proyecto(Burn down y Burn up), velocidad, así como algunos consejos y comparaciones con el ciclo de vida en cascada(historias de usuario vs casos de uso).

Después en las ultimas lecciones 6(Lean y Kanban) y 7(Deuda Técnica y Testing Ágil), hablan de cosas que vi en la facultad como Lean y sabia que de alguna forma se podía aplicar al desarrollo de software, pero nunca lo concretaron en ninguna clase, era mas para manufactura de piezas o productos de acero, cosas que realmente no me importan.

Entonces saltan nombres en el curso como Toyota y Deming hablando sobre el éxito de la calidad, los principios de la calidad y como se aplica al proceso de creación del software como un producto, eliminar el desperdicio, re aprendizaje, etc.

Aparece un concepto nuevo para mi llamado Kanban, que es la técnica principal de Lean, la traducción de Google es letrero, en el curso se maneja como tarjeta visual y trata de tener la visibilidad del trabajo, limitarlo y controlar el flujo del mismo.

Para la visibilidad del trabajo se habla de los elementos/ítems que tenemos como tareas, ya sea una historia de usuario, un issue, requisito, funcionalidad nueva, etc. Debemos definir les los estados en los que pueden estar y que significado tiene cada uno, ya que muchas veces lo que una persona del equipo entiende por terminado no es lo que otros piensan.

Por ejemplo yo puedo decir que ya esta reparado un error y hasta lo tengo en la rama de desarrollo, pero quizá si mi jefe me pregunta, el esperaría que ese reparado es por que ya esta en producción, con esto tratamos de evitar los malos entendidos.

También vuelve la importancia de los tableros en físico en el lugar de trabajo para que sea visible para todo el equipo y que tu tablero de SCRUM se vuelve casi lo mismo que un tablero de Kanban, la diferencia es que Kanban no limita por tiempo, cosa que SCRUM si, con los sprints.

En la limitación del trabajo trata el concepto WIP(Work In Progress, presente en Gitlab ), en el que no debemos de tener mas de "n" cosas sin terminar al mismo tiempo, se debe definir cuantas tareas pueden estar abiertas en cada uno de los estatus y respetarlo, para centrar el equipo en cerrar tareas, por eso de muchas tareas abiertas y ninguna terminada.

La última lección es más técnica y me da entender que no importa que metodología uses, ya sea tradicional o ágil, la excelencia tecnología es lo más importante.

Esto es así por qué si no controlas y mides la complejidad del código en tus proyectos, estos terminan arrastrándose como un zombi, en el que día a día se le van agregando pedazos y pedazos de parches y lo que antes te tomaba un día hacerlo, ahora te toma 2 semanas, ¿Les ha pasado?.

Aquí a los hacks, work arounds, métodos llenos de complejidad se les pone nombre y apellido, uno es el concepto de deuda técnica y el otro es el de la complejidad ciclomatica de los métodos o funciones.

La deuda técnica son nuestros pequeños pecados, que vamos dejando en el código para poder salir a producción, seguir operando, cosas que técnicamente están mal pero que por el contexto son necesarias.

Se explica similar a un crédito bancario donde adquieres deuda para poder ganar un beneficio a corto plazo, pero en algún momento tendrás que pagar sus intereses.

La complejidad ciclomatica en palabras simples es cuántos caminos diferentes pueden existir en tu código, estos caminos se crean por sentencias condicionantes o bucles.

Explicado lo anterior ¿por qué es tan importante mantener una complejidad ciclomatica baja?, La respuesta es que código simple es código sencillo de mantener y fácil de probar, debido a que también se aborda el tema de TDD(Test drive development), el cual evoluciona a BDD(Behavior Drive development).

El desarrollo dirigido por pruebas es una práctica que consiste en desarrollar las pruebas primero y después la implementación, seguido de un nuevo concepto llamado refactorización del código, en el que se pule el código, estás pruebas deben estar automatizadas y deben ejecutarse por lo menos diariamente al construir el ejecutable, aquí entra otro concepto técnico llamado integración continua.

La integración continua es una práctica de la cultura DevOps en la que se debe integrar el trabajo de todos los desarrolladores diariamente.

Veo que es un curso muy completo y vale mucho la pena si estas envuelto en esto del software, aunque no seas desarrollador, muchas técnicas e ideas de este curso te vendrán a la mente en el día a día de tu trabajo en los problemas de organización, normalmente los proyectos de software fracasan por mala organización, ademas se ve muy padre el certificado en Linkedin.

Fuentes:

» Leer más, comentarios, etc...

Fixed Buffer

«Terraformando» nuestra infraestructura desde Azure Pipelines

abril 23, 2019 08:20

La imagen muestra los logos de terraform, azure pipelines y azure cloud

En la última entrada, hablamos sobre Terraform como herramienta para para gestionar nuestra infraestructura mediante código. También hemos hablado hace poco sobre el despliegue continuo desde Azure Pipeline con ARM. Como pudimos comprobar, trabajar con Terraform nos facilita la vida respecto a lo que es el trabajo con ARM directamente. Hoy vamos a unir las dos cosas utilizando Terraform desde Azure Pipelines.

En su momento comentábamos que una de las grandes ventajas de Terraform es que mantiene el estado de los recursos. Esto es algo muy útil y que con trabajo local no supone ningún problema, pero eso se nos queda corto si trabajamos desde el agente de Azure Pipelines. Esto es porque cuando el agente termine va a borrar los recursos, y esto incluye el fichero de estado del despliegue. Para solucionar este «inconveniente» y persistir el estado entre despliegues, vamos a utilizar un Azure Storage donde guardar el fichero.

Una vez aclarado esto, ¡vamos a crear una WebApp en Linux y conectarla a una base de datos Sql Server!

Codificando la infraestructura

Para poder generar la infraestructura y conectarla entre si sin revisión manual, vamos a utilizar la potencia que nos ofrece Terraform para relacionar recursos y así añadir datos como la cadena de conexión. En GitHub está el proyecto completo, dividido en secciones para facilitar su mantenimiento, y utilizando la posibilidad de modular partes para crear la Web App y el Sql Server.

Utilizando estas ventajas, vamos a generar la cadena de conexión de la base de datos utilizando los datos de salida y se lo vamos a pasar a la configuración de la webapp. Para eso, vamos a crear una salida en el módulo de sql server donde vamos a generar la cadena de conexión:

output "conection_string_value" {
  description = "Sql Server ConnectionString"
  value = "Server=${azurerm_sql_server.sqlServer.fully_qualified_domain_name};Initial Catalog=${azurerm_sql_database.sqlServerDb.name};User Id=${var.SQL_ADMIN_ID};Password=${var.SQL_PASSWORD};"
}

Con esto, vamos a poder utilizarla más adelante:

module "linuxwebapp" {
  //...
  conection_string_value = "${module.sqlserver.conection_string_value}"
}

El código de la infraestructura es fácil de seguir, pero aun así recomiendo echarle un ojo en profundidad para entender el concepto anterior.

Creando la integración continua

Para poder desplegar Terraform desde Azure Pipelines, el primer paso es crear el pipeline de integración. De esto hablamos hace algún tiempo en una entrada sobre CI, pero esta vez vamos a utilizar la interfaz gráfica en vez de yml. Como se pueden ver, los pasos son los mismos que cuando utilizábamos yml:

La imagen muestra el pipeline de integración

Aquí solo hay un pequeño cambio respecto a lo que vimos anteriormente, y es que tenemos que meter los ficheros de Terraform al artefacto para poder utilizarlos más adelante:

La imagen señala los campos que hay que rellenar en la tarea de copiar archivos

Para configurar esta nueva tarea, basta con decirle el nombre de la carpeta donde está el código Terraform que queremos desplegar, ponerle el filtro de selección (** para seleccionarlo todo), y por último crear una carpeta en el directorio del artefacto y pegar los ficheros Terraform en él.

El hecho de utilizar la interfaz gráfica para la integración es solo por cambiar, pero para el despliegue solo se puede utilizar la interfaz gráfica de momento.

Ejecutar Terraform desde Azure Pipelines

Una vez que tenemos la integración lista, vamos a crear una Release para que despliegue la infraestructura y el proyecto. Para ahorrarnos trabajo, vamos a utilizar el Task «Terraform Build & Release Tasks«, así que vamos a instalarlo en el pipeline:

La imagen muestra el botón para instalar las Task  de Terraform en Azure Pipelines

Una vez que lo tenemos instalado, vamos a crear el pipeline donde instalaremos Terraform, lo inicializaremos y aplicaremos para desplegar los recursos, y por último publicaremos la web:

La imagen muestra el pipeline de release

Al igual que hacíamos con ARM, lo ideal sería tener diferentes ranuras donde tengamos las diferentes etapas de dev, pre, pro, etc.

Además, vamos a necesitar diferentes variables, que vamos a registrar también en el pipeline, para eso, vamos a la pestaña «Variables»:

La imagen señala la pestaña "Variables"

Y vamos a registrar las variables que necesitamos para nuestro Terraform:

La imagen muestra las variables del pipeline

Por convención, el pipeline le pasa directamente las variables que coincidan y empiecen por TF_VAR a Terraform siempre que no sean secretos. Esto es algo a tener en cuenta para evitarnos un comando apply larguísimo donde le pasemos muchísimas variables.

Volviendo al pipeline, lo primero que tenemos que hacer es instalar Terraform, para eso, vamos a utilizar la Task «Terraform Installer» y le vamos a indicar la versión de Terraform que queremos utilizar:

La imagen muestra donde indicar la versión

El siguiente paso, es configurar una Task de tipo «Terraform CLI» para ejecutar el comando init:

La imagen muestra la interfaz de la Task Terraform CLI

Dentro de esta Task, vamos a seleccionar el comando «init», y vamos a indicar la ruta donde está el código Terraform, por último, vamos a seleccionar el tipo de backend, que como dijimos al principio, será en Azure Storage, por lo tanto, seleccionamos «azurerm». Esto nos permite ampliar la configuración pulsando sobre «AzureRM Backend Configuration», y así indicarle los datos de configuración:

La imagen muestra la configuración del backend en el Task

Con esto listo, el último paso con Terraform es crear una tercera Task de tipo «Terraform CLI». Esta vez vamos a elegir el tipo de comando «apply», le vamos a indicar la ruta a donde están los ficheros de código Terrafom, y por último le vamos a indicar las opciones. Entre las opciones, vamos a indicarle «-auto-approve» para que no pida confirmación antes de desplegar los cambios, y le vamos a pasar todas las variables de tipo «secrets» mediante «-var VARIABLE=VARIABLE_PIPELINE» (recordemos que las demás variables se le pasan por convención):

Un ejemplo de las opciones utilizadas es:

-auto-approve -var AZURE_SUBSCRIPTION_ID=$(TF_VAR_AZURE_SUBSCRIPTION_ID) -var AZURE_CLIENT_ID=$(TF_VAR_AZURE_CLIENT_ID) -var AZURE_CLIENT_SECRET=$(TF_VAR_AZURE_CLIENT_SECRET) -var AZURE_TENANT_ID=$(TF_VAR_AZURE_TENANT_ID) -var SQL_PASSWORD=$(TF_VAR_SQL_PASSWORD)

Con esto, y si todo ha ido bien, ya vamos a conseguir desplegar los recursos en Azure. Para desplegar la Web, solo nos queda añadir un Task de tipo «Azure App Service Deploy» tal cual hicimos en el caso de ARM para desplegar la web.

Tras lanzar una release, dentro de nuestro portal en azure podremos encontrar algo como esto:

La imagen muestra los recursos de Azure deplegados mediante Terraform y Azure Pipelines

Para este ejemplo, hemos utilizado el template de ASP NET Core y le hemos añadido que ejecute las migraciones de la base de datos al iniciar, para que la web este lista para funcionar directamente.

Como siempre, he dejado el código fuente completo (tanto Terraform como la Web) en GitHub.

Conclusión

Como hemos podido comprobar, Terraform es una herramienta muy potente que nos facilita mucho la vida manejando infraestructura como código (IaC), además de permitirnos trabajar con múltiples proveedores. Podemos integrar perfectamente Terraform y Azure Pipelines, o cualquier otro servicio CI/CD utilizando los comandos directamente, por lo que es algo que vale la pena revisar y conocer.

**La entrada «Terraformando» nuestra infraestructura desde Azure Pipelines se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Koalite

Unpopular opinions: software development edition

abril 22, 2019 05:06

A lo largo del tiempo he ido escribiendo bastantes posts en los que intentaba analizar desde un punto de vista (más o menos) racional varios aspectos relacionados con el desarrollo de software. En este post aprovecharé para repasar algunos de ellos. En realidad, nada es ni tan blanco ni tan negro y en los posts enlazados podréis ver un poco mejor la gama de grises.

Quizá no sean exactamente opiniones impopulares. Más bien podríamos decir que son opiniones controvertidas sobre temas que pueden ser examinados con cierto detalle y en los que podemos (y debemos) replantearnos nuestro punto de vista periódicamente.

Veamos algunas.

Utilizar métodos estáticos está bien

En los diseños orientados a objetos se tiende a demonizar su uso, pero los métodos estáticos no son más que funciones, y como tales son muy útiles. Bien utilizados ofrecen alternativas interesantes a la hora de diseñar y pueden simplificar muchos escenarios de testing (en contra de lo que se suele pensar).

Utilizar tipos primitivos ayuda a escribir código más mantenible

Evitar modelar todo con strings o decimals es una cosa, pero introducir tipos específicos para cada concepto que te encuentras provoca que tengas que reimplementar muchas operaciones básicas que están disponibles para los tipos estándar.

Es preferible evitar utilizar clases

Si tu lenguaje lo permite, es mejor intentar diseñar utilizando estructuras más simples (valores, funciones o módulos) antes que empezar a pensar en clases. Los objetos acoplan estado y operaciones, pero además tienen un ciclo de vida que hay considerar. Si no vas a necesitar varias instancias de una clase, probablemente no necesites una clase.

No pasa nada porque (algunas) clases sean grandes

Partir la funcionalidad de un sistema en una miríada de pequeñas clases muy focalizadas y (supuestamente) reutilizables está muy bien, pero puede hacer más difícil entender como funciona todo que tener un código más líneal (y procedural) empaquetado en menos clases. Además, si necesitas garantizar que se mantienen ciertos invariantes en partes críticas de una aplicación, a veces sólo te quedan las clases como mecanismo para encapsular información y evitar usos indebidos de la misma.

En muchos casos utilizar TDD no tiene sentido

Pese a que suene a anatema en determinados ámbitos, hay muchas situaciones en las que utilizar TDD no ayuda a mejorar el diseño ni el proceso de desarrollo. Ni siquiera tener tests automatizados debería ser algo a perseguir sin pararse a pensar antes qué pretendes obtener con los tests.

La inyección de dependencias no debe ser la opción por defecto

Diseñar un sistema para permitir realizar inyección de dependencias en todos los puntos posibles añade una complejidad a la hora de utilizarlo que, sencillamente, no compensa si al final vas a tener una única implementación de cada dependencia. Refactoriza el código para usar inyección de dependencias cuando lo necesites, pero no lo hagas de forma especulativa.

Los ORMs son muy útiles (y no por evitarte escribir SQL)

Los ORMs tienen muchos detractores e implican varios sacrificios (curva de aprendizaje, abstracciones incompletas, …). Pese a todo ofrecen muchas ventajas a la hora de modelar, especialmente gracias a cosas como el Identity Map, la persistencia por alcance o el polimorfismo, que van mucho más allá de evitarte escribir SQL (algo de lo que nunca te puedes pretender aislar por completo).

Los repositorios son importantes (aunque utilices un ORM)

Cuando se utiliza un ORM existe cierta tendencia a despreciar el uso de repositorios por considerarlos una parte «ya cubierta» por el propio ORM. Utilizar repositorios es más que encapsular llamadas a una capa de persitencia. Utilizar repositorios permite establecer un lenguaje común sobre el tipo de operaciones que es pueden realizar sobre cada entidad y ayudar así a proteger los invariantes de tu dominio.

Dejar ficheros en una carpeta es un mecanismo de integración válido

Es indudable que se trata de un sistema prehistórico y con muchos inconvenientes frente a utilizar alternativas más modernas como APIs Web o colas de mensajes. Sin embargo, la facilidad para implementarlo, usarlo y comprobar los datos que se mueven cuando hay errores son ventajas que no hay despreciar.

Hay que comentar el código

El código limpio de los auténticos artesanos ágiles es completamente autoexplicativo y con un nivel de abstracción tal que permite leerlo como si fuese una novela. Sí, vale, me lo creo. Pero lo que no puede leerse es la mente de quien lo programó para saber por qué tomó unas deciciones y no otras. Puedes llevar esa documentación fuera del código (mensajes de commits, pull requests, etc.), pero documentar el porqué de las cosas junto al propio código hace que sea más sencillo consultarlo.

Asumir deuda técnica es algo perfectamente razonable

«Como clean-coder quiero que mi código sea limpio y mantenible para que así…» Todo eso está muy bien, pero nunca hay que olvidar que el código hasta que no está en producción no sirve para nada. Saber gestionar la deuda técnica, ser consciente de cuándo hay que asumirla y de cuándo hay que empezar a pagarla es clave para el desarrollo de software.

Subir las dependencias al repositorio de código es la mejor forma de crear compilaciones reproducibles

Incluir dentro del repositorio de código fuente las dependencias de terceros está considerado una mala práctica en muchos entornos, pero te permite independizarte por completo de servicios de terceros durante el proceso de compilación y garantizar que las compilaciones son realmente reproducibles.

Menos despliegue continuo y más despliegue de calidad

Queda muy bien presumir de que puedes hacer 400 despliegues en producción diarios y que tu tiempo para resolver una incidencia es menor de 4 minutos desde que la implementas hasta que llega a los usuarios, pero como usuario preferiría no haberme encontrado con la incidencia directamente. Hace falta dedicar más tiempo a diseñar y probar antes de empezar a utilizar a tus usuarios como testers gratuitos.

Conclusión

En el fondo, no hay tantas cosas en el desarrollo de software que sean realmente tan opinables.

No es cuestión de «yo tengo derecho a opinar como quiera y toda opinión es respetable». Es cuestión de analizar los pros y los contras de todo sin empezar a dar por sentado cosas sólo porque estén consideras buenas prácticas o las llevemos haciendo toda la vida.

Estos análisis no pueden realizarse de forma completamente abstracta y generalista y, ahí sí, entra en juego el contexto (personal, tecnológico, de equipo, de negocio, …) en el que nos encontramos a la hora de tomar decisiones.

No hay posts relacionados.

» Leer más, comentarios, etc...

Picando Código

Gleam – Nuevo lenguaje de programación funcional estáticamente tipado en BEAM

abril 17, 2019 12:30

El pasado lunes 15 de abril se publicó la primera versión de Gleam (0.1): Gleam es un lenguaje de programación funcional estáticamente tipado diseñado para escribir sistemas concurrentes mantenibles y escalables. Compila a Erlang y tiene interoperabilidad directa con otros lenguajes de BEAM (la máquina virtual de Erlang) como Erlang, Elixir y LFE.

Obviamente es un lenguaje bastante nuevo, así que no está listo para usar en producción. Está interesante y ya se puede probar, programar alguna cosa divertida y aprender a usarlo. El código fuente está disponible en GitHub bajo licencia Apache 2.0, y chat del proyecto se encuentra en IRC en #gleam-lang de Freenode. Aplaudo el uso de IRC para proyectos de Software Libre en vez de Slack 👏
¡El compilador está escrito en Rust! Y si bien no conozco Rust, parece que saca algo de inspiración de ese lenguaje en la sintaxis y le veo también algunas cosas de Elixir.

Principios de Gleam

Ser seguro

Un sistema de tipados expresivo inspirado por la familia de lenguajes ML nos ayuda a encontrar y prevenir bugs en tiempo de compilación, mucho antes que el código llegue a los usuarios.

Para los problemas que no pueden ser resueltos con el sistema de tipos (como que le caiga un rayo al servidor) el runtime Erlang/OTP provee mecanismos bien testeados para manejar fallas.

Ser amigable

Perseguir bugs puede ser estresante así que la realimentación del compilador debe ayudar y ser lo más clara posible. Queremos pasar más tiempo trabajando en nuestra aplicación y menos tiempo buscando errores tipográficos o descifrando mensajes de error crípticos.

Como comunidad queremos ser amigables también. Personas de todos los orígenes, géneros y niveles de experiencia son bienvenidas y deben recibir respeto por igual.

Ser performante

El runtime Erlang/OTP es conocido por su velocidad y habilidad para escalar, permitiendo que organizaciones como WhatsApp y Ericsson manejen cantidades masivas de tráfico de manera segura con baja latencia. Gleam debería aprovechar por completo el runtime y ser tan rápido como otros lenguajes de la BEAM como Erlang y Elixir.

Ser un buen ciudadano

Gleam facilita el uso de código escrito en otros lenguajes BEAM como Erlang, Elixir y LFE, así que hay un ecosistema rico de herramientas y librerías para que los usuarios de Gleam aprovechen.

A cambio los usuarios de lenguajes BEAM deberían poder aprovechar Gleam, ya sea usando librerías escritas en Gleam de manera transparente, o agregando módulos Gleam a sus proyectos existentes con mínimo trabajo.

Instalación

Para instalarlo, debemos seguir esta guía, Necesitamos tener Rust, Erlang y rebar3 instalados de antemano, clonar el código con git, y compilar el compilador. El soporte para editores de texto todavía está un poco en pañales, pero ya hay modos para Emacs, Vim y Visual Studio Code.

Empezando a programar

Al ser un lenguaje tan nuevo, todavía no es un proceso tan directo crear un proyecto. De todas formas sólo hay que seguir estos pasos. El compilador puede trabajar con proyectos generados con rebar3, una herramienta de builds de Erlang.

Los tipos básicos de Gleam son String, Bool (True || && False), Int (enteros) y Float (decimales). Los valores pueden nombrarse con let para ser reusados después, pero los valores contenidos son inmutables:

let x = 1
let y = x
let x = 2
x // => 2
y // => 1

Tiene Tuplas, colecciones ordenadas de tamaño fijo con elementos que pueden ser de distintos tipos:

{"Texto", 2, True}

Y acá me empiezo a acordar de Elixir, podemos extraer los valores de la tupla así:

> valores = {"Texto", 2, True}
{"Texto", 2, True}
> {a, b, c} = valores
{"Texto", 2, True}
> a
"Texto"
> b
2
)> c
True

Otra estructura de datos son las Listas: Colecciones ordenadas de elementos del mismo tipo:

[7, 8, 9]
// Y para agregar nuevos valores, como en Elixir, se usa:
> ["N" | ["a", "n", "d", "o"]]
["N", "a", "n", "d", "o"]

Por último están los Mapas, colecciones de nombre y valores que pueden ser de cualquier tipo:

{
  nombre = "Nicanor",
  edad = 20,
}

Y se puede acceder al valor con la sintaxis `mapa.nombre_del_campo`:

let persona = { nombre = "Nicanor", edad = 20 }
let nombre = persona.nombre
nombre // => "Nicanor"

Para actualizar o agregar valores a un mapa se usa la sintaxis: { mapa | nombre_del_campo = valor }:

let persona1 = { nombre = "Marcela", edad = 22 }
let persona2 = { persona1 | edad = 23, idioma: "español" }
persona1 // { nombre = "Marcela", edad = 22 }
persona2 // { nombre = "Marcela", edad = 23, idioma: "español" }

Los tipos del mapa dependen de los nombres y tipos de los campos. El compilador lleva un registro de los campos y valores de cada mapa y presenta un error en tiempo de compilación en caso de querer usar un campo que no exista o tenga el tipo incorrecto.

Las funciones con nombre se declaran con pub fn:

pub fn sumar(x, y) {
  x + y
}
pub fn multiplicar(x, y) {
  x * y
}

Y como son valores de primera clase se pueden asignar a variables, pasar a funciones y todo lo demás que se puede hacer con cualquier otro tipo de datos:

pub fn dos_veces(f, x) {
  f(f(x))
} // Wooooo!

También tiene funciones anónimas con una sintaxis similar:

pub fn ejecutar() {
  let sumar = fn(x, y) { x + y }
  sumar(1, 2)
}

No puedo evitar las comparaciones con Elixir, pero es a lo que me hace acuerdo… Hay una sintaxis corta para crear funciones anónimas que toman un argumento y lo pasan a otra función. Y esto se usa generalmente con pipes para crear una serie de transformaciones de datos:

pub fn sumar(x, y) {
  x + y
}
pub fn ejecutar() {
  // esto equivale a sumar(sumar(sumar(1,2), 4), 6)
  1
  |> sumar(_, 2)
  |> sumar(_, 4)
  |> sumar(_, 6)
}

Vistas las funciones otra cosa interesante es pasarles mapas como parámetros. El lenguaje apunta a ser muy permisivo con esto, y muestra un ejemplo de cómo funciona:

fn numero_siguiente(mapa) {
   mapa.numero + 1
}

El tipo de la función es fn({ a | numero = Int}) -> Int. La a en este caso puede ser «cualquier otro campo», así que la función se puede llamar con cualquier mapa siempre y cuando tenga el campo numero con un valor del tipo Int.

let articulo = { nombre: "tenedor", numero: 17 }
let nintendo = { nombre: "gameboy", numero: 4 }
let fernando = { nombre: "Fernando", edad: 33 }
numero_siguiente(articulo) // => 18
numero_siguiente(nintendo) // => 5
numero_siguiente(fernando) // => Compile time error! No numero field

La expresión case se usa como estructura de control por medio de la técnica «pattern matching» (ya escribí algo sobre Pattern Matching en el blog antes). Cómo se usa:

case numero {
| 0 -> "Cero"
| 1 -> "Uno"
| 2 -> "Dos"
| n -> "Otro número" // machea todo lo que no sea lo anterior
}

Como alternativa al if else de otros lenguajes, hace pattern matching con los valores Bool:

case alguna_condicion {
| True -> "Es verdadera"
| False -> "Es falsa"
}

La expresión case retorna un valor por lo que podemos asignarle el resultado a una variable. El pattern matching me resulta una cosa mágica y feliz, pero debe ser porque no estoy acostumbrado a usarlo.

El lenguaje también cuenta con Enums, y el tipo Bool está definido como uno:

enum Bool =
  | True
  | False

Una variante que muestra como ejemplo para extraer valores es:

enum User =
  | LoggedIn(String)
  | Guest
let diego = LoggedIn("Diego")
let leticia = LoggedIn("Leticia")
let visitor = Guest

Los enums también pueden ser «patternmacheados» para asignarle nombres a distintas variantes y podemos usarlos en un let.
🤯

Por último, al ser un lenguaje de la BEAM, podemos usar funciones de otros lenguajes directamente ¡Santa interoperabilidad Batman! Al ser lenguajes distintos, el compilador no puede determinar el tipo de las funciones, así que es la responsabilidad del programador en esos casos de hacer las cosas bien o veremos hermosas explosiones en nuestra aplicación. Pero llamar funciones de otros lenguajes es bastante sencillo:

// Llamando a la función uniform del módulo rand de Erlang:
pub external fn random_float() -> Float = "rand" "uniform"
// Llamando a IO.inspect de Elixir:
pub external fn inspect(a) -> a = "Elixir.IO" "inspect"

Como si fuera poco, también podemos importar tipos externos.

Conclusión

Si bien está en una etapa muy temprana de desarrollo, Gleam me resultó un lenguaje súper interesante que iré siguiendo con atención. Al ser tan nuevo, todavía hay mucho por implementar, así que resulta un buen aprendizaje y bastante interesante ir viendo cómo se desarrollan nuevas funcionalidades.

Elixir, Crystal y ahora Gleam son los lenguajes que me interesaría usar más aparte de Ruby, y en algún momento me voy a poner a leer también sobre Rust.

Si quieren aprender más de Gleam, pueden visitar su sitio web.

» Leer más, comentarios, etc...

Bitácora de Javier Gutiérrez Chamorro (Guti)

Necromancer’s DOS Navigator

abril 13, 2019 08:01

DOS Navigator comienza en 1991, un gestor de archivos para DOS que proporciona una cantidad de funciones que representan un salgo enorme en la tecnología de la época. Su evolución continua hasta 1998 cuando presentan la versión 1.50, aunque desde la 1.35 de 1995 decidieron dedicar sus esfuerzos al cliente de correo para Windows The …

Necromancer’s DOS Navigator Leer más »

Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

La entrada Necromancer’s DOS Navigator aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin