You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
snapd-extra-utils/snapd-seed-glue/cleanup.go

165 lines
6.4 KiB

// Copyright (C) 2024 Simon Quigley <tsimonq2@ubuntu.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/snapcore/snapd/store"
)
// cleanUpFiles removes partial, old, and orphaned snap and assertion files from the download and assertions directories.
func cleanUpFiles(snapsDir string, assertionsDir string) {
verboseLog("Starting cleanup process...")
// Load the seed.yaml data
seedData := loadSeedData()
// Create a map of valid snap and assertion files based on seed.yaml
validSnaps := make(map[string]bool)
validAssertions := make(map[string]bool)
// Populate valid snaps and assertions from the seed.yaml data
for _, snap := range seedData.Snaps {
// Ensure correct extraction of revision
revision := extractRevisionFromFile(snap.File)
if revision == "" {
verboseLog("Failed to extract revision from file name: %s", snap.File)
continue
}
snapFileName := fmt.Sprintf("%s_%s.snap", snap.Name, revision)
assertionFileName := fmt.Sprintf("%s_%s.assert", snap.Name, revision)
validSnaps[snapFileName] = true
validAssertions[assertionFileName] = true
}
// Log valid snaps and assertions
verboseLog("Valid Snaps: %v", validSnaps)
verboseLog("Valid Assertions: %v", validAssertions)
// Remove outdated or partial snap files
files, err := os.ReadDir(snapsDir)
if err != nil {
verboseLog("Error reading snaps directory for cleanup: %v", err)
} else {
for _, file := range files {
filePath := filepath.Join(snapsDir, file.Name())
if strings.HasSuffix(file.Name(), ".partial") || strings.HasSuffix(file.Name(), ".delta") {
verboseLog("Removing partial/delta file: %s\n", filePath)
if err := os.Remove(filePath); err != nil {
verboseLog("Failed to remove file %s: %v", filePath, err)
} else if verbose {
verboseLog("Removed partial/delta file: %s", filePath)
}
} else if strings.HasSuffix(file.Name(), ".snap") {
if !validSnaps[file.Name()] {
verboseLog("Removing outdated or orphaned snap file: %s\n", filePath)
if err := os.Remove(filePath); err != nil {
verboseLog("Failed to remove snap file %s: %v", filePath, err)
} else if verbose {
verboseLog("Removed snap file: %s", filePath)
}
} else {
verboseLog("Snap file %s is valid and retained.\n", file.Name())
}
}
}
}
// Remove orphaned assertion files
files, err = os.ReadDir(assertionsDir)
if err != nil {
verboseLog("Error reading assertions directory for cleanup: %v", err)
} else {
for _, file := range files {
filePath := filepath.Join(assertionsDir, file.Name())
if strings.HasSuffix(file.Name(), ".assert") {
if !validAssertions[file.Name()] {
verboseLog("Removing orphaned assertion file: %s\n", filePath)
if err := os.Remove(filePath); err != nil {
verboseLog("Failed to remove assertion file %s: %v", filePath, err)
} else if verbose {
verboseLog("Removed assertion file: %s", filePath)
}
} else {
verboseLog("Assertion file %s is valid and retained.\n", file.Name())
}
}
}
}
verboseLog("Cleanup process completed.")
}
// removeOrphanedFiles deletes the assertion and snap file corresponding to the removed snap.
func removeOrphanedFiles(snapName string, revision int, assertionsDir string, snapsDir string) {
assertionFilePath := filepath.Join(assertionsDir, fmt.Sprintf("%s_%d.assert", snapName, revision))
snapFilePath := filepath.Join(snapsDir, fmt.Sprintf("%s_%d.snap", snapName, revision))
if fileExists(assertionFilePath) {
err := os.Remove(assertionFilePath)
if err != nil {
verboseLog("Failed to remove assertion file %s: %v", assertionFilePath, err)
} else {
verboseLog("Removed assertion file: %s", assertionFilePath)
}
} else {
verboseLog("Assertion file %s does not exist. No action taken.", assertionFilePath)
}
if fileExists(snapFilePath) {
err := os.Remove(snapFilePath)
if err != nil {
verboseLog("Failed to remove snap file %s: %v", snapFilePath, err)
} else {
verboseLog("Removed snap file: %s", snapFilePath)
}
} else {
verboseLog("Snap file %s does not exist. No action taken.", snapFilePath)
}
}
// cleanUpCurrentSnaps removes snaps from currentSnaps that are not marked as required.
func cleanUpCurrentSnaps(assertionsDir string, snapsDir string) {
var filteredSnaps []*store.CurrentSnap
for _, snap := range currentSnaps {
if requiredSnaps[snap.InstanceName] {
filteredSnaps = append(filteredSnaps, snap)
} else {
verboseLog("Removing unnecessary snap: %s\n", snap.InstanceName)
removeOrphanedFiles(snap.InstanceName, snap.Revision.N, assertionsDir, snapsDir)
}
}
currentSnaps = filteredSnaps
// Log the updated currentSnaps
verboseLog("Filtered currentSnaps after cleanup:")
for _, snap := range currentSnaps {
verboseLog("- %s_%d.snap", snap.InstanceName, snap.Revision.N)
}
}
// removeStateJson removes the state.json file if it exists
func removeStateJson(stateJsonPath string) {
if _, err := os.Stat(stateJsonPath); err == nil {
if err := os.Remove(stateJsonPath); err != nil {
verboseLog("Failed to remove state.json: %v", err)
} else if verbose {
verboseLog("Removed state.json at %s", stateJsonPath)
}
}
}