Skip to content

Commit 7d22cf6

Browse files
authored
Merge pull request #1335 from dchen1107/release-v0.23
Release v0.23.4 for kubernetes 1.3
2 parents 6607e7c + fa873f0 commit 7d22cf6

File tree

11 files changed

+155
-92
lines changed

11 files changed

+155
-92
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
### 0.23.4 (2016-06-16)
4+
- Cherry-pick release:
5+
- Check for thin_is binary in path for devicemapper when using ThinPoolWatcher
6+
- Fix uint64 overflow issue for CPU stats
7+
38
### 0.23.3 (2016-06-08)
49
- Cherry-pick release:
510
- Cap the maximum consecutive du commands

container/docker/factory.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -197,24 +197,38 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo, ignoreMetrics c
197197
)
198198

199199
if dockerStorageDriver == devicemapperStorageDriver {
200-
// If the storage drive is devicemapper, create and start a
201-
// ThinPoolWatcher to monitor the size of container CoW layers with
202-
// thin_ls.
203-
dockerThinPoolName, err := dockerutil.DockerThinPoolName(*dockerInfo)
204-
if err != nil {
205-
return fmt.Errorf("couldn't find device mapper thin pool name: %v", err)
206-
}
207-
208-
dockerMetadataDevice, err := dockerutil.DockerMetadataDevice(*dockerInfo)
209-
if err != nil {
210-
return fmt.Errorf("couldn't determine devicemapper metadata device")
200+
_, err := devicemapper.ThinLsBinaryPresent()
201+
if err == nil {
202+
// If the storage driver is devicemapper, create and start a
203+
// ThinPoolWatcher to monitor the size of container CoW layers
204+
// with thin_ls.
205+
dockerThinPoolName, err := dockerutil.DockerThinPoolName(*dockerInfo)
206+
if err != nil {
207+
return fmt.Errorf("couldn't find device mapper thin pool name: %v", err)
208+
}
209+
210+
dockerMetadataDevice, err := dockerutil.DockerMetadataDevice(*dockerInfo)
211+
if err != nil {
212+
return fmt.Errorf("couldn't determine devicemapper metadata device: %v", err)
213+
}
214+
215+
thinPoolWatcher, err = devicemapper.NewThinPoolWatcher(dockerThinPoolName, dockerMetadataDevice)
216+
if err != nil {
217+
return fmt.Errorf("couldn't create thin pool watcher: %v", err)
218+
}
219+
220+
go thinPoolWatcher.Start()
221+
} else {
222+
msg := []string{
223+
"Couldn't locate thin_ls binary; not starting thin pool watcher.",
224+
"Containers backed by thin pools will not show accurate usage.",
225+
"err: %v",
226+
}
227+
glog.Errorf(strings.Join(msg, " "), err)
211228
}
212-
213-
thinPoolWatcher = devicemapper.NewThinPoolWatcher(dockerThinPoolName, dockerMetadataDevice)
214-
go thinPoolWatcher.Start()
215229
}
216230

217-
glog.Infof("registering Docker factory")
231+
glog.Infof("Registering Docker factory")
218232
f := &dockerFactory{
219233
cgroupSubsystems: cgroupSubsystems,
220234
client: client,

devicemapper/dmsetup_client.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,26 @@ import (
2121
"github.com/golang/glog"
2222
)
2323

24-
// DmsetupClient is a low-level client for interacting with devicemapper via
25-
// the dmsetup utility.
24+
// DmsetupClient is a low-level client for interacting with device mapper via
25+
// the `dmsetup` utility, which is provided by the `device-mapper` package.
2626
type DmsetupClient interface {
27+
// Table runs `dmsetup table` on the given device name and returns the
28+
// output or an error.
2729
Table(deviceName string) ([]byte, error)
30+
// Message runs `dmsetup message` on the given device, passing the given
31+
// message to the given sector, and returns the output or an error.
2832
Message(deviceName string, sector int, message string) ([]byte, error)
33+
// Status runs `dmsetup status` on the given device and returns the output
34+
// or an error.
2935
Status(deviceName string) ([]byte, error)
3036
}
3137

38+
// NewDmSetupClient returns a new DmsetupClient.
3239
func NewDmsetupClient() DmsetupClient {
3340
return &defaultDmsetupClient{}
3441
}
3542

36-
// defaultDmsetupClient implements the standard behavior for interacting with dmsetup.
43+
// defaultDmsetupClient is a functional DmsetupClient
3744
type defaultDmsetupClient struct{}
3845

3946
var _ DmsetupClient = &defaultDmsetupClient{}

devicemapper/fake/dmsetup_client_fake.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ type DmsetupCommand struct {
2323
Err error
2424
}
2525

26+
// NewFakeDmsetupClient returns a new fake DmsetupClient.
2627
func NewFakeDmsetupClient(t *testing.T, commands ...DmsetupCommand) *FakeDmsetupClient {
2728
if len(commands) == 0 {
2829
commands = make([]DmsetupCommand, 0)
2930
}
3031
return &FakeDmsetupClient{t: t, commands: commands}
3132
}
3233

33-
// FakeDmsetupClient is a thread-unsafe fake implementation of the DmsetupClient interface
34+
// FakeDmsetupClient is a thread-unsafe fake implementation of the
35+
// DmsetupClient interface
3436
type FakeDmsetupClient struct {
3537
t *testing.T
3638
commands []DmsetupCommand

devicemapper/fake/thin_ls_client_fake.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type FakeThinLsClient struct {
1818
err error
1919
}
2020

21+
// NewFakeThinLsClient returns a new fake ThinLsClient.
2122
func NewFakeThinLsClient(result map[string]uint64, err error) *FakeThinLsClient {
2223
return &FakeThinLsClient{result, err}
2324
}

devicemapper/thin_ls_client.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,47 @@ import (
2424
"github.com/golang/glog"
2525
)
2626

27-
// thinLsClient knows how to run a thin_ls very specific to CoW usage for containers.
27+
// thinLsClient knows how to run a thin_ls very specific to CoW usage for
28+
// containers.
2829
type thinLsClient interface {
30+
// ThinLs runs a thin ls on the given device, which is expected to be a
31+
// metadata device. The caller must hold the metadata snapshot for the
32+
// device.
2933
ThinLs(deviceName string) (map[string]uint64, error)
3034
}
3135

32-
func newThinLsClient() thinLsClient {
33-
return &defaultThinLsClient{}
36+
// newThinLsClient returns a thinLsClient or an error if the thin_ls binary
37+
// couldn't be located.
38+
func newThinLsClient() (thinLsClient, error) {
39+
thinLsPath, err := ThinLsBinaryPresent()
40+
if err != nil {
41+
return nil, fmt.Errorf("error creating thin_ls client: %v", err)
42+
}
43+
44+
return &defaultThinLsClient{thinLsPath}, nil
3445
}
3546

36-
type defaultThinLsClient struct{}
47+
// defaultThinLsClient is a functional thinLsClient
48+
type defaultThinLsClient struct {
49+
thinLsPath string
50+
}
3751

3852
var _ thinLsClient = &defaultThinLsClient{}
3953

40-
func (*defaultThinLsClient) ThinLs(deviceName string) (map[string]uint64, error) {
54+
func (c *defaultThinLsClient) ThinLs(deviceName string) (map[string]uint64, error) {
4155
args := []string{"--no-headers", "-m", "-o", "DEV,EXCLUSIVE_BYTES", deviceName}
4256
glog.V(4).Infof("running command: thin_ls %v", strings.Join(args, " "))
4357

44-
output, err := exec.Command("thin_ls", args...).Output()
58+
output, err := exec.Command(c.thinLsPath, args...).Output()
4559
if err != nil {
4660
return nil, fmt.Errorf("Error running command `thin_ls %v`: %v\noutput:\n\n%v", strings.Join(args, " "), err, string(output))
4761
}
4862

4963
return parseThinLsOutput(output), nil
5064
}
5165

52-
// parseThinLsOutput parses the output returned by thin_ls to build a map of device id -> usage.
66+
// parseThinLsOutput parses the output returned by thin_ls to build a map of
67+
// device id -> usage.
5368
func parseThinLsOutput(output []byte) map[string]uint64 {
5469
cache := map[string]uint64{}
5570

devicemapper/thin_pool_watcher.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import (
2222
"github.com/golang/glog"
2323
)
2424

25-
// ThinPoolWatcher maintains a cache of device name -> usage stats for a devicemapper thin-pool using thin_ls.
25+
// ThinPoolWatcher maintains a cache of device name -> usage stats for a
26+
// devicemapper thin-pool using thin_ls.
2627
type ThinPoolWatcher struct {
2728
poolName string
2829
metadataDevice string
@@ -34,20 +35,26 @@ type ThinPoolWatcher struct {
3435
thinLsClient thinLsClient
3536
}
3637

37-
// NewThinPoolWatcher returns a new ThinPoolWatcher for the given devicemapper thin pool name and metadata device.
38-
func NewThinPoolWatcher(poolName, metadataDevice string) *ThinPoolWatcher {
38+
// NewThinPoolWatcher returns a new ThinPoolWatcher for the given devicemapper
39+
// thin pool name and metadata device or an error.
40+
func NewThinPoolWatcher(poolName, metadataDevice string) (*ThinPoolWatcher, error) {
41+
thinLsClient, err := newThinLsClient()
42+
if err != nil {
43+
return nil, fmt.Errorf("encountered error creating thin_ls client: %v", err)
44+
}
45+
3946
return &ThinPoolWatcher{poolName: poolName,
4047
metadataDevice: metadataDevice,
4148
lock: &sync.RWMutex{},
4249
cache: make(map[string]uint64),
4350
period: 15 * time.Second,
4451
stopChan: make(chan struct{}),
4552
dmsetup: NewDmsetupClient(),
46-
thinLsClient: newThinLsClient(),
47-
}
53+
thinLsClient: thinLsClient,
54+
}, nil
4855
}
4956

50-
// Start starts the thin pool watcher.
57+
// Start starts the ThinPoolWatcher.
5158
func (w *ThinPoolWatcher) Start() {
5259
err := w.Refresh()
5360
if err != nil {
@@ -72,6 +79,7 @@ func (w *ThinPoolWatcher) Start() {
7279
}
7380
}
7481

82+
// Stop stops the ThinPoolWatcher.
7583
func (w *ThinPoolWatcher) Stop() {
7684
close(w.stopChan)
7785
}
@@ -80,6 +88,7 @@ func (w *ThinPoolWatcher) Stop() {
8088
func (w *ThinPoolWatcher) GetUsage(deviceId string) (uint64, error) {
8189
w.lock.RLock()
8290
defer w.lock.RUnlock()
91+
8392
v, ok := w.cache[deviceId]
8493
if !ok {
8594
return 0, fmt.Errorf("no cached value for usage of device %v", deviceId)
@@ -115,7 +124,8 @@ func (w *ThinPoolWatcher) Refresh() error {
115124
}
116125

117126
glog.Infof("reserving metadata snapshot for thin-pool %v", w.poolName)
118-
// NOTE: "0" in the call below is for the 'sector' argument to 'dmsetup message'. It's not needed for thin pools.
127+
// NOTE: "0" in the call below is for the 'sector' argument to 'dmsetup
128+
// message'. It's not needed for thin pools.
119129
if output, err := w.dmsetup.Message(w.poolName, 0, reserveMetadataMessage); err != nil {
120130
err = fmt.Errorf("error reserving metadata for thin-pool %v: %v output: %v", w.poolName, err, string(output))
121131
return err
@@ -144,7 +154,8 @@ const (
144154
thinPoolDmsetupStatusHeldMetadataRoot = 6
145155
)
146156

147-
// checkReservation checks to see whether the thin device is currently holding userspace metadata.
157+
// checkReservation checks to see whether the thin device is currently holding
158+
// userspace metadata.
148159
func (w *ThinPoolWatcher) checkReservation(poolName string) (bool, error) {
149160
glog.V(5).Infof("checking whether the thin-pool is holding a metadata snapshot")
150161
output, err := w.dmsetup.Status(poolName)
@@ -153,7 +164,8 @@ func (w *ThinPoolWatcher) checkReservation(poolName string) (bool, error) {
153164
}
154165

155166
tokens := strings.Split(string(output), " ")
156-
// Split returns the input as the last item in the result, adjust the number of tokens by one
167+
// Split returns the input as the last item in the result, adjust the
168+
// number of tokens by one
157169
if len(tokens) != thinPoolDmsetupStatusTokens+1 {
158170
return false, fmt.Errorf("unexpected output of dmsetup status command; expected 11 fields, got %v; output: %v", len(tokens), string(output))
159171
}

devicemapper/util.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2016 Google Inc. All Rights Reserved.
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+
package devicemapper
15+
16+
import (
17+
"fmt"
18+
"os"
19+
"path/filepath"
20+
)
21+
22+
// ThinLsBinaryPresent returns the location of the thin_ls binary in the mount
23+
// namespace cadvisor is running in or an error. The locations checked are:
24+
//
25+
// - /bin/
26+
// - /usr/sbin/
27+
// - /usr/bin/
28+
//
29+
// ThinLsBinaryPresent checks these paths relative to:
30+
//
31+
// 1. For non-containerized operation - `/`
32+
// 2. For containerized operation - `/rootfs`
33+
//
34+
// The thin_ls binary is provided by the device-mapper-persistent-data
35+
// package.
36+
func ThinLsBinaryPresent() (string, error) {
37+
var (
38+
thinLsPath string
39+
err error
40+
)
41+
42+
for _, path := range []string{"/bin", "/usr/sbin/", "/usr/bin"} {
43+
// try paths for non-containerized operation
44+
// note: thin_ls is most likely a symlink to pdata_tools
45+
thinLsPath = filepath.Join(path, "thin_ls")
46+
_, err = os.Stat(thinLsPath)
47+
if err == nil {
48+
return thinLsPath, nil
49+
}
50+
51+
// try paths for containerized operation
52+
thinLsPath = filepath.Join("/rootfs", thinLsPath)
53+
_, err = os.Stat(thinLsPath)
54+
if err == nil {
55+
return thinLsPath, nil
56+
}
57+
}
58+
59+
return "", fmt.Errorf("unable to find thin_ls binary")
60+
}

info/v2/conversion.go

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ func InstCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) {
206206
return 0, fmt.Errorf("cumulative stats decrease")
207207
}
208208
valueDelta := curValue - lastValue
209-
return (valueDelta * 1e9) / timeDeltaNs, nil
209+
// Use float64 to keep precision
210+
return uint64(float64(valueDelta) / float64(timeDeltaNs) * 1e9), nil
210211
}
211212
total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
212213
if err != nil {
@@ -268,57 +269,3 @@ func ContainerSpecFromV1(specV1 *v1.ContainerSpec, aliases []string, namespace s
268269
specV2.Namespace = namespace
269270
return specV2
270271
}
271-
272-
func instCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) {
273-
if last == nil {
274-
return nil, nil
275-
}
276-
if !cur.Timestamp.After(last.Timestamp) {
277-
return nil, fmt.Errorf("container stats move backwards in time")
278-
}
279-
if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) {
280-
return nil, fmt.Errorf("different number of cpus")
281-
}
282-
timeDelta := cur.Timestamp.Sub(last.Timestamp)
283-
if timeDelta <= 100*time.Millisecond {
284-
return nil, fmt.Errorf("time delta unexpectedly small")
285-
}
286-
// Nanoseconds to gain precision and avoid having zero seconds if the
287-
// difference between the timestamps is just under a second
288-
timeDeltaNs := uint64(timeDelta.Nanoseconds())
289-
convertToRate := func(lastValue, curValue uint64) (uint64, error) {
290-
if curValue < lastValue {
291-
return 0, fmt.Errorf("cumulative stats decrease")
292-
}
293-
valueDelta := curValue - lastValue
294-
return (valueDelta * 1e9) / timeDeltaNs, nil
295-
}
296-
total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
297-
if err != nil {
298-
return nil, err
299-
}
300-
percpu := make([]uint64, len(last.Cpu.Usage.PerCpu))
301-
for i := range percpu {
302-
var err error
303-
percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i])
304-
if err != nil {
305-
return nil, err
306-
}
307-
}
308-
user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User)
309-
if err != nil {
310-
return nil, err
311-
}
312-
system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System)
313-
if err != nil {
314-
return nil, err
315-
}
316-
return &CpuInstStats{
317-
Usage: CpuInstUsage{
318-
Total: total,
319-
PerCpu: percpu,
320-
User: user,
321-
System: system,
322-
},
323-
}, nil
324-
}

0 commit comments

Comments
 (0)