Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cluster: clean operations cannot cleanup monitor component files #1643

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions pkg/cluster/manager/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/pingcap/tiup/pkg/cluster/task"
"github.com/pingcap/tiup/pkg/logger/log"
"github.com/pingcap/tiup/pkg/meta"
"github.com/pingcap/tiup/pkg/set"
"github.com/pingcap/tiup/pkg/tui"
)

Expand Down Expand Up @@ -238,3 +239,26 @@ func (m *Manager) RestartCluster(name string, gOpt operator.Options, skipConfirm
log.Infof("Restarted cluster `%s` successfully", name)
return nil
}

// getMonitorHosts get the instance to ignore list if it marks itself as ignore_exporter
func getMonitorHosts(topo spec.Topology) (map[string]hostInfo, set.StringSet) {
// monitor
uniqueHosts := make(map[string]hostInfo) // host -> ssh-port, os, arch
noAgentHosts := set.NewStringSet()
topo.IterInstance(func(inst spec.Instance) {
// add the instance to ignore list if it marks itself as ignore_exporter
if inst.IgnoreMonitorAgent() {
noAgentHosts.Insert(inst.GetHost())
}

if _, found := uniqueHosts[inst.GetHost()]; !found {
uniqueHosts[inst.GetHost()] = hostInfo{
ssh: inst.GetSSHPort(),
os: inst.OS(),
arch: inst.Arch(),
}
}
})

return uniqueHosts, noAgentHosts
}
191 changes: 146 additions & 45 deletions pkg/cluster/manager/cleanup.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"context"
"fmt"
"path"
"path/filepath"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -51,51 +52,8 @@ func (m *Manager) CleanCluster(name string, gOpt operator.Options, cleanOpt oper
}

// calculate file paths to be deleted before the prompt
delFileMap := make(map[string]set.StringSet)
for _, com := range topo.ComponentsByStopOrder() {
instances := com.Instances()
retainDataRoles := set.NewStringSet(cleanOpt.RetainDataRoles...)
retainDataNodes := set.NewStringSet(cleanOpt.RetainDataNodes...)

for _, ins := range instances {
// not cleaning files of monitor agents if the instance does not have one
switch ins.ComponentName() {
case spec.ComponentNodeExporter,
spec.ComponentBlackboxExporter:
if ins.IgnoreMonitorAgent() {
continue
}
}

// Some data of instances will be retained
dataRetained := retainDataRoles.Exist(ins.ComponentName()) ||
retainDataNodes.Exist(ins.ID()) || retainDataNodes.Exist(ins.GetHost())

if dataRetained {
continue
}

dataPaths := set.NewStringSet()
logPaths := set.NewStringSet()

if cleanOpt.CleanupData && len(ins.DataDir()) > 0 {
for _, dataDir := range strings.Split(ins.DataDir(), ",") {
dataPaths.Insert(path.Join(dataDir, "*"))
}
}

if cleanOpt.CleanupLog && len(ins.LogDir()) > 0 {
for _, logDir := range strings.Split(ins.LogDir(), ",") {
logPaths.Insert(path.Join(logDir, "*.log"))
}
}

if delFileMap[ins.GetHost()] == nil {
delFileMap[ins.GetHost()] = set.NewStringSet()
}
delFileMap[ins.GetHost()].Join(logPaths).Join(dataPaths)
}
}
delFileMap := getCleanupFiles(topo,
cleanOpt.CleanupData, cleanOpt.CleanupLog, false, cleanOpt.RetainDataRoles, cleanOpt.RetainDataNodes)

if !skipConfirm {
target := ""
Expand Down Expand Up @@ -155,3 +113,146 @@ func (m *Manager) CleanCluster(name string, gOpt operator.Options, cleanOpt oper
log.Infof("Cleanup cluster `%s` successfully", name)
return nil
}

// cleanupFiles record the file that needs to be cleaned up
type cleanupFiles struct {
cleanupData bool // whether to clean up the data
cleanupLog bool // whether to clean up the log
cleanupTLS bool // whether to clean up the tls files
retainDataRoles []string // roles that don't clean up
retainDataNodes []string // roles that don't clean up
delFileMap map[string]set.StringSet
}

// getCleanupFiles get the files that need to be deleted
func getCleanupFiles(topo spec.Topology,
cleanupData, cleanupLog, cleanupTLS bool, retainDataRoles, retainDataNodes []string) map[string]set.StringSet {
c := &cleanupFiles{
cleanupData: cleanupData,
cleanupLog: cleanupLog,
cleanupTLS: cleanupTLS,
retainDataRoles: retainDataRoles,
retainDataNodes: retainDataNodes,
delFileMap: make(map[string]set.StringSet),
}

// calculate file paths to be deleted before the prompt
c.instanceCleanupFiles(topo)
c.monitorCleanupFiles(topo)

return c.delFileMap
}

// instanceCleanupFiles get the files that need to be deleted in the component
func (c *cleanupFiles) instanceCleanupFiles(topo spec.Topology) {
for _, com := range topo.ComponentsByStopOrder() {
instances := com.Instances()
retainDataRoles := set.NewStringSet(c.retainDataRoles...)
retainDataNodes := set.NewStringSet(c.retainDataNodes...)

for _, ins := range instances {
// not cleaning files of monitor agents if the instance does not have one
// may not work
switch ins.ComponentName() {
case spec.ComponentNodeExporter,
spec.ComponentBlackboxExporter:
if ins.IgnoreMonitorAgent() {
continue
}
}

// Some data of instances will be retained
dataRetained := retainDataRoles.Exist(ins.ComponentName()) ||
retainDataNodes.Exist(ins.ID()) || retainDataNodes.Exist(ins.GetHost())

if dataRetained {
continue
}

// prevent duplicate directories
dataPaths := set.NewStringSet()
logPaths := set.NewStringSet()
tlsPath := set.NewStringSet()

if c.cleanupData && len(ins.DataDir()) > 0 {
for _, dataDir := range strings.Split(ins.DataDir(), ",") {
dataPaths.Insert(path.Join(dataDir, "*"))
}
}

if c.cleanupLog && len(ins.LogDir()) > 0 {
for _, logDir := range strings.Split(ins.LogDir(), ",") {
logPaths.Insert(path.Join(logDir, "*.log"))
}
}

// clean tls data
if c.cleanupTLS && !topo.BaseTopo().GlobalOptions.TLSEnabled {
deployDir := spec.Abs(topo.BaseTopo().GlobalOptions.User, ins.DeployDir())
tlsDir := filepath.Join(deployDir, spec.TLSCertKeyDir)
tlsPath.Insert(tlsDir)
}

if c.delFileMap[ins.GetHost()] == nil {
c.delFileMap[ins.GetHost()] = set.NewStringSet()
}
c.delFileMap[ins.GetHost()].Join(logPaths).Join(dataPaths).Join(tlsPath)
}
}
}

// monitorCleanupFiles get the files that need to be deleted in the mointor
func (c *cleanupFiles) monitorCleanupFiles(topo spec.Topology) {
monitoredOptions := topo.BaseTopo().MonitoredOptions
if monitoredOptions == nil {
return
}
user := topo.BaseTopo().GlobalOptions.User

// get the host with monitor installed
uniqueHosts, noAgentHosts := getMonitorHosts(topo)
retainDataNodes := set.NewStringSet(c.retainDataNodes...)

// monitoring agents
for host := range uniqueHosts {
// determine if host don't need to delete
dataRetained := noAgentHosts.Exist(host) || retainDataNodes.Exist(host)
if dataRetained {
continue
}

deployDir := spec.Abs(user, monitoredOptions.DeployDir)

// prevent duplicate directories
dataPaths := set.NewStringSet()
logPaths := set.NewStringSet()
tlsPath := set.NewStringSet()

// data dir would be empty for components which don't need it
dataDir := monitoredOptions.DataDir
if c.cleanupData && len(dataDir) > 0 {
// the default data_dir is relative to deploy_dir
if !strings.HasPrefix(dataDir, "/") {
dataDir = filepath.Join(deployDir, dataDir)
}
dataPaths.Insert(path.Join(dataDir, "*"))
}

// log dir will always be with values, but might not used by the component
logDir := spec.Abs(user, monitoredOptions.LogDir)
if c.cleanupLog && len(logDir) > 0 {
logPaths.Insert(path.Join(logDir, "*.log"))
}

// clean tls data
if c.cleanupTLS && !topo.BaseTopo().GlobalOptions.TLSEnabled {
tlsDir := filepath.Join(deployDir, spec.TLSCertKeyDir)
tlsPath.Insert(tlsDir)
}

if c.delFileMap[host] == nil {
c.delFileMap[host] = set.NewStringSet()
}
c.delFileMap[host].Join(logPaths).Join(dataPaths).Join(tlsPath)
}
}