crowdsec/pkg/cwhub/itemremove.go
mmetc 6b0bdc5eeb
Refact pkg/cwhub: fix some known issues and reorganize files (#2616)
* bump gopkg.in/yaml.v3
* test: cannot remove local items with cscli
* test dangling links
* test: cannot install local item with cscli
* pkg/cwhub: reorg (move) functions in files
* allow hub upgrade with local items
* data download: honor Last-Modified header
* fatal -> warning when attempting to remove a local item (allows remove --all)
* cscli...inspect -o yaml|human: rename remote_path -> path
* Correct count of removed items
Still no separate counter for the --purge option, but should be clear enough
2023-11-28 23:51:51 +01:00

139 lines
3.2 KiB
Go

package cwhub
import (
"fmt"
"os"
log "github.com/sirupsen/logrus"
"slices"
)
// purge removes the actual config file that was downloaded.
func (i *Item) purge() (bool, error) {
if !i.State.Downloaded {
log.Debugf("removing %s: not downloaded -- no need to remove", i.Name)
return false, nil
}
src, err := i.downloadPath()
if err != nil {
return false, err
}
if err := os.Remove(src); err != nil {
if os.IsNotExist(err) {
log.Debugf("%s doesn't exist, no need to remove", src)
return false, nil
}
return false, fmt.Errorf("while removing file: %w", err)
}
i.State.Downloaded = false
log.Infof("Removed source file [%s]: %s", i.Name, src)
return true, nil
}
// disable removes the install link, and optionally the downloaded content.
func (i *Item) disable(purge bool, force bool) (bool, error) {
didRemove := true
err := i.removeInstallLink()
if os.IsNotExist(err) {
if !purge && !force {
link, _ := i.installPath()
return false, fmt.Errorf("link %s does not exist (override with --force or --purge)", link)
}
didRemove = false
} else if err != nil {
return false, err
}
i.State.Installed = false
didPurge := false
if purge {
if didPurge, err = i.purge(); err != nil {
return didRemove, err
}
}
ret := didRemove || didPurge
return ret, nil
}
// Remove disables the item, optionally removing the downloaded content.
func (i *Item) Remove(purge bool, force bool) (bool, error) {
if i.IsLocal() {
log.Warningf("%s is a local item, please delete manually", i.Name)
return false, nil
}
if i.State.Tainted && !force {
return false, fmt.Errorf("%s is tainted, use '--force' to remove", i.Name)
}
if !i.State.Installed && !purge {
log.Infof("removing %s: not installed -- no need to remove", i.Name)
return false, nil
}
removed := false
descendants, err := i.descendants()
if err != nil {
return false, err
}
ancestors := i.Ancestors()
for _, sub := range i.SubItems() {
if !sub.State.Installed {
continue
}
// if the sub depends on a collection that is not a direct or indirect dependency
// of the current item, it is not removed
for _, subParent := range sub.Ancestors() {
if !purge && !subParent.State.Installed {
continue
}
// the ancestor that would block the removal of the sub item is also an ancestor
// of the item we are removing, so we don't want false warnings
// (e.g. crowdsecurity/sshd-logs was not removed because it also belongs to crowdsecurity/linux,
// while we are removing crowdsecurity/sshd)
if slices.Contains(ancestors, subParent) {
continue
}
// the sub-item belongs to the item we are removing, but we already knew that
if subParent == i {
continue
}
if !slices.Contains(descendants, subParent) {
log.Infof("%s was not removed because it also belongs to %s", sub.Name, subParent.Name)
continue
}
}
subRemoved, err := sub.Remove(purge, force)
if err != nil {
return false, fmt.Errorf("unable to disable %s: %w", i.Name, err)
}
removed = removed || subRemoved
}
didDisable, err := i.disable(purge, force)
if err != nil {
return false, fmt.Errorf("while removing %s: %w", i.Name, err)
}
removed = removed || didDisable
return removed, nil
}