Mantener los hostnames anteriores a medida que se cambia de nombre

Para no romper links preexistentes.  Lo ideal sería que todos los Deploy
se vinculen al directorio principal para no romper links, pero la idea
es no romperlos (?)
This commit is contained in:
f 2021-08-14 20:21:30 -03:00
parent e6ea1001cd
commit 341a693a35
7 changed files with 111 additions and 16 deletions

View file

@ -5,20 +5,27 @@ require 'open3'
# Este modelo implementa los distintos tipos de alojamiento que provee # Este modelo implementa los distintos tipos de alojamiento que provee
# Sutty. # Sutty.
# #
# Cuando cambia el hostname de un Deploy, generamos un
# DeployAlternativeDomain en su lugar. Esto permite que no se rompan
# links preexistentes y que el nombre no pueda ser tomado por alguien
# más.
#
# TODO: Cambiar el nombre a algo que no sea industrial/militar. # TODO: Cambiar el nombre a algo que no sea industrial/militar.
class Deploy < ApplicationRecord class Deploy < ApplicationRecord
# Un sitio puede tener muchas formas de publicarse. # Un sitio puede tener muchas formas de publicarse.
belongs_to :site belongs_to :site
# Puede tener muchos access logs a través del hostname # Puede tener muchos access logs a través del hostname
has_many :access_logs, primary_key: 'hostname', foreign_key: 'host' has_many :access_logs, primary_key: 'hostname', foreign_key: 'host'
# Registro de las tareas ejecutadas
has_many :build_stats, dependent: :destroy
# Siempre generar el hostname # Siempre generar el hostname
after_initialize :default_hostname! after_initialize :default_hostname!
# Eliminar los archivos generados por el deploy. # Eliminar los archivos generados por el deploy.
before_destroy :remove_destination! before_destroy :remove_destination!
# Los hostnames alternativos se crean después de actualizar, cuando ya
# Registro de las tareas ejecutadas # se modificó el hostname.
has_many :build_stats, dependent: :destroy around_update :create_alternative_domain!, if: :destination_changed?
# Siempre tienen que pertenecer a un sitio # Siempre tienen que pertenecer a un sitio
validates :site, presence: true validates :site, presence: true
@ -34,6 +41,37 @@ class Deploy < ApplicationRecord
where(hostname: hostname).includes(:site).pluck(:name).first where(hostname: hostname).includes(:site).pluck(:name).first
end end
# Detecta si el destino existe y si no es un symlink roto.
def exist?
File.exist? destination
end
# Detecta si el link está roto
def broken?
File.symlink?(destination) && !File.exist?(File.readlink(destination))
end
# Ubicación del deploy
#
# @return [String] Una ruta en el sistema de archivos
def destination
File.join(Rails.root, '_deploy', hostname)
end
# Ubicación anterior del deploy
#
# @return [String] Una ruta en el sistema de archivos
def destination_was
return destination unless will_save_change_to_hostname?
File.join(Rails.root, '_deploy', hostname_was)
end
# Determina si la ubicación cambió
def destination_changed?
persisted? && will_save_change_to_hostname?
end
# Genera el hostname # Genera el hostname
# #
# @return [String] # @return [String]
@ -143,6 +181,20 @@ class Deploy < ApplicationRecord
raise NotImplementedError raise NotImplementedError
end end
# Cuando el deploy cambia de hostname, generamos un dominio
# alternativo para no romper links hacia este sitio.
def create_alternative_domain!
hw = hostname_was
# Aplicar la actualización
yield
# Crear el deploy alternativo con el nombre anterior una vez que
# lo cambiamos en la base de datos.
ad = site.deploys.create(type: 'DeployAlternativeDomain', hostname: hw)
ad.deploy if ad.persisted?
end
# Convierte el comando en una versión resumida. # Convierte el comando en una versión resumida.
# #
# @param [String] # @param [String]

View file

@ -37,6 +37,11 @@ class DeployHiddenService < DeployWww
private private
# No soportamos cambiar de onion
def destination_changed?
false
end
def implements_hostname_validation? def implements_hostname_validation?
true true
end end

View file

@ -3,7 +3,7 @@
# Alojamiento local, genera el sitio como si corriéramos `jekyll build`. # Alojamiento local, genera el sitio como si corriéramos `jekyll build`.
class DeployLocal < Deploy class DeployLocal < Deploy
# Asegurarse que el hostname es el permitido. # Asegurarse que el hostname es el permitido.
before_save :reset_hostname!, :default_hostname! before_validation :reset_hostname!, :default_hostname!
# Realizamos la construcción del sitio usando Jekyll y un entorno # Realizamos la construcción del sitio usando Jekyll y un entorno
# limpio para no pasarle secretos # limpio para no pasarle secretos
@ -30,13 +30,6 @@ class DeployLocal < Deploy
end.inject(:+) end.inject(:+)
end end
# La ubicación del sitio luego de generarlo.
#
# @return [String]
def destination
File.join(Rails.root, '_deploy', hostname)
end
# El hostname es el nombre del sitio más el dominio principal. # El hostname es el nombre del sitio más el dominio principal.
# #
# @return [String] # @return [String]

View file

@ -20,11 +20,6 @@ class DeployWww < Deploy
File.size destination File.size destination
end end
# @return [String]
def destination
File.join(Rails.root, '_deploy', hostname)
end
# El hostname por defecto incluye WWW # El hostname por defecto incluye WWW
# #
# @return [String] # @return [String]

View file

@ -143,6 +143,10 @@ en:
tienda_api_key: Store access key tienda_api_key: Store access key
errors: errors:
models: models:
deploy:
attributes:
hostname:
destination_exist: 'There already is a file in the destination'
site: site:
attributes: attributes:
name: name:

View file

@ -143,6 +143,10 @@ es:
tienda_api_key: Clave de acceso tienda_api_key: Clave de acceso
errors: errors:
models: models:
deploy:
attributes:
hostname:
destination_exist: 'Ya hay un archivo en esta ubicación'
site: site:
attributes: attributes:
name: name:

View file

@ -22,4 +22,46 @@ class Site::DeploymentTest < ActiveSupport::TestCase
assert site_pre.deploys.create(type: 'DeployAlternativeDomain', hostname: "#{dup_name}.#{Site.domain}") assert site_pre.deploys.create(type: 'DeployAlternativeDomain', hostname: "#{dup_name}.#{Site.domain}")
assert_not site.update(name: dup_name) assert_not site.update(name: dup_name)
end end
test 'al cambiar el nombre se crea un deploy alternativo' do
site_name = site.name
new_name = SecureRandom.hex
original_destination = site.deploy_local.destination
urls = [site.url]
assert site.deploy_local.deploy
assert_not site.deploy_local.destination_changed?
assert site.update(name: new_name)
urls << site.url
assert_equal urls.sort, site.urls.sort
assert File.symlink?(original_destination)
assert File.exist?(site.deploy_local.destination)
assert_equal 2, site.deploys.count
end
test 'al cambiar el nombre se renombra el directorio' do
site_name = site.name
new_name = "test-#{SecureRandom.hex}"
original_destination = site.deploy_local.destination
assert site.deploy_local.deploy
assert_not site.deploy_local.destination_changed?
assert site.update(name: new_name)
assert site.deploy_local.hostname.start_with?(new_name)
assert File.symlink?(original_destination)
assert File.exist?(site.deploy_local.destination)
end
test 'al cambiar el nombre varias veces se crean varios links' do
assert site.deploy_local.deploy
q = rand(3..10)
q.times do
assert site.update(name: "test-#{SecureRandom.hex}")
end
assert_equal q, site.deploys.count
end
end end