Update installer system for update approval functionality
Major milestone: Update installation system now works - Implemented unified installer interface with factory pattern - Created APT, DNF, and Docker installers - Integrated installer into agent command processing loop - Update approval button now actually installs packages Documentation updates: - Updated claude.md with Session 7 implementation log - Created clean, professional README.md for GitHub - Added screenshots section with 4 dashboard views - Preserved detailed development history in backup files Repository ready for GitHub alpha release with working installer functionality.
This commit is contained in:
156
aggregator-agent/internal/installer/dnf.go
Normal file
156
aggregator-agent/internal/installer/dnf.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package installer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/aggregator-project/aggregator-agent/internal/client"
|
||||
)
|
||||
|
||||
// DNFInstaller handles DNF package installations
|
||||
type DNFInstaller struct{}
|
||||
|
||||
// NewDNFInstaller creates a new DNF installer
|
||||
func NewDNFInstaller() *DNFInstaller {
|
||||
return &DNFInstaller{}
|
||||
}
|
||||
|
||||
// IsAvailable checks if DNF is available on this system
|
||||
func (i *DNFInstaller) IsAvailable() bool {
|
||||
_, err := exec.LookPath("dnf")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Install installs packages using DNF
|
||||
func (i *DNFInstaller) Install(packageName string) (*InstallResult, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Refresh package cache first
|
||||
refreshCmd := exec.Command("sudo", "dnf", "refresh", "-y")
|
||||
if output, err := refreshCmd.CombinedOutput(); err != nil {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: fmt.Sprintf("Failed to refresh DNF cache: %v\nStdout: %s", err, string(output)),
|
||||
DurationSeconds: int(time.Since(startTime).Seconds()),
|
||||
}, fmt.Errorf("dnf refresh failed: %w", err)
|
||||
}
|
||||
|
||||
// Install package
|
||||
installCmd := exec.Command("sudo", "dnf", "install", "-y", packageName)
|
||||
output, err := installCmd.CombinedOutput()
|
||||
duration := int(time.Since(startTime).Seconds())
|
||||
|
||||
if err != nil {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: fmt.Sprintf("DNF install failed: %v", err),
|
||||
Stdout: string(output),
|
||||
Stderr: "",
|
||||
ExitCode: getExitCode(err),
|
||||
DurationSeconds: duration,
|
||||
}, err
|
||||
}
|
||||
|
||||
return &InstallResult{
|
||||
Success: true,
|
||||
Stdout: string(output),
|
||||
Stderr: "",
|
||||
ExitCode: 0,
|
||||
DurationSeconds: duration,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InstallMultiple installs multiple packages using DNF
|
||||
func (i *DNFInstaller) InstallMultiple(packageNames []string) (*InstallResult, error) {
|
||||
if len(packageNames) == 0 {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: "No packages specified for installation",
|
||||
}, fmt.Errorf("no packages specified")
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
// Refresh package cache first
|
||||
refreshCmd := exec.Command("sudo", "dnf", "refresh", "-y")
|
||||
if output, err := refreshCmd.CombinedOutput(); err != nil {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: fmt.Sprintf("Failed to refresh DNF cache: %v\nStdout: %s", err, string(output)),
|
||||
DurationSeconds: int(time.Since(startTime).Seconds()),
|
||||
}, fmt.Errorf("dnf refresh failed: %w", err)
|
||||
}
|
||||
|
||||
// Install all packages in one command
|
||||
args := []string{"install", "-y"}
|
||||
args = append(args, packageNames...)
|
||||
installCmd := exec.Command("sudo", "dnf", args...)
|
||||
output, err := installCmd.CombinedOutput()
|
||||
duration := int(time.Since(startTime).Seconds())
|
||||
|
||||
if err != nil {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: fmt.Sprintf("DNF install failed: %v", err),
|
||||
Stdout: string(output),
|
||||
Stderr: "",
|
||||
ExitCode: getExitCode(err),
|
||||
DurationSeconds: duration,
|
||||
}, err
|
||||
}
|
||||
|
||||
return &InstallResult{
|
||||
Success: true,
|
||||
Stdout: string(output),
|
||||
Stderr: "",
|
||||
ExitCode: 0,
|
||||
DurationSeconds: duration,
|
||||
PackagesInstalled: packageNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Upgrade upgrades all packages using DNF
|
||||
func (i *DNFInstaller) Upgrade() (*InstallResult, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
// Refresh package cache first
|
||||
refreshCmd := exec.Command("sudo", "dnf", "refresh", "-y")
|
||||
if output, err := refreshCmd.CombinedOutput(); err != nil {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: fmt.Sprintf("Failed to refresh DNF cache: %v\nStdout: %s", err, string(output)),
|
||||
DurationSeconds: int(time.Since(startTime).Seconds()),
|
||||
}, fmt.Errorf("dnf refresh failed: %w", err)
|
||||
}
|
||||
|
||||
// Upgrade all packages
|
||||
upgradeCmd := exec.Command("sudo", "dnf", "upgrade", "-y")
|
||||
output, err := upgradeCmd.CombinedOutput()
|
||||
duration := int(time.Since(startTime).Seconds())
|
||||
|
||||
if err != nil {
|
||||
return &InstallResult{
|
||||
Success: false,
|
||||
ErrorMessage: fmt.Sprintf("DNF upgrade failed: %v", err),
|
||||
Stdout: string(output),
|
||||
Stderr: "",
|
||||
ExitCode: getExitCode(err),
|
||||
DurationSeconds: duration,
|
||||
}, err
|
||||
}
|
||||
|
||||
return &InstallResult{
|
||||
Success: true,
|
||||
Stdout: string(output),
|
||||
Stderr: "",
|
||||
ExitCode: 0,
|
||||
DurationSeconds: duration,
|
||||
Action: "upgrade",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPackageType returns type of packages this installer handles
|
||||
func (i *DNFInstaller) GetPackageType() string {
|
||||
return "dnf"
|
||||
}
|
||||
Reference in New Issue
Block a user