Blog

Empaquetando con Vagrant, cookbooks incluídos. Paths.

Empaquetando con Vagrant, cookbooks incluídos. Paths.

Getting Started With Django es una guía que, como su nombre indica, enseña Django. En ella, se utiliza una máquina VirtualBox a través de Vagrant. Quería usarla en otros manuales, pero me daba miedo estropearla. Podría haber creado una nueva, siguiendo el mismo procedimiento utilizado en la original. Sin embargo, ese proceso incluye un script de post-instalación que lleva un tiempo considerable. Deseaba evitarlo si era posible. Por ello, decidí emplear otra solución: copiar la máquina virtual. En el directorio de proyecto Vagrant de la máquina a copiar, cambiamos esta línea del fichero Vagrantfile:
config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = %w{cookbooks site-cookbooks} # Lo mismo que poner [cookbooks, site-cookbooks]
    ...
por esta otra:
config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = [File.expand_path("cookbooks", File.dirname(__FILE__)), File.expand_path("site-cookbooks", File.dirname(__FILE__))]
    ...
Después explicaremos el porqué de este cambio. Guardamos el fichero, y a continuación, empaquetamos la máquina virtual. En ella, incluímos lo relacionados con Chef Solo (en mi caso, archivo Cheffile y directorios cookbooks y site-cookbooks). Vagrantfile también está contenido, pero no mediante --include, sino con --vagrantfile.
[usuario@localhost gswd-vagrant]$ vagrant package --include Cheffile,cookbooks,site-cookbooks --vagrantfile Vagrantfile
[default] Clearing any previously set forwarded ports...
[default] Creating temporary directory for export...
[default] Exporting VM...
[default] Compressing package to: /home/usuario/Documents/django/GSWD/gswd-vagrant/package.box
[default] Packaging additional file: Cheffile
[default] Packaging additional file: cookbooks
[default] Packaging additional file: site-cookbooks
[default] Packaging additional file: Vagrantfile
Se genera un fichero package.box en el directorio actual. Añadimos este «box» a Vagrant, y le damos un nombre. Lo he llamado django_book_machine:
[usuario@localhost gswd-vagrant]$ vagrant box add django_book_machine package.box virtualbox
Downloading or copying the box...
Extracting box...te: 99.8M/s, Estimated time remaining: --:--:--)
Successfully added box 'django_book_machine' with provider 'virtualbox'!
La ruta de este nuevo box es ~/.vagrant.d/boxes/django_book_machine/virtualbox. A continuación, creamos un directorio de proyecto Vagrant para la nueva máquina virtual, que usará el box que acabamos de añadir:
[usuario@localhost django]$ mkdir django_book_vagrant
[usuario@localhost django_book_vagrant]$ vagrant init
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
Se produce un nuevo Vagrantfile, distinto al original. El original, recordemos, se sitúa en el box de django_book_machine (y por supuesto, también en el directorio de proyecto de la máquina original, desde donde lo hemos empaquetado mediante --vagrantfile). Editamos el fichero Vagrantfile que acabamos de generar, y en config.vm.box especificamos el nombre del «box» agregado anteriormente:
config.vm.box = "django_book_machine"
Ahora ya podemos arrancar la máquina virtual copiada:
[usuario@localhost django_book_vagrant]$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
...
La razón para el cambio de chef.cookbooks_path al comienzo de este documento es debida al orden de carga de nuestros Vagrantfiles. Como se puede ver en la documentación, pueden haber hasta cuatro, aunque en nuestro caso sólo son dos, que corresponden con el segundo y cuarto orden de carga:
2. Vagrantfile packaged with the box that is to be used for a given machine. 4. Vagrantfile from the project directory. This is the Vagrantfile that you’ll be modifying most of the time.
Como los directorios de nuestros dos Vagrantfiles son distintos, también lo son los paths a los que apuntan. chef.cookbooks_path = cookbooks apuntará a ~/.vagrant.d/boxes/django_book_machine/virtualbox/cookbooks para el Vagrantfile del box, y a django_book_vagrant/cookbooks para el Vagrantfile del directorio de proyecto, que es el último en cargar y el que por tanto tiene efecto. Falla porque ahí no están cookbooks y site-cookbooks:
[usuario@localhost django_book_vagrant]$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Clearing any previously set forwarded ports...
[default] The cookbook path '/home/usuario/Documents/django/django_book/django_book_vagrant/cookbooks' doesn't exist. Ignoring...
[default] The cookbook path '/home/usuario/Documents/django/django_book/django_book_vagrant/site-cookbooks' doesn't exist. Ignoring...
¿Cómo solucionamos esto? Sencillo: con direcciones absolutas en lugar de relativas (utilizar solamente el nombre del directorio como ocurría antes de realizar el cambio de chef.cookbooks_path es utilizar una ruta relativa). Podríamos ponerlo «a pelo», escribiendo chef.cookbooks_path = ~/.vagrant.d/boxes/django_book_machine/virtualbox/cookbooks, pero no podemos estar seguros de que una versión futura de Vagrant emplee uno distinto, en cuyo caso dejaría de funcionar. La solución es que Ruby, el lenguaje interpretado con el que está hecho Vagrantfile, haga el trabajo. File.dirname(__FILE__) nos devuelve el directorio en que el que __FILE__, en nuestro caso Vagrantfile, está contenido. Mediante File.expand_path(directorio_cookbooks, File.dirname(__FILE__)) juntamos File.dirname(__FILE__) y directorio_cookbooks.