Add S2I support and cleanup unused files

- Add complete Source-to-Image (S2I) deployment support
- Create .s2i/ directory with assemble/run scripts and environment config
- Add openshift-s2i.yaml template for S2I deployment
- Add scripts/deploy-s2i.sh for automated S2I deployment
- Add README-S2I.md with comprehensive S2I documentation
- Update README.md and AIAgents-Support.md with S2I information
- Clean up unused files: Dockerfile.simple, HTML backups, daemonset files
- Remove unused Makefile and openshift-git-deploy.yaml
- Update kustomization.yaml to use deployment instead of daemonset
- Update undeploy-complete.sh to remove deployment instead of daemonset
- Maintain clean and organized codebase structure
This commit is contained in:
2025-10-04 08:38:55 -03:00
parent 05915251c5
commit 34f4993510
18 changed files with 965 additions and 4916 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,701 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenShift Resource Governance Tool</title>
<!-- PatternFly 6.3.1 CSS -->
<link rel="stylesheet" href="https://unpkg.com/@patternfly/patternfly@6.3.1/patternfly.css">
<link rel="stylesheet" href="https://unpkg.com/@patternfly/patternfly@6.3.1/patternfly-addons.css">
<!-- PatternFly 6.3.1 Icons -->
<link rel="stylesheet" href="https://unpkg.com/@patternfly/patternfly@6.3.1/patternfly-icons.css">
<!-- Custom styles -->
<style>
.pf-c-page__main {
--pf-c-page__main--BackgroundColor: var(--pf-global--BackgroundColor--100);
}
.workload-card {
margin-bottom: var(--pf-global--spacer--md);
}
.metric-card {
text-align: center;
}
.metric-value {
font-size: var(--pf-global--FontSize--2xl);
font-weight: var(--pf-global--FontWeight--bold);
color: var(--pf-global--primary-color--100);
}
.metric-label {
font-size: var(--pf-global--FontSize--sm);
color: var(--pf-global--Color--200);
}
.severity-critical {
--pf-c-badge--m-read--BackgroundColor: var(--pf-global--danger-color--100);
}
.severity-warning {
--pf-c-badge--m-read--BackgroundColor: var(--pf-global--warning-color--100);
}
.severity-error {
--pf-c-badge--m-read--BackgroundColor: var(--pf-global--danger-color--200);
}
.severity-info {
--pf-c-badge--m-read--BackgroundColor: var(--pf-global--info-color--100);
}
.loading-spinner {
text-align: center;
padding: var(--pf-global--spacer--xl);
}
.error-message {
color: var(--pf-global--danger-color--100);
text-align: center;
padding: var(--pf-global--spacer--lg);
}
.breadcrumb-container {
margin-bottom: var(--pf-global--spacer--md);
}
.chart-container {
height: 300px;
margin-bottom: var(--pf-global--spacer--lg);
}
.workload-details {
margin-top: var(--pf-global--spacer--lg);
}
.yaml-content {
font-family: 'Courier New', monospace;
font-size: var(--pf-global--FontSize--sm);
background-color: var(--pf-global--BackgroundColor--200);
padding: var(--pf-global--spacer--md);
border-radius: var(--pf-global--BorderRadius--sm);
white-space: pre-wrap;
overflow-x: auto;
}
</style>
</head>
<body>
<div id="app">
<!-- Page Structure -->
<div class="pf-c-page" id="page-layout-default-nav">
<!-- Header -->
<header class="pf-c-page__header">
<div class="pf-c-page__header-brand">
<div class="pf-c-page__header-brand-toggle">
<button class="pf-c-button pf-m-plain" type="button" id="nav-toggle" aria-label="Global navigation" aria-expanded="true" aria-controls="primary-nav">
<i class="fas fa-bars" aria-hidden="true"></i>
</button>
</div>
<div class="pf-c-page__header-brand-link">
<img class="pf-c-brand" src="https://www.patternfly.org/assets/images/logo__pf--reverse-on-md.svg" alt="PatternFly" />
</div>
</div>
<div class="pf-c-page__header-tools">
<div class="pf-c-page__header-tools-group">
<div class="pf-c-page__header-tools-item">
<button class="pf-c-button pf-m-plain" type="button" aria-label="Settings">
<i class="fas fa-cog" aria-hidden="true"></i>
</button>
</div>
<div class="pf-c-page__header-tools-item">
<button class="pf-c-button pf-m-plain" type="button" aria-label="Help">
<i class="fas fa-question-circle" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</header>
<!-- Sidebar -->
<div class="pf-c-page__sidebar" id="primary-nav">
<div class="pf-c-page__sidebar-body">
<nav class="pf-c-nav" id="primary-nav" aria-label="Global">
<ul class="pf-c-nav__list">
<li class="pf-c-nav__item">
<a href="#" class="pf-c-nav__link" data-section="workload-scanner">
<i class="fas fa-search" aria-hidden="true"></i>
Workload Scanner
</a>
</li>
<li class="pf-c-nav__item">
<a href="#" class="pf-c-nav__link" data-section="historical-analysis">
<i class="fas fa-chart-line" aria-hidden="true"></i>
Historical Analysis
</a>
</li>
</ul>
</nav>
</div>
</div>
<!-- Main Content -->
<main class="pf-c-page__main" tabindex="-1">
<!-- Workload Scanner Section -->
<section class="pf-c-page__main-section" id="workload-scanner-section" style="display: block;">
<div class="pf-c-page__main-breadcrumb">
<nav class="pf-c-breadcrumb" aria-label="breadcrumb">
<ol class="pf-c-breadcrumb__list">
<li class="pf-c-breadcrumb__item">
<span class="pf-c-breadcrumb__item-divider">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
<a href="#" class="pf-c-breadcrumb__link">Workload Scanner</a>
</li>
</ol>
</nav>
</div>
<div class="pf-c-page__main-section">
<div class="pf-l-grid pf-m-gutter">
<!-- Page Title -->
<div class="pf-l-grid__item pf-m-12-col">
<div class="pf-c-content">
<h1>Workload Scanner</h1>
<p>Identify and analyze workloads with resource configuration issues</p>
</div>
</div>
<!-- Summary Cards -->
<div class="pf-l-grid__item pf-m-12-col">
<div class="pf-l-grid pf-m-gutter" id="summary-cards">
<!-- Cards will be populated by JavaScript -->
</div>
</div>
<!-- Workloads Table -->
<div class="pf-l-grid__item pf-m-12-col">
<div class="pf-c-card">
<div class="pf-c-card__header">
<div class="pf-c-card__title">
<h2>Workloads with Issues</h2>
</div>
<div class="pf-c-card__actions">
<button class="pf-c-button pf-m-primary" id="refresh-workloads">
<i class="fas fa-sync-alt" aria-hidden="true"></i>
Refresh
</button>
</div>
</div>
<div class="pf-c-card__body">
<div id="workloads-table-container">
<div class="loading-spinner">
<div class="pf-c-spinner" role="progressbar" aria-label="Loading workloads">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</div>
<div>Loading workloads...</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Historical Analysis Section -->
<section class="pf-c-page__main-section" id="historical-analysis-section" style="display: none;">
<div class="pf-c-page__main-breadcrumb">
<nav class="pf-c-breadcrumb" aria-label="breadcrumb">
<ol class="pf-c-breadcrumb__list">
<li class="pf-c-breadcrumb__item">
<span class="pf-c-breadcrumb__item-divider">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
<a href="#" class="pf-c-breadcrumb__link" data-section="workload-scanner">Workload Scanner</a>
</li>
<li class="pf-c-breadcrumb__item">
<span class="pf-c-breadcrumb__item-divider">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
<span class="pf-c-breadcrumb__item-text">Historical Analysis</span>
</li>
</ol>
</nav>
</div>
<div class="pf-c-page__main-section">
<div class="pf-l-grid pf-m-gutter">
<!-- Page Title -->
<div class="pf-l-grid__item pf-m-12-col">
<div class="pf-c-content">
<h1>Historical Analysis</h1>
<p>Resource consumption analysis and historical data</p>
</div>
</div>
<!-- Workloads List -->
<div class="pf-l-grid__item pf-m-12-col">
<div class="pf-c-card">
<div class="pf-c-card__header">
<div class="pf-c-card__title">
<h2>Available Workloads</h2>
</div>
<div class="pf-c-card__actions">
<button class="pf-c-button pf-m-primary" id="refresh-historical">
<i class="fas fa-sync-alt" aria-hidden="true"></i>
Refresh
</button>
</div>
</div>
<div class="pf-c-card__body">
<div id="historical-workloads-container">
<div class="loading-spinner">
<div class="pf-c-spinner" role="progressbar" aria-label="Loading historical data">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</div>
<div>Loading historical data...</div>
</div>
</div>
</div>
</div>
</div>
<!-- Workload Details (hidden initially) -->
<div class="pf-l-grid__item pf-m-12-col" id="workload-details-container" style="display: none;">
<div class="pf-c-card">
<div class="pf-c-card__header">
<div class="pf-c-card__title">
<h2 id="workload-details-title">Workload Details</h2>
</div>
<div class="pf-c-card__actions">
<button class="pf-c-button pf-m-plain" id="close-workload-details">
<i class="fas fa-times" aria-hidden="true"></i>
Close
</button>
</div>
</div>
<div class="pf-c-card__body">
<div id="workload-details-content">
<!-- Workload details will be populated here -->
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
</div>
</div>
<!-- PatternFly 6.3.1 JavaScript -->
<script src="https://unpkg.com/@patternfly/patternfly@6.3.1/patternfly.js"></script>
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Custom JavaScript -->
<script>
// Global variables
let currentData = null;
let currentSection = 'workload-scanner';
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
});
function initializeApp() {
// Setup navigation
setupNavigation();
// Load initial data
loadWorkloadScanner();
}
function setupNavigation() {
// Sidebar navigation
const navLinks = document.querySelectorAll('.pf-c-nav__link[data-section]');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const section = this.getAttribute('data-section');
showSection(section);
});
});
// Breadcrumb navigation
const breadcrumbLinks = document.querySelectorAll('.pf-c-breadcrumb__link[data-section]');
breadcrumbLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const section = this.getAttribute('data-section');
showSection(section);
});
});
// Close workload details
document.getElementById('close-workload-details').addEventListener('click', function() {
document.getElementById('workload-details-container').style.display = 'none';
});
// Refresh buttons
document.getElementById('refresh-workloads').addEventListener('click', loadWorkloadScanner);
document.getElementById('refresh-historical').addEventListener('click', loadHistoricalAnalysis);
}
function showSection(section) {
// Hide all sections
document.querySelectorAll('.pf-c-page__main-section').forEach(sec => {
sec.style.display = 'none';
});
// Show selected section
document.getElementById(section + '-section').style.display = 'block';
// Update active nav item
document.querySelectorAll('.pf-c-nav__link').forEach(link => {
link.classList.remove('pf-m-current');
});
document.querySelector(`.pf-c-nav__link[data-section="${section}"]`).classList.add('pf-m-current');
currentSection = section;
// Load section data
if (section === 'workload-scanner') {
loadWorkloadScanner();
} else if (section === 'historical-analysis') {
loadHistoricalAnalysis();
}
}
async function loadWorkloadScanner() {
try {
showLoading('workloads-table-container');
// Load cluster status
const clusterResponse = await fetch('/api/v1/cluster/status');
const clusterData = await clusterResponse.json();
// Load validations
const validationsResponse = await fetch('/api/v1/validations');
const validationsData = await validationsResponse.json();
currentData = { cluster: clusterData, validations: validationsData };
// Update summary cards
updateSummaryCards(clusterData);
// Update workloads table
updateWorkloadsTable(validationsData);
} catch (error) {
console.error('Error loading workload scanner data:', error);
showError('workloads-table-container', 'Failed to load workload data');
}
}
async function loadHistoricalAnalysis() {
try {
showLoading('historical-workloads-container');
// Load historical data
const response = await fetch('/api/v1/historical-analysis');
const data = await response.json();
updateHistoricalWorkloads(data);
} catch (error) {
console.error('Error loading historical analysis data:', error);
showError('historical-workloads-container', 'Failed to load historical data');
}
}
function updateSummaryCards(data) {
const container = document.getElementById('summary-cards');
const cards = [
{
title: 'Total Workloads',
value: data.total_pods || 0,
icon: 'fas fa-cube',
color: 'blue'
},
{
title: 'Namespaces',
value: data.total_namespaces || 0,
icon: 'fas fa-layer-group',
color: 'green'
},
{
title: 'Critical Issues',
value: data.critical_issues || 0,
icon: 'fas fa-exclamation-triangle',
color: 'red'
},
{
title: 'Warnings',
value: data.total_warnings || 0,
icon: 'fas fa-exclamation-circle',
color: 'orange'
}
];
container.innerHTML = cards.map(card => `
<div class="pf-l-grid__item pf-m-3-col">
<div class="pf-c-card metric-card">
<div class="pf-c-card__body">
<div class="metric-value">${card.value}</div>
<div class="metric-label">
<i class="${card.icon}" aria-hidden="true"></i>
${card.title}
</div>
</div>
</div>
</div>
`).join('');
}
function updateWorkloadsTable(data) {
const container = document.getElementById('workloads-table-container');
if (!data.namespaces || data.namespaces.length === 0) {
container.innerHTML = '<div class="error-message">No workload data available</div>';
return;
}
const tableHTML = `
<div class="pf-c-table">
<table class="pf-c-table__table" role="grid" aria-label="Workloads table">
<thead>
<tr class="pf-c-table__row">
<th class="pf-c-table__th">Namespace</th>
<th class="pf-c-table__th">Pods</th>
<th class="pf-c-table__th">Issues</th>
<th class="pf-c-table__th">Severity</th>
<th class="pf-c-table__th">Actions</th>
</tr>
</thead>
<tbody>
${data.namespaces.map(namespace => `
<tr class="pf-c-table__row">
<td class="pf-c-table__td">
<strong>${namespace.namespace}</strong>
</td>
<td class="pf-c-table__td">${Object.keys(namespace.pods || {}).length}</td>
<td class="pf-c-table__td">${namespace.total_validations || 0}</td>
<td class="pf-c-table__td">
<span class="pf-c-badge severity-${getHighestSeverity(namespace)}">
${getHighestSeverity(namespace)}
</span>
</td>
<td class="pf-c-table__td">
<div class="pf-c-button-group">
<button class="pf-c-button pf-m-primary pf-m-small" onclick="analyzeWorkload('${namespace.namespace}')">
Analyze
</button>
<button class="pf-c-button pf-m-secondary pf-m-small" onclick="fixWorkload('${namespace.namespace}')">
Fix
</button>
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
container.innerHTML = tableHTML;
}
function updateHistoricalWorkloads(data) {
const container = document.getElementById('historical-workloads-container');
if (!data.workloads || data.workloads.length === 0) {
container.innerHTML = '<div class="error-message">No historical data available</div>';
return;
}
const tableHTML = `
<div class="pf-c-table">
<table class="pf-c-table__table" role="grid" aria-label="Historical workloads table">
<thead>
<tr class="pf-c-table__row">
<th class="pf-c-table__th">Workload</th>
<th class="pf-c-table__th">Namespace</th>
<th class="pf-c-table__th">CPU Usage</th>
<th class="pf-c-table__th">Memory Usage</th>
<th class="pf-c-table__th">Last Updated</th>
<th class="pf-c-table__th">Actions</th>
</tr>
</thead>
<tbody>
${data.workloads.map(workload => `
<tr class="pf-c-table__row">
<td class="pf-c-table__td">
<strong>${workload.name}</strong>
</td>
<td class="pf-c-table__td">${workload.namespace}</td>
<td class="pf-c-table__td">${workload.cpu_usage || 'N/A'}</td>
<td class="pf-c-table__td">${workload.memory_usage || 'N/A'}</td>
<td class="pf-c-table__td">${workload.last_updated || 'N/A'}</td>
<td class="pf-c-table__td">
<button class="pf-c-button pf-m-primary pf-m-small" onclick="showWorkloadDetails('${workload.name}', '${workload.namespace}')">
View Details
</button>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
container.innerHTML = tableHTML;
}
function showWorkloadDetails(workloadName, namespace) {
// Update breadcrumb
const breadcrumb = document.querySelector('#historical-analysis-section .pf-c-breadcrumb__list');
breadcrumb.innerHTML = `
<li class="pf-c-breadcrumb__item">
<span class="pf-c-breadcrumb__item-divider">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
<a href="#" class="pf-c-breadcrumb__link" data-section="workload-scanner">Workload Scanner</a>
</li>
<li class="pf-c-breadcrumb__item">
<span class="pf-c-breadcrumb__item-divider">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
<a href="#" class="pf-c-breadcrumb__link" data-section="historical-analysis">Historical Analysis</a>
</li>
<li class="pf-c-breadcrumb__item">
<span class="pf-c-breadcrumb__item-divider">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
<span class="pf-c-breadcrumb__item-text">${workloadName}</span>
</li>
`;
// Update title
document.getElementById('workload-details-title').textContent = `${workloadName} - ${namespace}`;
// Load workload details
loadWorkloadDetails(workloadName, namespace);
// Show details container
document.getElementById('workload-details-container').style.display = 'block';
}
async function loadWorkloadDetails(workloadName, namespace) {
try {
const response = await fetch(`/api/v1/historical-analysis/${namespace}/${workloadName}`);
const data = await response.json();
updateWorkloadDetails(data);
} catch (error) {
console.error('Error loading workload details:', error);
document.getElementById('workload-details-content').innerHTML =
'<div class="error-message">Failed to load workload details</div>';
}
}
function updateWorkloadDetails(data) {
const container = document.getElementById('workload-details-content');
container.innerHTML = `
<div class="pf-l-grid pf-m-gutter">
<div class="pf-l-grid__item pf-m-6-col">
<div class="pf-c-card">
<div class="pf-c-card__header">
<div class="pf-c-card__title">
<h3>CPU Usage</h3>
</div>
</div>
<div class="pf-c-card__body">
<div class="chart-container" id="cpu-chart">
<!-- CPU chart will be rendered here -->
</div>
</div>
</div>
</div>
<div class="pf-l-grid__item pf-m-6-col">
<div class="pf-c-card">
<div class="pf-c-card__header">
<div class="pf-c-card__title">
<h3>Memory Usage</h3>
</div>
</div>
<div class="pf-c-card__body">
<div class="chart-container" id="memory-chart">
<!-- Memory chart will be rendered here -->
</div>
</div>
</div>
</div>
<div class="pf-l-grid__item pf-m-12-col">
<div class="pf-c-card">
<div class="pf-c-card__header">
<div class="pf-c-card__title">
<h3>Resource Recommendations</h3>
</div>
</div>
<div class="pf-c-card__body">
<div class="yaml-content">${data.recommendations || 'No recommendations available'}</div>
</div>
</div>
</div>
</div>
`;
}
function analyzeWorkload(namespace) {
console.log('Analyzing workload:', namespace);
// TODO: Implement workload analysis
}
function fixWorkload(namespace) {
console.log('Fixing workload:', namespace);
// TODO: Implement workload fixing
}
function getHighestSeverity(namespace) {
const breakdown = namespace.severity_breakdown || {};
if (breakdown.error > 0) return 'error';
if (breakdown.warning > 0) return 'warning';
if (breakdown.info > 0) return 'info';
return 'info';
}
function showLoading(containerId) {
const container = document.getElementById(containerId);
container.innerHTML = `
<div class="loading-spinner">
<div class="pf-c-spinner" role="progressbar" aria-label="Loading">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</div>
<div>Loading...</div>
</div>
`;
}
function showError(containerId, message) {
const container = document.getElementById(containerId);
container.innerHTML = `<div class="error-message">${message}</div>`;
}
</script>
</body>
</html>