Skip to content

Commit ec0e90f

Browse files
Merge pull request #17479 from sjenning/issue-17443
Automatic merge from submit-queue (batch tested with PRs 17932, 18037, 17479, 18051, 18052). ex: dockergc: various fixes Fixes #17443 xref https://bugzilla.redhat.com/show_bug.cgi?id=1532966 @legionus @dmage
2 parents c87e48d + 3547f93 commit ec0e90f

File tree

4 files changed

+128
-77
lines changed

4 files changed

+128
-77
lines changed

contrib/completions/bash/oc

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12069,22 +12069,6 @@ _oc_ex_dockergc()
1206912069
local_nonpersistent_flags+=("--image-gc-low-threshold=")
1207012070
flags+=("--minimum-ttl-duration=")
1207112071
local_nonpersistent_flags+=("--minimum-ttl-duration=")
12072-
flags+=("--no-headers")
12073-
local_nonpersistent_flags+=("--no-headers")
12074-
flags+=("--output=")
12075-
two_word_flags+=("-o")
12076-
local_nonpersistent_flags+=("--output=")
12077-
flags+=("--show-all")
12078-
flags+=("-a")
12079-
local_nonpersistent_flags+=("--show-all")
12080-
flags+=("--show-labels")
12081-
local_nonpersistent_flags+=("--show-labels")
12082-
flags+=("--sort-by=")
12083-
local_nonpersistent_flags+=("--sort-by=")
12084-
flags+=("--template=")
12085-
flags_with_completion+=("--template")
12086-
flags_completion+=("_filedir")
12087-
local_nonpersistent_flags+=("--template=")
1208812072
flags+=("--as=")
1208912073
flags+=("--as-group=")
1209012074
flags+=("--cache-dir=")

contrib/completions/zsh/oc

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12211,22 +12211,6 @@ _oc_ex_dockergc()
1221112211
local_nonpersistent_flags+=("--image-gc-low-threshold=")
1221212212
flags+=("--minimum-ttl-duration=")
1221312213
local_nonpersistent_flags+=("--minimum-ttl-duration=")
12214-
flags+=("--no-headers")
12215-
local_nonpersistent_flags+=("--no-headers")
12216-
flags+=("--output=")
12217-
two_word_flags+=("-o")
12218-
local_nonpersistent_flags+=("--output=")
12219-
flags+=("--show-all")
12220-
flags+=("-a")
12221-
local_nonpersistent_flags+=("--show-all")
12222-
flags+=("--show-labels")
12223-
local_nonpersistent_flags+=("--show-labels")
12224-
flags+=("--sort-by=")
12225-
local_nonpersistent_flags+=("--sort-by=")
12226-
flags+=("--template=")
12227-
flags_with_completion+=("--template")
12228-
flags_completion+=("_filedir")
12229-
local_nonpersistent_flags+=("--template=")
1223012214
flags+=("--as=")
1223112215
flags+=("--as-group=")
1223212216
flags+=("--cache-dir=")
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package dockergc
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
dockerapi "github.com/docker/engine-api/client"
8+
dockertypes "github.com/docker/engine-api/types"
9+
)
10+
11+
type dockerClient struct {
12+
// timeout is the timeout of short running docker operations.
13+
timeout time.Duration
14+
// docker API client
15+
client *dockerapi.Client
16+
}
17+
18+
func newDockerClient(timeout time.Duration) (*dockerClient, error) {
19+
client, err := dockerapi.NewEnvClient()
20+
if err != nil {
21+
return nil, err
22+
}
23+
return &dockerClient{
24+
client: client,
25+
timeout: timeout,
26+
}, nil
27+
}
28+
29+
func clientErr(ctx context.Context, err error) error {
30+
if ctx.Err() != nil {
31+
return ctx.Err()
32+
}
33+
return err
34+
}
35+
36+
func (c *dockerClient) getTimeoutContext() (context.Context, context.CancelFunc) {
37+
return context.WithTimeout(context.Background(), c.timeout)
38+
}
39+
40+
func (c *dockerClient) Info() (*dockertypes.Info, error) {
41+
ctx, cancel := c.getTimeoutContext()
42+
defer cancel()
43+
info, err := c.client.Info(ctx)
44+
if err := clientErr(ctx, err); err != nil {
45+
return nil, err
46+
}
47+
return &info, nil
48+
}
49+
50+
func (c *dockerClient) ContainerList(options dockertypes.ContainerListOptions) ([]dockertypes.Container, error) {
51+
ctx, cancel := c.getTimeoutContext()
52+
defer cancel()
53+
containers, err := c.client.ContainerList(ctx, options)
54+
if err := clientErr(ctx, err); err != nil {
55+
return nil, err
56+
}
57+
return containers, nil
58+
}
59+
60+
func (c *dockerClient) ContainerRemove(id string, opts dockertypes.ContainerRemoveOptions) error {
61+
ctx, cancel := c.getTimeoutContext()
62+
defer cancel()
63+
err := c.client.ContainerRemove(ctx, id, opts)
64+
return clientErr(ctx, err)
65+
}
66+
67+
func (c *dockerClient) ImageList(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) {
68+
ctx, cancel := c.getTimeoutContext()
69+
defer cancel()
70+
images, err := c.client.ImageList(ctx, opts)
71+
if err := clientErr(ctx, err); err != nil {
72+
return nil, err
73+
}
74+
return images, nil
75+
}
76+
77+
func (c *dockerClient) ImageRemove(image string, opts dockertypes.ImageRemoveOptions) error {
78+
ctx, cancel := c.getTimeoutContext()
79+
defer cancel()
80+
_, err := c.client.ImageRemove(ctx, image, opts)
81+
return clientErr(ctx, err)
82+
}

pkg/oc/experimental/dockergc/dockergc.go

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dockergc
22

33
import (
4-
"context"
54
"fmt"
65
"io"
76
"os"
@@ -11,15 +10,14 @@ import (
1110
"strings"
1211
"time"
1312

13+
"github.com/golang/glog"
1414
"github.com/spf13/cobra"
1515

1616
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
1717
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
1818

19-
dockerapi "github.com/docker/engine-api/client"
2019
dockertypes "github.com/docker/engine-api/types"
2120
dockerfilters "github.com/docker/engine-api/types/filters"
22-
configcmd "github.com/openshift/origin/pkg/config/cmd"
2321
"github.com/openshift/origin/pkg/oc/cli/util/clientcmd"
2422
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2523
)
@@ -31,12 +29,14 @@ const (
3129

3230
var (
3331
DefaultMinimumGCAge = metav1.Duration{Duration: time.Hour}
32+
33+
dockerTimeout = time.Duration(2 * time.Minute)
3434
)
3535

3636
// DockerGCConfigCmdOptions are options supported by the dockergc admin command.
3737
type dockerGCConfigCmdOptions struct {
38-
Action configcmd.BulkAction
39-
38+
// DryRun is true if the command was invoked with --dry-run=true
39+
DryRun bool
4040
// MinimumGCAge is the minimum age for a container or unused image before
4141
// it is garbage collected.
4242
MinimumGCAge metav1.Duration
@@ -68,10 +68,7 @@ var (
6868

6969
func NewCmdDockerGCConfig(f *clientcmd.Factory, parentName, name string, out, errout io.Writer) *cobra.Command {
7070
options := &dockerGCConfigCmdOptions{
71-
Action: configcmd.BulkAction{
72-
Out: out,
73-
ErrOut: errout,
74-
},
71+
DryRun: false,
7572
MinimumGCAge: DefaultMinimumGCAge,
7673
ImageGCHighThresholdPercent: DefaultImageGCHighThresholdPercent,
7774
ImageGCLowThresholdPercent: DefaultImageGCLowThresholdPercent,
@@ -93,8 +90,7 @@ func NewCmdDockerGCConfig(f *clientcmd.Factory, parentName, name string, out, er
9390
cmd.Flags().DurationVar(&options.MinimumGCAge.Duration, "minimum-ttl-duration", options.MinimumGCAge.Duration, "Minimum age for a container or unused image before it is garbage collected. Examples: '300ms', '10s' or '2h45m'.")
9491
cmd.Flags().Int32Var(&options.ImageGCHighThresholdPercent, "image-gc-high-threshold", options.ImageGCHighThresholdPercent, "The percent of disk usage after which image garbage collection is always run.")
9592
cmd.Flags().Int32Var(&options.ImageGCLowThresholdPercent, "image-gc-low-threshold", options.ImageGCLowThresholdPercent, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to.")
96-
97-
options.Action.BindForOutput(cmd.Flags())
93+
cmd.Flags().BoolVar(&options.DryRun, "dry-run", options.DryRun, "Run in single-pass mode with no effect.")
9894

9995
return cmd
10096
}
@@ -150,8 +146,8 @@ func parseDockerTimestamp(s string) (time.Time, error) {
150146
return time.Parse(time.RFC3339Nano, s)
151147
}
152148

153-
func doGarbageCollection(ctx context.Context, client *dockerapi.Client, options *dockerGCConfigCmdOptions, rootDir string) error {
154-
fmt.Println("gathering disk usage data")
149+
func doGarbageCollection(client *dockerClient, options *dockerGCConfigCmdOptions, rootDir string) error {
150+
glog.Infof("gathering disk usage data")
155151
capacityBytes, usageBytes, err := getRootDirInfo(rootDir)
156152
if err != nil {
157153
return err
@@ -160,97 +156,100 @@ func doGarbageCollection(ctx context.Context, client *dockerapi.Client, options
160156
highThresholdBytes := capacityBytes * int64(options.ImageGCHighThresholdPercent) / 100
161157
lowThresholdBytes := capacityBytes * int64(options.ImageGCLowThresholdPercent) / 100
162158
if usageBytes < highThresholdBytes {
163-
fmt.Printf("usage is under high threshold (%vMB < %vMB)\n", bytesToMB(usageBytes), bytesToMB(highThresholdBytes))
159+
glog.Infof("usage is under high threshold (%vMB < %vMB)", bytesToMB(usageBytes), bytesToMB(highThresholdBytes))
164160
return nil
165161
}
166162

167163
attemptToFreeBytes := usageBytes - lowThresholdBytes
168164
freedBytes := int64(0)
169-
fmt.Printf("usage exceeds high threshold (%vMB > %vMB), attempting to free %vMB\n", bytesToMB(usageBytes), bytesToMB(highThresholdBytes), bytesToMB(attemptToFreeBytes))
165+
glog.Infof("usage exceeds high threshold (%vMB > %vMB), attempting to free %vMB", bytesToMB(usageBytes), bytesToMB(highThresholdBytes), bytesToMB(attemptToFreeBytes))
170166

171167
// conatiners
172168
exitedFilter := dockerfilters.NewArgs()
173169
exitedFilter.Add("status", "exited")
174-
containers, err := client.ContainerList(ctx, dockertypes.ContainerListOptions{All: true, Filter: exitedFilter})
175-
if ctx.Err() == context.DeadlineExceeded {
176-
return ctx.Err()
177-
}
170+
containers, err := client.ContainerList(dockertypes.ContainerListOptions{All: true, Filter: exitedFilter})
178171
if err != nil {
179172
return err
180173
}
181-
fmt.Println(len(containers), "exited containers found")
174+
glog.Infof("%d exited containers found", len(containers))
182175
sort.Sort(oldestContainersFirst(containers))
183176
for _, c := range containers {
184177
if freedBytes > attemptToFreeBytes {
185-
fmt.Printf("usage is below low threshold, freed %vMB\n", bytesToMB(freedBytes))
178+
glog.Infof("usage is below low threshold, freed %vMB", bytesToMB(freedBytes))
186179
return nil
187180
}
188181
age := time.Now().Sub(time.Unix(c.Created, 0))
189182
if age < options.MinimumGCAge.Duration {
190-
fmt.Println("remaining containers are too young")
183+
glog.Infof("remaining containers are too young")
191184
break
192185
}
193-
fmt.Printf("removing container %v (size: %v, age: %v)\n", c.ID, c.SizeRw, age)
194-
err := client.ContainerRemove(ctx, c.ID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
186+
glog.Infof("removing container %v (size: %v, age: %v)", c.ID, c.SizeRw, age)
187+
var err error
188+
if !options.DryRun {
189+
err = client.ContainerRemove(c.ID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
190+
}
195191
if err != nil {
196-
fmt.Printf("unable to remove container: %v", err)
192+
glog.Infof("unable to remove container: %v", err)
197193
} else {
198194
freedBytes += c.SizeRw
199195
}
200196
}
201197

202198
// images
203-
images, err := client.ImageList(ctx, dockertypes.ImageListOptions{})
204-
if ctx.Err() == context.DeadlineExceeded {
205-
return ctx.Err()
206-
}
199+
images, err := client.ImageList(dockertypes.ImageListOptions{})
207200
if err != nil {
208201
return err
209202
}
210203
sort.Sort(oldestImagesFirst(images))
204+
glog.Infof("%d images found", len(images))
211205
for _, i := range images {
212206
if freedBytes > attemptToFreeBytes {
213-
fmt.Printf("usage is below low threshold, freed %vMB\n", bytesToMB(freedBytes))
207+
glog.Infof("usage is below low threshold, freed %vMB", bytesToMB(freedBytes))
214208
return nil
215209
}
216210
// filter openshift infra images
217211
if len(i.RepoTags) > 0 {
218212
if strings.HasPrefix(i.RepoTags[0], "registry.ops.openshift.com/openshift3") ||
219213
strings.HasPrefix(i.RepoTags[0], "docker.io/openshift") {
220-
fmt.Println("skipping infra image", i.RepoTags[0])
214+
glog.Infof("skipping infra image: %v", i.RepoTags[0])
221215
continue
222216
}
223217
}
224218
// filter young images
225219
age := time.Now().Sub(time.Unix(i.Created, 0))
226220
if age < options.MinimumGCAge.Duration {
227-
fmt.Println("remaining images are too young")
221+
glog.Infof("remaining images are too young")
228222
break
229223
}
230-
fmt.Printf("removing image %v (size: %v, age: %v)\n", i.ID, i.Size, age)
231-
_, err := client.ImageRemove(ctx, i.ID, dockertypes.ImageRemoveOptions{PruneChildren: true})
224+
glog.Infof("removing image %v (size: %v, age: %v)", i.ID, i.Size, age)
225+
var err error
226+
if !options.DryRun {
227+
err = client.ImageRemove(i.ID, dockertypes.ImageRemoveOptions{PruneChildren: true})
228+
}
232229
if err != nil {
233-
fmt.Printf("unable to remove container: %v", err)
230+
glog.Infof("unable to remove image: %v", err)
234231
} else {
235232
freedBytes += i.Size
236233
}
237234
}
235+
glog.Infof("unable to get below low threshold, %vMB freed", bytesToMB(freedBytes))
238236

239237
return nil
240238
}
241239

242240
// Run runs the dockergc command.
243241
func Run(f *clientcmd.Factory, options *dockerGCConfigCmdOptions, cmd *cobra.Command, args []string) error {
244-
fmt.Println("docker build garbage collection daemon")
245-
fmt.Printf("MinimumGCAge: %v, ImageGCHighThresholdPercent: %v, ImageGCLowThresholdPercent: %v\n", options.MinimumGCAge, options.ImageGCHighThresholdPercent, options.ImageGCLowThresholdPercent)
246-
client, err := dockerapi.NewEnvClient()
242+
glog.Infof("docker build garbage collection daemon")
243+
if options.DryRun {
244+
glog.Infof("Running in dry-run mode")
245+
}
246+
glog.Infof("MinimumGCAge: %v, ImageGCHighThresholdPercent: %v, ImageGCLowThresholdPercent: %v", options.MinimumGCAge, options.ImageGCHighThresholdPercent, options.ImageGCLowThresholdPercent)
247+
client, err := newDockerClient(dockerTimeout)
247248
if err != nil {
248249
return err
249250
}
250-
timeout := time.Duration(2 * time.Minute)
251-
ctx, cancel := context.WithTimeout(context.Background(), timeout)
252-
defer cancel()
253-
info, err := client.Info(ctx)
251+
252+
info, err := client.Info()
254253
if err != nil {
255254
return err
256255
}
@@ -263,11 +262,13 @@ func Run(f *clientcmd.Factory, options *dockerGCConfigCmdOptions, cmd *cobra.Com
263262
}
264263

265264
for {
266-
err := doGarbageCollection(ctx, client, options, rootDir)
265+
err := doGarbageCollection(client, options, rootDir)
267266
if err != nil {
268-
return err
267+
glog.Errorf("garbage collection attempt failed: %v", err)
268+
}
269+
if options.DryRun {
270+
return nil
269271
}
270272
<-time.After(time.Minute)
271-
return nil
272273
}
273274
}

0 commit comments

Comments
 (0)