Skip to content

Commit

Permalink
Added option to forcibly delete versions/assets or reject probational…
Browse files Browse the repository at this point in the history
… status.

If the manifest/summary files aren't available, this probably indicates that
there's something wrong with the version directory, in which case it should be
deleted. However, the deletion/rejection endpoints rely on these files to do
various things (e.g., updating the project usage).

Setting force=true will skip the additional checks if the manifest/summary
files aren't present to ensure that the version can be forcibly deleted.
The default is still force=false so that the caller can be notified of a
problem before possibly repeating the request with force=true.
  • Loading branch information
LTLA committed Feb 2, 2025
1 parent 050259a commit 1191c46
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 138 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ This file should be JSON-formatted with the following properties:
- `project`: string containing the name of the project.
- `asset`: string containing the name of the asset.
- `version`: string containing the name of the version.
- `force` (optional): boolean indicating whether a probational version should be forcibly deleted.
Occasionally necessary if the version contains corrupted summary or manifest files,
in which case they will be deleted but the project usage will need to be refreshed manually.

On success, the relevant version is removed from the registry.
The HTTP response will contain a JSON object with the `status` property set to `SUCCESS`.
Expand Down Expand Up @@ -345,6 +348,9 @@ This file should be JSON-formatted with the following properties:

- `project`: string containing the name of the project.
- `asset`: string containing the name of the asset.
- `force` (optional): boolean indicating whether the asset should be forcibly deleted.
Occasionally necessary if the asset contains corrupted manifest files,
in which case they will be deleted but the project usage will need to be refreshed manually.

On success, the asset is deleted.
The HTTP response will contain a JSON object with the `status` property set to `SUCCESS`.
Expand All @@ -356,6 +362,9 @@ This file should be JSON-formatted with the following properties:
- `project`: string containing the name of the project.
- `asset`: string containing the name of the asset.
- `version`: string containing the name of the version.
- `force` (optional): boolean indicating whether the version should be forcibly deleted.
Occasionally necessary if the version contains corrupted summary or manifest files,
in which case they will be deleted but the project usage will need to be refreshed manually.

On success, the version is deleted.
The HTTP response will contain a JSON object with the `type` property set to `SUCCESS`.
Expand Down
84 changes: 48 additions & 36 deletions delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func deleteAssetHandler(reqpath string, globals *globalConfiguration) error {
incoming := struct {
Project *string `json:"project"`
Asset *string `json:"asset"`
Force *bool `json:"force"`
}{}
{
handle, err := os.ReadFile(reqpath)
Expand All @@ -95,6 +96,8 @@ func deleteAssetHandler(reqpath string, globals *globalConfiguration) error {
}
}

force_deletion := incoming.Force != nil && *(incoming.Force)

project_dir := filepath.Join(globals.Registry, *(incoming.Project))
if _, err := os.Stat(project_dir); errors.Is(err, os.ErrNotExist) {
return nil
Expand All @@ -109,27 +112,29 @@ func deleteAssetHandler(reqpath string, globals *globalConfiguration) error {
if _, err := os.Stat(asset_dir); errors.Is(err, os.ErrNotExist) {
return nil
}
to_free, err := computeAssetUsage(asset_dir)
if err != nil {
return fmt.Errorf("failed to compute usage for %s; %v", asset_dir, err)
}
usage, err := readUsage(project_dir)
if err != nil {
return fmt.Errorf("failed to read usage for %s; %v", project_dir, err)
asset_usage, asset_usage_err := computeAssetUsage(asset_dir)
if asset_usage_err != nil && !force_deletion {
return fmt.Errorf("failed to compute usage for %s; %v", asset_dir, asset_usage_err)
}

err = os.RemoveAll(asset_dir)
if err != nil {
return fmt.Errorf("failed to delete %s; %v", asset_dir, err)
}

usage.Total -= to_free
if usage.Total < 0 {
usage.Total = 0
}
err = dumpJson(filepath.Join(project_dir, usageFileName), &usage)
if err != nil {
return fmt.Errorf("failed to update usage for %s; %v", project_dir, err)
if asset_usage_err == nil {
project_usage, err := readUsage(project_dir)
if err != nil {
return fmt.Errorf("failed to read usage for %s; %v", project_dir, err)
}
project_usage.Total -= asset_usage
if project_usage.Total < 0 {
project_usage.Total = 0
}
err = dumpJson(filepath.Join(project_dir, usageFileName), &project_usage)
if err != nil {
return fmt.Errorf("failed to update usage for %s; %v", project_dir, err)
}
}

payload := map[string]string {
Expand Down Expand Up @@ -158,6 +163,7 @@ func deleteVersionHandler(reqpath string, globals *globalConfiguration) error {
Project *string `json:"project"`
Asset *string `json:"asset"`
Version *string `json:"version"`
Force *bool `json:"force"`
}{}
{
handle, err := os.ReadFile(reqpath)
Expand All @@ -184,6 +190,8 @@ func deleteVersionHandler(reqpath string, globals *globalConfiguration) error {
}
}

force_deletion := incoming.Force != nil && *(incoming.Force)

// We lock the project directory as (i) it's convention to lock the entire
// project even if we're mutating a single asset and (ii) we need to update
// the usage anyway so we'd have to obtain this lock eventually.
Expand All @@ -206,35 +214,40 @@ func deleteVersionHandler(reqpath string, globals *globalConfiguration) error {
if _, err := os.Stat(version_dir); errors.Is(err, os.ErrNotExist) {
return nil
}
to_free, err := computeVersionUsage(version_dir)
if err != nil {
return fmt.Errorf("failed to compute usage for %s; %v", version_dir, err)
}
usage, err := readUsage(project_dir)
if err != nil {
return fmt.Errorf("failed to read usage for %s; %v", project_dir, err)

version_usage, version_usage_err := computeVersionUsage(version_dir)
if version_usage_err != nil && !force_deletion {
return fmt.Errorf("failed to compute usage for %s; %v", version_dir, version_usage_err)
}

summ, err := readSummary(version_dir)
if err != nil {
return fmt.Errorf("failed to read summary for %s; %v", version_dir, err)
summ, summ_err := readSummary(version_dir)
if summ_err != nil && !force_deletion {
return fmt.Errorf("failed to read summary for %s; %v", version_dir, summ_err)
}

err = os.RemoveAll(version_dir)
if err != nil {
return fmt.Errorf("failed to delete %s; %v", asset_dir, err)
}

usage.Total -= to_free
if usage.Total < 0 {
usage.Total = 0
}
err = dumpJson(filepath.Join(project_dir, usageFileName), &usage)
if err != nil {
return fmt.Errorf("failed to update usage for %s; %v", project_dir, err)
if version_usage_err == nil {
project_usage, err := readUsage(project_dir)
if err != nil {
return fmt.Errorf("failed to read usage for %s; %v", project_dir, err)
}

project_usage.Total -= version_usage
if project_usage.Total < 0 {
project_usage.Total = 0
}
err = dumpJson(filepath.Join(project_dir, usageFileName), &project_usage)
if err != nil {
return fmt.Errorf("failed to update usage for %s; %v", project_dir, err)
}
}

if summ.OnProbation == nil || !(*summ.OnProbation) {
if summ_err == nil && (summ.OnProbation == nil || !(*summ.OnProbation)) {
// Only need to make a log if the version is non-probational.
prev, err := readLatest(asset_dir)
was_latest := false
if err == nil {
Expand All @@ -257,11 +270,10 @@ func deleteVersionHandler(reqpath string, globals *globalConfiguration) error {
}

// Also refreshing the latest version.
_, err = refreshLatest(asset_dir)
if err != nil {
return fmt.Errorf("failed to update the latest version for %s; %v", asset_dir, err)
_, latest_err := refreshLatest(asset_dir)
if latest_err != nil && !force_deletion {
return fmt.Errorf("failed to update the latest version for %s; %v", asset_dir, latest_err)
}

}

return nil
Expand Down
Loading

0 comments on commit 1191c46

Please sign in to comment.