-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
159 lines (137 loc) · 3.79 KB
/
main.go
File metadata and controls
159 lines (137 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package main
import (
"context"
"log"
"os"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
)
var filterEnv string
var freshness int
var tickerWait time.Duration
var filterMap map[string]struct{}
func main() {
log.Println("Starting image cleaner")
log.Printf("Running with filter \"%s\", and time interval of %v and freshness of %v min", filterEnv, tickerWait, freshness)
ticker := time.NewTicker(tickerWait)
for range ticker.C {
runtime()
}
}
func init() {
// Docker Api version
_, ok := os.LookupEnv("DOCKER_API_VERSION")
if !ok {
os.Setenv("DOCKER_API_VERSION", "1.39")
}
// image filter
filterEnv, _ = os.LookupEnv("FILTER")
filterSlice := strings.Split(filterEnv, ",")
filterMap = make(map[string]struct{})
for _, f := range filterSlice {
filterMap[f] = struct{}{}
}
// for errors
var err error
// image freshness
freshString, _ := os.LookupEnv("FRESHNESS")
freshness, err = strconv.Atoi(freshString)
if err != nil {
freshness, _ = strconv.Atoi("-30")
}
if freshness > 0 {
freshness = freshness * -1
}
// ticker
tickerEnvWait, ok := os.LookupEnv("TIME_INTERVAL")
if !ok {
tickerEnvWait = "24h"
}
// var err error
tickerWait, err = time.ParseDuration(tickerEnvWait)
if err != nil {
log.Printf("Ticking with %d", tickerWait)
log.Println(err)
}
}
func runtime() {
cli, err := client.NewEnvClient()
if err != nil {
log.Println("Failed to initiate client")
panic(err)
}
log.Println("Listing images on host")
images := listDockerImages(cli)
var TotalSpaceReduced int64
freshImageTime := time.Now().Add(time.Minute * time.Duration(freshness))
log.Printf("Will skip images that were created after %v", freshness)
// prun containers
log.Println("Pruning unused containers")
pruneContainers(cli)
// Prune volumes
log.Println("Prunning unused volumes")
pruneUnusedVolumes(cli)
for _, image := range images {
if image.Created < freshImageTime.Unix() {
// Image are older than 30 minutes and can be deleted
for _, tag := range image.RepoTags {
// check if a tag is set on the image to skip delete
if _, ok := filterMap[tag]; ok {
log.Printf("Image %v, filttered out and will not be deleted", tag)
} else {
log.Printf("Cleaning image %v, with tags %v", image.ID, image.RepoTags)
reducedSpace := deleteDockerImage(image, cli)
TotalSpaceReduced = TotalSpaceReduced + reducedSpace
}
}
} else {
log.Printf("Skipping deletion of image %v, its to fresh", image.ID)
}
}
if TotalSpaceReduced > 0 {
printSize(TotalSpaceReduced)
}
}
func listDockerImages(cli *client.Client) []types.ImageSummary {
images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
if err != nil {
log.Println("Failed to list docker images on host")
log.Println(err)
}
return images
}
func deleteDockerImage(image types.ImageSummary, cli *client.Client) int64 {
// Set docker remove options
removeOptions := types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
}
// Delete image
_, err := cli.ImageRemove(context.Background(), image.ID, removeOptions)
if err != nil {
log.Printf("Can't remove image %v, image is being used by a running container", image.ID)
} else {
return image.Size
}
return 0
}
func printSize(reducedSpace int64) {
sizeInMb := float64(reducedSpace) / 1000 / 1000
log.Printf("Cleaned %.2fMB from disk", sizeInMb)
}
func pruneUnusedVolumes(cli *client.Client) {
_, volErr := cli.VolumesPrune(context.Background(), filters.Args{})
if volErr != nil {
log.Println("Failed to delete unused volumes")
}
}
func pruneContainers(cli *client.Client) {
_, pruneErr := cli.ContainersPrune(context.Background(), filters.Args{})
if pruneErr != nil {
log.Println("Failed to prune unused containers")
}
}