# Jenkins SCO — Deploy Server Jenkins centralizado para el despliegue de software en terminales SCO (Self-Checkout) en producción. ## Estructura del repositorio ``` . ├── docker-compose.yml └── data/ └── jenkins/ ├── Dockerfile # Imagen Jenkins con plugins ├── jenkins.yaml # Configuración JCasC (nodos, seguridad, jobs) ├── deploy/ │ └── install.sh # Script desplegado en cada SCO └── pipelines/ └── Jenkinsfile.ralph # Pipeline del cliente Ralph ``` ## Arquitectura - **Jenkins master**: corre en Docker en el servidor Minio (`186.67.82.140`). - **Agentes (SCOs)**: cada terminal SCO conecta al master vía JNLP (puerto 50000). El proceso del agente corre como `root`. - **Storage**: Minio almacena el software de cada cliente en buckets separados (ej: bucket `ralph`). ### Labels de agentes Formato: `sco ` | Cliente | Tienda | Agentes | |---|---|---| | ralph | yabucoa | sco02–sco07 | | ralph | san-lorenzo | sco03–sco06 | | ralph | rio-grande | sco01–sco06 | ## Levantar el servidor ```sh docker compose up -d --build ``` Jenkins queda disponible en `http://:8080` (admin / H0l4mund0!). ## Conectar un agente SCO 1. Descargar el agente desde Jenkins: ```sh curl -sO http://:8080/jnlpJars/agent.jar mv agent.jar /opt/jenkins-agent/agent.jar ``` 2. Obtener el secret del nodo: en Jenkins → *Manage Jenkins → Nodes → (nombre del nodo)*. 3. Crear el servicio systemd en el SCO: ```ini # /etc/systemd/system/jenkins-agent.service [Unit] Description=Jenkins Agent After=network.target [Service] User=root ExecStart=/usr/bin/java -jar /opt/jenkins-agent/agent.jar \ -url http://:8080/ \ -secret \ -name "" \ -workDir "/opt/jenkins-agent" Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` 4. Habilitar y arrancar: ```sh systemctl daemon-reload systemctl enable --now jenkins-agent ``` ## Ejecutar un deploy 1. En Jenkins, abrir el job `deploy-ralph`. 2. Click en **Build with Parameters**. 3. Seleccionar la **tienda** destino en el dropdown (yabucoa / san-lorenzo / rio-grande). 4. El pipeline despliega `install.sh ralph` en paralelo en todos los SCOs online de esa tienda. ## Qué hace `install.sh` Recibe el nombre del cliente como argumento (`$1`): 1. Genera config de rclone apuntando al Minio local. 2. Sincroniza el bucket `$CUSTOMER` desde Minio hacia `/opt/sco/` en el SCO (excluyendo datos persistentes del backend). 3. Ajusta permisos en `/opt/sco/`. 4. Instala servicios systemd desde `/opt/sco/resources/services/`. 5. Configura autostart del usuario `sco`. 6. Instala crontabs de `root` y `sco`. 7. Instala paquetes Python offline. 8. Carga imágenes Docker. 9. Inicializa carpetas y archivos de base de datos del cpi-server. 10. Genera `engine.properties` para SymmetricDS (solo en primera instalación). ## Operaciones de mantenimiento ### Agregar un nuevo cliente El deploy es idéntico para todos los clientes: el `install.sh` recibe el nombre del cliente como `$1`. Solo cambia el nombre del job, el Jenkinsfile, y los nodos registrados. **1. Registrar los nodos en `data/jenkins/jenkins.yaml`** Bajo la clave `nodes`, añadir un bloque por cada SCO del cliente: ```yaml - permanent: name: "nuevocliente-tienda1-sco01" labelString: "sco nuevocliente tienda1" remoteFS: "/opt/jenkins-agent" numExecutors: 1 retentionStrategy: "always" launcher: inbound: {} ``` Repetir para cada combinación tienda/SCO. Convención de nombres: - Nombre del nodo: `--` (ej: `ralph-yabucoa-sco02`) - Label: `sco ` (ej: `sco ralph yabucoa`) **2. Crear el Jenkinsfile en `data/jenkins/pipelines/Jenkinsfile.`** Copiar `Jenkinsfile.ralph` y ajustar dos cosas: ```groovy // En parameters: listar las tiendas del nuevo cliente choices: ['tienda1', 'tienda2', 'tienda3'], // En el stage Deploy: cambiar el nombre del cliente (dos lugares) def labelExpr = "nuevocliente && ${params.TIENDA}" sh "bash install.sh 'nuevocliente'" ``` **3. Agregar el job en `data/jenkins/jenkins.yaml`** Al final de la sección `jobs`: ```yaml - script: | pipelineJob('deploy-nuevocliente') { description('Deploy de install.sh a los SCOs de NuevoCliente.') definition { cps { script(new File('/opt/jenkins/Jenkinsfile.nuevocliente').text) sandbox(true) } } } ``` **4. Montar el Jenkinsfile en `docker-compose.yml`** ```yaml volumes: - ./data/jenkins/pipelines/Jenkinsfile.nuevocliente:/opt/jenkins/Jenkinsfile.nuevocliente:ro ``` **5. Recrear el contenedor** ```sh docker compose up -d --build ``` En el servidor remoto: ```sh # Copiar los archivos modificados al servidor primero, luego: docker compose up -d --build ``` --- ### Agregar una tienda a un cliente existente **1. Añadir los nodos de la nueva tienda en `jenkins.yaml`** (un bloque por SCO, igual que arriba). **2. Agregar la tienda al dropdown del Jenkinsfile del cliente:** ```groovy choices: ['yabucoa', 'san-lorenzo', 'rio-grande', 'nueva-tienda'], ``` **3. Aplicar en el servidor:** ```sh docker compose restart jenkins ``` No hace falta rebuild completo si solo cambia `jenkins.yaml` o el Jenkinsfile — el contenedor los monta como volúmenes y JCasC los recarga. > Si agregaste nodos nuevos pero no reiniciaste Jenkins, también podés recargar desde la UI: *Manage Jenkins → Configuration as Code → Reload existing configuration*. --- ### Agregar un SCO a una tienda existente Solo tocar `jenkins.yaml`: añadir el nodo nuevo bajo `nodes`. ```yaml - permanent: name: "ralph-yabucoa-sco08" labelString: "sco ralph yabucoa" remoteFS: "/opt/jenkins-agent" numExecutors: 1 retentionStrategy: "always" launcher: inbound: {} ``` Luego recargar la configuración: ```sh docker compose restart jenkins # o desde la UI: Manage Jenkins → Configuration as Code → Reload existing configuration ``` Una vez que Jenkins lo reconoce, conectar el agente en el SCO físico (ver sección "Conectar un agente SCO").