viernes, junio 23, 2017

jenkins approval, como hacer la lista de approvers de manera dinámica.

Algunas veces en el proceso de despliegue se necesita que alguien de un visto bueno o que alguien apruebe de forma manual un despliegue, aunque no es conveniente para continous delivery algunas reglas de negocio son así, pare esto jenkins dispone del plugin Promoted builds que nos permite configurar quien de forma manual una aprobación.

Se puede leer un buen caso de uso en este blog de cloudbees, empecemos a configurarlo para ver la problemática de este caso. Para activar la promoción se debe de activar la casilla "Promote builds when..."


En este ejemplo una vez que se ejecute el job de jenkins la promoción manual solo pintará en el log "despliegue" y solo lo podrá aprobar el usuario admin.


se puede ver el botón approve para el usuario admin y la salida sería:

Started by user admin
Building in workspace /var/lib/jenkins/workspace/test-approve
Promoting test-approve #1
[test-approve] $ /bin/sh -xe /tmp/jenkins552387212222026236.sh
+ echo despliegue
despliegue
build hudson.tasks.Shell@38223f2c SUCCESS
Finished: SUCCESS


Este plugin no dispone de una manaera de hacer que esta lista sea dinámica mediante variables del tipo $APPROVERS_LIST en el campo de  Approvers por lo que tenemos que modificar el job cada que se nos pida un nuevo approver.

Para solucionar esta deficioencia podemos agregar approvers de forma manual agregando el plugin Environment injector plugin y activar la promoción automática con un script de groovy.

Activaremos la casilla "Prepare an environment for the run" y agregaremos la variable de ambiente APPROVERS, o como es mi caso agregué un archivo properties con la configuración de la variable.

/var/lib/jenkins/secrets/approvers.properties
APPROVERS=admin,operator,approver1


En nuestra configuración de promoción agregaremos el siguiente script de groovy, el cuál lo que hace es modificar los approvers injectando la lista que se encuentre en APPROVERS




Y lo más importante es activar "Promote immediately once the build is complete" para que se ejecute el archivo de groovy una vez que nuestro job a sido costruido.

Ahora una vez que se construye el proyecto la lista de approvers debe de cambiar:

Dependiendo de la versión de jenkins tendrán que aprobar el uso del script de groovy, se hace en <jenkins_url>/scriptApproval/




Por alguna razón siempre hay que borrar la opción de classpath del plugin de groovy, sino jenkins se enojará mucho y nos lanzará una exception.





Dejo un caso de ejemplo en este repo, ansible-test-kitchen bastaría con ejecutarlo asi:
(master *)$ bundle exec kitchen converge default-ubuntu-promoted-builds-jenkins 

Y un pequeño video sobre este tema:


jueves, noviembre 03, 2016

Sobre wildfly y jboss-cli

Hace mucho que no trabajaba con jboss y en esta ocasión me tocó ocupar su versión gratuita Wildfly,

Wildlfy y jboss tienen una consola con la cuál se puede hacer scripting, sin más el  siguiente script lo ocupé para generar un nuevo servidor por cada ambiente que era necesario crear desde jenkins

Ver el siguiente gist:


Solo falta agregar unas variables de ambiente y correr el job con la opción "build with parameters"

miércoles, junio 01, 2016

Autoscalar RDS postgresql, más o menos.



Una de las cosas más difíciles de auto escalar son las bases de datos, aws amazon nos da la opción de escalar horizontalmente una base de datos mediante read replicas, lamentablemente en el caso de postgresql solo nos deja crear 5 y no hay opción de hacer read replicas de las read replicas (Esto se puede hacer en mysql).

Una forma de seguir creciendo es promover una read replica a master y crear sus otras 5 read replicas, pero hay que implementar un mecanismo de sincronización entre las dos master.

La otra es empezar con pocas read replicas he irlas creando de acuerdo a la demanda, esta opción es buena pero no es rápida, se basaría más en predecir el tráfico antes de que llegue, lo malo es que crear una read replica es  lento  aproximadamente unos 20 min.

Un escenario es tener creadas las 5 read replicas y escalarlas verticalmente cambiando el tipo de clase de instancias. Esta solución también es lenta, unos 7 u 8 min. Una ventaja de esta solución es que podemos jugar con los costos y por ejemplo tener solo 2 read replcias grandes y otras 3 micro.

Para que esta opción funcione necesitamos una arquitectura parecida a esta:



En nuestro ejemplo necesitaremos lo siguiente:
1. Tener creado un rds master con una read replica
2. Tener configurado 2 instancias con pgboucer uno para apuntar a master y otro a readreplica
3. Tener configurado un loadbalancer con esas dos instancias.

La modificación de las clase y encender el pgbouncer lo realizaremos a través de lambdas, las lambdas que usaremos son upgrade_db_instance_class y add_read_replica_to_elb.


Necesitaremos crear 2 topicos de SNS, para atacharlos a los eventos, uno de cloudwatch que disparará el aumento de la read replica y otro al finalizar la modificación de la read replica.



Y crearemos dos suscripciones, cada uno a su respectivo lambda:


Crearemos una alerta y mandaremos la notificación al topico 80_percent_rds


Y crearemos un Event Suscriptions en el rds para apuntarlo al topico upgrade_rds





Habiendo hecho todo eso ya podemos empezar con nuestra prueba.






Ocuparemos una instancia para lanzar las pruebas, estas se harán con pgbench y lo podemos instalar de la siguiente manera.

$ sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list'
$ wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install postgresql-contrib

Ahora meteremos datos a la instancia:
$ pgbench -h hgmigueldb.comvkfys9x22.us-east-1.rds.amazonaws.com -p 5432 -Uhgmiguel -i -s 300 hgmigueldb

Y así correremos las pruebas:
$pgbench -h internal-pgbouncer-elb-592002074.us-east-1.elb.amazonaws.com -p 6432 -Uhgmiguel -c 64 -j 4 -T 900 -C -S hgmigueldb

Como ven ocupamos muchas cosas del entorno de aws, SNS, Lambdas, RDS, ELB, y Cloudwatch para poder armar esta arquitectura. Llega a ser abrumante la cantidad de servicios que hay en amazon pero conocerlos todos nos ayuda a crear mejores soluciones.

martes, noviembre 11, 2014

groovy dinámico

Los closures en groovy son muy utilizados, pero transformar metodos a closures no tanto, hay muchas ventajas como poder pasarlos como parametros de funciones o como valores en un mapa, para esto se ocupa el operador (.&)

13   def dynamicMethods = [[regex:"hasPermission(.*)", method:this.&hasPermission],
14   [regex:"create(.*)Permission", method:this.&createPermission]]


En este caso estamos convirtiendo el método hasPermission() y createPermission() en un closure y pasandoselo a un mapa, el metodo puede ser llamado como se muestra en la línea 21.

17   def methodMissing(String name, args) {
18   | def method = dynamicMethods.find { name =~ it.regex}
19   | if(method) {
20   | | def match = name =~ method.regex
21   | | return method.method(match[0][1], * args)
22   | } else
23   | | throw new MissingMethodException(name, DemoDynamic , args)
24   }
25 
26 }

Ocupando una expresión regular y methodMissing podemos mandar a llamar dinámicamente el método que vive en el mapa.

32 assert demo.hasPermissionHgmiguel("write")
33 assert !demo.hasPermissionOtros("write")

Dejo un Gist como ejemplo de lo que quiero explicar:

lunes, mayo 26, 2014

Eliminar switch

Hace mucho que no programo en java o algo parecido, últimamente hago mucho groovy y estoy aprendiendo clojure, pero bueno, revisando código en el trabajo me topé con una estructura switch, y de inmediato llamo mi atención:


  List<Role> findInviteRoles() {
    List roles = []
    def authorities = springSecurityService.getAuthentication().authorities
    authorities.each {role ->
      switch (role){
        case 'ROLE_ROOT':
          roles += ['ROLE_ROOT', 'ROLE_ADMIN']
          break
        case 'ROLE_ADMIN':
          roles += ['ROLE_ADMIN']
          break
        case 'ROLE_1':
          roles += ['ROLE_2']
          break
      }
    }
    Role.where {
      authority in roles.unique()
    }.list()
  }


A lo cual me pareció que el switch iba sobrado y decidimos hacer un refactor. Se puede ver que ahora están separados los datos de la lógica, lo cuál en algunos casos facilita la mantenibilidad del código.

 List<Role> findInviteRoles() {
    List roles = []
    def permisos = ['ROLE_ROOT':['ROLE_ROOT', 'ROLE_ADMIN'], 
                    'ROLE_ADMIN':['ROLE_ADMIN'], 
                    'ROLE_1':['ROLE_2']]
    def authorities = springSecurityService.getAuthentication().authorities
    roles = authorities.collect {
      permisos."${it}"
    }.flatten().unique()
    Role.where {
      authority in roles
    }.list()
  }

A mi punto de vista queda más limpio y claro el objetivo del collect que del switch. Dejo la prueba unitaria.
  @Unroll
  def "test list granted invite roles with #userRole and result #listResult"() {
    setup: "mockering"
    SpringSecurityService.metaClass.getAuthentication = { [authorities: [userRole]] }
    def springSecurityService = Mock(SpringSecurityService)
    service.springSecurityService = springSecurityService

    systemRoles.each {
      def role = new Role(authority: it)
      role.save validate: false
    }

    when:
    def list = service.findInviteRoles()

    then:
    list?.authority == listResult

    where:
    userRole       | systemRoles                                                || listResult
    'ROLE_ROOT'    | ['ROLE_ADMIN', 'ROLE_ROOT', 'ROLE_GARBAGE']                || ['ROLE_ROOT', 'ROLE_ADMIN']
    'ROLE_ADMIN'   | ['ROLE_ADMIN', 'ROLE_ROOT', 'ROLE_GARBAGE']                || ['ROLE_ADMIN']
    'ROLE_1'       | ['ROLE_ADMIN', 'ROLE_ROOT', 'ROLE_GARBAGE', 'ROLE_2']      || ['ROLE_2']
    'ROLE_2'       | ['ROLE_ADMIN', 'ROLE_ROOT', 'ROLE_GARBAGE', 'ROLE_2']      || []
  }

miércoles, marzo 19, 2014

Métrica C.R.A.P. con grails


Qué es el anti patrón de riezgo de cambio (Change Risk Anti-Patterns C.R.A.P)?
es un método para analizar y predecir la cantidad de esfuerzo, trabajo y tiempo requerido para mantener la base de código existente. La fórmula de C.R.A.P. es la siguiente:

C.R.A.P.(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)

Donde comp(m) es la complejidad ciclomática del método m y cov(m) es la cobertura de pruebas del método m.

Más información  aqui:

Al intentar configurar CRAP para grails me topé con que no pude utilizar gmetrics, le di muchas vueltas hasta que lo abandoné, leyendo sobre métricas me topé con esta presentación y ahí me di cuenta de que codenarc ya contiene a CRAP entre sus reglas. He aquí como lo configuré:




buildconfig.groovy
 25 grails.project.dependency.resolution = {
 54 
 55   dependencies {
 66     compile 'org.gmetrics:GMetrics:0.6'
 69   }
 70 
 71   plugins {
 85     compile ':codenarc:0.20'
 99   }
100 }
101 
110 
111 codenarc.reports = {
116   MyHtmlReport('html') {
117     outputFile = 'target/CodeNarcReport.html'
118     title = 'Violations Report'
119   }
120 }
121 
122 codenarc.propertiesFile='grails-app/conf/codenarc.properties'
123 codenarc.ruleSetFiles = "rulesets/basic.xml,rulesets/exceptions.xml, rulesets/imports.xml,rulesets/grails.xml, rulesets/unused.xml, rulesets/size.xml, rulesets/concurrency.xml,rulesets/convention.xml,rulesets/design.xml,rulesets/groovyism.xml,rulesets/imports.xml,rulesets/logging.xml"
124 

codenarc.properties
 1 GrailsPublicControllerMethod.enabled=false
 2 CatchException.enabled=false
 3 CatchThrowable.enabled=false
 4 ThrowException.enabled=false
 5 ThrowRuntimeException.enabled=false
 6 GrailsStatelessService.enabled=false
 7 NestedBlockDepth.maxNestedBlockDepth=3
 8 CrapMetric.enabled=true
 9 CrapMetric.coberturaXmlFile=file:target/test-reports/cobertura/coverage.xml

Ahora solo basta ejecutar:
grails test-app -unit -coverage -xml
grails codenarc

Como resultado tendremos más violaciones a las reglas pero seguro ayuda a mejorar en algo.

➥ ItemGroupsController.groovy

Rule NamePriorityLine #Source Line / Message
CrapMetric276
[SRC]def update() {
[MSG]Violation in class com.winbits.api.catalog.controllers.ItemGroupsController. The CRAP score for method [update] is [90.0]


El límite está puesto a 30 como máximo, este método tiene un índice de 90 lo cual nos indica que esta muy crapy, para poder reducir el índice se puede hacer un refactor para reducir su complejidad o aumentar sus pruebas automatizadas, claro que si se hace un refactor primero se tienen que hacer las pruebas para que sea seguro hacerlo :-)





domingo, enero 20, 2013

Capcom SNES jostick

Hace rato que tengo un botón hecho con arduino, este botón hace que vim cambie de modo comando a modo inserción. Un amigo  vio como lo ocupaba y se le ocurrió la idea de transformar un control capcom power stick para el snes y pasarlo a usb para poder utilizarlo como un controlador para Traktor.


He aquí como se transformó:



Después de un tiempo de observación noté que una vez que le pasas tierra o corriente a la placa toda esta queda con electricidad, lo que nos facilita la tarea de cablear y ver como funciona, el integrado que hace que funcione con el snes no lo ocupamos para nada, y creo que sería mejor desoldarlo en algún momento. Para estar seguros ayuda mucho tener un multimetro.


Así que fue cosa de soldar un pequeño dispositivo para poder conectarla a la corriente del arduino y empezar a jugar un poco con las conexiones, después fue ir soldando las terminales de los botones y conectarlas directo a una terminal del arduino.



El primer intento lo hice con el arduino pro ethernet de sparkfun, pero para que funcionará necesitaría de otro programa que emulará los tecladazos debido a que este arduino no es reconocido como una interfaz usb hid, así que decidí comprar un arduino leonardo para este caso.




El código puede obtenerse de aqui, esta es la parte que lee los botones, utilicé la variable arrayLettersBits para pasarle la tecla que quería que se presionara:

  //Letras a pulsar en el teclado
  const String arrayLettersBits = "arbxyl512346";

  for (int i = 2; i <= maxButtonPin; i++) {
    buttonState = digitalRead(i);
    if (buttonState != HIGH) {
      Keyboard.press(arrayLettersBits[i-2]);
    } else {
        Keyboard.release(arrayLettersBits[i-2]);
    }
    
  }

Esta parte es la que lee la palanca, aqui utilicé un pequeño hack, puesto que los switches del Jostick me regresaban aveces 0.1v o algo así y el arduino lo estaba detectando como HIGH, es por esto que utilicé analogRead en vez de digitalRead.

for (int i = jostikInitPin; i <= jostikEndPin; i++) {
    buttonState = analogRead(i);
    if (buttonState == 1023.00) {
      switch(i) {
        case 0:
          delay(5);
          buttonState = analogRead(i);
          if (buttonState == 1023.00) {
            Keyboard.press(KEY_RIGHT_ARROW);
            jostickBits[i-jostikInitPin]='0';
          }
          break;
        case 1:
// Más código similar aqui
      }
    } else {
      switch(i) {
        case 0:
          Keyboard.release(KEY_RIGHT_ARROW);
          
          break;
        case 1:     
// Más código similar aqui
      }      
    }

Al final lo terminé probando con un emulador de SNES y un juego de street fighter.

Pero el verdadero uso será así:

lunes, octubre 22, 2012

namedqueries con left join

Una de las funciones que más me gustan de gorm es la de namedQueries, que no son otra cosa que queries que viven dentro de las clases de dominio a los cuales se les puede aplicar metodos como list, count, y pasarles argumentos, etc.

Supongamos que tenemos la siguiente estructura de BD

Persona -> Ocupacion -> Categoria

Y que queremos contar el numero de personas por categoría, incluyendo aquellas que no tengan ocupacion (ocupacion = null), el problema es que para llegar a la solución necesitamos de dos left join con sql, recordando que los nameqQueries utilizan la misma nomenclatura que los criteria builder se nos ocurre que hay que ocupar CriteriaSpecification.LEFT_JOIN.

El siguiente namedQuerie dentro de persona hará nos solucionará el problema

groupByCategoria {params ->
            ocupacion(CriteriaSpecification.LEFT_JOIN) {
                categoria(CriteriaSpecification.LEFT_JOIN) { }
                projections {
                        groupProperty("categoria")
                        rowCount("total")
                }
            }
        }


Y ya podremos ocuparlo de la siguiente manera:

Persona.groupByCategoria.list()

Esto generará una lista como la siguiente:
[Categoria : 1:3, Categoria : 2:1, null:1]


Creo que nunca me va a gustar hibernate, sobre todo porque le quita todo el poder del lenguaje sql, peor bueno, cuando no hay otra solución más vale aprender bien la tecnología usada.

domingo, agosto 19, 2012

Sobre grails y plugins


Por curiosas razones (ocupar cherokee como proxy server y no tener un módulo de AJP) tuvimos un bug con recursos protegidos por SSL, en concreto tomcat nos regresaba recursos con http en vez de https...

La primera aproximación fue excluir los recursos que daban problemas, lo cual no iba a prevenir que esto siguiera pasando, en la configuración se detalla como hacerlo, en concreto hay que usar grails.resources.adhoc.excludes para excluir los recursos que causan problemas.

Cuando volvió a producirse el error se investigó mejor y se analizó el código del plugin de  resurces, de lo cual se llego a un @todo que nos daba problemas... 


○ → find . -name "*.groovy"  | xargs grep -i todo | awk -F'/' '{out=$8; for(i=9;i<=NF;i++){out=out" "$i};print out}'

ResourceTagLib.groovy:               @todo where a module resource is bundled, need to satisfy deps of all resources in the bundle first!
ResourceTagLib.groovy:     * @todo Later, we implement ESP hooks here and add scope="user" or scope="shared"
ResourceTagLib.groovy:        def baseUrl = ''    @todo get from config
ResourceTagLib.groovy:               @todo do we need to toggle http https here based on current request protocol?
ResourceTagLib.groovy:     * @todo this currently won't work for absolute="true" invocations, it should just passthrough these

Navegando por las entrañables aguas del código fuente ajeno llegamos a la función que nos daba el error redirectToActualUrl, la solución fácil y para no esperar una actualización en el plugin fue  sustituir la función por medio de metaprogramación en groovy.


        ResourceProcessor.metaClass.redirectToActualUrl = {ResourceMeta res, request, response ->
            //... Codigo ...
            def u = (grailsApplication.config.grails.serverURL? grailsApplication.config.grails.serverURL.toString() : request.contextPath) + staticUrlPrefix+res.linkUrl
            //... Codigo ...

        }


Como moraleja no hay que confiar mucho en plugins ajenos, hay por todos lados @todo, uno de los que más me afectan son las pruebas unitarias, no toda la funcionalidad de GORM esta integrada, casi siempre se termina haciendo pruebas de integración para solventar este problema. Aún así grails es una herramienta excelente para programar aplicaciones web, no regresaría a java + frameworks para esto...


sábado, agosto 18, 2012

ssl y cherokee

Nadie va a negar que godaddy vende certificados muy baratos en comparación con verisign, así que  para empresas de nueva creación godaddy es la opción... Y por barato los problemas no tardan en aparecer.

Nuestro certificado marcó problemas con dispositivos móviles. En concreto no reconocía a la autoridad certificadora. Luego de investigar y de no tener idea en que parte había hecho las cosas mal descubrí en el siguiente blog la solución al problema... Y era tan simple como concatenar el archivo gd_bundle al certificado:

$cat gd_bundle.crt >> dominio.com.crt

powered by cherokee

Dejo la nota original.
http://fluidsurveys.com/cherokee-web-server-ssl-setup/

After trying many things, and contacting godaddy, who told me they did not support Cherokee, I finally managed to figure out how to get it to work. The answer is pretty simple, if you follow the rest of the directions about setting up SSL with Cherokee, pointing your virtual server to the correct ssl certificate (Ours was fluidsurveys.com.crt) and the proper key (fluidsurveys.com.key) there was no place to input the gd_bundle.crt (The inermediate certificate package that godaddy provided us). The solution is to combine your site certificate (fluidsurveys.com.key) with your bundle certificate, root certificate, or intermediate certificates. To do this, simply make a backup of your site certificate (fluidsurveys.com.crt), then cat the intermediate bundle with the original site certificate.  “cat gd_bundle.crt >> fluidsurveys.com.crt” This should now solve your issue with IE6 not recognizing the issuer of your SSL certificate.