Compare commits
No commits in common. "95a301ae3f0971c350b3bccba7de9ebaa2efa078" and "d0699d0b7ab8ee5879ba3dc86ffce28f46ed4682" have entirely different histories.
95a301ae3f
...
d0699d0b7a
@ -4,14 +4,11 @@
|
|||||||
exclude_paths:
|
exclude_paths:
|
||||||
- .cache/
|
- .cache/
|
||||||
- .github/
|
- .github/
|
||||||
- .gitea/
|
|
||||||
- .ansible/
|
- .ansible/
|
||||||
|
|
||||||
# Skip specific rules
|
# Skip specific rules
|
||||||
skip_list:
|
skip_list:
|
||||||
- yaml[line-length] # Allow longer lines in some cases
|
- yaml[line-length] # Allow longer lines in some cases
|
||||||
- yaml[document-start] # Allow missing document start in vault files
|
|
||||||
- yaml[truthy] # Allow different truthy values in workflow files
|
|
||||||
- name[casing] # Allow mixed case in task names
|
- name[casing] # Allow mixed case in task names
|
||||||
- args[module] # Skip args rule that causes "file name too long" issues
|
- args[module] # Skip args rule that causes "file name too long" issues
|
||||||
- var-naming[no-role-prefix] # Allow shorter variable names for readability
|
- var-naming[no-role-prefix] # Allow shorter variable names for readability
|
||||||
|
|||||||
@ -32,7 +32,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install Node.js for checkout action
|
- name: Install Node.js for checkout action
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y curl git
|
apt-get update && apt-get install -y curl
|
||||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||||||
apt-get install -y nodejs
|
apt-get install -y nodejs
|
||||||
|
|
||||||
@ -62,35 +62,109 @@ jobs:
|
|||||||
secret-scanning:
|
secret-scanning:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: zricethezav/gitleaks:latest
|
image: ubuntu:22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Install Node.js for checkout action
|
- name: Install Node.js for checkout action
|
||||||
run: |
|
run: |
|
||||||
apk add --no-cache nodejs npm curl
|
apt-get update && apt-get install -y curl
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||||||
|
apt-get install -y nodejs
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Scan for secrets
|
- name: Install git and Gitleaks
|
||||||
run: gitleaks detect --source . --no-banner --redact --exit-code 0
|
run: |
|
||||||
continue-on-error: true
|
apt-get update && apt-get install -y wget curl git
|
||||||
|
GITLEAKS_VERSION=$(curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//')
|
||||||
|
wget -q "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -O /tmp/gitleaks.tar.gz
|
||||||
|
tar -xzf /tmp/gitleaks.tar.gz -C /usr/local/bin/ gitleaks
|
||||||
|
chmod +x /usr/local/bin/gitleaks
|
||||||
|
gitleaks version
|
||||||
|
|
||||||
|
- name: Run Gitleaks secret scan
|
||||||
|
run: |
|
||||||
|
gitleaks detect --source . --verbose --no-banner --exit-code 1
|
||||||
|
|
||||||
dependency-scan:
|
dependency-scan:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: aquasec/trivy:latest
|
image: ubuntu:22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Install Node.js for checkout action
|
- name: Install Node.js for checkout action
|
||||||
run: |
|
run: |
|
||||||
apk add --no-cache nodejs npm curl
|
apt-get update && apt-get install -y curl
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||||||
|
apt-get install -y nodejs
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Scan dependencies
|
- name: Install Trivy
|
||||||
run: trivy fs --scanners vuln,secret --exit-code 0 .
|
run: |
|
||||||
|
apt-get update && apt-get install -y wget curl tar
|
||||||
|
# Try multiple download methods for reliability
|
||||||
|
echo "Downloading Trivy..."
|
||||||
|
if wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64.tar.gz" -O /tmp/trivy.tar.gz 2>&1; then
|
||||||
|
echo "Downloaded tar.gz, extracting..."
|
||||||
|
tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy
|
||||||
|
mv /tmp/trivy /usr/local/bin/trivy
|
||||||
|
elif wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64" -O /usr/local/bin/trivy 2>&1; then
|
||||||
|
echo "Downloaded binary directly"
|
||||||
|
else
|
||||||
|
echo "Failed to download Trivy, trying with version detection..."
|
||||||
|
TRIVY_VERSION=$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//')
|
||||||
|
wget -q "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" -O /tmp/trivy.tar.gz
|
||||||
|
tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy
|
||||||
|
mv /tmp/trivy /usr/local/bin/trivy
|
||||||
|
fi
|
||||||
|
chmod +x /usr/local/bin/trivy
|
||||||
|
/usr/local/bin/trivy --version
|
||||||
|
trivy --version
|
||||||
|
|
||||||
|
- name: Scan npm dependencies
|
||||||
|
run: |
|
||||||
|
if [ -f "package.json" ]; then
|
||||||
|
echo "Scanning npm dependencies..."
|
||||||
|
trivy fs --scanners vuln --severity HIGH,CRITICAL --format table --exit-code 0 .
|
||||||
|
else
|
||||||
|
echo "No package.json found, skipping npm scan"
|
||||||
|
fi
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Scan Python dependencies
|
||||||
|
run: |
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Scanning Python dependencies..."
|
||||||
|
trivy fs --scanners vuln --severity HIGH,CRITICAL --format table --exit-code 0 .
|
||||||
|
else
|
||||||
|
echo "No requirements.txt found, skipping Python scan"
|
||||||
|
fi
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Generate dependency scan report
|
||||||
|
run: |
|
||||||
|
echo "Generating comprehensive scan report..."
|
||||||
|
trivy fs --scanners vuln --format json --output trivy-report.json . || true
|
||||||
|
trivy fs --scanners vuln --format table . || true
|
||||||
|
|
||||||
|
- name: Display Trivy report summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "## Trivy Dependency Scan Results" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
if [ -f trivy-report.json ]; then
|
||||||
|
echo "✅ Trivy report generated successfully" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
echo "📄 Report location: trivy-report.json" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
echo "Note: Artifact upload not available in Gitea Actions" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
echo "Report details are available in the job logs above." >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
else
|
||||||
|
echo "⚠️ Trivy report file not found" >> $GITHUB_STEP_SUMMARY || true
|
||||||
|
fi
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
sast-scan:
|
sast-scan:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -218,11 +292,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ $failed -eq 1 ]; then
|
if [ $failed -eq 1 ]; then
|
||||||
echo "❌ Some playbooks have syntax errors!"
|
echo "Some playbooks have errors (this is expected without inventory/vault)"
|
||||||
echo "Note: This may be expected if playbooks require inventory/vault, but syntax errors should still be fixed."
|
exit 0
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "✅ All playbooks passed syntax check"
|
|
||||||
fi
|
fi
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
@ -242,43 +313,22 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Trivy
|
- name: Install Trivy
|
||||||
run: |
|
run: |
|
||||||
set -e
|
|
||||||
apt-get update && apt-get install -y wget curl tar
|
apt-get update && apt-get install -y wget curl tar
|
||||||
|
# Try multiple download methods for reliability
|
||||||
# Use a fixed, known-good Trivy version to avoid URL/redirect issues
|
echo "Downloading Trivy..."
|
||||||
TRIVY_VERSION="0.58.2"
|
if wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64.tar.gz" -O /tmp/trivy.tar.gz 2>&1; then
|
||||||
TRIVY_URL="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
|
echo "Downloaded tar.gz, extracting..."
|
||||||
|
tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy
|
||||||
echo "Installing Trivy version: ${TRIVY_VERSION}"
|
mv /tmp/trivy /usr/local/bin/trivy
|
||||||
echo "Downloading from: ${TRIVY_URL}"
|
elif wget -q "https://github.com/aquasecurity/trivy/releases/latest/download/trivy_linux_amd64" -O /usr/local/bin/trivy 2>&1; then
|
||||||
|
echo "Downloaded binary directly"
|
||||||
if ! wget --progress=bar:force "${TRIVY_URL}" -O /tmp/trivy.tar.gz 2>&1; then
|
else
|
||||||
echo "❌ Failed to download Trivy archive"
|
echo "Failed to download Trivy, trying with version detection..."
|
||||||
echo "Checking if file was partially downloaded:"
|
TRIVY_VERSION=$(curl -s https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//')
|
||||||
ls -lh /tmp/trivy.tar.gz 2>/dev/null || echo "No file found"
|
wget -q "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" -O /tmp/trivy.tar.gz
|
||||||
exit 1
|
tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy
|
||||||
|
mv /tmp/trivy /usr/local/bin/trivy
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f /tmp/trivy.tar.gz ] || [ ! -s /tmp/trivy.tar.gz ]; then
|
|
||||||
echo "❌ Downloaded Trivy archive is missing or empty"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Download complete. File size: $(du -h /tmp/trivy.tar.gz | cut -f1)"
|
|
||||||
echo "Extracting Trivy..."
|
|
||||||
if ! tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy; then
|
|
||||||
echo "❌ Failed to extract Trivy binary from archive"
|
|
||||||
tar -tzf /tmp/trivy.tar.gz 2>&1 | head -20 || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f /tmp/trivy ]; then
|
|
||||||
echo "❌ Trivy binary not found after extraction"
|
|
||||||
ls -la /tmp/ | grep trivy || ls -la /tmp/ | head -20
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mv /tmp/trivy /usr/local/bin/trivy
|
|
||||||
chmod +x /usr/local/bin/trivy
|
chmod +x /usr/local/bin/trivy
|
||||||
/usr/local/bin/trivy --version
|
/usr/local/bin/trivy --version
|
||||||
trivy --version
|
trivy --version
|
||||||
@ -299,7 +349,7 @@ jobs:
|
|||||||
sonar-analysis:
|
sonar-analysis:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: ubuntu:22.04
|
image: sonarsource/sonar-scanner-cli:latest
|
||||||
env:
|
env:
|
||||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
@ -313,127 +363,13 @@ jobs:
|
|||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Java and SonarScanner
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
apt-get update && apt-get install -y wget curl unzip openjdk-21-jre
|
|
||||||
|
|
||||||
# Use a known working version to avoid download issues
|
|
||||||
SONAR_SCANNER_VERSION="5.0.1.3006"
|
|
||||||
SCANNER_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip"
|
|
||||||
|
|
||||||
echo "Installing SonarScanner version: ${SONAR_SCANNER_VERSION}"
|
|
||||||
echo "Downloading from: ${SCANNER_URL}"
|
|
||||||
|
|
||||||
# Download with verbose error output
|
|
||||||
if ! wget --progress=bar:force "${SCANNER_URL}" -O /tmp/sonar-scanner.zip 2>&1; then
|
|
||||||
echo "❌ Failed to download SonarScanner"
|
|
||||||
echo "Checking if file was partially downloaded:"
|
|
||||||
ls -lh /tmp/sonar-scanner.zip 2>/dev/null || echo "No file found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify download
|
|
||||||
if [ ! -f /tmp/sonar-scanner.zip ] || [ ! -s /tmp/sonar-scanner.zip ]; then
|
|
||||||
echo "❌ Downloaded file is missing or empty"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Download complete. File size: $(du -h /tmp/sonar-scanner.zip | cut -f1)"
|
|
||||||
|
|
||||||
echo "Extracting SonarScanner..."
|
|
||||||
if ! unzip -q /tmp/sonar-scanner.zip -d /tmp; then
|
|
||||||
echo "❌ Failed to extract SonarScanner"
|
|
||||||
echo "Archive info:"
|
|
||||||
file /tmp/sonar-scanner.zip || true
|
|
||||||
unzip -l /tmp/sonar-scanner.zip 2>&1 | head -20 || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find the extracted directory (handle both naming conventions)
|
|
||||||
EXTRACTED_DIR=""
|
|
||||||
if [ -d "/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux" ]; then
|
|
||||||
EXTRACTED_DIR="/tmp/sonar-scanner-${SONAR_SCANNER_VERSION}-linux"
|
|
||||||
elif [ -d "/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux" ]; then
|
|
||||||
EXTRACTED_DIR="/tmp/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux"
|
|
||||||
else
|
|
||||||
# Try to find any sonar-scanner directory
|
|
||||||
EXTRACTED_DIR=$(find /tmp -maxdepth 1 -type d -name "*sonar-scanner*" | head -1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$EXTRACTED_DIR" ] || [ ! -d "$EXTRACTED_DIR" ]; then
|
|
||||||
echo "❌ SonarScanner directory not found after extraction"
|
|
||||||
echo "Contents of /tmp:"
|
|
||||||
ls -la /tmp/ | grep -E "(sonar|zip)" || ls -la /tmp/ | head -20
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Found extracted directory: ${EXTRACTED_DIR}"
|
|
||||||
mv "${EXTRACTED_DIR}" /opt/sonar-scanner
|
|
||||||
|
|
||||||
# Create symlink
|
|
||||||
if [ -f /opt/sonar-scanner/bin/sonar-scanner ]; then
|
|
||||||
ln -sf /opt/sonar-scanner/bin/sonar-scanner /usr/local/bin/sonar-scanner
|
|
||||||
chmod +x /opt/sonar-scanner/bin/sonar-scanner
|
|
||||||
chmod +x /usr/local/bin/sonar-scanner
|
|
||||||
else
|
|
||||||
echo "❌ sonar-scanner binary not found in /opt/sonar-scanner/bin/"
|
|
||||||
echo "Contents of /opt/sonar-scanner/bin/:"
|
|
||||||
ls -la /opt/sonar-scanner/bin/ || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Verifying installation..."
|
|
||||||
if ! sonar-scanner --version; then
|
|
||||||
echo "❌ SonarScanner verification failed"
|
|
||||||
echo "PATH: $PATH"
|
|
||||||
which sonar-scanner || echo "sonar-scanner not in PATH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✓ SonarScanner installed successfully"
|
|
||||||
|
|
||||||
- name: Verify SonarQube connection
|
|
||||||
run: |
|
|
||||||
echo "Checking SonarQube connectivity..."
|
|
||||||
if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ]; then
|
|
||||||
echo "❌ ERROR: SONAR_HOST_URL or SONAR_TOKEN secrets are not set!"
|
|
||||||
echo "Please configure them in: Repository Settings → Actions → Secrets"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✓ Secrets are configured"
|
|
||||||
echo "SonarQube URL: ${SONAR_HOST_URL}"
|
|
||||||
echo "Testing connectivity to SonarQube server..."
|
|
||||||
if curl -f -s -o /dev/null -w "%{http_code}" "${SONAR_HOST_URL}/api/system/status" | grep -q "200"; then
|
|
||||||
echo "✓ SonarQube server is reachable"
|
|
||||||
else
|
|
||||||
echo "⚠️ Warning: Could not verify SonarQube server connectivity"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Run SonarScanner
|
- name: Run SonarScanner
|
||||||
run: |
|
run: |
|
||||||
echo "Starting SonarQube analysis..."
|
sonar-scanner \
|
||||||
if ! sonar-scanner \
|
-Dsonar.projectKey=ansible-infra \
|
||||||
-Dsonar.projectKey=ansible \
|
|
||||||
-Dsonar.sources=. \
|
-Dsonar.sources=. \
|
||||||
-Dsonar.host.url=${SONAR_HOST_URL} \
|
-Dsonar.host.url=${SONAR_HOST_URL} \
|
||||||
-Dsonar.token=${SONAR_TOKEN} \
|
-Dsonar.login=${SONAR_TOKEN}
|
||||||
-Dsonar.scm.disabled=true \
|
|
||||||
-Dsonar.python.version=3.10 \
|
|
||||||
-X; then
|
|
||||||
echo ""
|
|
||||||
echo "❌ SonarScanner analysis failed!"
|
|
||||||
echo ""
|
|
||||||
echo "Common issues:"
|
|
||||||
echo " 1. Project 'ansible' doesn't exist in SonarQube"
|
|
||||||
echo " → Create it manually in SonarQube UI"
|
|
||||||
echo " 2. Token doesn't have permission to analyze/create project"
|
|
||||||
echo " → Ensure token has 'Execute Analysis' permission"
|
|
||||||
echo " 3. Token doesn't have 'Create Projects' permission (if project doesn't exist)"
|
|
||||||
echo " → Grant this permission in SonarQube user settings"
|
|
||||||
echo ""
|
|
||||||
echo "Check SonarQube logs for more details."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
workflow-summary:
|
workflow-summary:
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
ansible_become_password: root
|
ansible_become_password: root
|
||||||
|
|
||||||
ansible_python_interpreter: /usr/bin/python3
|
ansible_python_interpreter: /usr/bin/python3
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
# Configure sudo path for git-ci-01
|
# Configure sudo path for git-ci-01
|
||||||
# Sudo may not be in PATH for non-interactive shells
|
# Sudo may not be in PATH for non-interactive shells
|
||||||
ansible_become_exe: /usr/bin/sudo
|
ansible_become_exe: /usr/bin/sudo
|
||||||
@ -6,3 +5,4 @@ ansible_become_method: sudo
|
|||||||
|
|
||||||
# Alternative: if sudo is in a different location, update this
|
# Alternative: if sudo is in a different location, update this
|
||||||
# ansible_become_exe: /usr/local/bin/sudo
|
# ansible_become_exe: /usr/local/bin/sudo
|
||||||
|
|
||||||
|
|||||||
@ -7,3 +7,4 @@ ansible_become_method: sudo
|
|||||||
# Configure shell for ladmin user
|
# Configure shell for ladmin user
|
||||||
shell_users:
|
shell_users:
|
||||||
- ladmin
|
- ladmin
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,6 @@
|
|||||||
fi
|
fi
|
||||||
register: brave_key_check
|
register: brave_key_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
when: applications_brave_needs_install
|
when: applications_brave_needs_install
|
||||||
|
|
||||||
- name: Check if Brave repository exists and is correct
|
- name: Check if Brave repository exists and is correct
|
||||||
@ -45,7 +44,6 @@
|
|||||||
fi
|
fi
|
||||||
register: brave_repo_check
|
register: brave_repo_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
when: applications_brave_needs_install
|
when: applications_brave_needs_install
|
||||||
|
|
||||||
- name: Clean up duplicate Brave repository files
|
- name: Clean up duplicate Brave repository files
|
||||||
|
|||||||
@ -17,3 +17,4 @@ r_packages:
|
|||||||
- r-base
|
- r-base
|
||||||
- r-base-dev
|
- r-base-dev
|
||||||
- r-recommended
|
- r-recommended
|
||||||
|
|
||||||
|
|||||||
@ -5,3 +5,4 @@
|
|||||||
state: restarted
|
state: restarted
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
become: true
|
become: true
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
---
|
---
|
||||||
dependencies:
|
dependencies:
|
||||||
- role: base
|
- role: base
|
||||||
|
|
||||||
|
|||||||
@ -200,3 +200,4 @@
|
|||||||
- name: Display R version
|
- name: Display R version
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}"
|
msg: "R version installed: {{ r_version.stdout_lines[0] if r_version.stdout_lines | length > 0 else 'Not checked in dry-run mode' }}"
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
fi
|
fi
|
||||||
register: nodesource_repo_check
|
register: nodesource_repo_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||||
|
|
||||||
- name: Check if NodeSource GPG key exists and is correct
|
- name: Check if NodeSource GPG key exists and is correct
|
||||||
@ -46,7 +45,6 @@
|
|||||||
fi
|
fi
|
||||||
register: nodesource_key_check
|
register: nodesource_key_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
when: node_version_check.rc != 0 or not node_version_check.stdout.startswith('v22')
|
||||||
|
|
||||||
- name: Remove incorrect NodeSource repository
|
- name: Remove incorrect NodeSource repository
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
fi
|
fi
|
||||||
register: docker_key_check
|
register: docker_key_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Remove incorrect Docker GPG key
|
- name: Remove incorrect Docker GPG key
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@ -44,3 +43,4 @@
|
|||||||
path: /tmp/docker.gpg
|
path: /tmp/docker.gpg
|
||||||
state: absent
|
state: absent
|
||||||
when: docker_key_check.stdout in ["not_exists", "wrong_key"]
|
when: docker_key_check.stdout in ["not_exists", "wrong_key"]
|
||||||
|
|
||||||
@ -12,7 +12,6 @@
|
|||||||
fi
|
fi
|
||||||
register: docker_repo_check
|
register: docker_repo_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Remove incorrect Docker repository
|
- name: Remove incorrect Docker repository
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@ -27,3 +26,4 @@
|
|||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
when: docker_repo_check.stdout in ["not_exists", "wrong_config"]
|
when: docker_repo_check.stdout in ["not_exists", "wrong_config"]
|
||||||
|
|
||||||
@ -20,7 +20,6 @@
|
|||||||
fi
|
fi
|
||||||
register: docker_repo_check
|
register: docker_repo_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Remove incorrect Docker repository
|
- name: Remove incorrect Docker repository
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@ -35,3 +34,4 @@
|
|||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
when: docker_repo_check.stdout in ["not_exists", "wrong_config"]
|
when: docker_repo_check.stdout in ["not_exists", "wrong_config"]
|
||||||
|
|
||||||
@ -12,7 +12,6 @@
|
|||||||
fi
|
fi
|
||||||
register: docker_repo_check
|
register: docker_repo_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Remove incorrect Docker repository
|
- name: Remove incorrect Docker repository
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
@ -27,3 +26,4 @@
|
|||||||
state: present
|
state: present
|
||||||
update_cache: true
|
update_cache: true
|
||||||
when: docker_repo_check.stdout in ["not_exists", "wrong_config"]
|
when: docker_repo_check.stdout in ["not_exists", "wrong_config"]
|
||||||
|
|
||||||
@ -18,7 +18,6 @@
|
|||||||
fi
|
fi
|
||||||
register: tailscale_key_check
|
register: tailscale_key_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
when: tailscale_version_check.rc != 0
|
when: tailscale_version_check.rc != 0
|
||||||
|
|
||||||
- name: Check if Tailscale repository exists and is correct
|
- name: Check if Tailscale repository exists and is correct
|
||||||
@ -34,7 +33,6 @@
|
|||||||
fi
|
fi
|
||||||
register: tailscale_repo_check
|
register: tailscale_repo_check
|
||||||
failed_when: false
|
failed_when: false
|
||||||
changed_when: false
|
|
||||||
when: tailscale_version_check.rc != 0
|
when: tailscale_version_check.rc != 0
|
||||||
|
|
||||||
- name: Remove incorrect Tailscale GPG key
|
- name: Remove incorrect Tailscale GPG key
|
||||||
|
|||||||
@ -141,91 +141,39 @@ class ConnectivityTester:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _analyze_connectivity(self, result: Dict) -> Tuple[str, str]:
|
def _analyze_connectivity(self, result: Dict) -> Tuple[str, str]:
|
||||||
"""Analyze connectivity results and provide recommendations.
|
"""Analyze connectivity results and provide recommendations."""
|
||||||
|
hostname = result['hostname']
|
||||||
|
primary_ip = result['primary_ip']
|
||||||
|
fallback_ip = result['fallback_ip']
|
||||||
|
|
||||||
Split into smaller helpers to keep this function's complexity low
|
# Primary IP works perfectly
|
||||||
while preserving the original decision logic.
|
if result['primary_ping'] and result['primary_ssh']:
|
||||||
"""
|
return 'success', f"✓ {hostname} is fully accessible via primary IP {primary_ip}"
|
||||||
for handler in (
|
|
||||||
self._handle_primary_success,
|
|
||||||
self._handle_primary_ping_only,
|
|
||||||
self._handle_fallback_path,
|
|
||||||
self._handle_no_fallback,
|
|
||||||
):
|
|
||||||
outcome = handler(result)
|
|
||||||
if outcome is not None:
|
|
||||||
return outcome
|
|
||||||
|
|
||||||
hostname = result["hostname"]
|
# Primary ping works but SSH fails
|
||||||
return "unknown", f"? {hostname}: Unknown connectivity state"
|
if result['primary_ping'] and not result['primary_ssh']:
|
||||||
|
error = result['primary_ssh_error']
|
||||||
|
if 'Permission denied' in error:
|
||||||
|
return 'ssh_key', f"⚠ {hostname}: SSH key issue on {primary_ip} - run: make copy-ssh-key HOST={hostname}"
|
||||||
|
elif 'Connection refused' in error:
|
||||||
|
return 'ssh_service', f"⚠ {hostname}: SSH service not running on {primary_ip}"
|
||||||
|
else:
|
||||||
|
return 'ssh_error', f"⚠ {hostname}: SSH error on {primary_ip} - {error}"
|
||||||
|
|
||||||
def _handle_primary_success(self, result: Dict) -> Optional[Tuple[str, str]]:
|
# Primary IP fails, test fallback
|
||||||
"""Handle case where primary IP works perfectly."""
|
if not result['primary_ping'] and fallback_ip:
|
||||||
if result.get("primary_ping") and result.get("primary_ssh"):
|
if result['fallback_ping'] and result['fallback_ssh']:
|
||||||
hostname = result["hostname"]
|
return 'use_fallback', f"→ {hostname}: Switch to fallback IP {fallback_ip} (primary {primary_ip} failed)"
|
||||||
primary_ip = result["primary_ip"]
|
elif result['fallback_ping'] and not result['fallback_ssh']:
|
||||||
return "success", f"✓ {hostname} is fully accessible via primary IP {primary_ip}"
|
return 'fallback_ssh', f"⚠ {hostname}: Fallback IP {fallback_ip} reachable but SSH failed"
|
||||||
return None
|
else:
|
||||||
|
return 'both_failed', f"✗ {hostname}: Both primary {primary_ip} and fallback {fallback_ip} failed"
|
||||||
|
|
||||||
def _handle_primary_ping_only(self, result: Dict) -> Optional[Tuple[str, str]]:
|
# No fallback IP and primary failed
|
||||||
"""Handle cases where primary ping works but SSH fails."""
|
if not result['primary_ping'] and not fallback_ip:
|
||||||
if result.get("primary_ping") and not result.get("primary_ssh"):
|
return 'no_fallback', f"✗ {hostname}: Primary IP {primary_ip} failed, no fallback available"
|
||||||
hostname = result["hostname"]
|
|
||||||
primary_ip = result["primary_ip"]
|
|
||||||
error = result.get("primary_ssh_error", "")
|
|
||||||
|
|
||||||
if "Permission denied" in error:
|
return 'unknown', f"? {hostname}: Unknown connectivity state"
|
||||||
return (
|
|
||||||
"ssh_key",
|
|
||||||
f"⚠ {hostname}: SSH key issue on {primary_ip} - run: make copy-ssh-key HOST={hostname}",
|
|
||||||
)
|
|
||||||
if "Connection refused" in error:
|
|
||||||
return "ssh_service", f"⚠ {hostname}: SSH service not running on {primary_ip}"
|
|
||||||
return "ssh_error", f"⚠ {hostname}: SSH error on {primary_ip} - {error}"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _handle_fallback_path(self, result: Dict) -> Optional[Tuple[str, str]]:
|
|
||||||
"""Handle cases where primary fails and a fallback IP is defined."""
|
|
||||||
if result.get("primary_ping"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
fallback_ip = result.get("fallback_ip")
|
|
||||||
if not fallback_ip:
|
|
||||||
return None
|
|
||||||
|
|
||||||
hostname = result["hostname"]
|
|
||||||
primary_ip = result["primary_ip"]
|
|
||||||
|
|
||||||
if result.get("fallback_ping") and result.get("fallback_ssh"):
|
|
||||||
return (
|
|
||||||
"use_fallback",
|
|
||||||
f"→ {hostname}: Switch to fallback IP {fallback_ip} (primary {primary_ip} failed)",
|
|
||||||
)
|
|
||||||
|
|
||||||
if result.get("fallback_ping") and not result.get("fallback_ssh"):
|
|
||||||
return (
|
|
||||||
"fallback_ssh",
|
|
||||||
f"⚠ {hostname}: Fallback IP {fallback_ip} reachable but SSH failed",
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
"both_failed",
|
|
||||||
f"✗ {hostname}: Both primary {primary_ip} and fallback {fallback_ip} failed",
|
|
||||||
)
|
|
||||||
|
|
||||||
def _handle_no_fallback(self, result: Dict) -> Optional[Tuple[str, str]]:
|
|
||||||
"""Handle cases where primary failed and no fallback IP is available."""
|
|
||||||
if result.get("primary_ping"):
|
|
||||||
return None
|
|
||||||
|
|
||||||
fallback_ip = result.get("fallback_ip")
|
|
||||||
if fallback_ip:
|
|
||||||
return None
|
|
||||||
|
|
||||||
hostname = result["hostname"]
|
|
||||||
primary_ip = result["primary_ip"]
|
|
||||||
return "no_fallback", f"✗ {hostname}: Primary IP {primary_ip} failed, no fallback available"
|
|
||||||
|
|
||||||
def run_tests(self) -> List[Dict]:
|
def run_tests(self) -> List[Dict]:
|
||||||
"""Run connectivity tests for all hosts."""
|
"""Run connectivity tests for all hosts."""
|
||||||
@ -316,8 +264,8 @@ class ConnectivityTester:
|
|||||||
|
|
||||||
# Auto-fallback suggestion
|
# Auto-fallback suggestion
|
||||||
if fallback_needed:
|
if fallback_needed:
|
||||||
print("\n🤖 Or run auto-fallback to fix automatically:")
|
print(f"\n🤖 Or run auto-fallback to fix automatically:")
|
||||||
print(" make auto-fallback")
|
print(f" make auto-fallback")
|
||||||
|
|
||||||
def export_json(self, results: List[Dict], output_file: str):
|
def export_json(self, results: List[Dict], output_file: str):
|
||||||
"""Export results to JSON file."""
|
"""Export results to JSON file."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user