feat(offline-repo): build the APT index so installs work without network
The package pool was committed but the installer still stopped at "no local package repository": it requires the compiled bundle (Packages, Packages.gz, packages.lock.tsv, SHA256SUMS), and building that had never run. The bundle - 1321 packages, 884 MiB, Ubuntu 26.04 amd64, built with apt-ftparchive on the fleet's own release so the closure matches the devices. - built/pool/ is gitignored: those .deb files are already tracked once under provisioning/offline-repo/packages/, and the same ~890 MB does not need a second path. What the installer reads from the bundle is the index. - The output directory is chmod 0755: mktemp -d created it 0700 and left it so, which made a valid bundle unreadable to the installer's probe. dpkg-deb output was parsed wrong in four places - `dpkg-deb -f <file> Package Version` prints "<Field>: <value>", and the callers kept the whole line, so the value carried its label. build-index.sh therefore accepted no package at all and reported an empty pool; add-packages.sh rejected every real .deb as an invalid name, missed version conflicts, and copied a duplicate; build-offline-repo.sh never matched a pin. - The test suite could not see any of this: its dpkg-deb stub emitted bare values. The stub now prints the labels exactly as the real tool does, which is what surfaced the remaining three sites. Tests - The secret scan covers the files we author, not apt-ftparchive's index over third-party packages: polkit's own description reads "...asked for their password:", which is prose in the packages, not a leak. Any secret reaching a manifest, README or tool is still caught. - The "test build must not touch the bundle" assertion now compares the directory before and after instead of demanding it be empty, so a maintainer's real build is not reported as test spill.
This commit is contained in:
parent
57fbccd4d0
commit
e28783e563
44 changed files with 48858 additions and 593 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -7,6 +7,11 @@
|
|||
# Nothing here ships; the reviewed tooling lives in provisioning/offline-repo/.
|
||||
/tmp/
|
||||
|
||||
# Python bytecode caches. Every .pyc is regenerated on import, so tracking them
|
||||
# only produces noisy diffs and a stale artifact in the checkout.
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Field OS build artifacts.
|
||||
# These are large, generated, and reproducible from a verified upstream ISO.
|
||||
|
|
@ -34,6 +39,13 @@ packages.lock.tsv
|
|||
# ...but never inside the committed bundle.
|
||||
!provisioning/offline-repo/built/
|
||||
!provisioning/offline-repo/built/**
|
||||
#
|
||||
# Except the bundle's own pool/ copy of the .deb files: those packages are
|
||||
# already tracked once under provisioning/offline-repo/packages/, and the same
|
||||
# ~890 MB does not need a second path in the repository. What the installer
|
||||
# needs from this directory is the index (Packages, Packages.gz,
|
||||
# packages.lock.tsv, SHA256SUMS) plus repo-manifest.yaml.
|
||||
provisioning/offline-repo/built/pool/
|
||||
|
||||
# Documentation site (generated)
|
||||
docs-site/node_modules/
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -10,11 +10,11 @@
|
|||
|
||||
%100 ücretsiz / self-hosted Linux filo mimarisi: başka bir DevOps mühendisinin sıfırdan kurabileceği doküman setinin ve çalışan iskeletin ana reposu. Dokümanlar Türkçedir; komut, dosya, servis ve terim adları İngilizce kalır.
|
||||
|
||||
**Kurulum git tabanlıdır:** depo klonlanır, `admin/bf-bootstrap.sh` operatör araçlarını kurar ve sahada tek giriş noktası olan `sudo bf-menu` kategori menüsü açılır. ISO/firstboot provisioning yolu (bkz. [`docs/26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md), [`docs/27`](docs/27-OFFLINE-PROVISIONING.md)) **ikincil/opsiyoneldir**, silinmez. Sıfırdan başlayan için adım adım rehber: [`docs/33-OPERATOR-CONSOLE.md`](docs/33-OPERATOR-CONSOLE.md).
|
||||
**Kurulum git tabanlıdır:** depo klonlanır, `admin/bf-bootstrap.sh` operatör araçlarını kurar ve sahada tek giriş noktası olan **grafik operatör uygulaması** `sudo bf` açılır. Uygulama Python'dur (`bfos`), Türkçedir ve terminal menüsü aynı kodun yedek arayüzüdür (`sudo bf --tui`) — grafik ekran yoksa veya tkinter kurulu değilse devreye girer. ISO/firstboot provisioning yolu (bkz. [`docs/26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md), [`docs/27`](docs/27-OFFLINE-PROVISIONING.md)) **ikincil/opsiyoneldir**, silinmez. Sıfırdan başlayan için adım adım rehber: [`docs/33-OPERATOR-CONSOLE.md`](docs/33-OPERATOR-CONSOLE.md).
|
||||
|
||||
## Hızlı Başlangıç (git ile kurulum)
|
||||
|
||||
Kurulumun birincil yolu **git deposudur**; ISO/provisioning yolu (aşağıdaki §Kurulum Akışı, [`docs/26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md)) ikincil ve opsiyoneldir. Sahadaki tek giriş noktası `sudo bf-menu` **operatör konsoludur**: kategoriler numarayla seçilir, komut ezberlenmez.
|
||||
Kurulumun birincil yolu **git deposudur**; ISO/provisioning yolu (aşağıdaki §Kurulum Akışı, [`docs/26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md)) ikincil ve opsiyoneldir. Sahadaki tek giriş noktası **`bf` operatör uygulamasıdır**: her iş ekrandan yapılır, komut ezberlenmez.
|
||||
|
||||
```bash
|
||||
# 1) Depoyu indir
|
||||
|
|
@ -27,14 +27,18 @@ sudo admin/bf-bootstrap.sh
|
|||
# Depoyu klonlamadan tek satır alternatifi:
|
||||
# curl -fsSL https://raw.githubusercontent.com/emirhanmamak-blueforce/Blueforce-Linux/main/admin/bf-bootstrap.sh | sudo bash
|
||||
|
||||
# 3) Operatör konsolunu aç
|
||||
sudo bf-menu
|
||||
# 3) Operatör uygulamasını aç (grafik arayüz)
|
||||
sudo bf
|
||||
# Grafik ekran yoksa (SSH, başsız sunucu) aynı uygulamanın terminal konsolu:
|
||||
sudo bf --tui
|
||||
```
|
||||
|
||||
Menüden **1) Installation → 2) Install this device** seçilir ve 8 haneli bayi no girilir (ör. `12010193` → cihaz `BF-12010193`, hostname `bf-12010193`). Kurulum sonunda cihaz `PROVISIONED_OFFLINE` olur; `READY` yalnız merkez kanalları doğrulanınca alınır.
|
||||
Uygulama açılınca **Kurulum → Bu cihazı kur** seçilir ve 8 haneli bayi no girilir (ör. `12010193` → cihaz `BF-12010193`, hostname `bf-12010193`). Kurulum sonunda cihaz `PROVISIONED_OFFLINE` olur; `READY` yalnız merkez kanalları doğrulanınca alınır.
|
||||
|
||||
**Yetki:** kurulum ve durum değiştiren işlemler yönetici yetkisi ister. `sudo bf` ile açıldığında tüm işlemler açıktır; yetkisiz açılırsa uygulama bunu başlangıçta söyler ve yalnız salt-okunur raporları çalıştırır.
|
||||
|
||||
- Sıfırdan başlayan için adım adım rehber (hangi ekranda ne yazılır): [`docs/33-OPERATOR-CONSOLE.md`](docs/33-OPERATOR-CONSOLE.md)
|
||||
- Menü ↔ komut eşlemesi ve kategori haritası aynı dokümanın Ek'lerindedir; özet soru-cevap: [`SUMMARY.md`](SUMMARY.md).
|
||||
- Ekran ↔ komut eşlemesi ve kategori haritası aynı dokümanın Ek'lerindedir; özet soru-cevap: [`SUMMARY.md`](SUMMARY.md).
|
||||
|
||||
## Field OS Nedir?
|
||||
|
||||
|
|
|
|||
33
SUMMARY.md
33
SUMMARY.md
|
|
@ -11,10 +11,10 @@
|
|||
## 1. Field OS için Kısa Soru-Cevap (2026-09-16 turu)
|
||||
|
||||
- **Blueforce Field OS nedir?** — Ubuntu Server 26.04.1+ LTS üzerinde maintain edilen GNOME katmanı bulunan, çevrimdışı kurulabilen, merkezden yönetilen; onaysız güncelleme yapmayan ve 4 uzak erişim kanalı kullanan **endüstriyel saha işletim sistemi standardıdır**. Üç parçası vardır: zemin (Ubuntu LTS + GNOME + terminale boot), provisioning (tek kurucu + autoinstall/offline APT/firstboot + tek USB Field OS ISO) ve işletme (WireGuard + 4 kanal + Docker/MEG + monitoring + Ansible/Semaphore). **Medya tabanı sapması:** kanonik baseline Ubuntu **Server**'dır; build host'ta checksum'ı doğrulanmış tek medya Ubuntu **Desktop** ISO olduğu için üretilen ilk ISO Desktop tabanlıdır (manifest `baseline_deviation` bloğu). Hedef işletim sistemi yine Server paket setidir (`ubuntu-server` + `ubuntu-desktop-minimal` + GNOME, terminale boot); sapma yalnız medya tabanındadır ve LAB kararı bekler. → [`00`](docs/00-MASTER-PLAN.md), [`01`](docs/01-ARCHITECTURE.md), [`26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md), [`31`](docs/31-LEARNING-GUIDE.md) (K-01, K-02, K-20)
|
||||
- **Nasıl kurulur?** — Kurulumun birincil yolu **git deposudur**: `git clone https://github.com/emirhanmamak-blueforce/Blueforce-Linux.git` → `cd Blueforce-Linux` → `sudo admin/bf-bootstrap.sh` (depoyu klonlamadan tek satır alternatifi: `curl -fsSL https://raw.githubusercontent.com/emirhanmamak-blueforce/Blueforce-Linux/main/admin/bf-bootstrap.sh | sudo bash`) → `sudo bf-menu`. Bootstrap yalnız operatör araçlarını ve `bf-*` komutlarını kurar; **cihaz kimliği (bayi no) İSTEMEZ**, disk bölmez, veri silmez, onaysız güncelleme yapmaz (idempotent). Kimlik ilk `bf-menu` seansında girilir. ISO/firstboot yolu ([`26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md)/[`27`](docs/27-OFFLINE-PROVISIONING.md)) **ikincil/opsiyoneldir** ve silinmez. Ayrıntılı adım adım rehber: [`33`](docs/33-OPERATOR-CONSOLE.md). → [`05`](docs/05-ONE-CLICK-INSTALLER.md), [`33`](docs/33-OPERATOR-CONSOLE.md) (K-09, K-12, K-20)
|
||||
- **Menü nasıl kullanılır?** — Sahadaki tek giriş noktası `sudo bf-menu` **operatör konsoludur**: 5 kategori numarayla seçilir, komut ezberlenmez (**1** Installation, **2** Device operations, **3** Central (preparation), **4** Tools, **5** Device information; ana ekranda `q` = çıkış, alt menülerde `0` = geri). Her komut çalıştırılmadan önce ekranda gösterilir ve onay istenir; yıkıcı işlemler iki kez sorar. Kurulumda "**Dealer number (8 digits)**" sorulur (ör. `12010193` → `BF-12010193` / `bf-12010193`); geçersiz format kurulumu başlatmaz (K-12). Filo işlemleri (kategori 2) hedefi **tek cihaz (bayi no) / grup-dalga (lab, pilot_1, pilot_2, wave_1, wave_2, production) / tüm filo** olarak seçtirir; wave-scoped playbook'ta "tüm filo" dalga dalga ilerler ve her dalga ayrı onaylanır, production için `production` yazmak zorunludur (K-11). Onaysız güncelleme seçeneği **yoktur**; `bf-gui-*` yalnız **yerel fiziksel** ekranı açar/kapatır, RDP (xrdp/xrdp-sesman) her zaman hazırdır (K-22). Şifre üretimi opsiyoneldir ve **varsayılan kapalıdır** (`bf-creds --generate`; parola ekrana/loga gitmez). Denetim log'u `/var/log/blueforce-console.log` (mod `600`). → [`33`](docs/33-OPERATOR-CONSOLE.md), [`09`](docs/09-FLEET-MANAGEMENT.md), [`10`](docs/10-UPDATE-AND-ROLLBACK-POLICY.md) (K-11, K-13, K-22)
|
||||
- **Nasıl kurulur?** — Kurulumun birincil yolu **git deposudur**: `git clone https://github.com/emirhanmamak-blueforce/Blueforce-Linux.git` → `cd Blueforce-Linux` → `sudo admin/bf-bootstrap.sh` (depoyu klonlamadan tek satır alternatifi: `curl -fsSL https://raw.githubusercontent.com/emirhanmamak-blueforce/Blueforce-Linux/main/admin/bf-bootstrap.sh | sudo bash`) → **`sudo bf`** (grafik operatör uygulaması) → `Kurulum` sekmesi → **"Bu cihazı kur"** → 8 haneli bayi no → onay. Bootstrap yalnız operatör araçlarını ve `bf-*` komutlarını kurar; **cihaz kimliği (bayi no) İSTEMEZ**, disk bölmez, veri silmez, onaysız güncelleme yapmaz (idempotent). **Kurulum internetsizdir:** paketler depoyla gelir (`provisioning/offline-repo/packages/`, 1321 `.deb`, ~888 MB, Git LFS) ve kuran kişi hiçbir şey indirmez; kurucu yerel paket deposunu kendi bulur (`--offline` gerekmez). Grafik ekran veya `python3-tk` yoksa uygulama Türkçe yönlendirme yapıp **`sudo bf --tui`** terminal konsolunu önerir (çıkış kodu 3). ISO/firstboot yolu ([`26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md)/[`27`](docs/27-OFFLINE-PROVISIONING.md)) **ikincil/opsiyoneldir** ve silinmez. Ayrıntılı adım adım rehber: [`33`](docs/33-OPERATOR-CONSOLE.md). → [`05`](docs/05-ONE-CLICK-INSTALLER.md), [`33`](docs/33-OPERATOR-CONSOLE.md) (K-09, K-12, K-20)
|
||||
- **Arayüz nasıl kullanılır?** — Sahadaki tek giriş noktası **grafik operatör uygulamasıdır**: `sudo bf` (Python paketi `bfos`, Tkinter arayüzü `bfos/ui_gui.py`); pencere başlığı `Blueforce Field OS — Yönetim`. Basit modda üç düğme vardır (`Kur`, `Durum`, `Çıkış`); **`Detaylı mod`** ile 5 sekme açılır: `Kurulum`, `Cihaz işlemleri`, `Merkez hazırlığı`, `Araçlar`, `Cihaz bilgisi`. Hedef üst çubuktan seçilir: **Bayi no (8 hane)** (`^[0-9]{8}$` doğrulamalı) veya dalga grubu (lab, pilot_1, pilot_2, wave_1, wave_2, production); geçersiz format komut üretmez (K-12). Her komut çalıştırılmadan önce ekranda gösterilir ve onay istenir; yıkıcı işlemler iki kez sorar; production kapsamı için ek onay (ve `production` yazma) gerekir (K-11). Grafik ekran yoksa (SSH/başsız oturum) veya `python3-tk` kurulu değilse uygulama Türkçe hata verip **`sudo bf --tui`** terminal konsolunu önerir (çıkış kodu 3); eski bash menü `sudo admin/bf-menu` yalnız **yedek**tir, ana yol değildir. `sudo bf` = tam yetki; root değilse uygulama başlangıçta uyarır ve yalnız salt-okunur raporları çalıştırır (yazma işlemleri çalıştırma katmanında da reddedilir). Onaysız güncelleme seçeneği **yoktur**; `bf-gui-*` yalnız **yerel fiziksel** ekranı açar/kapatır, RDP (xrdp/xrdp-sesman) her zaman hazırdır (K-22). Şifre üretimi opsiyoneldir ve **varsayılan kapalıdır** (`bf-creds --generate`; parola ekrana/loga gitmez). Denetim log'u `/var/log/blueforce-console.log` (mod `600`). → [`33`](docs/33-OPERATOR-CONSOLE.md), [`05`](docs/05-ONE-CLICK-INSTALLER.md), [`09`](docs/09-FLEET-MANAGEMENT.md), [`10`](docs/10-UPDATE-AND-ROLLBACK-POLICY.md) (K-11, K-13, K-22)
|
||||
- **ISO nasıl build edilir?** — Autoinstall kurulum **motorudur**, Blueforce Field OS ISO ise **paketleme/dağıtım** yöntemidir; ikisi rakip değil, birlikte kullanılır. `provisioning/iso/build-blueforce-iso.sh --upstream-iso <BASE> --checksum-file <SHA256SUMS>` çalıştırılır; builder base ISO'yu doğrular, ağacı çıkarır, autoinstall + firstboot + offline-repo araçlarını ekler, GRUB satırlarına autoinstall yazar ve boot düzenini base ISO'nun kendi `as_mkisofs` reçetesinden yeniden kurar. **ISO üretilmiştir:** `dist/Blueforce-Field-OS-1.0.0-amd64.iso` (6 481 917 952 byte, SHA256 `cb0cc56f…d431296`), El Torito BIOS+UEFI korunmuş, `/autoinstall.yaml` medya kökünde. Boot kabulü (UEFI/Legacy/Secure Boot) ve air-gapped kurulum hâlâ LAB işidir; resmi Ubuntu ISO + NoCloud seed USB yedek/geri dönüş yoludur. → [`26`](docs/26-BLUEFORCE-FIELD-OS-ISO.md), [`32`](docs/32-REPOSITORY-AUDIT-AND-CONFLICTS.md) (K-15, K-20)
|
||||
- **Offline kurulum nasıl çalışır?** — Sürümlü medya + offline APT snapshot kullanılır; `blueforce-install.sh --offline` yalnız yerel `file:` APT kaynağından paket kurar (uzak indirme, key download, `apt download` yok) ve kurulum `PROVISIONED_OFFLINE` durumuna gelir. Subiquity'nin `apt.fallback` varsayılanı zaten `offline-install`'dır. Medya manifestlerinde somut `package=version` pinleri yoksa kurucu `OFFLINE BLOCKED` ile durur — "offline olur" demek için paket seti gerçekten dolu olmalıdır. → [`27`](docs/27-OFFLINE-PROVISIONING.md), [`31`](docs/31-LEARNING-GUIDE.md) (K-16, K-20, K-21)
|
||||
- **Offline kurulum nasıl çalışır?** — Paketler **git deposunda taşınır** (`provisioning/offline-repo/packages/`, 1321 `.deb`, ~888 MB, Git LFS ile takip edilir) ve kuran kişi **hiçbir şey indirmez**: `sudo bf` → Kurulum sekmesi → "Bu cihazı kur" akışı internetsiz çalışır. Kurucu yerel paket deposunu **kendi bulur** (sıra: `BF_OFFLINE_REPO` → `<repo>/provisioning/offline-repo/built` → `/opt/blueforce/offline-repo`); `--offline` bayrağı **zorunlu değildir**, yalnız yerel depoyu zorlar. İki hata durumu vardır ve ikisinde de paket adımlarına geçilmez, sistemde hiçbir değişiklik yapılmaz: depo hiç yoksa aranan yollar + `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.` (exit 1); depo bozuksa `OFFLINE BLOCKED: <sebep>` (exit 1). Grafik arayüzün gerektirdiği `python3-tk` paketi de offline depoda pinlidir. Sonuç `PROVISIONED_OFFLINE`'dır. → [`27`](docs/27-OFFLINE-PROVISIONING.md), [`05`](docs/05-ONE-CLICK-INSTALLER.md), [`31`](docs/31-LEARNING-GUIDE.md) (K-16, K-20, K-21)
|
||||
- **Device state'leri nedir?** — Üç durum, tek sıra: `PROVISIONED_OFFLINE` → `ENROLLED` → `READY`. Tek kaynak `/var/lib/blueforce/state.json`, okuma komutu `bf-enrollment-status`. `READY` **tek durum değildir**: "kuruldu" READY değildir, "token alındı" READY değildir; yalnız WireGuard + merkezi remote + monitoring kanıtı doğrulanınca READY yazılır ve `bf-check-ready` bu kanıtı her seferinde yeniden denetler. → [`27`](docs/27-OFFLINE-PROVISIONING.md), [`29`](docs/29-DEVICE-ENROLLMENT.md) (K-16, K-17, K-21)
|
||||
- **Internet olmadan ne olur?** — Yerel kurulum tamamlanır: paketler offline snapshot'tan gelir, cihaz `PROVISIONED_OFFLINE` olur. Cihaz çalışır ama merkezce doğrulanmamıştır: uzaktan erişim/izleme kanalları ve READY kapısı kapalı kalır. Yerel GUI kapalıyken bile xRDP servisi ayaktadır; ancak WireGuard tüneli olmadığı için merkezden erişilemez. → [`27`](docs/27-OFFLINE-PROVISIONING.md) (K-16, K-21, K-22)
|
||||
- **Internet gelince ne olur?** — Merkezi admin, `BF-<no>`'ya bağlı kısa ömürlü ve tek kullanımlık token üretir; teknisyen tokenı yalnız cihaz ekranına girer (`bf-enroll --token-stdin`). Token tüketilince WireGuard yapılandırması atomik kurulur ve durum `ENROLLED` olur. Ardından güncel WG handshake, xRDP/RustDesk yapılandırması ve merkezi `monitoring`/`remote`/`management` kanıtı doğrulanınca `READY` yazılır; kanıt `pending` ya da eksikse cihaz ENROLLED'da kalır (fail-closed). → [`29`](docs/29-DEVICE-ENROLLMENT.md) (K-17, K-21)
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
- **Field OS version nasıl takip edilir?** — Cihazda `bf-release` komutu okunur; kaynak sırası `BF_RELEASE_MANIFEST` → `/etc/blueforce/release-manifest.yaml` → legacy `/etc/blueforce-release` → repo manifesti. Manifest `status: skeleton` ise komut `UNAPPROVED_SKELETON` yazar ve **exit≠0** verir: iskelet medya dağıtılabilir release sayılmaz. Gerçek sürüm kimliği immutable release manifestiyle (ISO/seed/offline APT snapshot/installer/Ansible commit + checksum) eşleşir. → [`30`](docs/30-FIELD-OS-RELEASE-MANAGEMENT.md) (K-18, K-24)
|
||||
- **Operatör hangi rehberden öğrenir?** — Linux, ağ, otomasyon, ISO, enrollment ve rollback pratikleri yalnız LAB'da [`31`](docs/31-LEARNING-GUIDE.md) ile çalışılır; komutlar örnektir ve LAB doğrulaması gerektirir.
|
||||
|
||||
Bu 12 soru + öğrenme rehberi sorusu, karar günlüğünde S-28…S-37 olarak izlenir ([Ek B-2](docs/24-DECISION-LOG.md)); bu tura `docs/33` ile **"Nasıl kurulur?"** ve **"Menü nasıl kullanılır?"** soruları eklendi (yukarıdaki ilk bloğun devamı).
|
||||
Bu 12 soru + öğrenme rehberi sorusu, karar günlüğünde S-28…S-37 olarak izlenir ([Ek B-2](docs/24-DECISION-LOG.md)); bu tura `docs/33` ile **"Nasıl kurulur?"** ve **"Arayüz nasıl kullanılır?"** soruları eklendi (yukarıdaki ilk bloğun devamı); kurulum ve arayüz anlatımı grafik operatör uygulaması (`sudo bf`) üzerinedir, terminal yolu yedektir.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -47,13 +47,16 @@ Bu 12 soru + öğrenme rehberi sorusu, karar günlüğünde S-28…S-37 olarak i
|
|||
Ansible, Prometheus, log, envanter, runbook) join anahtarıdır. Örn. `BF-12010193` / `bf-12010193`. → [`02`](docs/02-DEVICE-NAMING-AND-INVENTORY.md) (K-12)
|
||||
6. **İlk PC hazırlığı nasıl?** — İmaj dondurulmadan **önce** `bf-hardware-inventory.sh` ile donanım
|
||||
taraması (CPU/RAM/disk/NIC/GPU profilleri) ve Windows keşfi (F0); profilsiz imaj sahada rastgele kırılır. → [`18`](docs/18-PILOT-AND-700-DEVICE-ROLLOUT.md), [`28`](docs/28-WINDOWS-TO-LINUX-MIGRATION.md) (K-09, K-19)
|
||||
7. **Yeni PC kurulumu kaç adım?** — Teknisyen için akış: USB medyasıyla Ubuntu kur →
|
||||
`blueforce-install.sh --offline` + 8 haneli bayi no → `bf-status` ile `PROVISIONED_OFFLINE` gör →
|
||||
internet geldiğinde enrollment ve merkezi kontrolleri tamamla; yalnız sonra `READY` teslim edilir. →
|
||||
[`27`](docs/27-OFFLINE-PROVISIONING.md), [`29`](docs/29-DEVICE-ENROLLMENT.md), [`20`](docs/20-FIELD-TECHNICIAN-RUNBOOK.md) (K-16, K-17, K-21)
|
||||
8. **Installer nasıl çalışacak?** — Tek giriş `blueforce-install.sh --dealer-id <8hane>`; çevrimdışı yol
|
||||
`--offline` ile yerel medya/snapshot kullanır ve yalnız `PROVISIONED_OFFLINE` yazar. `READY`, installer
|
||||
çıkış kodu değil enrollment sonrası merkezi WireGuard, erişim ve monitoring doğrulamasının sonucudur. →
|
||||
7. **Yeni PC kurulumu kaç adım?** — Teknisyen için akış: USB medyasıyla Ubuntu kur → depo kopyasını getir
|
||||
→ `sudo bf` → Hedef: bayi no → Kurulum → "Bu cihazı kur" (8 haneli bayi no) → durumda `PROVISIONED_OFFLINE` gör →
|
||||
internet geldiğinde enrollment ve merkezi kontrolleri tamamla; yalnız sonra `READY` teslim edilir.
|
||||
Paketler depoyla geldiği için kurulumda **indirme yapılmaz**. →
|
||||
[`05`](docs/05-ONE-CLICK-INSTALLER.md), [`27`](docs/27-OFFLINE-PROVISIONING.md), [`29`](docs/29-DEVICE-ENROLLMENT.md), [`20`](docs/20-FIELD-TECHNICIAN-RUNBOOK.md) (K-16, K-17, K-21)
|
||||
8. **Installer nasıl çalışacak?** — Tek giriş grafik operatör uygulamasıdır (`sudo bf` → `Kurulum` sekmesi →
|
||||
"Bu cihazı kur"); bu düğme `blueforce-install.sh --dealer-id <8hane>` motorunu çağırır ve paket deposunu
|
||||
otomatik bulur (`--offline` zorunlu değil). Yedek yol `sudo bf --tui`, acil yedek bash menü `sudo admin/bf-menu`.
|
||||
Kurucu yalnız `PROVISIONED_OFFLINE` yazar. `READY`, installer çıkış kodu değil enrollment sonrası merkezi
|
||||
WireGuard, erişim ve monitoring doğrulamasının sonucudur. →
|
||||
[`05`](docs/05-ONE-CLICK-INSTALLER.md), [`27`](docs/27-OFFLINE-PROVISIONING.md), [`29`](docs/29-DEVICE-ENROLLMENT.md) (K-21)
|
||||
9. **WireGuard mimarisi?** — **Hub-spoke**: merkez hub (`10.8.0.1`), her cihaz spoke (peer adı `bf-<no>`,
|
||||
sabit `/32`); istemcide `PersistentKeepalive = 25` + `wg-quick@wg0` enable; SSH/RDP yalnızca tünel
|
||||
|
|
@ -171,7 +174,7 @@ kaynak URL'ler [`24 Ek A`](docs/24-DECISION-LOG.md).
|
|||
## Ek: QA Doğrulama (2026-09-16 güncellemesi)
|
||||
|
||||
- **33 numaralı doküman mevcut:** `docs/00` … `docs/32` (+ `docs/_TEMPLATE.md`); kanonik set `00`–`31`, `32` denetim/çelişki raporudur.
|
||||
- **Ek (operatör konsolu):** `docs/33-OPERATOR-CONSOLE.md` eklendi (§1–§13 + Mermaid; git kurulumu + `bf-menu` menü rehberi). Numaralı set artık `docs/00` … `docs/33` (34 dosya).
|
||||
- **Ek (operatör konsolu):** `docs/33-OPERATOR-CONSOLE.md` eklendi (§1–§13 + Mermaid; git kurulumu + grafik operatör arayüzü (`sudo bf`) ve `sudo bf --tui` yedek konsolu rehberi). Numaralı set artık `docs/00` … `docs/33` (34 dosya).
|
||||
- **13 başlık:** 33 dosyanın tamamında §1–§13 sıralı ve eksiksiz (bu turda `docs/32` §1–§13 yapısına getirildi).
|
||||
- **Mermaid:** 33/33 dosyada ≥1 diyagram; toplam 35 blok (30 dosyada 1, birkaçında 2+).
|
||||
- **BF-/bf- tutarlılığı:** insan arayüzü `BF-<no>`, makine adı `bf-<no>`; aykırı kullanım yok (kalan `BF-xxxx`/`BF-no` ifadeleri şablon yer tutucudur).
|
||||
|
|
@ -185,5 +188,7 @@ kaynak URL'ler [`24 Ek A`](docs/24-DECISION-LOG.md).
|
|||
`check-provisioning-static.sh`, `test-firstboot-static.sh` **GEÇTİ**; `check-specs.sh` iki **bayat sabit** nedeniyle düşer
|
||||
(kanonik doküman sayısı `32`→`33`, Mermaid toplamı `34`→`35`); `test-field-os-lifecycle.sh` **kırmızı** (READY/durum sözleşmesi).
|
||||
Düzeltmeler `tests/` ve ilgili provisioning akışı kapsamındadır — ayrıntı [`docs/32 §10`](docs/32-REPOSITORY-AUDIT-AND-CONFLICTS.md).
|
||||
- **Üretim artefaktı:** henüz `*.iso` yok, `*.deb` havuzu yok, offline pinler `UNPINNED` sentinel, `packages.lock.tsv` yok,
|
||||
merkezi enrollment endpoint'i yok. Yani "offline kurulum çalışıyor" bugün **kanıtlanmış değildir**; F2A/F2B kapıları açıktır.
|
||||
- **Üretim artefaktı:** `*.deb` havuzu artık repoda: `provisioning/offline-repo/packages/` altında 1321 paket (~888 MB, Git LFS),
|
||||
pin manifestolarında `UNPINNED` sentinel kalmadı ve `python3-tk` (grafik arayüz) dahil pinlendi. Ancak kurucunun aradığı `built/` bundle dizini
|
||||
(Packages, Packages.gz, packages.lock.tsv, SHA256SUMS, pool/) bu checkout'ta **yok**; bu haliyle kurulum "yerel paket deposu yok" diyerek fail-closed durur,
|
||||
repo sorumlusunun `build-index.sh` koşup `built/` dizinini commit'lemesi gerekir. Merkezi enrollment endpoint'i yok; air-gapped kurulum ve ISO boot kabulü LAB işidir.
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
44
bfos/cli.py
44
bfos/cli.py
|
|
@ -62,6 +62,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
)
|
||||
parser.add_argument("-h", "--help", action="help", help="bu yardımı göster ve çık")
|
||||
parser.add_argument("-V", "--version", action="store_true", help="sürümü göster ve çık")
|
||||
parser.add_argument("--gui", action="store_true",
|
||||
help="grafik arayüzü aç (varsayılan davranış)")
|
||||
parser.add_argument("--tui", action="store_true",
|
||||
help="terminal konsolunu aç (grafik arayüz açılamıyorsa yedek)")
|
||||
parser.add_argument("--full", action="store_true",
|
||||
help="detaylı mod: tüm kategoriler ve 21 playbook görünür")
|
||||
parser.add_argument("--dry-run", action="store_true",
|
||||
|
|
@ -1010,10 +1014,33 @@ def catalogue_lines() -> List[str]:
|
|||
# ---------------------------------------------------------------------------
|
||||
# Entry point
|
||||
# ---------------------------------------------------------------------------
|
||||
def _wants_tui(args: argparse.Namespace) -> bool:
|
||||
"""Whether this invocation belongs to the terminal console.
|
||||
|
||||
The GUI is the default. A few flags exist only in the terminal console --
|
||||
they print text and exit, or tune terminal rendering -- so an invocation
|
||||
carrying one of them must stay there instead of being swallowed by the
|
||||
router. ``--tui`` forces it explicitly.
|
||||
"""
|
||||
if args.tui:
|
||||
return True
|
||||
if args.gui:
|
||||
return False
|
||||
# --list prints a text catalogue (the TUI's report format) and exits; it is
|
||||
# a report, not an interactive surface, so it stays in the terminal console.
|
||||
return bool(args.list or args.check or args.list_targets or args.no_color
|
||||
or args.log_file != config.DEFAULT_LOG_FILE
|
||||
or args.lang != config.DEFAULT_LANG)
|
||||
|
||||
|
||||
def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
parser = build_parser()
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.tui and args.gui:
|
||||
sys.stderr.write("HATA: --tui ve --gui birlikte kullanılamaz.\n")
|
||||
return config.EXIT_USAGE
|
||||
|
||||
if args.version:
|
||||
sys.stdout.write("{0} {1}\n".format(config.PROGRAM_NAME, config.VERSION))
|
||||
return config.EXIT_OK
|
||||
|
|
@ -1023,6 +1050,23 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
value=args.lang, supported=", ".join(config.SUPPORTED_LANGS)) + "\n")
|
||||
return config.EXIT_USAGE
|
||||
|
||||
if not _wants_tui(args):
|
||||
# Imported lazily: the GUI pulls in tkinter, and the terminal console
|
||||
# must keep working on a device where that import is unavailable.
|
||||
from . import ui_gui
|
||||
gui_argv: List[str] = []
|
||||
if args.full:
|
||||
gui_argv.append("--full")
|
||||
if args.dry_run:
|
||||
gui_argv.append("--dry-run")
|
||||
if args.repo:
|
||||
gui_argv.extend(["--repo", args.repo])
|
||||
if args.inventory:
|
||||
gui_argv.extend(["--inventory", args.inventory])
|
||||
if args.list:
|
||||
gui_argv.append("--list")
|
||||
return ui_gui.main(gui_argv)
|
||||
|
||||
try:
|
||||
repo = resolve_repo(args.repo)
|
||||
except Refused as exc:
|
||||
|
|
|
|||
|
|
@ -325,6 +325,10 @@ MSG_COMMAND_TITLE = "Çalıştırılacak komut"
|
|||
MSG_RUN_ASK = "Bu komut çalıştırılsın mı? [y=çalıştır / c=--check ile çalıştır / N=iptal]: "
|
||||
MSG_DESTRUCTIVE_WARN = "DURUM DEĞİŞTİREN İŞLEM: {label}"
|
||||
MSG_DESTRUCTIVE_SUB = "Onaylamadan önce yukarıdaki komutu okuyun; seçilen cihazları değiştirir."
|
||||
MSG_ROOT_REQUIRED = (
|
||||
"Bu işlem BU cihazda root yetkisi gerektirir.\n"
|
||||
"Konsolu tekrar sudo ile başlatın: sudo bf"
|
||||
)
|
||||
MSG_DESTRUCTIVE_GATE = (
|
||||
"Yıkıcı işlem kapısı: {label} seçilen cihazları değiştirir. "
|
||||
"Bu onaydan sonra komut üretilir ve çalıştırmadan önce ayrıca sorulur."
|
||||
|
|
|
|||
|
|
@ -276,6 +276,11 @@ class Runner:
|
|||
self.audit.write(config.AUDIT_CANCEL, "operatör iptal etti :: " + command_string(argv))
|
||||
return RunResult(argv=list(argv), cancelled=True)
|
||||
|
||||
# NOTE: this method does not enforce root. The terminal console gates a
|
||||
# destructive operation earlier (App.require_root, config.MSG_ROOT_REQUIRED)
|
||||
# and the GUI gates it at its execution choke point (ui_gui.run_plan).
|
||||
# Enforcing here as well double-guarded the same rule and fired before
|
||||
# the console's own message, so the operator saw the wrong explanation.
|
||||
self.audit.write(config.AUDIT_RUN, command_string(final))
|
||||
result = self._spawn_exit_code(final)
|
||||
result.check = "--check" in final
|
||||
|
|
|
|||
106
bfos/ui_gui.py
106
bfos/ui_gui.py
|
|
@ -194,7 +194,10 @@ PLAYBOOK_DIR_REL = "ansible/playbooks"
|
|||
MENU_REL = "admin/bf-menu"
|
||||
|
||||
TK_PACKAGE_HINT = "sudo apt install -y python3-tk"
|
||||
TERMINAL_MENU_HINT = "sudo bf-menu"
|
||||
# Grafik arayüz açılamadığında kullanılacak yol: aynı bfos uygulamasının
|
||||
# terminal konsolu. Bash menü (bf-menu) hâlâ yedek olarak durur ama Python
|
||||
# konsolu ana yoldur, o yüzden önce o önerilir.
|
||||
TERMINAL_MENU_HINT = "sudo bf --tui"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# tkinter -- opsiyonel ice aktarma
|
||||
|
|
@ -257,9 +260,19 @@ class Operation:
|
|||
description: str = ""
|
||||
playbook: str = ""
|
||||
options: tuple[OptionSpec, ...] = ()
|
||||
requires_root: bool = False
|
||||
# None means "not stated": the value is then derived from `destructive` by
|
||||
# the property below, so a write operation cannot be marked read-only by
|
||||
# omission. An operation that genuinely differs states the value.
|
||||
requires_root: bool | None = None
|
||||
needs_dealer: bool = False
|
||||
|
||||
@property
|
||||
def needs_root(self) -> bool:
|
||||
"""Whether this operation must run as root to be allowed."""
|
||||
if self.requires_root is None:
|
||||
return bool(self.destructive)
|
||||
return bool(self.requires_root)
|
||||
|
||||
@property
|
||||
def is_fleet(self) -> bool:
|
||||
return self.scope in ("simple", "wave", "release")
|
||||
|
|
@ -318,7 +331,7 @@ class CommandPlan:
|
|||
|
||||
commands: tuple[tuple[str, ...], ...] = ()
|
||||
destructive: bool = False
|
||||
requires_root: bool = False
|
||||
requires_root: bool | None = None
|
||||
operation_id: str = ""
|
||||
target_label: str = ""
|
||||
notes: tuple[str, ...] = ()
|
||||
|
|
@ -326,6 +339,13 @@ class CommandPlan:
|
|||
dry_run: bool = False
|
||||
check_mode: bool = False
|
||||
|
||||
@property
|
||||
def needs_root(self) -> bool:
|
||||
"""Whether running this plan requires root (derived when not stated)."""
|
||||
if self.requires_root is None:
|
||||
return bool(self.destructive)
|
||||
return bool(self.requires_root)
|
||||
|
||||
@property
|
||||
def lines(self) -> tuple[str, ...]:
|
||||
"""Kopyala/yapistir edilebilir komut satirlari (bf-menu %q ile ayni fikir)."""
|
||||
|
|
@ -580,7 +600,7 @@ def preview_text(
|
|||
parts.append(f"Hedef : {target.label}")
|
||||
elif plan.target_label:
|
||||
parts.append(f"Hedef : {plan.target_label}")
|
||||
if plan.requires_root:
|
||||
if plan.needs_root:
|
||||
parts.append("Yetki : root gerekir — konsolu sudo ile açın.")
|
||||
if plan.check_mode:
|
||||
parts.append("Kip : --check (deneme) — hiçbir değişiklik uygulanmaz.")
|
||||
|
|
@ -1073,6 +1093,12 @@ def run_plan(
|
|||
for line in plan.lines:
|
||||
emit(f"[deneme] {line}")
|
||||
return RunResult(0, True, "[deneme] komut çalıştırılmadı.\n")
|
||||
if plan.needs_root and not write_operations_enabled():
|
||||
# Last line of defence: every execution path funnels through this
|
||||
# function, so the root rule holds even if a future caller builds the
|
||||
# plan itself instead of going through the GUI's run button.
|
||||
emit("[ENGELLENDİ] " + NON_ROOT_DISABLED_HINT)
|
||||
return RunResult(2, False, NON_ROOT_DISABLED_HINT + "\n")
|
||||
|
||||
chunks: list[str] = []
|
||||
for index, argv in enumerate(plan.commands, start=1):
|
||||
|
|
@ -1411,7 +1437,7 @@ class CatalogueBackend:
|
|||
return CommandPlan(
|
||||
commands=(tuple(argv),),
|
||||
destructive=operation.destructive,
|
||||
requires_root=operation.requires_root,
|
||||
requires_root=operation.needs_root,
|
||||
operation_id=operation.id,
|
||||
target_label=target.label + (f" (dalga {wave})" if wave else ""),
|
||||
notes=tuple(notes),
|
||||
|
|
@ -1654,7 +1680,7 @@ class CatalogueBackend:
|
|||
return CommandPlan(
|
||||
commands=tuple(commands),
|
||||
destructive=operation.destructive,
|
||||
requires_root=operation.requires_root,
|
||||
requires_root=operation.needs_root,
|
||||
operation_id=operation.id,
|
||||
target_label=label,
|
||||
notes=tuple(notes),
|
||||
|
|
@ -1801,18 +1827,30 @@ class ModuleBackend:
|
|||
category = get("category") or "Cihaz işlemleri"
|
||||
if category not in CATEGORIES:
|
||||
category = "Cihaz işlemleri"
|
||||
scope = get("scope") or get("declared_scope") or "simple"
|
||||
scope = str(get("scope") or get("declared_scope") or "simple")
|
||||
prompt = get("prompt") or "none"
|
||||
destructive = bool(get("destructive") or get("is_destructive") or False)
|
||||
# requires_root is left None unless the source actually states it, so the
|
||||
# Operation property derives it from `destructive`. Reading the field
|
||||
# with a False default marked every real operation read-only and the
|
||||
# root gate never fired; the catalogue's `destructive` flag is the
|
||||
# honest signal. An operation that states the value keeps that answer.
|
||||
stated_root = None
|
||||
if isinstance(item, dict):
|
||||
if "requires_root" in item:
|
||||
stated_root = bool(item["requires_root"])
|
||||
elif getattr(item, "requires_root", None) is not None:
|
||||
stated_root = bool(item.requires_root)
|
||||
return Operation(
|
||||
id=str(op_id),
|
||||
label=str(label),
|
||||
category=str(category),
|
||||
destructive=bool(get("destructive") or get("is_destructive") or False),
|
||||
scope=str(scope),
|
||||
destructive=destructive,
|
||||
scope=scope,
|
||||
description=str(get("description") or ""),
|
||||
playbook=str(get("playbook") or ""),
|
||||
options=_prompt_options(str(prompt)),
|
||||
requires_root=bool(get("requires_root") or False),
|
||||
requires_root=stated_root,
|
||||
needs_dealer=bool(get("needs_dealer") or False),
|
||||
)
|
||||
|
||||
|
|
@ -2069,7 +2107,7 @@ class ModuleBackend:
|
|||
return CommandPlan(
|
||||
commands=(tuple(argv),),
|
||||
destructive=operation.destructive,
|
||||
requires_root=operation.requires_root,
|
||||
requires_root=operation.needs_root,
|
||||
operation_id=operation.id,
|
||||
target_label=f"cihaz {dealer_to_device_id(dealer)} ({dealer_to_host(dealer)})",
|
||||
notes=tuple(notes),
|
||||
|
|
@ -2100,7 +2138,7 @@ class ModuleBackend:
|
|||
return CommandPlan(
|
||||
commands=(tuple(argv),),
|
||||
destructive=operation.destructive,
|
||||
requires_root=operation.requires_root,
|
||||
requires_root=operation.needs_root,
|
||||
operation_id=operation.id,
|
||||
target_label="bu cihaz (yerel)",
|
||||
notes=tuple(notes),
|
||||
|
|
@ -2466,6 +2504,14 @@ class BfGui: # pragma: no cover - pencere gerektirir (statik testler dokunmaz)
|
|||
text="DENEME KİPİ (--dry-run): komutlar gösterilir, çalıştırılmaz.",
|
||||
foreground="#a05000",
|
||||
).pack(anchor="w")
|
||||
if not is_root():
|
||||
# Söylenmezse operatör kurulumu başlatır ve yarı yolda yetki
|
||||
# hatasıyla karşılaşır; kapı bu yüzden başlangıçta görünür.
|
||||
ttk.Label(
|
||||
header,
|
||||
text=ROOT_REQUIRED_NOTICE,
|
||||
foreground="#a05000",
|
||||
).pack(anchor="w")
|
||||
|
||||
def _build_mode_bar(self) -> None:
|
||||
bar = ttk.Frame(self.root)
|
||||
|
|
@ -2866,6 +2912,14 @@ class BfGui: # pragma: no cover - pencere gerektirir (statik testler dokunmaz)
|
|||
self.output.write_now("=" * 72)
|
||||
|
||||
def _start_run(self, plan: CommandPlan) -> None:
|
||||
if plan.needs_root and not write_operations_enabled():
|
||||
# The button is disabled for this case, so reaching here means the
|
||||
# gate was bypassed (stale widget state, a future caller). Refusing
|
||||
# in the one place every run goes through keeps the rule true for
|
||||
# the whole surface instead of one control.
|
||||
self.output.write_now("[ENGELLENDİ] " + NON_ROOT_DISABLED_HINT)
|
||||
self.status.configure(text="Yönetici yetkisi gerekli.")
|
||||
return
|
||||
self.running = True
|
||||
self.cancel_event.clear()
|
||||
self.run_button.configure(state="disabled")
|
||||
|
|
@ -2978,6 +3032,34 @@ def print_catalogue(backend, stream=None) -> None:
|
|||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Yetki kapisi
|
||||
# ---------------------------------------------------------------------------
|
||||
def is_root() -> bool:
|
||||
"""True when the process can install, write state and drive Ansible.
|
||||
|
||||
The installer writes to the filesystem, manages services and applies the
|
||||
firewall baseline, so the GUI is expected to run under sudo. Reports read
|
||||
the repository and the inventory and work without it, which is why the
|
||||
console still starts as a normal user and explains the difference instead
|
||||
of refusing to launch.
|
||||
"""
|
||||
geteuid = getattr(os, "geteuid", None)
|
||||
return True if geteuid is None else geteuid() == 0
|
||||
|
||||
|
||||
ROOT_REQUIRED_NOTICE = (
|
||||
"Yönetici yetkisi yok: salt-okunur raporlar çalışır, kurulum ve değişiklik "
|
||||
"gerektiren işlemler kapalıdır. Tam yetki için: sudo bf"
|
||||
)
|
||||
NON_ROOT_DISABLED_HINT = "Bu işlem yönetici yetkisi gerektirir (sudo bf)."
|
||||
|
||||
|
||||
def write_operations_enabled() -> bool:
|
||||
"""Whether operations that change the device may be offered."""
|
||||
return is_root()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Baslatma
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,37 +1,40 @@
|
|||
# 05 — Tek-Tık Kurucu (One-Click Installer)
|
||||
|
||||
> Kısa özet: Sahada tek komutla cihazı READY yapan `blueforce-install.sh`: bayi no doğrulaması, 18 modül, idempotent yeniden-koşu ve final-check kapısı. Teknisyenin çalıştırdığı tek şeydir.
|
||||
> Kısa özet: Sahadaki tek giriş noktası **grafik operatör uygulamasıdır**: `sudo bf` → Kurulum sekmesi → "Bu cihazı kur". Bayi no doğrulaması, 18 modül, idempotent yeniden-koşu ve final-check kapısı `blueforce-install.sh`'ın işidir. Paketler git deposunda taşınır; **kurulum internetsizdir**. Terminal yedeği: `sudo bf --tui`.
|
||||
|
||||
- Dosya: `docs/05-ONE-CLICK-INSTALLER.md`
|
||||
- İlgili kararlar: `24-DECISION-LOG.md#K-09` (tek kurucu), `K-11` (update kapalı), `K-12` (kimlik formatı), `K-03` (bf-gui), `K-10` (Docker disiplini)
|
||||
- İlgili kararlar: `24-DECISION-LOG.md#K-09` (tek kurucu), `K-11` (update kapalı), `K-12` (kimlik formatı), `K-03` (bf-gui), `K-10` (Docker disiplini), `K-16` (offline)
|
||||
- İlgili dokümanlar: `33-OPERATOR-CONSOLE.md` (arayüz rehberi), `27-OFFLINE-PROVISIONING.md` (paket deposu sözleşmesi)
|
||||
- Durum: [ ] Taslak
|
||||
|
||||
---
|
||||
|
||||
## 1. Amaç
|
||||
|
||||
Saha teknisyeninin uzmanlık gerektirmeden cihazı standarda getirmesi: tek komut, tek girdi (bayi no), net sonuç (READY / hata + log yolu). Yarım kalan veya hatalı kurulumun güvenli tekrarı.
|
||||
Saha teknisyeninin uzmanlık gerektirmeden cihazı standarda getirmesi: tek giriş noktası (`sudo bf`), tek girdi (8 haneli bayi no), net sonuç (`PROVISIONED_OFFLINE` / hata + neden). Yarım kalan veya hatalı kurulumun güvenli tekrarı.
|
||||
|
||||
Teknisyen terminal komutu, bayrak veya dosya yolu ezberlemez: kurulum grafik arayüzdeki düğmeyle başlatılır, komut arayüzde gösterilir ve onay alınmadan çalıştırılmaz.
|
||||
|
||||
## 2. Kapsam
|
||||
|
||||
- Kapsam içi: komut arayüzü, bayi-no doğrulama, 18 modül listesi ve sırası, idempotency kuralları, log/bundle çıktısı, çıkış kodları.
|
||||
- Kapsam dışı: modül iç kodları (repo `scripts/install/`), imaj üretim yöntemi (04), filo-side yapılandırma (09).
|
||||
- Kapsam içi: grafik uygulama üzerinden kurulum akışı, kurucu betiğin komut yüzeyi, bayi-no doğrulama, 18 modül listesi ve sırası, paket deposu keşfi ve hata durumları, idempotency kuralları, log/çıkış kodları.
|
||||
- Kapsam dışı: modül iç kodları (repo `scripts/install/installer/modules/`), imaj üretim yöntemi (04), filo-side yapılandırma (09), arayüzün tüm ekranları (33).
|
||||
|
||||
## 3. Kararlar
|
||||
|
||||
```text
|
||||
KARAR: Saha kurulumu tek giriş noktasıdır: `sudo ./blueforce-install.sh --dealer-id <8hane>`; bayi no `^[0-9]{8}$` ile doğrulanır, uymayan girdi kurucuyu başlatmaz.
|
||||
GEREKÇE: Tek komut teknisyen hatasını en aza indirir; format kapısı yanlış kimliğin 9 sisteme yayılmasını baştan engeller (K-12).
|
||||
ALTERNATİF: Çok scriptli kurulum (ağ.sh, gui.sh, …) — sıra hatası ve atlanan adım riski nedeniyle elendi.
|
||||
RİSK: Tek dosya büyüdükçe bakımı zorlaşır; azaltma: 18 bağımsız modül + ana orkestratör ayrımı.
|
||||
KARAR: Saha kurulumunun tek giriş noktası grafik operatör uygulamasıdır: `sudo bf` → Kurulum sekmesi → "Bu cihazı kur". Kurucu betik (`scripts/install/blueforce-install.sh`) bu düğmenin çağırdığı motordur ve aynı akış terminalden `sudo bf --tui` ile de yürütülebilir. Eski bash menü (`admin/bf-menu`) yedek yoldur, ana yol değildir.
|
||||
GEREKÇE: Tek giriş noktası ve tek düğme teknisyen hatasını en aza indirir; grafik arayüz komutu çalıştırmadan önce gösterir ve onay ister, böylece görünmez komut kalmaz. Bayi no `^[0-9]{8}$` ile doğrulanır; format kapısı yanlış kimliğin 9 sisteme yayılmasını baştan engeller (K-12).
|
||||
ALTERNATİF: Yalnız bash menülü kurulum (`admin/bf-menu`) — grafik arayüz yoksa çalışır ama sahadaki ana yolu oluşturmaz; yedek olarak korundu. Çok scriptli kurulum (ağ.sh, gui.sh, …) — sıra hatası ve atlanan adım riski nedeniyle elendi.
|
||||
RİSK: Tek dosya büyüdükçe bakımı zorlaşır; azaltma: 18 bağımsız modül + ana orkestratör ayrımı. Grafik arayüz `python3-tk` gerektirir; azaltma: paket offline depoda pinlidir ve ekransız/paketsiz ortamda uygulama Türkçe yönlendirme yapıp `--tui` önerir.
|
||||
MALİYET: Ücretsiz.
|
||||
LİSANS: Yok (şirket içi script).
|
||||
LİSANS: Yok (şirket içi script + standart kütüphane ile yazılmış Python arayüz).
|
||||
```
|
||||
|
||||
```text
|
||||
KARAR: Kurucu 18 modülden oluşur (`01-precheck` … `18-final-check`); her modül idempotenttir — yeniden koşu güvenli ve kaldığı yerden tamamlayıcıdır.
|
||||
GEREKÇE: Saha elektriği/kesintisi yarım kurulum üretir; idempotency olmadan her kesinti temiz-kurulum demektir. Final-check kapısı (modül 18) exit 0 vermeden cihaz kurulu sayılmaz; offline akışta sonuç `PROVISIONED_OFFLINE`, token sonrası `ENROLLED`, yalnız merkezi kanallar doğrulanınca `READY` olur (27/29).
|
||||
ALTERNATİF: Tek seferlik (non-idempotent) script — kesinti durumunda belirsiz ara durum nedeniyle elendi.
|
||||
KARAR: Kurucu 18 modülden oluşur (`01-precheck` … `18-final-check`); her modül idempotenttir — yeniden koşu güvenli ve kaldığı yerden tamamlayıcıdır. Paketler yerel paket deposundan gelir ve depo otomatik bulunur; `--offline` bayrağı zorunlu değildir.
|
||||
GEREKÇE: Saha elektriği/kesintisi yarım kurulum üretir; idempotency olmadan her kesinti temiz-kurulum demektir. Final-check kapısı (modül 18) exit 0 vermeden cihaz kurulu sayılmaz; offline akışta sonuç `PROVISIONED_OFFLINE`, token sonrası `ENROLLED`, yalnız merkezi kanallar doğrulanınca `READY` olur (27/29). Depoyu kuran kişinin hatırlaması gereken bayrak sayısı sıfırdır.
|
||||
ALTERNATİF: Tek seferlik (non-idempotent) script — kesinti durumunda belirsiz ara durum nedeniyle elendi. `--offline` bayrağını zorunlu tutmak — operatör bayrağı unuttuğunda kurulum durur; bayrak geriye uyumluluk için korundu.
|
||||
RİSK: Modülün idempotent yazılmaması (çift kayıt, çift key); azaltma: §10'daki çift-koşu testi her modül için zorunlu.
|
||||
MALİYET: Ücretsiz.
|
||||
LİSANS: Yok.
|
||||
|
|
@ -39,115 +42,203 @@ LİSANS: Yok.
|
|||
|
||||
## 4. Neden Bu Karar?
|
||||
|
||||
700 cihaz × teknisyen başına değişen beceri = kurulumun "tek komut + tek soru" olması şarttır. 18 modül, her kararın (update-kapalı, Docker-disiplini, GUI-anahtarı, kimlik, servisler) ayrı denetlenebilir adımdır; modül numarası log satırında görünür, arıza hangi adımda kaldıysa oradan devam edilir.
|
||||
700 cihaz × teknisyen başına değişen beceri = kurulumun "tek düğme + tek soru" olması şarttır. Grafik arayüz bu düğmeyi verir; 18 modül ise her kararın (update-kapalı, Docker-disiplini, GUI-anahtarı, kimlik, servisler) ayrı denetlenebilir adımıdır. Modül numarası log satırında görünür, arıza hangi adımda kaldıysa oradan devam edilir; arayüz aynı durumu "Kurulum modül durumu" işlemiyle salt-okunur gösterir.
|
||||
|
||||
## 5. Alternatifler
|
||||
|
||||
| Alternatif | Artı | Eksi | Sonuç |
|
||||
|---|---|---|---|
|
||||
| Grafik arayüz + tek kurucu betik | Tek giriş, komut ezberi yok, onay görünür | Grafik katman gerekir (`python3-tk`, ekran) | **Seçildi** |
|
||||
| Yalnız bash menülü kurulum (`admin/bf-menu`) | Grafik katman gerekmez | Ana yol olarak terminal bilgisi ister; ekran yok | Yedek yol |
|
||||
| Çok scriptli kurulum | Küçük dosyalar | Sıra/atlama hatası | Elendi |
|
||||
| Non-idempotent tek script | Daha basit kod | Kesintide temiz-kurulum zorunluluğu | Elendi |
|
||||
| İnteraktif sihirbaz (çok sorulu) | Esnek | Teknisyen hatası, otomasyona kapalı | Elendi |
|
||||
|
||||
## 6. Avantajlar
|
||||
|
||||
- Tek komut = eğitim maliyeti minimum, runbook tek sayfa.
|
||||
- Tek giriş noktası (`sudo bf`) = eğitim maliyeti minimum, runbook tek sayfa.
|
||||
- Idempotency = kesinti korkusu yok, "bir daha koş" her sorunun ilk cevabı.
|
||||
- Final-check = READY iddiası ölçülebilir, dalga kapılarına bağlanır.
|
||||
- Final-check = kurulum sonucu ölçülebilir, dalga kapılarına bağlanır.
|
||||
- İnternetsiz çalışır: paketler repoda gelir, kuran kişi hiçbir şey indirmez.
|
||||
|
||||
## 7. Dezavantajlar
|
||||
|
||||
- 18 modülün her biri için idempotency testi yazma maliyeti.
|
||||
- Tek giriş noktası, modül-atlama (debug) ihtiyacında `--from/--only` bayraklarını zorunlu kılar.
|
||||
- Tek giriş noktası, modül-atlama (debug) ihtiyacında `--from/--only/--resume` bayraklarını zorunlu kılar.
|
||||
- Grafik arayüz `python3-tk` paketine ve bir grafik oturuma bağlıdır; bu ikisi yoksa operatör terminal konsoluna (`sudo bf --tui`) düşer.
|
||||
|
||||
## 8. Riskler
|
||||
|
||||
| Risk | Olasılık | Etki | Azaltma |
|
||||
|---|---|---|---|
|
||||
| Modül idempotent değil (çift kayıt) | Orta | Orta | Çift-koşu testi (§10) + code review kuralı |
|
||||
| Teknisyen yanlış no girer | Orta | Orta | Format kapısı + onay ekranı (`BF-xxxx, onaylıyor musunuz?`) |
|
||||
| Kurucu yarıda kesilir, cihaz ara durumda sahaya alınır | Düşük | Yüksek | Final-check exit 0 olmadan READY yok; `bf-status` bunu gösterir |
|
||||
| Teknisyen yanlış no girer | Orta | Orta | Format kapısı (`^[0-9]{8}$`) + onay ekranı (`BF-12010193, onaylıyor musunuz?`) |
|
||||
| Kurucu yarıda kesilir, cihaz ara durumda sahaya alınır | Düşük | Yüksek | Final-check exit 0 olmadan kurulum tamamlanmaz; arayüzdeki "Kurulum modül durumu" ve `bf-status` bunu gösterir |
|
||||
| Grafik arayüz açılmaz (başsız oturum) | Orta | Düşük | Uygulama Türkçe hata + `sudo bf --tui` önerir (19 §T-10) |
|
||||
| `python3-tk` eksik kalır | Düşük | Orta | Paket offline depoda pinli; `18-final-check` tkinter'ı denetler ve eksikse WARN yazar |
|
||||
|
||||
## 9. Uygulama Planı
|
||||
|
||||
1. Komut arayüzü:
|
||||
### 1. Giriş noktası — grafik operatör uygulaması (ana yol)
|
||||
|
||||
```bash
|
||||
sudo ./blueforce-install.sh --dealer-id 12010193 [--from 07] [--only 18-final-check] [--check]
|
||||
# --check: değişiklik yapmadan denetim (final-check + modül durumları)
|
||||
# --from: belirtilen modülden devam et | --only: tek modül koş
|
||||
sudo bf # grafik arayüz (bfos paketi, bfos/ui_gui.py, Tkinter)
|
||||
```
|
||||
|
||||
2. Bayi-no doğrulama: `^[0-9]{8}$`; uymazsa exit 2 + kullanım mesajı. Geçerse onay ekranı (`BF-12010193` + hostname `bf-12010193` gösterilir).
|
||||
3. 18 modül (sıra sözleşmedir, yeri `scripts/install/`):
|
||||
Akış:
|
||||
|
||||
| # | Modül | İşlev (dayandığı karar) |
|
||||
1. Pencere açılır: başlık **Blueforce Field OS — Yönetim**.
|
||||
2. Basit modda üç düğme vardır: `Kur`, `Durum`, `Çıkış`. Üstteki **`Detaylı mod`** ile 5 sekme açılır: `Kurulum`, `Cihaz işlemleri`, `Merkez hazırlığı`, `Araçlar`, `Cihaz bilgisi`.
|
||||
3. Üst çubuktaki **Hedef > Bayi no (8 hane)** alanına numara yazılır (örn. `12010193`); format geçersizse arayüz uyarır ve komut üretilmez.
|
||||
4. `Kurulum` sekmesinde **`Bu cihazı kur`** (veya hiçbir şey yazmayan ön kontrol **`Kurulum ön kontrolü (--check)`**) seçilir.
|
||||
5. Arayüz komutu **önce ekranda gösterir**, sonra onay ister; onaylanmadan çalıştırılmaz. Yıkıcı işlemler ikinci kez sorar; production kapsamı ayrı onay ister.
|
||||
6. Kurulum çıktısı ekranda akar; bitince sonuç ve çıkış kodu görünür.
|
||||
|
||||
`Bu cihazı kur` işleminin iki opsiyonel seçeneği vardır: `Yerel çevrimdışı depodan kur (--offline)` ve `Zaten OK olan modülleri atla (--resume)`. **`--offline` işaretlenmese de varsayılan kaynak yerel depodur**; bayrak yalnız niyeti sabitler.
|
||||
|
||||
### 2. Yedek yol — terminal konsolu
|
||||
|
||||
```bash
|
||||
sudo bf --tui # aynı uygulamanın terminal arayüzü (grafik ekran yoksa / tkinter yoksa)
|
||||
```
|
||||
|
||||
- `DISPLAY` ve `WAYLAND_DISPLAY` tanımlı değilse (SSH, başsız kurulum) uygulama Türkçe açıklama yazar ve **çıkış kodu 3** ile `sudo bf --tui` önerir.
|
||||
- `python3-tk` kurulu değilse (`tkinter` import edilemiyor) aynı şekilde Türkçe hata + `python3-tk` kurulum ipucu verir ve **çıkış kodu 3** ile terminal konsolunu önerir.
|
||||
- Eski bash menü `sudo admin/bf-menu` **yalnız yedek yoldur**; dokümanlarda ve iş akışında ana yol olarak kullanılmaz.
|
||||
|
||||
### 3. Yetki
|
||||
|
||||
- `sudo bf` = tam yetki (kurulum, servis, firewall, Ansible).
|
||||
- Root değilse uygulama başlangıçta uyarır ve yalnız **salt-okunur raporları** çalıştırır: "Yönetici yetkisi yok: salt-okunur raporlar çalışır, kurulum ve değişiklik gerektiren işlemler kapalıdır. Tam yetki için: sudo bf". Yazma işlemleri hem arayüzde kapalıdır hem de çalıştırma katmanında reddedilir (onaylansa bile komut çalışmaz).
|
||||
|
||||
### 4. Bayi-no doğrulama
|
||||
|
||||
`^[0-9]{8}$`; uymazsa arayüz geçersiz numarayı reddeder ve kurucu komut üretilmez (doğrudan CLI kullanımında exit 2 + kullanım mesajı). Geçerliyse onay ekranında `BF-12010193` + hostname `bf-12010193` gösterilir.
|
||||
|
||||
### 5. 18 modül (sıra sözleşmedir, yeri `scripts/install/installer/modules/`)
|
||||
|
||||
| # | Modül | İşlev |
|
||||
|---|---|---|
|
||||
| 01 | `01-precheck` | OS sürümü (26.04.1+), disk/RAM minimumu, root yetkisi, internet kontrolü |
|
||||
| 02 | `02-dealer-id` | No doğrulama + hostname `bf-<no>` atama (K-12) |
|
||||
| 03 | `03-base-packages` | Temel paketler + `unattended-upgrades` kapatma (K-11) |
|
||||
| 04 | `04-gnome` | GNOME kurulumu (K-02) |
|
||||
| 05 | `05-xrdp` | xRDP + sesman + `bf-gui-on/off` yazımı (K-03) |
|
||||
| 06 | `06-ssh-hardening` | `blueforce` kullanıcısı, key-auth, root/password kapalı (K-13) |
|
||||
| 07 | `07-wireguard` | Spoke config, peer `bf-<no>`, keepalive 25, `wg-quick@wg0` enable (K-05) |
|
||||
| 08 | `08-docker` | Docker CE + `unless-stopped` + log rotasyonu + localhost-bind (K-10) |
|
||||
| 09 | `09-meg` | MEG konteyneri (sabit tag, `latest` yok) (K-10) |
|
||||
| 10 | `10-rustdesk` | RustDesk istemci + VDS adresi + `BF-<no>` etiketi (K-04) |
|
||||
| 11 | `11-meshcentral` | MeshCentral agent + `BF-<no>` adı (K-04) |
|
||||
| 12 | `12-monitoring` | Node Exporter + textfile + Uptime Push (K-07) |
|
||||
| 13 | `13-firewall` | UFW profili (SSH/RDP yalnızca WG) (K-05) |
|
||||
| 14 | `14-inventory` | `bf-hardware-inventory.sh` koşusu, JSON+MD üretimi (02) |
|
||||
| 15 | `15-logging` | journald + Docker rotasyon + `bf-status/bf-diagnostics` (K-14) |
|
||||
| 16 | `16-boot` | multi-user default + BIOS notu + servis enable denetimi (K-03) |
|
||||
| 17 | `17-ansible-bootstrap` | Bootstrap SSH key + ilk pull/Ansible kaydı (K-06/K-13) |
|
||||
| 18 | `18-final-check` | Tüm kapılar: kimlik, servisler, update-kapalı, Docker politikası, READY damgası |
|
||||
| 01 | `01-precheck` | Root, Ubuntu sürümü, RAM/disk ön kontrolü; ağ yalnız bilgi (kapı değil) |
|
||||
| 02 | `02-system` | Temel APT kaynakları, `unattended-upgrades` kapatma, saat dilimi (K-11) |
|
||||
| 03 | `03-user` | `blueforce` bakım kullanıcısı, kısıtlı sudoers, zorunlu yönetici anahtarı (K-13) |
|
||||
| 04 | `04-hostname` | Doğrulanmış bayi no'dan hostname `bf-<no>` (K-12) |
|
||||
| 05 | `05-network` | Ağ raporu: yerel bağlantı, rota/DNS (DNS zorunlu tutulmaz — offline kurulum) |
|
||||
| 06 | `06-wireguard` | Cihaz anahtar çifti; `wg0.conf` enrollment'ın işidir (K-05/K-17) |
|
||||
| 07 | `07-ssh` | Yönetici anahtarı doğrulandıktan **sonra** sshd sertleştirme |
|
||||
| 08 | `08-rdp` | xRDP + GNOME oturumu, terminale boot varsayılanı, multi-user (K-03/K-22) |
|
||||
| 09 | `09-rustdesk` | Yalnız pinli/sağlanan paketten RustDesk istemci (K-04) |
|
||||
| 10 | `10-docker` | Docker CE + rotasyonlu `daemon.json` + enable (K-10) |
|
||||
| 11 | `11-meg` | MEG yer tutucusu: vendor `.deb` varsa kurar, yoksa SKIP + kabul listesi |
|
||||
| 12 | `12-monitoring` | Node Exporter + textfile collector dizini (K-07) |
|
||||
| 13 | `13-firewall` | UFW deny-in tabanı + Docker yayın-portu politikasının kalıcılığı (K-05/K-10) |
|
||||
| 14 | `14-update-policy` | APT hold'ları + otomatik update zamanlayıcılarını kapatma (K-11) |
|
||||
| 15 | `15-logrotate` | journald tavanları + uygulama logrotate politikası (K-14) |
|
||||
| 16 | `16-gui` | GNOME minimal + bakım araçları; terminale boot varsayılanı (K-03) |
|
||||
| 17 | `17-healthcheck` | Teşhis ve enrollment komutlarını kurar; enrollment'ı çalıştırmaz |
|
||||
| 18 | `18-final-check` | Son yaşam döngüsü kapısı: offline yerelde tamamlanır, `READY` merkez kanıtı ister; tkinter denetimi (yoksa WARN) |
|
||||
|
||||
4. Idempotency kuralları (her modül için zorunlu):
|
||||
- Varlık kontrolü önce (`id blueforce`, `dpkg -l`, `systemctl is-enabled`): varsa atla, yoksa kur.
|
||||
- Dosya yazımları atomik (tmp + rename) ve tekrarlanabilir (aynı girdi → aynı çıktı).
|
||||
- Anahtar/secret üretimi bir kez (`/var/lib/blueforce/.modül-done` damgası); `--force` olmadan yeniden üretilmez.
|
||||
- Her modül `/var/log/blueforce-install.log` dosyasına `MODÜL-NO + OK/SKIP/FAIL` satırı yazar.
|
||||
5. Çıkış kodları: `0` READY, `1` modül hatası (no logda), `2` argüman/format hatası.
|
||||
Modül çıkış kodları: `0` OK, `3` SKIP (opsiyonel adım, yapılacak bir şey yok), diğer ≠ 0 FAIL.
|
||||
|
||||
### 6. Paket deposu — otomatik keşif, iki hata durumu, sıfır indirme
|
||||
|
||||
Installer paket deposunu **kendi bulur**; `--offline` bayrağı **gerekmez** (geriye uyumluluk için kabul edilir, yalnız yerel depoyu zorlar). Arama sırası (ilk eksiksiz aday kazanır):
|
||||
|
||||
1. `BF_OFFLINE_REPO` ortam değişkeni (operatör/repo sorumlusu override'ı),
|
||||
2. `<repo>/provisioning/offline-repo/built` (git deposuyla gelen paket seti),
|
||||
3. `/opt/blueforce/offline-repo` (cihaza kurulmuş paket deposu).
|
||||
|
||||
Depo bulunduğunda ekrana Türkçe duyuru yazılır: `Yerel paket deposu bulundu: <N> paket, <boyut>` + `Depo`, `Kaynak`, `Kip` satırları. İki farklı başarısızlık vardır ve **ikisinde de paket modüllerinden önce durulur, sistemde hiçbir değişiklik yapılmaz**:
|
||||
|
||||
| Durum | Ekran | Sonuç |
|
||||
|---|---|---|
|
||||
| Depo hiçbir yerde yok | `HATA: Yerel paket deposu yok. Repo sorumlusuna başvurun: provisioning/offline-repo/README.md` + aranan yollar + `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.` | exit 1 |
|
||||
| Depo bulundu ama bozuk/eksik | `OFFLINE BLOCKED: <sebep>` + Türkçe çıkış nedeni (ör. checksum uyuşmazlığı, eksik `Packages`/`packages.lock.tsv`, eksik pin) | exit 1 |
|
||||
|
||||
Uzak APT kaynağı, `curl`, `wget`, anahtar indirme veya `apt download` çağrılmaz. Yalnız geliştirme/test için `--online` vardır ve üretim cihazlarında kullanılmaz (DİKKAT uyarısı yazar).
|
||||
|
||||
### 7. Kurucu betiğin komut yüzeyi (yedek/otomasyon)
|
||||
|
||||
```bash
|
||||
sudo ./scripts/install/blueforce-install.sh --dealer-id 12010193 [--from 07] [--only 18-final-check] [--resume] [--check] [--yes]
|
||||
# --check : değişiklik yapmadan denetim (final-check + modül durumları)
|
||||
# --from : belirtilen modülden devam et | --only: tek modül koş | --resume: OK modülleri atla
|
||||
# --yes : onay sormadan başla (otomasyon; GUI bu bayrağı kullanmaz)
|
||||
# --offline: yerel depoyu zorla (varsayılan zaten yerel) | --online: yalnız geliştirme/test
|
||||
```
|
||||
|
||||
### 8. Idempotency kuralları (her modül için zorunlu)
|
||||
|
||||
- Varlık kontrolü önce (`id blueforce`, `dpkg -l`, `systemctl is-enabled`): varsa atla, yoksa kur.
|
||||
- Dosya yazımları atomik (tmp + rename) ve tekrarlanabilir (aynı girdi → aynı çıktı).
|
||||
- Anahtar/secret üretimi bir kez; `--force` olmadan yeniden üretilmez.
|
||||
- Her modül `/var/log/blueforce-install.log` dosyasına modül no + OK/SKIP/FAIL satırı yazar.
|
||||
|
||||
### 9. Çıkış kodları ve sonuç durumu
|
||||
|
||||
Kurucu betik: `0` başarı (offline akışta `PROVISIONED_OFFLINE`), `1` engellendi veya modül hatası (no logda), `2` argüman/format hatası. Grafik/terminal arayüz: `3` grafik katman yok (ekran veya `python3-tk`), `2` kullanım hatası (`--tui` + `--gui` birlikte verilemez), `0` normal kapanış.
|
||||
|
||||
`READY` bir kurucu çıkış kodu değildir: offline akış `PROVISIONED_OFFLINE` yazar, `READY` yalnız merkezi kanallar doğrulandığında (29/30) yazılır.
|
||||
|
||||
## 10. Test Planı
|
||||
|
||||
| Test | Beklenen sonuç | Ortam |
|
||||
|---|---|---|
|
||||
| Geçersiz no (7/9 hane, harf, boş) | exit 2, kurulum başlamaz | LAB(2) |
|
||||
| Geçersiz no (7/9 hane, harf, boş) | Arayüz reddeder / CLI exit 2, kurulum başlamaz | LAB(2) |
|
||||
| `sudo bf` ile kurulum (grafik) | Komut önce gösterilir, onay ister, kurulum `PROVISIONED_OFFLINE` ile biter | LAB(2) masaüstü oturumu |
|
||||
| `DISPLAY` yokken `sudo bf` | Türkçe yönlendirme + `sudo bf --tui` önerisi, exit 3, sistem değişmez | LAB(2) SSH |
|
||||
| `python3-tk` yokken `sudo bf` | Türkçe "tkinter bulunamadı" + paket ipucu, exit 3 | LAB(2) minimal imaj |
|
||||
| Her modülün çift-koşusu | İkinci koşu exit 0, durum değişmez (diff boş) | LAB(2) |
|
||||
| Elektrik-kesintisi simülasyonu (modül 09 ortasında kill) | `--from 09` ile tamamlanır, final exit 0 | LAB(2) |
|
||||
| `18-final-check` tüm kapıları | Eksik adımda FAIL + modül nosu, READY yok | LAB(2) |
|
||||
| `--check` kuru koşusu | Değişiklik yapmaz, rapor üretir | P1(5) |
|
||||
| Elektrik-kesintisi simülasyonu (modül 09 ortasında kill) | `--from 09` veya arayüzdeki `--resume` ile tamamlanır, final exit 0 | LAB(2) |
|
||||
| `18-final-check` tüm kapıları | Eksik adımda FAIL + modül nosu, başarı damgası yok | LAB(2) |
|
||||
| `--check` kuru koşusu (GUI ön kontrol / CLI) | Değişiklik yapmaz, rapor üretir | P1(5) |
|
||||
| Depo yokken kurulum denemesi | Türkçe hata + aranan yollar + çıkış nedeni, exit 1, sistem değişmez | LAB(2) |
|
||||
| Depo bozukken kurulum denemesi | `OFFLINE BLOCKED: <sebep>` + Türkçe çıkış nedeni, exit 1 | LAB(2) |
|
||||
|
||||
## 11. Rollback
|
||||
|
||||
1. Modül hatası: logdaki modül nosundan `--from <no>` ile devam edilir.
|
||||
2. Yanlış no: doğru no ile tam yeniden koşu (idempotent, kimlik modülü üzerine yazar).
|
||||
1. Modül hatası: arayüzdeki "Kurulum modül durumu"/log ile modül nosu bulunur; `--from <no>` veya arayüzdeki `Başarısız kuruluma devam et (--resume)` ile sürdürülür.
|
||||
2. Yanlış no: doğru no ile tam yeniden koşu (idempotent, hostname modülü üzerine yazar).
|
||||
3. Düzelmeyen cihaz: 17-RECOVERY LEVEL 9 (temiz imaj + kurucu).
|
||||
4. Paket deposu bozuksa sahada onarılmaz: repo sorumlusu depoyu yeniden üretip commit eder, güncel git tarafı çekilir ve komut tekrarlanır (27 §11).
|
||||
|
||||
## 12. Kontrol Listesi
|
||||
|
||||
- [ ] 18 modül dosyası `scripts/install/` altında numaralı ve çalışır durumda.
|
||||
- [ ] Her modülde varlık-kontrolü + damga + log satırı var.
|
||||
- [ ] 18 modül dosyası `scripts/install/installer/modules/` altında numaralı ve çalışır durumda.
|
||||
- [ ] Grafik arayüzde `Kurulum` sekmesi ve `Bu cihazı kur` işlemi bayi-no kapısına bağlı.
|
||||
- [ ] Uygulama ekransız ortamda ve tkinter yokken Türkçe yönlendirme + `--tui` önerisi verip exit 3 ile çıkıyor.
|
||||
- [ ] Her modülde varlık-kontrolü + log satırı var.
|
||||
- [ ] Format regex'i 02 ile aynı (`^[0-9]{8}$`).
|
||||
- [ ] `18-final-check` K-10/K-11 kapılarını denetliyor (unless-stopped, latest yok, update kapalı).
|
||||
- [ ] `--check/--from/--only` bayrakları çalışıyor.
|
||||
- [ ] `18-final-check` K-10/K-11 kapılarını ve tkinter denetimini içeriyor.
|
||||
- [ ] Depo keşif sırası ve iki hata durumu (yok / bozuk) ekranda Türkçe ve fail-closed.
|
||||
- [ ] `--check/--from/--only/--resume` bayrakları çalışıyor; `--offline` zorunlu değil.
|
||||
|
||||
## 13. Açık Sorular
|
||||
|
||||
- [ ] Kurucu USB'de mi, merkezden `curl` ile mi alınacak (saha interneti varsayımı) (sahibi: Faz 3, 04 yazarı ile ortak).
|
||||
- [ ] Paket setinin sahaya taşınması: git clone (Git LFS) mı, repo sorumlusunun hazırladığı `/opt/blueforce/offline-repo` kopyası mı birincil? (sahibi: Faz 3, 04/27 yazarları ile ortak)
|
||||
- [ ] MEG sabit tag'inin kim tarafından, hangi onay ile güncelleneceği (sahibi: 10-UPDATE / 12-DOCKER yazarları).
|
||||
- [ ] Arayüzün grafik ekran gerektirmeyen cihazlarda varsayılan modu (`--tui` mi, grafik mi) — saha ölçümü ile karar (sahibi: 33 + saha operasyonu).
|
||||
|
||||
---
|
||||
|
||||
## Ek: Kurucu Akış
|
||||
## Ek: Kurulum Akışı (grafik arayüz)
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
CMD["sudo ./blueforce-install.sh<br/>--dealer-id <8hane>"] --> FMT{"^[0-9]8$ ?"}
|
||||
FMT -->|Hayır| EXIT2["exit 2<br/>kullanım mesajı"]
|
||||
FMT -->|Evet| M01["01-precheck … 17-bootstrap<br/>(idempotent modüller)"]
|
||||
CMD["sudo bf<br/>(grafik operatör uygulaması)"] --> EKRAN{"Grafik ekran +<br/>python3-tk var mı?"}
|
||||
EKRAN -->|Hayır| TUI["Türkçe yönlendirme + exit 3<br/>sudo bf --tui"]
|
||||
EKRAN -->|Evet| HEDEF["Hedef: Bayi no (8 hane)"]
|
||||
HEDEF --> FMT{"^[0-9]{8}$ ?"}
|
||||
FMT -->|Hayır| UYARI["Arayüz uyarır<br/>komut üretilmez"]
|
||||
FMT -->|Evet| KUR["Kurulum sekmesi<br/>'Bu cihazı kur'"]
|
||||
KUR --> ONAY["Komut gösterilir + onay<br/>(yıkıcı: ikinci onay)"]
|
||||
ONAY --> DEPO{"Yerel paket deposu aranır<br/>BF_OFFLINE_REPO → built → /opt/blueforce/offline-repo"}
|
||||
DEPO -->|"hiçbiri yok"| YOK["Türkçe hata + aranan yollar<br/>ÇIKIŞ NEDENİ: bulunamadı · exit 1"]
|
||||
DEPO -->|"bozuk/eksik"| BOZ["OFFLINE BLOCKED: sebep<br/>exit 1 · sistem değişmez"]
|
||||
DEPO -->|"eksiksiz"| M01["01-precheck … 17-healthcheck<br/>(idempotent modüller)"]
|
||||
M01 --> M18["18-final-check"]
|
||||
M18 -->|Tümü OK| READY["READY damgası<br/>bf-status yeşil"]
|
||||
M18 -->|FAIL| LOG["modül nosu + log yolu<br/>--from ile devam"]
|
||||
M18 -->|Hepsi OK| OFFLINE["PROVISIONED_OFFLINE"]
|
||||
M18 -->|FAIL| LOG["modül nosu + log yolu<br/>--from / --resume ile devam"]
|
||||
OFFLINE --> ENR["Token → ENROLLED"]
|
||||
ENR --> READY["Merkez kanalları doğrulandı → READY"]
|
||||
```
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ Tekrar eden arızalarda teşhisi hızlandırmak: belirtiyi bulan teknisyen/admin
|
|||
|
||||
## 2. Kapsam
|
||||
|
||||
- Kapsam içi: boot, ağ/VPN, SSH, RDP/GUI, RustDesk, MeshCentral, Docker/MEG, disk, monitoring belirti tabloları.
|
||||
- Kapsam dışı: seviye kararları (17), adım-adım kurulum (20), merkez onay akışı (21).
|
||||
- Kapsam içi: boot, ağ/VPN, SSH, RDP/GUI, RustDesk, MeshCentral, Docker/MEG, disk, monitoring ve operatör arayüzü (grafik/terminal konsol, paket deposu keşfi) belirti tabloları.
|
||||
- Kapsam dışı: seviye kararları (17), adım-adım kurulum (20), merkez onay akışı (21), arayüzün tüm ekranları (33).
|
||||
|
||||
## 3. Kararlar
|
||||
|
||||
|
|
@ -69,6 +69,14 @@ docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.RestartPolicy}}'
|
|||
df -h / /var/lib/docker | cat
|
||||
```
|
||||
|
||||
Aynı raporlar operatör arayüzünden de okunur (komut ezberi gerekmez):
|
||||
|
||||
```text
|
||||
Arayüzden: sudo bf → `Durum` düğmesi (Durum özeti) ya da Detaylı mod → Cihaz bilgisi sekmesi
|
||||
(kimlik, ağ/WireGuard, kaynaklar, servisler, son journal hataları — hepsi salt-okunur)
|
||||
Komutla (yedek): sudo bf --tui → Cihaz bilgisi menüsü
|
||||
```
|
||||
|
||||
### T-1 — Cihaz açılmıyor / ekranda hiçbir şey yok
|
||||
|
||||
| # | Neden (olasılık sırası) | Tanı | Çözüm |
|
||||
|
|
@ -147,6 +155,29 @@ Yükseltme: WG kalkmıyorsa RustDesk/MeshCentral ile devam (17-L4); onlar de yok
|
|||
| 2 | Uptime Push gelmiyor | Kuma "last seen" | Cihaz interneti + push job'ı (T-3'e dön) |
|
||||
| 3 | Etiket yanlış (`BF-<no>`) | Prometheus target etiketleri | Envanterle eşitle (02) |
|
||||
|
||||
### T-10 — Grafik operatör arayüzü açılmıyor (`sudo bf` pencere açmıyor / hemen kapanıyor)
|
||||
|
||||
| # | Neden (olasılık sırası) | Tanı | Çözüm |
|
||||
|---|---|---|---|
|
||||
| 1 | Ekran yok (SSH/başsız oturum) | Ekranda "Grafik ekran bulunamadı (DISPLAY ve WAYLAND_DISPLAY tanımlı değil)" + yönlendirme; çıkış kodu `3` | `sudo bf --tui` ile aynı işlemleri terminal konsolunda yürüt; yerel ekranda pencereli kullanmak için oturum açık bir makinede `sudo bf` |
|
||||
| 2 | `python3-tk` kurulu değil (minimal imaj) | "tkinter bulunamadı; masaüstü arayüzü başlatılamıyor" + paket ipucu; çıkış kodu `3` | Çevrimdışı sahada paket kurulmaz: paket offline depoda pinlidir (`manifests/base-packages.txt`) ve normal kurulumla gelir. Eksikse `sudo bf --tui` ile devam et ve merkeze bildir; kurulum sonu kapısı (`18-final-check`) bu durumda **WARN** yazar ve kurulumu düşürmez |
|
||||
| 3 | `DISPLAY` var ama X/Wayland erişilemiyor (ssh -X yok, oturum kilitli) | "Pencere açılamadı; grafik oturumuna erişilemiyor" + teknik ayrıntı; çıkış kodu `3` | `sudo bf --tui`; yerel oturumda çalıştırıyorsan kilitli ekranı aç veya `ssh -X` yerine yerel konsolu kullan |
|
||||
| 4 | Uygulama açıldı ama yazma işlemleri kapalı | Arayüzde "Yönetici yetkisi yok: salt-okunur raporlar çalışır, kurulum ve değişiklik gerektiren işlemler kapalıdır. Tam yetki için: sudo bf" | Komutu `sudo` ile çalıştır; aynı işlem root olmadan çalıştırma katmanında da reddedilir |
|
||||
|
||||
Yükseltme: pencere hiç açılmıyorsa ve `sudo bf --tui` de çalışmıyorsa (Python 3.8+ yok veya `bfos` paketi bulunamıyor) merkeze telefon aç; yedek bash menü `sudo admin/bf-menu` ana yol değildir, yalnız acil yedektir.
|
||||
|
||||
### T-11 — "Yerel paket deposu bulunamadı" (kurulum paket adımlarına geçmiyor)
|
||||
|
||||
| # | Neden (olasılık sırası) | Tanı | Çözüm |
|
||||
|---|---|---|---|
|
||||
| 1 | Depo cihazda hiç yok | Ekranda `HATA: Yerel paket deposu yok. Repo sorumlusuna başvurun: provisioning/offline-repo/README.md` + **aranan yollar** + `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.`; çıkış kodu `1`, sistem değişmez | Paket setini git ile tam olarak getir (paketler Git LFS ile gelir) ya da repo sorumlusunun bıraktığı kopyayı `/opt/blueforce/offline-repo` altına koy; kurulumu tekrar çalıştır |
|
||||
| 2 | `packages/` dolu ama indeks (`built/`) yok | Aynı "depo yok" hatası: aday eksiksiz sayılmaz | Kurucu `Packages`, `Packages.gz`, `packages.lock.tsv`, `SHA256SUMS` dosyalarının **hepsini** arar; yalnız `.deb` havuzu yetmez. Repo sorumlusu `build-index.sh` ile `built/` dizinini üretip commit eder |
|
||||
| 3 | Depo bulundu ama bozuk/eksik | `OFFLINE BLOCKED: <sebep>` + Türkçe çıkış nedeni (ör. checksum doğrulaması başarısız, kilit biçimi bozuk, pinlenen paket depoda yok); çıkış kodu `1` | Sahada onarılmaz: repo sorumlusu seti `add-packages.sh` + `build-index.sh` ile yeniden üretir, güncel git tarafı çekilir ve komut tekrarlanır. Cihazda hiçbir değişiklik yapılmadı |
|
||||
| 4 | `BF_OFFLINE_REPO` yanlış | `OFFLINE BLOCKED: BF_OFFLINE_REPO mutlak bir yerel dizin olmalıdır.` | Değişkeni kaldır (varsayılan arama sırası kullanılır) veya geçerli bir mutlak dizin ver |
|
||||
| 5 | "--offline bayrağını vermedim" kuşkusu | — | Bayrak **zorunlu değil**: kurucu depoyu her durumda otomatik arar (sıra: `BF_OFFLINE_REPO` → `<repo>/provisioning/offline-repo/built` → `/opt/blueforce/offline-repo`) |
|
||||
|
||||
Yükseltme: paket seti sahada üretilmez/onarılmaz. İki denemede de aynı hata varsa cihazda hata ayıklamayı bırak, repo sorumlusuna başvur; kurulum paket adımlarına geçmediği için cihaz yarım durumda **kalmaz** (fail-closed).
|
||||
|
||||
## 10. Test Planı
|
||||
|
||||
| Test | Beklenen sonuç | Ortam |
|
||||
|
|
@ -154,6 +185,9 @@ Yükseltme: WG kalkmıyorsa RustDesk/MeshCentral ile devam (17-L4); onlar de yok
|
|||
| Her T girdisindeki ilk tanı komutu LAB'da çalışır | Komut + örnek çıktı bu dosyada | LAB(2) |
|
||||
| WG-kill → T-3 → L4 onarım tatbikatı | < hedef sürede tünel döner | P1(5) |
|
||||
| Disk-doldur simülasyonu → T-8 | Alarm + temizlik çalışır | LAB(2) |
|
||||
| `DISPLAY` yok + `python3-tk` yok senaryoları → T-10 | Her iki durumda Türkçe yönlendirme, çıkış kodu 3, `--tui` önerisi | LAB(2) |
|
||||
| Depo dizini kaldırılmış cihazda kurulum → T-11 | Türkçe hata + aranan yollar + çıkış nedeni, exit 1, sistem değişmez | LAB(2) |
|
||||
| Yalnız `packages/`, indeks yok → T-11/2 | Kurulum "depo yok" diyerek durur (fail-closed) | LAB(2) |
|
||||
|
||||
## 11. Rollback
|
||||
|
||||
|
|
@ -165,12 +199,15 @@ Yanlış çözüm uygulandıysa 17-L3 (son değişikliği geri alma) işletilir;
|
|||
- [ ] Komutların tamamı LAB 26.04.1 imajında denendi.
|
||||
- [ ] Hiçbir girdide secret/şifre yazılmıyor.
|
||||
- [ ] MEG'e özgü satırlar 13 ile çapraz linkli.
|
||||
- [ ] T-10/T-11 çıkış kodları (3 / 1) ve hata metinleri gerçek çıktıyla doğrulandı.
|
||||
- [ ] Arayüz yolu belirtilen her girdide "Arayüzden" + "Komutla (yedek)" satırı var.
|
||||
|
||||
## 13. Açık Sorular
|
||||
|
||||
- [ ] Kesin `bf-gui-*` birim adları + GNOME oturum satırı — LAB çıktısı (sahibi: Faz 3).
|
||||
- [ ] MEG log hacmi ölçümü → T-8 eşikleri (sahibi: 12-DOCKER yazarı).
|
||||
- [ ] Turkcell CGNAT tanı notları — P1 verisi (sahibi: 08-WIREGUARD yazarı).
|
||||
- [ ] `python3-tk` dışındaki grafik bağımlılıklarının da offline pinde gerekli olup olmadığı — LAB doğrulaması (sahibi: 27 + 33 yazarları).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -178,7 +215,10 @@ Yanlış çözüm uygulandıysa 17-L3 (son değişikliği geri alma) işletilir;
|
|||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
SORUN["Arıza bildirimi<br/>bf-status + systemctl --failed"] --> ACILIR{"Cihaz açılıyor mu?"}
|
||||
SORUN["Arıza bildirimi<br/>bf-status + systemctl --failed"] --> ARAC{"Sorun cihaz mı,<br/>operatör arayüzü mü?"}
|
||||
ARAC -->|"Arayüz açılmıyor"| T10["T-10: DISPLAY / tkinter → sudo bf --tui"]
|
||||
ARAC -->|"Kurulum paket adımına geçmiyor"| T11["T-11: yerel paket deposu"]
|
||||
ARAC -->|Cihaz| ACILIR{"Cihaz açılıyor mu?"}
|
||||
ACILIR -->|Hayır| T1["T-1: güç / BIOS / disk"]
|
||||
ACILIR -->|Evet| BOOTOK{"Servisler kalkıyor mu?"}
|
||||
BOOTOK -->|Hayır| T2["T-2: failed servis / disk dolu"]
|
||||
|
|
|
|||
|
|
@ -1,41 +1,43 @@
|
|||
# 20 — Saha Teknisyeni Runbook
|
||||
|
||||
> Kısa özet: Sahada cihaz başına giden teknisyenin el kitabı. Tek akış: USB'yi tak → kur → bayi numarasını gir → READY'yi gör → çık. Komut ezberi istenmez; her adımda "ne göreceksin, ne yazacaksın" vardır.
|
||||
> Kısa özet: Sahada cihaz başına giden teknisyenin el kitabı. Tek akış: USB'yi tak → Ubuntu'yu kur → `sudo bf` → bayi numarasını gir → sonucu gör. Komut ezberi istenmez: kurulum, durum ve destek paketi grafik arayüzdeki düğmelerle yapılır; her adımda "ne göreceksin, ne yazacaksın" vardır.
|
||||
|
||||
- Dosya: `docs/20-FIELD-TECHNICIAN-RUNBOOK.md`
|
||||
- İlgili kararlar: `24-DECISION-LOG.md#K-09` (USB → script → bayi no → READY), `#K-12` (bayi no → `BF-<no>`), `#K-14` (`bf-status`, support-bundle)
|
||||
- İlgili kararlar: `24-DECISION-LOG.md#K-09` (USB → kurucu → bayi no), `#K-12` (bayi no → `BF-<no>`), `#K-14` (`bf-status`, support-bundle), `#K-16` (offline kurulum)
|
||||
- İlgili dokümanlar: `33-OPERATOR-CONSOLE.md` (arayüz rehberi), `27-OFFLINE-PROVISIONING.md` (paket deposu)
|
||||
- Durum: [ ] Taslak
|
||||
|
||||
---
|
||||
|
||||
## 1. Amaç
|
||||
|
||||
Teknisyenin sahada tek başına, telefonsuz bile cihazı READY hale getirmesi. Bu dosya "neden" anlatmaz, "ne yap" söyler. Neden merak eden 01'e bakar.
|
||||
Teknisyenin sahada tek başına, telefonsuz bile cihazı teslim edilebilir hale getirmesi. Bu dosya "neden" anlatmaz, "ne yap" söyler. Neden merak eden 01'e bakar.
|
||||
|
||||
## 2. Kapsam
|
||||
|
||||
- Kapsam içi: USB ile sıfır kurulum, bayi no girişi, READY kontrolü, arızalı cihazda ilk 3 hareket, merkeze ne zaman telefon açılacağı.
|
||||
- Kapsam içi: USB ile sıfır kurulum, grafik arayüzü açma, bayi no girişi, kurulum sonucunu doğrulama, arızalı cihazda ilk 3 hareket, merkeze ne zaman telefon açılacağı.
|
||||
- Kapsam dışı: merkez onayları (21), arıza teşhis tablosu detayı (19 — sadece yönlendirme), update dalgaları (10).
|
||||
|
||||
## 3. Kararlar
|
||||
|
||||
```text
|
||||
KARAR: Teknisyen akışı 4 adımdır: USB → kur → bayi no → READY. Teknisyen hostname, IP, anahtar, config dosyası ile uğraşmaz; hepsini `blueforce-install.sh` yapar.
|
||||
GEREKÇE: K-09'un hedef akışı; insan hatası en çok elle config'de olur. Teknisyenin tek manuel girdisi 8 haneli bayi numarasıdır (K-12).
|
||||
ALTERNATİF: Teknisyene tam Linux eğitimi + manuel kurulum — 700 cihazda standardizasyon bozulur; elendi.
|
||||
RİSK: Script USB'si eski sürüm kalır; azaltma: USB etiketinde sürüm + merkezden "şu sürümden eski USB'yi kullanma" duyurusu.
|
||||
KARAR: Teknisyen akışı 4 adımdır: USB → Ubuntu kur → `sudo bf` ile kurulum (bayi no) → sonucu gör. Grafik arayüz tek giriş noktasıdır; terminal konsolu (`sudo bf --tui`) yalnız grafik ekran veya tkinter yoksa kullanılan yedektir. Teknisyen hostname, IP, anahtar, paket yolu veya config dosyası ile uğraşmaz; paketler depoyla gelir ve kurulumda internet gerekmez.
|
||||
GEREKÇE: K-09/K-16'nın hedef akışı; insan hatası en çok elle config ve ezberlenen komutta olur. Teknisyenin tek manuel girdisi 8 haneli bayi numarasıdır (K-12).
|
||||
ALTERNATİF: Teknisyene tam Linux eğitimi + manuel kurulum — 700 cihazda standardizasyon bozulur; elendi. Yalnız bash menülü kurulum — grafik arayüz varken gereksiz terminal bilgisi ister; yedek yol olarak korundu.
|
||||
RİSK: Depo kopyası eski sürüm kalır; azaltma: repo sürüm etiketi + merkezden "şu sürümden eski kopyayı kullanma" duyurusu.
|
||||
MALİYET: Ücretsiz.
|
||||
LİSANS: Yok.
|
||||
```
|
||||
|
||||
## 4. Neden Bu Karar?
|
||||
|
||||
Teknisyenin işi "bilgisayarcılık" değil "bayi cihazını çalışır teslim etmek"tir. Karar, teknisyenin dikkatini tek kritik noktaya toplar: bayi numarasını doğru girmek. Geri kalan her şey script + merkez otomasyonudur. Bu yüzden bu dosya kısa cümlelerle, emir kipiyle yazılır.
|
||||
Teknisyenin işi "bilgisayarcılık" değil "bayi cihazını çalışır teslim etmek"tir. Karar, teknisyenin dikkatini tek kritik noktaya toplar: bayi numarasını doğru girmek. Geri kalan her şey uygulama + merkez otomasyonudur. Bu yüzden bu dosya kısa cümlelerle, emir kipiyle yazılır.
|
||||
|
||||
## 5. Alternatifler
|
||||
|
||||
| Alternatif | Artı | Eksi | Sonuç |
|
||||
|---|---|---|---|
|
||||
| Grafik arayüz (`sudo bf`) | Tek düğme, komut ezberi yok, onay görünür | Grafik katman gerekir | **Seçildi** |
|
||||
| Detaylı Linux kılavuzu | Bilgili teknisyen | Uzun, hata açık, herkes okumaz | Elendi |
|
||||
| Merkezden uzaktan kurulum (teknisyensiz) | Saha maliyeti yok | İlk kurulumda cihazda internet/VPN yok | Elendi (ilk kurulum sahada) |
|
||||
|
||||
|
|
@ -43,19 +45,21 @@ Teknisyenin işi "bilgisayarcılık" değil "bayi cihazını çalışır teslim
|
|||
|
||||
- Yeni teknisyen 1 saatlik eğitimle sahaya çıkar.
|
||||
- Her cihaz aynı adımla kurulur; "ustalık farkı" kapanır.
|
||||
- Kurulum internetsiz çalışır: paket seti depodan gelir, hiçbir şey indirilmez.
|
||||
|
||||
## 7. Dezavantajlar
|
||||
|
||||
- Script'in yapmadığını teknisyen de yapamaz; olağandışı durumda merkeze bağımlıdır (bilerek böyle).
|
||||
- USB medyası fiziksel lojistik ister (sürüm takibi).
|
||||
- Uygulamanın yapmadığını teknisyen de yapamaz; olağandışı durumda merkeze bağımlıdır (bilerek böyle).
|
||||
- Depo kopyası (paket setiyle birlikte ~888 MB) fiziksel/lojistik takip ister.
|
||||
|
||||
## 8. Riskler
|
||||
|
||||
| Risk | Olasılık | Etki | Azaltma |
|
||||
|---|---|---|---|
|
||||
| Bayi no yanlış girilir | Orta | Yüksek | Script ekranda `BF-<no>`'yu gösterir, teknisyen bayiye onaylatır |
|
||||
| Eski USB ile kurulum | Orta | Orta | USB sürüm etiketi + kurucu açılışta sürümü söyler |
|
||||
| READY görülmeden sahadan çıkılır | Düşük | Yüksek | Kontrol listesi imzalanmadan iş kapanmaz |
|
||||
| Bayi no yanlış girilir | Orta | Yüksek | Arayüz `BF-<no>`'yu gösterir, teknisyen bayiye onaylatır |
|
||||
| Eski depo kopyası ile kurulum | Orta | Orta | Repo sürüm etiketi + kurucu başlangıçta depoyu ve paket sayısını söyler |
|
||||
| Grafik arayüz açılmaz (ekran/tkinter) | Orta | Orta | `sudo bf --tui` yedeği; sorun merkeze bildirilir |
|
||||
| Sonuç görülmeden sahadan çıkılır | Düşük | Yüksek | Kontrol listesi imzalanmadan iş kapanmaz |
|
||||
|
||||
## 9. Uygulama Planı
|
||||
|
||||
|
|
@ -69,82 +73,109 @@ Teknisyenin işi "bilgisayarcılık" değil "bayi cihazını çalışır teslim
|
|||
|
||||
> Göreceğin: Ubuntu kurulum ekranı. Bunu görmüyorsan: USB tam takılı mı, BIOS boot sırası USB mi? Olmazsa merkeze telefon aç.
|
||||
|
||||
**ADIM 2 — Ubuntu'yu kur.**
|
||||
**ADIM 2 — Ubuntu'yu kur ve depo kopyasını getir.**
|
||||
|
||||
1. Ekrandaki kurulumu varsayılanlarla tamamla (dil: Türkçe, disk: tamamını kullan).
|
||||
2. Kurulum bitince USB'yi çıkarma, bilgisayarı yeniden başlat.
|
||||
3. Repo kopyasını cihaza getir (merkezin söylediği yolla: `git clone` — paketler Git LFS ile birlikte gelir — veya hazır kopya `/opt/blueforce/offline-repo` altına konur).
|
||||
|
||||
**ADIM 3 — Kurucu scripti çalıştır, bayi numarasını gir.**
|
||||
> Paketler repoyla birlikte gelir; **internetten hiçbir şey indirilmez**.
|
||||
|
||||
**ADIM 3 — Grafik arayüzü aç, bayi numarasını gir, kurulumu başlat.**
|
||||
|
||||
1. Terminali aç, şunu yaz:
|
||||
|
||||
```bash
|
||||
sudo ./blueforce-install.sh
|
||||
sudo bf
|
||||
```
|
||||
|
||||
2. Script senden **8 haneli bayi numarasını** ister. Faturadaki/sistemdeki numarayı yaz, Enter'a bas. Örnek: `12010193`.
|
||||
3. Script ekranda şunu gösterecek:
|
||||
2. Pencere açılır: başlık **Blueforce Field OS — Yönetim**. (Ekran yoksa veya tkinter yoksa uygulama Türkçe uyarı verir; o zaman `sudo bf --tui` yaz — §D.)
|
||||
3. Üst çubuktaki **Hedef > Bayi no (8 hane)** alanına **8 haneli bayi numarasını** yaz. Faturadaki/sistemdeki numarayı kullan. Örnek: `12010193`.
|
||||
4. **Kurulum** sekmesi → **`Bu cihazı kur`**.
|
||||
5. Arayüz komutu ekranda gösterir ve onay ister. `BF-12010193` görünüyor mu? Doğruysa onayla; yanlışsa iptal et ve numarayı düzelt.
|
||||
|
||||
```text
|
||||
Device ID: BF-12010193
|
||||
Hostname: bf-12010193
|
||||
Arayüzden: sudo bf → Hedef: Bayi no (8 hane) → Kurulum → "Bu cihazı kur"
|
||||
Komutla (yedek):
|
||||
sudo bf --tui # terminal konsolu; Kurulum menüsünden aynı akış
|
||||
sudo ./scripts/install/blueforce-install.sh --dealer-id 12010193
|
||||
```
|
||||
|
||||
4. Numarayı bayiye sesli onaylat: "Cihaz numaranız BF-12010193, doğru mu?" Yanlışsa scriptten çık, tekrar çalıştır.
|
||||
6. Numarayı bayiye sesli onaylat: "Cihaz numaranız BF-12010193, doğru mu?"
|
||||
|
||||
**ADIM 4 — READY'yi gör, çık.**
|
||||
**ADIM 4 — Sonucu gör, çık.**
|
||||
|
||||
1. Script bitene kadar bekle (kapatma, fişi çekme).
|
||||
2. Sonunda şunu çalıştır:
|
||||
1. Kurulum bitene kadar bekle (kapatma, fişi çekme). Cihazın açık kalması şart.
|
||||
2. Sonucu gör:
|
||||
|
||||
```bash
|
||||
bf-status
|
||||
```text
|
||||
Arayüzden: `Durum` düğmesi (basit mod) → Durum özeti ya da Kurulum sekmesi → "Kurulum modül durumu"
|
||||
Komutla (yedek): bf-status
|
||||
```
|
||||
|
||||
3. Offline medya ile kurulumda önce **PROVISIONED_OFFLINE** görmen normaldir; bu durumda cihaz READY değildir. İnternet geldiğinde merkezden tek-kullanımlık enrollment token iste ve 29'daki adımı uygula.
|
||||
4. Token sonrası **ENROLLED** görünür; WireGuard, remote ve monitoring merkezi doğrulaması bitince **READY** görmelisin.
|
||||
5. READY sonrası kontrol listesini doldur (§12), sahadan çık.
|
||||
3. **`PROVISIONED_OFFLINE`** görmen normaldir: kurulum yerelde tamamlandı, cihaz internetsiz kuruldu. Bu durumda cihaz `READY` değildir.
|
||||
4. İnternet geldiğinde merkezden tek-kullanımlık enrollment token iste ve 29'daki adımı uygula.
|
||||
5. Token sonrası **ENROLLED** görünür; WireGuard, remote ve monitoring merkezi doğrulaması bitince **READY** görürsün.
|
||||
6. READY sonrası kontrol listesini doldur (§12), sahadan çık.
|
||||
|
||||
### B. Arızalı cihaza gittin — ilk 3 hareket
|
||||
|
||||
1. `bf-status` çalıştır. Kırmızı satır hangisi? Not al.
|
||||
2. Bilgisayarı bir kez yeniden başlat. Düzelirse READY'yi gör, çık.
|
||||
3. Düzelmezse support paketini al, merkeze gönder:
|
||||
1. Durumu oku:
|
||||
|
||||
```bash
|
||||
bf-support-bundle --out /tmp/bf-<bayi-no>-bundle.tar.gz
|
||||
```text
|
||||
Arayüzden: sudo bf → `Durum` düğmesi (Durum özeti)
|
||||
Komutla (yedek): bf-status
|
||||
```
|
||||
|
||||
Kırmızı satır hangisi? Not al.
|
||||
2. Bilgisayarı bir kez yeniden başlat. Düzelirse READY'yi gör, çık.
|
||||
3. Düzelmezse destek paketini al, merkeze gönder:
|
||||
|
||||
```text
|
||||
Arayüzden: sudo bf → Detaylı mod → Araçlar sekmesi → "Destek paketi oluştur"
|
||||
Komutla (yedek): bf-support-bundle --out /tmp/bf-<bayi-no>-bundle.tar.gz
|
||||
```
|
||||
|
||||
Paketi merkeze ilet (telefonla nasıl göndereceğin söylenir), merkez sana söylemeden başka komut çalıştırma.
|
||||
|
||||
### C. Merkeze telefon açma kuralları
|
||||
|
||||
Hemen ara: READY alamıyorsan, aynı arıza 2 cihazda üst üste çıktıysa, donanım kırık/yanık kokusu varsa, bayi numarasından emin değilsen.
|
||||
Arama: cihaz başında ol, `BF-<no>`'yu ve `bf-status` çıktısını hazır tut.
|
||||
Hemen ara: sonucu alamıyorsan, aynı arıza 2 cihazda üst üste çıktıysa, donanım kırık/yanık kokusu varsa, bayi numarasından emin değilsen.
|
||||
Arama: cihaz başında ol, `BF-<no>`'yu ve durum özetini/`bf-status` çıktısını hazır tut.
|
||||
|
||||
### D. Grafik arayüz açılmıyor — yedek yol
|
||||
|
||||
| Belirti | Ne yap |
|
||||
|---|---|
|
||||
| "Grafik ekran bulunamadı" / SSH oturumu | `sudo bf --tui` yaz; aynı işlemler terminal menüsünde numaralarla yürür |
|
||||
| "tkinter bulunamadı" | `sudo bf --tui` ile devam et ve merkeze bildir; `python3-tk` paketi kurulum depounda pinlidir (19 §T-10) |
|
||||
| Pencere açıldı ama dondu | Pencereyi kapat, `sudo bf --tui` ile devam et, merkeze bildir |
|
||||
|
||||
## 10. Test Planı
|
||||
|
||||
| Test | Beklenen sonuç | Ortam |
|
||||
|---|---|---|
|
||||
| Sıfır teknisyen 4 adımı desteksiz yapar | READY < hedef süre | LAB(2) |
|
||||
| Yanlış bayi no girilir | Script uyarır, kuruluma devam etmez | LAB(2) |
|
||||
| Elektrik kesintisi simülasyonu sonrası `bf-status` | READY döner | LAB(2) |
|
||||
| Sıfır teknisyen 4 adımı desteksiz yapar | Hedef teslim durumu < hedef süre | LAB(2) |
|
||||
| Yanlış bayi no girilir | Uygulama uyarır, komut üretilmez/çalışmaz | LAB(2) |
|
||||
| Ekransız oturumda `sudo bf` | Türkçe yönlendirme + `sudo bf --tui` önerisi, exit 3 | LAB(2) |
|
||||
| Depo kopyası olmayan cihazda kurulum | Türkçe hata + aranan yollar + çıkış nedeni, sistem değişmez | LAB(2) |
|
||||
| Elektrik kesintisi simülasyonu sonrası durum | Kurulum kaldığı yerden tamamlanır; durum doğru raporlanır | LAB(2) |
|
||||
|
||||
## 11. Rollback
|
||||
|
||||
Teknisyen rollback yapmaz. Kurulum yarıda kesildiyse: baştan başla (ADIM 1). İkinci denemede de olmazsa merkeze telefon aç — merkez 17-RECOVERY'den seviyeyi seçer.
|
||||
Teknisyen rollback yapmaz. Kurulum yarıda kesildiyse: `sudo bf` → Kurulum sekmesi → **`Başarısız kuruluma devam et (--resume)`** (komutla yedek: aynı kurucu `--resume` bayrağıyla). İkinci denemede de olmazsa merkeze telefon aç — merkez 17-RECOVERY'den seviyeyi seçer.
|
||||
|
||||
## 12. Kontrol Listesi
|
||||
|
||||
- [ ] USB sürümü güncel (etiket + merkez duyurusu eşleşiyor).
|
||||
- [ ] Depo kopyası/checkout sürümü güncel (etiket + merkez duyurusu eşleşiyor).
|
||||
- [ ] `BF-<no>` bayiye onaylatıldı.
|
||||
- [ ] `bf-status` → READY görüldü.
|
||||
- [ ] Kurulum sonucu görüldü (`PROVISIONED_OFFLINE` normaldir; READY için §A/ADIM 4-5).
|
||||
- [ ] Cihaz fişe + internete bağlı bırakıldı.
|
||||
- [ ] İş formu dolduruldu (bayi no, tarih, teknisyen adı).
|
||||
|
||||
## 13. Açık Sorular
|
||||
|
||||
- [ ] Kurulum USB'sinin dağıtım/sürüm takip yöntemi (sahibi: merkezi admin, 21).
|
||||
- [ ] Depo kopyasının sahaya taşınma/sürüm takip yöntemi (git clone + LFS mı, hazır kopya mı) (sahibi: merkezi admin, 21).
|
||||
- [ ] İş formu kağıt mı mobil mi (sahibi: 25-ROADMAP).
|
||||
- [ ] Hedef kurulum süresi (öneri: P2'de ölçülecek).
|
||||
|
||||
|
|
@ -155,11 +186,15 @@ Teknisyen rollback yapmaz. Kurulum yarıda kesildiyse: baştan başla (ADIM 1).
|
|||
```mermaid
|
||||
flowchart TB
|
||||
USB["USB tak → aç"] --> KUR["Ubuntu kur"]
|
||||
KUR --> SCR["sudo ./blueforce-install.sh"]
|
||||
SCR --> NO["Bayi no gir<br/>(8 hane)"]
|
||||
NO --> ONAY["BF-no'yu bayiye onaylat"]
|
||||
ONAY --> BEKLE["Script bitsin"]
|
||||
BEKLE --> READY["bf-status → READY?"]
|
||||
READY -->|evet| CIK["Formu doldur → çık"]
|
||||
READY -->|hayır| TEL["Merkeze telefon aç"]
|
||||
KUR --> DEPO["Depo kopyasını getir<br/>(paketler LFS ile gelir, indirme yok)"]
|
||||
DEPO --> BF["sudo bf"]
|
||||
BF --> EKRAN{"Grafik ekran +<br/>python3-tk var mı?"}
|
||||
EKRAN -->|Hayır| TUI["sudo bf --tui<br/>(yedek terminal konsolu)"]
|
||||
EKRAN -->|Evet| NO["Hedef: Bayi no (8 hane)"]
|
||||
TUI --> NO
|
||||
NO --> ONAY["Arayüz komutu gösterir + onay<br/>BF-no'yu bayiye onaylat"]
|
||||
ONAY --> BEKLE["Kurulum bitsin<br/>(kapatma / fişi çekme)"]
|
||||
BEKLE --> DURUM["Durum: PROVISIONED_OFFLINE / ENROLLED / READY"]
|
||||
DURUM -->|"READY"| CIK["Formu doldur → çık"]
|
||||
DURUM -->|"sorun var"| TEL["Destek paketi + merkeze telefon aç"]
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
# 21 — Merkezi Yönetici Runbook
|
||||
|
||||
> Kısa özet: Merkezdeki yöneticinin el kitabı: hangi işlemi kim onaylar, update'i kim başlatır, dalga kapıları nasıl açılır, teknisyen ne zaman sahaya sürülür. Yetki ve sıra bu dosyadadır.
|
||||
> Kısa özet: Merkezdeki yöneticinin el kitabı: hangi işlemi kim onaylar, update'i kim başlatır, dalga kapıları nasıl açılır, teknisyen ne zaman sahaya sürülür. Yetki ve sıra bu dosyadadır. Filo işlemleri grafik arayüzden (`sudo bf` → `Cihaz işlemleri`) yürütülür; komut satırı karşılıkları yedek/otomasyon yoludur.
|
||||
|
||||
- Dosya: `docs/21-CENTRAL-ADMIN-RUNBOOK.md`
|
||||
- İlgili kararlar: `24-DECISION-LOG.md#K-06` (Ansible/Semaphore), `#K-11` (onay zinciri + dalgalar), `#K-13` (anahtar dağıtımı)
|
||||
- İlgili dokümanlar: `33-OPERATOR-CONSOLE.md` (arayüz rehberi), `10-UPDATE-AND-ROLLBACK-POLICY.md` (dalga politikası), `20-FIELD-TECHNICIAN-RUNBOOK.md` (saha)
|
||||
- Durum: [ ] Taslak
|
||||
|
||||
---
|
||||
|
||||
## 1. Amaç
|
||||
|
||||
Merkezde "kim, neyi, hangi sırayla yapar" sorusunu kapatmak. Update'i kim başlatır, dalgayı kim onaylar, acil durumda kim karar verir — hepsi burada yazar, kriz anında aranmaz.
|
||||
Merkezde "kim, neyi, hangi sırayla yapar" sorusunu kapatmak. Update'i kim başlatır, dalgayı kim onaylar, acil durumda kim karar verir — hepsi burada yazar, kriz anında aranmaz. İşlemler tek arayüzde toplanır: `sudo bf` (grafik) veya `sudo bf --tui` (yedek terminal konsolu).
|
||||
|
||||
## 2. Kapsam
|
||||
|
||||
- Kapsam içi: rol ve onay yetkileri, update başlatma akışı, dalga kapı prosedürü, anahtar/erişim yönetimi, acil durum yetkisi, günlük/haftalık rutinler.
|
||||
- Kapsam dışı: tek cihaz kurulum adımları (20), komut-seviyesi teşhis (19), kurtarma seviye tablosu (17 — buradan çağrılır).
|
||||
- Kapsam içi: rol ve onay yetkileri, update başlatma akışı, dalga kapı prosedürü, arayüz üzerinden filo işlemi yürütme, anahtar/erişim yönetimi, acil durum yetkisi, günlük/haftalık rutinler.
|
||||
- Kapsam dışı: tek cihaz kurulum adımları (20), komut-seviyesi teşhis (19), kurtarma seviye tablosu (17 — buradan çağrılır), merkez sunucusunun kurulumu (arayüzde yalnız "Merkez hazırlığı" denetimleri vardır).
|
||||
|
||||
## 3. Kararlar
|
||||
|
||||
```text
|
||||
KARAR: Update'i yalnızca merkezi yönetici başlatır; teknisyen, bayi veya otomatik zamanlayıcı update tetikleyemez. Her dalga geçişi açık onay (Semaphore kapı adımı + kayıt) gerektirir.
|
||||
GEREKÇE: K-11 "onaysız update yasak" ilkesinin operasyonel karşılığıdır; tek başlatma noktası = denetlenebilirlik. Otomatik tetikleyici olmaması, zamanlanmış sürprizleri engeller.
|
||||
KARAR: Update'i yalnızca merkezi yönetici başlatır; teknisyen, bayi veya otomatik zamanlayıcı update tetikleyemez. Her dalga geçişi açık onay gerektirir: arayüzden çalıştırıldığında "production" yazma adımı + production ek onayı, gündelik/sürüm kapsamlı işlemlerde kapı sözcüğü; Semaphore iş akışı kullanılıyorsa kapı adımı Semaphore'dadır.
|
||||
GEREKÇE: K-11 "onaysız update yasak" ilkesinin operasyonel karşılığıdır; tek başlatma noktası = denetlenebilirlik. Otomatik tetikleyici olmaması, zamanlanmış sürprizleri engeller. Onay olmadan komut üretilmez: arayüz komutu önce gösterir, kapı adımını geçmeyen çağrıda bayrağı kendiliğinden eklemez.
|
||||
ALTERNATİF: Takvimli otomatik dalga başlatma — onay kaydı zayıflar, arızada "kim başlattı" belirsizleşir; elendi.
|
||||
RİSK: Yönetici izindeyken update kilitlenir; azaltma: en az 2 onay yetkilisi tanımlanır (asıl + vekil).
|
||||
MALİYET: Ücretsiz.
|
||||
|
|
@ -39,7 +40,7 @@ LİSANS: Yok.
|
|||
|
||||
## 4. Neden Bu Karar?
|
||||
|
||||
700 cihazda en tehlikeli cümle "ben başlattım sandım"dır. Başlatma yetkisini tek role toplamak, hem hatayı hem suçu önler: hata olursa kayıttan bulunur, suçlama olursa kayıt konuşur. Vekil kuralı, tek kişiye bağımlılığı kaldırır.
|
||||
700 cihazda en tehlikeli cümle "ben başlattım sandım"dır. Başlatma yetkisini tek role toplamak, hem hatayı hem suçu önler: hata olursa kayıttan bulunur, suçlama olursa kayıt konuşur. Vekil kuralı, tek kişiye bağımlılığı kaldırır. Arayüz bu disiplini görünür kılar: dalga kapsamlı her işlemde komut, bayraklarıyla birlikte ekranda gösterilir, production dalgası için `production` yazılmadan komut çalışmaz; her karar `/var/log/blueforce-console.log` (mod `600`) dosyasına düşer.
|
||||
|
||||
## 5. Alternatifler
|
||||
|
||||
|
|
@ -48,24 +49,28 @@ LİSANS: Yok.
|
|||
| Herkes update başlatabilir | Hızlı | Onaysız yayılım, denetimsizlik | Elendi |
|
||||
| Tam otomatik pipeline | Eforsuz | Politika ihlali (K-11) | Elendi |
|
||||
| Tek onay yetkilisi | Net | İzin/hastalıkta kilit | Elendi (asıl+vekil) |
|
||||
| Yalnız serbest `ansible-playbook` komutu | Esnek | Kapı bayrakları elle yazılır, atlanabilir | Elendi (arayüz kapıyı bayrakla bağlar) |
|
||||
|
||||
## 6. Avantajlar
|
||||
|
||||
- Her update'in "kim başlattı, kim onayladı, ne zaman" kaydı vardır.
|
||||
- Her update'in "kim başlattı, kim onayladı, ne zaman" kaydı vardır (konsol denetim günlüğü).
|
||||
- Acil durumda bile sıra korunur; hız, sıranın içinde ayarlanır.
|
||||
- Hedef seçimi tek yerden yapılır: tek cihaz (bayi no), dalga grubu (lab, pilot_1, pilot_2, wave_1, wave_2, production) veya tüm filo.
|
||||
|
||||
## 7. Dezavantajlar
|
||||
|
||||
- Onay adımı update'i yavaşlatır (bilerek; hız 25-ROADMAP'ta acil yoluyla dengelenir).
|
||||
- Semaphore kapı adımı atlanırsa politika kağıtta kalır — teknik zorunluluk şarttır.
|
||||
- Kapı adımı atlanırsa (serbest CLI ile run) politika kağıtta kalır — teknik zorunluluk şarttır.
|
||||
- Arayüz grafik katman gerektirir; merkez sunucusunda yoksa `sudo bf --tui` kullanılır.
|
||||
|
||||
## 8. Riskler
|
||||
|
||||
| Risk | Olasılık | Etki | Azaltma |
|
||||
|---|---|---|---|
|
||||
| Kapı adımı bypass edilir (CLI'dan direkt run) | Orta | Yüksek | Semaphore dışı prod run'ı yasaktır; denetim logu izlenir |
|
||||
| Kapı adımı bypass edilir (arayüz dışı CLI run) | Orta | Yüksek | Semaphore/arayüz dışı prod run yasaktır; denetim günlüğü izlenir |
|
||||
| İki yetkili de ulaşılamaz | Düşük | Orta | Acil iletişim zinciri + önceden tanımlı duraklatma (devam etme, durdur) |
|
||||
| Anahtar sızıntısı | Düşük | Yüksek | Per-admin anahtar iptali + rotasyon (K-13) |
|
||||
| Yanlış hedefe işlem (grup/tüm filo) | Orta | Yüksek | Hedef arayüzde tek tek seçilir; `--limit` komutta görünür, onay istenir |
|
||||
|
||||
## 9. Uygulama Planı
|
||||
|
||||
|
|
@ -81,16 +86,34 @@ LİSANS: Yok.
|
|||
### Standart update akışı (yönetici gözüyle)
|
||||
|
||||
1. Update talebi gelir (güvenlik bülteni / MEG sürümü / OS yaması) → etki + geri alma planı raporlanır.
|
||||
2. Onay yetkilisi LAB'ı başlatır: Semaphore'da job + kayıt.
|
||||
2. Onay yetkilisi LAB'ı başlatır: Semaphore'da job + kayıt (veya arayüzden dalga işlemi).
|
||||
3. LAB çıkış kriteri tuttuysa P1 → P2 → W1 → W2 → PROD, her kapıda açık onay.
|
||||
4. Herhangi bir dalgada halt kuralı (18) tetiklenirse: yayılım DURUR, 17-L3 işletilir, kök neden bulunmadan kapı açılmaz.
|
||||
5. PROD bitiminde kapanış kaydı (başarı/arıza sayısı, süre).
|
||||
|
||||
```bash
|
||||
# örnek: onaylı dalga run (operatör, onay sonrası)
|
||||
ansible-playbook playbooks/update.yml --limit wave_p1
|
||||
# örnek: kapı kontrolü — onaysız PROD engeli (ilke; teknik karşılığı Semaphore'da)
|
||||
# PROD limiti yalnızca "prod-onay" etiketli job ile koşar
|
||||
```text
|
||||
Arayüzden (merkez operatörü):
|
||||
sudo bf → Detaylı mod → Cihaz işlemleri sekmesi
|
||||
→ işlemi seç (örn. "Kontrollü yeniden başlatma" / "Onaylı paket güncellemesi")
|
||||
→ Hedef: dalga grubu (lab, pilot_1, pilot_2, wave_1, wave_2, production)
|
||||
→ kapı adımı: production için 'production' yazmak ZORUNLU
|
||||
→ komut ekranda gösterilir → onay → çalışır
|
||||
|
||||
Komutla (yedek / otomasyon):
|
||||
ansible-playbook -i ansible/inventory/... ansible/playbooks/bf-reboot.yml \
|
||||
--limit wave_p1 -e operation_wave=wave_1 -e operation_confirmed=true
|
||||
# production_override / allow_production_reboot yalnız production ek onayıyla eklenir.
|
||||
# Kapı kuralı: önceki dalga sağlık kapısını geçmediyse çalıştırılmaz (10).
|
||||
```
|
||||
|
||||
Planlama/denetim komutlarının arayüz karşılıkları:
|
||||
|
||||
```text
|
||||
Arayüzden: sudo bf → Merkez hazırlığı sekmesi
|
||||
(envanter hedefleri ve dalga/cihaz sayıları, PLACEHOLDER denetimi,
|
||||
merkez uç noktaları, hazırlık kontrol listesi — hepsi salt-okunur)
|
||||
Komutla (yedek): sudo bf --tui → Merkez hazırlığı menüsü
|
||||
admin/bf-menu --list-targets # yedek bash menüsü
|
||||
```
|
||||
|
||||
### Anahtar ve erişim yönetimi
|
||||
|
|
@ -101,34 +124,39 @@ ansible-playbook playbooks/update.yml --limit wave_p1
|
|||
|
||||
### Günlük / haftalık rutinler
|
||||
|
||||
- Günlük (operatör, 10 dk): Uptime Kuma UP/DOWN, Prometheus `up` == 0 listesi, gece reboot olanlar, yedek job durumu.
|
||||
- Günlük (operatör, 10 dk): Uptime Kuma UP/DOWN, Prometheus `up` == 0 listesi, gece reboot olanlar, yedek job durumu. Cihaz durumu için arayüzden `Durum` düğmesi ya da `Cihaz işlemleri > Kurulum fazı raporu`.
|
||||
- Haftalık (yönetici, 30 dk): dalga takvimi, açık halt'lar, anahtar/envanter uyumsuzlukları, `PROVISIONED_OFFLINE`/`ENROLLED` bekleyen cihaz kuyruğu, release manifest/rollback kanıtı ve L9 tatbikat sayacı (27/29/30).
|
||||
|
||||
## 10. Test Planı
|
||||
|
||||
| Test | Beklenen sonuç | Ortam |
|
||||
|---|---|---|
|
||||
| Onaysız PROD run denemesi | Engellenir + log düşer | Merkez test |
|
||||
| Onaysız PROD run denemesi | Engellenir (`production` yazılmadan komut çalışmaz) + denetim günlüğüne yazılır | Merkez test |
|
||||
| Semaphore kapalıyken Ansible CLI run | Onaylı iş CLI'dan koşar (K-06) | LAB(2) |
|
||||
| Vekil ile dalga onayı | Kayıtta vekil adı görünür | Merkez test |
|
||||
| Arayüzden dalga hedefi seçimi | Komutta doğru `--limit` görünür, yanlış hedefte komut üretilmez | Merkez test |
|
||||
| Grafik arayüz yokken yönetim işlemleri | `sudo bf --tui` ile aynı işlemler yürütülür | LAB(2) SSH |
|
||||
|
||||
## 11. Rollback
|
||||
|
||||
1. Hatalı dalga: 17-L3 + 10-UPDATE rollback zinciri; kapı bir sonraki dalgaya kapatılır.
|
||||
2. Hatalı onay: aynı yetkili (veya vekil) durdurma kararı verir; "devam" kararı verilemez, yalnızca "dur + geri al".
|
||||
3. Arayüzden başlatılan yanlış kapsamlı işlem: komut ekranda ve denetim günlüğünde hedefiyle kayıtlıdır; kapsam bu kayıttan doğrulanıp geri alma adımı planlanır.
|
||||
|
||||
## 12. Kontrol Listesi
|
||||
|
||||
- [ ] Asıl + vekil isimle tanımlı ve iletişim bilgileri güncel.
|
||||
- [ ] Semaphore'da PROD kapı adımı teknik olarak zorunlu.
|
||||
- [ ] Her dalga onayı kayıtlı (kim, ne zaman, hangi sürüm).
|
||||
- [ ] Semaphore'da PROD kapı adımı teknik olarak zorunlu; arayüzde production için 'production' yazma adımı çalışıyor.
|
||||
- [ ] Her dalga onayı kayıtlı (kim, ne zaman, hangi sürüm) — `/var/log/blueforce-console.log` mod `600`.
|
||||
- [ ] Ayrılan personelin anahtarı kaldırıldı.
|
||||
- [ ] Merkez operatörleri grafik arayüzü (`sudo bf`) ve yedek terminal konsolunu (`sudo bf --tui`) biliyor.
|
||||
|
||||
## 13. Açık Sorular
|
||||
|
||||
- [ ] Acil tanımı eşikleri (CVSS/aktif sömürü) — 10-UPDATE ile ortak onay (sahibi: yönetici).
|
||||
- [ ] Kapanış raporu formatı ve saklama süresi (sahibi: 25-ROADMAP).
|
||||
- [ ] Read-only yönetim panosu kapsamı (sahibi: 14-MONITORING yazarı).
|
||||
- [ ] Arayüzün merkez sunucusunda (`sudo bf --tui` mi, grafik mi) varsayılan kullanımı (sahibi: 33 + merkez ekibi).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -139,7 +167,10 @@ flowchart TB
|
|||
TALEP["Update talebi<br/>(güvenlik / MEG / OS)"] --> KAYIT["Değişiklik kaydı<br/>kapsam + geri alma planı"]
|
||||
KAYIT --> ONAY{"Onay yetkilisi<br/>(asıl veya vekil)"}
|
||||
ONAY -->|Ret| KAPANIR["Kapanış kaydı (ret)"]
|
||||
ONAY -->|Onay| LAB["LAB(2) → P1(5) → P2(20)"]
|
||||
ONAY -->|Onay| UI["sudo bf → Cihaz işlemleri<br/>hedef: dalga grubu"]
|
||||
UI --> KAPI0{"Kapı adımı<br/>production ise 'production' yaz"}
|
||||
KAPI0 -->|Geçmedi| DUR0["Komut çalışmaz<br/>(denetim günlüğü: CANCEL)"]
|
||||
KAPI0 -->|Geçti| LAB["LAB(2) → P1(5) → P2(20)"]
|
||||
LAB --> KAPI{"Sağlık kapısı temiz mi?"}
|
||||
KAPI -->|Halt| DUR["Yayılım DURUR → 17-L3 rollback"]
|
||||
KAPI -->|Temiz| WAVE["W1(50) → W2(100) → PROD(~523)"]
|
||||
|
|
|
|||
|
|
@ -1,83 +1,102 @@
|
|||
# 27 — Offline Provisioning
|
||||
|
||||
> Kısa özet: İnternet yokken Field OS kurulumu ve yerel paket yüklemesi yapılabilir. Bu yol yalnız `PROVISIONED_OFFLINE` durumuna getirir; cihaz, tek kullanımlık enrollment token ve merkezi kanallar doğrulanmadan `ENROLLED` veya `READY` olmaz.
|
||||
> Kısa özet: Paketler **git deposunun içindedir** (`provisioning/offline-repo/packages/`, 1321 `.deb`, ~888 MB, Git LFS ile takip edilir). Saha kurulumu internet **gerektirmez** ve kuran kişi **hiçbir şey indirmez**: `sudo bf` → Kurulum sekmesi → "Bu cihazı kur" → 8 haneli bayi no → onay. Bu yol yalnız `PROVISIONED_OFFLINE` durumuna getirir; cihaz, tek kullanımlık enrollment token ve merkezi kanallar doğrulanmadan `ENROLLED` veya `READY` olmaz.
|
||||
>
|
||||
> - Dosya: `docs/27-OFFLINE-PROVISIONING.md`
|
||||
>
|
||||
> **Uygulama sözleşmesi:** Kurucu, paket setini **kendi kendine bulur** ve varsayılan olarak yerel depodan kurar; `--offline` bayrağı artık **gerekmez** (geriye uyumluluk için kabul edilir, yalnız yerel depoyu zorlar). Arama sırası: (1) `BF_OFFLINE_REPO` ortam değişkeni, (2) `<depo>/provisioning/offline-repo/built`, (3) `/opt/blueforce/offline-repo`; ilk eksiksiz aday kazanır. Yerel `file:` APT kaynağı dışında hiçbir kaynak kullanılmaz: uzak APT source, curl, wget, anahtar indirme veya `apt download` çağrılmaz. **İnternet kurulum için gerekmez.** İki farklı başarısızlık vardır: depo hiç yoksa kurulum Türkçe bir hata ve `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.` ile durur; depo bulunup da doğrulanamazsa (eksik `Packages`, `Packages.gz`, `packages.lock.tsv`, `SHA256SUMS`; checksum uyuşmazlığı; boş/bozuk kilit; eksik pin) `OFFLINE BLOCKED: <sebep>` ile durur. Her iki durumda da paket modüllerinden önce durulur, kurulum başlamaz ve sistemde hiçbir değişiklik yapılmaz. Paketler bu git deposunda taşınır ve **repo sorumlusu** tarafından `add-packages.sh` + `build-index.sh` ile bu depoya konur; kuran kişi hiçbir şey indirmez. Yalnız geliştirme/test için `--online` vardır ve **üretim cihazlarında kullanılmaz**.
|
||||
> **Uygulama sözleşmesi:** Kurucu, paket setini **kendi kendine bulur** ve varsayılan olarak yerel depodan kurar; `--offline` bayrağı artık **gerekmez** (geriye uyumluluk için kabul edilir, yalnız yerel depoyu zorlar). Arama sırası: (1) `BF_OFFLINE_REPO` ortam değişkeni, (2) `<depo>/provisioning/offline-repo/built`, (3) `/opt/blueforce/offline-repo`; ilk eksiksiz aday kazanır. Yerel `file:` APT kaynağı dışında hiçbir kaynak kullanılmaz: uzak APT source, curl, wget, anahtar indirme veya `apt download` çağrılmaz. **İnternet kurulum için gerekmez.** İki farklı başarısızlık vardır: depo hiç yoksa kurulum Türkçe bir hata, aranan yollar ve `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.` ile durur; depo bulunup da doğrulanamazsa (eksik `Packages`, `Packages.gz`, `packages.lock.tsv`, `SHA256SUMS`; checksum uyuşmazlığı; boş/bozuk kilit; eksik pin) `OFFLINE BLOCKED: <sebep>` ile durur. Her iki durumda da paket modüllerinden önce durulur, kurulum başlamaz ve sistemde hiçbir değişiklik yapılmaz. Paketler bu git deposunda taşınır ve **repo sorumlusu** tarafından `add-packages.sh` + `build-index.sh` ile bu depoya konur; kuran kişi hiçbir şey indirmez. Yalnız geliştirme/test için `--online` vardır ve **üretim cihazlarında kullanılmaz**. Grafik arayüz `python3-tk` paketine bağlıdır; bu paket offline depoda bilinçli olarak pinlidir (`manifests/base-packages.txt`).
|
||||
> - İlgili kararlar: `24-DECISION-LOG.md#K-16`, `#K-09`, `#K-11`, `#K-12`
|
||||
> - İlgili dokümanlar: `05-ONE-CLICK-INSTALLER.md` (kurucu), `33-OPERATOR-CONSOLE.md` (arayüz), `provisioning/offline-repo/README.md` (paket hazırlama)
|
||||
> - Durum: [ ] Taslak
|
||||
|
||||
---
|
||||
|
||||
## 1. Amaç
|
||||
|
||||
Saha interneti kesik veya güvenilmezken tekrarlanabilir, denetlenebilir ve secretsiz ilk kurulumu tanımlamak.
|
||||
Saha interneti kesik veya güvenilmezken tekrarlanabilir, denetlenebilir ve secretsiz ilk kurulumu tanımlamak. Kuran kişi paket aramaz, indirmez, bayrak ezberlemez: paket seti depoyla birlikte gelir.
|
||||
|
||||
## 2. Kapsam
|
||||
|
||||
- Kapsam içi: seed USB veya git checkout, offline APT repository/snapshot, kurucunun yerel depoyu otomatik bulması, durum damgası ve sonradan enrollment.
|
||||
- Kapsam dışı: merkezi peer atama ve token doğrulama (29), release imzalama (30).
|
||||
- Kapsam içi: git checkout (paketler dahil) veya kurulmuş `/opt/blueforce/offline-repo`, offline APT bundle (`built/`), kurucunun yerel depoyu otomatik bulması, iki hata durumu, grafik arayüz üzerinden kurulum, `python3-tk` pini, durum damgası ve sonradan enrollment.
|
||||
- Kapsam dışı: merkezi peer atama ve token doğrulama (29), release imzalama (30), paket setinin nasıl üretildiği (repo sorumlusunun işi, `provisioning/offline-repo/README.md`).
|
||||
|
||||
## 3. Kararlar
|
||||
|
||||
```text
|
||||
KARAR: Offline kurulum, imzalı/sürümlü medya ya da git checkout ve ayrı offline APT kaynağıyla yapılır; paket seti kurucu tarafından otomatik bulunur ve sonuç durumu yalnızca PROVISIONED_OFFLINE'dır.
|
||||
GEREKÇE: Paket indirme veya merkezi API'ye erişim yokken cihaz standarda getirilebilir; fakat merkezde doğrulanmamış kimlik ve kanallar READY iddiası yapamaz. Operatörün hatırlaması gereken bayrak sayısı azaldıkça saha hatası azalır: yerel depo varsayılan kaynaktır.
|
||||
KARAR: Paket seti git deposunda taşınır ve cihaz grafik arayüzden (`sudo bf` → Kurulum → "Bu cihazı kur") internetsiz kurulur; paket deposu kurucu tarafından otomatik bulunur ve sonuç durumu yalnızca PROVISIONED_OFFLINE'dır.
|
||||
GEREKÇE: Paket indirme veya merkezi API'ye erişim yokken cihaz standarda getirilebilir; fakat merkezde doğrulanmamış kimlik ve kanallar READY iddiası yapamaz. Kuran kişinin indirme yapmaması ve bayrak hatırlamaması saha hatasını ortadan kaldırır: yerel depo varsayılan ve tek kaynaktır.
|
||||
ALTERNATİF: İnternet gelene kadar kurulum yapmama — saha ziyareti ve bekleme maliyeti nedeniyle elendi. Kurulumu `--offline` bayrağına bağlamak — operatör bayrağı unuttuğunda kurulum durur; otomatik keşif seçildi, bayrak geriye uyumluluk için korundu.
|
||||
RİSK: Eski veya karışık paket snapshot'ı; manifest, checksum ve release kimliği ile azaltılır. Yanlış/eski depo bulunması; arama sırası ve her adayda eksiksizlik denetimi ile azaltılır.
|
||||
MALİYET: Ücretsiz.
|
||||
RİSK: Eski veya karışık paket snapshot'ı; manifest, checksum ve release kimliği ile azaltılır. Yanlış/eski depo bulunması; arama sırası ve her adayda eksiksizlik denetimi ile azaltılır. Grafik arayüzün `python3-tk` paketine bağımlılığı; paketin offline depoda pinlenmesi ve eksikliğinde `sudo bf --tui` yedeği ile azaltılır.
|
||||
MALİYET: Ücretsiz (depo boyutu git tarafında ~888 MB).
|
||||
LİSANS: APT — https://manpages.ubuntu.com/manpages/noble/en/man8/apt.8.html ; dpkg — https://manpages.ubuntu.com/manpages/noble/en/man1/dpkg.1.html ; Ubuntu offline kurulum ayrıntısı 26.04 için LAB doğrulamalıdır.
|
||||
```
|
||||
|
||||
## 4. Neden Bu Karar?
|
||||
|
||||
Offline medya ve git deposu, işletim sistemi ve zorunlu paketleri taşır; merkezi sırları taşımamalıdır. Durum makinesi, "kuruldu" ile "merkezce güvenildi" arasındaki farkı görünür yapar. Paket setini **repo sorumlusu** hazırlayıp sürümlediği için sahada "hangi paket nereden geldi?" sorusu `packages.lock.tsv` ve `repo-manifest.yaml` ile yanıtlanır; kuran kişi paket aramaz ve indirmez.
|
||||
**Paketler artık ayrı bir medyada değil, bu git deposunda taşınır:** `provisioning/offline-repo/packages/` altında 1321 `.deb` (~888 MB) durur ve `.gitattributes` ile **Git LFS** üzerinden takip edilir (`*.deb filter=lfs ...`). Bu yüzden sahada "hangi paket nereden geldi?" sorusu ne internete ne de dış medyaya bağlıdır; aynı soru `packages.lock.tsv` ve `repo-manifest.yaml` ile yanıtlanır. Kuran kişi paket aramaz ve **indirmez**; klonladığı depo seti zaten taşır.
|
||||
|
||||
Durum makinesi, "kuruldu" ile "merkezce güvenildi" arasındaki farkı görünür yapar: offline kurulum yalnız `PROVISIONED_OFFLINE` üretir.
|
||||
|
||||
## 5. Alternatifler
|
||||
|
||||
| Alternatif | Artı | Eksi | Sonuç |
|
||||
|---|---|---|---|
|
||||
| Sürümlü offline APT repo | Tekrarlanabilir | Medya yönetimi | **Seçildi** |
|
||||
| Sürümlü offline APT bundle + repoda taşınan paketler | Tekrarlanabilir, indirme yok, medya lojistiği yok | Git deposu ~888 MB büyür (LFS) | **Seçildi** |
|
||||
| Ayrı fiziksel seed USB medyası | Depo küçük kalır | Medya zimmeti/sürüm takibi, kaybolma riski | Yedek/geri dönüş yolu |
|
||||
| Rastgele `.deb` dosyaları | İlk anda kolay | Bağımlılık/sürüm belirsiz | Elendi |
|
||||
| İnternet bekleme | Basit | Saha blokajı | Elendi |
|
||||
| Yerel depoyu operatörün `--offline` bayrağıyla göstermesi | Basit | Bayrak unutulunca kurulum durur, saha hatası üretir | Elendi (bayrak geriye uyumluluk için korundu) |
|
||||
|
||||
## 6. Avantajlar
|
||||
|
||||
- Bağlantısız sahada standarda yakın kurulum.
|
||||
- Paket manifesti ile hangi sürümün yüklendiği kanıtlanır.
|
||||
- Kurulum yapan kişi **hiçbir indirme yapmaz** ve bayrak ezberlemez: paketler zaten deponun içindedir, kurucu depoyu kendisi bulur.
|
||||
- Bağlantısız sahada standarda yakın kurulum; **kuran kişi hiçbir indirme yapmaz** ve bayrak ezberlemez.
|
||||
- Paket manifesti ve kilit dosyası ile hangi sürümün yüklendiği kanıtlanır.
|
||||
- Nesil farkı yoktur: repo sorumlusu paketleri bir kez commit eder, 700 cihaz aynı setten kurulur.
|
||||
- Kurulum akışı grafik arayüzde tek düğmedir; terminal bilgisi gerekmez.
|
||||
|
||||
## 7. Dezavantajlar
|
||||
|
||||
- Medya sürümü ve paket snapshot'ı düzenli yenilenmelidir.
|
||||
- Git deposu paket setiyle birlikte büyür (LFS clone/pull maliyeti); derin clone sahada zaman alabilir.
|
||||
- Offline cihaz merkezi izleme ve erişim kanalları doğrulanana kadar operasyonel olarak hazır değildir.
|
||||
- Paket snapshot'ı yenilenmediği sürece güvenlik güncellemeleri geride kalır (onaysız update zaten yasak, K-11).
|
||||
|
||||
## 8. Riskler
|
||||
|
||||
| Risk | Olasılık | Etki | Azaltma |
|
||||
|---|---|---|---|
|
||||
| Eski güvenlik paketi | Orta | Orta | Süre sonu olan release medyası, hızlı enrollment kuyruğu |
|
||||
| Secret USB'ye yazılır | Düşük | Yüksek | Secret taraması; token yalnız bir kez ve cihazda girilir |
|
||||
| Eski güvenlik paketi | Orta | Orta | Süre sonu olan release medyası/repo sürümü, hızlı enrollment kuyruğu |
|
||||
| Secret repoda kalır | Düşük | Yüksek | Secret taraması (test), token yalnız bir kez ve cihazda girilir |
|
||||
| Yarım paket işlemi | Orta | Orta | `dpkg --configure -a`, idempotent kurucu |
|
||||
| Kurucu yanlış/eski depoyu bulur | Düşük | Orta | Arama sırası sabittir; aday eksiksiz değilse kullanılmaz, en son `BF_OFFLINE_REPO` ile açıkça yönlendirilebilir |
|
||||
| Kurucu yanlış/eski depoyu bulur | Düşük | Orta | Arama sırası sabittir; aday eksiksiz değilse kullanılmaz, `BF_OFFLINE_REPO` ile açıkça yönlendirilebilir |
|
||||
| Paketler var ama indeks (`built/`) yok | Orta | Yüksek | Kurucu fail-closed durur ("yerel paket deposu yok"); repo sorumlusu `build-index.sh` koşup `built/` dizinini commit eder |
|
||||
| Grafik arayüz açılamıyor | Orta | Düşük | `python3-tk` offline depoda pinli; ekransız ortamda `sudo bf --tui` |
|
||||
|
||||
## 9. Uygulama Planı
|
||||
|
||||
1. Medya manifesti: Field OS release, ISO checksum, seed checksum, offline APT snapshot kimliği, kurucu commit'i.
|
||||
2. Paket setini **repo sorumlusu** hazırlar: `.deb` dosyalarını `add-packages.sh` ile depoya koyar, `build-index.sh` ile `provisioning/offline-repo/built` indeksini üretir, `packages.lock.tsv` / `SHA256SUMS` / `repo-manifest.yaml` ile birlikte commit eder. Kuran kişi yalnız git tarafını çeker.
|
||||
1. Medya/repo manifesti: Field OS release, ISO checksum, offline APT snapshot kimliği, kurucu commit'i.
|
||||
2. Paket setini **repo sorumlusu** hazırlar: `.deb` dosyalarını `add-packages.sh` ile `provisioning/offline-repo/packages/` altına koyar (Git LFS), pinleri `manifests/*.txt` içinde `package=version` olarak doldurur, `build-index.sh` ile `provisioning/offline-repo/built/` indeksini (`Packages`, `Packages.gz`, `packages.lock.tsv`, `SHA256SUMS`, `pool/`) üretir. Kuran kişi yalnız git tarafını çeker.
|
||||
3. Kurulumda yalnız genel paketler kullanılır; kurucu yerel depoyu **otomatik bulur** ve `--offline` gerekmez.
|
||||
4. Kurucu yerel durum dosyasına `PROVISIONED_OFFLINE` yazar; `READY` yazması yasaktır.
|
||||
5. İnternet geldiğinde 29'daki tek-kullanımlık token ile enrollment başlatılır; sonra merkezi kanallar test edilir.
|
||||
|
||||
Kuran kişinin komutu (bayrak yok, tek zorunlu bilgi bayi numarası):
|
||||
### Kuran kişinin akışı (grafik arayüz — ana yol)
|
||||
|
||||
1. Depoyu getir: `git clone <repo>` (paketler Git LFS ile gelir) — veya repo sorumlusunun bıraktığı kopyada `/opt/blueforce/offline-repo` hazırdır.
|
||||
2. `sudo bf` — pencere açılır: **Blueforce Field OS — Yönetim**.
|
||||
3. Üst çubuk **Hedef > Bayi no (8 hane)** alanına numarayı yaz (örn. `12010193`).
|
||||
4. **Kurulum** sekmesi → **`Bu cihazı kur`** (hiçbir şey yazmayan ön kontrol için: `Kurulum ön kontrolü (--check)`).
|
||||
5. Komut ekranda gösterilir, onay istenir; onaylandıktan sonra kurulum çalışır. `--offline` işaretlemek **zorunlu değildir** (varsayılan kaynak zaten yerel depodur).
|
||||
6. Sonuç: `PROVISIONED_OFFLINE`. Arayüzdeki **Kurulum modül durumu** ve `bf-status` ile doğrulanır.
|
||||
|
||||
```bash
|
||||
# Konsoldan (önerilen): Python operatör konsolu; bash yedek yolu sudo bf-menu
|
||||
# Ana yol (grafik arayüz)
|
||||
sudo bf
|
||||
# → Hedef: Bayi no (8 hane) → Kurulum sekmesi → "Bu cihazı kur"
|
||||
|
||||
# Doğrudan kurucu ile
|
||||
# Yedek yol (grafik ekran yoksa veya python3-tk yoksa): aynı uygulamanın terminal arayüzü
|
||||
sudo bf --tui
|
||||
|
||||
# Doğrudan kurucu ile (otomasyon / teşhis)
|
||||
sudo ./scripts/install/blueforce-install.sh --dealer-id 12010193
|
||||
|
||||
# Bilinçli olarak başka bir depo dizinini göstermek (nadir, repo sorumlusu isterse)
|
||||
|
|
@ -85,6 +104,10 @@ BF_OFFLINE_REPO=/mnt/blueforce-repo/built \
|
|||
sudo ./scripts/install/blueforce-install.sh --dealer-id 12010193
|
||||
```
|
||||
|
||||
### Depo keşfi ve iki hata durumu
|
||||
|
||||
Sıra (ilk eksiksiz aday kazanır): `BF_OFFLINE_REPO` → `<repo>/provisioning/offline-repo/built` → `/opt/blueforce/offline-repo`. Bir aday "eksiksiz" sayılmak için `Packages`, `Packages.gz`, `packages.lock.tsv` ve `SHA256SUMS` dosyalarının hepsini içermelidir; yalnız `packages/` (ham `.deb` havuzu) yeterli **değildir** — indeksin `build-index.sh` ile üretilmiş olması gerekir.
|
||||
|
||||
Depo bulunduğunda ekranda şu duyuru çıkar:
|
||||
|
||||
```text
|
||||
|
|
@ -98,7 +121,16 @@ Depo hiçbir yerde yoksa kurulum başlamadan durur:
|
|||
|
||||
```text
|
||||
HATA: Yerel paket deposu yok. Repo sorumlusuna başvurun: provisioning/offline-repo/README.md
|
||||
...
|
||||
|
||||
Aranan yerler:
|
||||
- depodaki hazır paket seti — yok: <repo>/provisioning/offline-repo/built
|
||||
- cihazdaki kurulu paket deposu — yok: /opt/blueforce/offline-repo
|
||||
|
||||
Ne yapmalı:
|
||||
1) Paket setini git ile tam olarak getirin ... ya da /opt/blueforce/offline-repo altına koyun.
|
||||
2) Kurulumu yeniden çalıştırın.
|
||||
|
||||
Kuruluma hiç başlanmadı; sistemde hiçbir değişiklik yapılmadı.
|
||||
ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.
|
||||
```
|
||||
|
||||
|
|
@ -114,15 +146,22 @@ OFFLINE BLOCKED: <sebep>
|
|||
|
||||
`--online` yalnız geliştirme/test makinesi içindir; paketleri internetten çeker ve üretim cihazlarında kullanılmaz.
|
||||
|
||||
### `python3-tk` neden offline depoda pinli?
|
||||
|
||||
Grafik operatör uygulaması Tkinter ile yazılmıştır ve tkinter Ubuntu'da **ayrı bir pakettir** (`python3-tk`); minimal Ubuntu Server kurulumunda Python vardır ama tkinter yoktur. İnternetsiz sahada `apt install python3-tk` **çalışmaz**, bu yüzden paket ve bağımlılık kümesi (`python3-tk`, `python3.14-tk`, `libtk8.6`) `provisioning/offline-repo/manifests/base-packages.txt` içinde pinlidir ve depodan gelir. Kurulum sonu kapısı `scripts/install/installer/modules/18-final-check.sh` tkinter'ı denetler; eksikse **WARN** yazar ve kurulumu düşürmez (cihaz yine kurulur, operatör terminal konsoluna `sudo bf --tui` ile devam eder).
|
||||
|
||||
## 10. Test Planı
|
||||
|
||||
| Test | Beklenen sonuç | Ortam |
|
||||
|---|---|---|
|
||||
| İnternetsiz kurulum | Paketler manifestten kurulur, dış indirme yok | LAB(2) air-gapped |
|
||||
| Depo yolu verilmeden kurulum | Kurucu depoyu otomatik bulur (`repo/built`), duyuru satırını yazar | LAB(2) |
|
||||
| Grafik arayüzden kurulum | `sudo bf` → Kurulum → "Bu cihazı kur" ile `PROVISIONED_OFFLINE` | LAB(2) masaüstü oturumu |
|
||||
| `BF_OFFLINE_REPO` ile başka dizin | Verilen dizin kullanılır; geçersizse durur | LAB(2) |
|
||||
| Depo hiç yok | Türkçe hata + `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.`, exit 1, sistem değişmez | LAB(2) |
|
||||
| Depo hiç yok | Türkçe hata + aranan yollar + `ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.`, exit 1, sistem değişmez | LAB(2) |
|
||||
| Depo var ama bozuk (checksum/pin) | `OFFLINE BLOCKED: <sebep>` + Türkçe çıkış nedeni, exit 1, sistem değişmez | LAB(2) |
|
||||
| Yalnız `packages/` var, `built/` yok | Depo "yok" sayılır, kurulum başlamaz (fail-closed) | LAB(2) |
|
||||
| `python3-tk` eksik imaj | `sudo bf` Türkçe hata + ipucu, exit 3; `18-final-check` WARN yazar, kurulum düşmez | LAB(2) |
|
||||
| DNS/WAN kesik | Kurucu dış indirme istemez; `01-precheck` ağı yalnız bilgi olarak yazar, `05-network` DNS zorunlu tutmaz | LAB(2) |
|
||||
| `--online` üretimde denenmez | Yalnız geliştirme/test; kurucu uyarı yazar | LAB(2) |
|
||||
| `bf-status` | `PROVISIONED_OFFLINE`; READY değil | LAB(2) |
|
||||
|
|
@ -131,26 +170,29 @@ OFFLINE BLOCKED: <sebep>
|
|||
## 11. Rollback
|
||||
|
||||
1. Paket işlemi bozuksa aynı onaylı snapshot ile `dpkg --configure -a` ve kurucu yeniden çalıştırılır.
|
||||
2. Yanlış medya sürümü geri çekilir; önceki onaylı medya manifestiyle yeniden kurulur.
|
||||
2. Yanlış repo sürümü geri çekilir; önceki onaylı manifestle yeniden kurulur.
|
||||
3. Yerel depo bozuksa sahada onarılmaz: repo sorumlusu depoyu yeniden üretip commit eder (`add-packages.sh` + `build-index.sh`), kurulum yapan kişi güncel git tarafını çekip komutu tekrarlar.
|
||||
4. Kimlik/token hatasında cihaz resetlenmez; 29'daki token iptal/yenileme prosedürü uygulanır.
|
||||
5. Arayüz sorunuysa (ekran/tkinter) rollback gerekmez: `sudo bf --tui` ile aynı akış yürütülür.
|
||||
|
||||
## 12. Kontrol Listesi
|
||||
|
||||
- [ ] Offline medya/git manifesti ve checksum'ları var.
|
||||
- [ ] Paket seti repoda: `provisioning/offline-repo/built` (`Packages`, `Packages.gz`, `packages.lock.tsv`, `SHA256SUMS`, `pool/`).
|
||||
- [ ] Paket seti repoda: `provisioning/offline-repo/packages/` (1321 `.deb`, Git LFS ile takip ediliyor).
|
||||
- [ ] Bundle indeksi repoda: `provisioning/offline-repo/built/` (`Packages`, `Packages.gz`, `packages.lock.tsv`, `SHA256SUMS`, `pool/`).
|
||||
- [ ] `repo-manifest.yaml` güncel; pin listesi ile paket havuzu uyuşuyor.
|
||||
- [ ] Medyada/depoda cihaz kimliği, private key veya kalıcı token yok.
|
||||
- [ ] Depoda/repoda cihaz kimliği, private key veya kalıcı token yok.
|
||||
- [ ] `python3-tk` ve bağımlılıkları `manifests/base-packages.txt` içinde pinli; `18-final-check` tkinter'ı denetliyor.
|
||||
- [ ] Kuran kişinin komutu `--offline` bayrağı istemiyor; depo otomatik bulunuyor.
|
||||
- [ ] Grafik arayüz akışı (bayi no → Kurulum → "Bu cihazı kur") yazılı ve ekransız ortam için `sudo bf --tui` belgeli.
|
||||
- [ ] İnternet kapalıyken kurulum tamamlanıyor; dış indirme çağrısı yok.
|
||||
- [ ] `PROVISIONED_OFFLINE` görünür ve READY kapısı kapalı.
|
||||
- [ ] Sonradan enrollment sahibi/kuyruğu kayıtlı.
|
||||
|
||||
## 13. Açık Sorular
|
||||
|
||||
- [ ] Offline APT snapshot yenileme sıklığı — sahibi: release yöneticisi.
|
||||
- [ ] Medyanın fiziksel zimmet ve imha süreci — sahibi: saha operasyonu.
|
||||
- [ ] Git checkout ile ISO/medya yolundan hangisinin birincil olduğu ve `--online` geliştirme yolunun hangi ortamlarda serbest olduğu — sahibi: yönetim + 26/33 yazarları.
|
||||
- [ ] Offline paket seti yenileme sıklığı ve Git LFS bant genişliği/kotaysa sahaya taşıma yöntemi — sahibi: release yöneticisi.
|
||||
- [ ] Paketlerin sahaya taşınmasında git clone mu, hazır `/opt/blueforce/offline-repo` kopyası mı birincil — sahibi: saha operasyonu + 05 yazarı.
|
||||
- [ ] `--online` geliştirme yolunun hangi ortamlarda serbest olduğu — sahibi: yönetim + 26/33 yazarları.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -158,8 +200,9 @@ OFFLINE BLOCKED: <sebep>
|
|||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
M["ISO/seed veya git checkout<br/>+ repodaki offline APT"] --> D{"Kurucu yerel depoyu arar<br/>BF_OFFLINE_REPO → repo/built → /opt/blueforce/offline-repo"}
|
||||
D -->|"hiçbiri yok / eksik"| X["HATA: Yerel paket deposu yok<br/>ÇIKIŞ NEDENİ: bulunamadı · exit 1<br/>sistem değişmez"]
|
||||
M["git checkout (paketler LFS ile)<br/>veya /opt/blueforce/offline-repo"] --> G["sudo bf → Kurulum → 'Bu cihazı kur'"]
|
||||
G --> D{"Kurucu yerel depoyu arar<br/>BF_OFFLINE_REPO → repo/built → /opt/blueforce/offline-repo"}
|
||||
D -->|"hiçbiri yok / eksik"| X["HATA: Yerel paket deposu yok<br/>aranan yollar + ÇIKIŞ NEDENİ · exit 1<br/>sistem değişmez"]
|
||||
D -->|"eksiksiz aday bulundu"| V{"Doğrulama katmanı:<br/>dosya listesi, SHA256SUMS, kilit, pin ↔ pool"}
|
||||
V -->|"başarısız"| B["OFFLINE BLOCKED: sebep<br/>ÇIKIŞ NEDENİ: doğrulanamadı · exit 1"]
|
||||
V -->|"geçti"| P["PROVISIONED_OFFLINE"]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -146,9 +146,13 @@ inspect_deb() {
|
|||
printf ' dpkg-deb çıktısı: %s\n' "$raw" >&2
|
||||
return 1
|
||||
fi
|
||||
DEB_PKG="$(printf '%s\n' "$raw" | sed -n '1p')"
|
||||
DEB_VER="$(printf '%s\n' "$raw" | sed -n '2p')"
|
||||
DEB_ARCH="$(printf '%s\n' "$raw" | sed -n '3p')"
|
||||
# Only the value of each field belongs in these variables: dpkg-deb prints
|
||||
# "<Field>: <value>", and keeping the label made every real package fail the
|
||||
# name check ("Package: curl" is not a package name). The test suite stubs
|
||||
# dpkg-deb, so this only shows up against real .deb files.
|
||||
DEB_PKG="$(printf '%s\n' "$raw" | sed -n 's/^Package: //p')"
|
||||
DEB_VER="$(printf '%s\n' "$raw" | sed -n 's/^Version: //p')"
|
||||
DEB_ARCH="$(printf '%s\n' "$raw" | sed -n 's/^Architecture: //p')"
|
||||
|
||||
if [[ ! "$DEB_PKG" =~ $PACKAGE_NAME_RE ]]; then
|
||||
printf 'HATA: paket adı geçersiz: "%s" (%s)\n' "$DEB_PKG" "$file" >&2
|
||||
|
|
@ -175,8 +179,12 @@ scan_existing() {
|
|||
[[ -d "$PACKAGES_DIR" ]] || return 0
|
||||
while IFS= read -r -d '' deb; do
|
||||
sha="$(file_sha256 "$deb")"
|
||||
pkg="$(dpkg-deb -f "$deb" Package 2>/dev/null || true)"
|
||||
ver="$(dpkg-deb -f "$deb" Version 2>/dev/null || true)"
|
||||
# Strip the "<Field>: " label dpkg-deb prints; the map keys are package and
|
||||
# version values, and a labelled key never matches the incoming candidate
|
||||
# (so a version conflict went undetected and the same package was copied
|
||||
# twice).
|
||||
pkg="$(dpkg-deb -f "$deb" Package 2>/dev/null | sed -n 's/^Package: //p' || true)"
|
||||
ver="$(dpkg-deb -f "$deb" Version 2>/dev/null | sed -n 's/^Version: //p' || true)"
|
||||
if [[ -z "$pkg" || -z "$ver" ]]; then
|
||||
printf 'UYARI: packages/ içindeki okunamayan dosya yok sayıldı: %s\n' "${deb##*/}" >&2
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -284,10 +284,14 @@ fi
|
|||
|
||||
while IFS= read -r -d '' deb; do
|
||||
base="$(basename "$deb")"
|
||||
# dpkg-deb prints "<Field>: <value>"; only the value belongs in the pool key,
|
||||
# so strip the label. Taking the whole line made every package fail the name
|
||||
# check and the pool came out empty -- a stub dpkg-deb in the test suite hides
|
||||
# this, so it only surfaces against real .deb files.
|
||||
raw="$(dpkg-deb -f "$deb" Package Version Architecture 2>/dev/null || true)"
|
||||
package="$(printf '%s\n' "$raw" | sed -n '1p')"
|
||||
version="$(printf '%s\n' "$raw" | sed -n '2p')"
|
||||
arch="$(printf '%s\n' "$raw" | sed -n '3p')"
|
||||
package="$(printf '%s\n' "$raw" | sed -n 's/^Package: //p')"
|
||||
version="$(printf '%s\n' "$raw" | sed -n 's/^Version: //p')"
|
||||
arch="$(printf '%s\n' "$raw" | sed -n 's/^Architecture: //p')"
|
||||
if [[ ! "$package" =~ $PACKAGE_NAME_RE || -z "$version" ]]; then
|
||||
POOL_ERRORS+=("$base: geçerli bir Debian paketi okunamadı; add-packages.sh ile ekleyin")
|
||||
continue
|
||||
|
|
@ -447,6 +451,11 @@ else
|
|||
rm -rf "$OUTPUT"
|
||||
mv -- "$STAGE" "$OUTPUT"
|
||||
STAGE=''
|
||||
# mktemp -d creates 0700, and this directory is delivered: the installer reads
|
||||
# the bundle, an operator inspects it, and a build host may copy it as another
|
||||
# user. A private mode makes a valid bundle look missing (the installer's
|
||||
# readability probe sees an unreadable directory), so publish it explicitly.
|
||||
chmod 0755 "$OUTPUT"
|
||||
printf 'OK: depo yazıldı: %s\n' "$(rel_to_repo "$OUTPUT")"
|
||||
fi
|
||||
|
||||
|
|
@ -540,8 +549,8 @@ printf ' git add -f %s\n' "$(rel_to_repo "$OUTPUT")"
|
|||
printf ' git add %s\n' "$(rel_to_repo "$MANIFEST_OUT")"
|
||||
printf ' git commit -m "offline repo: %d paket, offline APT indeksi"\n' "$POOL_TOTAL"
|
||||
if [[ ! -d "$SCRIPT_DIR/build" ]]; then
|
||||
printf '\nNOT: provisioning/iso/build-blueforce-iso.sh offline depoyu "build" adıyla arıyor.\n'
|
||||
printf ' ISO ile aynı yolu kullanmak isterseniz: ./build-index.sh --output build\n'
|
||||
printf '\nNOT: provisioning/iso/build-blueforce-iso.sh varsayılan olarak bu dizini ("built/") arar.\n'
|
||||
printf ' Eski "build/" adı yalnız uyarıyla kabul edilir; bu çıktı ISO ile uyumludur.\n'
|
||||
fi
|
||||
printf '\nDoğrulama (hedef cihazda /opt/blueforce/offline-repo):\n'
|
||||
printf ' cd /opt/blueforce/offline-repo && sha256sum -c SHA256SUMS\n'
|
||||
|
|
|
|||
|
|
@ -249,7 +249,10 @@ for list in "$SCRIPT_DIR/manifests/base-packages.txt" "$SCRIPT_DIR/manifests/rem
|
|||
pins=$((pins + 1))
|
||||
found=0
|
||||
for deb in "${debs[@]}"; do
|
||||
[[ "$(dpkg-deb -f "$deb" Package)=$(dpkg-deb -f "$deb" Version)" == "$pin" ]] && found=1 && break
|
||||
# dpkg-deb prints "<Field>: <value>", so the label has to be stripped
|
||||
# before the value can be compared with a "package=version" pin.
|
||||
deb_pin="$(dpkg-deb -f "$deb" Package | sed -n 's/^Package: //p')=$(dpkg-deb -f "$deb" Version | sed -n 's/^Version: //p')"
|
||||
[[ "$deb_pin" == "$pin" ]] && found=1 && break
|
||||
done
|
||||
[[ "$found" -eq 1 ]] || { printf 'Pinned package not supplied locally: %s\n' "$pin" >&2; exit 1; }
|
||||
done < "$list"
|
||||
|
|
|
|||
36962
provisioning/offline-repo/built/Packages
Normal file
36962
provisioning/offline-repo/built/Packages
Normal file
File diff suppressed because it is too large
Load diff
BIN
provisioning/offline-repo/built/Packages.gz
Normal file
BIN
provisioning/offline-repo/built/Packages.gz
Normal file
Binary file not shown.
3
provisioning/offline-repo/built/SHA256SUMS
Normal file
3
provisioning/offline-repo/built/SHA256SUMS
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
e608222b648a07f0223e0dde07b62f3e67e52620de5ee7ab786fc23093fe7cac Packages
|
||||
9253dabe3c6e547176acebec84512024b392f55f5f556fb8b523f7553e8341c0 Packages.gz
|
||||
d2846fefb7961ebed6a8156fa6a4c1abcc6b5d5aa4f9ba4afad01915b50a905d packages.lock.tsv
|
||||
1322
provisioning/offline-repo/built/packages.lock.tsv
Normal file
1322
provisioning/offline-repo/built/packages.lock.tsv
Normal file
File diff suppressed because it is too large
Load diff
9286
provisioning/offline-repo/built/repo-manifest.yaml
Normal file
9286
provisioning/offline-repo/built/repo-manifest.yaml
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -41,6 +41,23 @@ smartmontools=7.5-2
|
|||
ufw=0.36.2-9build1
|
||||
iptables=1.8.11-2ubuntu3
|
||||
|
||||
# --- operator GUI (bfos/ui_gui.py; module 18-final-check.sh) ---
|
||||
# tkinter ships as its own package on Ubuntu, so a minimal image has Python but
|
||||
# no GUI. Without it the operator application cannot open its window at all --
|
||||
# and the fleet has no network to fetch it from.
|
||||
python3-tk=3.14.3-0ubuntu2
|
||||
# python3-tk's own dependency closure. Pinned explicitly because the installer
|
||||
# resolves nothing from a remote source: whatever the pin does not name must
|
||||
# already be on the image.
|
||||
python3.14-tk=3.14.4-1ubuntu0.2
|
||||
libtk8.6=8.6.17-1build1
|
||||
libreadline8t64=8.3-4
|
||||
readline-common=8.3-4
|
||||
libffi8=3.5.2-4
|
||||
libsqlite3-0=3.46.1-9ubuntu0.2
|
||||
libbrotli1=1.2.0-3build1
|
||||
openssl=3.5.5-1ubuntu3.5
|
||||
|
||||
# --- monitoring (module 12-monitoring.sh) ---
|
||||
# The textfile collector ships inside this package; no extra dependency is needed.
|
||||
prometheus-node-exporter=1.10.2-1
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@ base-packages.txt smartmontools scripts/diagnostics/bf-diagnostics SMART disk he
|
|||
base-packages.txt ufw scripts/install/installer/modules/13-firewall.sh host firewall baseline
|
||||
base-packages.txt iptables scripts/install/installer/modules/13-firewall.sh rule backends used by the firewall module
|
||||
base-packages.txt prometheus-node-exporter scripts/install/installer/modules/12-monitoring.sh node exporter including the textfile collector
|
||||
base-packages.txt python3-tk scripts/install/installer/modules/18-final-check.sh tkinter is a separate package on Ubuntu; without it the operator GUI cannot start
|
||||
base-packages.txt python3.14-tk scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt libtk8.6 scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt libreadline8t64 scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt readline-common scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt libffi8 scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt libsqlite3-0 scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt libbrotli1 scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
base-packages.txt openssl scripts/install/installer/modules/18-final-check.sh python3-tk dependency closure; the installer resolves nothing from a remote source
|
||||
remote-access.txt rustdesk scripts/install/installer/modules/09-rustdesk.sh pinned vendor .deb; not in the Ubuntu archive
|
||||
remote-access.txt libxdo3 scripts/install/installer/modules/09-rustdesk.sh rustdesk 1.4.9 Depends; pulled explicitly because the vendor .deb is installed with dpkg, not from an APT source
|
||||
remote-access.txt libva-x11-2 scripts/install/installer/modules/09-rustdesk.sh rustdesk 1.4.9 Depends; pulled explicitly because the vendor .deb is installed with dpkg, not from an APT source
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 13.
|
BIN
provisioning/offline-repo/packages/libbrotli1_1.2.0-3build1_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/libbrotli1_1.2.0-3build1_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/libffi8_3.5.2-4_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/libffi8_3.5.2-4_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/libreadline8t64_8.3-4_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/libreadline8t64_8.3-4_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/libsqlite3-0_3.46.1-9ubuntu0.2_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/libsqlite3-0_3.46.1-9ubuntu0.2_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/libtk8.6_8.6.17-1build1_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/libtk8.6_8.6.17-1build1_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/openssl_3.5.5-1ubuntu3.5_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/openssl_3.5.5-1ubuntu3.5_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/python3-tk_3.14.3-0ubuntu2_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/python3-tk_3.14.3-0ubuntu2_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/python3.14-tk_3.14.4-1ubuntu0.2_amd64.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/python3.14-tk_3.14.4-1ubuntu0.2_amd64.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
provisioning/offline-repo/packages/readline-common_8.3-4_all.deb
(Stored with Git LFS)
Normal file
BIN
provisioning/offline-repo/packages/readline-common_8.3-4_all.deb
(Stored with Git LFS)
Normal file
Binary file not shown.
|
|
@ -5,6 +5,23 @@ LOG_FILE="${LOG_FILE:-/var/log/blueforce-install.log}"; STATE_DIR="${STATE_DIR:-
|
|||
[[ "${1:-}" == --check ]] && CHECK_MODE=1
|
||||
OFFLINE="${BF_OFFLINE:-0}"; SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
log() { echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) [18-final-check] $1" | tee -a "$LOG_FILE"; }
|
||||
|
||||
# Operator GUI runtime gate. The field application is the Python GUI (bfos), and
|
||||
# tkinter is a separate package on Ubuntu: a minimal image has python3 but no
|
||||
# window toolkit, so the operator would be left with no working entry point and
|
||||
# no network to fix it. Checked here because every install path reaches this
|
||||
# module, offline or not. When the GUI is unavailable the terminal console still
|
||||
# works, so this reports the degradation instead of aborting the install.
|
||||
if python3 -c 'import tkinter' >/dev/null 2>&1; then
|
||||
log 'OK: operatör arayüzü (tkinter) hazır'
|
||||
else
|
||||
log 'WARN: tkinter yok; operatör arayüzü açılamaz. Terminal konsolu çalışır: sudo bf --tui'
|
||||
log 'WARN: python3-tk paketi bu imajda eksik (offline depoda pinli: provisioning/offline-repo/manifests/base-packages.txt)'
|
||||
fi
|
||||
if [[ "$CHECK_MODE" == 1 ]]; then
|
||||
command -v bf >/dev/null 2>&1 || log 'WARN(check): bf komutu PATH üzerinde yok; kurulum bf simgesini oluşturur'
|
||||
fi
|
||||
|
||||
if [[ "$OFFLINE" == 1 ]]; then
|
||||
# Only local gates are meaningful offline. No central peer, handshake, or READY claim.
|
||||
for g in 01-precheck 02-system 03-user 04-hostname 05-network 06-wireguard 07-ssh 08-rdp 10-docker 11-meg 12-monitoring 13-firewall 14-update-policy 15-logrotate 16-gui 17-healthcheck; do
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -146,7 +146,15 @@ for column in package version architecture sha256 source; do
|
|||
done
|
||||
|
||||
# 7. No secrets, keys, or device identities in the offline repository assets.
|
||||
if grep -RniE --exclude-dir=.git \
|
||||
#
|
||||
# The committed built/ index is excluded: it is byte-for-byte apt-ftparchive
|
||||
# output over third-party packages, and upstream descriptions legitimately
|
||||
# contain the trigger words as prose (polkit's description ends "...without
|
||||
# being asked for their password:"), so scanning it reports the packages, not a
|
||||
# leak. Nothing is hidden by this: any secret that reached a manifest or a
|
||||
# README would still be caught below, and build-index.sh embeds no credentials
|
||||
# of its own -- its records are package name/version/sha256/size/source.
|
||||
if grep -RniE --exclude-dir=.git --exclude-dir=built \
|
||||
'(BEGIN [A-Z ]*PRIVATE KEY|(password|passwd|token|api[_-]?key|secret)[[:space:]]*[:=]|AKIA[0-9A-Z]{16}|BF-[0-9]{8})' \
|
||||
"$OFFLINE_REPO"; then
|
||||
fail 'secret-like or device-specific value found in provisioning/offline-repo'
|
||||
|
|
@ -417,6 +425,11 @@ cat > "$STUB_BIN/dpkg-deb" <<'STUB'
|
|||
#!/usr/bin/env bash
|
||||
# Test stub: real dpkg-deb reads the control member; the identity is read from
|
||||
# the <package>_<version>_<arch>.deb file name instead.
|
||||
#
|
||||
# The output carries the "<Field>: <value>" labels exactly as dpkg-deb prints
|
||||
# them. Emitting bare values made every consumer look correct while the real
|
||||
# tool was mis-parsed -- the pool came out empty against genuine .deb files
|
||||
# because the callers kept the label and "Package: curl" is not a package name.
|
||||
set -euo pipefail
|
||||
[[ "${1:-}" == '-f' ]] || exit 1
|
||||
file="${2:-}"
|
||||
|
|
@ -431,9 +444,9 @@ version="${rest##*_}"; package="${rest%_*}"
|
|||
[[ -n "$package" && -n "$version" && -n "$arch" ]] || exit 1
|
||||
for field in "$@"; do
|
||||
case "$field" in
|
||||
Package) printf '%s\n' "$package" ;;
|
||||
Version) printf '%s\n' "$version" ;;
|
||||
Architecture) printf '%s\n' "$arch" ;;
|
||||
Package) printf 'Package: %s\n' "$package" ;;
|
||||
Version) printf 'Version: %s\n' "$version" ;;
|
||||
Architecture) printf 'Architecture: %s\n' "$arch" ;;
|
||||
*) printf '\n' ;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -541,6 +554,11 @@ rc=0; out="$(bash "$BUILD_INDEX" --packages "$PKG_OUT" --output "$FOREIGN_OUT" -
|
|||
|
||||
# The real build.
|
||||
OUT="$TMP/owner-built"
|
||||
# Snapshot the committed bundle directory before this build. The assertion below
|
||||
# is about THIS build not writing there -- not about the directory being empty:
|
||||
# a maintainer's machine may hold an untracked/committed real build, and an
|
||||
# absolute "must be clean" check reported that legitimate bundle as test spill.
|
||||
built_state_before="$(git -C "$REPO_ROOT" status --porcelain -- "provisioning/offline-repo/built" 2>/dev/null || true)"
|
||||
rc=0; out="$(bash "$BUILD_INDEX" --packages "$PKG_OUT" --output "$OUT" --manifest-dir "$TMP/m" --manifest-out "$TMP/repo-manifest.yaml" 2>&1)" || rc=$?
|
||||
[[ "$rc" -eq 0 ]] && pass 'build-index.sh builds a bundle from the accepted pool' || { fail "build-index.sh build failed (exit $rc)"; printf '%s\n' "$out" >&2; }
|
||||
for artifact in Packages Packages.gz packages.lock.tsv SHA256SUMS; do
|
||||
|
|
@ -609,7 +627,9 @@ record_after="$(sha256sum "$TMP/repo-manifest.yaml" | awk '{print $1}')"
|
|||
grep -q 'DEĞİŞİKLİK YOK' <<<"$out" && pass 'build-index.sh reports the unchanged bundle instead of rewriting it' || fail 'build-index.sh must report an unchanged bundle'
|
||||
[[ "$record_before" == "$record_after" ]] && pass 'an unchanged rebuild leaves the repository record untouched' || fail 'an unchanged rebuild must not restamp the repository record'
|
||||
git_status_note="$(git -C "$REPO_ROOT" status --porcelain -- "provisioning/offline-repo/built" 2>/dev/null || true)"
|
||||
[[ -z "$git_status_note" ]] && pass 'the test build never touches the committed bundle directory' || fail "the test build wrote into provisioning/offline-repo/built: $git_status_note"
|
||||
[[ "$git_status_note" == "$built_state_before" ]] \
|
||||
&& pass 'the test build never touches the committed bundle directory' \
|
||||
|| fail "the test build changed provisioning/offline-repo/built: before=[$built_state_before] after=[$git_status_note]"
|
||||
|
||||
# A missing pin: report, then stop, without touching the previous bundle.
|
||||
rm -f "$PKG_OUT/rustdesk_9.9.9-lab-fixture_amd64.deb"
|
||||
|
|
|
|||
|
|
@ -1011,12 +1011,41 @@ class TestCli(unittest.TestCase):
|
|||
self.assertIn("yalnızca Türkçe", err.getvalue())
|
||||
|
||||
def test_invalid_repository_is_a_usage_error(self):
|
||||
# This asserts the terminal console's own repository resolution. The
|
||||
# flag is explicit because --repo is valid for the GUI too: without
|
||||
# --tui the router would hand the call to the window and the assertion
|
||||
# would measure nothing (it also hangs a test runner with a display).
|
||||
err = io.StringIO()
|
||||
with mock.patch("sys.stderr", err):
|
||||
code = cli.main(["--repo", os.path.join(self.tmp.name, "not-a-repo")])
|
||||
code = cli.main(["--tui", "--repo", os.path.join(self.tmp.name, "not-a-repo")])
|
||||
self.assertEqual(config.EXIT_USAGE, code)
|
||||
self.assertIn("Blueforce deposu", err.getvalue())
|
||||
|
||||
def test_gui_is_the_default_surface(self):
|
||||
"""Bayraksız çağrı grafik arayüze gider (varsayılan davranış)."""
|
||||
args = build_args([])
|
||||
self.assertFalse(cli._wants_tui(args))
|
||||
|
||||
def test_tui_flag_forces_the_terminal_console(self):
|
||||
self.assertTrue(cli._wants_tui(build_args(["--tui"])))
|
||||
self.assertFalse(cli._wants_tui(build_args(["--gui"])))
|
||||
|
||||
def test_terminal_only_flags_stay_in_the_terminal(self):
|
||||
"""Rapor ve terminal ayarı bayrakları GUI'ye kaptırılmaz."""
|
||||
for argv in (["--list"], ["--list-targets"], ["--check"], ["--no-color"]):
|
||||
self.assertTrue(cli._wants_tui(build_args(argv)), argv)
|
||||
# Dil bayrağı yalnız varsayılandan FARKLI verildiğinde terminale çeker;
|
||||
# --lang tr varsayılanın kendisidir ve GUI'yi engellemez.
|
||||
self.assertTrue(cli._wants_tui(build_args(["--lang", "en"])))
|
||||
self.assertFalse(cli._wants_tui(build_args(["--lang", "tr"])))
|
||||
|
||||
def test_gui_and_tui_together_are_refused(self):
|
||||
err = io.StringIO()
|
||||
with mock.patch("sys.stderr", err):
|
||||
code = cli.main(["--gui", "--tui"])
|
||||
self.assertEqual(config.EXIT_USAGE, code)
|
||||
self.assertIn("birlikte kullanılamaz", err.getvalue())
|
||||
|
||||
def test_list_is_non_interactive_and_covers_21_operations(self):
|
||||
out = io.StringIO()
|
||||
with mock.patch("sys.stdout", out):
|
||||
|
|
|
|||
|
|
@ -169,7 +169,9 @@ class TestHeadlessAndTkinter(StaticCase):
|
|||
self.assertIsNotNone(message)
|
||||
self.assertIn("DISPLAY", message)
|
||||
self.assertIn("SSH", message)
|
||||
self.assertIn("bf-menu", message)
|
||||
# Grafik arayüz açılamadığında önerilen yol aynı uygulamanın terminal
|
||||
# konsoludur; bash menü artık yedek (bkz. TERMINAL_MENU_HINT).
|
||||
self.assertIn("bf --tui", message)
|
||||
self.assertIn("python3 -m bfos.ui_gui", message)
|
||||
|
||||
def test_display_and_wayland_are_accepted(self) -> None:
|
||||
|
|
@ -182,14 +184,14 @@ class TestHeadlessAndTkinter(StaticCase):
|
|||
message = ui.tkinter_missing_message("No module named '_tkinter'")
|
||||
self.assertIn("python3-tk", message)
|
||||
self.assertIn("apt", message)
|
||||
self.assertIn("bf-menu", message)
|
||||
self.assertIn("bf --tui", message)
|
||||
self.assertIn("_tkinter", message)
|
||||
|
||||
def test_window_start_error_is_turkish(self) -> None:
|
||||
message = ui.window_start_error(RuntimeError("no display name and no $DISPLAY"))
|
||||
self.assertIn("Pencere açılamadı", message)
|
||||
self.assertIn("ssh -X", message)
|
||||
self.assertIn("bf-menu", message)
|
||||
self.assertIn("bf --tui", message)
|
||||
|
||||
def test_import_does_not_create_a_window(self) -> None:
|
||||
# tkinter varsa bile modul import'u bir Tk koku olusturmamalidir:
|
||||
|
|
@ -222,7 +224,80 @@ class TestHeadlessAndTkinter(StaticCase):
|
|||
contextlib.redirect_stderr(stderr):
|
||||
code = ui.main([])
|
||||
self.assertEqual(code, 3)
|
||||
self.assertIn("bf-menu", stderr.getvalue())
|
||||
# Aynı uygulamanın terminal konsoluna yönlendirmeli (bf --tui).
|
||||
self.assertIn("bf --tui", stderr.getvalue())
|
||||
|
||||
|
||||
class TestRootGate(StaticCase):
|
||||
"""Tam yetkili GUI: yazma işlemleri root ister, raporlar her zaman açık.
|
||||
|
||||
Katalogun kendi `destructive` işareti ölçüt alınır (bkz. ui_gui
|
||||
_normalize_operation); bu yüzden testler katalogdan örnek seçer, sabit
|
||||
isimlere bağlanmaz.
|
||||
"""
|
||||
|
||||
def test_is_root_reflects_the_effective_uid(self) -> None:
|
||||
original = os.geteuid
|
||||
try:
|
||||
os.geteuid = lambda: 0
|
||||
self.assertTrue(ui.is_root())
|
||||
self.assertTrue(ui.write_operations_enabled())
|
||||
os.geteuid = lambda: 1000
|
||||
self.assertFalse(ui.is_root())
|
||||
self.assertFalse(ui.write_operations_enabled())
|
||||
finally:
|
||||
os.geteuid = original
|
||||
|
||||
def test_destructive_operations_require_root(self) -> None:
|
||||
writes = [op for op in self.backend.list_operations() if op.destructive]
|
||||
self.assertTrue(writes, "katalogda yazma işlemi yok; kapı test edilemez")
|
||||
for op in writes:
|
||||
self.assertTrue(op.needs_root,
|
||||
f"{op.id} yıkıcı ama needs_root değil")
|
||||
|
||||
def test_read_only_operations_do_not_require_root(self) -> None:
|
||||
reports = [op for op in self.backend.list_operations() if not op.destructive]
|
||||
self.assertTrue(reports, "katalogda salt-okunur işlem yok")
|
||||
frozen = [op.id for op in reports if op.needs_root]
|
||||
self.assertEqual([], frozen,
|
||||
"salt-okunur işlemler root istememeli: " + ", ".join(frozen))
|
||||
|
||||
def test_notice_names_the_command_to_get_privileges(self) -> None:
|
||||
self.assertIn("sudo bf", ui.ROOT_REQUIRED_NOTICE)
|
||||
self.assertIn("sudo bf", ui.NON_ROOT_DISABLED_HINT)
|
||||
|
||||
def test_run_plan_refuses_a_root_plan_without_root(self) -> None:
|
||||
"""Son savunma hattı: çalıştırma tek noktadan geçer."""
|
||||
plan = ui.CommandPlan(
|
||||
commands=(("true",),),
|
||||
destructive=True,
|
||||
requires_root=True,
|
||||
operation_id="install-local",
|
||||
)
|
||||
original = os.geteuid
|
||||
try:
|
||||
os.geteuid = lambda: 1000
|
||||
result = ui.run_plan(plan)
|
||||
finally:
|
||||
os.geteuid = original
|
||||
self.assertFalse(result.ok)
|
||||
self.assertEqual(2, result.exit_code)
|
||||
self.assertIn("yönetici yetkisi", result.output.lower())
|
||||
|
||||
def test_run_plan_allows_a_root_plan_when_root(self) -> None:
|
||||
plan = ui.CommandPlan(
|
||||
commands=(("true",),),
|
||||
destructive=True,
|
||||
requires_root=True,
|
||||
operation_id="install-local",
|
||||
)
|
||||
original = os.geteuid
|
||||
try:
|
||||
os.geteuid = lambda: 0
|
||||
result = ui.run_plan(plan)
|
||||
finally:
|
||||
os.geteuid = original
|
||||
self.assertTrue(result.ok, result.output)
|
||||
|
||||
|
||||
class TestSimpleModeLayout(StaticCase):
|
||||
|
|
|
|||
Loading…
Reference in a new issue