* 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
140 lines
3.2 KiB
Go
140 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
|
|
}
|