mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	add a client to get container info from kubelet
This commit is contained in:
		
				
					committed by
					
						
						Nan Monnand Deng
					
				
			
			
				
	
			
			
			
						parent
						
							c7d31fabbc
						
					
				
				
					commit
					6878f105c0
				
			
							
								
								
									
										91
									
								
								pkg/client/containerinfo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								pkg/client/containerinfo.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2014 Google Inc. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/cadvisor/info"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ContainerInfoGetter interface {
 | 
			
		||||
	GetContainerInfo(host, podID, containerID string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
 | 
			
		||||
	GetMachineInfo(host string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type HTTPContainerInfoGetter struct {
 | 
			
		||||
	Client *http.Client
 | 
			
		||||
	Port   int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *HTTPContainerInfoGetter) getContainerInfo(host, path string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
 | 
			
		||||
	var body io.Reader
 | 
			
		||||
	if req != nil {
 | 
			
		||||
		content, err := json.Marshal(req)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		body = bytes.NewBuffer(content)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	request, err := http.NewRequest(
 | 
			
		||||
		"GET",
 | 
			
		||||
		fmt.Sprintf("http://%v/stats/%v",
 | 
			
		||||
			net.JoinHostPort(host, strconv.Itoa(self.Port)),
 | 
			
		||||
			path,
 | 
			
		||||
		),
 | 
			
		||||
		body,
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response, err := self.Client.Do(request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
	if response.StatusCode != http.StatusOK {
 | 
			
		||||
		return nil, fmt.Errorf("trying to get info for %v from %v; received status %v",
 | 
			
		||||
			path, host, response.Status)
 | 
			
		||||
	}
 | 
			
		||||
	decoder := json.NewDecoder(response.Body)
 | 
			
		||||
	var cinfo info.ContainerInfo
 | 
			
		||||
	err = decoder.Decode(&cinfo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &cinfo, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *HTTPContainerInfoGetter) GetContainerInfo(host, podID, containerID string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
 | 
			
		||||
	return self.getContainerInfo(
 | 
			
		||||
		host,
 | 
			
		||||
		fmt.Sprintf("%v/%v", podID, containerID),
 | 
			
		||||
		req,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *HTTPContainerInfoGetter) GetMachineInfo(host string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
 | 
			
		||||
	return self.getContainerInfo(host, "", req)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										171
									
								
								pkg/client/containerinfo_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								pkg/client/containerinfo_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2014 Google Inc. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"path"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/cadvisor/info"
 | 
			
		||||
	itest "github.com/google/cadvisor/info/test"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func testHTTPContainerInfoGetter(
 | 
			
		||||
	req *info.ContainerInfoRequest,
 | 
			
		||||
	cinfo *info.ContainerInfo,
 | 
			
		||||
	podID string,
 | 
			
		||||
	containerID string,
 | 
			
		||||
	status int,
 | 
			
		||||
	t *testing.T,
 | 
			
		||||
) {
 | 
			
		||||
	expectedPath := "/stats"
 | 
			
		||||
	if len(podID) > 0 && len(containerID) > 0 {
 | 
			
		||||
		expectedPath = path.Join(expectedPath, podID, containerID)
 | 
			
		||||
	}
 | 
			
		||||
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if status != 0 {
 | 
			
		||||
			w.WriteHeader(status)
 | 
			
		||||
		}
 | 
			
		||||
		if strings.TrimRight(r.URL.Path, "/") != strings.TrimRight(expectedPath, "/") {
 | 
			
		||||
			t.Fatalf("Received request to an invalid path. Should be %v. got %v",
 | 
			
		||||
				expectedPath, r.URL.Path)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		decoder := json.NewDecoder(r.Body)
 | 
			
		||||
		var receivedReq info.ContainerInfoRequest
 | 
			
		||||
		err := decoder.Decode(&receivedReq)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		// Note: This will not make a deep copy of req.
 | 
			
		||||
		// So changing req after Get*Info would be a race.
 | 
			
		||||
		expectedReq := req
 | 
			
		||||
		// Fill any empty fields with default value
 | 
			
		||||
		expectedReq = expectedReq.FillDefaults()
 | 
			
		||||
		if !reflect.DeepEqual(expectedReq, &receivedReq) {
 | 
			
		||||
			t.Errorf("received wrong request")
 | 
			
		||||
		}
 | 
			
		||||
		encoder := json.NewEncoder(w)
 | 
			
		||||
		err = encoder.Encode(cinfo)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
	hostURL, err := url.Parse(ts.URL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	parts := strings.Split(hostURL.Host, ":")
 | 
			
		||||
 | 
			
		||||
	port, err := strconv.Atoi(parts[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerInfoGetter := &HTTPContainerInfoGetter{
 | 
			
		||||
		Client: http.DefaultClient,
 | 
			
		||||
		Port:   port,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var receivedContainerInfo *info.ContainerInfo
 | 
			
		||||
	if len(podID) > 0 && len(containerID) > 0 {
 | 
			
		||||
		receivedContainerInfo, err = containerInfoGetter.GetContainerInfo(parts[0], podID, containerID, req)
 | 
			
		||||
	} else {
 | 
			
		||||
		receivedContainerInfo, err = containerInfoGetter.GetMachineInfo(parts[0], req)
 | 
			
		||||
	}
 | 
			
		||||
	if status == 0 || status == http.StatusOK {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("received unexpected error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !receivedContainerInfo.Eq(cinfo) {
 | 
			
		||||
			t.Error("received unexpected container info")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			t.Error("did not receive expected error.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHTTPContainerInfoGetterGetContainerInfoSuccessfully(t *testing.T) {
 | 
			
		||||
	req := &info.ContainerInfoRequest{
 | 
			
		||||
		NumStats:   10,
 | 
			
		||||
		NumSamples: 10,
 | 
			
		||||
	}
 | 
			
		||||
	req = req.FillDefaults()
 | 
			
		||||
	cinfo := itest.GenerateRandomContainerInfo(
 | 
			
		||||
		"dockerIDWhichWillNotBeChecked", // docker ID
 | 
			
		||||
		2, // Number of cores
 | 
			
		||||
		req,
 | 
			
		||||
		1*time.Second,
 | 
			
		||||
	)
 | 
			
		||||
	testHTTPContainerInfoGetter(req, cinfo, "somePodID", "containerNameInK8S", 0, t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHTTPContainerInfoGetterGetMachineInfoSuccessfully(t *testing.T) {
 | 
			
		||||
	req := &info.ContainerInfoRequest{
 | 
			
		||||
		NumStats:   10,
 | 
			
		||||
		NumSamples: 10,
 | 
			
		||||
	}
 | 
			
		||||
	req = req.FillDefaults()
 | 
			
		||||
	cinfo := itest.GenerateRandomContainerInfo(
 | 
			
		||||
		"dockerIDWhichWillNotBeChecked", // docker ID
 | 
			
		||||
		2, // Number of cores
 | 
			
		||||
		req,
 | 
			
		||||
		1*time.Second,
 | 
			
		||||
	)
 | 
			
		||||
	testHTTPContainerInfoGetter(req, cinfo, "", "", 0, t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHTTPContainerInfoGetterGetContainerInfoWithError(t *testing.T) {
 | 
			
		||||
	req := &info.ContainerInfoRequest{
 | 
			
		||||
		NumStats:   10,
 | 
			
		||||
		NumSamples: 10,
 | 
			
		||||
	}
 | 
			
		||||
	req = req.FillDefaults()
 | 
			
		||||
	cinfo := itest.GenerateRandomContainerInfo(
 | 
			
		||||
		"dockerIDWhichWillNotBeChecked", // docker ID
 | 
			
		||||
		2, // Number of cores
 | 
			
		||||
		req,
 | 
			
		||||
		1*time.Second,
 | 
			
		||||
	)
 | 
			
		||||
	testHTTPContainerInfoGetter(req, cinfo, "somePodID", "containerNameInK8S", http.StatusNotFound, t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHTTPContainerInfoGetterGetMachineInfoWithError(t *testing.T) {
 | 
			
		||||
	req := &info.ContainerInfoRequest{
 | 
			
		||||
		NumStats:   10,
 | 
			
		||||
		NumSamples: 10,
 | 
			
		||||
	}
 | 
			
		||||
	req = req.FillDefaults()
 | 
			
		||||
	cinfo := itest.GenerateRandomContainerInfo(
 | 
			
		||||
		"dockerIDWhichWillNotBeChecked", // docker ID
 | 
			
		||||
		2, // Number of cores
 | 
			
		||||
		req,
 | 
			
		||||
		1*time.Second,
 | 
			
		||||
	)
 | 
			
		||||
	testHTTPContainerInfoGetter(req, cinfo, "", "", http.StatusNotFound, t)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## 0.1.3 (2014-07-14)
 | 
			
		||||
- Add support for systemd systems.
 | 
			
		||||
- Fixes for UI with InfluxDB storage driver.
 | 
			
		||||
 | 
			
		||||
## 0.1.2 (2014-07-10)
 | 
			
		||||
- Added Storage Driver concept (flag: storage_driver), default is the in-memory driver
 | 
			
		||||
- Implemented InfluxDB storage driver
 | 
			
		||||
 
 | 
			
		||||
@@ -110,32 +110,7 @@ func TestGetContainerInfo(t *testing.T) {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We cannot use DeepEqual() to compare them directly,
 | 
			
		||||
	// because json en/decoded time may have precision issues.
 | 
			
		||||
	if !reflect.DeepEqual(returned.ContainerReference, cinfo.ContainerReference) {
 | 
			
		||||
		t.Errorf("received unexpected container ref")
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(returned.Subcontainers, cinfo.Subcontainers) {
 | 
			
		||||
		t.Errorf("received unexpected subcontainers")
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(returned.Spec, cinfo.Spec) {
 | 
			
		||||
		t.Errorf("received unexpected spec")
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(returned.StatsPercentiles, cinfo.StatsPercentiles) {
 | 
			
		||||
		t.Errorf("received unexpected spec")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, expectedStats := range cinfo.Stats {
 | 
			
		||||
		returnedStats := returned.Stats[i]
 | 
			
		||||
		if !expectedStats.Eq(returnedStats) {
 | 
			
		||||
			t.Errorf("received unexpected stats")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, expectedSample := range cinfo.Samples {
 | 
			
		||||
		returnedSample := returned.Samples[i]
 | 
			
		||||
		if !expectedSample.Eq(returnedSample) {
 | 
			
		||||
			t.Errorf("received unexpected sample")
 | 
			
		||||
		}
 | 
			
		||||
	if !returned.Eq(cinfo) {
 | 
			
		||||
		t.Error("received unexpected ContainerInfo")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,14 @@ import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/libcontainer"
 | 
			
		||||
	"github.com/docker/libcontainer/cgroups"
 | 
			
		||||
	"github.com/docker/libcontainer/cgroups/fs"
 | 
			
		||||
	"github.com/docker/libcontainer/cgroups/systemd"
 | 
			
		||||
	"github.com/fsouza/go-dockerclient"
 | 
			
		||||
	"github.com/google/cadvisor/container"
 | 
			
		||||
	"github.com/google/cadvisor/info"
 | 
			
		||||
@@ -98,8 +100,11 @@ func (self *dockerContainerHandler) splitName() (string, string, error) {
 | 
			
		||||
	if nestedLevels > 0 {
 | 
			
		||||
		// we are running inside a docker container
 | 
			
		||||
		upperLevel := strings.Repeat("../../", nestedLevels)
 | 
			
		||||
		//parent = strings.Join([]string{parent, upperLevel}, "/")
 | 
			
		||||
		parent = fmt.Sprintf("%v%v", upperLevel, parent)
 | 
			
		||||
		parent = filepath.Join(upperLevel, parent)
 | 
			
		||||
	}
 | 
			
		||||
	// Strip the last "/"
 | 
			
		||||
	if parent[len(parent)-1] == '/' {
 | 
			
		||||
		parent = parent[:len(parent)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return parent, id, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -237,7 +242,15 @@ func (self *dockerContainerHandler) GetStats() (stats *info.ContainerStats, err
 | 
			
		||||
		Parent: parent,
 | 
			
		||||
		Name:   id,
 | 
			
		||||
	}
 | 
			
		||||
	s, err := fs.GetStats(cg)
 | 
			
		||||
 | 
			
		||||
	// TODO(vmarmol): Use libcontainer's Stats() in the new API when that is ready.
 | 
			
		||||
	// Use systemd paths if systemd is being used.
 | 
			
		||||
	var s *cgroups.Stats
 | 
			
		||||
	if systemd.UseSystemd() {
 | 
			
		||||
		s, err = systemd.GetStats(cg)
 | 
			
		||||
	} else {
 | 
			
		||||
		s, err = fs.GetStats(cg)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ FROM busybox:ubuntu-14.04
 | 
			
		||||
MAINTAINER kyurtsever@google.com dengnan@google.com vmarmol@google.com jason@swindle.me
 | 
			
		||||
 | 
			
		||||
# Get cAdvisor binaries.
 | 
			
		||||
ADD http://storage.googleapis.com/cadvisor-bin/cadvisor /usr/bin/cadvisor
 | 
			
		||||
ADD http://storage.googleapis.com/cadvisor-bin/cadvisor-0.1.3 /usr/bin/cadvisor
 | 
			
		||||
RUN chmod +x /usr/bin/cadvisor
 | 
			
		||||
 | 
			
		||||
EXPOSE 8080
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ RUN apt-get update && apt-get install -y -q --no-install-recommends pkg-config l
 | 
			
		||||
# Get the lcmtfy and cAdvisor binaries.
 | 
			
		||||
ADD http://storage.googleapis.com/cadvisor-bin/lmctfy/lmctfy /usr/bin/lmctfy
 | 
			
		||||
ADD http://storage.googleapis.com/cadvisor-bin/lmctfy/libre2.so.0.0.0 /usr/lib/libre2.so.0
 | 
			
		||||
ADD http://storage.googleapis.com/cadvisor-bin/cadvisor-0.1.2 /usr/bin/cadvisor
 | 
			
		||||
ADD http://storage.googleapis.com/cadvisor-bin/cadvisor-0.1.3 /usr/bin/cadvisor
 | 
			
		||||
RUN chmod +x /usr/bin/lmctfy && chmod +x /usr/bin/cadvisor
 | 
			
		||||
 | 
			
		||||
EXPOSE 8080
 | 
			
		||||
 
 | 
			
		||||
@@ -110,6 +110,52 @@ type ContainerInfo struct {
 | 
			
		||||
	StatsPercentiles *ContainerStatsPercentiles `json:"stats_summary,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContainerInfo may be (un)marshaled by json or other en/decoder. In that
 | 
			
		||||
// case, the Timestamp field in each stats/sample may not be precisely
 | 
			
		||||
// en/decoded.  This will lead to small but acceptable differences between a
 | 
			
		||||
// ContainerInfo and its encode-then-decode version.  Eq() is used to compare
 | 
			
		||||
// two ContainerInfo accepting small difference (<10ms) of Time fields.
 | 
			
		||||
func (self *ContainerInfo) Eq(b *ContainerInfo) bool {
 | 
			
		||||
 | 
			
		||||
	// If both self and b are nil, then Eq() returns true
 | 
			
		||||
	if self == nil {
 | 
			
		||||
		return b == nil
 | 
			
		||||
	}
 | 
			
		||||
	if b == nil {
 | 
			
		||||
		return self == nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// For fields other than time.Time, we will compare them precisely.
 | 
			
		||||
	// This would require that any slice should have same order.
 | 
			
		||||
	if !reflect.DeepEqual(self.ContainerReference, b.ContainerReference) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(self.Subcontainers, b.Subcontainers) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(self.Spec, b.Spec) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(self.StatsPercentiles, b.StatsPercentiles) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, expectedStats := range b.Stats {
 | 
			
		||||
		selfStats := self.Stats[i]
 | 
			
		||||
		if !expectedStats.Eq(selfStats) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, expectedSample := range b.Samples {
 | 
			
		||||
		selfSample := self.Samples[i]
 | 
			
		||||
		if !expectedSample.Eq(selfSample) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *ContainerInfo) StatsAfter(ref time.Time) []*ContainerStats {
 | 
			
		||||
	n := len(self.Stats) + 1
 | 
			
		||||
	for i, s := range self.Stats {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,4 +15,4 @@
 | 
			
		||||
package info
 | 
			
		||||
 | 
			
		||||
// Version of cAdvisor.
 | 
			
		||||
const VERSION = "0.1.2"
 | 
			
		||||
const VERSION = "0.1.3"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user