github.com/fafucoder/cilium@v1.6.11/cilium/cmd/cleanup.go (about) 1 // Copyright 2017 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cmd 16 17 import ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "strings" 23 "syscall" 24 25 "github.com/cilium/cilium/common" 26 "github.com/cilium/cilium/pkg/bpf" 27 "github.com/cilium/cilium/pkg/defaults" 28 "github.com/cilium/cilium/pkg/maps/tunnel" 29 "github.com/cilium/cilium/pkg/netns" 30 "github.com/cilium/cilium/pkg/option" 31 32 "github.com/spf13/cobra" 33 "github.com/spf13/viper" 34 "github.com/vishvananda/netlink" 35 ) 36 37 // configCmd represents the config command 38 var cleanupCmd = &cobra.Command{ 39 Use: "cleanup", 40 Short: "Reset the agent state", 41 Run: func(cmd *cobra.Command, args []string) { 42 common.RequireRootPrivilege("cleanup") 43 runCleanup() 44 }, 45 } 46 47 var ( 48 cleanAll bool 49 cleanBPF bool 50 force bool 51 ) 52 53 const ( 54 allFlagName = "all-state" 55 bpfFlagName = "bpf-state" 56 forceFlagName = "force" 57 58 cleanCiliumEnvVar = "CLEAN_CILIUM_STATE" 59 cleanBpfEnvVar = "CLEAN_CILIUM_BPF_STATE" 60 ) 61 62 const ( 63 ciliumLinkPrefix = "cilium_" 64 ciliumNetNSPrefix = "cilium-" 65 hostLinkPrefix = "lxc" 66 hostLinkLen = len(hostLinkPrefix + "XXXXX") 67 cniConfigV1 = "/etc/cni/net.d/10-cilium-cni.conf" 68 cniConfigV2 = "/etc/cni/net.d/00-cilium-cni.conf" 69 cniConfigV3 = "/etc/cni/net.d/05-cilium-cni.conf" 70 ) 71 72 func init() { 73 rootCmd.AddCommand(cleanupCmd) 74 75 cleanupCmd.Flags().BoolVarP(&cleanAll, allFlagName, "", false, "Remove all cilium state") 76 cleanupCmd.Flags().BoolVarP(&cleanBPF, bpfFlagName, "", false, "Remove BPF state") 77 cleanupCmd.Flags().BoolVarP(&force, forceFlagName, "f", false, "Skip confirmation") 78 79 option.BindEnv(allFlagName) 80 option.BindEnv(bpfFlagName) 81 82 bindEnv(cleanCiliumEnvVar, cleanCiliumEnvVar) 83 bindEnv(cleanBpfEnvVar, cleanBpfEnvVar) 84 85 if err := viper.BindPFlags(cleanupCmd.Flags()); err != nil { 86 Fatalf("viper failed to bind to flags: %v\n", err) 87 } 88 } 89 90 func bindEnv(flagName string, envName string) { 91 if err := viper.BindEnv(flagName, envName); err != nil { 92 Fatalf("Unable to bind flag %s to env variable %s: %s", flagName, envName, err) 93 } 94 } 95 96 // cleanupFunc represents a function to cleanup specific state items. 97 type cleanupFunc func() error 98 99 // cleanup represents a set of cleanup actions. 100 type cleanup interface { 101 // whatWillBeRemoved contains descriptions of 102 // the removed state. 103 whatWillBeRemoved() []string 104 // cleanupFuncs contains a set of functions that 105 // carry out cleanup actions. 106 cleanupFuncs() []cleanupFunc 107 } 108 109 // bpfCleanup represents cleanup actions for BPF state. 110 type bpfCleanup struct{} 111 112 func (c bpfCleanup) whatWillBeRemoved() []string { 113 return []string{ 114 fmt.Sprintf("all BPF maps in %s containing '%s' and '%s'", 115 bpf.MapPrefixPath(), ciliumLinkPrefix, tunnel.MapName), 116 fmt.Sprintf("mounted bpffs at %s", bpf.GetMapRoot()), 117 } 118 } 119 120 func (c bpfCleanup) cleanupFuncs() []cleanupFunc { 121 return []cleanupFunc{ 122 removeAllMaps, 123 } 124 } 125 126 // ciliumCleanup represents cleanup actions for non-BPF state. 127 type ciliumCleanup struct { 128 routes map[int]netlink.Route 129 links map[int]netlink.Link 130 netNSs []string 131 } 132 133 func newCiliumCleanup() ciliumCleanup { 134 routes, links, err := findRoutesAndLinks() 135 if err != nil { 136 fmt.Fprintf(os.Stderr, "Error: %s\n", err) 137 } 138 139 netNSs, err := netns.ListNamedNetNSWithPrefix(ciliumNetNSPrefix) 140 if err != nil { 141 fmt.Fprintf(os.Stderr, "Error: %s\n", err) 142 } 143 144 return ciliumCleanup{routes, links, netNSs} 145 } 146 147 func (c ciliumCleanup) whatWillBeRemoved() []string { 148 toBeRemoved := []string{ 149 fmt.Sprintf("mounted cgroupv2 at %s", defaults.DefaultCgroupRoot), 150 fmt.Sprintf("library code in %s", defaults.LibraryPath), 151 fmt.Sprintf("endpoint state in %s", defaults.RuntimePath), 152 fmt.Sprintf("CNI configuration at %s, %s, %s", 153 cniConfigV1, cniConfigV2, cniConfigV3), 154 } 155 156 if len(c.routes) > 0 { 157 section := "routes\n" 158 for _, v := range c.routes { 159 section += fmt.Sprintf("%v\n", v) 160 } 161 toBeRemoved = append(toBeRemoved, section) 162 } 163 164 if len(c.links) > 0 { 165 section := "links\n" 166 for _, v := range c.links { 167 section += fmt.Sprintf("%v\n", v) 168 } 169 toBeRemoved = append(toBeRemoved, section) 170 } 171 172 if len(c.netNSs) > 0 { 173 section := "network namespaces\n" 174 for _, n := range c.netNSs { 175 section += fmt.Sprintf("%s\n", n) 176 } 177 toBeRemoved = append(toBeRemoved, section) 178 } 179 180 return toBeRemoved 181 } 182 183 func (c ciliumCleanup) cleanupFuncs() []cleanupFunc { 184 cleanupRoutesAndLinks := func() error { 185 return removeRoutesAndLinks(c.routes, c.links) 186 } 187 188 cleanupNamedNetNSs := func() error { 189 return removeNamedNetNSs(c.netNSs) 190 } 191 192 return []cleanupFunc{ 193 unmountCgroup, 194 removeDirs, 195 removeCNI, 196 cleanupRoutesAndLinks, 197 cleanupNamedNetNSs, 198 } 199 } 200 201 func runCleanup() { 202 // Abort if the agent is running, err == nil is handled correctly by Stat. 203 if _, err := os.Stat(defaults.PidFilePath); !os.IsNotExist(err) { 204 fmt.Fprintf(os.Stderr, "Agent should not be running when cleaning up\n"+ 205 "Found pidfile %s\n", defaults.PidFilePath) 206 os.Exit(1) 207 } 208 209 cleanAll = viper.GetBool(allFlagName) || viper.GetBool(cleanCiliumEnvVar) 210 cleanBPF = viper.GetBool(bpfFlagName) || viper.GetBool(cleanBpfEnvVar) 211 212 // if no flags are specifed then clean all 213 if (cleanAll || cleanBPF) == false { 214 cleanAll = true 215 } 216 217 var cleanups []cleanup 218 219 if cleanAll || cleanBPF { 220 cleanups = append(cleanups, bpfCleanup{}) 221 } 222 223 if cleanAll { 224 cleanups = append(cleanups, newCiliumCleanup()) 225 } 226 227 if len(cleanups) == 0 { 228 return 229 } 230 231 showWhatWillBeRemoved(cleanups) 232 if !force && !confirmCleanup() { 233 return 234 } 235 236 var cleanupFuncs []cleanupFunc 237 for _, cleanup := range cleanups { 238 cleanupFuncs = append(cleanupFuncs, cleanup.cleanupFuncs()...) 239 } 240 241 // ENOENT and similar errors are ignored. Should print all other 242 // errors seen, but continue. So that one remove function does not 243 // prevent the remaining from running. 244 for _, clean := range cleanupFuncs { 245 if err := clean(); err != nil { 246 fmt.Fprintf(os.Stderr, "Error: %s\n", err) 247 } 248 } 249 } 250 251 func showWhatWillBeRemoved(cleanups []cleanup) { 252 var toBeRemoved []string 253 254 for _, cleanup := range cleanups { 255 toBeRemoved = append(toBeRemoved, cleanup.whatWillBeRemoved()...) 256 } 257 258 warning := "Warning: Destructive operation. You are about to remove:\n" 259 for _, warn := range toBeRemoved { 260 warning += fmt.Sprintf("- %s\n", warn) 261 } 262 263 fmt.Printf(warning) 264 } 265 266 func confirmCleanup() bool { 267 fmt.Printf("The command is non-revertible, do you want to continue [y/N]?\n") 268 var res string 269 fmt.Scanln(&res) 270 return res == "y" 271 } 272 273 func removeCNI() error { 274 os.Remove(cniConfigV1) 275 os.Remove(cniConfigV2) 276 277 err := os.Remove(cniConfigV3) 278 if os.IsNotExist(err) { 279 return nil 280 } 281 return err 282 } 283 284 func unmountCgroup() error { 285 cgroupRoot := defaults.DefaultCgroupRoot 286 cgroupRootStat, err := os.Stat(cgroupRoot) 287 if err != nil { 288 return nil 289 } else if !cgroupRootStat.IsDir() { 290 return fmt.Errorf("%s is a file which is not a directory", cgroupRoot) 291 } 292 293 log.Info("Trying to unmount ", cgroupRoot) 294 if err := syscall.Unmount(cgroupRoot, syscall.MNT_FORCE); err != nil { 295 return fmt.Errorf("Failed to unmount %s: %s", cgroupRoot, err) 296 } 297 return nil 298 } 299 300 func removeDirs() error { 301 dirs := []string{defaults.RuntimePath, defaults.LibraryPath} 302 for _, dir := range dirs { 303 // In the unlikely case one of the constants is the root directory, abort. 304 if dir == "/" { 305 return fmt.Errorf("will not remove root directory %s", dir) 306 } 307 if err := os.RemoveAll(dir); err != nil { 308 if os.IsNotExist(err) { 309 continue 310 } 311 return err 312 } 313 } 314 return nil 315 } 316 317 func removeAllMaps() error { 318 mapDir := bpf.MapPrefixPath() 319 maps, err := ioutil.ReadDir(mapDir) 320 if err != nil { 321 if os.IsNotExist(err) { 322 return nil 323 } 324 return err 325 } 326 327 for _, m := range maps { 328 name := m.Name() 329 // Skip non Cilium looking maps 330 if !strings.HasPrefix(name, ciliumLinkPrefix) && name != tunnel.MapName { 331 continue 332 } 333 if err = os.Remove(filepath.Join(mapDir, name)); err != nil { 334 return err 335 } 336 fmt.Printf("removed map %s\n", m.Name()) 337 } 338 return err 339 } 340 341 func linkMatch(linkName string) bool { 342 return strings.HasPrefix(linkName, ciliumLinkPrefix) || 343 strings.HasPrefix(linkName, hostLinkPrefix) && len(linkName) == hostLinkLen 344 } 345 346 func findRoutesAndLinks() (map[int]netlink.Route, map[int]netlink.Link, error) { 347 routesToRemove := map[int]netlink.Route{} 348 linksToRemove := map[int]netlink.Link{} 349 350 if routes, err := netlink.RouteList(nil, netlink.FAMILY_V4); err == nil { 351 for _, r := range routes { 352 link, err := netlink.LinkByIndex(r.LinkIndex) 353 if err != nil { 354 if strings.Contains(err.Error(), "Link not found") { 355 continue 356 } 357 return routesToRemove, linksToRemove, err 358 } 359 360 linkName := link.Attrs().Name 361 if !linkMatch(linkName) { 362 continue 363 } 364 routesToRemove[r.LinkIndex] = r 365 linksToRemove[link.Attrs().Index] = link 366 } 367 } 368 369 if links, err := netlink.LinkList(); err == nil { 370 for _, link := range links { 371 linkName := link.Attrs().Name 372 if !linkMatch(linkName) { 373 continue 374 } 375 linksToRemove[link.Attrs().Index] = link 376 } 377 } 378 return routesToRemove, linksToRemove, nil 379 } 380 381 func removeRoutesAndLinks(routes map[int]netlink.Route, links map[int]netlink.Link) error { 382 for _, route := range routes { 383 if err := netlink.RouteDel(&route); err != nil { 384 return err 385 } 386 fmt.Printf("removed route %v\n", route) 387 } 388 389 for _, link := range links { 390 if err := netlink.LinkDel(link); err != nil { 391 if strings.Contains(err.Error(), "Link not found") || 392 strings.Contains(err.Error(), "no such device") { 393 continue 394 } 395 return err 396 } 397 fmt.Printf("removed link %s\n", link.Attrs().Name) 398 } 399 return nil 400 } 401 402 func removeNamedNetNSs(netNSs []string) error { 403 for _, n := range netNSs { 404 if err := netns.RemoveNetNSWithName(n); err != nil { 405 if strings.Contains(err.Error(), "No such file") { 406 continue 407 } 408 return err 409 } 410 fmt.Printf("removed network namespace %s\n", n) 411 } 412 return nil 413 }