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