• Cómo migrar una app de Google Play a Amazon App Store

    Para migrar en Amazon App Store una app que ya ha sido instalada en en Google, es suficiente con muy pocos cambios.

    Preparacion de la APP


    Version Name: en tu AndroidManifest.xml usa el atributo android:versionName para definir el nombre que será mostrado a los usurios.
    Version Number: android:versionCode Igual que en Google Play
    Device filtering: En tu AndroidManifest.xml especificar los dispositivos en los que se vara la apliciacion. Este atributo es obligatorio en Amazon

    Por ejemplo:

    <supports-screens
    android:smallScreens="false"
    android:normalScreens="true"
    android:largeScreens="true"
    android:xlargeScreens="false"
    android:requiresSmallestWidthDp="335"/>

    y recomendable en Google Play. Lo suyo es dejarlo tambien en Google, con lo que en archivo APK prácticamente solo hay que hacer un cambio: el archivo APK debe compilarse sin claves para firmar.

    El proceso de subir el archivo se hace en la consola de desarrolladores. Es muy intuitivo y parecido al de Google Play. El título de la app que se rellena en la ficha solo será para uso de la consola, pero es necesario que no sea demasiado común para no confundirlo con otras apps, según me dijeron en Amazon al rechazarme una app durante un test previo a la publicación.

    En resumen:

    1. Diferencias en librerías

      Prácticamente funcionan todas la librerías, con algunas salvedades relacionadas con los recursos computacionales de Amazon

      //developer.amazon.com/es/docs/app-submission/migrate-existing-app.html#APIsandServices

    2. Device Filtering

      <supports-screens
      android:smallScreens=»false»
      android:normalScreens=»true»
      android:largeScreens=»true»
      android:xlargeScreens=»false»
      android:requiresSmallestWidthDp=»335″/>

    3. Subir APK a Amazon App Store

      Puede ser suficiente el mismo código fuente de Google Play, compilado sin firmar.

    Más documentación: //developer.amazon.com/es/docs/app-submission/getting-started.html

  • Vagrant y Puppet Server para pruebas de entornos

    El objetivo es tener en Vagrant un servidor Puppet y al menos dos servidores agentes. En este post no trataré la creación de manifiestos, sino que llegará hasta la firma automática de certificados SSL de cuantos agentes se deseen.

    Estos son entornos son muy útiles para reproducir en una máquina local, o servidor dedicado, un entorno de producción para poner a prueba la configuración de los servicios y aplicaciones.

    Empezando con Vagrant

    Todo los scripts los he usado con la versión de Vagrant de los repositorios de Ubuntu 18.04 y Virtualbox como aprovisionador también de los repositorios.

    Puppet Server

    Vagrant.configure("2") do |config|
      config.vm.box = "generic/debian9"
      config.vm.hostname = "obelix"
      config.vm.network "public_network",ip: "192.168.1.90",bridge: "enp12s0"
      config.vm.provider "virtualbox"# do |vb|
         vb.gui = false
         vb.memory = "2048"
      end
      config.ssh.insert_key = false
      config.ssh.private_key_path = ["~/.vagrant.d/insecure_private_key","/home/gustavo/.ssh/gmoreno", ]
      config.vm.provision "file", source: "~/.ssh/gmoreno.pub", destination: "~/.ssh/authorized_keys"
      config.vm.provision "file", source: "puppet.conf", destination: "/home/vagrant/puppet.conf"
      config.vm.provision "shell", path: server.sh
    end
    

    config.vm.box: la caja o «imagen» que usará para el aprovisionador. En este caso una imagén generica de Debian Stretch

    config.vm.hostname: el hostname que tendrá la VM.

    config.vm.network: confgiura una red publica, con una dirección IP estática y el bridge de la misma interfaz del sistema anfitrión.

    config.vm.provider: Define Virtualbox como aprovisonador, sin entorno gráfico y con 2048 Mb de memoría RAM.

    En el siguiente segmento se aprovisona una llave para hacer una conexión por SSH:

     config.ssh.insert_key = false
     config.ssh.private_key_path = ["~/.vagrant.d/insecure_private_key","/home/usuario/.ssh/id_rsa", ]
     config.vm.provision "file", source: "~/.ssh/id_rsa.pub", destination: "~/.ssh/authorized_keys"

    Con estos envíamos a la VM una llave privada SSH (llamada en este caso id_rsa). También una llave pública que se añade al authorized_keys. De este modo, se prepara el logueo por SSH. Harán falta algunas instrucciones más.

     config.vm.provision "file", source: "puppet.conf", destination: "/home/vagrant/puppet.conf"
     config.vm.provision "shell", path: server.sh

    Se copia el archivo puppet.conf desde el anfitrión a la VM y el script bootstrap para el aprovisionamiento de la maquina virtual.

    El puppet.conf está en el mismo directorio que el Vagrantfile

    [main]
    server = puppetserver
    certname = puppetserver
    
    [master]
    dns_alt_names = puppetserver,puppet
    

    También el bootstrap, llamado aquí server.sh

    ##### Permite el logueo SSH "normal" #####
    sed -i -e "\\#PasswordAuthentication yes# s#PasswordAuthentication yes#PasswordAuthentication no#g" /etc/ssh/sshd_config
    systemctl restart sshd.service
    #####              #####             #####
    
    #### Instala Puppet Server 5.3 desde Puppetlabs ####    
    APT_DOMAIN="//apt.puppetlabs.com"
    PCK_PUPPET="puppet5-release-stretch.deb"
    PUPPET_DEB="${APT_DOMAIN}/${PCK_PUPPET}"
    wget ${PUPPET_DEB}
    dpkg -i ${PCK_PUPPET}
    apt-get update
    apt-get install -y puppetserver 
    
    #### Copia de puppet.conf al directorio de configuración ####
    cp -f /home/vagrant/puppet.conf /etc/puppetlabs/puppet/puppet.conf
    #### Wilcard de nodos cuyo certificado serán autofirmados ####
    echo '*.casa' > /etc/puppetlabs/puppet/autosign.conf
    
    #### Memoria JVM de 2g por defecto a 500 mb  ###
    sed -i s/Xmx2g/Xmx500m/g /etc/default/puppetserver;
    sed -i s/Xms2g/Xms500m/g /etc/default/puppetserver;
    #### Y reinicio del Puppet Server ####
    systemctl start puppetserver
    

    Nodos

    En un Vagranfile pueden configurarse más de una VM. El script siguiente incluye una función que es llamada para cada máquina que se desee, con distintos nombres para el archivo puppet.conf

    def agent(hostname,ip,ssh_key_path,puppet_conf,shell)
    
      Vagrant.configure("2") do |config|
        config.vm.define hostname do |a|
          a.vm.box = "generic/debian9"
          a.vm.provider "virtualbox"
          a.vm.hostname = hostname
          a.vm.network "public_network",ip: ip, bridge: "enp12s0"
          a.ssh.insert_key = false
          a.ssh.private_key_path = ["~/.vagrant.d/insecure_private_key",ssh_key_path]
          a.vm.provision "file", source: ssh_key_path+".pub", destination: "~/.ssh/authorized_keys"
          a.vm.provision "file", source: puppet_conf, destination: "/home/vagrant/puppet.conf"
          a.vm.provision "shell", path: shell
        end
      end
    end
    
    agent("nginx","192.168.1.50","/home/usuario/.ssh/id_rsa","puppet-nginx.conf","agente.sh")
    agent("postgres","192.168.1.51","/home/usuario/.ssh/id_rsa","puppet-postgres.conf","agente.sh")
    

    Es muy similar al Vagranfile del servidor. Cabe destacar la llamada:

    agent("nginx","192.168.1.50","/home/usuario/.ssh/id_rsa","puppet-nginx.conf","agente.sh").

    Lo parametros que se introducen son, en este orden: nombre de la VM y hostname, dirección IP de la máquina, path de la llave RSA, el nombre del archivo puppet.conf y el nombre del archivo bootstrap.

    El bootstrap agente.sh para el nodo puede ser como este:

    sed -i -e «\#PasswordAuthentication yes# s#PasswordAuthentication yes#PasswordAuthentication no#g» /etc/ssh/sshd_config
    systemctl restart sshd.service

    echo "192.168.1.90 puppetserver puppet" >> /etc/hosts
    wget //apt.puppetlabs.com/puppet5-release-stretch.deb
    dpkg -i puppet5-release-stretch.deb
    apt-get update
    apt-get -y install puppet-agent
    cp /home/vagrant/puppet.conf /etc/puppetlabs/puppet/puppet.conf -f
    mkdir /var/lib/puppet
    ln -s /opt/puppetlabs/puppet/bin/puppet /bin/puppet
    puppet agent -t

    Y el puppet.conf

    [main]
    ssldir = /var/lib/puppet/ssl
    server = puppetserver
    certname = testing-nginx-r0001.casa
    
    [master]
    vardir = /var/lib/puppet
    cadir  = /var/lib/puppet/ssl/ca
    dns_alt_names = puppetserver,puppet

    El certname se debe ir cambiando a cada nueva máquina creada si se mantiene el mismo servidor. La razón para ello es que el servidor recibiría la petición de firmar un certificado con un nombre que ya existe y se negaría a hacerlo.

    En el ejemplo se usan los archivos de configuración con los nombres puppet-nginx.conf y puppet-postgres.conf entre los cuales la única diferencia de contenido es el certname.

    Gustavo Moreno

  • Despliega manifiestos Puppet en remoto

    Por sábado, 30 diciembre, 2017 0 , Permalink

    El objetivo de los scripts en Bash del repositorio que enlaza este artículo es actualizar de manera remota un servidor Puppet 4 con los últimos cambios realizados en la rama master de un repositorio Git. Los scripts se distribuyen en un módulo de Puppet. Las variables de los scripts deberán ser modificadas por el operador para adaptarlas a las necesidades de su entorno.

    Continuar Leyendo…

  • Puppet y relaciones de dependencia entre recursos

    Por miércoles, 1 marzo, 2017 0 Permalink

    Puppet declara dependencias de manera implícita, pero no siempre se cargan los recursos de la manera esperada. En Puppet hay tres maneras de declaraciones implícitas

    • title hash: Es una ordenación aleatoria pero consistente
    • manifest: Ordena siguiendo el orden de declaración en el manifiesto (por defecto en Puppet 4)
    • random: Aleatorio en cada ejecución. Es útil para debuggar problemas con relaciones de dependencia

    Los metaparámetros de dependencia explicita proveen un medio de organización de dependencias muy potente. Veámoslo.

    Ordenando recursos

    En la siguiente declaración se declara una dependencia:

    package { 'nginx':
           ensure => 'present',
           before => Service['nginx']
    
    }
    
    service { 'nginx':
           ensure => 'running',
           enabled => 'true',
           require => Package['nginx']
    }

    En este caso, el servicio ‘nginx’ se ejecutará sólo si el packete ‘nginx’ está presente. Los metaparámetros before y require son reduntanes en este caso y no ofrecen datos a otros recursos. En la declaraciones explicitas no es necesario declarar relaciones innecesarias, ya que hace más frágil el manifiesto.

    Disparadores de eventos

    Los metaparámetros notify y subscribe funcionan de manera similar a los dos anteriores, pero también enviarán eventos de refresco (refresh events) al recurso dependiente si la dependencia cambia. Por ejemplo, un servicio dependiente se recargará si su configuración cambia.

    package { 'nginx':
           ensure => 'latest',
           notify => Service['nginx']
    
    }
    
    service { 'nginx':
           ensure => 'running',
           enabled => 'true',
           subscribe => Package['nginx']
    }

    Cuando el paquete nginx se actualice a la última versión, el recurso enviará un refresh event al servicio nginx para que se recargue. Ambos metaparámetros son redundantes.

    El evento de refresco tiene un significado especial para recursos exec con el atributo refreshonly. Estos recursos sólo se aplicarán sí, y sólo si, la dependencia cambia.

    file {'nombre':
      ensure  => 'file',
      source  => 'puppet://puppetserver/modules/module_name/name.sh',
      path => '/usr/local/bin/name.sh',
      notify => Exec['name.sh']
    }
    
    exec {'name.sh'':
      path => '/usr/local/bin',
      logoutput => 'true'
      refreshonly => true,
    }

    En script name.sh no se ejecutará a menos que el archivo se haya creado.

    Flechas para encadenar recursos

    La relaciones dependencia anteriores pueden declararse usando flechas aunque, sin embargo, su uso está desaconsejado por ser de lectura más difícil. Por ejemplo, para instalar Nginx antes que iniciar el servicio:

    Package ['nginx'] -> Service['nginx']

    Para envíar un evento de refresco:

    Package ['nginx'] ~> Service['nginx']

    Es posible expresarlo a la inversa, pero esto no habría que hacerlo nunca:

     Service['nginx'] <~ Package ['nginx']

    Colectores

    Un colector es un grupo de varios recursos. Hay que usarlo con precaución, ya que puede tener consecuencias inintencionadas nefastas. Se declara con el tipo de recurso seguido de <| atributo |> Por ejemplo:

    User <||>                                          # Todos los usuarios declarados 
    Service <| ensure == running |>    # Todos los servicios que están ejecutándose
    Package <| tag == 'http' |>         # Todos los paquetes etiquetado con 'http'

    La condición de búsqueda puede ser booleana:

    Service <| (ensure == running) or (enabled == true ) |>
    Service <| (ensure == running) and (title != system' ) |>

    En resumen

    Hemos visto los siguientes controles:

    • before y la flecha ->    Expresa una dependencia
    • notify y la flecha ~>    Envía un refresh event
    • require                           Exprea un recurso dependiente
    • subscribe                       Se aplica cuando recibe un refresh event
    • refreshonly                   Se ejectuta sólo cuando recibe un refresh event

  • Facter en Puppet

    Por domingo, 19 febrero, 2017 0 Permalink

    En mi último post, vimos como instalar Puppet en la arquitectura master-agent y la creación de un módulo para declarar algunos recursos. En este, quiero mostrar cómo configurar un parámetro utilizando la aplicación Facter y despues, cómo extender Facter para tomar ese parámetro desde un archivo que lo contenga, creado «a mano» en el agente. Utilizaré la configuración del post anterior de la serie automatizaciones para el ejemplo un servicio PHP-FPM

    Modulo «zona horaria»

    El modulo que crearemos se llamará zonahoraría. Tomará la zona horaria desde un parámetro del sistema y en la segunda parte del post modificaré el manifiesto para lea desde un fact a medida.

    Instalando PHP

    Instalamos el módulo de PHP:

    cd /etc/puppetlabs/code/environment/test/modules
    /opt/puppetlabs/bin/puppet module install mayflower-php --version 4.0.0-beta1
    

     Creando módulo «zonahoraria»

    En el directorio de módulos:

    /opt/puppetlabs/bin/puppet module generate enredadera-zonahoraria
    

     Creando init.pp

    El init.pp del módulo utilizará la clase del módulo recién instalado (podéis encontar más información acerca del módulo en este enlace).

    class zonahoraria::setup {
    
    }

     Configuración global

    class { '::php::globals':
        php_version			=> '7.0',
        config_root			=> '/etc/php/7.0',
    }

     Configuración de PHP

    class { '::php':
          ensure               => latest,
          manage_repos         => true,
          fpm                  => true,
          dev                  => true,
          composer             => true,
          pear                 => true,
          phpunit              => false,
          settings             =>	{
                'PHP/max_execution_time'  => '90',
                'PHP/max_input_time'      => '300',
                'PHP/memory_limit'        => '64M',
                'PHP/post_max_size'       => '32M',
                'PHP/upload_max_filesize' => '32M',
                'Date/date.timezone'      => $timezone,
                },
          extensions    => {
                              imagick   => {
                                               provider => pecl,
                             }
          }
    }

    La variable $timezone tiene el valor (en el agente):

    /opt/puppetlabs/bin/facter timezone
    
    CET

    Precendecia

    La clase ::php::global debe ejecutarse antes de la clase ::php, por lo que ha de utilizarse algún parámetro como require para garantizar el orden de ejecución o un operador de precedencia.

    En el post anterior utilizamos el operador -> de un modo que en esta clase podríamos usar:

    Class[::php::global] -> Class['::php]
    

    No obstante, puede definirse la precedencia en las propias declaraciones de los recursos y, de este modo, la clase «zonahoraria» mostraría la siguiente estructura:

    class zonahoraria::setup {
    
      class { '::php::globals':
                
                ...
    
     	}->
      class { '::php':
                
                ...
    
            }
    }

    Ejecutando el manifiesto

    Pedimos el manifiesto desde el agente para su ejecución:

    /opt/puppetlabs/bin/puppet agent -t

     Comprobación

    Comprobamos que parámetro timezone.

    cat /etc/php/7.0/fpm/php.ini | grep timezone
    
    ; Defines the default timezone used by the date functions
    ; http://php.net/date.timezone
    ;date.timezone =
    date.timezone =CET
    

     Leyendo el parámetro desde un archivo en el agente

    Para ello hay que escribir un archivo a mano en la máquina del agente y ampliar las funcionalidades de Facter para que lea este archivo.

    Archivo con parámetro

    Creamos el archivo con la zona horaria:

    echo " Europe/Madrid" > /usr/local/etc/timezone

     Montaje del directorio

    Editamos de nuevo el archivo fileserver.conf  y añadimos el punto de montaje /usr/local/etc:

    [files]
      path /usr/local/bin:/usr/local/etc
      allow *
    

     Ampliando el comportamiento facter

    Facter permite añadir nuevos «hechos», que pueden escribirse en Ruby, y utilizarse en los manifiestos.

    Creamos un fact en el módulo «zonahoraria»:

    mkdir -p /etc/puppetlabs/code/environments/test/modules/zonahoraria/lib/facter
    vim /etc/puppetlabs/code/environments/test/modules/zonahoraria/lib/facter/zonahoraria.rb
    
    

     Fact

    Facter.add(:zonahoraria) do
      setcode do
        if File.exists? '/usr/local/etc/timezone'
          zone = File.readlines('/usr/local/etc/timezone')
          zone[0]
        end
      end
    end

     Cambiando el fact en el manifiesto

    Con la siguiente línea reemplazmos la variable de facter timezone por la que hemos creado, zonahoraria:

    find modules/zonahoraria/manifests/init.pp | xargs perl -pi -e 's/timezone/zonahoraria/g'

     Actualizando el parámetro en el agente

    Pedimos el manifiesto al master:

    /opt/puppetlabs/bin puppet agent -t

     Por ultimo

    Comprobamos que el parámetro leído del archivo se haya actualizado:

    cat /etc/php/7.0/fpm/php.ini | grep timezone
    ; Defines the default timezone used by the date functions
    ; http://php.net/date.timezone
    ;date.timezone =
    date.timezone = Europe/Madrid
    

    Esta es la idea básica de declaración de facts en Puppet. Por supuesto, hay mucho más. Una lista completa de los facts del sistema pueden obtenserse con el comando /opt/puppetlabs/bin/facter

  • Configuración de Puppet en Debian Jessie y creación de un módulo

    Por domingo, 19 febrero, 2017 0 , Permalink

    Cuando tienes hacer un despliegue en muchos servidores o automatizar tareas repetitivas, Puppet es una gran opción. En base a manifiestos declarativos, Puppet se asegura de que existan recursos, los crea sin aún no existen y los utiliza. A diferencia de muchos otros lenguajes, en los manifiestos no se definen los procesos para aprovechar los recursos, sino que se describe «lo que debe haber» y Puppet se encarga de hacerlo realidad. Así ha sido dicho y así será hecho.

    Continuar Leyendo…

  • Listar archivos de directorio remoto en WordPress con Linux

    Por miércoles, 2 marzo, 2016 0 Permalink

    Cuando sea necesario listar contenidos de carpetas compartidas en una intranet con WordPress, hay algunas posibilidades. Una de ellas es cargar el recurso compartido con enlaces con un aspecto similar a éste -> file://FileServer/Public

    El problema de cargar el directorio remoto con el protocolo file:// es que para muchos navegadores son necesarios instalar plugins en cada uno de ellos. Es una solución válida, pero requiere instalar los plugins en cada navegador de los ordenadores que utilicen la intranet, lo que podría ser un dolor en redes de gran tamaño.

    Otra opción es montar el directorio remoto con cifs en un punto de montaje del host virtual dónde está la instalación del WordPress. De ese modo, WordPress podrá acceder a los archivos remotos a través de los enlaces en la máquina local como si estuvieran físicamente en ésta.

    Para mostrar el contenido de un directorio local como contenido de un post o página existe el plugin m1DownloadList.

    Preparativos

    Si no tenemos cifs, lo instalamos:

    $ sudo apt install cifs-utils

    Tenemos en una máquina remota una carpeta compartida SMB con permisos de solo lectura llamada \\SRVFILE\Public con credenciales para el usuario gato con el password Pajaro

    Vayamos primero a instalar los paquetes necesarios en el servidor Linux. Este post está escrito pensando en Debian y derivadas, pero en otras distribuciones será muy, muy parecido.

    Supongamos que tenemos el host virtual en la ruta:

    /srv/wordpress/intranet

    Entonces, creamos un directorio dentro de esa ruta:

    $ sudo mkdir /srv/wordpress/intranet/srvfile

    Credenciales

    Creamos en el servidor donde está alojado el WordPress un archivo con las credenciales cifs, llamado /etc/smbcredentials, con el suguiente contenido:

    username=gato
    password=Pajaro

    Interesa que el contenido de este archivo sea leído por el mínimo número de usuarios posible, lo ideal sería que sólo lo lea root en los sitemas Debian o el usuario con permisos de administración de distribuciones Ubuntu. Como regla general, sería de ayuda establecer estos permisos:

    $ sudo chmod 500 /etc/smbcredentials

    Montaje automático al arranque

    En el archivo /etc/fstab están definidas las particiones que se montarán en el sistema de archivos al inicio del sistema, además de comparticiones remotas.  Las particiones y las shares «se montan» (en el argot linuxero) en cualquier punto del árbol de directorios, lo que equivaldría a decir que las carpetas compartidas pueden mapearse hacia cualquier lugar. Es en la ruta /srv/wordpress/intranet/srvfile donde montaremos el recurso compartido.

    Editamos el archivo /etc/fstab con permisos de escritura:

    $ sudo vi /etc/fstab

    Calma: si no te gusta Vi, puedes usar Emacs, Nano o aquel con el que te sientas mas cómodo 😉

    y añadimos la siguiente línea:

    //SRVFILE/Public     /srv/wordpress/intranet/srvfile     cifs     credentials=/etc/smbpasswords       0     0

    Hay mas opciones, claro; para ello te dejo que consultes las páginas man y lo tunees al pelo según tus necesidades 🙂

    Para comprobar que esté todo bien, no es imprescindible reiniciar. Podemos montar la carpeta compartida con el comando:

    $ sudo mount -a

    Lo que montará toda la tabla de sistemas de archivos del /etc/fstab

    Si todo ha ido bien, veremos los archivos compartidos con el comando:

    $ ls /srv/wordpress/intranet/srvfile

    Plugin de WordPress M1 Download List

    Se trata de un plugin para listar directorios locales sin subirlos con la interfaz de WordPress.

    Su uso es muy sencillo; tras activar el plugin, basta crear una página o artículo con un shortcode definiendo en él la ruta local a partir del directorio raíz del host virtual.

    Como indican las instrucciones del plugin, para visualizar el recurso compartido anteriormente es suficiente con introducir este shortcode:

    [m1dll path="srvfile/"]

    Y saldrá algo como esto:

    m1downloadlist

    Es posible que tengas que configurar el juego de caracteres para que se visualicen los acentos y eñes, pero eso vendrá más adelante.

     

  • Juegos de iconos RAVEfinity-X para Linux Mint 17 y Ubuntu 14.04

    Por viernes, 4 julio, 2014 0 , Permalink

    Nos llega de la mano de RAVEfinity un set de iconos para Linux Mint 17, y otras distribuciones derivadas de Ubuntu 14.04. Esta vez se trata del juego RAVEfinity-X, un mezcla de los juegos Faenza, Humanity, Elementary y otros temas de iconos. Está compuesto de 12 colores con variantes para darles un matiz claro u oscuro.

    Continuar Leyendo…

  • Impresora de red Brother DCP-7055W con Linux Mint

    La impresora de red Brother DCP-7055W tiene una configuracón sencilla gracias a un script que se puede descargar desde la página del fabricante.

    Doy por hecho que tenemos la impresora ya configurada. Si no es así, hay que configurar los parámetros TCP/IP y SSID desde la misma pantalla LED de la impresora o con la aplicación que han incluido en el CD.