Creando una aplicación en Python con SAP BTP foundry (Parte 2)

Inicialmente la entrada de Python tenia que ser una sola entrada pero al querer crear el fichero MTAR para hacer el deploy mediante mi sistema CD/CI he visto que no hay una guia fácil para crear el fichero mta.yaml.

Así que en la entrada de hoy aprenderemos a crear un fichero mta.yaml para una aplicación Python y algun truco extra para añadir seguridad a nuestra aplicación. En esta entrada también profundizaremos en los conceptos de este tipo de ficheros.

mbt - npm

Si te perdiste el post anterior, aquí lo tienes:

Creando el fichero mta.yaml

Lo primero, aunque sea trivial es crear el dichero «mta.yaml» en la raíz de nuestro proyecto. Y hasta aquí el blog de hoy, espero que os sea de utilidad… Nada, una broma para romper el hielo 😉

Lo siguiente es añadir los módulos y requires necesarios para realizar los despliegues. Como os he comentado partiremos de la base que la aplicación tiene una API en python y una pequeña aplicación web basado en el post anterior.

Diseccionando el fichero mta.yaml

Lo primero que vamos a hacer es definir la estructura básica del fichero que consta de la cabecera, los módulos y los resources, esta sera la estructura que usaremos para empezar. Para seguir esta guia te aconsejo que te centres en mirar cada una de las partes que vamos añadiendo. Al final del post añadiré el resultado del fichero.

ID: myapp
_schema-version: '3.3'
version: 1.0.1
modules:
resources:

Estas son las distintas secciones:

  • ID: Nombre de la aplicación, esto tendra su importancia en la asignación de nombres, por ejemplo la URL
  • _Schea-version: Versión del fichero mta, siempre es aconsejable revisar cual es la ultima version del fichero por si hay nuevas funcionalidades.
  • version: Versión de nuestra aplicación para su despliegue, cloud foundry nos permite subir la misma version deployada o superior. Mediante comandos podemos podemos forzar versiones anteriores. Como norma general al realizar un despliegue a productivo se sube la versión.
  • modules: aplicaciones que se crearan en nuestro space, también se definen los bindings y los parámetros de esa aplicación
  • resources: servicios que se crearán o se reutilizarán (porque ya estan creadas). Aquí por ejemplo añadiremos servicios con los que tenemos que hacer binding.

Si alguno de los conceptos anteriores te despista, no te preocupes, seguramente ahora con el ejemplo lo entenderas.

En nuestra aplicación del post anterior generamos de manera manual un servicio XSUAA, como ya sabréis, este servicio nos permite gestionar la seguridad de nuestras aplicaciones. En el Post anterior lo creamos a mano, pero en esta entrada queremos automatizar todo mediante CD/CI por lo que vamos a añadir la creación del servicio, la creación la añadiremos en el bloque «resources»:

resources:
  - name: myapp_uaa
    parameters:
      path: ./xs-security.json  
      service: xsuaa    
      service-plan: application
      service-keys: 
        - name: myapp-service-key      
    type: org.cloudfoundry.managed-service  

Empiezo por el final, mediante el parámetro «type» indicaremos que es necesario crear el servicio al realizar el deploy. En el caso que el servicio exista, podemos utilizar «org.cloudfoundry.existing-service».

Lo siguiente es el servicio que definiremos mediante «name» (con el nombre que queramos) y le añadiremos las siguientes propiedades:

  • path: path del fichero de configuracion JSON, en este caso, mediante este fichero podemos definir los scopes
  • service: indicamos el servicio a crear, en este caso xsuaa, pero podria ser el de destinations, un contenedor de base de datos (HDI), etc.
  • service-plan: en algunos casos los servicios tiene distintos planes que pueden implicar distintos costes o/y puede tener distintas funcionalidades. En nuestro caso le indicamos que queremos el tipo application ya que este servicio nos permitirá tanto tener un login para el usuario como utilizar las service keys.
  • service-keys: crearemos un juego de claves para poder utilizar nuestro servicio mediante API con client credentials. Este paso no es necesario si solo se quiere utilizar el appRouter como punto de entrada.

Una vez definidos los servicios que vamos a utilizar, nos toca dar de alta las aplicaciones. Esto lo haremos en la sección modules. Vamos a empezar con nuestra aplicación python.

modules:
  - name: myapp
    type: python
    path: .
    parameters:
      memory: 200M
      disk-quota: 800M
      command: python server.py
    requires:
    - name: myapp_uaa
    provides:
    - name: myapp-binding-dest
      properties:
        srv-url: ${default-url}

Lo primero sera definir la aplicación con un nombre, en nuestro caso en name pondremos «myapp». Todas las aplicaciones tiene un tipo que definiremos con type, esto le permite a Cloud Foundry seleccionar el build pack apropiado para cada contenedor.

Lo siguiente es añadir el path de nuestra aplicación. En el post anterior nuestra aplicación la añadimos en la carpeta raíz por lo que con «.» será suficiente.

Pasemos a las propiedades:

  • parameters: aquí podemos definir los parámetros «memory» y «disk-quota» que aunque no son obligatorios siempre se recomienda añadir para optimizar el espacio reservado para la aplicación. El parámetro que si es obligatorio para esta API es el «command» que nos permitirá definir como arrancamos el servidor.
  • requires: esto permitirá hacer un binding a nuestra aplicación con el servicio xsuaa que hemos definido anteriormente, así nos ahorramos hacer el binding de manera manual o por consola.
  • provides: este parámetro tampoco es obligatorio, lo he añadido para mostraros como hacer una aplicación que dependa toda ella de si misma, es decir, no hace falta que definamos la destination a nivel de subcuenta ni hagamos binding con el servicio de destinations. La propia aplicación llevará la destination. en este punto lo que se define realmente es la URL del servicio donde decimos que la que nos proponga la plataforma ya nos va bien.

Sigamos ahora con el último punto, nuestro appRouter para que el usuario pueda entrar a la aplicació de una manera segura, el siguiente código lo añadiremos después de la aplicación anterior.

  - name: myappAppRouter
    type: approuter.nodejs
    path: web
    build-parameters:
      ignore: ["node_modules/"] 
    parameters:
     disk-quota: 256M
     memory: 128M
    requires:
    - name: myapp_uaa

    - name: myapp-binding-dest
      group: destinations
      properties:
         forwardAuthToken: true
         url: ~{srv-url}
         name: myapp-binding-dest

Como en el caso anterior, lo primero sera darle un nombre a este AppRouter con el parametro «name«. En este caso, y como ya sabréis, el appRouter es una aplicación tipo node.js, por lo que lo añadiremos en el «type«. Si os acordais del post anterior, todo lo referente al appRouter lo añadimos en la carpeta web, así que lo indicaremos en «path».

Lo siguiente es un pequeño truco, mediante «build-parameters» le avisaremos al deployer que no queremos subir las carpetas de librerías, esto nos ahorrara tiempo ya que al hacer el despliegue se vuelven a descargar, así que el fichero sera mas liviano.

Como en la aplicación anterior, vamos a tener cuidado de ajustar los parámetros de memoria para no gastar mas de lo necesaria, los que añado en este ejemplo son los recomendados por SAP.

Y por último llegamos al «requires» donde haremos un binding con el servicio XSUAA para que podamos gestionar el login entre la aplicación en Python y nuestro appRouter y un binding con el servicio interno que hemos creado en la API, donde además le añadiremos de manera dynamica la url que se asigne a la aplicación Python.

Y ya tenemos nuestro fichero de despliegue apunto. Vamos a verlo entero:

Fichero compleo

ID: myapp
_schema-version: '3.3'
version: 1.0.1
modules:
  - name: myapp
    type: python
    path: .
    parameters:
      memory: 200M
      disk-quota: 800M
      command: python server.py
    requires:
    - name: myapp_uaa
    provides:
    - name: myapp-binding-dest
      properties:
        srv-url: ${default-url}
  - name: myappAppRouter
    type: approuter.nodejs
    path: web
    build-parameters:
      ignore: ["node_modules/"] 
    parameters:
     disk-quota: 256M
     memory: 128M
    requires:
    - name: myapp_uaa
    - name: myapp-binding-dest
      group: destinations
      properties:
         forwardAuthToken: true
         url: ~{srv-url}
         name: myapp-binding-dest
resources:
  - name: myapp_uaa
    parameters:
      path: ./xs-security.json  
      service: xsuaa    
      service-plan: application
      service-keys: 
        - name: myapp-service-key      
    type: org.cloudfoundry.managed-service   

Despliegue

Añado esta sección como adicional, ya que el despliegue se realiza como con cualquier otra aplicación en SAP BTP foundry, es decir:

  • mbt build –> para construir nuestro fichero MTAR
  • cf deploy –> para realizar el deploy

Truco de seguridad

Como truco extra, os añado una pequeña función en python para validar la seguridad de vuestro servicio:

    if 'authorization' not in request.headers:
        abort(403)
    access_token = request.headers.get('authorization')[7:]
    print(access_token)
    security_context = xssec.create_security_context(access_token, uaa_service)
    isAuthorized_uaa = security_context.check_scope('uaa.resource')
    isAuthorized = security_context.check_scope('openid')
    #print( isAuthorized)
    print( security_context)
    if not isAuthorized and not isAuthorized_uaa:
        abort(403)

Con este código podemos validar los scopes de manera fácil y en el caso que el usuario no tenga toquen lanzar una excepción. Este exemple en concreto valida que exista uno de los dos scopes «openid» o «uaa.resource» ya que dependiendo de la entrada del usuario (por API directo o por AppRouter) tendrá uno o el otro, pero podemos jugar con otros scopes personalizados.


Como veis con un simple fichero podemos preparar nuestra estrategia CI/CD en aplicaciones python desplegadas sobre SAP BTP Foundry.

Como siempre suscribete, dale a la campanita de notificaciones y comparte en redes para estar a la última.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.