Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Refactored version of collector #65

Merged
merged 1 commit into from
Jan 20, 2017

Conversation

marcin-krolik
Copy link
Collaborator

@marcin-krolik marcin-krolik commented Jan 4, 2017

Fixes #21, #34, #56

Summary of changes:

  • removed dependency to openlibcontainers/runc
  • re-factored metric collection process, only requested metrics are gathered per requested docker id
  • proper discovery of cgroupfs

TODO:

  • refactor filesystem metrics
  • refactor unit tests
  • documentation update
  • handling of blkio
  • additional unit tests for container/cgroupfs package

func initClient(c *collector, endpoint string) error {
dc, err := container.NewDockerClient(endpoint)
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing log

Copy link
Collaborator Author

@marcin-krolik marcin-krolik Jan 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logs moved to collector level. This package only returns errors now

@@ -0,0 +1,164 @@
package collector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing header

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

header added

fields := strings.Split(txt, " ")
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
if opt == subsystem {
return fields[4], nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possible index out of range?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would require to /proc/self/mountinfo change its format. I think it's not needed

}
}

v, err := strconv.ParseUint(fields[0], 10, 64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use "major" instead of "v"?

}
major := v

v, err = strconv.ParseUint(fields[1], 10, 64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use "minor" instead of "v"?


stats.Cgroups.PidsStats.Current = current

var max uint64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max shouldn't be initialized with some default value? What will happen if limit = "max"?

Copy link
Collaborator Author

@marcin-krolik marcin-krolik Jan 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is initialized to default value of uint64 which is 0. For limit equal to zero, it means no limit.

What will happen if limit = "max"?

In this case limit will be set to defult value of max

if err != nil {
return err
}
fmt.Println(pid, isHost, procfs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary print

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

http://www.apache.org/licenses/LICENSE-2.0.txt


Copyright 2015 Intel Corporation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2017, also in other files? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was agreed before dates in header files don't have to be updated while introducing changes to already released plugin.

return *policy, nil
}

type collector struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this type definition on the top, after const definition?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unexported type. It's quite common code style to keep unexported types below exported ones

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @kindermoumoute's comment because this is a type which is returned by New() in line 94, so it will be great to have a definition of this structure after const or variables (which are also unexported but are placed on the top despite this).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New() now returns interface. collector type as unexported type stays behind exported API

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, that is fine for me.

} // the end of range over ids
}

if len(metrics) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be the first thing to test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. It tests if metrics collection process actually collected anything

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, I thought you were catching if len(mts) == 0. This case should never be allowed by the framework though.

// namespace plugin name
PLUGIN_NAME = "docker"
// version of plugin
PLUGIN_VERSION = 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, increment the version of plugin

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version increased to 6

return &collector{
containers: map[string]*container.ContainerData{},
mounts: map[string]string{},
}, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this refactoring, returning error from New() is no longer needed - returned err is always nil. Consider removing error from output and updating go-doc for this function

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great

c.conf, err = getDockerConfig(mts[0].Config)
if err != nil {
log.WithFields(log.Fields{
"plugin": "docker_collector",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding "plugin" as log's field duplicates information added by framework, so this field might be removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed logging in whole module

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great

}

return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that tcp metrics and tcp6 metrics are exactly the same. what is more the way of retrieving these data differs only in the taken path. For this refactored code distinguishing between tcp and tcp is made on defined structures, please consider putting all this code from GetStats in different function with input argument pointing to "net/tcp" or "net/tcp6" depending on a case and use tcpStatsFromProc directly, so functions TcpStatsFromProc and Tcp6StatsFromProc are not longer needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for changing that, now it looks good


if len(containers) == 0 {
return nil, fmt.Errorf("No docker containers found %d", len(containers))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that this if statement does not have sense because this containers map always contains "root". So, what do you think about moving this statement before adding "root" to this map, but only logging this fact as an "INFO" or "DEBUG", because even there is no docker containers, collection should not fail as it works currently (in a sense by accident).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for moving it up and adding a log, now it looks good

hugetlb_stats/\<size\>/max_usage | uint64 | Max "hugepagesize" hugetlb usage recorded
hugetlb_stats/\<size\>/usage | uint64 | Current usage for "hugepagesize" hugetlb
| |
blkio_stats/io_merged_recursive/\<N\>/value | uint64 | Total number of bios/requests merged into requests belonging from all the descendant cgroups
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcin-krolik, what's about major, minor, and op for blkio_stats?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the occasion if you could remove metric "network/name" which is no longer exposed (because of including name as a dynamic element), it will be great :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, but @marcin-krolik - could you make descriptions of these major, minor, and op blkio_stats consistent with the rest? Also, a new line might be put between these points:
1) Hierarchical version of cgroups counter which in addition to the cgroup's own value includes the sum of all hierarchical children's values (2) Each blkio statistic additionally exposes major, minor and op metric

cpu_stats/throttling_data/throttled_time | uint64 | The total time duration for which tasks in a cgroup have been throttled
cpu_stats/cpu_shares | uint64 | contains an integer value that specifies a relative share of CPU time available to the tasks in a cgroup
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep consistency, consider changing contains an integer value that specifies a relative share of CPU time available to the tasks in a cgroup to The relative share of CPU time available to the tasks in a cgroup. Please, take into account the rest of similar cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, thanks

blkio_stats/io_serviced_recursive/\<N\>/value | uint64 | Number of IOs (bio) issued to the disk from all the descendant cgroups
blkio_stats/io_time_recursive/\<N\>/value | uint64 | Disk time allocated to cgroup per device in milliseconds from all the descendant cgroups
blkio_stats/io_wait_time_recursive/\<N\>/value | uint64 | Total amount of time the IOs for this cgroup spent waiting in the scheduler queues for service from all the descendant cgroups
blkio_stats/sectors_recursive/\<N\>/value | uint64 | number of sectors transferred to/from disk from all descendant group

<sup>(1)</sup> Hierarchical version of cgroups counter which in addition to the cgroup's own value includes the sum of all hierarchical children's values

Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like to update the link to Kernel cgroups documentation, currenty it does not work.
So METRICS.md, line 131 should be changed to:
Read more about cgroups in [Kernel documentation](https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

cgroupfs string
driver string
mounts map[string]string
conf map[string]string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coud you add comments to the rest of stucture's fields as it's done for "containers" and "client"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

}

for group := range groups {
// during initialization of docker client information about running containers is collectd
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"collectd" probably to collected"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, now it is ok


if rid != "root" {
if c.cgroupfs == "systemd" {
cpath = filepath.Join(cpath, "system.slice", fmt.Sprintf("docker-%s.scope", c.containers[rid].ID))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding here a log with information about the path to cgroups? I suppose it will be helfpul based on issues which were reported in the past.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After changing this fragment of code, there is still lack of logging of cgroup paths - I suppose that based on past issues putting a log there will be very helpful.

// only log error when it was not possible to access metric source
if err != nil {
log.WithFields(log.Fields{
"plugin": "docker_collector",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplication - you do not need "plugin": "docker_collector" in logs, because Snap's framework will add that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great

@@ -367,38 +369,10 @@ func TestCollectMetrics(t *testing.T) {
})
})
})
Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like to add tests covering cases for filesystem metrics and alsonew ones (hugetlb, blkio and cpuset statistics)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it could be delivered in separate PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, that works for me

c.driver = drv

log.WithFields(log.Fields{
"plugin": "docker_collector",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field "plugin": "docker_collector" might be removed.

Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, now it looks great


func getDockerConfig(cfg plugin.Config) (map[string]string, error) {
config := make(map[string]string)
values := []string{"endpoint", "procfs"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to declare strings "endpoint" and " procfs" as a constant? Also, if this getDockerConfig function is added, maybe checking all required configs will be better and then return combined error or []error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These strings are used only once, I don't see any point in adding them as constants.

values := []string{"endpoint", "procfs"}
var err error
for _, v := range values {
config[v], err = getStringFromConfig(cfg, v)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why cfg.GetString(key) is not called directly, but a new function getStringFromConfig was used for do it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be. Changed to direct call

}

op := ""
valueField := 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Light suggestion - consider renaming valueField to indexOfFieldValue or sth similar that clearly suggest that this is an index, not a value itself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to index

}

func getCFQStats(path string, stats *container.Statistics) error {
var blkioStats []container.BlkioStatEntry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Light suggestion - consider renaming blkioStats to blkioStatEntry to highlight that this is only a part of blkio stats, of course up to you.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are single entries, and each entry is blkio statistic, quite descriptive. I don't see the need to change that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.sectors_recursive")); err != nil {
return err
}
stats.Cgroups.BlkioStats.SectorsRecursive = blkioStats
Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing this statement:

if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.sectors_recursive")); err != nil {
   return err
}
stats.Cgroups.BlkioStats.SectorsRecursive = blkioStats

to

if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.sectors_recursive")); err == nil {
  stats.Cgroups.BlkioStats.SectorsRecursive = blkioStats
} else {
 errs=append(errs, err)
}

and returning []error with error messages from all called getBlkioStat

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With current implementation it fails early with first error. What would be an advantage of your approach?

Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main advantage of this approach is informing a user about all blkiostats errors in a first run, but I can agree that for this case it does not matter so much. However, this is really helpful in such cases like checking if config options are correct - that allows avoiding many runs with fails.

So, it looks ok

if blkioStats, err := getBlkioStat(filepath.Join(path, "blkio.io_serviced_recursive")); err == nil && blkioStats != nil {
return getCFQStats(path, stats)
}
return getStats(path, stats) // Use generic stats as fallback
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you put here (or above functions getCFQStats and getStats ) what distinguishes them - I mean why, based on on availability of blkio.io_serviced_recursive, one of them is called, otherwise the second one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already stated in comment from line 44 // Try to read CFQ stats available on all CFQ enabled kernels first and then // Use generic stats as fallback in line 48

Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance, these comments were not clear for me, but thanks for explanation. 👍

@@ -200,17 +206,38 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
}

// GetFsStats returns filesystem statistics for a given container
func GetFsStats(container *docker.Container) (map[string]wrapper.FilesystemInterface, error) {
func (du *DiskUsageCollector) GetStats(stats *container.Statistics, opts container.GetStatOpt) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now GetStats() routine is basing on a constant path to storage mount point, /var/lib/docker. It might be good idea to discover this path while initializing docker client with call to GetDockerParam("DockerRootDir") - see Docker API /info endpoint.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Thanks!

return err
}

drv, err := dc.GetDockerParam("Driver")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider change it to c.driver, err = dc.GetDockerParam("Driver"), so it will exactly the same way how it's done above`

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

log.WithFields(log.Fields{
"plugin": "docker_collector",
"block": "initClient",
}).Infof("Docker client initialized with %s and %s drivers", drv, c.cgroupfs)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing drv to c.driver in this info-log message

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


// GetStats reads throttling metrics from Cpu Group from cpu.stat
func (cpu *Cpu) GetStats(stats *container.Statistics, opts container.GetStatOpt) error {
path, err := opts.GetStringValue("cgroup_path")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a concern about calling opts.GetStringValue("cgroup_path") in each of GetStats functions. Putting cgroup_path as an input argument instead of opts might have more sense, so these GetStats function will be delacred in the following way:
func (cpu *Cpu) GetStats(stats *container.Statistics, cgroup_path string) error
func (cpuacct *CpuAcct) GetStats(stats *container.Statistics, cgroup_path string) error
func (cpuShares *CpuShares) GetStats(stats *container.Statistics, cgroup_path container.GetStatOpt) error

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that for network GetStats not only cgroup_path is needed, but also others fields from opts, so please ignore my comment above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will complicate interface. For other groups then cgroups getters other sets of parameters is required. With opts it easier to handle different cases

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I agree with that

nameArray := strings.Split(st.Name(), "-")
pageSize, err := units.RAMInBytes(nameArray[1])
if err != nil {
return []string{}, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return nil, err

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
for _, st := range files {
nameArray := strings.Split(st.Name(), "-")
pageSize, err := units.RAMInBytes(nameArray[1])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcin-krolik, please verify if this nameArray[1] does not cause potential out-of -range error in case when st.Name() does not contain "-" and splitting returns only one element.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such situation would require changes in structure of kernel file

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return 0, err
}

return strconv.ParseUint(strings.Trim(string(raw), "\n"), 10, 64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing strings.Trim(string(raw), "\n") to strings.TrimSpace(string(raw)) what will remove "\n" and others white spaces

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


value, err = strconv.ParseUint(fields[1], 10, 64)
if err != nil {
return name, value, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding to error message some info which will be helpful to point which value cannot be parsed to uint, for example:

fmt.Errorf("Cannot parse value for field %s, err=%s", field[0], err.Error())

or maybe put some log- it might be here or in a place where parseEntry is called.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParseUint error already contains such information eg. strconv.ParseUint: parsing "33f": invalid syntax

hugetlbStats.MaxUsage = maxUsage
hugetlbStats.Failcnt = failcnt

stats.Cgroups.HugetlbStats[pageSize] = hugetlbStats
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion: consider removing hugetlbStats var in this case, because it's not really needed. It might be implemented in the following way:

stats.Cgroups.HugetlbStats[pageSize] = container.HugetlbStats{
Usage: usage,
MaxUsage: maxUsage,
Failcnt: failcnt,
}

but up to you - if you prefer leave it as it is now, that's ok for me too

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return err
}

if val, ok := stats.Cgroups.MemoryStats.Stats["cache"]; ok {
Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a comment to explain why stats.Cgroups.MemoryStats.Stats is checked as a first to get cache value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

// MemoryCache implements StatGetter interface
type MemoryCache struct{}

// GetStats reads memory cache metric from Memory Group from memory.stat
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, verify if this description is really equivalent to how this func works

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is, cache value is taken from memory.stat file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


var max uint64
if limit != "max" {
max, err = strconv.ParseUint(strings.Trim(string(limit), "\n"), 10, 64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using strings.TrimSpace(string(limit)) instead

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


// DockerClient holds fsouza go-dockerclient instance ready for communication with the server endpoint `unix:///var/run/docker.sock`,
// cache instance which is used to store output from docker container inspect (to avoid execute inspect request multiply times, it is called only once per container)
// and diskUsageCollector which is responsible for collecting container disk usage (based on `du -u` command) in the background
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if this comment is up to date - I noticed that diskUsageCollector is no longer in DockerClient.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return info, nil
}

// GetDockerParam returns given parameter value fro running docker engine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fro should be from

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


// GetDockerParam returns given parameter value fro running docker engine
func (dc *DockerClient) GetDockerParam(param string) (string, error) {
env, err := dc.cl.Info()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about changing it to GetDockerParams and allowing to make a request for a few params, so dc.cl.Info() will be executed only once. Does it have a sense?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After change, it looks great


for scanner.Scan() {
txt := scanner.Text()
fields := strings.Split(txt, " ")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion - consider using here strings.Fields(txt)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

scanner := bufio.NewScanner(f)
for scanner.Scan() {
txt := scanner.Text()
fields := strings.Split(txt, " ")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion - consider using here strings.Fields(txt)

@@ -52,11 +54,12 @@ type RealFsInfo struct {
labels map[string]string
}

var collector DiskUsageCollector
//var collector DiskUsageCollector

type DiskUsageCollector struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider re-use here the comment from client.go#L5 about DiskUsageCollector

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

"module": "network",
"block": "GetStats",
}).Errorf("Unknown tcp stats file %s", tcp.StatsFile)
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here a new error should be created and returned, consider put here return fmt.Errorf("Unknown tcp stats file %s", tcp.StatsFile"). At the moment it looks like the last err's value is returned what means "nil".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return val.(int), nil
}

return 1, fmt.Errorf("could not find value for key %s", key)
Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it return 1? Returning 0 seems to be more appropriate, but up to you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


return stats
func (m *MockTcp6) GetStats(stats *container.Statistics, opts container.GetStatOpt) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mocking for Tcp6 might be removed after TCP6 structure is no longer used

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

func (opt GetStatOpt) GetStringValue(key string) (string, error) {
val, exists := opt[key]
if exists {
return val.(string), nil
Copy link
Contributor

@IzabellaRaulin IzabellaRaulin Jan 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding checking the result of type assertion. It might look like below:

func (opt GetStatOpt)getStringValue(key string)(string, error) {
  if val, exists := opt[key]; exists {
	if valStr, ok := val.(string); ok {
	    return valStr, nil
	} 
	return "", fmt.Errorf("Value for key `%s` is expected to be string, but it isn't", key)
  }	
  return "", fmt.Errorf("Cannot find value for key `%s`", key)
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check added

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@marcin-krolik marcin-krolik force-pushed the feat/refactor branch 7 times, most recently from 10c63e1 to 40c85d5 Compare January 19, 2017 11:11
@marcin-krolik marcin-krolik changed the title WIP: refactored version of collector Refactored version of collector Jan 19, 2017
Copy link
Contributor

@IzabellaRaulin IzabellaRaulin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@marcin-krolik marcin-krolik merged commit 350969e into intelsdi-x:master Jan 20, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add option to configure procfs path for use inside docker containers
5 participants