Skip to content
Snippets Groups Projects
Verified Commit 9dea1877 authored by Geoffrey Arthaud's avatar Geoffrey Arthaud
Browse files

Added package files cleaner

parent 230c5591
Branches
Tags
No related merge requests found
......@@ -10,6 +10,7 @@
mod packages;
mod package;
mod package_files;
mod delete_file;
pub use self::packages::Packages;
pub use self::packages::PackageType;
......@@ -18,4 +19,6 @@ pub use self::packages::PackageStatus;
pub use self::package::Package;
pub use self::package_files::PackageFiles;
\ No newline at end of file
pub use self::package_files::PackageFiles;
pub use self::delete_file::DeletePackageFile;
\ No newline at end of file
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use derive_builder::Builder;
use gitlab::api::common::NameOrId;
use gitlab::api::endpoint_prelude::*;
/// Delete a package.
#[derive(Debug, Builder)]
pub struct DeletePackageFile<'a> {
/// The project to delete the package from.
#[builder(setter(into))]
project: NameOrId<'a>,
/// The ID of the package.
package: u64,
/// The ID of the file.
file: u64
}
impl<'a> DeletePackageFile<'a> {
/// Create a builder for the endpoint.
pub fn builder() -> DeletePackageFileBuilder<'a> {
DeletePackageFileBuilder::default()
}
}
impl<'a> Endpoint for DeletePackageFile<'a> {
fn method(&self) -> Method {
Method::DELETE
}
fn endpoint(&self) -> Cow<'static, str> {
format!("projects/{}/packages/{}/package_files/{}", self.project, self.package, self.file)
.into()
}
}
......@@ -125,4 +125,8 @@ pub fn input_clean_artifacts() -> Option<i64> {
} else {
None
}
}
pub fn input_clean_files() -> bool {
Confirm::new().with_prompt("Delete obsolete files ?").interact().unwrap_or(false)
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ pub mod gitlab_connection;
pub mod pipeline_analysis;
pub mod pipeline_clean;
pub mod package_analysis;
pub mod package_clean;
pub const STORAGE_LIMIT: u64 = 2_000_000_000;
pub const REPO_LIMIT: u64 = 100_000_000;
......
......@@ -32,6 +32,20 @@ pub struct PackageWithFile {
pub sorted_files: Vec<GitlabPackageFile>,
}
pub struct FileFromPackage {
pub package_id: u64,
pub file: GitlabPackageFile
}
impl FileFromPackage {
pub fn build(package: &GitlabPackage, file: GitlabPackageFile) -> FileFromPackage {
FileFromPackage {
package_id: package.id,
file
}
}
}
pub struct PackageAnalysisJob {
pub gitlab: Gitlab,
pub project: Project,
......@@ -42,7 +56,7 @@ pub struct PackageAnalysisReport {
pub project: Project,
pub packages: Vec<PackageWithFile>,
pub report_status: Vec<ReportStatus>,
pub obsolete_files: Vec<GitlabPackageFile>,
pub obsolete_files: Vec<FileFromPackage>,
pub savable_files: usize,
pub savable_bytes: u64,
}
......@@ -110,7 +124,9 @@ impl ReportJob for PackageAnalysisJob {
let obsolete_ids = detect_obsolete_files(&package, &files);
savable_files += obsolete_ids.len();
obsolete_ids.iter().for_each(|i| obsolete_files.push(files[*i].clone()));
obsolete_ids.iter()
.for_each(|i| obsolete_files.push(
FileFromPackage::build(&package, files[*i].clone())));
savable_bytes += obsolete_ids.into_iter()
.map(|i| files[i].size)
.sum::<u64>();
......
use std::sync::mpsc;
use gitlab::api::{ApiError, Query};
use human_bytes::human_bytes;
use crate::{api, Reportable, ReportPending, ReportStatus};
use crate::diagnosis::{GITLAB_SCOPE_ERROR, RemedyJob};
use crate::diagnosis::package_analysis::{FileFromPackage, PackageAnalysisReport};
pub struct PackageCleanJob {
pub package_report: PackageAnalysisReport,
}
pub struct PackageCleanReport {
pub saved_bytes: u64,
pub deleted_files: Vec<FileFromPackage>,
pub report_status: Vec<ReportStatus>,
}
impl Reportable for PackageCleanReport {
fn report(&self) -> Vec<ReportStatus> {
self.report_status.clone()
}
}
impl PackageCleanReport {
fn fatal_error(id: u64, msg: &str) -> Self {
Self {
saved_bytes: 0,
deleted_files: vec![],
report_status: vec![ReportStatus::ERROR(format!("Package {} - Error : {}", id, msg))],
}
}
}
impl RemedyJob for PackageCleanJob {
type Report = PackageCleanReport;
fn remedy(self) -> ReportPending<Self::Report> {
let (tx, rx) = mpsc::channel();
let count = self.package_report.obsolete_files.len();
ReportPending {
pending_msg: "Deleting obsolete packages files".to_string(),
job: std::thread::spawn(move || {
let mut deleted_packages_files = vec![];
for (i, file) in self.package_report.obsolete_files.into_iter().enumerate() {
let mut retry = 0;
loop {
let endpoint = api::packages::DeletePackageFile::builder()
.project(self.package_report.project.id)
.package(file.package_id)
.file(file.file.id)
.build()
.unwrap();
let query = gitlab::api::ignore(endpoint)
.query(&self.package_report.gitlab);
match query {
Ok(_) => {
deleted_packages_files.push(file);
break;
}
Err(e) => {
match e {
ApiError::Gitlab { msg } => {
return match msg.as_str() {
msg if msg.contains(GITLAB_SCOPE_ERROR) => {
PackageCleanReport::fatal_error(
file.file.id,
"Your token has insufficient privileges to delete packages")
}
other => {
PackageCleanReport::fatal_error(
file.file.id,
other)
}
}
}
ApiError::Client {source} => {
retry += 1;
if retry >= 3 {
return PackageCleanReport::fatal_error(
file.file.id,
source.to_string().as_str());
}
}
_ => {
return PackageCleanReport::fatal_error(
file.file.id,
e.to_string().as_str());
}
}
}
}
}
let _ = tx.send(i);
}
let saved_bytes = deleted_packages_files.iter().map(|f| f.file.size).sum();
PackageCleanReport {
saved_bytes,
report_status: vec![ReportStatus::OK(format!("Deleted {} packages, {} saved.",
deleted_packages_files.len(),
human_bytes(saved_bytes as f64)))],
deleted_files: deleted_packages_files,
}
}),
progress: Some(rx),
total: Some(count),
}
}
}
impl PackageCleanJob {
pub fn from(package_report: PackageAnalysisReport) -> Self {
Self {
package_report
}
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ use cli::Args;
use crate::diagnosis::{RemedyJob, Reportable, ReportJob, ReportPending};
use crate::diagnosis::gitlab_connection::ConnectionJob;
use crate::diagnosis::package_analysis::PackageAnalysisJob;
use crate::diagnosis::package_clean::PackageCleanJob;
use crate::diagnosis::pipeline_analysis::PipelineAnalysisJob;
use crate::diagnosis::pipeline_clean::PipelineCleanJob;
use crate::diagnosis::ReportStatus;
......@@ -48,6 +49,15 @@ fn main() {
// Analysis of packages
let report_pending = PackageAnalysisJob::from(&connection_data).diagnose();
let report = cli::display_report_pending(report_pending);
eprintln!("{:?}", report.obsolete_files);
let package_report = cli::display_report_pending(report_pending);
if !package_report.obsolete_files.is_empty() {
if cli::input_clean_files() {
let report_pending = PackageCleanJob::from(package_report).remedy();
let _ = cli::display_report_pending(report_pending);
} else {
cli::console_report_statuses(
&[ReportStatus::WARNING("Files deletion cancelled".to_string())],
2);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment