Fix warewulfd api behavior when deleting distribution overlay
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -159,70 +159,72 @@ func (overlay Overlay) AddFile(filePath string, content []byte, parents bool, fo
|
||||
return os.WriteFile(fullPath, content, 0o644)
|
||||
}
|
||||
|
||||
func (overlay Overlay) Delete(force bool) (err error) {
|
||||
wwlog.Info("Deleting overlay %s, force: %v", overlay.Name(), force)
|
||||
if overlay.IsDistributionOverlay() {
|
||||
return fmt.Errorf("cannot delete a distribution overlay: %s", overlay.Name())
|
||||
}
|
||||
if force {
|
||||
err := os.RemoveAll(overlay.Path())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete overlay forcely: %w", err)
|
||||
}
|
||||
} else {
|
||||
// remove rootfs at first
|
||||
if err = os.Remove(overlay.Rootfs()); err != nil {
|
||||
return fmt.Errorf("failed to delete overlay: %w", err)
|
||||
}
|
||||
if overlay.Exists() {
|
||||
if err = os.Remove(overlay.Path()); err != nil {
|
||||
return fmt.Errorf("failed to delete overlay: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteFile deletes a file or the entire overlay directory.
|
||||
// before deletion.
|
||||
func (overlay Overlay) DeleteFile(filePath string, force, cleanup bool) (err error) {
|
||||
wwlog.Info("Deleting file %s from overlay %s, force: %v, cleanup: %v", filePath, overlay.Name(), force, cleanup)
|
||||
if filePath == "" {
|
||||
if overlay.IsDistributionOverlay() {
|
||||
return fmt.Errorf("cannot delete a distribution overlay: %s", overlay.Name())
|
||||
// first check if file exists
|
||||
if !util.IsFile(overlay.File(filePath)) {
|
||||
return fmt.Errorf("file %s does not exist in overlay %s", filePath, overlay.Name())
|
||||
}
|
||||
if overlay.IsDistributionOverlay() {
|
||||
siteOverlay, err := overlay.CloneSiteOverlay()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to clone distribution overlay '%s' to site overlay: %w", overlay.Name(), err)
|
||||
}
|
||||
if force {
|
||||
err := os.RemoveAll(overlay.Path())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete overlay forcely: %w", err)
|
||||
}
|
||||
} else {
|
||||
// remove rootfs at first
|
||||
if err = os.Remove(overlay.Rootfs()); err != nil {
|
||||
return fmt.Errorf("failed to delete overlay: %w", err)
|
||||
}
|
||||
if overlay.Exists() {
|
||||
if err = os.Remove(overlay.Path()); err != nil {
|
||||
return fmt.Errorf("failed to delete overlay: %w", err)
|
||||
}
|
||||
}
|
||||
// replace the overlay with newly created siteOverlay
|
||||
overlay = siteOverlay
|
||||
}
|
||||
fullPath := overlay.File(filePath)
|
||||
if force {
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return fmt.Errorf("failed to delete file %s from overlay %s: %w", filePath, overlay.Name(), err)
|
||||
}
|
||||
} else {
|
||||
// first check if file exists
|
||||
if !util.IsFile(overlay.File(filePath)) {
|
||||
return fmt.Errorf("file %s does not exist in overlay %s", filePath, overlay.Name())
|
||||
}
|
||||
if overlay.IsDistributionOverlay() {
|
||||
siteOverlay, err := overlay.CloneSiteOverlay()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to clone distribution overlay '%s' to site overlay: %w", overlay.Name(), err)
|
||||
}
|
||||
// replace the overlay with newly created siteOverlay
|
||||
overlay = siteOverlay
|
||||
}
|
||||
fullPath := overlay.File(filePath)
|
||||
if force {
|
||||
if err := os.RemoveAll(fullPath); err != nil {
|
||||
return fmt.Errorf("failed to delete file %s from overlay %s: %w", filePath, overlay.Name(), err)
|
||||
}
|
||||
} else {
|
||||
if err := os.Remove(fullPath); err != nil {
|
||||
return fmt.Errorf("failed to delete file %s from overlay %s: %w", filePath, overlay.Name(), err)
|
||||
}
|
||||
if err := os.Remove(fullPath); err != nil {
|
||||
return fmt.Errorf("failed to delete file %s from overlay %s: %w", filePath, overlay.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if cleanup {
|
||||
// cleanup the empty parents
|
||||
i := path.Dir(fullPath)
|
||||
for i != overlay.Rootfs() {
|
||||
wwlog.Debug("Evaluating directory to remove: %s", i)
|
||||
err := os.Remove(i)
|
||||
if err != nil {
|
||||
// if the directory is not empty, we stop here
|
||||
if !os.IsNotExist(err) {
|
||||
wwlog.Debug("Could not remove directory %s: %v", i, err)
|
||||
}
|
||||
break
|
||||
if cleanup {
|
||||
// cleanup the empty parents
|
||||
i := path.Dir(fullPath)
|
||||
for i != overlay.Rootfs() {
|
||||
wwlog.Debug("Evaluating directory to remove: %s", i)
|
||||
err := os.Remove(i)
|
||||
if err != nil {
|
||||
// if the directory is not empty, we stop here
|
||||
if !os.IsNotExist(err) {
|
||||
wwlog.Debug("Could not remove directory %s: %v", i, err)
|
||||
}
|
||||
wwlog.Debug("Removed empty directory: %s", i)
|
||||
i = path.Dir(i)
|
||||
break
|
||||
}
|
||||
wwlog.Debug("Removed empty directory: %s", i)
|
||||
i = path.Dir(i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -232,7 +232,7 @@ func createOverlay() usecase.Interactor {
|
||||
func deleteOverlay() usecase.Interactor {
|
||||
type deleteOverlayInput struct {
|
||||
Name string `path:"name" required:"true" description:"Name of overlay to delete"`
|
||||
Force bool `query:"force" default:"false" description:"Whether to delete a non-empty overlay, default:'false'"`
|
||||
Force bool `query:"force" default:"false" description:"Whether to delete a non-empty or in-use overlay, default:'false'"`
|
||||
}
|
||||
|
||||
u := usecase.NewInteractor(func(ctx context.Context, input deleteOverlayInput, output *OverlayResponse) error {
|
||||
@@ -241,7 +241,11 @@ func deleteOverlay() usecase.Interactor {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isSiteOverlay := overlay_.IsSiteOverlay()
|
||||
|
||||
if overlay_.IsDistributionOverlay() {
|
||||
return status.Wrap(fmt.Errorf("distribution overlay: %s", overlay_.Name()), status.InvalidArgument)
|
||||
}
|
||||
|
||||
if !input.Force {
|
||||
if registry, err := node.New(); err != nil {
|
||||
return err
|
||||
@@ -255,11 +259,11 @@ func deleteOverlay() usecase.Interactor {
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := os.RemoveAll(overlay_.Path()); err != nil {
|
||||
if err := overlay_.Delete(input.Force); err != nil {
|
||||
return status.Wrap(fmt.Errorf("failed to remove overlay: %v", err), status.Code(409))
|
||||
}
|
||||
*output = *NewOverlayResponse(input.Name)
|
||||
output.Site = isSiteOverlay
|
||||
output.Site = overlay_.IsSiteOverlay()
|
||||
return nil
|
||||
})
|
||||
u.SetTitle("Delete an overlay")
|
||||
|
||||
@@ -108,7 +108,7 @@ var overlayTests = map[string]struct {
|
||||
response: `{"files":null, "site":true}`,
|
||||
},
|
||||
|
||||
"delete overlay": {
|
||||
"force delete site overlay": {
|
||||
initFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/test/": "",
|
||||
},
|
||||
@@ -117,6 +117,16 @@ var overlayTests = map[string]struct {
|
||||
},
|
||||
response: `{"files":[], "site":true}`,
|
||||
},
|
||||
|
||||
"force delete distribution overlay": {
|
||||
initFiles: map[string]string{
|
||||
"/usr/share/warewulf/overlays/test/email.ww": sampleTemplate,
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodDelete, serverURL+"/api/overlays/test?force=true", nil)
|
||||
},
|
||||
status: 400,
|
||||
},
|
||||
}
|
||||
|
||||
func TestOverlayAPI(t *testing.T) {
|
||||
@@ -160,8 +170,10 @@ func TestOverlayAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), tt.response) //nolint:govet // tt.response is used as a format string with special tokens
|
||||
if tt.response != "" {
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), tt.response) //nolint:govet // tt.response is used as a format string with special tokens
|
||||
}
|
||||
|
||||
for _, fileName := range tt.resultFiles {
|
||||
assert.DirExists(t, env.GetPath(fileName))
|
||||
|
||||
Reference in New Issue
Block a user