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  }