Páginas

domingo, 12 de enero de 2020

Qué son las “The Three Ways” de DevOps.


Antecedentes

Uno de los conceptos fundamentales en Lean es el de “Value Stream”.  El “Value Stream” entendido cómo la secuencia de actividades requeridas para diseñar, producir, entregar un bien o dar servicio a un cliente; esto incluye los flujos de información y de materiales necesarios.
En Devops se define el “Value Stream”  de TI como el proceso requerido para convertir una hipótesis de negocio en un servicio proporcionado por la tecnología que entrega valor al cliente.
El tiempo de entrega es el tiempo que transcurre desde que el cliente hace una petición hasta que le es entregado el producto o servicio. El tiempo de proceso es el que transcurre desde que el trabajo se empieza a hacer hasta que se termina. Ver la siguiente figura:



El tiempo de entrega siempre es superior o igual al de proceso. Si se mejora cualquiera de estos tiempos la entrega de valor es más pronta y cuanto antes se entregue el valor más competitiva será la organización.

Particularizando en DevOps

En el caso de DevOps el “ Value Stream” de TI en muchas organizaciones está compuesto por múltiples actividades que llevan a cabo diversos profesionales con habilidades y conocimientos distintos. En “The DevOps HandBook” se muestra la siguiente figura:


 Aquí se representa el flujo de trabajo para poner en marcha un sistema en el entorno productivo, desde su concepción hasta su implantación. Al final de este flujo los clientes pueden utilizarlo y de esta forma recoger el valor que entrega el sistema.  La figura muestra un flujo de trabajo con muchas trasferencias, es decir en un paso el equipo correspondiente  hace un trabajo (por ejemplo:  desarrollar) a continuación se transfiere el trabajo al equipo que tiene que dar el siguiente paso (por ejemplo: Instalar el sistema en el entorno de pre-producción). Cada paso tiene sus propios tiempos de entrega y proceso. Mejorando estos tiempos entregaremos antes las aplicaciones, apps, webs, etc.
Además también hay que tener en cuenta el porcentaje de trabajo completado y asegurado. Este es el porcentaje de trabajo que en cada paso llega utilizable tal cual al siguiente paso, es decir se puede usar sin pedir más información o corregir la información suministrada.  Por ejemplo: para la instalación del sistema en preproducción, el equipo no tiene suficientes instrucciones para la creación de las tablas de la base de datos. Cuanto más elevado sea este porcentaje, habrá menos idas y venidas, no se re-trabajará y de nuevo se entregará el trabajo con más prontitud.

Las “Three ways”

En “The DevOps HandBook” los autores proponen una secuencia de mejora del “Value Stream” de DevOps. En la secuencia recomiendan abordar las mejoras en tres fases, que son las tres formas de mejorar. Estas son:

  •  Primera forma: Mejorar el flujo.
  •  Segunda forma: Hacer buen uso del feedback.
  • Tercera forma: Establecer el aprendizaje y mejora continua.

Recomiendan abordar las mejoras de estas 3 formas y hacerlo una tras otra. Es decir, primero poner el foco en mejorar el flujo, luego agregar al objetivo usar el feedback y para terminar sistematizar la forma en que se aprende de los errores y los éxitos e incorporar ese aprendizaje a la  mejora.

Primera forma: Mejorar el flujo

Aquí se trata de conseguir que el flujo de actividades para llevar los sistemas al entorno productivo sea constante y sin sobresaltos. Para ello existen múltiples recomendaciones:

  • Hacer el trabajo visible
  • Reducir el tamaño de los paquetes de trabajo es decir entregar menos funcionalidades juntas, pero más a menudo.
  • Automatizar los trabajos.
  • Construir con calidad.
  • Integración/entrega/despliegue continuos.
  • Fácil creación de entornos.
  • Prepararse para el cambio.

Segunda forma: Hacer buen uso del feedback

Se trata de establecer mecanismos para que en cada paso seamos capaces de hacer llegar información sobre el resultado de su trabajo a los pasos anteriores.  Cuando los equipos conocen el resultado de su trabajo corriente abajo, son capaces de aprender de sus éxitos y sus errores. Repitiendo lo que se hace bien y cambiando lo que hacen mal para intentar no repetir los errores.

Tercera forma: Establecer el aprendizaje y la mejora continua

Permitir un enfoque dinámico, disciplinado y científico hacía la experimentación y la aceptación de riesgos para aprender de los éxitos y los fracasos. Por ejemplo, estableciendo programas de uso de ataques a los propios sistemas como los recomendados por el Chaos Monkey. Esta es una iniciativa de Nexflix, donde periódicamente lanzan ataques contra sus propios servidores para observar las consecuencias y hacerse más resistentes a posibles problemas.

Bibliografía

The DevOps Handbook
 John Willis; Jez Humble; Gene Kim; Patrick Debois
 Publicado por IT Revolution Press, 2016

lunes, 6 de enero de 2020

Javascript: Ejemplo de llamada síncrona al api fetch


Objetivo

En este post se explica cómo utilizar el API Fetch para obtener un fichero json. Lo interesante de este ejemplo es cómo hay que codificar las funciones, para que el orden en que se ejecutan las sentencias javascript sea el adecuado y de esta forma evitar asincronías no deseadas.

El API fetch es asíncrono, esto quiere decir que cuando se ejecuta, se lanza la petición y la ejecución continúa sin esperar a que el resultado haya sido recuperado. Si esta forma asíncrona de funcionamiento no se tiene en cuenta, no hay garantía de obtener la respuesta adecuada. Es decir, si el proceso a realizar depende de los datos recibidos no se puede realizar dicho proceso hasta que se haya recibido la respuesta.

Punto de partida

En este ejemplo se usa el API REST  https://jsonplaceholder.typicode.com/ , es un API displonible de forma gratuita para poder realizar pruebas. Para crear la infraestructura de este ejemplo, se inicia un proyecto node express (https://expressjs.com/en/starter/hello-world.html). Este framework se usa para construir la aplicación que arranca un servidor que resuelve peticiones http por el puerto 3000.

Ejemplo

Para ejecutar la prueba se usa el comando "node app.js". Este comando arranca la aplicación web que escucha en el puerto 3000

node app.js
Example app listening on port 3000!

Una vez arracada, abrir una ventana del browser y escribir http://localhost:3000/damepost
Siguiendo la estructura de un proyecto node express, para dar respuesta a esta petición se crea el módulo app.js. Este módulo es el que arranca la aplicación y tiene codificada la ruta “/damepost”. Ver a continuación app.get. Aquí es donde se indica lo que la aplicación tiene que hacer cuando se escribe la petición http http://localhost:3000/damepost

app.js

constexpress = require('express')
const app = express()
const port = 3000
const f = require('./functionsRefactored.js')

app.get('/damepost', async (req, res) => res.send(await f.hagoFetch())) 

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Cuando se realiza la petición http://localhost:3000/damepost, la aplicación llama a la función

 async (req, res) => res.send(await f.hagoFetch())

Esta arrow function llama a hagoFetch(). Esta función está exportada desde el módulo functionsRefactored.js que se muestra a continuación.

functionsRefactored.js

fetch = require('node-fetch');

async function hagoFetch() {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1')
    .catch(error =>{console.error(error)})
  const json = await response.json();
  return json
}

exports.hagoFetch=hagoFetch;


En hagoFecth se solicita el fichero json que devuelve todos los posts del usuario cuyo código de identificación es 1. La solicitud se hace con await, es decir el control de ejecución para y espera hasta que response.json() termina para continuar. 
 
Una vez que se dispone de response, se obtiene el fichero json con el método json(). Este método también se invoca de forma síncrona con await. Por lo tanto, hagoFetch() no retorna la constante json hasta que no contiene todos los datos.

Resumen

Convertir en síncrona la petición fetch, se ha conseguido mediante dos llamadas con await.

  1.  await fetch(….) no devuelve la constante response hasta que  fetch ha terminado.  
  2.  await response.json() no devuelve la constante json hasta que la función ha terminado.

Para poder hacer await dentro de la función  hagoFetch() se ha tenido que declarar como función async. A su vez la llamada a la función hagoFetch en app.js se hace con await. Para poder hacer await la arrow function de app.get para la ruta /damepost está declarada como async.

Espero que esto sea de utilidad para alguien por ahí que se esté pegando con fetch y la asincronía. Si tenéis alguna duda dejad un comentario.

lunes, 7 de octubre de 2019

Ejemplo de Verify FindOne() de MongoCollection de Jongo con JUnit y Mockito

Planteamiento y código

Este ejemplo está elaborado con Java 1.5, JUnit 4.12, Mockito 1.9.5, MongoDB driver 3.10.2, Jongo 1.3.0 y la base de datos MongoDB server 4.0.

Se muestra cómo preparar una prueba para un módulo de acceso a base de datos. Lo más remarcable es la forma de acceder a verificar que el método FindOne(query) es invocado una vez.
El siguiente módulo accede a una base de datos MongoDB, para buscar un usuario y comprobar si su contraseña coincide con una dada que contiene el objetoUsuarioBean. 

El módulo que accede a la base de datos y es el objeto de la prueba. O sea el SUT (Subject Under Test)

UsuarioColeccion.java

import org.jongo.Jongo;
import org.jongo.MongoCollection;
import com.mongodb.DB;
import com.mongodb.MongoClient;

public class UsuarioColeccion {
    private DB db;
    private MongoCollection mongoCollection;
   
    public MongoCollection getMongoCollection() {
      return mongoCollection;
    }
   
    public void setMongoCollection(MongoCollection mongoCollection){
        this.mongoCollection = mongoCollection;
    }

    @SuppressWarnings("deprecation")
    public UsuarioColeccion()  {
        db = new MongoClient().getDB("UsuariosDB");
        mongoCollection = new Jongo(db).getCollection("Usuarios");       
    }
        

    public boolean buscarUsuarioContrasena(UsuarioBean bean){ 
            String query="{_id: '"+bean.getNombreUsuario()+"'}";   
            UsuarioBean usuario = 

                 getMongoCollection().findOne(query).as(UsuarioBean.class);      
            if (usuario==null)
                return false;
            else           
                return usuario.getContraseña().equals(bean.getContraseña());
    }
}


El código del bean

UsuarioBean.java

import org.jongo.marshall.jackson.oid.Id;

@SuppressWarnings("deprecation")
public class UsuarioBean {
  
    @Id
    private String nombreUsuario;
    public String getNombreUsuario() {
        return nombreUsuario;
    }

    private String contraseña;
    public String getContraseña() {
        return contraseña;
    }

    public UsuarioBean() { }
    public UsuarioBean(String nombreUsuario, String contraseña) {
        this.nombreUsuario = nombreUsuario;
        this.contraseña = contraseña;       
    }
}
 


Se construye una prueba unitaria que no accede a la base de datos, este acceso se comprobará en las pruebas de integración. La prueba unitaria verifica que se ejecuta el método FindOne() de MongoCollection de Jongo una sola vez cuando se accede a buscar un documento en la base de datos. 

El módulo de prueba unitaria es

UsuarioColeccionTest.java

import java.net.UnknownHostException;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import org.jongo.FindOne;
import org.jongo.MongoCollection;

import estratecno.Mongo.UsuarioBean;
import estratecno.Mongo.UsuarioColeccion;

public class UsuarioColeccionTest {
   
    UsuarioColeccion coleccionUsuarios;
    UsuarioBean bean;
    MongoCollection mongoCollection;
   
    @Before
    public void before() throws UnknownHostException {
       coleccionUsuarios = new UsuarioColeccion();   
       bean = new UsuarioBean("Pepe","abcd");
       mongoCollection = Mockito.mock(MongoCollection.class);
    } 


    @Test
    public void cuandoBuscaUsuarioUsaFindOne(){
        String query="{_id: '"+bean.getNombreUsuario()+"'}";
        FindOne findResult = Mockito.mock(FindOne.class);
        Mockito.doReturn(findResult).when(mongoCollection).findOne(query);
        coleccionUsuarios.setMongoCollection(mongoCollection);
        coleccionUsuarios.buscarUsuarioContrasena(bean);       
        Mockito.verify(mongoCollection, Mockito.times(1)).findOne(query);       
    }

}
 

Asuntos remarcables del ejemplo:

  • Aunque UsuarioColección crea su propia MongoCollection en el constructor,  la prueba unitaria mockea una MongoCollection que le inyecta a UsuarioColeccion. Esta MongoCollection que suplanta a la real, permite hacer la prueba sin acceder a la Base de Datos Real. La inyección se hace con:       
        coleccionUsuarios.setMongoCollection(mongoCollection);
  • Para decirle al suplantador que la MongoCollection auténtica llama al método findOne()  se codifican las expectativas de Mockito:
        FindOne findResult = Mockito.mock(FindOne.class);
        Mockito.doReturn(findResult).when(mongoCollection).findOne(query);


         En este caso la expectativa hace uso de un suplantador de FindOne llamado findResult. 
         findResult es necesario para crear la  expectativa como  tipo  que  devuelve  el  método 
         findOne(query).
  • La forma de verificar que se llama a findOne() una sola vez es:
        Mockito.verify(mongoCollection, Mockito.times(1)).findOne(query);