API de SSH
Los helpers para conexión SSH, ejecución remota y transferencia de archivos
están disponibles bajo ptool.ssh y p.ssh.
ptool.ssh.connect
v0.1.0- Introduced.
ptool.ssh.connect(target_or_options) prepara un manejador de conexión SSH
respaldado por el comando ssh del sistema y devuelve un objeto Connection.
ssh debe estar disponible en PATH.
Argumentos:
target_or_options(string|table, obligatorio):- Cuando se proporciona una cadena, se trata como un destino SSH.
- Cuando se proporciona una tabla, actualmente admite:
target(string, opcional): Cadena de destino SSH, como"[email protected]"o"[email protected]:2222".host(string, opcional): Nombre de host o dirección IP.user(string, opcional): Nombre de usuario SSH. Por defecto usa$USER, o"root"si$USERno está disponible.port(integer, opcional): Puerto SSH. Por defecto es22.auth(table, opcional): Configuración de autenticación.host_key(table, opcional): Configuración de verificación de clave de host.connect_timeout_ms(integer, opcional): Timeout en milisegundos. Por defecto es10000.keepalive_interval_ms(integer, opcional): Intervalo de keepalive en milisegundos.
Ejemplos de cadenas de destino admitidas:
local a = ptool.ssh.connect("[email protected]")
local b = ptool.ssh.connect("[email protected]:2222")
local c = ptool.ssh.connect("[2001:db8::10]:2222")
Campos de auth:
private_key_file(string, opcional): Ruta a un archivo de clave privada.private_key_passphrase(string, opcional): Frase de contraseña de la clave privada. Actualmente no es compatible.password(string, opcional): Autenticación basada en contraseña. Actualmente no es compatible.
Comportamiento de autenticación:
- Si se proporciona
auth.private_key_file,ptoolinvocasshcon esa clave mediante-iy también estableceIdentitiesOnly=yes. - Si se proporciona
auth.private_key_passphraseoauth.password,ptool.ssh.connect(...)falla porque esta API no pasa esos secretos al comandosshdel sistema. - En caso contrario, la autenticación se delega a la configuración local de
OpenSSH, incluidas opciones y mecanismos como
IdentityFile,ProxyJump,ProxyCommand,ssh-agenty certificados. - Las rutas de clave relativas se resuelven desde el directorio de runtime
actual de
ptool, por lo que siguenptool.cd(...). ~y~/...se expanden en las rutas de clave.
Campos de host_key:
verify(string, opcional): Modo de verificación de la clave de host. Valores admitidos:"known_hosts": Verifica contra un archivoknown_hosts(predeterminado)."ignore": Omite la verificación de la clave de host.
known_hosts_file(string, opcional): Ruta a un archivoknown_hosts. Solo se usa cuandoverify = "known_hosts".
Comportamiento de clave de host:
- Si
verify = "ignore",ptoolinvocasshconStrictHostKeyChecking=noyUserKnownHostsFile=/dev/null. - Si
verify = "known_hosts"y se proporcionaknown_hosts_file,ptoolinvocasshconStrictHostKeyChecking=yesy eseUserKnownHostsFile. - Si
verify = "known_hosts"y se omiteknown_hosts_file, o sihost_keyse omite por completo, el manejo de la clave de host se delega a la configuración local de OpenSSH y a sus valores predeterminados. - Las rutas relativas de
known_hosts_filese resuelven desde el directorio de runtime actual deptool. ~y~/...se expanden enknown_hosts_file.- Cuando
known_hosts_filese proporciona explícitamente, reemplaza el valor predeterminado deUserKnownHostsFileque usa el comandosshlocal para esta conexión.
Ejemplo:
local ssh = ptool.ssh.connect({
host = "example.com",
user = "deploy",
port = 22,
auth = {
private_key_file = "~/.ssh/id_ed25519",
},
host_key = {
verify = "known_hosts",
},
})
Connection
v0.1.0- Introduced.
Connection representa un manejador de conexión respaldado por OpenSSH
devuelto por ptool.ssh.connect().
Está implementado como un userdata de Lua.
Campos y métodos:
- Campos:
conn.host(string)conn.user(string)conn.port(integer)conn.target(string)
- Métodos:
conn:run(...)->tableconn:run_capture(...)->tableconn:path(path)->RemotePathconn:exists(path)->booleanconn:is_file(path)->booleanconn:is_dir(path)->booleanconn:upload(local_path, remote_path[, options])->tableconn:download(remote_path, local_path[, options])->tableconn:close()->nil
run
v0.1.0- Introduced.
Canonical API name: ptool.ssh.Connection:run.
conn:run(...) ejecuta un comando remoto a través de la conexión SSH actual.
Se admiten las siguientes formas de llamada:
conn:run("hostname")
conn:run("echo", "hello world")
conn:run("echo", {"hello", "world"})
conn:run("hostname", { stdout = "capture" })
conn:run("echo", {"hello", "world"}, { stdout = "capture" })
conn:run({ cmd = "git", args = {"rev-parse", "HEAD"} })
Reglas de argumentos:
conn:run(cmdline):cmdlinese envía como la cadena de comando remoto.conn:run(cmd, argsline):cmdse trata como el comando, yargslinese divide usando reglas de estilo shell (shlex).conn:run(cmd, args):cmdes una cadena yargses un arreglo de cadenas. Los argumentos se escapan para shell antes de la ejecución remota.conn:run(cmdline, options):optionssobrescribe esta invocación.conn:run(cmd, args, options):optionssobrescribe esta invocación.conn:run(options):optionses una tabla.- Cuando el segundo argumento es una tabla: si es un arreglo (claves enteras
consecutivas
1..n), se trata comoargs; en caso contrario se trata comooptions.
Cuando se usa conn:run(options), options actualmente admite:
cmd(string, obligatorio): Nombre del comando o ruta del ejecutable.args(string[], opcional): Lista de argumentos.cwd(string, opcional): Directorio de trabajo remoto. Se aplica anteponiendocd ... &&al comando shell remoto generado.env(table, opcional): Variables de entorno remotas, donde claves y valores son cadenas. Se aplica anteponiendoexport ... &&al comando shell remoto generado.stdin(string, opcional): Cadena enviada al stdin del proceso remoto.echo(boolean, opcional): Si debe imprimir el comando remoto antes de ejecutarlo. Por defecto estrue.check(boolean, opcional): Si debe lanzar un error inmediatamente cuando el código de salida no sea0. Por defecto esfalse.stdout(string, opcional): Estrategia de manejo de stdout. Valores admitidos:"inherit": Hereda el terminal actual (predeterminado)."capture": Captura enres.stdout."null": Descarta la salida.
stderr(string, opcional): Estrategia de manejo de stderr. Valores admitidos:"inherit": Hereda el terminal actual (predeterminado)."capture": Captura enres.stderr."null": Descarta la salida.
Cuando se usan las formas abreviadas, la tabla options solo admite:
stdin(string, opcional): Cadena enviada al stdin del proceso remoto.echo(boolean, opcional): Si debe imprimir el comando remoto antes de ejecutarlo. Por defecto estrue.check(boolean, opcional): Si debe lanzar un error inmediatamente cuando el código de salida no sea0. Por defecto esfalse.stdout(string, opcional): Estrategia de manejo de stdout. Valores admitidos:"inherit": Hereda el terminal actual (predeterminado)."capture": Captura enres.stdout."null": Descarta la salida.
stderr(string, opcional): Estrategia de manejo de stderr. Valores admitidos:"inherit": Hereda el terminal actual (predeterminado)."capture": Captura enres.stderr."null": Descarta la salida.
Reglas del valor de retorno:
- Siempre se devuelve una tabla con los siguientes campos:
ok(boolean): Si el código de salida remoto es0.code(integer|nil): Código de salida remoto. Si el proceso remoto termina por señal, este valor esnil.target(string): Cadena de destino SSH con el formatouser@host:port.stdout(string, opcional): Presente cuandostdout = "capture".stderr(string, opcional): Presente cuandostderr = "capture".assert_ok(self)(function): Lanza un error cuandook = false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local res = ssh:run("uname -a", { stdout = "capture" })
print(res.target)
print(res.stdout)
local res2 = ssh:run({
cmd = "git",
args = {"rev-parse", "HEAD"},
cwd = "/srv/app",
env = {
FOO = "bar",
},
stdout = "capture",
check = true,
})
print(res2.stdout)
run_capture
Unreleased- Introduced.
Canonical API name: ptool.ssh.Connection:run_capture.
conn:run_capture(...) ejecuta un comando remoto a través de la conexión SSH
actual.
Acepta las mismas formas de llamada, reglas de argumentos, reglas del valor de
retorno y opciones que conn:run(...).
La única diferencia es el manejo predeterminado de flujos:
stdoutusa"capture"por defecto.stderrusa"capture"por defecto.
Todavía puedes sobrescribir cualquiera de esos campos explícitamente en
options.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local res = ssh:run_capture("uname -a")
print(res.stdout)
local res2 = ssh:run_capture({
cmd = "sh",
args = {"-c", "printf 'out'; printf 'err' >&2"},
cwd = "/srv/app",
})
print(res2.stdout)
print(res2.stderr)
local res3 = ssh:run_capture("echo hello", {
stderr = "inherit",
})
print(res3.stdout)
path
v0.1.0- Introduced.
Canonical API name: ptool.ssh.Connection:path.
conn:path(path) crea un objeto RemotePath reutilizable vinculado a la
conexión SSH actual.
path(string, obligatorio): La ruta remota.- Devuelve: Un objeto
RemotePathque puede pasarse aconn:upload(...),conn:download(...)yptool.fs.copy(...).
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local remote_release = ssh:path("/srv/app/releases/current.tar.gz")
ssh:download(remote_release, "./tmp/current.tar.gz")
exists
v0.2.0- Introduced.
Canonical API name: ptool.ssh.Connection:exists.
conn:exists(path) comprueba si existe una ruta remota.
path(string|remote path, obligatorio): La ruta remota que se debe comprobar. Puede ser una cadena o un valor creado porconn:path(...).- Devuelve:
truecuando la ruta remota existe; de lo contrario,false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
print(ssh:exists("/srv/app"))
print(ssh:path("/srv/app/releases/current.tar.gz"):exists())
is_file
v0.2.0- Introduced.
Canonical API name: ptool.ssh.Connection:is_file.
conn:is_file(path) comprueba si una ruta remota existe y es un archivo
regular.
path(string|remote path, obligatorio): La ruta remota que se debe comprobar. Puede ser una cadena o un valor creado porconn:path(...).- Devuelve:
truecuando la ruta remota es un archivo; de lo contrario,false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local remote_tarball = ssh:path("/srv/app/releases/current.tar.gz")
if ssh:is_file(remote_tarball) then
print("release tarball exists")
end
is_dir
v0.2.0- Introduced.
Canonical API name: ptool.ssh.Connection:is_dir.
conn:is_dir(path) comprueba si una ruta remota existe y es un directorio.
path(string|remote path, obligatorio): La ruta remota que se debe comprobar. Puede ser una cadena o un valor creado porconn:path(...).- Devuelve:
truecuando la ruta remota es un directorio; de lo contrario,false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local releases = ssh:path("/srv/app/releases")
if releases:is_dir() then
print("releases directory is ready")
end
upload
v0.1.0- Introduced.
Canonical API name: ptool.ssh.Connection:upload.
conn:upload(local_path, remote_path[, options]) sube un archivo o directorio
local al host remoto.
local_path(string, obligatorio): El archivo o directorio local que se va a subir.remote_path(string|remote path, obligatorio): La ruta de destino en el host remoto. Puede ser una cadena o un valor creado porconn:path(...).options(table, opcional): Opciones de transferencia.- Devuelve: Una tabla con los siguientes campos:
bytes(integer): El número de bytes de archivos regulares subidos. Cuando se sube un directorio, es la suma de los tamaños de los archivos subidos.from(string): La ruta local de origen.to(string): La ruta remota de destino.
Opciones de transferencia admitidas:
parents(boolean, opcional): Crea el directorio padre deremote_pathantes de subir. Por defecto esfalse.overwrite(boolean, opcional): Si se puede reemplazar un archivo de destino existente. Por defecto estrue.echo(boolean, opcional): Si debe imprimir la transferencia antes de ejecutarla. Por defecto esfalse.
Comportamiento con directorios:
- Cuando
local_pathes un archivo, el comportamiento no cambia. - Cuando
local_pathes un directorio yremote_pathno existe,remote_pathse convierte en la raíz del directorio de destino. - Cuando
local_pathes un directorio yremote_pathya existe como directorio, el directorio de origen se crea dentro de él usando el basename del directorio de origen. overwrite = falserechaza un directorio de destino ya existente para la raíz final del directorio.- Las subidas de directorios requieren que
taresté disponible en el host remoto.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local remote_tarball = ssh:path("/srv/app/releases/current.tar.gz")
local res = ssh:upload("./dist/app.tar.gz", remote_tarball, {
parents = true,
overwrite = true,
echo = true,
})
print(res.bytes)
print(res.to)
Ejemplo con directorio:
local ssh = ptool.ssh.connect("[email protected]")
local res = ssh:upload("./dist/assets", "/srv/app/releases", {
parents = true,
overwrite = true,
echo = true,
})
print(res.bytes)
print(res.to) -- [email protected]:22:/srv/app/releases
download
v0.1.0- Introduced.
Canonical API name: ptool.ssh.Connection:download.
conn:download(remote_path, local_path[, options]) descarga un archivo o
directorio remoto a una ruta local.
remote_path(string|remote path, obligatorio): La ruta de origen en el host remoto. Puede ser una cadena o un valor creado porconn:path(...).local_path(string, obligatorio): La ruta de destino local.options(table, opcional): Opciones de transferencia.- Devuelve: Una tabla con los siguientes campos:
bytes(integer): El número de bytes de archivos regulares descargados. Cuando se descarga un directorio, es la suma de los tamaños de los archivos descargados.from(string): La ruta remota de origen.to(string): La ruta local de destino.
Opciones de transferencia admitidas:
parents(boolean, opcional): Crea el directorio padre delocal_pathantes de descargar. Por defecto esfalse.overwrite(boolean, opcional): Si se puede reemplazar un archivo de destino existente. Por defecto estrue.echo(boolean, opcional): Si debe imprimir la transferencia antes de ejecutarla. Por defecto esfalse.
Comportamiento con directorios:
- Cuando
remote_pathes un archivo, el comportamiento no cambia. - Cuando
remote_pathes un directorio ylocal_pathno existe,local_pathse convierte en la raíz del directorio de destino. - Cuando
remote_pathes un directorio ylocal_pathya existe como directorio, el directorio remoto de origen se crea dentro de él usando el basename del directorio remoto. overwrite = falserechaza un directorio de destino ya existente para la raíz final del directorio.- Las descargas de directorios requieren que
taresté disponible en el host remoto.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local res = ssh:download("/srv/app/logs/app.log", "./tmp/app.log", {
parents = true,
overwrite = false,
echo = true,
})
print(res.bytes)
print(res.from)
Ejemplo con directorio:
local ssh = ptool.ssh.connect("[email protected]")
local res = ssh:download("/srv/app/releases/assets", "./tmp/releases", {
parents = true,
overwrite = true,
echo = true,
})
print(res.bytes)
print(res.from)
close
v0.1.0- Introduced.
Canonical API name: ptool.ssh.Connection:close.
conn:close() cierra el manejador de conexión SSH.
Comportamiento:
- Después de cerrarla, la conexión ya no puede usarse.
- Cerrar una conexión que ya está cerrada está permitido y no tiene efecto.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
ssh:close()
RemotePath
v0.1.0- Introduced.
RemotePath representa una ruta remota vinculada a un Connection y devuelta
por conn:path(path).
Está implementado como un userdata de Lua.
Métodos:
remote:exists()->booleanremote:is_file()->booleanremote:is_dir()->boolean
exists
remote:exists() comprueba si existe la ruta remota.
- Devuelve:
truecuando la ruta remota existe; de lo contrario,false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local remote_release = ssh:path("/srv/app/releases/current.tar.gz")
print(remote_release:exists())
is_file
remote:is_file() comprueba si la ruta remota existe y es un archivo regular.
- Devuelve:
truecuando la ruta remota es un archivo; de lo contrario,false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local remote_tarball = ssh:path("/srv/app/releases/current.tar.gz")
if remote_tarball:is_file() then
print("release tarball exists")
end
is_dir
remote:is_dir() comprueba si la ruta remota existe y es un directorio.
- Devuelve:
truecuando la ruta remota es un directorio; de lo contrario,false.
Ejemplo:
local ssh = ptool.ssh.connect("[email protected]")
local releases = ssh:path("/srv/app/releases")
if releases:is_dir() then
print("releases directory is ready")
end