github.com/abayer/test-infra@v0.0.5/images/bootstrap/barnacle/main.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /* 18 Implements cleaning up unwanted docker data root contents. 19 This is similar to `docker system prune` but tuned to our needs. 20 We use it to allow persisting base images while cleaning up everything else. 21 */ 22 23 package main 24 25 import ( 26 "context" 27 "encoding/json" 28 "log" 29 "sort" 30 "time" 31 32 "github.com/docker/docker/api/types" 33 "github.com/docker/docker/api/types/filters" 34 "github.com/docker/docker/client" 35 ) 36 37 var ( 38 // images newer than this will be deleted 39 deleteYoungerThanDuration = time.Hour * 24 * 7 40 ) 41 42 func removeAllContainers(cli *client.Client) { 43 // list all containers 44 listOptions := types.ContainerListOptions{ 45 Quiet: true, 46 All: true, 47 } 48 containers, err := cli.ContainerList(context.Background(), listOptions) 49 if err != nil { 50 log.Printf("Failed to list containers: %v\n", err) 51 return 52 } 53 54 // reverse sort by Creation time so we delete newest containers first 55 sort.Slice(containers, func(i, j int) bool { 56 return containers[i].Created > containers[j].Created 57 }) 58 59 // stop then remove (which implicitly kills) each container 60 duration := time.Second * 1 61 removeOptions := types.ContainerRemoveOptions{ 62 RemoveVolumes: true, 63 Force: true, 64 } 65 for _, container := range containers { 66 log.Printf("Stopping container: %v %s with ID: %s\n", 67 container.Names, container.Image, container.ID) 68 err = cli.ContainerStop(context.Background(), container.ID, &duration) 69 if err != nil { 70 log.Printf("Error stopping container: %v\n", err) 71 } 72 73 log.Printf("Removing container: %v %s with ID: %s\n", 74 container.Names, container.Image, container.ID) 75 err = cli.ContainerRemove(context.Background(), container.ID, removeOptions) 76 if err != nil { 77 log.Printf("Error removing container: %v\n", err) 78 } 79 } 80 } 81 82 func removeImages(cli *client.Client, images []types.ImageSummary) { 83 // reverse sort by Creation time so we delete newest images first 84 sort.Slice(images, func(i, j int) bool { 85 return images[i].Created > images[j].Created 86 }) 87 // remove each image 88 removeOptions := types.ImageRemoveOptions{ 89 Force: true, 90 PruneChildren: true, 91 } 92 for _, image := range images { 93 log.Printf("Deleting image: %v with ID: %s and size %d\n", 94 image.RepoTags, image.ID, image.Size) 95 _, err := cli.ImageRemove(context.Background(), image.ID, removeOptions) 96 if err != nil { 97 log.Printf("Failed to delete image. %v\n", err) 98 } 99 } 100 } 101 102 func removeDanglingImages(cli *client.Client) { 103 args := filters.NewArgs() 104 args.Add("dangling", "true") 105 images, err := cli.ImageList(context.Background(), types.ImageListOptions{ 106 All: false, 107 Filters: args, 108 }) 109 if err != nil { 110 log.Printf("Failed to list images: %v", err) 111 return 112 } 113 removeImages(cli, images) 114 } 115 116 func pruneVolumes(cli *client.Client) { 117 report, err := cli.VolumesPrune(context.Background(), filters.NewArgs()) 118 if err != nil { 119 log.Printf("Failed to prune volumes: %v\n", err) 120 return 121 } 122 marshalled, err := json.Marshal(report) 123 if err != nil { 124 log.Printf("Failed to marshal report: %v", err) 125 return 126 } 127 log.Printf("Volume Prune results: %s\n", marshalled) 128 } 129 130 func removeRecentImages(cli *client.Client, minimumAge time.Duration) { 131 // get images 132 images, err := cli.ImageList(context.Background(), types.ImageListOptions{ 133 All: false, 134 }) 135 if err != nil { 136 log.Printf("Failed to list images: %v", err) 137 return 138 } 139 140 // select images to delete 141 now := time.Now() 142 deleteImages := []types.ImageSummary{} 143 for _, image := range images { 144 age := now.Sub(time.Unix(image.Created, 0)) 145 // delete recently created images 146 if age <= deleteYoungerThanDuration { 147 deleteImages = append(deleteImages, image) 148 } 149 } 150 151 // delete them 152 removeImages(cli, deleteImages) 153 } 154 155 func main() { 156 log.SetPrefix("[Barnacle] ") 157 log.Println("Cleaning up Docker data root...") 158 159 // create docker client 160 cli, err := client.NewEnvClient() 161 if err != nil { 162 log.Fatalf("Failed to create Docker client: %v\n", err) 163 } 164 165 // make sure all containers are removed before removing images 166 log.Println("Removing all containers.") 167 removeAllContainers(cli) 168 169 // remove containers young enough to not likely be pulled base images 170 log.Println("Removing recently created images.") 171 removeRecentImages(cli, deleteYoungerThanDuration) 172 173 // delete remaining dangling images 174 log.Println("Pruning dangling images.") 175 removeDanglingImages(cli) 176 177 // prune volumes 178 log.Println("Pruning volumes.") 179 pruneVolumes(cli) 180 181 // all done! 182 log.Println("Done cleaning up Docker data root.") 183 }