From c9603c6ea2f1fd07ab662f9ca5d2b5676357207f Mon Sep 17 00:00:00 2001 From: Mark Knapp Date: Fri, 13 Sep 2019 15:13:59 -0400 Subject: [PATCH 01/35] Add a flag to adjust mount timeout Signed-off-by: Mark Knapp --- collector/filesystem_linux.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/collector/filesystem_linux.go b/collector/filesystem_linux.go index 5ba4fe1573..98e51b8758 100644 --- a/collector/filesystem_linux.go +++ b/collector/filesystem_linux.go @@ -26,14 +26,17 @@ import ( "github.com/prometheus/common/log" "golang.org/x/sys/unix" + kingpin "gopkg.in/alecthomas/kingpin.v2" ) const ( defIgnoredMountPoints = "^/(dev|proc|sys|var/lib/docker/.+)($|/)" defIgnoredFSTypes = "^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|iso9660|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$" - mountTimeout = 30 * time.Second ) +var mountTimeout = kingpin.Flag("collector.filesystem.mount-timeout", + "how long to wait for a mount to respond before marking it as stale"). + Hidden().Default("5s").Duration() var stuckMounts = make(map[string]struct{}) var stuckMountsMtx = &sync.Mutex{} @@ -118,7 +121,7 @@ func stuckMountWatcher(mountPoint string, success chan struct{}) { select { case <-success: // Success - case <-time.After(mountTimeout): + case <-time.After(*mountTimeout): // Timed out, mark mount as stuck stuckMountsMtx.Lock() select { From d089776e8b81876610f3e07f068ab3fe769ef3b0 Mon Sep 17 00:00:00 2001 From: Sven Haardiek Date: Wed, 18 Sep 2019 21:31:15 +0200 Subject: [PATCH 02/35] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5ef96388a978c54173e1b1ec8e7bcb41fc7d130d Author: Sven Haardiek Date: Wed Sep 18 20:45:23 2019 +0200 block variables Signed-off-by: Sven Haardiek commit c1177382e241994618a8ab7dd9842027d597b0df Author: Sven Haardiek Date: Wed Sep 18 20:38:33 2019 +0200 Use SI Units Signed-off-by: Sven Haardiek commit 04e4f99c423872d3094f21f89a8235b233a01941 Merge: 5417c98 f3538e1 Author: Sven Haardiek Date: Wed Sep 18 19:20:17 2019 +0200 Merge branch 'master' into power_supply_class commit 5417c9820a40b37b490caedeaa3526883380b9bf Author: Sven Haardiek Date: Wed Sep 4 23:02:39 2019 +0200 Drop averages Signed-off-by: Sven Haardiek commit 1f1447dbe7bbdcdabebf4c968beb14c67d89dd9f Author: Sven Haardiek Date: Wed Sep 4 22:56:00 2019 +0200 Update Copyright Signed-off-by: Sven Haardiek commit 9677425059a3bf61cd7498cf7b5f05d5af7a626b Merge: 0b51589 d3478a2 Author: Sven Haardiek Date: Mon Sep 2 22:02:53 2019 +0200 Merge branch 'master' into power_supply_class commit 0b51589f390cc1b33ea4728d85fca3a3b231cf3f Author: PrometheusBot Date: Fri Aug 30 13:32:17 2019 +0200 makefile: update Makefile.common with newer version (#1466) Signed-off-by: prombot commit af2b9e849c7b69237b7fa0e9a289c929ec7173a0 Author: Boris Momčilović Date: Tue Aug 27 14:24:11 2019 +0200 Ipvs firewall mark (#1455) * IPVS: include firewall mark label Signed-off-by: Boris Momčilović commit 773f99de7f699900a00b4d35340e356fe7098ee7 Author: Paul Gier Date: Tue Aug 27 02:26:19 2019 -0500 update procfs to v0.0.4 (#1457) Signed-off-by: Paul Gier commit 6f8a4f4348f62700cbf7eeb2657851237e13c35d Author: beorn7 Date: Tue Aug 20 18:49:12 2019 +0200 Update legendLink This still had the 'k8s' in as it was copied and pasted from the kubernetes-mixin. Signed-off-by: beorn7 commit d758cf394cfbed9e87e116a24d72050066cd039a Author: beorn7 Date: Wed Aug 14 22:24:24 2019 +0200 Make the severity of "critical" alerts configurable This addresses the blissful scenario where single-node failures are unproblematic. No reason to wake somebody up if a node is about to screw itself up by filling the disk. Signed-off-by: beorn7 commit 041b9e1e785f5f43bbef97c0c76d205181d08890 Author: beorn7 Date: Thu Aug 15 16:43:57 2019 +0200 Add line for number of cores to load graph Backported from the node dashboard in the kubernetes-mixin. Signed-off-by: beorn7 commit 5552bb3a6b2be1e3dd1a93dbdb9650bd0363a922 Author: beorn7 Date: Thu Aug 15 16:36:10 2019 +0200 Fix title of CPU panel to usage We use the `mode="idle"` metric, but we are inverting it, so this is usage, and that's intended. Signed-off-by: beorn7 commit db0571b402233323ed7e222e53f7ef7738520f49 Author: beorn7 Date: Thu Aug 15 16:32:54 2019 +0200 node-mixin: Improve disk usage panel - Use a stacked graph instead of a gauge as development over time is especially useful for disk space usage. - By only taking one metric per device into account, we avoid double-counting for devices that are mounted multiple times. Signed-off-by: beorn7 commit 3822e096c5d27d06b9c9a68beff81ef23f12eb36 Author: Björn Rabenstein Date: Thu Aug 15 00:40:51 2019 +0200 node-mxin: Improve nodes dashboard (#1448) * node-mixin: Improve nodes dashboard - Use stacking where it makes sense. - Normalize idle CPU so that stacking is more meaningful. - Consistently fill where stacking is used but don't fill where not. - Fix y axis max value for Idle CPU panel. - Fix y axis min value for memory usage panel. - Use `$__interval` for range where applicable (and set min step to 1m). - Make the right Y axis for disk I/O actually work. This is just an incremental improvements. It doesn't touch the more involved TODOs. Signed-off-by: beorn7 commit fbced86b9835e1b196c15ddcac01ba3cfcf369cc Author: beorn7 Date: Tue Aug 13 21:54:28 2019 +0200 node-mixin: Fix various straight-forward issues in the USE dashboards - Normalize cluster memory utilisation. - Fix missing `1m` in memory saturation. - Have both disk-related row next to each other instead with the network row in between. - Correctly render transmit network traffic as negative, using `seriesOverrides` and `min: null` for the y-axis. - Make panel and row naming consistent. - Remove legend where it would just display a single entry with exactly the title of the panel. - Fix metric name in individual node CPU Saturation panel. - Break up disk space utilisation by device in the panel for an individual node. NB: All of that doesn't touch any more subtle issues captured in the various TODOs. Signed-off-by: beorn7 commit 5bdf0625023cf7d05e0f65c6b6a1303637772ca6 Author: Sandro Jäckel Date: Wed Aug 7 09:19:20 2019 +0200 Update rootfs syntax in Docker example (#1443) Signed-off-by: Sandro Jäckel commit b59f081d45a3ca65957900ec33772dca25a3066f Author: Phil Frost Date: Tue Aug 6 13:08:06 2019 -0400 Fix seconds reported by schedstat (#1426) Upstream bugfix: https://github.com/prometheus/procfs/pull/191 Signed-off-by: Phil Frost commit ac9a059ae81fa31f9963614483af3b5e3bfd672c Author: Sven Haardiek Date: Sun Aug 4 20:15:36 2019 +0200 Try to make it work for PowerPC Signed-off-by: Sven Haardiek commit c81acf3b009e8538783489d1468f33faf65d8b01 Merge: c064116 75462bf Author: Sven Haardiek Date: Sun Aug 4 20:14:16 2019 +0200 Merge remote-tracking branch 'upstream/master' into power_supply_class Signed-off-by: Sven Haardiek commit c0641162c3a432f29df30c8d0632a7756d7d2bff Merge: 06f6e3e 0b710bb Author: Sven Haardiek Date: Fri Aug 2 18:30:28 2019 +0200 Merge branch 'master' into power_supply_class Signed-off-by: Sven Haardiek commit 06f6e3e8b2a9b2e3f345b6d312a777731bb4b403 Author: Sven Haardiek Date: Fri Mar 22 15:36:03 2019 +0100 Fix Pull Request comments * concise metric conditions * combine info about power supply to one metric Signed-off-by: Sven Haardiek commit 785c3735c4626de56f8341f800ab7bb5e2594d08 Author: Sven Haardiek Date: Sat Mar 9 18:47:52 2019 +0100 Use sys.ttar instead of uploading the files Signed-off-by: Sven Haardiek commit e07bff5d938457147b9009aef7d42d763018cd66 Author: Sven Haardiek Date: Sat Mar 9 18:34:50 2019 +0100 Add information about from /sys/class/power_supply Signed-off-by: Sven Haardiek commit 55b3e34840c9dfc6513ae8e69b6479d5842a3091 Author: Sven Haardiek Date: Sat Mar 9 18:09:45 2019 +0100 Use cyclecount instead of cycle_count since it is a gauge Signed-off-by: Sven Haardiek commit 602350b333cf9353d2cd0ffd40206c96ffe29941 Author: Sven Haardiek Date: Sat Mar 9 18:09:25 2019 +0100 other build options Signed-off-by: Sven Haardiek commit 5aa38f678451d5b63ffdc32336345a1ff6703725 Author: Sven Haardiek Date: Sat Mar 9 18:08:56 2019 +0100 Update fixtures Signed-off-by: Sven Haardiek commit c6acc474a4224b8d9f7b178d0d2e02636d8629ea Author: Sven Haardiek Date: Sat Mar 9 17:20:30 2019 +0100 Update command line parameter flag Signed-off-by: Sven Haardiek commit f5a329e6ae5ed3b16aa866d67b944f1a73edfe42 Author: Sven Haardiek Date: Sat Mar 9 17:20:06 2019 +0100 Update procfs dependency Signed-off-by: Sven Haardiek commit 38d5fa5165643d6a44dc863b3a1696774259ac0d Merge: 5a7ce69 28f3582 Author: Sven Haardiek Date: Sat Mar 9 16:28:29 2019 +0100 Merge branch 'power_supply_class' of github.com:shaardie/node_exporter into power_supply_class commit 5a7ce69505079c9c090e44448cfbd7ffb2b04df7 Author: Sven Haardiek Date: Sat Oct 20 18:55:49 2018 +0200 Updated Metrics of Power Supply Class Signed-off-by: Sven Haardiek commit 690ab1b9c1f2e183b7088cf81c7f266d85ee6df6 Author: Sven Haardiek Date: Fri Oct 19 20:03:42 2018 +0200 Start work on Power Supply Collector Signed-off-by: Sven Haardiek commit 28f358222bbac4315fbf44d94da36d4b0ff2ed55 Author: Sven Haardiek Date: Sat Oct 20 18:55:49 2018 +0200 Updated Metrics of Power Supply Class Signed-off-by: Sven Haardiek commit 751d99b818503e9a4430b10c39760f180349b294 Author: Sven Haardiek Date: Fri Oct 19 20:03:42 2018 +0200 Start work on Power Supply Collector Signed-off-by: Sven Haardiek Signed-off-by: Sven Haardiek --- collector/fixtures/e2e-64k-page-output.txt | 35 +++ collector/fixtures/e2e-output.txt | 35 +++ collector/fixtures/sys.ttar | 283 +++++++++++++++++++++ collector/powersupplyclass.go | 194 ++++++++++++++ 4 files changed, 547 insertions(+) create mode 100644 collector/powersupplyclass.go diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index c7fe830fe8..729deff9c8 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2393,6 +2393,40 @@ node_nfsd_server_rpcs_total 18628 # HELP node_nfsd_server_threads Total number of NFSd kernel threads that are running. # TYPE node_nfsd_server_threads gauge node_nfsd_server_threads 8 +# HELP node_power_supply_capacity capacity value of /sys/class/power_supply/. +# TYPE node_power_supply_capacity gauge +node_power_supply_capacity{power_supply="BAT0"} 81 +# HELP node_power_supply_cyclecount cyclecount value of /sys/class/power_supply/. +# TYPE node_power_supply_cyclecount gauge +node_power_supply_cyclecount{power_supply="BAT0"} 0 +# HELP node_power_supply_energy_full energy_full value of /sys/class/power_supply/. +# TYPE node_power_supply_energy_full gauge +node_power_supply_energy_full{power_supply="BAT0"} 4.507e+07 +# HELP node_power_supply_energy_full_design energy_full_design value of /sys/class/power_supply/. +# TYPE node_power_supply_energy_full_design gauge +node_power_supply_energy_full_design{power_supply="BAT0"} 4.752e+07 +# HELP node_power_supply_energy_now energy_now value of /sys/class/power_supply/. +# TYPE node_power_supply_energy_now gauge +node_power_supply_energy_now{power_supply="BAT0"} 3.658e+07 +# HELP node_power_supply_info info of /sys/class/power_supply/. +# TYPE node_power_supply_info gauge +node_power_supply_info{power_supply="AC",type="Mains"} 1 +node_power_supply_info{capacity_level="Normal",manufacturer="LGC",model_name="LNV-45N1",power_supply="BAT0",serial_number="38109",status="Discharging",technology="Li-ion",type="Battery"} 1 +# HELP node_power_supply_online online value of /sys/class/power_supply/. +# TYPE node_power_supply_online gauge +node_power_supply_online{power_supply="AC"} 0 +# HELP node_power_supply_power_now power_now value of /sys/class/power_supply/. +# TYPE node_power_supply_power_now gauge +node_power_supply_power_now{power_supply="BAT0"} 5.002e+06 +# HELP node_power_supply_present present value of /sys/class/power_supply/. +# TYPE node_power_supply_present gauge +node_power_supply_present{power_supply="BAT0"} 1 +# HELP node_power_supply_voltage_min_design voltage_min_design value of /sys/class/power_supply/. +# TYPE node_power_supply_voltage_min_design gauge +node_power_supply_voltage_min_design{power_supply="BAT0"} 1.08e+07 +# HELP node_power_supply_voltage_now voltage_now value of /sys/class/power_supply/. +# TYPE node_power_supply_voltage_now gauge +node_power_supply_voltage_now{power_supply="BAT0"} 1.166e+07 # HELP node_pressure_cpu_waiting_seconds_total Total time in seconds that processes have waited for CPU time # TYPE node_pressure_cpu_waiting_seconds_total counter node_pressure_cpu_waiting_seconds_total 14.036781000000001 @@ -2492,6 +2526,7 @@ node_scrape_collector_success{collector="netdev"} 1 node_scrape_collector_success{collector="netstat"} 1 node_scrape_collector_success{collector="nfs"} 1 node_scrape_collector_success{collector="nfsd"} 1 +node_scrape_collector_success{collector="powersupplyclass"} 1 node_scrape_collector_success{collector="pressure"} 1 node_scrape_collector_success{collector="processes"} 1 node_scrape_collector_success{collector="qdisc"} 1 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index a10db8a4c0..41129c4a23 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2393,6 +2393,40 @@ node_nfsd_server_rpcs_total 18628 # HELP node_nfsd_server_threads Total number of NFSd kernel threads that are running. # TYPE node_nfsd_server_threads gauge node_nfsd_server_threads 8 +# HELP node_power_supply_capacity capacity value of /sys/class/power_supply/. +# TYPE node_power_supply_capacity gauge +node_power_supply_capacity{power_supply="BAT0"} 81 +# HELP node_power_supply_cyclecount cyclecount value of /sys/class/power_supply/. +# TYPE node_power_supply_cyclecount gauge +node_power_supply_cyclecount{power_supply="BAT0"} 0 +# HELP node_power_supply_energy_full energy_full value of /sys/class/power_supply/. +# TYPE node_power_supply_energy_full gauge +node_power_supply_energy_full{power_supply="BAT0"} 45.07 +# HELP node_power_supply_energy_full_design energy_full_design value of /sys/class/power_supply/. +# TYPE node_power_supply_energy_full_design gauge +node_power_supply_energy_full_design{power_supply="BAT0"} 47.52 +# HELP node_power_supply_energy_watthour energy_watthour value of /sys/class/power_supply/. +# TYPE node_power_supply_energy_watthour gauge +node_power_supply_energy_watthour{power_supply="BAT0"} 36.58 +# HELP node_power_supply_info info of /sys/class/power_supply/. +# TYPE node_power_supply_info gauge +node_power_supply_info{power_supply="AC",type="Mains"} 1 +node_power_supply_info{capacity_level="Normal",manufacturer="LGC",model_name="LNV-45N1",power_supply="BAT0",serial_number="38109",status="Discharging",technology="Li-ion",type="Battery"} 1 +# HELP node_power_supply_online online value of /sys/class/power_supply/. +# TYPE node_power_supply_online gauge +node_power_supply_online{power_supply="AC"} 0 +# HELP node_power_supply_power_watt power_watt value of /sys/class/power_supply/. +# TYPE node_power_supply_power_watt gauge +node_power_supply_power_watt{power_supply="BAT0"} 5.002 +# HELP node_power_supply_present present value of /sys/class/power_supply/. +# TYPE node_power_supply_present gauge +node_power_supply_present{power_supply="BAT0"} 1 +# HELP node_power_supply_voltage_min_design voltage_min_design value of /sys/class/power_supply/. +# TYPE node_power_supply_voltage_min_design gauge +node_power_supply_voltage_min_design{power_supply="BAT0"} 10.8 +# HELP node_power_supply_voltage_volt voltage_volt value of /sys/class/power_supply/. +# TYPE node_power_supply_voltage_volt gauge +node_power_supply_voltage_volt{power_supply="BAT0"} 11.66 # HELP node_pressure_cpu_waiting_seconds_total Total time in seconds that processes have waited for CPU time # TYPE node_pressure_cpu_waiting_seconds_total counter node_pressure_cpu_waiting_seconds_total 14.036781000000001 @@ -2492,6 +2526,7 @@ node_scrape_collector_success{collector="netdev"} 1 node_scrape_collector_success{collector="netstat"} 1 node_scrape_collector_success{collector="nfs"} 1 node_scrape_collector_success{collector="nfsd"} 1 +node_scrape_collector_success{collector="powersupplyclass"} 1 node_scrape_collector_success{collector="pressure"} 1 node_scrape_collector_success{collector="processes"} 1 node_scrape_collector_success{collector="qdisc"} 1 diff --git a/collector/fixtures/sys.ttar b/collector/fixtures/sys.ttar index 8502ec6b44..27aa0a7954 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -995,6 +995,289 @@ Lines: 1 1 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/power_supply +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/power_supply/AC +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/online +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/power_supply/AC/power +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/async +Lines: 1 +disabled +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/autosuspend_delay_ms +Lines: 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/control +Lines: 1 +auto +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/runtime_active_kids +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/runtime_active_time +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/runtime_enabled +Lines: 1 +disabled +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/runtime_status +Lines: 1 +unsupported +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/runtime_suspended_time +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/runtime_usage +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup +Lines: 1 +enabled +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_abort_count +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_active +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_active_count +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_count +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_expire_count +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_last_time_ms +Lines: 1 +7888 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_max_time_ms +Lines: 1 +2 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_prevent_sleep_time_ms +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/power/wakeup_total_time_ms +Lines: 1 +2 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/type +Lines: 1 +Mains +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/AC/uevent +Lines: 2 +POWER_SUPPLY_NAME=AC +POWER_SUPPLY_ONLINE=0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/power_supply/BAT0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/alarm +Lines: 1 +2253000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/capacity +Lines: 1 +81 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/capacity_level +Lines: 1 +Normal +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/charge_start_threshold +Lines: 1 +95 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/charge_stop_threshold +Lines: 1 +100 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/cycle_count +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/energy_full +Lines: 1 +45070000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/energy_full_design +Lines: 1 +47520000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/energy_now +Lines: 1 +36580000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/manufacturer +Lines: 1 +LGC +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/model_name +Lines: 1 +LNV-45N1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/power_supply/BAT0/power +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/async +Lines: 1 +disabled +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/autosuspend_delay_ms +Lines: 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/control +Lines: 1 +auto +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/runtime_active_kids +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/runtime_active_time +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/runtime_enabled +Lines: 1 +disabled +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/runtime_status +Lines: 1 +unsupported +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/runtime_suspended_time +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power/runtime_usage +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/power_now +Lines: 1 +5002000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/present +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/serial_number +Lines: 1 +38109 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/status +Lines: 1 +Discharging +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/technology +Lines: 1 +Li-ion +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/type +Lines: 1 +Battery +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/uevent +Lines: 16 +POWER_SUPPLY_NAME=BAT0 +POWER_SUPPLY_STATUS=Discharging +POWER_SUPPLY_PRESENT=1 +POWER_SUPPLY_TECHNOLOGY=Li-ion +POWER_SUPPLY_CYCLE_COUNT=0 +POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000 +POWER_SUPPLY_VOLTAGE_NOW=11660000 +POWER_SUPPLY_POWER_NOW=5002000 +POWER_SUPPLY_ENERGY_FULL_DESIGN=47520000 +POWER_SUPPLY_ENERGY_FULL=45070000 +POWER_SUPPLY_ENERGY_NOW=36580000 +POWER_SUPPLY_CAPACITY=81 +POWER_SUPPLY_CAPACITY_LEVEL=Normal +POWER_SUPPLY_MODEL_NAME=LNV-45N1 +POWER_SUPPLY_MANUFACTURER=LGC +POWER_SUPPLY_SERIAL_NUMBER=38109 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/voltage_min_design +Lines: 1 +10800000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/power_supply/BAT0/voltage_now +Lines: 1 +11660000 +Mode: 444 Directory: sys/class/thermal Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/collector/powersupplyclass.go b/collector/powersupplyclass.go new file mode 100644 index 0000000000..3b5c8d7fc7 --- /dev/null +++ b/collector/powersupplyclass.go @@ -0,0 +1,194 @@ +// Copyright 2019 The Prometheus Authors +// 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. + +// +build !nopowersupplyclass +// +build linux + +package collector + +import ( + "fmt" + "regexp" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/procfs/sysfs" + "gopkg.in/alecthomas/kingpin.v2" +) + +var ( + powerSupplyClassIgnoredPowerSupplies = kingpin.Flag("collector.powersupply.ignored-supplies", "Regexp of power supplies to ignore for powersupplyclass collector.").Default("^$").String() +) + +type powerSupplyClassCollector struct { + subsystem string + ignoredPattern *regexp.Regexp + metricDescs map[string]*prometheus.Desc +} + +func init() { + registerCollector("powersupplyclass", defaultEnabled, NewPowerSupplyClassCollector) +} + +func NewPowerSupplyClassCollector() (Collector, error) { + pattern := regexp.MustCompile(*powerSupplyClassIgnoredPowerSupplies) + return &powerSupplyClassCollector{ + subsystem: "power_supply", + ignoredPattern: pattern, + metricDescs: map[string]*prometheus.Desc{}, + }, nil +} + +func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error { + powerSupplyClass, err := getPowerSupplyClassInfo(c.ignoredPattern) + if err != nil { + return fmt.Errorf("could not get power_supply class info: %s", err) + } + for _, powerSupply := range powerSupplyClass { + + for name, value := range map[string]*int64{ + "authentic": powerSupply.Authentic, + "calibrate": powerSupply.Calibrate, + "capacity": powerSupply.Capacity, + "capacity_alert_max": powerSupply.CapacityAlertMax, + "capacity_alert_min": powerSupply.CapacityAlertMin, + "cyclecount": powerSupply.CycleCount, + "online": powerSupply.Online, + "present": powerSupply.Present, + "time_to_empty_seconds": powerSupply.TimeToEmptyNow, + "time_to_full_seconds": powerSupply.TimeToFullNow, + } { + if value != nil { + pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value), powerSupply.Name, prometheus.GaugeValue) + } + } + + for name, value := range map[string]*int64{ + "current_boot": powerSupply.CurrentBoot, + "current_max": powerSupply.CurrentMax, + "current_ampere": powerSupply.CurrentNow, + "energy_empty": powerSupply.EnergyEmpty, + "energy_empty_design": powerSupply.EnergyEmptyDesign, + "energy_full": powerSupply.EnergyFull, + "energy_full_design": powerSupply.EnergyFullDesign, + "energy_watthour": powerSupply.EnergyNow, + "voltage_boot": powerSupply.VoltageBoot, + "voltage_max": powerSupply.VoltageMax, + "voltage_max_design": powerSupply.VoltageMaxDesign, + "voltage_min": powerSupply.VoltageMin, + "voltage_min_design": powerSupply.VoltageMinDesign, + "voltage_volt": powerSupply.VoltageNow, + "voltage_ocv": powerSupply.VoltageOCV, + "charge_control_limit": powerSupply.ChargeControlLimit, + "charge_control_limit_max": powerSupply.ChargeControlLimitMax, + "charge_counter": powerSupply.ChargeCounter, + "charge_empty": powerSupply.ChargeEmpty, + "charge_empty_design": powerSupply.ChargeEmptyDesign, + "charge_full": powerSupply.ChargeFull, + "charge_full_design": powerSupply.ChargeFullDesign, + "charge_ampere": powerSupply.ChargeNow, + "charge_term_current": powerSupply.ChargeTermCurrent, + "constant_charge_current": powerSupply.ConstantChargeCurrent, + "constant_charge_current_max": powerSupply.ConstantChargeCurrentMax, + "constant_charge_voltage": powerSupply.ConstantChargeVoltage, + "constant_charge_voltage_max": powerSupply.ConstantChargeVoltageMax, + "precharge_current": powerSupply.PrechargeCurrent, + "input_current_limit": powerSupply.InputCurrentLimit, + "power_watt": powerSupply.PowerNow, + } { + if value != nil { + pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/1e6, powerSupply.Name, prometheus.GaugeValue) + } + } + + for name, value := range map[string]*int64{ + "temp_celsius": powerSupply.Temp, + "temp_alert_max_celsius": powerSupply.TempAlertMax, + "temp_alert_min_celsius": powerSupply.TempAlertMin, + "temp_ambient_celsius": powerSupply.TempAmbient, + "temp_ambient_max_celsius": powerSupply.TempAmbientMax, + "temp_ambient_min_celsius": powerSupply.TempAmbientMin, + "temp_max_celsius": powerSupply.TempMax, + "temp_min_celsius": powerSupply.TempMin, + } { + if value != nil { + pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/10.0, powerSupply.Name, prometheus.GaugeValue) + } + } + + var ( + keys []string + values []string + ) + for name, value := range map[string]string{ + "power_supply": powerSupply.Name, + "capacity_level": powerSupply.CapacityLevel, + "charge_type": powerSupply.ChargeType, + "health": powerSupply.Health, + "manufacturer": powerSupply.Manufacturer, + "model_name": powerSupply.ModelName, + "serial_number": powerSupply.SerialNumber, + "status": powerSupply.Status, + "technology": powerSupply.Technology, + "type": powerSupply.Type, + "usb_type": powerSupply.UsbType, + "scope": powerSupply.Scope, + } { + if value != "" { + keys = append(keys, name) + values = append(values, value) + } + } + + fieldDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, c.subsystem, "info"), + "info of /sys/class/power_supply/.", + keys, + nil, + ) + ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...) + + } + + return nil +} + +func pushPowerSupplyMetric(ch chan<- prometheus.Metric, subsystem string, name string, value float64, powerSupplyName string, valueType prometheus.ValueType) { + fieldDesc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, subsystem, name), + fmt.Sprintf("%s value of /sys/class/power_supply/.", name), + []string{"power_supply"}, + nil, + ) + + ch <- prometheus.MustNewConstMetric(fieldDesc, valueType, value, powerSupplyName) +} + +func getPowerSupplyClassInfo(ignore *regexp.Regexp) (sysfs.PowerSupplyClass, error) { + fs, err := sysfs.NewFS(*sysPath) + if err != nil { + return nil, err + } + powerSupplyClass, err := fs.PowerSupplyClass() + + if err != nil { + return powerSupplyClass, fmt.Errorf("error obtaining power_supply class info: %s", err) + } + + for device := range powerSupplyClass { + if ignore.MatchString(device) { + delete(powerSupplyClass, device) + } + } + + return powerSupplyClass, nil +} From 8864a7bf0d06b636a1fd28f27259856ef999c419 Mon Sep 17 00:00:00 2001 From: Roman Pertl <533172+roock@users.noreply.github.com> Date: Mon, 28 Oct 2019 09:21:45 +0100 Subject: [PATCH 03/35] Fix grammer in examples/init.d (#1497) Fix the grammar of an error message. Signed-off-by: Roman Pertl --- examples/init.d/node_exporter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/init.d/node_exporter b/examples/init.d/node_exporter index 12d28aa527..32c2deefdf 100755 --- a/examples/init.d/node_exporter +++ b/examples/init.d/node_exporter @@ -10,7 +10,7 @@ OPTIONS="--web.listen-address=:9100" if [ -f /etc/rc.d/init.d/functions ]; then . /etc/rc.d/init.d/functions else - echo "/etc/rc.d/init.d/functions is not exists" + echo "/etc/rc.d/init.d/functions does not exist" exit 0 fi From 5a7b85876d6108a91f0d8673c0d7eca38687671b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Knecht?= Date: Thu, 24 Oct 2019 18:13:36 +0200 Subject: [PATCH 04/35] docs/node-mixin: Improve memory pressure rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `instance:node_memory_swap_io_pages:rate1m` rule was intended to measure the amount of memory pressure a system is under, but its name is a bit misleading (it specifically refers to swap), and the rate of `node_vmstat_pgmajfault` is a better metric for memory pressure (see #1524). This commit renames `instance:node_memory_swap_io_pages:rate1m` to `instance:node_vmstat_pgmajfault:rate1m`, and defines it as `rate(node_vmstat_pgmajfault{%(nodeExporterSelector)s}[1m])`. The dashboards are updated accordingly. Signed-off-by: Benoît Knecht --- docs/node-mixin/dashboards/use.libsonnet | 8 ++++---- docs/node-mixin/rules/rules.libsonnet | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/node-mixin/dashboards/use.libsonnet b/docs/node-mixin/dashboards/use.libsonnet index 8463ab95ac..d2a568f53a 100644 --- a/docs/node-mixin/dashboards/use.libsonnet +++ b/docs/node-mixin/dashboards/use.libsonnet @@ -53,8 +53,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, ) .addPanel( - g.panel('Memory Saturation (Swapped Pages)') + - g.queryPanel('instance:node_memory_swap_io_pages:rate1m{%(nodeExporterSelector)s}' % $._config, '{{instance}}', legendLink) + + g.panel('Memory Saturation (Major Page Faults)') + + g.queryPanel('instance:node_vmstat_pgmajfault:rate1m{%(nodeExporterSelector)s}' % $._config, '{{instance}}', legendLink) + g.stack + { yaxes: g.yaxes('rps') }, ) @@ -201,8 +201,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; { yaxes: g.yaxes('percentunit') }, ) .addPanel( - g.panel('Memory Saturation (pages swapped per second)') + - g.queryPanel('instance:node_memory_swap_io_pages:rate1m{%(nodeExporterSelector)s, instance="$instance"}' % $._config, 'Swap IO') + + g.panel('Memory Saturation (Major Page Faults)') + + g.queryPanel('instance:node_vmstat_pgmajfault:rate1m{%(nodeExporterSelector)s, instance="$instance"}' % $._config, 'Major page faults') + { yaxes: g.yaxes('short'), legend+: { show: false }, diff --git a/docs/node-mixin/rules/rules.libsonnet b/docs/node-mixin/rules/rules.libsonnet index 85f7618d81..6b396e3bd3 100644 --- a/docs/node-mixin/rules/rules.libsonnet +++ b/docs/node-mixin/rules/rules.libsonnet @@ -50,13 +50,9 @@ ||| % $._config, }, { - record: 'instance:node_memory_swap_io_pages:rate1m', + record: 'instance:node_vmstat_pgmajfault:rate1m', expr: ||| - ( - rate(node_vmstat_pgpgin{%(nodeExporterSelector)s}[1m]) - + - rate(node_vmstat_pgpgout{%(nodeExporterSelector)s}[1m]) - ) + rate(node_vmstat_pgmajfault{%(nodeExporterSelector)s}[1m]) ||| % $._config, }, { From c6914477f511fad69f6121c0abf25cdf035ae374 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Wed, 30 Oct 2019 22:52:36 +0100 Subject: [PATCH 05/35] Fix the normalization for the cluster-wide dashboards We actually have to count or sum, respectively, _all_ the selected metrics for the cluster-wide view. Which means it's easiest to use the `scalar` approach after all (but only in the cluster dashboard). This still propagates all the labels. I have extended the comment for the `nodeExporterSelector` to note that the cluster dashboard only makes sense if all the selected node exporter actually belong to the same cluster. Since this is jsonnet, users can easily disable the cluster dashboard. Or even create multiple instances of the dashboards with different `nodeExporterSelector`s for different clusters. Signed-off-by: beorn7 --- docs/node-mixin/config.libsonnet | 7 +++- docs/node-mixin/dashboards/use.libsonnet | 48 +++++++----------------- 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/docs/node-mixin/config.libsonnet b/docs/node-mixin/config.libsonnet index b25c393960..fdea71d218 100644 --- a/docs/node-mixin/config.libsonnet +++ b/docs/node-mixin/config.libsonnet @@ -2,7 +2,12 @@ _config+:: { // Selectors are inserted between {} in Prometheus queries. - // Select the metrics coming from the node exporter. + // Select the metrics coming from the node exporter. Note that all + // the selected metrics are shown stacked on top of each other in + // the 'USE Method / Cluster' dashboard. Consider disabling that + // dashboard if mixing up all those metrics in the same dashboard + // doesn't make sense (e.g. because they are coming from different + // clusters). nodeExporterSelector: 'job="node"', // Select the fstype for filesystem-related queries. If left diff --git a/docs/node-mixin/dashboards/use.libsonnet b/docs/node-mixin/dashboards/use.libsonnet index d2a568f53a..3d2e16d432 100644 --- a/docs/node-mixin/dashboards/use.libsonnet +++ b/docs/node-mixin/dashboards/use.libsonnet @@ -15,9 +15,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; instance:node_cpu_utilisation:rate1m{%(nodeExporterSelector)s} * instance:node_num_cpu:sum{%(nodeExporterSelector)s} - / ignoring (instance) group_left - sum without (instance) (instance:node_num_cpu:sum{%(nodeExporterSelector)s}) ) + / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s})) ||| % $._config, '{{instance}}', legendLink) + g.stack + { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, @@ -27,11 +26,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; // average relates to the "CPU saturation" in the title. g.panel('CPU Saturation (load1 per CPU)') + g.queryPanel(||| - ( - instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s} - / ignoring (instance) group_left - count without (instance) (instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s}) - ) + instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s} + / scalar(count(instance:node_load1_per_cpu:ratio{%(nodeExporterSelector)s})) ||| % $._config, '{{instance}}', legendLink) + g.stack + // TODO: Does `max: 1` make sense? The stack can go over 1 in high-load scenarios. @@ -43,11 +39,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; .addPanel( g.panel('Memory Utilisation') + g.queryPanel(||| - ( - instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s} - / ignoring (instance) group_left - count without (instance) (instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s}) - ) + instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s} + / scalar(count(instance:node_memory_utilisation:ratio{%(nodeExporterSelector)s})) ||| % $._config, '{{instance}}', legendLink) + g.stack + { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, @@ -123,11 +116,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; // TODO: Does the partition by device make sense? Using the most utilized device per // instance might make more sense. g.queryPanel(||| - ( - instance_device:node_disk_io_time_seconds:rate1m{%(nodeExporterSelector)s} - / ignoring (instance, device) group_left - count without (instance, device) (instance_device:node_disk_io_time_seconds:rate1m{%(nodeExporterSelector)s}) - ) + instance_device:node_disk_io_time_seconds:rate1m{%(nodeExporterSelector)s} + / scalar(count(instance_device:node_disk_io_time_seconds:rate1m{%(nodeExporterSelector)s})) ||| % $._config, '{{instance}} {{device}}', legendLink) + g.stack + { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, @@ -135,11 +125,8 @@ local g = import 'grafana-builder/grafana.libsonnet'; .addPanel( g.panel('Disk IO Saturation') + g.queryPanel(||| - ( - instance_device:node_disk_io_time_weighted_seconds:rate1m{%(nodeExporterSelector)s} - / ignoring (instance, device) group_left - count without (instance, device) (instance_device:node_disk_io_time_weighted_seconds:rate1m{%(nodeExporterSelector)s}) - ) + instance_device:node_disk_io_time_weighted_seconds:rate1m{%(nodeExporterSelector)s} + / scalar(count(instance_device:node_disk_io_time_weighted_seconds:rate1m{%(nodeExporterSelector)s})) ||| % $._config, '{{instance}} {{device}}', legendLink) + g.stack + { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, @@ -150,19 +137,12 @@ local g = import 'grafana-builder/grafana.libsonnet'; .addPanel( g.panel('Disk Space Utilisation') + g.queryPanel(||| - ( - sum without (device) ( - max without (fstype, mountpoint) ( - node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s} - node_filesystem_avail_bytes{%(nodeExporterSelector)s, %(fsSelector)s} - ) - ) - / ignoring (instance) group_left - sum without (instance, device) ( - max without (fstype, mountpoint) ( - node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s} - ) + sum without (device) ( + max without (fstype, mountpoint) ( + node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s} - node_filesystem_avail_bytes{%(nodeExporterSelector)s, %(fsSelector)s} ) - ) + ) + / scalar(sum(max without (fstype, mountpoint) (node_filesystem_size_bytes{%(nodeExporterSelector)s, %(fsSelector)s}))) ||| % $._config, '{{instance}}', legendLink) + g.stack + { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, From 40570924b1c23883237c8d55efdf81858dbeee92 Mon Sep 17 00:00:00 2001 From: paulfantom Date: Fri, 1 Nov 2019 15:43:31 +0100 Subject: [PATCH 06/35] docs/node-mixin/dashboards: do not mix tabs and spaces Signed-off-by: paulfantom --- docs/node-mixin/dashboards/use.libsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/node-mixin/dashboards/use.libsonnet b/docs/node-mixin/dashboards/use.libsonnet index 3d2e16d432..3c855a6064 100644 --- a/docs/node-mixin/dashboards/use.libsonnet +++ b/docs/node-mixin/dashboards/use.libsonnet @@ -16,7 +16,7 @@ local g = import 'grafana-builder/grafana.libsonnet'; * instance:node_num_cpu:sum{%(nodeExporterSelector)s} ) - / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s})) + / scalar(sum(instance:node_num_cpu:sum{%(nodeExporterSelector)s})) ||| % $._config, '{{instance}}', legendLink) + g.stack + { yaxes: g.yaxes({ format: 'percentunit', max: 1 }) }, From aede04172cbcd072629e0a5f43cd0435e26a28d4 Mon Sep 17 00:00:00 2001 From: ksherryBAE <44226893+ksherryBAE@users.noreply.github.com> Date: Fri, 15 Nov 2019 23:12:57 +0000 Subject: [PATCH 07/35] Adding TLS to node exporter - cleaner version (#1277) Add support for https connections. Signed-off-by: ksherryBAE Signed-off-by: James Ritchie Signed-off-by: Simon Pasquier Signed-off-by: Ben RIdley --- .circleci/config.yml | 2 +- go.mod | 2 + go.sum | 4 + https/README.md | 24 + https/testdata/server.crt | 96 + https/testdata/server.key | 28 + https/testdata/tls-ca-chain.pem | 173 ++ .../tls_config_auth_clientCAs_invalid.bad.yml | 4 + .../tls_config_auth_clientCAs_missing.bad.yml | 4 + https/testdata/tls_config_empty.yml | 0 https/testdata/tls_config_junk.yml | 20 + https/testdata/tls_config_noAuth.bad.yml | 4 + .../tls_config_noAuth.good.blocking.yml | 5 + https/testdata/tls_config_noAuth.good.yml | 5 + .../tls_config_noAuth_certPath_empty.bad.yml | 3 + ...tls_config_noAuth_certPath_invalid.bad.yml | 3 + ...nfig_noAuth_certPath_keyPath_empty.bad.yml | 3 + ...ig_noAuth_certPath_keyPath_invalid.bad.yml | 3 + .../tls_config_noAuth_keyPath_empty.bad.yml | 3 + .../tls_config_noAuth_keyPath_invalid.bad.yml | 3 + https/tls_config.go | 123 + https/tls_config_test.go | 362 +++ https/web-config.yml | 10 + node_exporter.go | 8 +- tls_config_noAuth.bad.yml | 0 vendor/github.com/pkg/errors/.gitignore | 24 + vendor/github.com/pkg/errors/.travis.yml | 15 + vendor/github.com/pkg/errors/LICENSE | 23 + vendor/github.com/pkg/errors/README.md | 52 + vendor/github.com/pkg/errors/appveyor.yml | 32 + vendor/github.com/pkg/errors/errors.go | 282 ++ vendor/github.com/pkg/errors/stack.go | 147 + vendor/gopkg.in/yaml.v2/.travis.yml | 12 + vendor/gopkg.in/yaml.v2/LICENSE | 201 ++ vendor/gopkg.in/yaml.v2/LICENSE.libyaml | 31 + vendor/gopkg.in/yaml.v2/NOTICE | 13 + vendor/gopkg.in/yaml.v2/README.md | 133 + vendor/gopkg.in/yaml.v2/apic.go | 739 +++++ vendor/gopkg.in/yaml.v2/decode.go | 775 +++++ vendor/gopkg.in/yaml.v2/emitterc.go | 1685 +++++++++++ vendor/gopkg.in/yaml.v2/encode.go | 390 +++ vendor/gopkg.in/yaml.v2/go.mod | 5 + vendor/gopkg.in/yaml.v2/parserc.go | 1095 +++++++ vendor/gopkg.in/yaml.v2/readerc.go | 412 +++ vendor/gopkg.in/yaml.v2/resolve.go | 258 ++ vendor/gopkg.in/yaml.v2/scannerc.go | 2696 +++++++++++++++++ vendor/gopkg.in/yaml.v2/sorter.go | 113 + vendor/gopkg.in/yaml.v2/writerc.go | 26 + vendor/gopkg.in/yaml.v2/yaml.go | 466 +++ vendor/gopkg.in/yaml.v2/yamlh.go | 738 +++++ vendor/gopkg.in/yaml.v2/yamlprivateh.go | 173 ++ vendor/modules.txt | 4 + 52 files changed, 11430 insertions(+), 2 deletions(-) create mode 100644 https/README.md create mode 100644 https/testdata/server.crt create mode 100644 https/testdata/server.key create mode 100644 https/testdata/tls-ca-chain.pem create mode 100644 https/testdata/tls_config_auth_clientCAs_invalid.bad.yml create mode 100644 https/testdata/tls_config_auth_clientCAs_missing.bad.yml create mode 100644 https/testdata/tls_config_empty.yml create mode 100644 https/testdata/tls_config_junk.yml create mode 100644 https/testdata/tls_config_noAuth.bad.yml create mode 100644 https/testdata/tls_config_noAuth.good.blocking.yml create mode 100644 https/testdata/tls_config_noAuth.good.yml create mode 100644 https/testdata/tls_config_noAuth_certPath_empty.bad.yml create mode 100644 https/testdata/tls_config_noAuth_certPath_invalid.bad.yml create mode 100644 https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml create mode 100644 https/testdata/tls_config_noAuth_certPath_keyPath_invalid.bad.yml create mode 100644 https/testdata/tls_config_noAuth_keyPath_empty.bad.yml create mode 100644 https/testdata/tls_config_noAuth_keyPath_invalid.bad.yml create mode 100644 https/tls_config.go create mode 100644 https/tls_config_test.go create mode 100644 https/web-config.yml create mode 100644 tls_config_noAuth.bad.yml create mode 100644 vendor/github.com/pkg/errors/.gitignore create mode 100644 vendor/github.com/pkg/errors/.travis.yml create mode 100644 vendor/github.com/pkg/errors/LICENSE create mode 100644 vendor/github.com/pkg/errors/README.md create mode 100644 vendor/github.com/pkg/errors/appveyor.yml create mode 100644 vendor/github.com/pkg/errors/errors.go create mode 100644 vendor/github.com/pkg/errors/stack.go create mode 100644 vendor/gopkg.in/yaml.v2/.travis.yml create mode 100644 vendor/gopkg.in/yaml.v2/LICENSE create mode 100644 vendor/gopkg.in/yaml.v2/LICENSE.libyaml create mode 100644 vendor/gopkg.in/yaml.v2/NOTICE create mode 100644 vendor/gopkg.in/yaml.v2/README.md create mode 100644 vendor/gopkg.in/yaml.v2/apic.go create mode 100644 vendor/gopkg.in/yaml.v2/decode.go create mode 100644 vendor/gopkg.in/yaml.v2/emitterc.go create mode 100644 vendor/gopkg.in/yaml.v2/encode.go create mode 100644 vendor/gopkg.in/yaml.v2/go.mod create mode 100644 vendor/gopkg.in/yaml.v2/parserc.go create mode 100644 vendor/gopkg.in/yaml.v2/readerc.go create mode 100644 vendor/gopkg.in/yaml.v2/resolve.go create mode 100644 vendor/gopkg.in/yaml.v2/scannerc.go create mode 100644 vendor/gopkg.in/yaml.v2/sorter.go create mode 100644 vendor/gopkg.in/yaml.v2/writerc.go create mode 100644 vendor/gopkg.in/yaml.v2/yaml.go create mode 100644 vendor/gopkg.in/yaml.v2/yamlh.go create mode 100644 vendor/gopkg.in/yaml.v2/yamlprivateh.go diff --git a/.circleci/config.yml b/.circleci/config.yml index dea2b5917c..9fe2779823 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ jobs: steps: - checkout - run: sudo pip install codespell - - run: codespell --skip=".git,./vendor,ttar,go.mod,go.sum" -L uint,packages\',uptodate + - run: codespell --skip=".git,./vendor,ttar,go.mod,go.sum,*pem" -L uint,packages\',uptodate build: machine: diff --git a/go.mod b/go.mod index 43c90eb053..c1a58fb161 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/mattn/go-xmlrpc v0.0.1 github.com/mdlayher/genetlink v0.0.0-20190828143517-e35f2bf499b9 // indirect github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee + github.com/pkg/errors v0.8.1 github.com/prometheus/client_golang v1.0.0 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 github.com/prometheus/common v0.7.0 @@ -22,6 +23,7 @@ require ( golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect golang.org/x/sys v0.0.0-20190902133755-9109b7679e13 gopkg.in/alecthomas/kingpin.v2 v2.2.6 + gopkg.in/yaml.v2 v2.2.2 ) go 1.13 diff --git a/go.sum b/go.sum index bc0f23affa..9d615c6fbe 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -130,6 +131,9 @@ golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/https/README.md b/https/README.md new file mode 100644 index 0000000000..b8c7ac4e95 --- /dev/null +++ b/https/README.md @@ -0,0 +1,24 @@ +# HTTPS Package for Prometheus + +The `https` directory contains a Go package and a sample configuration file for running `node_exporter` with HTTPS instead of HTTP. +When running a server with TLS use the flag `--web.config` + +e.g. `./node_exporter --web.config="web-config.yml"` +If the config is kept within the https directory. + +The config file should be written in YAML format, and is reloaded on each connection to check for new certificates and/or authentication policy. + +##Sample Config: +``` +tlsConfig : + # Certificate and key files for server to use to authenticate to client + tlsCertPath : + tlsKeyPath : + + # Server policy for client authentication. Maps to ClientAuth Policies + # For more detail on clientAuth options: [ClientAuthType](https://golang.org/pkg/crypto/tls/#ClientAuthType) + [ clientAuth : | default = "NoClientCert" ] + + # CA certificate for client certificate authentication to the server + [ clientCAs : ] +``` diff --git a/https/testdata/server.crt b/https/testdata/server.crt new file mode 100644 index 0000000000..2ead96984b --- /dev/null +++ b/https/testdata/server.crt @@ -0,0 +1,96 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus TLS CA + Validity + Not Before: Apr 5 08:06:57 2019 GMT + Not After : Mar 26 08:06:57 2059 GMT + Subject: C=US, O=Prometheus, CN=prometheus.example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:bd:6c:b6:7f:d1:2f:be:e4:41:eb:5d:ff:50:78: + 03:2b:76:03:da:01:48:20:13:90:66:c9:ce:6e:06: + e5:fa:2d:0d:c0:b0:46:28:44:10:a0:61:79:87:a2: + 98:4c:29:fa:f9:bb:0f:44:c7:90:5c:5c:55:60:cd: + 45:da:b8:e4:dd:28:72:c8:8b:a1:3e:4b:00:09:82: + b0:2c:dc:d6:17:c9:02:f4:cd:26:c7:11:28:f3:77: + b5:97:c2:76:c2:e0:07:d7:34:5b:e0:ed:1a:59:a5: + b4:b7:16:09:3d:35:bd:d9:03:07:9d:7c:3b:f0:63: + bd:5e:02:99:cf:32:e1:ac:4c:7a:3e:4c:b2:8e:98: + 68:07:4f:59:dc:0d:bf:cc:83:04:5c:d8:90:f0:73: + da:2b:08:17:c4:36:a7:d8:94:3d:b6:c0:af:29:0a: + d3:19:5f:eb:7d:cc:4d:05:56:11:0a:ee:b1:f3:d7: + c9:5a:3c:8c:57:16:91:51:14:f8:20:4e:0f:29:9e: + 04:21:e6:f1:e4:e8:44:af:d7:25:92:08:64:fc:2c: + 1c:2e:4f:71:53:91:53:1d:e5:f9:7b:52:0f:21:da: + 5c:dd:19:68:96:ca:70:6a:f1:c4:0d:07:af:f8:65: + 13:92:e9:ef:65:b3:89:86:fd:c0:74:5c:a4:6b:49: + 62:c5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Basic Constraints: + CA:FALSE + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Subject Key Identifier: + 00:61:01:AD:25:44:8A:EF:E1:2C:EC:83:5A:3A:3B:EA:A0:BD:E1:45 + X509v3 Authority Key Identifier: + keyid:4D:02:BF:71:95:6A:AA:58:C5:9C:B8:83:67:5E:64:16:99:E1:2A:9E + + Authority Information Access: + CA Issuers - URI:http://example.com/ca/tls-ca.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://example.com/ca/tls-ca.crl + + X509v3 Subject Alternative Name: + IP Address:127.0.0.1, IP Address:127.0.0.0, DNS:localhost + Signature Algorithm: sha1WithRSAEncryption + 77:97:e4:ef:db:10:8e:62:50:96:4a:6e:f5:a4:f9:1f:19:3b: + c8:a4:dd:b3:f6:11:41:1a:fb:e3:f8:dd:0e:64:e5:2b:00:b9: + e6:25:9f:2e:e1:d2:9a:cd:b6:f2:41:4d:27:dd:2c:9a:af:97: + 79:e8:cf:61:fb:cf:be:25:c6:e1:19:a0:c8:90:44:a0:76:8a: + 45:d4:37:22:e5:d4:80:b4:b3:0f:a8:33:08:24:ad:21:0b:b7: + 98:46:93:90:8a:ae:77:0c:cb:b8:59:d3:3b:9b:fb:16:5a:22: + ca:c2:97:9d:78:1b:fc:23:fc:a0:42:54:40:de:88:4b:07:2b: + 19:4e:0e:79:bf:c9:9f:01:a6:46:c5:55:fa:9f:c0:0d:8a:a6: + e1:47:16:a6:0e:be:23:c9:e9:58:d6:31:71:8c:80:9c:16:64: + f0:14:08:22:a1:23:7c:98:b9:62:d1:4a:ce:e3:5c:59:fb:41: + 87:a5:3b:36:dd:3d:45:48:b0:b0:77:6f:de:58:2a:27:4d:56: + 20:54:08:20:c8:6d:79:b5:b9:e6:3a:03:24:0f:6d:67:39:20: + 78:10:2f:47:85:83:c1:4d:17:33:79:84:75:27:fa:47:67:59: + 56:cc:33:7b:a5:77:aa:59:9a:98:30:10:1a:78:43:34:8f:ed: + c2:a1:a3:ea +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET +MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkxGjAYBgNVBAMMEVByb21ldGhldXMgVExTIENBMCAXDTE5 +MDQwNTA4MDY1N1oYDzIwNTkwMzI2MDgwNjU3WjBDMQswCQYDVQQGEwJVUzETMBEG +A1UECgwKUHJvbWV0aGV1czEfMB0GA1UEAwwWcHJvbWV0aGV1cy5leGFtcGxlLmNv +bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1stn/RL77kQetd/1B4 +Ayt2A9oBSCATkGbJzm4G5fotDcCwRihEEKBheYeimEwp+vm7D0THkFxcVWDNRdq4 +5N0ocsiLoT5LAAmCsCzc1hfJAvTNJscRKPN3tZfCdsLgB9c0W+DtGlmltLcWCT01 +vdkDB518O/BjvV4Cmc8y4axMej5Mso6YaAdPWdwNv8yDBFzYkPBz2isIF8Q2p9iU +PbbArykK0xlf633MTQVWEQrusfPXyVo8jFcWkVEU+CBODymeBCHm8eToRK/XJZII +ZPwsHC5PcVORUx3l+XtSDyHaXN0ZaJbKcGrxxA0Hr/hlE5Lp72WziYb9wHRcpGtJ +YsUCAwEAAaOCAREwggENMA4GA1UdDwEB/wQEAwIFoDAJBgNVHRMEAjAAMB0GA1Ud +JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUAGEBrSVEiu/hLOyD +Wjo76qC94UUwHwYDVR0jBBgwFoAUTQK/cZVqqljFnLiDZ15kFpnhKp4wPAYIKwYB +BQUHAQEEMDAuMCwGCCsGAQUFBzAChiBodHRwOi8vZXhhbXBsZS5jb20vY2EvdGxz +LWNhLmNlcjAxBgNVHR8EKjAoMCagJKAihiBodHRwOi8vZXhhbXBsZS5jb20vY2Ev +dGxzLWNhLmNybDAgBgNVHREEGTAXhwR/AAABhwR/AAAAgglsb2NhbGhvc3QwDQYJ +KoZIhvcNAQEFBQADggEBAHeX5O/bEI5iUJZKbvWk+R8ZO8ik3bP2EUEa++P43Q5k +5SsAueYlny7h0prNtvJBTSfdLJqvl3noz2H7z74lxuEZoMiQRKB2ikXUNyLl1IC0 +sw+oMwgkrSELt5hGk5CKrncMy7hZ0zub+xZaIsrCl514G/wj/KBCVEDeiEsHKxlO +Dnm/yZ8BpkbFVfqfwA2KpuFHFqYOviPJ6VjWMXGMgJwWZPAUCCKhI3yYuWLRSs7j +XFn7QYelOzbdPUVIsLB3b95YKidNViBUCCDIbXm1ueY6AyQPbWc5IHgQL0eFg8FN +FzN5hHUn+kdnWVbMM3uld6pZmpgwEBp4QzSP7cKho+o= +-----END CERTIFICATE----- diff --git a/https/testdata/server.key b/https/testdata/server.key new file mode 100644 index 0000000000..e1226c0e1f --- /dev/null +++ b/https/testdata/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9bLZ/0S++5EHr +Xf9QeAMrdgPaAUggE5Bmyc5uBuX6LQ3AsEYoRBCgYXmHophMKfr5uw9Ex5BcXFVg +zUXauOTdKHLIi6E+SwAJgrAs3NYXyQL0zSbHESjzd7WXwnbC4AfXNFvg7RpZpbS3 +Fgk9Nb3ZAwedfDvwY71eApnPMuGsTHo+TLKOmGgHT1ncDb/MgwRc2JDwc9orCBfE +NqfYlD22wK8pCtMZX+t9zE0FVhEK7rHz18laPIxXFpFRFPggTg8pngQh5vHk6ESv +1yWSCGT8LBwuT3FTkVMd5fl7Ug8h2lzdGWiWynBq8cQNB6/4ZROS6e9ls4mG/cB0 +XKRrSWLFAgMBAAECggEAezQ0V1o11dEc1vuiTjJgzWnLA4aF5OcUquZjb8jo2Blp +soR0fUgYEFiV9RRaPl+nr7ptKe0rBgfAOGALKUHNCdN/JNU8oQmjEoyADg3s6jeB +xruQlzWgDwszf2uqVwHj16Nkhx1wYBKZQeQBSmCkBHwl/daKHcahqn3CkLOleKx+ +Qlc3BzWNaGte6qpJMs0It3by1FuxRwVz5VkL8uhzj0WIOYMA84t0gTnFH9gfRO3F +licotxg/Nl5M36wWcfL8Jq++72AtaKcD1jUEwuQpogrVeqflmeHwn/TlL++Hv6Xe +Lq0jt3OCUKUV40eq9c5uEgTmyrVHMDkfFdXzutdMAQKBgQDsSMXk7P4SX6u6uTjV +In9eWw6ZyJ2aL6VB9co/NMsj49GrrFT8VX9d+JPe9P/n6tuGcFbymNep22njRksR +0ItpW1NFRR/R3g0kYe1EhkRpNm6fhY9oIuR9xhcNnPNYkqAKT3T/dxrzbwsNhomi +X8aht/eCz4ZsK/KdOGTkPozxgQKBgQDNOvrclT1Wl4bxONp9pEV5XpRSD/qigfIp +i5wxy7ihX/QY9RToIWJDnzMVLnEYe64RB2WB8/4WwNPOQcuaxXbFUFct/2NdhTnS +ToJPgPe819zW9t1FLTf1fHtsRBpGFtbhdlUDOiOtJiMXYiwlRh2uyWFhjOo8TNUE +qMwai0vLRQKBgQCDH4t6lC4W4jK5x2oLlT5bjWqX2uXjF8e8x/q5gsGspBPKEjOD +aKrq6jSdSRbui73RaGxH6pvb7iBf+LVWKIYFLKIUUdzrqS9f3lw+Z8h1HrjbG9JO +dvaX+aL3cf71S0E3F4sU7fLt3tSiZ+PfUQk424+mbyXox6a2qwIKS9AJgQKBgHCu +dHROYJo9ojKpo5Ueb6K+4jLYYSV+sYZMCBtzHlFETNKzJaJ6SeiU7Ugw8pmdtqnU +5M/gNl8pymFR0MeOqbKWdPdlZJpBfsjQoE2kouEFqFRCwKStui7IBUAheEeJXLv3 +659U+aek69l35oMkp0GDgjs8UpN/H+pp/36Hgrr9AoGAftWU405rpStHEdRVrazP +FibQesT9HOdJgmm1gNIhj+PnFs7lKER9p0Wdl79QnIqjwyhjCXL94TFerzTKLY2c +IRj5dcRHiiT0iK8wq8bzGNYCqV73oQXaUFMiutNAArXwzwuvPFPWNBQsjLzeDLeC +mcOsCcPAk8cLYtVfZo2sP3g= +-----END PRIVATE KEY----- diff --git a/https/testdata/tls-ca-chain.pem b/https/testdata/tls-ca-chain.pem new file mode 100644 index 0000000000..722264d89b --- /dev/null +++ b/https/testdata/tls-ca-chain.pem @@ -0,0 +1,173 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus Root CA + Validity + Not Before: Apr 5 08:00:37 2019 GMT + Not After : Mar 26 08:00:37 2059 GMT + Subject: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus TLS CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:aa:d2:34:6b:ed:f1:f4:01:08:e5:00:9f:75:c8: + ba:fc:4b:72:c6:04:93:af:f1:f6:b5:ce:01:0d:c6: + bd:d3:16:98:9d:e5:51:56:12:58:16:ee:18:6e:f0: + 68:a9:42:16:65:cf:e3:31:f5:90:79:9d:13:32:87: + 3b:1f:65:fd:84:88:a4:56:3d:26:54:69:05:27:5a: + ea:89:02:e7:31:9b:7d:7f:76:93:54:70:bc:17:92: + 06:9f:9f:90:4a:8a:cf:82:a7:7b:7c:71:c4:fa:34: + 56:00:32:1a:85:c5:f8:e4:4a:63:43:37:9d:60:84: + 4d:78:6e:87:12:c4:2b:1f:93:a5:fe:cc:5e:f1:df: + c1:97:ff:b7:3e:20:38:1d:71:15:11:ec:6c:7a:cc: + 0e:87:52:31:b1:b9:74:c3:07:1c:42:4b:1e:c1:17: + bc:e4:13:b7:b0:20:2e:c4:07:93:bd:a8:11:f9:da: + a7:d0:df:4a:48:be:9b:6d:65:c3:ae:58:56:c0:9f: + 17:c5:d8:32:b1:04:22:fb:5b:18:f6:20:10:50:ec: + 2d:10:4f:cc:48:8f:f2:75:dd:33:a4:0e:f5:55:da: + 2c:89:a1:3a:52:bb:11:11:0b:97:27:17:73:35:da: + 10:71:b3:9f:a8:42:91:e6:3a:66:00:f9:e5:11:8f: + 5b:57 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + 4D:02:BF:71:95:6A:AA:58:C5:9C:B8:83:67:5E:64:16:99:E1:2A:9E + X509v3 Authority Key Identifier: + keyid:3C:1E:A8:C6:4C:05:4D:20:EC:88:DB:29:D4:7B:F9:12:5D:CE:EA:1A + + Authority Information Access: + CA Issuers - URI:https://example.com/ca/root-ca.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:https://example.com/ca/root-ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 63:fc:ba:30:a5:05:d6:76:14:f1:77:38:b1:41:6f:81:d9:b4: + 02:fd:bc:e5:f6:d9:e6:73:e0:71:cf:4c:fb:13:b5:6b:bd:b9: + c6:f6:28:18:36:e1:8c:d9:93:b3:78:4a:3d:39:1b:f4:fb:69: + 75:24:ae:e1:a0:2f:94:05:bf:10:3c:3e:d2:2b:a8:f3:31:25: + 2e:ed:13:ad:60:5d:22:9a:26:15:20:86:98:73:4c:f6:4b:48: + b8:1f:67:ba:4e:c9:47:ed:85:dc:38:dc:02:0c:fb:54:d5:2e: + 6c:b4:95:18:51:d1:ae:ea:e8:fb:b4:19:50:04:bc:31:7e:51: + 9e:85:29:4d:c8:f7:26:d6:d6:8d:35:2d:9e:e2:06:16:38:e2: + 56:80:ec:f3:a3:34:e3:28:c4:e8:10:d0:8a:a6:6f:20:9a:b9: + dc:b9:90:6b:ba:8a:27:2c:29:72:28:55:e7:59:a6:a7:90:ec: + 32:e8:d0:26:4a:c1:44:dd:20:bf:dc:4d:1e:7e:cc:e5:a2:5b: + e8:df:3d:4b:01:aa:48:56:17:e9:29:d8:71:83:05:36:8c:11: + 4f:77:b8:95:20:b7:c7:21:06:c2:87:97:b4:6b:d3:f7:23:ba: + 4d:5f:15:d1:0c:4d:6e:f1:6a:9d:57:5c:02:6a:d7:31:18:ef: + 5c:fc:f8:04 +-----BEGIN CERTIFICATE----- +MIIELTCCAxWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJVUzET +MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkxGzAZBgNVBAMMElByb21ldGhldXMgUm9vdCBDQTAgFw0x +OTA0MDUwODAwMzdaGA8yMDU5MDMyNjA4MDAzN1owaTELMAkGA1UEBhMCVVMxEzAR +BgNVBAoMClByb21ldGhldXMxKTAnBgNVBAsMIFByb21ldGhldXMgQ2VydGlmaWNh +dGUgQXV0aG9yaXR5MRowGAYDVQQDDBFQcm9tZXRoZXVzIFRMUyBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKrSNGvt8fQBCOUAn3XIuvxLcsYEk6/x +9rXOAQ3GvdMWmJ3lUVYSWBbuGG7waKlCFmXP4zH1kHmdEzKHOx9l/YSIpFY9JlRp +BSda6okC5zGbfX92k1RwvBeSBp+fkEqKz4Kne3xxxPo0VgAyGoXF+ORKY0M3nWCE +TXhuhxLEKx+Tpf7MXvHfwZf/tz4gOB1xFRHsbHrMDodSMbG5dMMHHEJLHsEXvOQT +t7AgLsQHk72oEfnap9DfSki+m21lw65YVsCfF8XYMrEEIvtbGPYgEFDsLRBPzEiP +8nXdM6QO9VXaLImhOlK7ERELlycXczXaEHGzn6hCkeY6ZgD55RGPW1cCAwEAAaOB +3DCB2TAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E +FgQUTQK/cZVqqljFnLiDZ15kFpnhKp4wHwYDVR0jBBgwFoAUPB6oxkwFTSDsiNsp +1Hv5El3O6howPgYIKwYBBQUHAQEEMjAwMC4GCCsGAQUFBzAChiJodHRwczovL2V4 +YW1wbGUuY29tL2NhL3Jvb3QtY2EuY2VyMDMGA1UdHwQsMCowKKAmoCSGImh0dHBz +Oi8vZXhhbXBsZS5jb20vY2Evcm9vdC1jYS5jcmwwDQYJKoZIhvcNAQEFBQADggEB +AGP8ujClBdZ2FPF3OLFBb4HZtAL9vOX22eZz4HHPTPsTtWu9ucb2KBg24YzZk7N4 +Sj05G/T7aXUkruGgL5QFvxA8PtIrqPMxJS7tE61gXSKaJhUghphzTPZLSLgfZ7pO +yUfthdw43AIM+1TVLmy0lRhR0a7q6Pu0GVAEvDF+UZ6FKU3I9ybW1o01LZ7iBhY4 +4laA7POjNOMoxOgQ0IqmbyCaudy5kGu6iicsKXIoVedZpqeQ7DLo0CZKwUTdIL/c +TR5+zOWiW+jfPUsBqkhWF+kp2HGDBTaMEU93uJUgt8chBsKHl7Rr0/cjuk1fFdEM +TW7xap1XXAJq1zEY71z8+AQ= +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus Root CA + Validity + Not Before: Apr 5 07:55:00 2019 GMT + Not After : Mar 26 07:55:00 2059 GMT + Subject: C=US, O=Prometheus, OU=Prometheus Certificate Authority, CN=Prometheus Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:bf:b9:e2:ab:5f:61:22:e1:4e:cd:ee:da:b0:26: + 2e:bb:b0:7e:1c:ce:10:be:16:29:35:0c:0c:1d:93: + 01:29:2a:f6:f9:c2:6e:5c:10:44:ca:f8:dc:ad:7a: + 06:64:0f:8a:18:ad:b2:a2:94:49:c9:ba:8c:45:94: + 7c:d9:e0:11:45:d8:16:79:a2:20:9f:8c:63:60:72: + 2a:5b:f9:66:80:ac:85:67:01:5a:eb:91:c1:d2:88: + 87:9e:4c:18:c9:f2:f0:7a:18:c0:e6:ab:2c:78:de: + 5f:b2:22:4e:94:9c:f5:cd:e6:e2:33:30:e9:20:10: + a6:a1:75:eb:59:ab:45:a9:f7:3e:54:40:ae:05:25: + be:74:c5:3a:fd:af:73:16:60:45:7c:4a:e0:0e:0d: + a1:15:7f:9a:1f:c2:a7:04:ad:ef:b3:e4:f6:00:2c: + 4e:0b:04:90:49:ee:d3:db:a6:12:c4:91:0b:32:4f: + 11:84:c7:c4:8a:ef:51:66:7a:b0:20:2f:cb:95:8d: + 96:57:60:66:5e:f9:4f:5a:94:9c:71:ad:eb:ca:70: + 3e:62:06:c2:3a:29:f8:9e:86:af:da:07:78:f8:31: + af:42:48:49:9e:4a:df:1b:27:1f:44:35:81:6d:fa: + 7a:c5:6a:0a:35:23:c7:c4:d5:fe:c9:9e:61:c9:30: + cd:1f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Subject Key Identifier: + 3C:1E:A8:C6:4C:05:4D:20:EC:88:DB:29:D4:7B:F9:12:5D:CE:EA:1A + X509v3 Authority Key Identifier: + keyid:3C:1E:A8:C6:4C:05:4D:20:EC:88:DB:29:D4:7B:F9:12:5D:CE:EA:1A + + Signature Algorithm: sha1WithRSAEncryption + 56:2f:79:e5:12:91:f5:19:a7:d1:32:28:fd:e3:9d:8f:e1:3c: + bb:a3:a5:f2:55:8a:03:ad:2c:1d:18:82:e1:7f:19:75:d9:47: + 5b:e7:7c:e4:a5:e0:eb:dc:7e:24:a3:7d:99:1a:cf:39:ba:a5: + b4:b8:45:68:83:cf:70:ad:56:f2:34:73:65:fc:6c:b0:53:9a: + 79:04:f7:3e:7e:4b:22:1b:e7:76:23:20:bc:9c:05:a2:5d:01: + d2:f0:09:49:17:b2:61:74:1a:5b:f4:e0:fd:ce:11:ba:13:4a: + e6:07:11:7d:30:e2:11:87:ee:33:1a:68:de:67:f4:ac:b5:58: + 1a:ac:cf:7a:2d:fd:c3:44:5b:4b:cd:6c:ff:f6:49:b4:55:4a: + 09:a0:92:2d:57:3b:69:85:54:3e:e9:ec:ef:b2:a5:7a:29:75: + 2b:f8:eb:4b:d4:cf:68:ee:3e:c8:63:7e:12:eb:e4:2f:63:a3: + a7:c8:0f:e9:39:ff:5c:29:65:7f:25:f0:42:bf:07:ba:06:b8: + 5e:d6:56:ba:f8:67:56:1b:42:aa:b3:04:d8:6e:88:10:a5:70: + b5:81:04:a4:90:a3:f0:83:4d:0c:6b:12:5d:a4:4c:83:5a:ff: + a8:7a:86:61:ff:0f:4c:e5:0f:17:d1:64:3c:bd:d9:22:7e:b7: + fa:9b:83:ba +-----BEGIN CERTIFICATE----- +MIIDtDCCApygAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJVUzET +MBEGA1UECgwKUHJvbWV0aGV1czEpMCcGA1UECwwgUHJvbWV0aGV1cyBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkxGzAZBgNVBAMMElByb21ldGhldXMgUm9vdCBDQTAgFw0x +OTA0MDUwNzU1MDBaGA8yMDU5MDMyNjA3NTUwMFowajELMAkGA1UEBhMCVVMxEzAR +BgNVBAoMClByb21ldGhldXMxKTAnBgNVBAsMIFByb21ldGhldXMgQ2VydGlmaWNh +dGUgQXV0aG9yaXR5MRswGQYDVQQDDBJQcm9tZXRoZXVzIFJvb3QgQ0EwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/ueKrX2Ei4U7N7tqwJi67sH4czhC+ +Fik1DAwdkwEpKvb5wm5cEETK+NytegZkD4oYrbKilEnJuoxFlHzZ4BFF2BZ5oiCf +jGNgcipb+WaArIVnAVrrkcHSiIeeTBjJ8vB6GMDmqyx43l+yIk6UnPXN5uIzMOkg +EKahdetZq0Wp9z5UQK4FJb50xTr9r3MWYEV8SuAODaEVf5ofwqcEre+z5PYALE4L +BJBJ7tPbphLEkQsyTxGEx8SK71FmerAgL8uVjZZXYGZe+U9alJxxrevKcD5iBsI6 +Kfiehq/aB3j4Ma9CSEmeSt8bJx9ENYFt+nrFago1I8fE1f7JnmHJMM0fAgMBAAGj +YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQ8 +HqjGTAVNIOyI2ynUe/kSXc7qGjAfBgNVHSMEGDAWgBQ8HqjGTAVNIOyI2ynUe/kS +Xc7qGjANBgkqhkiG9w0BAQUFAAOCAQEAVi955RKR9Rmn0TIo/eOdj+E8u6Ol8lWK +A60sHRiC4X8ZddlHW+d85KXg69x+JKN9mRrPObqltLhFaIPPcK1W8jRzZfxssFOa +eQT3Pn5LIhvndiMgvJwFol0B0vAJSReyYXQaW/Tg/c4RuhNK5gcRfTDiEYfuMxpo +3mf0rLVYGqzPei39w0RbS81s//ZJtFVKCaCSLVc7aYVUPuns77Kleil1K/jrS9TP +aO4+yGN+EuvkL2Ojp8gP6Tn/XCllfyXwQr8Huga4XtZWuvhnVhtCqrME2G6IEKVw +tYEEpJCj8INNDGsSXaRMg1r/qHqGYf8PTOUPF9FkPL3ZIn63+puDug== +-----END CERTIFICATE----- diff --git a/https/testdata/tls_config_auth_clientCAs_invalid.bad.yml b/https/testdata/tls_config_auth_clientCAs_invalid.bad.yml new file mode 100644 index 0000000000..c34cc4fea1 --- /dev/null +++ b/https/testdata/tls_config_auth_clientCAs_invalid.bad.yml @@ -0,0 +1,4 @@ +tlsConfig : + tlsCertPath : "testdata/server.crt" + tlsKeyPath : "testdata/server.key" + clientCAs : "somefile" \ No newline at end of file diff --git a/https/testdata/tls_config_auth_clientCAs_missing.bad.yml b/https/testdata/tls_config_auth_clientCAs_missing.bad.yml new file mode 100644 index 0000000000..fc92932ccb --- /dev/null +++ b/https/testdata/tls_config_auth_clientCAs_missing.bad.yml @@ -0,0 +1,4 @@ +tlsConfig : + tlsCertPath : "testdata/server.crt" + tlsKeyPath : "testdata/server.key" + clientAuth : "RequireAndVerifyClientCert" \ No newline at end of file diff --git a/https/testdata/tls_config_empty.yml b/https/testdata/tls_config_empty.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/https/testdata/tls_config_junk.yml b/https/testdata/tls_config_junk.yml new file mode 100644 index 0000000000..568a7c4041 --- /dev/null +++ b/https/testdata/tls_config_junk.yml @@ -0,0 +1,20 @@ +hWkNKCp3fvIx3jKnsaBI +TuEjdwNS8A2vYdFbiKqr +ay3RiOtykgt4m6m3KOol +ZreGpJRGmpDSVV9cioiF +r7kDOHhHU2frvv0nLcY2 +uQMQM4XgqFkCG6gFAIJZ +g99tTkrZhN9b6pkJ6J2y +rzdt729HrA2RblDGYfjs +MW7GxrBdlCnliYJGPhfr +g9kaXxMXcDwsw0C0rv0u +637ZmfRGElb6VBVOtgqn +RG0MRezjLYCJQBMUdRDE +RzO4VicAzj7asVZAT3oo +nPw267UONk7h7KBYRgch +Alj38foWqjV3heXXdahm +TrMzMgl6JIQ1x4OZB5i4 +qlrXFJoeV6Pr77nuiEh9 +3yE5vMnnKHm2nImEfzMG +bI01UDObHRSaoJLC0vTD +G9tlcKU883NkQ6nsxJ8Y diff --git a/https/testdata/tls_config_noAuth.bad.yml b/https/testdata/tls_config_noAuth.bad.yml new file mode 100644 index 0000000000..f0dd228833 --- /dev/null +++ b/https/testdata/tls_config_noAuth.bad.yml @@ -0,0 +1,4 @@ +tlsConfig : + tlsCertPath : "testdata/server.crt" + tlsKeyPath : "testdata/server.key" + clientCAs : "testdata/tls-ca-chain.pem" diff --git a/https/testdata/tls_config_noAuth.good.blocking.yml b/https/testdata/tls_config_noAuth.good.blocking.yml new file mode 100644 index 0000000000..f567693b5f --- /dev/null +++ b/https/testdata/tls_config_noAuth.good.blocking.yml @@ -0,0 +1,5 @@ +tlsConfig : + tlsCertPath : "testdata/server.crt" + tlsKeyPath : "testdata/server.key" + clientAuth : "RequireAndVerifyClientCert" + clientCAs: "testdata/tls-ca-chain.pem" \ No newline at end of file diff --git a/https/testdata/tls_config_noAuth.good.yml b/https/testdata/tls_config_noAuth.good.yml new file mode 100644 index 0000000000..76e46cf4ce --- /dev/null +++ b/https/testdata/tls_config_noAuth.good.yml @@ -0,0 +1,5 @@ +tlsConfig : + tlsCertPath : "testdata/server.crt" + tlsKeyPath : "testdata/server.key" + clientAuth : "VerifyClientCertIfGiven" + clientCAs : "testdata/tls-ca-chain.pem" diff --git a/https/testdata/tls_config_noAuth_certPath_empty.bad.yml b/https/testdata/tls_config_noAuth_certPath_empty.bad.yml new file mode 100644 index 0000000000..39c7abd210 --- /dev/null +++ b/https/testdata/tls_config_noAuth_certPath_empty.bad.yml @@ -0,0 +1,3 @@ +tlsConfig : + tlsCertPath : "" + tlsKeyPath : "testdata/server.key" \ No newline at end of file diff --git a/https/testdata/tls_config_noAuth_certPath_invalid.bad.yml b/https/testdata/tls_config_noAuth_certPath_invalid.bad.yml new file mode 100644 index 0000000000..5bdbd1a7fa --- /dev/null +++ b/https/testdata/tls_config_noAuth_certPath_invalid.bad.yml @@ -0,0 +1,3 @@ +tlsConfig : + tlsCertPath : "somefile" + tlsKeyPath : "testdata/server.key" \ No newline at end of file diff --git a/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml b/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml new file mode 100644 index 0000000000..938e5d6ddf --- /dev/null +++ b/https/testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml @@ -0,0 +1,3 @@ +tlsConfig : + tlsCertPath : "" + tlsKeyPath : "" \ No newline at end of file diff --git a/https/testdata/tls_config_noAuth_certPath_keyPath_invalid.bad.yml b/https/testdata/tls_config_noAuth_certPath_keyPath_invalid.bad.yml new file mode 100644 index 0000000000..b93ffd6969 --- /dev/null +++ b/https/testdata/tls_config_noAuth_certPath_keyPath_invalid.bad.yml @@ -0,0 +1,3 @@ +tlsConfig : + tlsCertPath : "somefile" + tlsKeyPath : "somefile" \ No newline at end of file diff --git a/https/testdata/tls_config_noAuth_keyPath_empty.bad.yml b/https/testdata/tls_config_noAuth_keyPath_empty.bad.yml new file mode 100644 index 0000000000..424f92f1db --- /dev/null +++ b/https/testdata/tls_config_noAuth_keyPath_empty.bad.yml @@ -0,0 +1,3 @@ +tlsConfig : + tlsCertPath : "testdata/server.crt" + tlsKeyPath : "" \ No newline at end of file diff --git a/https/testdata/tls_config_noAuth_keyPath_invalid.bad.yml b/https/testdata/tls_config_noAuth_keyPath_invalid.bad.yml new file mode 100644 index 0000000000..2625074bca --- /dev/null +++ b/https/testdata/tls_config_noAuth_keyPath_invalid.bad.yml @@ -0,0 +1,3 @@ +tlsConfig : + tlsCertPath : "testdata/server.cert" + tlsKeyPath : "somefile" \ No newline at end of file diff --git a/https/tls_config.go b/https/tls_config.go new file mode 100644 index 0000000000..dd473d89c3 --- /dev/null +++ b/https/tls_config.go @@ -0,0 +1,123 @@ +// Copyright 2019 The Prometheus Authors +// 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 https allows the implementation of TLS. +package https + +import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" + "net/http" + + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +type Config struct { + TLSConfig TLSStruct `yaml:"tlsConfig"` +} + +type TLSStruct struct { + TLSCertPath string `yaml:"tlsCertPath"` + TLSKeyPath string `yaml:"tlsKeyPath"` + ClientAuth string `yaml:"clientAuth"` + ClientCAs string `yaml:"clientCAs"` +} + +func getTLSConfig(configPath string) (*tls.Config, error) { + content, err := ioutil.ReadFile(configPath) + if err != nil { + return nil, err + } + c := &Config{} + err = yaml.Unmarshal(content, c) + if err != nil { + return nil, err + } + return configToTLSConfig(&c.TLSConfig) +} + +func configToTLSConfig(c *TLSStruct) (*tls.Config, error) { + cfg := &tls.Config{} + if len(c.TLSCertPath) == 0 { + return nil, errors.New("missing TLSCertPath") + } + if len(c.TLSKeyPath) == 0 { + return nil, errors.New("missing TLSKeyPath") + } + loadCert := func() (*tls.Certificate, error) { + cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath) + if err != nil { + return nil, errors.Wrap(err, "failed to load X509KeyPair") + } + return &cert, nil + } + // Confirm that certificate and key paths are valid. + if _, err := loadCert(); err != nil { + return nil, err + } + cfg.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { + return loadCert() + } + + if len(c.ClientCAs) > 0 { + clientCAPool := x509.NewCertPool() + clientCAFile, err := ioutil.ReadFile(c.ClientCAs) + if err != nil { + return nil, err + } + clientCAPool.AppendCertsFromPEM(clientCAFile) + cfg.ClientCAs = clientCAPool + } + if len(c.ClientAuth) > 0 { + switch s := (c.ClientAuth); s { + case "NoClientCert": + cfg.ClientAuth = tls.NoClientCert + case "RequestClientCert": + cfg.ClientAuth = tls.RequestClientCert + case "RequireClientCert": + cfg.ClientAuth = tls.RequireAnyClientCert + case "VerifyClientCertIfGiven": + cfg.ClientAuth = tls.VerifyClientCertIfGiven + case "RequireAndVerifyClientCert": + cfg.ClientAuth = tls.RequireAndVerifyClientCert + case "": + cfg.ClientAuth = tls.NoClientCert + default: + return nil, errors.New("Invalid ClientAuth: " + s) + } + } + if len(c.ClientCAs) > 0 && cfg.ClientAuth == tls.NoClientCert { + return nil, errors.New("Client CA's have been configured without a Client Auth Policy") + } + return cfg, nil +} + +// Listen starts the server on the given address. If tlsConfigPath isn't empty the server connection will be started using TLS. +func Listen(server *http.Server, tlsConfigPath string) error { + if (tlsConfigPath) == "" { + return server.ListenAndServe() + } + var err error + server.TLSConfig, err = getTLSConfig(tlsConfigPath) + if err != nil { + return err + } + // Set the GetConfigForClient method of the HTTPS server so that the config + // and certs are reloaded on new connections. + server.TLSConfig.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) { + return getTLSConfig(tlsConfigPath) + } + return server.ListenAndServeTLS("", "") +} diff --git a/https/tls_config_test.go b/https/tls_config_test.go new file mode 100644 index 0000000000..717f201c48 --- /dev/null +++ b/https/tls_config_test.go @@ -0,0 +1,362 @@ +// Copyright 2019 The Prometheus Authors +// 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 https + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "net" + "net/http" + "regexp" + "sync" + "testing" + "time" +) + +var ( + port = getPort() + + ErrorMap = map[string]*regexp.Regexp{ + "HTTP Response to HTTPS": regexp.MustCompile(`server gave HTTP response to HTTPS client`), + "No such file": regexp.MustCompile(`no such file`), + "Invalid argument": regexp.MustCompile(`invalid argument`), + "YAML error": regexp.MustCompile(`yaml`), + "Invalid ClientAuth": regexp.MustCompile(`invalid ClientAuth`), + "TLS handshake": regexp.MustCompile(`tls`), + "HTTP Request to HTTPS server": regexp.MustCompile(`HTTP`), + "Invalid CertPath": regexp.MustCompile(`missing TLSCertPath`), + "Invalid KeyPath": regexp.MustCompile(`missing TLSKeyPath`), + "ClientCA set without policy": regexp.MustCompile(`Client CA's have been configured without a Client Auth Policy`), + } +) + +func getPort() string { + listener, err := net.Listen("tcp", ":0") + if err != nil { + panic(err) + } + defer listener.Close() + p := listener.Addr().(*net.TCPAddr).Port + return fmt.Sprintf(":%v", p) +} + +type TestInputs struct { + Name string + Server func() *http.Server + UseNilServer bool + YAMLConfigPath string + ExpectedError *regexp.Regexp + UseTLSClient bool +} + +func TestYAMLFiles(t *testing.T) { + testTables := []*TestInputs{ + { + Name: `path to config yml invalid`, + YAMLConfigPath: "somefile", + ExpectedError: ErrorMap["No such file"], + }, + { + Name: `empty config yml`, + YAMLConfigPath: "testdata/tls_config_empty.yml", + ExpectedError: ErrorMap["Invalid CertPath"], + }, + { + Name: `invalid config yml (invalid structure)`, + YAMLConfigPath: "testdata/tls_config_junk.yml", + ExpectedError: ErrorMap["YAML error"], + }, + { + Name: `invalid config yml (cert path empty)`, + YAMLConfigPath: "testdata/tls_config_noAuth_certPath_empty.bad.yml", + ExpectedError: ErrorMap["Invalid CertPath"], + }, + { + Name: `invalid config yml (key path empty)`, + YAMLConfigPath: "testdata/tls_config_noAuth_keyPath_empty.bad.yml", + ExpectedError: ErrorMap["Invalid KeyPath"], + }, + { + Name: `invalid config yml (cert path and key path empty)`, + YAMLConfigPath: "testdata/tls_config_noAuth_certPath_keyPath_empty.bad.yml", + ExpectedError: ErrorMap["Invalid CertPath"], + }, + { + Name: `invalid config yml (cert path invalid)`, + YAMLConfigPath: "testdata/tls_config_noAuth_certPath_invalid.bad.yml", + ExpectedError: ErrorMap["No such file"], + }, + { + Name: `invalid config yml (key path invalid)`, + YAMLConfigPath: "testdata/tls_config_noAuth_keyPath_invalid.bad.yml", + ExpectedError: ErrorMap["No such file"], + }, + { + Name: `invalid config yml (cert path and key path invalid)`, + YAMLConfigPath: "testdata/tls_config_noAuth_certPath_keyPath_invalid.bad.yml", + ExpectedError: ErrorMap["No such file"], + }, + { + Name: `invalid config yml (invalid ClientAuth)`, + YAMLConfigPath: "testdata/tls_config_noAuth.bad.yml", + ExpectedError: ErrorMap["ClientCA set without policy"], + }, + { + Name: `invalid config yml (invalid ClientCAs filepath)`, + YAMLConfigPath: "testdata/tls_config_auth_clientCAs_invalid.bad.yml", + ExpectedError: ErrorMap["No such file"], + }, + } + for _, testInputs := range testTables { + t.Run(testInputs.Name, testInputs.Test) + } +} + +func TestServerBehaviour(t *testing.T) { + testTables := []*TestInputs{ + { + Name: `empty string YAMLConfigPath and default client`, + YAMLConfigPath: "", + ExpectedError: nil, + }, + { + Name: `empty string YAMLConfigPath and TLS client`, + YAMLConfigPath: "", + UseTLSClient: true, + ExpectedError: ErrorMap["HTTP Response to HTTPS"], + }, + { + Name: `valid tls config yml and default client`, + YAMLConfigPath: "testdata/tls_config_noAuth.good.yml", + ExpectedError: ErrorMap["HTTP Request to HTTPS server"], + }, + { + Name: `valid tls config yml and tls client`, + YAMLConfigPath: "testdata/tls_config_noAuth.good.yml", + UseTLSClient: true, + ExpectedError: nil, + }, + } + for _, testInputs := range testTables { + t.Run(testInputs.Name, testInputs.Test) + } +} + +func TestConfigReloading(t *testing.T) { + errorChannel := make(chan error, 1) + var once sync.Once + recordConnectionError := func(err error) { + once.Do(func() { + errorChannel <- err + }) + } + defer func() { + if recover() != nil { + recordConnectionError(errors.New("Panic in test function")) + } + }() + + goodYAMLPath := "testdata/tls_config_noAuth.good.yml" + badYAMLPath := "testdata/tls_config_noAuth.good.blocking.yml" + + server := &http.Server{ + Addr: port, + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello World!")) + }), + } + defer func() { + server.Close() + }() + + go func() { + defer func() { + if recover() != nil { + recordConnectionError(errors.New("Panic starting server")) + } + }() + err := Listen(server, badYAMLPath) + recordConnectionError(err) + }() + + client := getTLSClient() + + TestClientConnection := func() error { + time.Sleep(250 * time.Millisecond) + r, err := client.Get("https://localhost" + port) + if err != nil { + return (err) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return (err) + } + if string(body) != "Hello World!" { + return (errors.New(string(body))) + } + return (nil) + } + + err := TestClientConnection() + if err == nil { + recordConnectionError(errors.New("Connection accepted but should have failed.")) + } else { + swapFileContents(goodYAMLPath, badYAMLPath) + defer swapFileContents(goodYAMLPath, badYAMLPath) + err = TestClientConnection() + if err != nil { + recordConnectionError(errors.New("Connection failed but should have been accepted.")) + } else { + + recordConnectionError(nil) + } + } + + err = <-errorChannel + if err != nil { + t.Errorf(" *** Failed test: %s *** Returned error: %v", "TestConfigReloading", err) + } +} + +func (test *TestInputs) Test(t *testing.T) { + errorChannel := make(chan error, 1) + var once sync.Once + recordConnectionError := func(err error) { + once.Do(func() { + errorChannel <- err + }) + } + defer func() { + if recover() != nil { + recordConnectionError(errors.New("Panic in test function")) + } + }() + + var server *http.Server + if test.UseNilServer { + server = nil + } else { + server = &http.Server{ + Addr: port, + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello World!")) + }), + } + defer func() { + server.Close() + }() + } + go func() { + defer func() { + if recover() != nil { + recordConnectionError(errors.New("Panic starting server")) + } + }() + err := Listen(server, test.YAMLConfigPath) + recordConnectionError(err) + }() + + var ClientConnection func() (*http.Response, error) + if test.UseTLSClient { + ClientConnection = func() (*http.Response, error) { + client := getTLSClient() + return client.Get("https://localhost" + port) + } + } else { + ClientConnection = func() (*http.Response, error) { + client := http.DefaultClient + return client.Get("http://localhost" + port) + } + } + go func() { + time.Sleep(250 * time.Millisecond) + r, err := ClientConnection() + if err != nil { + recordConnectionError(err) + return + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + recordConnectionError(err) + return + } + if string(body) != "Hello World!" { + recordConnectionError(errors.New(string(body))) + return + } + recordConnectionError(nil) + }() + err := <-errorChannel + if test.isCorrectError(err) == false { + if test.ExpectedError == nil { + t.Logf("Expected no error, got error: %v", err) + } else { + t.Logf("Expected error matching regular expression: %v", test.ExpectedError) + t.Logf("Got: %v", err) + } + t.Fail() + } +} + +func (test *TestInputs) isCorrectError(returnedError error) bool { + switch { + case returnedError == nil && test.ExpectedError == nil: + case returnedError != nil && test.ExpectedError != nil && test.ExpectedError.MatchString(returnedError.Error()): + default: + return false + } + return true +} + +func getTLSClient() *http.Client { + cert, err := ioutil.ReadFile("testdata/tls-ca-chain.pem") + if err != nil { + panic("Unable to start TLS client. Check cert path") + } + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: func() *x509.CertPool { + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(cert) + return caCertPool + }(), + }, + }, + } + return client +} + +func swapFileContents(file1, file2 string) error { + content1, err := ioutil.ReadFile(file1) + if err != nil { + return err + } + content2, err := ioutil.ReadFile(file2) + if err != nil { + return err + } + err = ioutil.WriteFile(file1, content2, 0644) + if err != nil { + return err + } + err = ioutil.WriteFile(file2, content1, 0644) + if err != nil { + return err + } + return nil +} diff --git a/https/web-config.yml b/https/web-config.yml new file mode 100644 index 0000000000..0f439dacf0 --- /dev/null +++ b/https/web-config.yml @@ -0,0 +1,10 @@ +tlsConfig : + # Certificate and key files for server to use to authenticate to client + tlsCertPath : + tlsKeyPath : + + # Server policy for client authentication. Maps to ClientAuth Policies + [ clientAuth : ] + + # CA certificate for client certificate authentication to the server + [ clientCAs : ] diff --git a/node_exporter.go b/node_exporter.go index ceee4e1099..b48170c16c 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -24,6 +24,7 @@ import ( "github.com/prometheus/common/log" "github.com/prometheus/common/version" "github.com/prometheus/node_exporter/collector" + "github.com/prometheus/node_exporter/https" "gopkg.in/alecthomas/kingpin.v2" ) @@ -147,6 +148,10 @@ func main() { "web.max-requests", "Maximum number of parallel scrape requests. Use 0 to disable.", ).Default("40").Int() + configFile = kingpin.Flag( + "web.config", + "Path to config yaml file that can enable TLS or authentication.", + ).Default("").String() ) log.AddFlags(kingpin.CommandLine) @@ -169,7 +174,8 @@ func main() { }) log.Infoln("Listening on", *listenAddress) - if err := http.ListenAndServe(*listenAddress, nil); err != nil { + server := &http.Server{Addr: *listenAddress} + if err := https.Listen(server, *configFile); err != nil { log.Fatal(err) } } diff --git a/tls_config_noAuth.bad.yml b/tls_config_noAuth.bad.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 0000000000..daf913b1b3 --- /dev/null +++ b/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 0000000000..d4b92663ba --- /dev/null +++ b/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,15 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 0000000000..835ba3e755 --- /dev/null +++ b/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md new file mode 100644 index 0000000000..6483ba2afb --- /dev/null +++ b/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 0000000000..a932eade02 --- /dev/null +++ b/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 0000000000..7421f326ff --- /dev/null +++ b/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,282 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which when applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// together with the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required, the errors.WithStack and +// errors.WithMessage functions destructure errors.Wrap into its component +// operations: annotating an error with a stack trace and with a message, +// respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error that does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// Although the causer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported: +// +// %s print the error. If the error has a Cause it will be +// printed recursively. +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface: +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// The returned errors.StackTrace type is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// Although the stackTracer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is called, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +// WithMessagef annotates err with the format specifier. +// If err is nil, WithMessagef returns nil. +func WithMessagef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 0000000000..2874a048cf --- /dev/null +++ b/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,147 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +} diff --git a/vendor/gopkg.in/yaml.v2/.travis.yml b/vendor/gopkg.in/yaml.v2/.travis.yml new file mode 100644 index 0000000000..9f556934d8 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - 1.9 + - tip + +go_import_path: gopkg.in/yaml.v2 diff --git a/vendor/gopkg.in/yaml.v2/LICENSE b/vendor/gopkg.in/yaml.v2/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/vendor/gopkg.in/yaml.v2/LICENSE.libyaml b/vendor/gopkg.in/yaml.v2/LICENSE.libyaml new file mode 100644 index 0000000000..8da58fbf6f --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/LICENSE.libyaml @@ -0,0 +1,31 @@ +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original copyright and license: + + apic.go + emitterc.go + parserc.go + readerc.go + scannerc.go + writerc.go + yamlh.go + yamlprivateh.go + +Copyright (c) 2006 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/gopkg.in/yaml.v2/NOTICE b/vendor/gopkg.in/yaml.v2/NOTICE new file mode 100644 index 0000000000..866d74a7ad --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/NOTICE @@ -0,0 +1,13 @@ +Copyright 2011-2016 Canonical Ltd. + +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. diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/gopkg.in/yaml.v2/README.md new file mode 100644 index 0000000000..b50c6e8775 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/README.md @@ -0,0 +1,133 @@ +# YAML support for the Go language + +Introduction +------------ + +The yaml package enables Go programs to comfortably encode and decode YAML +values. It was developed within [Canonical](https://www.canonical.com) as +part of the [juju](https://juju.ubuntu.com) project, and is based on a +pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) +C library to parse and generate YAML data quickly and reliably. + +Compatibility +------------- + +The yaml package supports most of YAML 1.1 and 1.2, including support for +anchors, tags, map merging, etc. Multi-document unmarshalling is not yet +implemented, and base-60 floats from YAML 1.1 are purposefully not +supported since they're a poor design and are gone in YAML 1.2. + +Installation and usage +---------------------- + +The import path for the package is *gopkg.in/yaml.v2*. + +To install it, run: + + go get gopkg.in/yaml.v2 + +API documentation +----------------- + +If opened in a browser, the import path itself leads to the API documentation: + + * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) + +API stability +------------- + +The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). + + +License +------- + +The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. + + +Example +------- + +```Go +package main + +import ( + "fmt" + "log" + + "gopkg.in/yaml.v2" +) + +var data = ` +a: Easy! +b: + c: 2 + d: [3, 4] +` + +// Note: struct fields must be public in order for unmarshal to +// correctly populate the data. +type T struct { + A string + B struct { + RenamedC int `yaml:"c"` + D []int `yaml:",flow"` + } +} + +func main() { + t := T{} + + err := yaml.Unmarshal([]byte(data), &t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t:\n%v\n\n", t) + + d, err := yaml.Marshal(&t) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- t dump:\n%s\n\n", string(d)) + + m := make(map[interface{}]interface{}) + + err = yaml.Unmarshal([]byte(data), &m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m:\n%v\n\n", m) + + d, err = yaml.Marshal(&m) + if err != nil { + log.Fatalf("error: %v", err) + } + fmt.Printf("--- m dump:\n%s\n\n", string(d)) +} +``` + +This example will generate the following output: + +``` +--- t: +{Easy! {2 [3 4]}} + +--- t dump: +a: Easy! +b: + c: 2 + d: [3, 4] + + +--- m: +map[a:Easy! b:map[c:2 d:[3 4]]] + +--- m dump: +a: Easy! +b: + c: 2 + d: + - 3 + - 4 +``` + diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/gopkg.in/yaml.v2/apic.go new file mode 100644 index 0000000000..1f7e87e672 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/apic.go @@ -0,0 +1,739 @@ +package yaml + +import ( + "io" +) + +func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { + //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) + + // Check if we can move the queue at the beginning of the buffer. + if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { + if parser.tokens_head != len(parser.tokens) { + copy(parser.tokens, parser.tokens[parser.tokens_head:]) + } + parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] + parser.tokens_head = 0 + } + parser.tokens = append(parser.tokens, *token) + if pos < 0 { + return + } + copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) + parser.tokens[parser.tokens_head+pos] = *token +} + +// Create a new parser object. +func yaml_parser_initialize(parser *yaml_parser_t) bool { + *parser = yaml_parser_t{ + raw_buffer: make([]byte, 0, input_raw_buffer_size), + buffer: make([]byte, 0, input_buffer_size), + } + return true +} + +// Destroy a parser object. +func yaml_parser_delete(parser *yaml_parser_t) { + *parser = yaml_parser_t{} +} + +// String read handler. +func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + if parser.input_pos == len(parser.input) { + return 0, io.EOF + } + n = copy(buffer, parser.input[parser.input_pos:]) + parser.input_pos += n + return n, nil +} + +// Reader read handler. +func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { + return parser.input_reader.Read(buffer) +} + +// Set a string input. +func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_string_read_handler + parser.input = input + parser.input_pos = 0 +} + +// Set a file input. +func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { + if parser.read_handler != nil { + panic("must set the input source only once") + } + parser.read_handler = yaml_reader_read_handler + parser.input_reader = r +} + +// Set the source encoding. +func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { + if parser.encoding != yaml_ANY_ENCODING { + panic("must set the encoding only once") + } + parser.encoding = encoding +} + +// Create a new emitter object. +func yaml_emitter_initialize(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{ + buffer: make([]byte, output_buffer_size), + raw_buffer: make([]byte, 0, output_raw_buffer_size), + states: make([]yaml_emitter_state_t, 0, initial_stack_size), + events: make([]yaml_event_t, 0, initial_queue_size), + } +} + +// Destroy an emitter object. +func yaml_emitter_delete(emitter *yaml_emitter_t) { + *emitter = yaml_emitter_t{} +} + +// String write handler. +func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + *emitter.output_buffer = append(*emitter.output_buffer, buffer...) + return nil +} + +// yaml_writer_write_handler uses emitter.output_writer to write the +// emitted text. +func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { + _, err := emitter.output_writer.Write(buffer) + return err +} + +// Set a string output. +func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_string_write_handler + emitter.output_buffer = output_buffer +} + +// Set a file output. +func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { + if emitter.write_handler != nil { + panic("must set the output target only once") + } + emitter.write_handler = yaml_writer_write_handler + emitter.output_writer = w +} + +// Set the output encoding. +func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { + if emitter.encoding != yaml_ANY_ENCODING { + panic("must set the output encoding only once") + } + emitter.encoding = encoding +} + +// Set the canonical output style. +func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { + emitter.canonical = canonical +} + +//// Set the indentation increment. +func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { + if indent < 2 || indent > 9 { + indent = 2 + } + emitter.best_indent = indent +} + +// Set the preferred line width. +func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { + if width < 0 { + width = -1 + } + emitter.best_width = width +} + +// Set if unescaped non-ASCII characters are allowed. +func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { + emitter.unicode = unicode +} + +// Set the preferred line break character. +func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { + emitter.line_break = line_break +} + +///* +// * Destroy a token object. +// */ +// +//YAML_DECLARE(void) +//yaml_token_delete(yaml_token_t *token) +//{ +// assert(token); // Non-NULL token object expected. +// +// switch (token.type) +// { +// case YAML_TAG_DIRECTIVE_TOKEN: +// yaml_free(token.data.tag_directive.handle); +// yaml_free(token.data.tag_directive.prefix); +// break; +// +// case YAML_ALIAS_TOKEN: +// yaml_free(token.data.alias.value); +// break; +// +// case YAML_ANCHOR_TOKEN: +// yaml_free(token.data.anchor.value); +// break; +// +// case YAML_TAG_TOKEN: +// yaml_free(token.data.tag.handle); +// yaml_free(token.data.tag.suffix); +// break; +// +// case YAML_SCALAR_TOKEN: +// yaml_free(token.data.scalar.value); +// break; +// +// default: +// break; +// } +// +// memset(token, 0, sizeof(yaml_token_t)); +//} +// +///* +// * Check if a string is a valid UTF-8 sequence. +// * +// * Check 'reader.c' for more details on UTF-8 encoding. +// */ +// +//static int +//yaml_check_utf8(yaml_char_t *start, size_t length) +//{ +// yaml_char_t *end = start+length; +// yaml_char_t *pointer = start; +// +// while (pointer < end) { +// unsigned char octet; +// unsigned int width; +// unsigned int value; +// size_t k; +// +// octet = pointer[0]; +// width = (octet & 0x80) == 0x00 ? 1 : +// (octet & 0xE0) == 0xC0 ? 2 : +// (octet & 0xF0) == 0xE0 ? 3 : +// (octet & 0xF8) == 0xF0 ? 4 : 0; +// value = (octet & 0x80) == 0x00 ? octet & 0x7F : +// (octet & 0xE0) == 0xC0 ? octet & 0x1F : +// (octet & 0xF0) == 0xE0 ? octet & 0x0F : +// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; +// if (!width) return 0; +// if (pointer+width > end) return 0; +// for (k = 1; k < width; k ++) { +// octet = pointer[k]; +// if ((octet & 0xC0) != 0x80) return 0; +// value = (value << 6) + (octet & 0x3F); +// } +// if (!((width == 1) || +// (width == 2 && value >= 0x80) || +// (width == 3 && value >= 0x800) || +// (width == 4 && value >= 0x10000))) return 0; +// +// pointer += width; +// } +// +// return 1; +//} +// + +// Create STREAM-START. +func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + encoding: encoding, + } +} + +// Create STREAM-END. +func yaml_stream_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + } +} + +// Create DOCUMENT-START. +func yaml_document_start_event_initialize( + event *yaml_event_t, + version_directive *yaml_version_directive_t, + tag_directives []yaml_tag_directive_t, + implicit bool, +) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: implicit, + } +} + +// Create DOCUMENT-END. +func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + implicit: implicit, + } +} + +///* +// * Create ALIAS. +// */ +// +//YAML_DECLARE(int) +//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) +//{ +// mark yaml_mark_t = { 0, 0, 0 } +// anchor_copy *yaml_char_t = NULL +// +// assert(event) // Non-NULL event object is expected. +// assert(anchor) // Non-NULL anchor is expected. +// +// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 +// +// anchor_copy = yaml_strdup(anchor) +// if (!anchor_copy) +// return 0 +// +// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) +// +// return 1 +//} + +// Create SCALAR. +func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + anchor: anchor, + tag: tag, + value: value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-START. +func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } + return true +} + +// Create SEQUENCE-END. +func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + } + return true +} + +// Create MAPPING-START. +func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(style), + } +} + +// Create MAPPING-END. +func yaml_mapping_end_event_initialize(event *yaml_event_t) { + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + } +} + +// Destroy an event object. +func yaml_event_delete(event *yaml_event_t) { + *event = yaml_event_t{} +} + +///* +// * Create a document object. +// */ +// +//YAML_DECLARE(int) +//yaml_document_initialize(document *yaml_document_t, +// version_directive *yaml_version_directive_t, +// tag_directives_start *yaml_tag_directive_t, +// tag_directives_end *yaml_tag_directive_t, +// start_implicit int, end_implicit int) +//{ +// struct { +// error yaml_error_type_t +// } context +// struct { +// start *yaml_node_t +// end *yaml_node_t +// top *yaml_node_t +// } nodes = { NULL, NULL, NULL } +// version_directive_copy *yaml_version_directive_t = NULL +// struct { +// start *yaml_tag_directive_t +// end *yaml_tag_directive_t +// top *yaml_tag_directive_t +// } tag_directives_copy = { NULL, NULL, NULL } +// value yaml_tag_directive_t = { NULL, NULL } +// mark yaml_mark_t = { 0, 0, 0 } +// +// assert(document) // Non-NULL document object is expected. +// assert((tag_directives_start && tag_directives_end) || +// (tag_directives_start == tag_directives_end)) +// // Valid tag directives are expected. +// +// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error +// +// if (version_directive) { +// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) +// if (!version_directive_copy) goto error +// version_directive_copy.major = version_directive.major +// version_directive_copy.minor = version_directive.minor +// } +// +// if (tag_directives_start != tag_directives_end) { +// tag_directive *yaml_tag_directive_t +// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) +// goto error +// for (tag_directive = tag_directives_start +// tag_directive != tag_directives_end; tag_directive ++) { +// assert(tag_directive.handle) +// assert(tag_directive.prefix) +// if (!yaml_check_utf8(tag_directive.handle, +// strlen((char *)tag_directive.handle))) +// goto error +// if (!yaml_check_utf8(tag_directive.prefix, +// strlen((char *)tag_directive.prefix))) +// goto error +// value.handle = yaml_strdup(tag_directive.handle) +// value.prefix = yaml_strdup(tag_directive.prefix) +// if (!value.handle || !value.prefix) goto error +// if (!PUSH(&context, tag_directives_copy, value)) +// goto error +// value.handle = NULL +// value.prefix = NULL +// } +// } +// +// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, +// tag_directives_copy.start, tag_directives_copy.top, +// start_implicit, end_implicit, mark, mark) +// +// return 1 +// +//error: +// STACK_DEL(&context, nodes) +// yaml_free(version_directive_copy) +// while (!STACK_EMPTY(&context, tag_directives_copy)) { +// value yaml_tag_directive_t = POP(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// } +// STACK_DEL(&context, tag_directives_copy) +// yaml_free(value.handle) +// yaml_free(value.prefix) +// +// return 0 +//} +// +///* +// * Destroy a document object. +// */ +// +//YAML_DECLARE(void) +//yaml_document_delete(document *yaml_document_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// tag_directive *yaml_tag_directive_t +// +// context.error = YAML_NO_ERROR // Eliminate a compiler warning. +// +// assert(document) // Non-NULL document object is expected. +// +// while (!STACK_EMPTY(&context, document.nodes)) { +// node yaml_node_t = POP(&context, document.nodes) +// yaml_free(node.tag) +// switch (node.type) { +// case YAML_SCALAR_NODE: +// yaml_free(node.data.scalar.value) +// break +// case YAML_SEQUENCE_NODE: +// STACK_DEL(&context, node.data.sequence.items) +// break +// case YAML_MAPPING_NODE: +// STACK_DEL(&context, node.data.mapping.pairs) +// break +// default: +// assert(0) // Should not happen. +// } +// } +// STACK_DEL(&context, document.nodes) +// +// yaml_free(document.version_directive) +// for (tag_directive = document.tag_directives.start +// tag_directive != document.tag_directives.end +// tag_directive++) { +// yaml_free(tag_directive.handle) +// yaml_free(tag_directive.prefix) +// } +// yaml_free(document.tag_directives.start) +// +// memset(document, 0, sizeof(yaml_document_t)) +//} +// +///** +// * Get a document node. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_node(document *yaml_document_t, index int) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (index > 0 && document.nodes.start + index <= document.nodes.top) { +// return document.nodes.start + index - 1 +// } +// return NULL +//} +// +///** +// * Get the root object. +// */ +// +//YAML_DECLARE(yaml_node_t *) +//yaml_document_get_root_node(document *yaml_document_t) +//{ +// assert(document) // Non-NULL document object is expected. +// +// if (document.nodes.top != document.nodes.start) { +// return document.nodes.start +// } +// return NULL +//} +// +///* +// * Add a scalar node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_scalar(document *yaml_document_t, +// tag *yaml_char_t, value *yaml_char_t, length int, +// style yaml_scalar_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// value_copy *yaml_char_t = NULL +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// assert(value) // Non-NULL value is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (length < 0) { +// length = strlen((char *)value) +// } +// +// if (!yaml_check_utf8(value, length)) goto error +// value_copy = yaml_malloc(length+1) +// if (!value_copy) goto error +// memcpy(value_copy, value, length) +// value_copy[length] = '\0' +// +// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// yaml_free(tag_copy) +// yaml_free(value_copy) +// +// return 0 +//} +// +///* +// * Add a sequence node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_sequence(document *yaml_document_t, +// tag *yaml_char_t, style yaml_sequence_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_item_t +// end *yaml_node_item_t +// top *yaml_node_item_t +// } items = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error +// +// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, items) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Add a mapping node to a document. +// */ +// +//YAML_DECLARE(int) +//yaml_document_add_mapping(document *yaml_document_t, +// tag *yaml_char_t, style yaml_mapping_style_t) +//{ +// struct { +// error yaml_error_type_t +// } context +// mark yaml_mark_t = { 0, 0, 0 } +// tag_copy *yaml_char_t = NULL +// struct { +// start *yaml_node_pair_t +// end *yaml_node_pair_t +// top *yaml_node_pair_t +// } pairs = { NULL, NULL, NULL } +// node yaml_node_t +// +// assert(document) // Non-NULL document object is expected. +// +// if (!tag) { +// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG +// } +// +// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error +// tag_copy = yaml_strdup(tag) +// if (!tag_copy) goto error +// +// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error +// +// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, +// style, mark, mark) +// if (!PUSH(&context, document.nodes, node)) goto error +// +// return document.nodes.top - document.nodes.start +// +//error: +// STACK_DEL(&context, pairs) +// yaml_free(tag_copy) +// +// return 0 +//} +// +///* +// * Append an item to a sequence node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_sequence_item(document *yaml_document_t, +// sequence int, item int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// assert(document) // Non-NULL document is required. +// assert(sequence > 0 +// && document.nodes.start + sequence <= document.nodes.top) +// // Valid sequence id is required. +// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) +// // A sequence node is required. +// assert(item > 0 && document.nodes.start + item <= document.nodes.top) +// // Valid item id is required. +// +// if (!PUSH(&context, +// document.nodes.start[sequence-1].data.sequence.items, item)) +// return 0 +// +// return 1 +//} +// +///* +// * Append a pair of a key and a value to a mapping node. +// */ +// +//YAML_DECLARE(int) +//yaml_document_append_mapping_pair(document *yaml_document_t, +// mapping int, key int, value int) +//{ +// struct { +// error yaml_error_type_t +// } context +// +// pair yaml_node_pair_t +// +// assert(document) // Non-NULL document is required. +// assert(mapping > 0 +// && document.nodes.start + mapping <= document.nodes.top) +// // Valid mapping id is required. +// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) +// // A mapping node is required. +// assert(key > 0 && document.nodes.start + key <= document.nodes.top) +// // Valid key id is required. +// assert(value > 0 && document.nodes.start + value <= document.nodes.top) +// // Valid value id is required. +// +// pair.key = key +// pair.value = value +// +// if (!PUSH(&context, +// document.nodes.start[mapping-1].data.mapping.pairs, pair)) +// return 0 +// +// return 1 +//} +// +// diff --git a/vendor/gopkg.in/yaml.v2/decode.go b/vendor/gopkg.in/yaml.v2/decode.go new file mode 100644 index 0000000000..e4e56e28e0 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/decode.go @@ -0,0 +1,775 @@ +package yaml + +import ( + "encoding" + "encoding/base64" + "fmt" + "io" + "math" + "reflect" + "strconv" + "time" +) + +const ( + documentNode = 1 << iota + mappingNode + sequenceNode + scalarNode + aliasNode +) + +type node struct { + kind int + line, column int + tag string + // For an alias node, alias holds the resolved alias. + alias *node + value string + implicit bool + children []*node + anchors map[string]*node +} + +// ---------------------------------------------------------------------------- +// Parser, produces a node tree out of a libyaml event stream. + +type parser struct { + parser yaml_parser_t + event yaml_event_t + doc *node + doneInit bool +} + +func newParser(b []byte) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + if len(b) == 0 { + b = []byte{'\n'} + } + yaml_parser_set_input_string(&p.parser, b) + return &p +} + +func newParserFromReader(r io.Reader) *parser { + p := parser{} + if !yaml_parser_initialize(&p.parser) { + panic("failed to initialize YAML emitter") + } + yaml_parser_set_input_reader(&p.parser, r) + return &p +} + +func (p *parser) init() { + if p.doneInit { + return + } + p.expect(yaml_STREAM_START_EVENT) + p.doneInit = true +} + +func (p *parser) destroy() { + if p.event.typ != yaml_NO_EVENT { + yaml_event_delete(&p.event) + } + yaml_parser_delete(&p.parser) +} + +// expect consumes an event from the event stream and +// checks that it's of the expected type. +func (p *parser) expect(e yaml_event_type_t) { + if p.event.typ == yaml_NO_EVENT { + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + } + if p.event.typ == yaml_STREAM_END_EVENT { + failf("attempted to go past the end of stream; corrupted value?") + } + if p.event.typ != e { + p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) + p.fail() + } + yaml_event_delete(&p.event) + p.event.typ = yaml_NO_EVENT +} + +// peek peeks at the next event in the event stream, +// puts the results into p.event and returns the event type. +func (p *parser) peek() yaml_event_type_t { + if p.event.typ != yaml_NO_EVENT { + return p.event.typ + } + if !yaml_parser_parse(&p.parser, &p.event) { + p.fail() + } + return p.event.typ +} + +func (p *parser) fail() { + var where string + var line int + if p.parser.problem_mark.line != 0 { + line = p.parser.problem_mark.line + // Scanner errors don't iterate line before returning error + if p.parser.error == yaml_SCANNER_ERROR { + line++ + } + } else if p.parser.context_mark.line != 0 { + line = p.parser.context_mark.line + } + if line != 0 { + where = "line " + strconv.Itoa(line) + ": " + } + var msg string + if len(p.parser.problem) > 0 { + msg = p.parser.problem + } else { + msg = "unknown problem parsing YAML content" + } + failf("%s%s", where, msg) +} + +func (p *parser) anchor(n *node, anchor []byte) { + if anchor != nil { + p.doc.anchors[string(anchor)] = n + } +} + +func (p *parser) parse() *node { + p.init() + switch p.peek() { + case yaml_SCALAR_EVENT: + return p.scalar() + case yaml_ALIAS_EVENT: + return p.alias() + case yaml_MAPPING_START_EVENT: + return p.mapping() + case yaml_SEQUENCE_START_EVENT: + return p.sequence() + case yaml_DOCUMENT_START_EVENT: + return p.document() + case yaml_STREAM_END_EVENT: + // Happens when attempting to decode an empty buffer. + return nil + default: + panic("attempted to parse unknown event: " + p.event.typ.String()) + } +} + +func (p *parser) node(kind int) *node { + return &node{ + kind: kind, + line: p.event.start_mark.line, + column: p.event.start_mark.column, + } +} + +func (p *parser) document() *node { + n := p.node(documentNode) + n.anchors = make(map[string]*node) + p.doc = n + p.expect(yaml_DOCUMENT_START_EVENT) + n.children = append(n.children, p.parse()) + p.expect(yaml_DOCUMENT_END_EVENT) + return n +} + +func (p *parser) alias() *node { + n := p.node(aliasNode) + n.value = string(p.event.anchor) + n.alias = p.doc.anchors[n.value] + if n.alias == nil { + failf("unknown anchor '%s' referenced", n.value) + } + p.expect(yaml_ALIAS_EVENT) + return n +} + +func (p *parser) scalar() *node { + n := p.node(scalarNode) + n.value = string(p.event.value) + n.tag = string(p.event.tag) + n.implicit = p.event.implicit + p.anchor(n, p.event.anchor) + p.expect(yaml_SCALAR_EVENT) + return n +} + +func (p *parser) sequence() *node { + n := p.node(sequenceNode) + p.anchor(n, p.event.anchor) + p.expect(yaml_SEQUENCE_START_EVENT) + for p.peek() != yaml_SEQUENCE_END_EVENT { + n.children = append(n.children, p.parse()) + } + p.expect(yaml_SEQUENCE_END_EVENT) + return n +} + +func (p *parser) mapping() *node { + n := p.node(mappingNode) + p.anchor(n, p.event.anchor) + p.expect(yaml_MAPPING_START_EVENT) + for p.peek() != yaml_MAPPING_END_EVENT { + n.children = append(n.children, p.parse(), p.parse()) + } + p.expect(yaml_MAPPING_END_EVENT) + return n +} + +// ---------------------------------------------------------------------------- +// Decoder, unmarshals a node into a provided value. + +type decoder struct { + doc *node + aliases map[*node]bool + mapType reflect.Type + terrors []string + strict bool +} + +var ( + mapItemType = reflect.TypeOf(MapItem{}) + durationType = reflect.TypeOf(time.Duration(0)) + defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) + ifaceType = defaultMapType.Elem() + timeType = reflect.TypeOf(time.Time{}) + ptrTimeType = reflect.TypeOf(&time.Time{}) +) + +func newDecoder(strict bool) *decoder { + d := &decoder{mapType: defaultMapType, strict: strict} + d.aliases = make(map[*node]bool) + return d +} + +func (d *decoder) terror(n *node, tag string, out reflect.Value) { + if n.tag != "" { + tag = n.tag + } + value := n.value + if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { + if len(value) > 10 { + value = " `" + value[:7] + "...`" + } else { + value = " `" + value + "`" + } + } + d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) +} + +func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { + terrlen := len(d.terrors) + err := u.UnmarshalYAML(func(v interface{}) (err error) { + defer handleErr(&err) + d.unmarshal(n, reflect.ValueOf(v)) + if len(d.terrors) > terrlen { + issues := d.terrors[terrlen:] + d.terrors = d.terrors[:terrlen] + return &TypeError{issues} + } + return nil + }) + if e, ok := err.(*TypeError); ok { + d.terrors = append(d.terrors, e.Errors...) + return false + } + if err != nil { + fail(err) + } + return true +} + +// d.prepare initializes and dereferences pointers and calls UnmarshalYAML +// if a value is found to implement it. +// It returns the initialized and dereferenced out value, whether +// unmarshalling was already done by UnmarshalYAML, and if so whether +// its types unmarshalled appropriately. +// +// If n holds a null value, prepare returns before doing anything. +func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { + if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { + return out, false, false + } + again := true + for again { + again = false + if out.Kind() == reflect.Ptr { + if out.IsNil() { + out.Set(reflect.New(out.Type().Elem())) + } + out = out.Elem() + again = true + } + if out.CanAddr() { + if u, ok := out.Addr().Interface().(Unmarshaler); ok { + good = d.callUnmarshaler(n, u) + return out, true, good + } + } + } + return out, false, false +} + +func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { + switch n.kind { + case documentNode: + return d.document(n, out) + case aliasNode: + return d.alias(n, out) + } + out, unmarshaled, good := d.prepare(n, out) + if unmarshaled { + return good + } + switch n.kind { + case scalarNode: + good = d.scalar(n, out) + case mappingNode: + good = d.mapping(n, out) + case sequenceNode: + good = d.sequence(n, out) + default: + panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) + } + return good +} + +func (d *decoder) document(n *node, out reflect.Value) (good bool) { + if len(n.children) == 1 { + d.doc = n + d.unmarshal(n.children[0], out) + return true + } + return false +} + +func (d *decoder) alias(n *node, out reflect.Value) (good bool) { + if d.aliases[n] { + // TODO this could actually be allowed in some circumstances. + failf("anchor '%s' value contains itself", n.value) + } + d.aliases[n] = true + good = d.unmarshal(n.alias, out) + delete(d.aliases, n) + return good +} + +var zeroValue reflect.Value + +func resetMap(out reflect.Value) { + for _, k := range out.MapKeys() { + out.SetMapIndex(k, zeroValue) + } +} + +func (d *decoder) scalar(n *node, out reflect.Value) bool { + var tag string + var resolved interface{} + if n.tag == "" && !n.implicit { + tag = yaml_STR_TAG + resolved = n.value + } else { + tag, resolved = resolve(n.tag, n.value) + if tag == yaml_BINARY_TAG { + data, err := base64.StdEncoding.DecodeString(resolved.(string)) + if err != nil { + failf("!!binary value contains invalid base64 data") + } + resolved = string(data) + } + } + if resolved == nil { + if out.Kind() == reflect.Map && !out.CanAddr() { + resetMap(out) + } else { + out.Set(reflect.Zero(out.Type())) + } + return true + } + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + // We've resolved to exactly the type we want, so use that. + out.Set(resolvedv) + return true + } + // Perhaps we can use the value as a TextUnmarshaler to + // set its value. + if out.CanAddr() { + u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) + if ok { + var text []byte + if tag == yaml_BINARY_TAG { + text = []byte(resolved.(string)) + } else { + // We let any value be unmarshaled into TextUnmarshaler. + // That might be more lax than we'd like, but the + // TextUnmarshaler itself should bowl out any dubious values. + text = []byte(n.value) + } + err := u.UnmarshalText(text) + if err != nil { + fail(err) + } + return true + } + } + switch out.Kind() { + case reflect.String: + if tag == yaml_BINARY_TAG { + out.SetString(resolved.(string)) + return true + } + if resolved != nil { + out.SetString(n.value) + return true + } + case reflect.Interface: + if resolved == nil { + out.Set(reflect.Zero(out.Type())) + } else if tag == yaml_TIMESTAMP_TAG { + // It looks like a timestamp but for backward compatibility + // reasons we set it as a string, so that code that unmarshals + // timestamp-like values into interface{} will continue to + // see a string and not a time.Time. + // TODO(v3) Drop this. + out.Set(reflect.ValueOf(n.value)) + } else { + out.Set(reflect.ValueOf(resolved)) + } + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch resolved := resolved.(type) { + case int: + if !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case int64: + if !out.OverflowInt(resolved) { + out.SetInt(resolved) + return true + } + case uint64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case float64: + if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { + out.SetInt(int64(resolved)) + return true + } + case string: + if out.Type() == durationType { + d, err := time.ParseDuration(resolved) + if err == nil { + out.SetInt(int64(d)) + return true + } + } + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch resolved := resolved.(type) { + case int: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case int64: + if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case uint64: + if !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + case float64: + if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { + out.SetUint(uint64(resolved)) + return true + } + } + case reflect.Bool: + switch resolved := resolved.(type) { + case bool: + out.SetBool(resolved) + return true + } + case reflect.Float32, reflect.Float64: + switch resolved := resolved.(type) { + case int: + out.SetFloat(float64(resolved)) + return true + case int64: + out.SetFloat(float64(resolved)) + return true + case uint64: + out.SetFloat(float64(resolved)) + return true + case float64: + out.SetFloat(resolved) + return true + } + case reflect.Struct: + if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { + out.Set(resolvedv) + return true + } + case reflect.Ptr: + if out.Type().Elem() == reflect.TypeOf(resolved) { + // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? + elem := reflect.New(out.Type().Elem()) + elem.Elem().Set(reflect.ValueOf(resolved)) + out.Set(elem) + return true + } + } + d.terror(n, tag, out) + return false +} + +func settableValueOf(i interface{}) reflect.Value { + v := reflect.ValueOf(i) + sv := reflect.New(v.Type()).Elem() + sv.Set(v) + return sv +} + +func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { + l := len(n.children) + + var iface reflect.Value + switch out.Kind() { + case reflect.Slice: + out.Set(reflect.MakeSlice(out.Type(), l, l)) + case reflect.Array: + if l != out.Len() { + failf("invalid array: want %d elements but got %d", out.Len(), l) + } + case reflect.Interface: + // No type hints. Will have to use a generic sequence. + iface = out + out = settableValueOf(make([]interface{}, l)) + default: + d.terror(n, yaml_SEQ_TAG, out) + return false + } + et := out.Type().Elem() + + j := 0 + for i := 0; i < l; i++ { + e := reflect.New(et).Elem() + if ok := d.unmarshal(n.children[i], e); ok { + out.Index(j).Set(e) + j++ + } + } + if out.Kind() != reflect.Array { + out.Set(out.Slice(0, j)) + } + if iface.IsValid() { + iface.Set(out) + } + return true +} + +func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { + switch out.Kind() { + case reflect.Struct: + return d.mappingStruct(n, out) + case reflect.Slice: + return d.mappingSlice(n, out) + case reflect.Map: + // okay + case reflect.Interface: + if d.mapType.Kind() == reflect.Map { + iface := out + out = reflect.MakeMap(d.mapType) + iface.Set(out) + } else { + slicev := reflect.New(d.mapType).Elem() + if !d.mappingSlice(n, slicev) { + return false + } + out.Set(slicev) + return true + } + default: + d.terror(n, yaml_MAP_TAG, out) + return false + } + outt := out.Type() + kt := outt.Key() + et := outt.Elem() + + mapType := d.mapType + if outt.Key() == ifaceType && outt.Elem() == ifaceType { + d.mapType = outt + } + + if out.IsNil() { + out.Set(reflect.MakeMap(outt)) + } + l := len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + k := reflect.New(kt).Elem() + if d.unmarshal(n.children[i], k) { + kkind := k.Kind() + if kkind == reflect.Interface { + kkind = k.Elem().Kind() + } + if kkind == reflect.Map || kkind == reflect.Slice { + failf("invalid map key: %#v", k.Interface()) + } + e := reflect.New(et).Elem() + if d.unmarshal(n.children[i+1], e) { + d.setMapIndex(n.children[i+1], out, k, e) + } + } + } + d.mapType = mapType + return true +} + +func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { + if d.strict && out.MapIndex(k) != zeroValue { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) + return + } + out.SetMapIndex(k, v) +} + +func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { + outt := out.Type() + if outt.Elem() != mapItemType { + d.terror(n, yaml_MAP_TAG, out) + return false + } + + mapType := d.mapType + d.mapType = outt + + var slice []MapItem + var l = len(n.children) + for i := 0; i < l; i += 2 { + if isMerge(n.children[i]) { + d.merge(n.children[i+1], out) + continue + } + item := MapItem{} + k := reflect.ValueOf(&item.Key).Elem() + if d.unmarshal(n.children[i], k) { + v := reflect.ValueOf(&item.Value).Elem() + if d.unmarshal(n.children[i+1], v) { + slice = append(slice, item) + } + } + } + out.Set(reflect.ValueOf(slice)) + d.mapType = mapType + return true +} + +func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + name := settableValueOf("") + l := len(n.children) + + var inlineMap reflect.Value + var elemType reflect.Type + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) + elemType = inlineMap.Type().Elem() + } + + var doneFields []bool + if d.strict { + doneFields = make([]bool, len(sinfo.FieldsList)) + } + for i := 0; i < l; i += 2 { + ni := n.children[i] + if isMerge(ni) { + d.merge(n.children[i+1], out) + continue + } + if !d.unmarshal(ni, name) { + continue + } + if info, ok := sinfo.FieldsMap[name.String()]; ok { + if d.strict { + if doneFields[info.Id] { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) + continue + } + doneFields[info.Id] = true + } + var field reflect.Value + if info.Inline == nil { + field = out.Field(info.Num) + } else { + field = out.FieldByIndex(info.Inline) + } + d.unmarshal(n.children[i+1], field) + } else if sinfo.InlineMap != -1 { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + value := reflect.New(elemType).Elem() + d.unmarshal(n.children[i+1], value) + d.setMapIndex(n.children[i+1], inlineMap, name, value) + } else if d.strict { + d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) + } + } + return true +} + +func failWantMap() { + failf("map merge requires map or sequence of maps as the value") +} + +func (d *decoder) merge(n *node, out reflect.Value) { + switch n.kind { + case mappingNode: + d.unmarshal(n, out) + case aliasNode: + an, ok := d.doc.anchors[n.value] + if ok && an.kind != mappingNode { + failWantMap() + } + d.unmarshal(n, out) + case sequenceNode: + // Step backwards as earlier nodes take precedence. + for i := len(n.children) - 1; i >= 0; i-- { + ni := n.children[i] + if ni.kind == aliasNode { + an, ok := d.doc.anchors[ni.value] + if ok && an.kind != mappingNode { + failWantMap() + } + } else if ni.kind != mappingNode { + failWantMap() + } + d.unmarshal(ni, out) + } + default: + failWantMap() + } +} + +func isMerge(n *node) bool { + return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) +} diff --git a/vendor/gopkg.in/yaml.v2/emitterc.go b/vendor/gopkg.in/yaml.v2/emitterc.go new file mode 100644 index 0000000000..a1c2cc5262 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/emitterc.go @@ -0,0 +1,1685 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Flush the buffer if needed. +func flush(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) { + return yaml_emitter_flush(emitter) + } + return true +} + +// Put a character to the output buffer. +func put(emitter *yaml_emitter_t, value byte) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + emitter.buffer[emitter.buffer_pos] = value + emitter.buffer_pos++ + emitter.column++ + return true +} + +// Put a line break to the output buffer. +func put_break(emitter *yaml_emitter_t) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + switch emitter.line_break { + case yaml_CR_BREAK: + emitter.buffer[emitter.buffer_pos] = '\r' + emitter.buffer_pos += 1 + case yaml_LN_BREAK: + emitter.buffer[emitter.buffer_pos] = '\n' + emitter.buffer_pos += 1 + case yaml_CRLN_BREAK: + emitter.buffer[emitter.buffer_pos+0] = '\r' + emitter.buffer[emitter.buffer_pos+1] = '\n' + emitter.buffer_pos += 2 + default: + panic("unknown line break setting") + } + emitter.column = 0 + emitter.line++ + return true +} + +// Copy a character from a string into buffer. +func write(emitter *yaml_emitter_t, s []byte, i *int) bool { + if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { + return false + } + p := emitter.buffer_pos + w := width(s[*i]) + switch w { + case 4: + emitter.buffer[p+3] = s[*i+3] + fallthrough + case 3: + emitter.buffer[p+2] = s[*i+2] + fallthrough + case 2: + emitter.buffer[p+1] = s[*i+1] + fallthrough + case 1: + emitter.buffer[p+0] = s[*i+0] + default: + panic("unknown character width") + } + emitter.column++ + emitter.buffer_pos += w + *i += w + return true +} + +// Write a whole string into buffer. +func write_all(emitter *yaml_emitter_t, s []byte) bool { + for i := 0; i < len(s); { + if !write(emitter, s, &i) { + return false + } + } + return true +} + +// Copy a line break character from a string into buffer. +func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { + if s[*i] == '\n' { + if !put_break(emitter) { + return false + } + *i++ + } else { + if !write(emitter, s, i) { + return false + } + emitter.column = 0 + emitter.line++ + } + return true +} + +// Set an emitter error and return false. +func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_EMITTER_ERROR + emitter.problem = problem + return false +} + +// Emit an event. +func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.events = append(emitter.events, *event) + for !yaml_emitter_need_more_events(emitter) { + event := &emitter.events[emitter.events_head] + if !yaml_emitter_analyze_event(emitter, event) { + return false + } + if !yaml_emitter_state_machine(emitter, event) { + return false + } + yaml_event_delete(event) + emitter.events_head++ + } + return true +} + +// Check if we need to accumulate more events before emitting. +// +// We accumulate extra +// - 1 event for DOCUMENT-START +// - 2 events for SEQUENCE-START +// - 3 events for MAPPING-START +// +func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { + if emitter.events_head == len(emitter.events) { + return true + } + var accumulate int + switch emitter.events[emitter.events_head].typ { + case yaml_DOCUMENT_START_EVENT: + accumulate = 1 + break + case yaml_SEQUENCE_START_EVENT: + accumulate = 2 + break + case yaml_MAPPING_START_EVENT: + accumulate = 3 + break + default: + return false + } + if len(emitter.events)-emitter.events_head > accumulate { + return false + } + var level int + for i := emitter.events_head; i < len(emitter.events); i++ { + switch emitter.events[i].typ { + case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: + level++ + case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: + level-- + } + if level == 0 { + return false + } + } + return true +} + +// Append a directive to the directives stack. +func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { + for i := 0; i < len(emitter.tag_directives); i++ { + if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") + } + } + + // [Go] Do we actually need to copy this given garbage collection + // and the lack of deallocating destructors? + tag_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(tag_copy.handle, value.handle) + copy(tag_copy.prefix, value.prefix) + emitter.tag_directives = append(emitter.tag_directives, tag_copy) + return true +} + +// Increase the indentation level. +func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { + emitter.indents = append(emitter.indents, emitter.indent) + if emitter.indent < 0 { + if flow { + emitter.indent = emitter.best_indent + } else { + emitter.indent = 0 + } + } else if !indentless { + emitter.indent += emitter.best_indent + } + return true +} + +// State dispatcher. +func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { + switch emitter.state { + default: + case yaml_EMIT_STREAM_START_STATE: + return yaml_emitter_emit_stream_start(emitter, event) + + case yaml_EMIT_FIRST_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, true) + + case yaml_EMIT_DOCUMENT_START_STATE: + return yaml_emitter_emit_document_start(emitter, event, false) + + case yaml_EMIT_DOCUMENT_CONTENT_STATE: + return yaml_emitter_emit_document_content(emitter, event) + + case yaml_EMIT_DOCUMENT_END_STATE: + return yaml_emitter_emit_document_end(emitter, event) + + case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, true) + + case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_flow_sequence_item(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_KEY_STATE: + return yaml_emitter_emit_flow_mapping_key(emitter, event, false) + + case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, true) + + case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: + return yaml_emitter_emit_flow_mapping_value(emitter, event, false) + + case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, true) + + case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return yaml_emitter_emit_block_sequence_item(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: + return yaml_emitter_emit_block_mapping_key(emitter, event, false) + + case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, true) + + case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: + return yaml_emitter_emit_block_mapping_value(emitter, event, false) + + case yaml_EMIT_END_STATE: + return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") + } + panic("invalid emitter state") +} + +// Expect STREAM-START. +func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_STREAM_START_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") + } + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = event.encoding + if emitter.encoding == yaml_ANY_ENCODING { + emitter.encoding = yaml_UTF8_ENCODING + } + } + if emitter.best_indent < 2 || emitter.best_indent > 9 { + emitter.best_indent = 2 + } + if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { + emitter.best_width = 80 + } + if emitter.best_width < 0 { + emitter.best_width = 1<<31 - 1 + } + if emitter.line_break == yaml_ANY_BREAK { + emitter.line_break = yaml_LN_BREAK + } + + emitter.indent = -1 + emitter.line = 0 + emitter.column = 0 + emitter.whitespace = true + emitter.indention = true + + if emitter.encoding != yaml_UTF8_ENCODING { + if !yaml_emitter_write_bom(emitter) { + return false + } + } + emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE + return true +} + +// Expect DOCUMENT-START or STREAM-END. +func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + + if event.typ == yaml_DOCUMENT_START_EVENT { + + if event.version_directive != nil { + if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { + return false + } + } + + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { + return false + } + if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { + return false + } + } + + for i := 0; i < len(default_tag_directives); i++ { + tag_directive := &default_tag_directives[i] + if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { + return false + } + } + + implicit := event.implicit + if !first || emitter.canonical { + implicit = false + } + + if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if event.version_directive != nil { + implicit = false + if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if len(event.tag_directives) > 0 { + implicit = false + for i := 0; i < len(event.tag_directives); i++ { + tag_directive := &event.tag_directives[i] + if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { + return false + } + if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + if yaml_emitter_check_empty_document(emitter) { + implicit = false + } + if !implicit { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { + return false + } + if emitter.canonical { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + } + + emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE + return true + } + + if event.typ == yaml_STREAM_END_EVENT { + if emitter.open_ended { + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_END_STATE + return true + } + + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") +} + +// Expect the root node. +func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { + emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) + return yaml_emitter_emit_node(emitter, event, true, false, false, false) +} + +// Expect DOCUMENT-END. +func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if event.typ != yaml_DOCUMENT_END_EVENT { + return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !event.implicit { + // [Go] Allocate the slice elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_flush(emitter) { + return false + } + emitter.state = yaml_EMIT_DOCUMENT_START_STATE + emitter.tag_directives = emitter.tag_directives[:0] + return true +} + +// Expect a flow item node. +func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a flow key node. +func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + emitter.flow_level++ + } + + if event.typ == yaml_MAPPING_END_EVENT { + emitter.flow_level-- + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + if emitter.canonical && !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + + if !first { + if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { + return false + } + } + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + + if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a flow value node. +func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if emitter.canonical || emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block item node. +func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { + return false + } + } + if event.typ == yaml_SEQUENCE_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) + return yaml_emitter_emit_node(emitter, event, false, true, false, false) +} + +// Expect a block key node. +func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { + if first { + if !yaml_emitter_increase_indent(emitter, false, false) { + return false + } + } + if event.typ == yaml_MAPPING_END_EVENT { + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true + } + if !yaml_emitter_write_indent(emitter) { + return false + } + if yaml_emitter_check_simple_key(emitter) { + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, true) + } + if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { + return false + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a block value node. +func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { + if simple { + if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { + return false + } + } else { + if !yaml_emitter_write_indent(emitter) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { + return false + } + } + emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) + return yaml_emitter_emit_node(emitter, event, false, false, true, false) +} + +// Expect a node. +func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, + root bool, sequence bool, mapping bool, simple_key bool) bool { + + emitter.root_context = root + emitter.sequence_context = sequence + emitter.mapping_context = mapping + emitter.simple_key_context = simple_key + + switch event.typ { + case yaml_ALIAS_EVENT: + return yaml_emitter_emit_alias(emitter, event) + case yaml_SCALAR_EVENT: + return yaml_emitter_emit_scalar(emitter, event) + case yaml_SEQUENCE_START_EVENT: + return yaml_emitter_emit_sequence_start(emitter, event) + case yaml_MAPPING_START_EVENT: + return yaml_emitter_emit_mapping_start(emitter, event) + default: + return yaml_emitter_set_emitter_error(emitter, + fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) + } +} + +// Expect ALIAS. +func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SCALAR. +func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_select_scalar_style(emitter, event) { + return false + } + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if !yaml_emitter_increase_indent(emitter, true, false) { + return false + } + if !yaml_emitter_process_scalar(emitter) { + return false + } + emitter.indent = emitter.indents[len(emitter.indents)-1] + emitter.indents = emitter.indents[:len(emitter.indents)-1] + emitter.state = emitter.states[len(emitter.states)-1] + emitter.states = emitter.states[:len(emitter.states)-1] + return true +} + +// Expect SEQUENCE-START. +func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || + yaml_emitter_check_empty_sequence(emitter) { + emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE + } + return true +} + +// Expect MAPPING-START. +func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { + if !yaml_emitter_process_anchor(emitter) { + return false + } + if !yaml_emitter_process_tag(emitter) { + return false + } + if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || + yaml_emitter_check_empty_mapping(emitter) { + emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE + } else { + emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE + } + return true +} + +// Check if the document content is an empty scalar. +func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { + return false // [Go] Huh? +} + +// Check if the next events represent an empty sequence. +func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT +} + +// Check if the next events represent an empty mapping. +func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { + if len(emitter.events)-emitter.events_head < 2 { + return false + } + return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && + emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT +} + +// Check if the next node can be expressed as a simple key. +func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { + length := 0 + switch emitter.events[emitter.events_head].typ { + case yaml_ALIAS_EVENT: + length += len(emitter.anchor_data.anchor) + case yaml_SCALAR_EVENT: + if emitter.scalar_data.multiline { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + + len(emitter.scalar_data.value) + case yaml_SEQUENCE_START_EVENT: + if !yaml_emitter_check_empty_sequence(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + case yaml_MAPPING_START_EVENT: + if !yaml_emitter_check_empty_mapping(emitter) { + return false + } + length += len(emitter.anchor_data.anchor) + + len(emitter.tag_data.handle) + + len(emitter.tag_data.suffix) + default: + return false + } + return length <= 128 +} + +// Determine an acceptable scalar style. +func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 + if no_tag && !event.implicit && !event.quoted_implicit { + return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") + } + + style := event.scalar_style() + if style == yaml_ANY_SCALAR_STYLE { + style = yaml_PLAIN_SCALAR_STYLE + } + if emitter.canonical { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + if emitter.simple_key_context && emitter.scalar_data.multiline { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + + if style == yaml_PLAIN_SCALAR_STYLE { + if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || + emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + if no_tag && !event.implicit { + style = yaml_SINGLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { + if !emitter.scalar_data.single_quoted_allowed { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { + if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + } + + if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { + emitter.tag_data.handle = []byte{'!'} + } + emitter.scalar_data.style = style + return true +} + +// Write an anchor. +func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { + if emitter.anchor_data.anchor == nil { + return true + } + c := []byte{'&'} + if emitter.anchor_data.alias { + c[0] = '*' + } + if !yaml_emitter_write_indicator(emitter, c, true, false, false) { + return false + } + return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) +} + +// Write a tag. +func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { + if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { + return true + } + if len(emitter.tag_data.handle) > 0 { + if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { + return false + } + if len(emitter.tag_data.suffix) > 0 { + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + } + } else { + // [Go] Allocate these slices elsewhere. + if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { + return false + } + if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { + return false + } + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { + return false + } + } + return true +} + +// Write a scalar. +func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { + switch emitter.scalar_data.style { + case yaml_PLAIN_SCALAR_STYLE: + return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_SINGLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_DOUBLE_QUOTED_SCALAR_STYLE: + return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) + + case yaml_LITERAL_SCALAR_STYLE: + return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) + + case yaml_FOLDED_SCALAR_STYLE: + return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) + } + panic("unknown scalar style") +} + +// Check if a %YAML directive is valid. +func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { + if version_directive.major != 1 || version_directive.minor != 1 { + return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") + } + return true +} + +// Check if a %TAG directive is valid. +func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { + handle := tag_directive.handle + prefix := tag_directive.prefix + if len(handle) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") + } + if handle[0] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") + } + if handle[len(handle)-1] != '!' { + return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") + } + for i := 1; i < len(handle)-1; i += width(handle[i]) { + if !is_alpha(handle, i) { + return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") + } + } + if len(prefix) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") + } + return true +} + +// Check if an anchor is valid. +func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { + if len(anchor) == 0 { + problem := "anchor value must not be empty" + if alias { + problem = "alias value must not be empty" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + for i := 0; i < len(anchor); i += width(anchor[i]) { + if !is_alpha(anchor, i) { + problem := "anchor value must contain alphanumerical characters only" + if alias { + problem = "alias value must contain alphanumerical characters only" + } + return yaml_emitter_set_emitter_error(emitter, problem) + } + } + emitter.anchor_data.anchor = anchor + emitter.anchor_data.alias = alias + return true +} + +// Check if a tag is valid. +func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { + if len(tag) == 0 { + return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") + } + for i := 0; i < len(emitter.tag_directives); i++ { + tag_directive := &emitter.tag_directives[i] + if bytes.HasPrefix(tag, tag_directive.prefix) { + emitter.tag_data.handle = tag_directive.handle + emitter.tag_data.suffix = tag[len(tag_directive.prefix):] + return true + } + } + emitter.tag_data.suffix = tag + return true +} + +// Check if a scalar is valid. +func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { + var ( + block_indicators = false + flow_indicators = false + line_breaks = false + special_characters = false + + leading_space = false + leading_break = false + trailing_space = false + trailing_break = false + break_space = false + space_break = false + + preceded_by_whitespace = false + followed_by_whitespace = false + previous_space = false + previous_break = false + ) + + emitter.scalar_data.value = value + + if len(value) == 0 { + emitter.scalar_data.multiline = false + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = false + return true + } + + if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { + block_indicators = true + flow_indicators = true + } + + preceded_by_whitespace = true + for i, w := 0, 0; i < len(value); i += w { + w = width(value[i]) + followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) + + if i == 0 { + switch value[i] { + case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': + flow_indicators = true + block_indicators = true + case '?', ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '-': + if followed_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } else { + switch value[i] { + case ',', '?', '[', ']', '{', '}': + flow_indicators = true + case ':': + flow_indicators = true + if followed_by_whitespace { + block_indicators = true + } + case '#': + if preceded_by_whitespace { + flow_indicators = true + block_indicators = true + } + } + } + + if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { + special_characters = true + } + if is_space(value, i) { + if i == 0 { + leading_space = true + } + if i+width(value[i]) == len(value) { + trailing_space = true + } + if previous_break { + break_space = true + } + previous_space = true + previous_break = false + } else if is_break(value, i) { + line_breaks = true + if i == 0 { + leading_break = true + } + if i+width(value[i]) == len(value) { + trailing_break = true + } + if previous_space { + space_break = true + } + previous_space = false + previous_break = true + } else { + previous_space = false + previous_break = false + } + + // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. + preceded_by_whitespace = is_blankz(value, i) + } + + emitter.scalar_data.multiline = line_breaks + emitter.scalar_data.flow_plain_allowed = true + emitter.scalar_data.block_plain_allowed = true + emitter.scalar_data.single_quoted_allowed = true + emitter.scalar_data.block_allowed = true + + if leading_space || leading_break || trailing_space || trailing_break { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if trailing_space { + emitter.scalar_data.block_allowed = false + } + if break_space { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + } + if space_break || special_characters { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + emitter.scalar_data.single_quoted_allowed = false + emitter.scalar_data.block_allowed = false + } + if line_breaks { + emitter.scalar_data.flow_plain_allowed = false + emitter.scalar_data.block_plain_allowed = false + } + if flow_indicators { + emitter.scalar_data.flow_plain_allowed = false + } + if block_indicators { + emitter.scalar_data.block_plain_allowed = false + } + return true +} + +// Check if the event data is valid. +func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { + + emitter.anchor_data.anchor = nil + emitter.tag_data.handle = nil + emitter.tag_data.suffix = nil + emitter.scalar_data.value = nil + + switch event.typ { + case yaml_ALIAS_EVENT: + if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { + return false + } + + case yaml_SCALAR_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + if !yaml_emitter_analyze_scalar(emitter, event.value) { + return false + } + + case yaml_SEQUENCE_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + + case yaml_MAPPING_START_EVENT: + if len(event.anchor) > 0 { + if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { + return false + } + } + if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { + if !yaml_emitter_analyze_tag(emitter, event.tag) { + return false + } + } + } + return true +} + +// Write the BOM character. +func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { + if !flush(emitter) { + return false + } + pos := emitter.buffer_pos + emitter.buffer[pos+0] = '\xEF' + emitter.buffer[pos+1] = '\xBB' + emitter.buffer[pos+2] = '\xBF' + emitter.buffer_pos += 3 + return true +} + +func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { + indent := emitter.indent + if indent < 0 { + indent = 0 + } + if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { + if !put_break(emitter) { + return false + } + } + for emitter.column < indent { + if !put(emitter, ' ') { + return false + } + } + emitter.whitespace = true + emitter.indention = true + return true +} + +func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, indicator) { + return false + } + emitter.whitespace = is_whitespace + emitter.indention = (emitter.indention && is_indention) + emitter.open_ended = false + return true +} + +func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + if !write_all(emitter, value) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { + if need_whitespace && !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + for i := 0; i < len(value); { + var must_write bool + switch value[i] { + case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': + must_write = true + default: + must_write = is_alpha(value, i) + } + if must_write { + if !write(emitter, value, &i) { + return false + } + } else { + w := width(value[i]) + for k := 0; k < w; k++ { + octet := value[i] + i++ + if !put(emitter, '%') { + return false + } + + c := octet >> 4 + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + + c = octet & 0x0f + if c < 10 { + c += '0' + } else { + c += 'A' - 10 + } + if !put(emitter, c) { + return false + } + } + } + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + if !emitter.whitespace { + if !put(emitter, ' ') { + return false + } + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + + emitter.whitespace = false + emitter.indention = false + if emitter.root_context { + emitter.open_ended = true + } + + return true +} + +func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { + return false + } + + spaces := false + breaks := false + for i := 0; i < len(value); { + if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + spaces = true + } else if is_break(value, i) { + if !breaks && value[i] == '\n' { + if !put_break(emitter) { + return false + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if value[i] == '\'' { + if !put(emitter, '\'') { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + spaces = false + breaks = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { + spaces := false + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { + return false + } + + for i := 0; i < len(value); { + if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || + is_bom(value, i) || is_break(value, i) || + value[i] == '"' || value[i] == '\\' { + + octet := value[i] + + var w int + var v rune + switch { + case octet&0x80 == 0x00: + w, v = 1, rune(octet&0x7F) + case octet&0xE0 == 0xC0: + w, v = 2, rune(octet&0x1F) + case octet&0xF0 == 0xE0: + w, v = 3, rune(octet&0x0F) + case octet&0xF8 == 0xF0: + w, v = 4, rune(octet&0x07) + } + for k := 1; k < w; k++ { + octet = value[i+k] + v = (v << 6) + (rune(octet) & 0x3F) + } + i += w + + if !put(emitter, '\\') { + return false + } + + var ok bool + switch v { + case 0x00: + ok = put(emitter, '0') + case 0x07: + ok = put(emitter, 'a') + case 0x08: + ok = put(emitter, 'b') + case 0x09: + ok = put(emitter, 't') + case 0x0A: + ok = put(emitter, 'n') + case 0x0b: + ok = put(emitter, 'v') + case 0x0c: + ok = put(emitter, 'f') + case 0x0d: + ok = put(emitter, 'r') + case 0x1b: + ok = put(emitter, 'e') + case 0x22: + ok = put(emitter, '"') + case 0x5c: + ok = put(emitter, '\\') + case 0x85: + ok = put(emitter, 'N') + case 0xA0: + ok = put(emitter, '_') + case 0x2028: + ok = put(emitter, 'L') + case 0x2029: + ok = put(emitter, 'P') + default: + if v <= 0xFF { + ok = put(emitter, 'x') + w = 2 + } else if v <= 0xFFFF { + ok = put(emitter, 'u') + w = 4 + } else { + ok = put(emitter, 'U') + w = 8 + } + for k := (w - 1) * 4; ok && k >= 0; k -= 4 { + digit := byte((v >> uint(k)) & 0x0F) + if digit < 10 { + ok = put(emitter, digit+'0') + } else { + ok = put(emitter, digit+'A'-10) + } + } + } + if !ok { + return false + } + spaces = false + } else if is_space(value, i) { + if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { + if !yaml_emitter_write_indent(emitter) { + return false + } + if is_space(value, i+1) { + if !put(emitter, '\\') { + return false + } + } + i += width(value[i]) + } else if !write(emitter, value, &i) { + return false + } + spaces = true + } else { + if !write(emitter, value, &i) { + return false + } + spaces = false + } + } + if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { + return false + } + emitter.whitespace = false + emitter.indention = false + return true +} + +func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { + if is_space(value, 0) || is_break(value, 0) { + indent_hint := []byte{'0' + byte(emitter.best_indent)} + if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { + return false + } + } + + emitter.open_ended = false + + var chomp_hint [1]byte + if len(value) == 0 { + chomp_hint[0] = '-' + } else { + i := len(value) - 1 + for value[i]&0xC0 == 0x80 { + i-- + } + if !is_break(value, i) { + chomp_hint[0] = '-' + } else if i == 0 { + chomp_hint[0] = '+' + emitter.open_ended = true + } else { + i-- + for value[i]&0xC0 == 0x80 { + i-- + } + if is_break(value, i) { + chomp_hint[0] = '+' + emitter.open_ended = true + } + } + } + if chomp_hint[0] != 0 { + if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { + return false + } + } + return true +} + +func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + breaks := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + } + if !write(emitter, value, &i) { + return false + } + emitter.indention = false + breaks = false + } + } + + return true +} + +func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { + if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { + return false + } + if !yaml_emitter_write_block_scalar_hints(emitter, value) { + return false + } + + if !put_break(emitter) { + return false + } + emitter.indention = true + emitter.whitespace = true + + breaks := true + leading_spaces := true + for i := 0; i < len(value); { + if is_break(value, i) { + if !breaks && !leading_spaces && value[i] == '\n' { + k := 0 + for is_break(value, k) { + k += width(value[k]) + } + if !is_blankz(value, k) { + if !put_break(emitter) { + return false + } + } + } + if !write_break(emitter, value, &i) { + return false + } + emitter.indention = true + breaks = true + } else { + if breaks { + if !yaml_emitter_write_indent(emitter) { + return false + } + leading_spaces = is_blank(value, i) + } + if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { + if !yaml_emitter_write_indent(emitter) { + return false + } + i += width(value[i]) + } else { + if !write(emitter, value, &i) { + return false + } + } + emitter.indention = false + breaks = false + } + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/encode.go b/vendor/gopkg.in/yaml.v2/encode.go new file mode 100644 index 0000000000..0ee738e11b --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/encode.go @@ -0,0 +1,390 @@ +package yaml + +import ( + "encoding" + "fmt" + "io" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +// jsonNumber is the interface of the encoding/json.Number datatype. +// Repeating the interface here avoids a dependency on encoding/json, and also +// supports other libraries like jsoniter, which use a similar datatype with +// the same interface. Detecting this interface is useful when dealing with +// structures containing json.Number, which is a string under the hood. The +// encoder should prefer the use of Int64(), Float64() and string(), in that +// order, when encoding this type. +type jsonNumber interface { + Float64() (float64, error) + Int64() (int64, error) + String() string +} + +type encoder struct { + emitter yaml_emitter_t + event yaml_event_t + out []byte + flow bool + // doneInit holds whether the initial stream_start_event has been + // emitted. + doneInit bool +} + +func newEncoder() *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_string(&e.emitter, &e.out) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func newEncoderWithWriter(w io.Writer) *encoder { + e := &encoder{} + yaml_emitter_initialize(&e.emitter) + yaml_emitter_set_output_writer(&e.emitter, w) + yaml_emitter_set_unicode(&e.emitter, true) + return e +} + +func (e *encoder) init() { + if e.doneInit { + return + } + yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) + e.emit() + e.doneInit = true +} + +func (e *encoder) finish() { + e.emitter.open_ended = false + yaml_stream_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) destroy() { + yaml_emitter_delete(&e.emitter) +} + +func (e *encoder) emit() { + // This will internally delete the e.event value. + e.must(yaml_emitter_emit(&e.emitter, &e.event)) +} + +func (e *encoder) must(ok bool) { + if !ok { + msg := e.emitter.problem + if msg == "" { + msg = "unknown problem generating YAML content" + } + failf("%s", msg) + } +} + +func (e *encoder) marshalDoc(tag string, in reflect.Value) { + e.init() + yaml_document_start_event_initialize(&e.event, nil, nil, true) + e.emit() + e.marshal(tag, in) + yaml_document_end_event_initialize(&e.event, true) + e.emit() +} + +func (e *encoder) marshal(tag string, in reflect.Value) { + if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { + e.nilv() + return + } + iface := in.Interface() + switch m := iface.(type) { + case jsonNumber: + integer, err := m.Int64() + if err == nil { + // In this case the json.Number is a valid int64 + in = reflect.ValueOf(integer) + break + } + float, err := m.Float64() + if err == nil { + // In this case the json.Number is a valid float64 + in = reflect.ValueOf(float) + break + } + // fallback case - no number could be obtained + in = reflect.ValueOf(m.String()) + case time.Time, *time.Time: + // Although time.Time implements TextMarshaler, + // we don't want to treat it as a string for YAML + // purposes because YAML has special support for + // timestamps. + case Marshaler: + v, err := m.MarshalYAML() + if err != nil { + fail(err) + } + if v == nil { + e.nilv() + return + } + in = reflect.ValueOf(v) + case encoding.TextMarshaler: + text, err := m.MarshalText() + if err != nil { + fail(err) + } + in = reflect.ValueOf(string(text)) + case nil: + e.nilv() + return + } + switch in.Kind() { + case reflect.Interface: + e.marshal(tag, in.Elem()) + case reflect.Map: + e.mapv(tag, in) + case reflect.Ptr: + if in.Type() == ptrTimeType { + e.timev(tag, in.Elem()) + } else { + e.marshal(tag, in.Elem()) + } + case reflect.Struct: + if in.Type() == timeType { + e.timev(tag, in) + } else { + e.structv(tag, in) + } + case reflect.Slice, reflect.Array: + if in.Type().Elem() == mapItemType { + e.itemsv(tag, in) + } else { + e.slicev(tag, in) + } + case reflect.String: + e.stringv(tag, in) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if in.Type() == durationType { + e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) + } else { + e.intv(tag, in) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + e.uintv(tag, in) + case reflect.Float32, reflect.Float64: + e.floatv(tag, in) + case reflect.Bool: + e.boolv(tag, in) + default: + panic("cannot marshal type: " + in.Type().String()) + } +} + +func (e *encoder) mapv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + keys := keyList(in.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + e.marshal("", k) + e.marshal("", in.MapIndex(k)) + } + }) +} + +func (e *encoder) itemsv(tag string, in reflect.Value) { + e.mappingv(tag, func() { + slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) + for _, item := range slice { + e.marshal("", reflect.ValueOf(item.Key)) + e.marshal("", reflect.ValueOf(item.Value)) + } + }) +} + +func (e *encoder) structv(tag string, in reflect.Value) { + sinfo, err := getStructInfo(in.Type()) + if err != nil { + panic(err) + } + e.mappingv(tag, func() { + for _, info := range sinfo.FieldsList { + var value reflect.Value + if info.Inline == nil { + value = in.Field(info.Num) + } else { + value = in.FieldByIndex(info.Inline) + } + if info.OmitEmpty && isZero(value) { + continue + } + e.marshal("", reflect.ValueOf(info.Key)) + e.flow = info.Flow + e.marshal("", value) + } + if sinfo.InlineMap >= 0 { + m := in.Field(sinfo.InlineMap) + if m.Len() > 0 { + e.flow = false + keys := keyList(m.MapKeys()) + sort.Sort(keys) + for _, k := range keys { + if _, found := sinfo.FieldsMap[k.String()]; found { + panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) + } + e.marshal("", k) + e.flow = false + e.marshal("", m.MapIndex(k)) + } + } + } + }) +} + +func (e *encoder) mappingv(tag string, f func()) { + implicit := tag == "" + style := yaml_BLOCK_MAPPING_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_MAPPING_STYLE + } + yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) + e.emit() + f() + yaml_mapping_end_event_initialize(&e.event) + e.emit() +} + +func (e *encoder) slicev(tag string, in reflect.Value) { + implicit := tag == "" + style := yaml_BLOCK_SEQUENCE_STYLE + if e.flow { + e.flow = false + style = yaml_FLOW_SEQUENCE_STYLE + } + e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) + e.emit() + n := in.Len() + for i := 0; i < n; i++ { + e.marshal("", in.Index(i)) + } + e.must(yaml_sequence_end_event_initialize(&e.event)) + e.emit() +} + +// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. +// +// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported +// in YAML 1.2 and by this package, but these should be marshalled quoted for +// the time being for compatibility with other parsers. +func isBase60Float(s string) (result bool) { + // Fast path. + if s == "" { + return false + } + c := s[0] + if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { + return false + } + // Do the full match. + return base60float.MatchString(s) +} + +// From http://yaml.org/type/float.html, except the regular expression there +// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. +var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +func (e *encoder) stringv(tag string, in reflect.Value) { + var style yaml_scalar_style_t + s := in.String() + canUsePlain := true + switch { + case !utf8.ValidString(s): + if tag == yaml_BINARY_TAG { + failf("explicitly tagged !!binary data must be base64-encoded") + } + if tag != "" { + failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) + } + // It can't be encoded directly as YAML so use a binary tag + // and encode it as base64. + tag = yaml_BINARY_TAG + s = encodeBase64(s) + case tag == "": + // Check to see if it would resolve to a specific + // tag when encoded unquoted. If it doesn't, + // there's no need to quote it. + rtag, _ := resolve("", s) + canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s) + } + // Note: it's possible for user code to emit invalid YAML + // if they explicitly specify a tag and a string containing + // text that's incompatible with that tag. + switch { + case strings.Contains(s, "\n"): + style = yaml_LITERAL_SCALAR_STYLE + case canUsePlain: + style = yaml_PLAIN_SCALAR_STYLE + default: + style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + e.emitScalar(s, "", tag, style) +} + +func (e *encoder) boolv(tag string, in reflect.Value) { + var s string + if in.Bool() { + s = "true" + } else { + s = "false" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) intv(tag string, in reflect.Value) { + s := strconv.FormatInt(in.Int(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) uintv(tag string, in reflect.Value) { + s := strconv.FormatUint(in.Uint(), 10) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) timev(tag string, in reflect.Value) { + t := in.Interface().(time.Time) + s := t.Format(time.RFC3339Nano) + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) floatv(tag string, in reflect.Value) { + // Issue #352: When formatting, use the precision of the underlying value + precision := 64 + if in.Kind() == reflect.Float32 { + precision = 32 + } + + s := strconv.FormatFloat(in.Float(), 'g', -1, precision) + switch s { + case "+Inf": + s = ".inf" + case "-Inf": + s = "-.inf" + case "NaN": + s = ".nan" + } + e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) nilv() { + e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) +} + +func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { + implicit := tag == "" + e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) + e.emit() +} diff --git a/vendor/gopkg.in/yaml.v2/go.mod b/vendor/gopkg.in/yaml.v2/go.mod new file mode 100644 index 0000000000..1934e87694 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/go.mod @@ -0,0 +1,5 @@ +module "gopkg.in/yaml.v2" + +require ( + "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 +) diff --git a/vendor/gopkg.in/yaml.v2/parserc.go b/vendor/gopkg.in/yaml.v2/parserc.go new file mode 100644 index 0000000000..81d05dfe57 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/parserc.go @@ -0,0 +1,1095 @@ +package yaml + +import ( + "bytes" +) + +// The parser implements the following grammar: +// +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// implicit_document ::= block_node DOCUMENT-END* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// block_node_or_indentless_sequence ::= +// ALIAS +// | properties (block_content | indentless_block_sequence)? +// | block_content +// | indentless_block_sequence +// block_node ::= ALIAS +// | properties block_content? +// | block_content +// flow_node ::= ALIAS +// | properties flow_content? +// | flow_content +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// block_content ::= block_collection | flow_collection | SCALAR +// flow_content ::= flow_collection | SCALAR +// block_collection ::= block_sequence | block_mapping +// flow_collection ::= flow_sequence | flow_mapping +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// block_mapping ::= BLOCK-MAPPING_START +// ((KEY block_node_or_indentless_sequence?)? +// (VALUE block_node_or_indentless_sequence?)?)* +// BLOCK-END +// flow_sequence ::= FLOW-SEQUENCE-START +// (flow_sequence_entry FLOW-ENTRY)* +// flow_sequence_entry? +// FLOW-SEQUENCE-END +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// flow_mapping ::= FLOW-MAPPING-START +// (flow_mapping_entry FLOW-ENTRY)* +// flow_mapping_entry? +// FLOW-MAPPING-END +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? + +// Peek the next token in the token queue. +func peek_token(parser *yaml_parser_t) *yaml_token_t { + if parser.token_available || yaml_parser_fetch_more_tokens(parser) { + return &parser.tokens[parser.tokens_head] + } + return nil +} + +// Remove the next token from the queue (must be called after peek_token). +func skip_token(parser *yaml_parser_t) { + parser.token_available = false + parser.tokens_parsed++ + parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN + parser.tokens_head++ +} + +// Get the next event. +func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { + // Erase the event object. + *event = yaml_event_t{} + + // No events after the end of the stream or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { + return true + } + + // Generate the next event. + return yaml_parser_state_machine(parser, event) +} + +// Set parser error. +func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { + parser.error = yaml_PARSER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = problem_mark + return false +} + +// State dispatcher. +func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { + //trace("yaml_parser_state_machine", "state:", parser.state.String()) + + switch parser.state { + case yaml_PARSE_STREAM_START_STATE: + return yaml_parser_parse_stream_start(parser, event) + + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, true) + + case yaml_PARSE_DOCUMENT_START_STATE: + return yaml_parser_parse_document_start(parser, event, false) + + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return yaml_parser_parse_document_content(parser, event) + + case yaml_PARSE_DOCUMENT_END_STATE: + return yaml_parser_parse_document_end(parser, event) + + case yaml_PARSE_BLOCK_NODE_STATE: + return yaml_parser_parse_node(parser, event, true, false) + + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return yaml_parser_parse_node(parser, event, true, true) + + case yaml_PARSE_FLOW_NODE_STATE: + return yaml_parser_parse_node(parser, event, false, false) + + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, true) + + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_block_sequence_entry(parser, event, false) + + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_indentless_sequence_entry(parser, event) + + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, true) + + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return yaml_parser_parse_block_mapping_key(parser, event, false) + + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return yaml_parser_parse_block_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, true) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return yaml_parser_parse_flow_sequence_entry(parser, event, false) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) + + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) + + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, true) + + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return yaml_parser_parse_flow_mapping_key(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, false) + + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return yaml_parser_parse_flow_mapping_value(parser, event, true) + + default: + panic("invalid parser state") + } +} + +// Parse the production: +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ +func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_STREAM_START_TOKEN { + return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) + } + parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + encoding: token.encoding, + } + skip_token(parser) + return true +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* +func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { + + token := peek_token(parser) + if token == nil { + return false + } + + // Parse extra document end indicators. + if !implicit { + for token.typ == yaml_DOCUMENT_END_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && + token.typ != yaml_TAG_DIRECTIVE_TOKEN && + token.typ != yaml_DOCUMENT_START_TOKEN && + token.typ != yaml_STREAM_END_TOKEN { + // Parse an implicit document. + if !yaml_parser_process_directives(parser, nil, nil) { + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_BLOCK_NODE_STATE + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + } else if token.typ != yaml_STREAM_END_TOKEN { + // Parse an explicit document. + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + start_mark := token.start_mark + if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { + return false + } + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_DOCUMENT_START_TOKEN { + yaml_parser_set_parser_error(parser, + "did not find expected ", token.start_mark) + return false + } + parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) + parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE + end_mark := token.end_mark + + *event = yaml_event_t{ + typ: yaml_DOCUMENT_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + version_directive: version_directive, + tag_directives: tag_directives, + implicit: false, + } + skip_token(parser) + + } else { + // Parse the stream end. + parser.state = yaml_PARSE_END_STATE + *event = yaml_event_t{ + typ: yaml_STREAM_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + } + + return true +} + +// Parse the productions: +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** +// +func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || + token.typ == yaml_TAG_DIRECTIVE_TOKEN || + token.typ == yaml_DOCUMENT_START_TOKEN || + token.typ == yaml_DOCUMENT_END_TOKEN || + token.typ == yaml_STREAM_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + return yaml_parser_process_empty_scalar(parser, event, + token.start_mark) + } + return yaml_parser_parse_node(parser, event, true, false) +} + +// Parse the productions: +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// +func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + start_mark := token.start_mark + end_mark := token.start_mark + + implicit := true + if token.typ == yaml_DOCUMENT_END_TOKEN { + end_mark = token.end_mark + skip_token(parser) + implicit = false + } + + parser.tag_directives = parser.tag_directives[:0] + + parser.state = yaml_PARSE_DOCUMENT_START_STATE + *event = yaml_event_t{ + typ: yaml_DOCUMENT_END_EVENT, + start_mark: start_mark, + end_mark: end_mark, + implicit: implicit, + } + return true +} + +// Parse the productions: +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** +func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { + //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_ALIAS_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + *event = yaml_event_t{ + typ: yaml_ALIAS_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + anchor: token.value, + } + skip_token(parser) + return true + } + + start_mark := token.start_mark + end_mark := token.start_mark + + var tag_token bool + var tag_handle, tag_suffix, anchor []byte + var tag_mark yaml_mark_t + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + start_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } else if token.typ == yaml_TAG_TOKEN { + tag_token = true + tag_handle = token.value + tag_suffix = token.suffix + start_mark = token.start_mark + tag_mark = token.start_mark + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_ANCHOR_TOKEN { + anchor = token.value + end_mark = token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + } + + var tag []byte + if tag_token { + if len(tag_handle) == 0 { + tag = tag_suffix + tag_suffix = nil + } else { + for i := range parser.tag_directives { + if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { + tag = append([]byte(nil), parser.tag_directives[i].prefix...) + tag = append(tag, tag_suffix...) + break + } + } + if len(tag) == 0 { + yaml_parser_set_parser_error_context(parser, + "while parsing a node", start_mark, + "found undefined tag handle", tag_mark) + return false + } + } + } + + implicit := len(tag) == 0 + if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_SCALAR_TOKEN { + var plain_implicit, quoted_implicit bool + end_mark = token.end_mark + if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { + plain_implicit = true + } else if len(tag) == 0 { + quoted_implicit = true + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + value: token.value, + implicit: plain_implicit, + quoted_implicit: quoted_implicit, + style: yaml_style_t(token.style), + } + skip_token(parser) + return true + } + if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { + // [Go] Some of the events below can be merged as they differ only on style. + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), + } + return true + } + if token.typ == yaml_FLOW_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_SEQUENCE_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), + } + return true + } + if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { + end_mark = token.end_mark + parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), + } + return true + } + if len(anchor) > 0 || len(tag) > 0 { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: start_mark, + end_mark: end_mark, + anchor: anchor, + tag: tag, + implicit: implicit, + quoted_implicit: false, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true + } + + context := "while parsing a flow node" + if block { + context = "while parsing a block node" + } + yaml_parser_set_parser_error_context(parser, context, start_mark, + "did not find expected node content", token.start_mark) + return false +} + +// Parse the productions: +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* +// +func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } else { + parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } + if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block collection", context_mark, + "did not find expected '-' indicator", token.start_mark) +} + +// Parse the productions: +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * +func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_BLOCK_ENTRY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_BLOCK_ENTRY_TOKEN && + token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, true, false) + } + parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? + } + return true +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* +// +// BLOCK-END +// ********* +// +func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ == yaml_KEY_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } else { + parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + } else if token.typ == yaml_BLOCK_END_TOKEN { + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true + } + + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a block mapping", context_mark, + "did not find expected key", token.start_mark) +} + +// Parse the productions: +// block_mapping ::= BLOCK-MAPPING_START +// +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END +// +// +func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + mark := token.end_mark + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_KEY_TOKEN && + token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_BLOCK_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, true, true) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) + } + parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow sequence", context_mark, + "did not find expected ',' or ']'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_START_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + implicit: true, + style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), + } + skip_token(parser) + return true + } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + + *event = yaml_event_t{ + typ: yaml_SEQUENCE_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + + skip_token(parser) + return true +} + +// +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + mark := token.end_mark + skip_token(parser) + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * +// +func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token := peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Parse the productions: +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * +// +func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { + token := peek_token(parser) + if token == nil { + return false + } + parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? + } + return true +} + +// Parse the productions: +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * +// +func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { + if first { + token := peek_token(parser) + parser.marks = append(parser.marks, token.start_mark) + skip_token(parser) + } + + token := peek_token(parser) + if token == nil { + return false + } + + if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + if !first { + if token.typ == yaml_FLOW_ENTRY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } else { + context_mark := parser.marks[len(parser.marks)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + return yaml_parser_set_parser_error_context(parser, + "while parsing a flow mapping", context_mark, + "did not find expected ',' or '}'", token.start_mark) + } + } + + if token.typ == yaml_KEY_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_VALUE_TOKEN && + token.typ != yaml_FLOW_ENTRY_TOKEN && + token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } else { + parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + + parser.state = parser.states[len(parser.states)-1] + parser.states = parser.states[:len(parser.states)-1] + parser.marks = parser.marks[:len(parser.marks)-1] + *event = yaml_event_t{ + typ: yaml_MAPPING_END_EVENT, + start_mark: token.start_mark, + end_mark: token.end_mark, + } + skip_token(parser) + return true +} + +// Parse the productions: +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * +// +func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { + token := peek_token(parser) + if token == nil { + return false + } + if empty { + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) + } + if token.typ == yaml_VALUE_TOKEN { + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { + parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) + return yaml_parser_parse_node(parser, event, false, false) + } + } + parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE + return yaml_parser_process_empty_scalar(parser, event, token.start_mark) +} + +// Generate an empty scalar event. +func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { + *event = yaml_event_t{ + typ: yaml_SCALAR_EVENT, + start_mark: mark, + end_mark: mark, + value: nil, // Empty + implicit: true, + style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), + } + return true +} + +var default_tag_directives = []yaml_tag_directive_t{ + {[]byte("!"), []byte("!")}, + {[]byte("!!"), []byte("tag:yaml.org,2002:")}, +} + +// Parse directives. +func yaml_parser_process_directives(parser *yaml_parser_t, + version_directive_ref **yaml_version_directive_t, + tag_directives_ref *[]yaml_tag_directive_t) bool { + + var version_directive *yaml_version_directive_t + var tag_directives []yaml_tag_directive_t + + token := peek_token(parser) + if token == nil { + return false + } + + for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { + if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { + if version_directive != nil { + yaml_parser_set_parser_error(parser, + "found duplicate %YAML directive", token.start_mark) + return false + } + if token.major != 1 || token.minor != 1 { + yaml_parser_set_parser_error(parser, + "found incompatible YAML document", token.start_mark) + return false + } + version_directive = &yaml_version_directive_t{ + major: token.major, + minor: token.minor, + } + } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { + value := yaml_tag_directive_t{ + handle: token.value, + prefix: token.prefix, + } + if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { + return false + } + tag_directives = append(tag_directives, value) + } + + skip_token(parser) + token = peek_token(parser) + if token == nil { + return false + } + } + + for i := range default_tag_directives { + if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { + return false + } + } + + if version_directive_ref != nil { + *version_directive_ref = version_directive + } + if tag_directives_ref != nil { + *tag_directives_ref = tag_directives + } + return true +} + +// Append a tag directive to the directives stack. +func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { + for i := range parser.tag_directives { + if bytes.Equal(value.handle, parser.tag_directives[i].handle) { + if allow_duplicates { + return true + } + return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) + } + } + + // [Go] I suspect the copy is unnecessary. This was likely done + // because there was no way to track ownership of the data. + value_copy := yaml_tag_directive_t{ + handle: make([]byte, len(value.handle)), + prefix: make([]byte, len(value.prefix)), + } + copy(value_copy.handle, value.handle) + copy(value_copy.prefix, value.prefix) + parser.tag_directives = append(parser.tag_directives, value_copy) + return true +} diff --git a/vendor/gopkg.in/yaml.v2/readerc.go b/vendor/gopkg.in/yaml.v2/readerc.go new file mode 100644 index 0000000000..7c1f5fac3d --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/readerc.go @@ -0,0 +1,412 @@ +package yaml + +import ( + "io" +) + +// Set the reader error and return 0. +func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { + parser.error = yaml_READER_ERROR + parser.problem = problem + parser.problem_offset = offset + parser.problem_value = value + return false +} + +// Byte order marks. +const ( + bom_UTF8 = "\xef\xbb\xbf" + bom_UTF16LE = "\xff\xfe" + bom_UTF16BE = "\xfe\xff" +) + +// Determine the input stream encoding by checking the BOM symbol. If no BOM is +// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. +func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { + // Ensure that we had enough bytes in the raw buffer. + for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { + if !yaml_parser_update_raw_buffer(parser) { + return false + } + } + + // Determine the encoding. + buf := parser.raw_buffer + pos := parser.raw_buffer_pos + avail := len(buf) - pos + if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { + parser.encoding = yaml_UTF16LE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { + parser.encoding = yaml_UTF16BE_ENCODING + parser.raw_buffer_pos += 2 + parser.offset += 2 + } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { + parser.encoding = yaml_UTF8_ENCODING + parser.raw_buffer_pos += 3 + parser.offset += 3 + } else { + parser.encoding = yaml_UTF8_ENCODING + } + return true +} + +// Update the raw buffer. +func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { + size_read := 0 + + // Return if the raw buffer is full. + if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { + return true + } + + // Return on EOF. + if parser.eof { + return true + } + + // Move the remaining bytes in the raw buffer to the beginning. + if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { + copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) + } + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] + parser.raw_buffer_pos = 0 + + // Call the read handler to fill the buffer. + size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) + parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] + if err == io.EOF { + parser.eof = true + } else if err != nil { + return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) + } + return true +} + +// Ensure that the buffer contains at least `length` characters. +// Return true on success, false on failure. +// +// The length is supposed to be significantly less that the buffer size. +func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { + if parser.read_handler == nil { + panic("read handler must be set") + } + + // [Go] This function was changed to guarantee the requested length size at EOF. + // The fact we need to do this is pretty awful, but the description above implies + // for that to be the case, and there are tests + + // If the EOF flag is set and the raw buffer is empty, do nothing. + if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { + // [Go] ACTUALLY! Read the documentation of this function above. + // This is just broken. To return true, we need to have the + // given length in the buffer. Not doing that means every single + // check that calls this function to make sure the buffer has a + // given length is Go) panicking; or C) accessing invalid memory. + //return true + } + + // Return if the buffer contains enough characters. + if parser.unread >= length { + return true + } + + // Determine the input encoding if it is not known yet. + if parser.encoding == yaml_ANY_ENCODING { + if !yaml_parser_determine_encoding(parser) { + return false + } + } + + // Move the unread characters to the beginning of the buffer. + buffer_len := len(parser.buffer) + if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { + copy(parser.buffer, parser.buffer[parser.buffer_pos:]) + buffer_len -= parser.buffer_pos + parser.buffer_pos = 0 + } else if parser.buffer_pos == buffer_len { + buffer_len = 0 + parser.buffer_pos = 0 + } + + // Open the whole buffer for writing, and cut it before returning. + parser.buffer = parser.buffer[:cap(parser.buffer)] + + // Fill the buffer until it has enough characters. + first := true + for parser.unread < length { + + // Fill the raw buffer if necessary. + if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { + if !yaml_parser_update_raw_buffer(parser) { + parser.buffer = parser.buffer[:buffer_len] + return false + } + } + first = false + + // Decode the raw buffer. + inner: + for parser.raw_buffer_pos != len(parser.raw_buffer) { + var value rune + var width int + + raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos + + // Decode the next character. + switch parser.encoding { + case yaml_UTF8_ENCODING: + // Decode a UTF-8 character. Check RFC 3629 + // (http://www.ietf.org/rfc/rfc3629.txt) for more details. + // + // The following table (taken from the RFC) is used for + // decoding. + // + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // --------------------+------------------------------------ + // 0000 0000-0000 007F | 0xxxxxxx + // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Additionally, the characters in the range 0xD800-0xDFFF + // are prohibited as they are reserved for use with UTF-16 + // surrogate pairs. + + // Determine the length of the UTF-8 sequence. + octet := parser.raw_buffer[parser.raw_buffer_pos] + switch { + case octet&0x80 == 0x00: + width = 1 + case octet&0xE0 == 0xC0: + width = 2 + case octet&0xF0 == 0xE0: + width = 3 + case octet&0xF8 == 0xF0: + width = 4 + default: + // The leading octet is invalid. + return yaml_parser_set_reader_error(parser, + "invalid leading UTF-8 octet", + parser.offset, int(octet)) + } + + // Check if the raw buffer contains an incomplete character. + if width > raw_unread { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-8 octet sequence", + parser.offset, -1) + } + break inner + } + + // Decode the leading octet. + switch { + case octet&0x80 == 0x00: + value = rune(octet & 0x7F) + case octet&0xE0 == 0xC0: + value = rune(octet & 0x1F) + case octet&0xF0 == 0xE0: + value = rune(octet & 0x0F) + case octet&0xF8 == 0xF0: + value = rune(octet & 0x07) + default: + value = 0 + } + + // Check and decode the trailing octets. + for k := 1; k < width; k++ { + octet = parser.raw_buffer[parser.raw_buffer_pos+k] + + // Check if the octet is valid. + if (octet & 0xC0) != 0x80 { + return yaml_parser_set_reader_error(parser, + "invalid trailing UTF-8 octet", + parser.offset+k, int(octet)) + } + + // Decode the octet. + value = (value << 6) + rune(octet&0x3F) + } + + // Check the length of the sequence against the value. + switch { + case width == 1: + case width == 2 && value >= 0x80: + case width == 3 && value >= 0x800: + case width == 4 && value >= 0x10000: + default: + return yaml_parser_set_reader_error(parser, + "invalid length of a UTF-8 sequence", + parser.offset, -1) + } + + // Check the range of the value. + if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { + return yaml_parser_set_reader_error(parser, + "invalid Unicode character", + parser.offset, int(value)) + } + + case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: + var low, high int + if parser.encoding == yaml_UTF16LE_ENCODING { + low, high = 0, 1 + } else { + low, high = 1, 0 + } + + // The UTF-16 encoding is not as simple as one might + // naively think. Check RFC 2781 + // (http://www.ietf.org/rfc/rfc2781.txt). + // + // Normally, two subsequent bytes describe a Unicode + // character. However a special technique (called a + // surrogate pair) is used for specifying character + // values larger than 0xFFFF. + // + // A surrogate pair consists of two pseudo-characters: + // high surrogate area (0xD800-0xDBFF) + // low surrogate area (0xDC00-0xDFFF) + // + // The following formulas are used for decoding + // and encoding characters using surrogate pairs: + // + // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) + // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) + // W1 = 110110yyyyyyyyyy + // W2 = 110111xxxxxxxxxx + // + // where U is the character value, W1 is the high surrogate + // area, W2 is the low surrogate area. + + // Check for incomplete UTF-16 character. + if raw_unread < 2 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 character", + parser.offset, -1) + } + break inner + } + + // Get the character. + value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) + + // Check for unexpected low surrogate area. + if value&0xFC00 == 0xDC00 { + return yaml_parser_set_reader_error(parser, + "unexpected low surrogate area", + parser.offset, int(value)) + } + + // Check for a high surrogate area. + if value&0xFC00 == 0xD800 { + width = 4 + + // Check for incomplete surrogate pair. + if raw_unread < 4 { + if parser.eof { + return yaml_parser_set_reader_error(parser, + "incomplete UTF-16 surrogate pair", + parser.offset, -1) + } + break inner + } + + // Get the next character. + value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) + + // Check for a low surrogate area. + if value2&0xFC00 != 0xDC00 { + return yaml_parser_set_reader_error(parser, + "expected low surrogate area", + parser.offset+2, int(value2)) + } + + // Generate the value of the surrogate pair. + value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) + } else { + width = 2 + } + + default: + panic("impossible") + } + + // Check if the character is in the allowed range: + // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) + // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) + // | [#x10000-#x10FFFF] (32 bit) + switch { + case value == 0x09: + case value == 0x0A: + case value == 0x0D: + case value >= 0x20 && value <= 0x7E: + case value == 0x85: + case value >= 0xA0 && value <= 0xD7FF: + case value >= 0xE000 && value <= 0xFFFD: + case value >= 0x10000 && value <= 0x10FFFF: + default: + return yaml_parser_set_reader_error(parser, + "control characters are not allowed", + parser.offset, int(value)) + } + + // Move the raw pointers. + parser.raw_buffer_pos += width + parser.offset += width + + // Finally put the character into the buffer. + if value <= 0x7F { + // 0000 0000-0000 007F . 0xxxxxxx + parser.buffer[buffer_len+0] = byte(value) + buffer_len += 1 + } else if value <= 0x7FF { + // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) + parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) + buffer_len += 2 + } else if value <= 0xFFFF { + // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) + buffer_len += 3 + } else { + // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) + parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) + parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) + parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) + buffer_len += 4 + } + + parser.unread++ + } + + // On EOF, put NUL into the buffer and return. + if parser.eof { + parser.buffer[buffer_len] = 0 + buffer_len++ + parser.unread++ + break + } + } + // [Go] Read the documentation of this function above. To return true, + // we need to have the given length in the buffer. Not doing that means + // every single check that calls this function to make sure the buffer + // has a given length is Go) panicking; or C) accessing invalid memory. + // This happens here due to the EOF above breaking early. + for buffer_len < length { + parser.buffer[buffer_len] = 0 + buffer_len++ + } + parser.buffer = parser.buffer[:buffer_len] + return true +} diff --git a/vendor/gopkg.in/yaml.v2/resolve.go b/vendor/gopkg.in/yaml.v2/resolve.go new file mode 100644 index 0000000000..6c151db6fb --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/resolve.go @@ -0,0 +1,258 @@ +package yaml + +import ( + "encoding/base64" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +type resolveMapItem struct { + value interface{} + tag string +} + +var resolveTable = make([]byte, 256) +var resolveMap = make(map[string]resolveMapItem) + +func init() { + t := resolveTable + t[int('+')] = 'S' // Sign + t[int('-')] = 'S' + for _, c := range "0123456789" { + t[int(c)] = 'D' // Digit + } + for _, c := range "yYnNtTfFoO~" { + t[int(c)] = 'M' // In map + } + t[int('.')] = '.' // Float (potentially in map) + + var resolveMapList = []struct { + v interface{} + tag string + l []string + }{ + {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, + {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, + {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, + {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, + {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, + {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, + {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, + {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, + {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, + {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, + {"<<", yaml_MERGE_TAG, []string{"<<"}}, + } + + m := resolveMap + for _, item := range resolveMapList { + for _, s := range item.l { + m[s] = resolveMapItem{item.v, item.tag} + } + } +} + +const longTagPrefix = "tag:yaml.org,2002:" + +func shortTag(tag string) string { + // TODO This can easily be made faster and produce less garbage. + if strings.HasPrefix(tag, longTagPrefix) { + return "!!" + tag[len(longTagPrefix):] + } + return tag +} + +func longTag(tag string) string { + if strings.HasPrefix(tag, "!!") { + return longTagPrefix + tag[2:] + } + return tag +} + +func resolvableTag(tag string) bool { + switch tag { + case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: + return true + } + return false +} + +var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) + +func resolve(tag string, in string) (rtag string, out interface{}) { + if !resolvableTag(tag) { + return tag, in + } + + defer func() { + switch tag { + case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: + return + case yaml_FLOAT_TAG: + if rtag == yaml_INT_TAG { + switch v := out.(type) { + case int64: + rtag = yaml_FLOAT_TAG + out = float64(v) + return + case int: + rtag = yaml_FLOAT_TAG + out = float64(v) + return + } + } + } + failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) + }() + + // Any data is accepted as a !!str or !!binary. + // Otherwise, the prefix is enough of a hint about what it might be. + hint := byte('N') + if in != "" { + hint = resolveTable[in[0]] + } + if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { + // Handle things we can lookup in a map. + if item, ok := resolveMap[in]; ok { + return item.tag, item.value + } + + // Base 60 floats are a bad idea, were dropped in YAML 1.2, and + // are purposefully unsupported here. They're still quoted on + // the way out for compatibility with other parser, though. + + switch hint { + case 'M': + // We've already checked the map above. + + case '.': + // Not in the map, so maybe a normal float. + floatv, err := strconv.ParseFloat(in, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + + case 'D', 'S': + // Int, float, or timestamp. + // Only try values as a timestamp if the value is unquoted or there's an explicit + // !!timestamp tag. + if tag == "" || tag == yaml_TIMESTAMP_TAG { + t, ok := parseTimestamp(in) + if ok { + return yaml_TIMESTAMP_TAG, t + } + } + + plain := strings.Replace(in, "_", "", -1) + intv, err := strconv.ParseInt(plain, 0, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain, 0, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + if yamlStyleFloat.MatchString(plain) { + floatv, err := strconv.ParseFloat(plain, 64) + if err == nil { + return yaml_FLOAT_TAG, floatv + } + } + if strings.HasPrefix(plain, "0b") { + intv, err := strconv.ParseInt(plain[2:], 2, 64) + if err == nil { + if intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + uintv, err := strconv.ParseUint(plain[2:], 2, 64) + if err == nil { + return yaml_INT_TAG, uintv + } + } else if strings.HasPrefix(plain, "-0b") { + intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) + if err == nil { + if true || intv == int64(int(intv)) { + return yaml_INT_TAG, int(intv) + } else { + return yaml_INT_TAG, intv + } + } + } + default: + panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") + } + } + return yaml_STR_TAG, in +} + +// encodeBase64 encodes s as base64 that is broken up into multiple lines +// as appropriate for the resulting length. +func encodeBase64(s string) string { + const lineLen = 70 + encLen := base64.StdEncoding.EncodedLen(len(s)) + lines := encLen/lineLen + 1 + buf := make([]byte, encLen*2+lines) + in := buf[0:encLen] + out := buf[encLen:] + base64.StdEncoding.Encode(in, []byte(s)) + k := 0 + for i := 0; i < len(in); i += lineLen { + j := i + lineLen + if j > len(in) { + j = len(in) + } + k += copy(out[k:], in[i:j]) + if lines > 1 { + out[k] = '\n' + k++ + } + } + return string(out[:k]) +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only + // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" + // from the set of examples. +} + +// parseTimestamp parses s as a timestamp string and +// returns the timestamp and reports whether it succeeded. +// Timestamp formats are defined at http://yaml.org/type/timestamp.html +func parseTimestamp(s string) (time.Time, bool) { + // TODO write code to check all the formats supported by + // http://yaml.org/type/timestamp.html instead of using time.Parse. + + // Quick check: all date formats start with YYYY-. + i := 0 + for ; i < len(s); i++ { + if c := s[i]; c < '0' || c > '9' { + break + } + } + if i != 4 || i == len(s) || s[i] != '-' { + return time.Time{}, false + } + for _, format := range allowedTimestampFormats { + if t, err := time.Parse(format, s); err == nil { + return t, true + } + } + return time.Time{}, false +} diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go new file mode 100644 index 0000000000..077fd1dd2d --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/scannerc.go @@ -0,0 +1,2696 @@ +package yaml + +import ( + "bytes" + "fmt" +) + +// Introduction +// ************ +// +// The following notes assume that you are familiar with the YAML specification +// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in +// some cases we are less restrictive that it requires. +// +// The process of transforming a YAML stream into a sequence of events is +// divided on two steps: Scanning and Parsing. +// +// The Scanner transforms the input stream into a sequence of tokens, while the +// parser transform the sequence of tokens produced by the Scanner into a +// sequence of parsing events. +// +// The Scanner is rather clever and complicated. The Parser, on the contrary, +// is a straightforward implementation of a recursive-descendant parser (or, +// LL(1) parser, as it is usually called). +// +// Actually there are two issues of Scanning that might be called "clever", the +// rest is quite straightforward. The issues are "block collection start" and +// "simple keys". Both issues are explained below in details. +// +// Here the Scanning step is explained and implemented. We start with the list +// of all the tokens produced by the Scanner together with short descriptions. +// +// Now, tokens: +// +// STREAM-START(encoding) # The stream start. +// STREAM-END # The stream end. +// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. +// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. +// DOCUMENT-START # '---' +// DOCUMENT-END # '...' +// BLOCK-SEQUENCE-START # Indentation increase denoting a block +// BLOCK-MAPPING-START # sequence or a block mapping. +// BLOCK-END # Indentation decrease. +// FLOW-SEQUENCE-START # '[' +// FLOW-SEQUENCE-END # ']' +// BLOCK-SEQUENCE-START # '{' +// BLOCK-SEQUENCE-END # '}' +// BLOCK-ENTRY # '-' +// FLOW-ENTRY # ',' +// KEY # '?' or nothing (simple keys). +// VALUE # ':' +// ALIAS(anchor) # '*anchor' +// ANCHOR(anchor) # '&anchor' +// TAG(handle,suffix) # '!handle!suffix' +// SCALAR(value,style) # A scalar. +// +// The following two tokens are "virtual" tokens denoting the beginning and the +// end of the stream: +// +// STREAM-START(encoding) +// STREAM-END +// +// We pass the information about the input stream encoding with the +// STREAM-START token. +// +// The next two tokens are responsible for tags: +// +// VERSION-DIRECTIVE(major,minor) +// TAG-DIRECTIVE(handle,prefix) +// +// Example: +// +// %YAML 1.1 +// %TAG ! !foo +// %TAG !yaml! tag:yaml.org,2002: +// --- +// +// The correspoding sequence of tokens: +// +// STREAM-START(utf-8) +// VERSION-DIRECTIVE(1,1) +// TAG-DIRECTIVE("!","!foo") +// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") +// DOCUMENT-START +// STREAM-END +// +// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole +// line. +// +// The document start and end indicators are represented by: +// +// DOCUMENT-START +// DOCUMENT-END +// +// Note that if a YAML stream contains an implicit document (without '---' +// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be +// produced. +// +// In the following examples, we present whole documents together with the +// produced tokens. +// +// 1. An implicit document: +// +// 'a scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// STREAM-END +// +// 2. An explicit document: +// +// --- +// 'a scalar' +// ... +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// SCALAR("a scalar",single-quoted) +// DOCUMENT-END +// STREAM-END +// +// 3. Several documents in a stream: +// +// 'a scalar' +// --- +// 'another scalar' +// --- +// 'yet another scalar' +// +// Tokens: +// +// STREAM-START(utf-8) +// SCALAR("a scalar",single-quoted) +// DOCUMENT-START +// SCALAR("another scalar",single-quoted) +// DOCUMENT-START +// SCALAR("yet another scalar",single-quoted) +// STREAM-END +// +// We have already introduced the SCALAR token above. The following tokens are +// used to describe aliases, anchors, tag, and scalars: +// +// ALIAS(anchor) +// ANCHOR(anchor) +// TAG(handle,suffix) +// SCALAR(value,style) +// +// The following series of examples illustrate the usage of these tokens: +// +// 1. A recursive sequence: +// +// &A [ *A ] +// +// Tokens: +// +// STREAM-START(utf-8) +// ANCHOR("A") +// FLOW-SEQUENCE-START +// ALIAS("A") +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A tagged scalar: +// +// !!float "3.14" # A good approximation. +// +// Tokens: +// +// STREAM-START(utf-8) +// TAG("!!","float") +// SCALAR("3.14",double-quoted) +// STREAM-END +// +// 3. Various scalar styles: +// +// --- # Implicit empty plain scalars do not produce tokens. +// --- a plain scalar +// --- 'a single-quoted scalar' +// --- "a double-quoted scalar" +// --- |- +// a literal scalar +// --- >- +// a folded +// scalar +// +// Tokens: +// +// STREAM-START(utf-8) +// DOCUMENT-START +// DOCUMENT-START +// SCALAR("a plain scalar",plain) +// DOCUMENT-START +// SCALAR("a single-quoted scalar",single-quoted) +// DOCUMENT-START +// SCALAR("a double-quoted scalar",double-quoted) +// DOCUMENT-START +// SCALAR("a literal scalar",literal) +// DOCUMENT-START +// SCALAR("a folded scalar",folded) +// STREAM-END +// +// Now it's time to review collection-related tokens. We will start with +// flow collections: +// +// FLOW-SEQUENCE-START +// FLOW-SEQUENCE-END +// FLOW-MAPPING-START +// FLOW-MAPPING-END +// FLOW-ENTRY +// KEY +// VALUE +// +// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and +// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' +// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the +// indicators '?' and ':', which are used for denoting mapping keys and values, +// are represented by the KEY and VALUE tokens. +// +// The following examples show flow collections: +// +// 1. A flow sequence: +// +// [item 1, item 2, item 3] +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-SEQUENCE-START +// SCALAR("item 1",plain) +// FLOW-ENTRY +// SCALAR("item 2",plain) +// FLOW-ENTRY +// SCALAR("item 3",plain) +// FLOW-SEQUENCE-END +// STREAM-END +// +// 2. A flow mapping: +// +// { +// a simple key: a value, # Note that the KEY token is produced. +// ? a complex key: another value, +// } +// +// Tokens: +// +// STREAM-START(utf-8) +// FLOW-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// FLOW-ENTRY +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// FLOW-ENTRY +// FLOW-MAPPING-END +// STREAM-END +// +// A simple key is a key which is not denoted by the '?' indicator. Note that +// the Scanner still produce the KEY token whenever it encounters a simple key. +// +// For scanning block collections, the following tokens are used (note that we +// repeat KEY and VALUE here): +// +// BLOCK-SEQUENCE-START +// BLOCK-MAPPING-START +// BLOCK-END +// BLOCK-ENTRY +// KEY +// VALUE +// +// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation +// increase that precedes a block collection (cf. the INDENT token in Python). +// The token BLOCK-END denote indentation decrease that ends a block collection +// (cf. the DEDENT token in Python). However YAML has some syntax pecularities +// that makes detections of these tokens more complex. +// +// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators +// '-', '?', and ':' correspondingly. +// +// The following examples show how the tokens BLOCK-SEQUENCE-START, +// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: +// +// 1. Block sequences: +// +// - item 1 +// - item 2 +// - +// - item 3.1 +// - item 3.2 +// - +// key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 3.1",plain) +// BLOCK-ENTRY +// SCALAR("item 3.2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Block mappings: +// +// a simple key: a value # The KEY token is produced here. +// ? a complex key +// : another value +// a mapping: +// key 1: value 1 +// key 2: value 2 +// a sequence: +// - item 1 +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a simple key",plain) +// VALUE +// SCALAR("a value",plain) +// KEY +// SCALAR("a complex key",plain) +// VALUE +// SCALAR("another value",plain) +// KEY +// SCALAR("a mapping",plain) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML does not always require to start a new block collection from a new +// line. If the current line contains only '-', '?', and ':' indicators, a new +// block collection may start at the current line. The following examples +// illustrate this case: +// +// 1. Collections in a sequence: +// +// - - item 1 +// - item 2 +// - key 1: value 1 +// key 2: value 2 +// - ? complex key +// : complex value +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-ENTRY +// BLOCK-MAPPING-START +// KEY +// SCALAR("complex key") +// VALUE +// SCALAR("complex value") +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// 2. Collections in a mapping: +// +// ? a sequence +// : - item 1 +// - item 2 +// ? a mapping +// : key 1: value 1 +// key 2: value 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("a sequence",plain) +// VALUE +// BLOCK-SEQUENCE-START +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// KEY +// SCALAR("a mapping",plain) +// VALUE +// BLOCK-MAPPING-START +// KEY +// SCALAR("key 1",plain) +// VALUE +// SCALAR("value 1",plain) +// KEY +// SCALAR("key 2",plain) +// VALUE +// SCALAR("value 2",plain) +// BLOCK-END +// BLOCK-END +// STREAM-END +// +// YAML also permits non-indented sequences if they are included into a block +// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: +// +// key: +// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. +// - item 2 +// +// Tokens: +// +// STREAM-START(utf-8) +// BLOCK-MAPPING-START +// KEY +// SCALAR("key",plain) +// VALUE +// BLOCK-ENTRY +// SCALAR("item 1",plain) +// BLOCK-ENTRY +// SCALAR("item 2",plain) +// BLOCK-END +// + +// Ensure that the buffer contains the required number of characters. +// Return true on success, false on failure (reader error or memory error). +func cache(parser *yaml_parser_t, length int) bool { + // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) + return parser.unread >= length || yaml_parser_update_buffer(parser, length) +} + +// Advance the buffer pointer. +func skip(parser *yaml_parser_t) { + parser.mark.index++ + parser.mark.column++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) +} + +func skip_line(parser *yaml_parser_t) { + if is_crlf(parser.buffer, parser.buffer_pos) { + parser.mark.index += 2 + parser.mark.column = 0 + parser.mark.line++ + parser.unread -= 2 + parser.buffer_pos += 2 + } else if is_break(parser.buffer, parser.buffer_pos) { + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) + } +} + +// Copy a character to a string buffer and advance pointers. +func read(parser *yaml_parser_t, s []byte) []byte { + w := width(parser.buffer[parser.buffer_pos]) + if w == 0 { + panic("invalid character sequence") + } + if len(s) == 0 { + s = make([]byte, 0, 32) + } + if w == 1 && len(s)+w <= cap(s) { + s = s[:len(s)+1] + s[len(s)-1] = parser.buffer[parser.buffer_pos] + parser.buffer_pos++ + } else { + s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) + parser.buffer_pos += w + } + parser.mark.index++ + parser.mark.column++ + parser.unread-- + return s +} + +// Copy a line break character to a string buffer and advance pointers. +func read_line(parser *yaml_parser_t, s []byte) []byte { + buf := parser.buffer + pos := parser.buffer_pos + switch { + case buf[pos] == '\r' && buf[pos+1] == '\n': + // CR LF . LF + s = append(s, '\n') + parser.buffer_pos += 2 + parser.mark.index++ + parser.unread-- + case buf[pos] == '\r' || buf[pos] == '\n': + // CR|LF . LF + s = append(s, '\n') + parser.buffer_pos += 1 + case buf[pos] == '\xC2' && buf[pos+1] == '\x85': + // NEL . LF + s = append(s, '\n') + parser.buffer_pos += 2 + case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): + // LS|PS . LS|PS + s = append(s, buf[parser.buffer_pos:pos+3]...) + parser.buffer_pos += 3 + default: + return s + } + parser.mark.index++ + parser.mark.column = 0 + parser.mark.line++ + parser.unread-- + return s +} + +// Get the next token. +func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { + // Erase the token object. + *token = yaml_token_t{} // [Go] Is this necessary? + + // No tokens after STREAM-END or error. + if parser.stream_end_produced || parser.error != yaml_NO_ERROR { + return true + } + + // Ensure that the tokens queue contains enough tokens. + if !parser.token_available { + if !yaml_parser_fetch_more_tokens(parser) { + return false + } + } + + // Fetch the next token from the queue. + *token = parser.tokens[parser.tokens_head] + parser.tokens_head++ + parser.tokens_parsed++ + parser.token_available = false + + if token.typ == yaml_STREAM_END_TOKEN { + parser.stream_end_produced = true + } + return true +} + +// Set the scanner error and return false. +func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { + parser.error = yaml_SCANNER_ERROR + parser.context = context + parser.context_mark = context_mark + parser.problem = problem + parser.problem_mark = parser.mark + return false +} + +func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { + context := "while parsing a tag" + if directive { + context = "while parsing a %TAG directive" + } + return yaml_parser_set_scanner_error(parser, context, context_mark, problem) +} + +func trace(args ...interface{}) func() { + pargs := append([]interface{}{"+++"}, args...) + fmt.Println(pargs...) + pargs = append([]interface{}{"---"}, args...) + return func() { fmt.Println(pargs...) } +} + +// Ensure that the tokens queue contains at least one token which can be +// returned to the Parser. +func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { + // While we need more tokens to fetch, do it. + for { + // Check if we really need to fetch more tokens. + need_more_tokens := false + + if parser.tokens_head == len(parser.tokens) { + // Queue is empty. + need_more_tokens = true + } else { + // Check if any potential simple key may occupy the head position. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + if simple_key.possible && simple_key.token_number == parser.tokens_parsed { + need_more_tokens = true + break + } + } + } + + // We are finished. + if !need_more_tokens { + break + } + // Fetch the next token. + if !yaml_parser_fetch_next_token(parser) { + return false + } + } + + parser.token_available = true + return true +} + +// The dispatcher for token fetchers. +func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { + // Ensure that the buffer is initialized. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we just started scanning. Fetch STREAM-START then. + if !parser.stream_start_produced { + return yaml_parser_fetch_stream_start(parser) + } + + // Eat whitespaces and comments until we reach the next token. + if !yaml_parser_scan_to_next_token(parser) { + return false + } + + // Remove obsolete potential simple keys. + if !yaml_parser_stale_simple_keys(parser) { + return false + } + + // Check the indentation level against the current column. + if !yaml_parser_unroll_indent(parser, parser.mark.column) { + return false + } + + // Ensure that the buffer contains at least 4 characters. 4 is the length + // of the longest indicators ('--- ' and '... '). + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + // Is it the end of the stream? + if is_z(parser.buffer, parser.buffer_pos) { + return yaml_parser_fetch_stream_end(parser) + } + + // Is it a directive? + if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { + return yaml_parser_fetch_directive(parser) + } + + buf := parser.buffer + pos := parser.buffer_pos + + // Is it the document start indicator? + if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) + } + + // Is it the document end indicator? + if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { + return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) + } + + // Is it the flow sequence start indicator? + if buf[pos] == '[' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) + } + + // Is it the flow mapping start indicator? + if parser.buffer[parser.buffer_pos] == '{' { + return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) + } + + // Is it the flow sequence end indicator? + if parser.buffer[parser.buffer_pos] == ']' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_SEQUENCE_END_TOKEN) + } + + // Is it the flow mapping end indicator? + if parser.buffer[parser.buffer_pos] == '}' { + return yaml_parser_fetch_flow_collection_end(parser, + yaml_FLOW_MAPPING_END_TOKEN) + } + + // Is it the flow entry indicator? + if parser.buffer[parser.buffer_pos] == ',' { + return yaml_parser_fetch_flow_entry(parser) + } + + // Is it the block entry indicator? + if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { + return yaml_parser_fetch_block_entry(parser) + } + + // Is it the key indicator? + if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_key(parser) + } + + // Is it the value indicator? + if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_value(parser) + } + + // Is it an alias? + if parser.buffer[parser.buffer_pos] == '*' { + return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) + } + + // Is it an anchor? + if parser.buffer[parser.buffer_pos] == '&' { + return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) + } + + // Is it a tag? + if parser.buffer[parser.buffer_pos] == '!' { + return yaml_parser_fetch_tag(parser) + } + + // Is it a literal scalar? + if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, true) + } + + // Is it a folded scalar? + if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { + return yaml_parser_fetch_block_scalar(parser, false) + } + + // Is it a single-quoted scalar? + if parser.buffer[parser.buffer_pos] == '\'' { + return yaml_parser_fetch_flow_scalar(parser, true) + } + + // Is it a double-quoted scalar? + if parser.buffer[parser.buffer_pos] == '"' { + return yaml_parser_fetch_flow_scalar(parser, false) + } + + // Is it a plain scalar? + // + // A plain scalar may start with any non-blank characters except + // + // '-', '?', ':', ',', '[', ']', '{', '}', + // '#', '&', '*', '!', '|', '>', '\'', '\"', + // '%', '@', '`'. + // + // In the block context (and, for the '-' indicator, in the flow context + // too), it may also start with the characters + // + // '-', '?', ':' + // + // if it is followed by a non-space character. + // + // The last rule is more restrictive than the specification requires. + // [Go] Make this logic more reasonable. + //switch parser.buffer[parser.buffer_pos] { + //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': + //} + if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || + parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || + parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || + (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level == 0 && + (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && + !is_blankz(parser.buffer, parser.buffer_pos+1)) { + return yaml_parser_fetch_plain_scalar(parser) + } + + // If we don't determine the token type so far, it is an error. + return yaml_parser_set_scanner_error(parser, + "while scanning for the next token", parser.mark, + "found character that cannot start any token") +} + +// Check the list of potential simple keys and remove the positions that +// cannot contain simple keys anymore. +func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { + // Check for a potential simple key for each flow level. + for i := range parser.simple_keys { + simple_key := &parser.simple_keys[i] + + // The specification requires that a simple key + // + // - is limited to a single line, + // - is shorter than 1024 characters. + if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { + + // Check if the potential simple key to be removed is required. + if simple_key.required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", simple_key.mark, + "could not find expected ':'") + } + simple_key.possible = false + } + } + return true +} + +// Check if a simple key may start at the current position and add it if +// needed. +func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { + // A simple key is required at the current position if the scanner is in + // the block context and the current column coincides with the indentation + // level. + + required := parser.flow_level == 0 && parser.indent == parser.mark.column + + // + // If the current position may start a simple key, save it. + // + if parser.simple_key_allowed { + simple_key := yaml_simple_key_t{ + possible: true, + required: required, + token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), + } + simple_key.mark = parser.mark + + if !yaml_parser_remove_simple_key(parser) { + return false + } + parser.simple_keys[len(parser.simple_keys)-1] = simple_key + } + return true +} + +// Remove a potential simple key at the current flow level. +func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { + i := len(parser.simple_keys) - 1 + if parser.simple_keys[i].possible { + // If the key is required, it is an error. + if parser.simple_keys[i].required { + return yaml_parser_set_scanner_error(parser, + "while scanning a simple key", parser.simple_keys[i].mark, + "could not find expected ':'") + } + } + // Remove the key from the stack. + parser.simple_keys[i].possible = false + return true +} + +// Increase the flow level and resize the simple key list if needed. +func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { + // Reset the simple key on the next level. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // Increase the flow level. + parser.flow_level++ + return true +} + +// Decrease the flow level. +func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { + if parser.flow_level > 0 { + parser.flow_level-- + parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] + } + return true +} + +// Push the current indentation level to the stack and set the new level +// the current column is greater than the indentation level. In this case, +// append or insert the specified token into the token queue. +func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + if parser.indent < column { + // Push the current indentation level to the stack and set the new + // indentation level. + parser.indents = append(parser.indents, parser.indent) + parser.indent = column + + // Create a token and insert it into the queue. + token := yaml_token_t{ + typ: typ, + start_mark: mark, + end_mark: mark, + } + if number > -1 { + number -= parser.tokens_parsed + } + yaml_insert_token(parser, number, &token) + } + return true +} + +// Pop indentation levels from the indents stack until the current level +// becomes less or equal to the column. For each indentation level, append +// the BLOCK-END token. +func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { + // In the flow context, do nothing. + if parser.flow_level > 0 { + return true + } + + // Loop through the indentation levels in the stack. + for parser.indent > column { + // Create a token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + + // Pop the indentation level. + parser.indent = parser.indents[len(parser.indents)-1] + parser.indents = parser.indents[:len(parser.indents)-1] + } + return true +} + +// Initialize the scanner and produce the STREAM-START token. +func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { + + // Set the initial indentation. + parser.indent = -1 + + // Initialize the simple key stack. + parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) + + // A simple key is allowed at the beginning of the stream. + parser.simple_key_allowed = true + + // We have started. + parser.stream_start_produced = true + + // Create the STREAM-START token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_START_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + encoding: parser.encoding, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the STREAM-END token and shut down the scanner. +func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { + + // Force new line. + if parser.mark.column != 0 { + parser.mark.column = 0 + parser.mark.line++ + } + + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the STREAM-END token and append it to the queue. + token := yaml_token_t{ + typ: yaml_STREAM_END_TOKEN, + start_mark: parser.mark, + end_mark: parser.mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. +func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. + token := yaml_token_t{} + if !yaml_parser_scan_directive(parser, &token) { + return false + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the DOCUMENT-START or DOCUMENT-END token. +func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset the indentation level. + if !yaml_parser_unroll_indent(parser, -1) { + return false + } + + // Reset simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + parser.simple_key_allowed = false + + // Consume the token. + start_mark := parser.mark + + skip(parser) + skip(parser) + skip(parser) + + end_mark := parser.mark + + // Create the DOCUMENT-START or DOCUMENT-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. +func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // The indicators '[' and '{' may start a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // Increase the flow level. + if !yaml_parser_increase_flow_level(parser) { + return false + } + + // A simple key may follow the indicators '[' and '{'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. +func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // Reset any potential simple key on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Decrease the flow level. + if !yaml_parser_decrease_flow_level(parser) { + return false + } + + // No simple keys after the indicators ']' and '}'. + parser.simple_key_allowed = false + + // Consume the token. + + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. + token := yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + } + // Append the token to the queue. + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the FLOW-ENTRY token. +func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after ','. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the FLOW-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_FLOW_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the BLOCK-ENTRY token. +func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { + // Check if the scanner is in the block context. + if parser.flow_level == 0 { + // Check if we are allowed to start a new entry. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "block sequence entries are not allowed in this context") + } + // Add the BLOCK-SEQUENCE-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { + return false + } + } else { + // It is an error for the '-' indicator to occur in the flow context, + // but we let the Parser detect and report about it because the Parser + // is able to point to the context. + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '-'. + parser.simple_key_allowed = true + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the BLOCK-ENTRY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_BLOCK_ENTRY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the KEY token. +func yaml_parser_fetch_key(parser *yaml_parser_t) bool { + + // In the block context, additional checks are required. + if parser.flow_level == 0 { + // Check if we are allowed to start a new key (not nessesary simple). + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping keys are not allowed in this context") + } + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Reset any potential simple keys on the current flow level. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // Simple keys are allowed after '?' in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the KEY token and append it to the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the VALUE token. +func yaml_parser_fetch_value(parser *yaml_parser_t) bool { + + simple_key := &parser.simple_keys[len(parser.simple_keys)-1] + + // Have we found a simple key? + if simple_key.possible { + // Create the KEY token and insert it into the queue. + token := yaml_token_t{ + typ: yaml_KEY_TOKEN, + start_mark: simple_key.mark, + end_mark: simple_key.mark, + } + yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) + + // In the block context, we may need to add the BLOCK-MAPPING-START token. + if !yaml_parser_roll_indent(parser, simple_key.mark.column, + simple_key.token_number, + yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { + return false + } + + // Remove the simple key. + simple_key.possible = false + + // A simple key cannot follow another simple key. + parser.simple_key_allowed = false + + } else { + // The ':' indicator follows a complex key. + + // In the block context, extra checks are required. + if parser.flow_level == 0 { + + // Check if we are allowed to start a complex value. + if !parser.simple_key_allowed { + return yaml_parser_set_scanner_error(parser, "", parser.mark, + "mapping values are not allowed in this context") + } + + // Add the BLOCK-MAPPING-START token if needed. + if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { + return false + } + } + + // Simple keys after ':' are allowed in the block context. + parser.simple_key_allowed = parser.flow_level == 0 + } + + // Consume the token. + start_mark := parser.mark + skip(parser) + end_mark := parser.mark + + // Create the VALUE token and append it to the queue. + token := yaml_token_t{ + typ: yaml_VALUE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the ALIAS or ANCHOR token. +func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { + // An anchor or an alias could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow an anchor or an alias. + parser.simple_key_allowed = false + + // Create the ALIAS or ANCHOR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_anchor(parser, &token, typ) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the TAG token. +func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { + // A tag could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a tag. + parser.simple_key_allowed = false + + // Create the TAG token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_tag(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. +func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { + // Remove any potential simple keys. + if !yaml_parser_remove_simple_key(parser) { + return false + } + + // A simple key may follow a block scalar. + parser.simple_key_allowed = true + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_block_scalar(parser, &token, literal) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. +func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_flow_scalar(parser, &token, single) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Produce the SCALAR(...,plain) token. +func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { + // A plain scalar could be a simple key. + if !yaml_parser_save_simple_key(parser) { + return false + } + + // A simple key cannot follow a flow scalar. + parser.simple_key_allowed = false + + // Create the SCALAR token and append it to the queue. + var token yaml_token_t + if !yaml_parser_scan_plain_scalar(parser, &token) { + return false + } + yaml_insert_token(parser, -1, &token) + return true +} + +// Eat whitespaces and comments until the next token is found. +func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { + + // Until the next token is not found. + for { + // Allow the BOM mark to start a line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { + skip(parser) + } + + // Eat whitespaces. + // Tabs are allowed: + // - in the flow context + // - in the block context, but not at the beginning of the line or + // after '-', '?', or ':' (complex value). + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Eat a comment until a line break. + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // If it is a line break, eat it. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + + // In the block context, a new line may start a simple key. + if parser.flow_level == 0 { + parser.simple_key_allowed = true + } + } else { + break // We have found a token. + } + } + + return true +} + +// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { + // Eat '%'. + start_mark := parser.mark + skip(parser) + + // Scan the directive name. + var name []byte + if !yaml_parser_scan_directive_name(parser, start_mark, &name) { + return false + } + + // Is it a YAML directive? + if bytes.Equal(name, []byte("YAML")) { + // Scan the VERSION directive value. + var major, minor int8 + if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { + return false + } + end_mark := parser.mark + + // Create a VERSION-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_VERSION_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + major: major, + minor: minor, + } + + // Is it a TAG directive? + } else if bytes.Equal(name, []byte("TAG")) { + // Scan the TAG directive value. + var handle, prefix []byte + if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { + return false + } + end_mark := parser.mark + + // Create a TAG-DIRECTIVE token. + *token = yaml_token_t{ + typ: yaml_TAG_DIRECTIVE_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + prefix: prefix, + } + + // Unknown directive. + } else { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unknown directive name") + return false + } + + // Eat the rest of the line including any comments. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + return true +} + +// Scan the directive name. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^ +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^ +// +func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { + // Consume the directive name. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + var s []byte + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the name is empty. + if len(s) == 0 { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "could not find expected directive name") + return false + } + + // Check for an blank character after the name. + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a directive", + start_mark, "found unexpected non-alphabetical character") + return false + } + *name = s + return true +} + +// Scan the value of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^^^^^^ +func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the major version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { + return false + } + + // Eat '.'. + if parser.buffer[parser.buffer_pos] != '.' { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected digit or '.' character") + } + + skip(parser) + + // Consume the minor version number. + if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { + return false + } + return true +} + +const max_number_length = 2 + +// Scan the version number of VERSION-DIRECTIVE. +// +// Scope: +// %YAML 1.1 # a comment \n +// ^ +// %YAML 1.1 # a comment \n +// ^ +func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { + + // Repeat while the next character is digit. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var value, length int8 + for is_digit(parser.buffer, parser.buffer_pos) { + // Check if the number is too long. + length++ + if length > max_number_length { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "found extremely long version number") + } + value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the number was present. + if length == 0 { + return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", + start_mark, "did not find expected version number") + } + *number = value + return true +} + +// Scan the value of a TAG-DIRECTIVE token. +// +// Scope: +// %TAG !yaml! tag:yaml.org,2002: \n +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +// +func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { + var handle_value, prefix_value []byte + + // Eat whitespaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a handle. + if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { + return false + } + + // Expect a whitespace. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blank(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace") + return false + } + + // Eat whitespaces. + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Scan a prefix. + if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { + return false + } + + // Expect a whitespace or line break. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", + start_mark, "did not find expected whitespace or line break") + return false + } + + *handle = handle_value + *prefix = prefix_value + return true +} + +func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { + var s []byte + + // Eat the indicator character. + start_mark := parser.mark + skip(parser) + + // Consume the value. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + end_mark := parser.mark + + /* + * Check if length of the anchor is greater than 0 and it is followed by + * a whitespace character or one of the indicators: + * + * '?', ':', ',', ']', '}', '%', '@', '`'. + */ + + if len(s) == 0 || + !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || + parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '`') { + context := "while scanning an alias" + if typ == yaml_ANCHOR_TOKEN { + context = "while scanning an anchor" + } + yaml_parser_set_scanner_error(parser, context, start_mark, + "did not find expected alphabetic or numeric character") + return false + } + + // Create a token. + *token = yaml_token_t{ + typ: typ, + start_mark: start_mark, + end_mark: end_mark, + value: s, + } + + return true +} + +/* + * Scan a TAG token. + */ + +func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { + var handle, suffix []byte + + start_mark := parser.mark + + // Check if the tag is in the canonical form. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + if parser.buffer[parser.buffer_pos+1] == '<' { + // Keep the handle as '' + + // Eat '!<' + skip(parser) + skip(parser) + + // Consume the tag value. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + + // Check for '>' and eat it. + if parser.buffer[parser.buffer_pos] != '>' { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find the expected '>'") + return false + } + + skip(parser) + } else { + // The tag has either the '!suffix' or the '!handle!suffix' form. + + // First, try to scan a handle. + if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { + return false + } + + // Check if it is, indeed, handle. + if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { + // Scan the suffix now. + if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { + return false + } + } else { + // It wasn't a handle after all. Scan the rest of the tag. + if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { + return false + } + + // Set the handle to '!'. + handle = []byte{'!'} + + // A special case: the '!' tag. Set the handle to '' and the + // suffix to '!'. + if len(suffix) == 0 { + handle, suffix = suffix, handle + } + } + } + + // Check the character which ends the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if !is_blankz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a tag", + start_mark, "did not find expected whitespace or line break") + return false + } + + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_TAG_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: handle, + suffix: suffix, + } + return true +} + +// Scan a tag handle. +func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { + // Check the initial '!' character. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] != '!' { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + + var s []byte + + // Copy the '!' character. + s = read(parser, s) + + // Copy all subsequent alphabetical and numerical characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_alpha(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check if the trailing character is '!' and copy it. + if parser.buffer[parser.buffer_pos] == '!' { + s = read(parser, s) + } else { + // It's either the '!' tag or not really a tag handle. If it's a %TAG + // directive, it's an error. If it's a tag token, it must be a part of URI. + if directive && string(s) != "!" { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected '!'") + return false + } + } + + *handle = s + return true +} + +// Scan a tag. +func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { + //size_t length = head ? strlen((char *)head) : 0 + var s []byte + hasTag := len(head) > 0 + + // Copy the head if needed. + // + // Note that we don't copy the leading '!' character. + if len(head) > 1 { + s = append(s, head[1:]...) + } + + // Scan the tag. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // The set of characters that may appear in URI is as follows: + // + // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', + // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', + // '%'. + // [Go] Convert this into more reasonable logic. + for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || + parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || + parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || + parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || + parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || + parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || + parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || + parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || + parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || + parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || + parser.buffer[parser.buffer_pos] == '%' { + // Check if it is a URI-escape sequence. + if parser.buffer[parser.buffer_pos] == '%' { + if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { + return false + } + } else { + s = read(parser, s) + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + hasTag = true + } + + if !hasTag { + yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find expected tag URI") + return false + } + *uri = s + return true +} + +// Decode an URI-escape sequence corresponding to a single UTF-8 character. +func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { + + // Decode the required number of characters. + w := 1024 + for w > 0 { + // Check for a URI-escaped octet. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + + if !(parser.buffer[parser.buffer_pos] == '%' && + is_hex(parser.buffer, parser.buffer_pos+1) && + is_hex(parser.buffer, parser.buffer_pos+2)) { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "did not find URI escaped octet") + } + + // Get the octet. + octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) + + // If it is the leading octet, determine the length of the UTF-8 sequence. + if w == 1024 { + w = width(octet) + if w == 0 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect leading UTF-8 octet") + } + } else { + // Check if the trailing octet is correct. + if octet&0xC0 != 0x80 { + return yaml_parser_set_scanner_tag_error(parser, directive, + start_mark, "found an incorrect trailing UTF-8 octet") + } + } + + // Copy the octet and move the pointers. + *s = append(*s, octet) + skip(parser) + skip(parser) + skip(parser) + w-- + } + return true +} + +// Scan a block scalar. +func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { + // Eat the indicator '|' or '>'. + start_mark := parser.mark + skip(parser) + + // Scan the additional block scalar indicators. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check for a chomping indicator. + var chomping, increment int + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + // Set the chomping method and eat the indicator. + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + + // Check for an indentation indicator. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if is_digit(parser.buffer, parser.buffer_pos) { + // Check that the indentation is greater than 0. + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + + // Get the indentation level and eat the indicator. + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + } + + } else if is_digit(parser.buffer, parser.buffer_pos) { + // Do the same as above, but in the opposite order. + + if parser.buffer[parser.buffer_pos] == '0' { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found an indentation indicator equal to 0") + return false + } + increment = as_digit(parser.buffer, parser.buffer_pos) + skip(parser) + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { + if parser.buffer[parser.buffer_pos] == '+' { + chomping = +1 + } else { + chomping = -1 + } + skip(parser) + } + } + + // Eat whitespaces and comments to the end of the line. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for is_blank(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.buffer[parser.buffer_pos] == '#' { + for !is_breakz(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + } + + // Check if we are at the end of the line. + if !is_breakz(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "did not find expected comment or line break") + return false + } + + // Eat a line break. + if is_break(parser.buffer, parser.buffer_pos) { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + skip_line(parser) + } + + end_mark := parser.mark + + // Set the indentation level if it was specified. + var indent int + if increment > 0 { + if parser.indent >= 0 { + indent = parser.indent + increment + } else { + indent = increment + } + } + + // Scan the leading line breaks and determine the indentation level if needed. + var s, leading_break, trailing_breaks []byte + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + + // Scan the block scalar content. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + var leading_blank, trailing_blank bool + for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { + // We are at the beginning of a non-empty line. + + // Is it a trailing whitespace? + trailing_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Check if we need to fold the leading line break. + if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { + // Do we need to join the lines by space? + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } + } else { + s = append(s, leading_break...) + } + leading_break = leading_break[:0] + + // Append the remaining line breaks. + s = append(s, trailing_breaks...) + trailing_breaks = trailing_breaks[:0] + + // Is it a leading whitespace? + leading_blank = is_blank(parser.buffer, parser.buffer_pos) + + // Consume the current line. + for !is_breakz(parser.buffer, parser.buffer_pos) { + s = read(parser, s) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + leading_break = read_line(parser, leading_break) + + // Eat the following indentation spaces and line breaks. + if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { + return false + } + } + + // Chomp the tail. + if chomping != -1 { + s = append(s, leading_break...) + } + if chomping == 1 { + s = append(s, trailing_breaks...) + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_LITERAL_SCALAR_STYLE, + } + if !literal { + token.style = yaml_FOLDED_SCALAR_STYLE + } + return true +} + +// Scan indentation spaces and line breaks for a block scalar. Determine the +// indentation level if needed. +func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { + *end_mark = parser.mark + + // Eat the indentation spaces and line breaks. + max_indent := 0 + for { + // Eat the indentation spaces. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { + skip(parser) + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + if parser.mark.column > max_indent { + max_indent = parser.mark.column + } + + // Check for a tab character messing the indentation. + if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { + return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", + start_mark, "found a tab character where an indentation space is expected") + } + + // Have we found a non-empty line? + if !is_break(parser.buffer, parser.buffer_pos) { + break + } + + // Consume the line break. + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + // [Go] Should really be returning breaks instead. + *breaks = read_line(parser, *breaks) + *end_mark = parser.mark + } + + // Determine the indentation level if needed. + if *indent == 0 { + *indent = max_indent + if *indent < parser.indent+1 { + *indent = parser.indent + 1 + } + if *indent < 1 { + *indent = 1 + } + } + return true +} + +// Scan a quoted scalar. +func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { + // Eat the left quote. + start_mark := parser.mark + skip(parser) + + // Consume the content of the quoted scalar. + var s, leading_break, trailing_breaks, whitespaces []byte + for { + // Check that there are no document indicators at the beginning of the line. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected document indicator") + return false + } + + // Check for EOF. + if is_z(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", + start_mark, "found unexpected end of stream") + return false + } + + // Consume non-blank characters. + leading_blanks := false + for !is_blankz(parser.buffer, parser.buffer_pos) { + if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { + // Is is an escaped single quote. + s = append(s, '\'') + skip(parser) + skip(parser) + + } else if single && parser.buffer[parser.buffer_pos] == '\'' { + // It is a right single quote. + break + } else if !single && parser.buffer[parser.buffer_pos] == '"' { + // It is a right double quote. + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { + // It is an escaped line break. + if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { + return false + } + skip(parser) + skip_line(parser) + leading_blanks = true + break + + } else if !single && parser.buffer[parser.buffer_pos] == '\\' { + // It is an escape sequence. + code_length := 0 + + // Check the escape character. + switch parser.buffer[parser.buffer_pos+1] { + case '0': + s = append(s, 0) + case 'a': + s = append(s, '\x07') + case 'b': + s = append(s, '\x08') + case 't', '\t': + s = append(s, '\x09') + case 'n': + s = append(s, '\x0A') + case 'v': + s = append(s, '\x0B') + case 'f': + s = append(s, '\x0C') + case 'r': + s = append(s, '\x0D') + case 'e': + s = append(s, '\x1B') + case ' ': + s = append(s, '\x20') + case '"': + s = append(s, '"') + case '\'': + s = append(s, '\'') + case '\\': + s = append(s, '\\') + case 'N': // NEL (#x85) + s = append(s, '\xC2') + s = append(s, '\x85') + case '_': // #xA0 + s = append(s, '\xC2') + s = append(s, '\xA0') + case 'L': // LS (#x2028) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA8') + case 'P': // PS (#x2029) + s = append(s, '\xE2') + s = append(s, '\x80') + s = append(s, '\xA9') + case 'x': + code_length = 2 + case 'u': + code_length = 4 + case 'U': + code_length = 8 + default: + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found unknown escape character") + return false + } + + skip(parser) + skip(parser) + + // Consume an arbitrary escape code. + if code_length > 0 { + var value int + + // Scan the character value. + if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { + return false + } + for k := 0; k < code_length; k++ { + if !is_hex(parser.buffer, parser.buffer_pos+k) { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "did not find expected hexdecimal number") + return false + } + value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) + } + + // Check the value and write the character. + if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { + yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", + start_mark, "found invalid Unicode character escape code") + return false + } + if value <= 0x7F { + s = append(s, byte(value)) + } else if value <= 0x7FF { + s = append(s, byte(0xC0+(value>>6))) + s = append(s, byte(0x80+(value&0x3F))) + } else if value <= 0xFFFF { + s = append(s, byte(0xE0+(value>>12))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } else { + s = append(s, byte(0xF0+(value>>18))) + s = append(s, byte(0x80+((value>>12)&0x3F))) + s = append(s, byte(0x80+((value>>6)&0x3F))) + s = append(s, byte(0x80+(value&0x3F))) + } + + // Advance the pointer. + for k := 0; k < code_length; k++ { + skip(parser) + } + } + } else { + // It is a non-escaped non-blank character. + s = read(parser, s) + } + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + // Check if we are at the end of the scalar. + if single { + if parser.buffer[parser.buffer_pos] == '\'' { + break + } + } else { + if parser.buffer[parser.buffer_pos] == '"' { + break + } + } + + // Consume blank characters. + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Join the whitespaces or fold line breaks. + if leading_blanks { + // Do we need to fold line breaks? + if len(leading_break) > 0 && leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Eat the right quote. + skip(parser) + end_mark := parser.mark + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_SINGLE_QUOTED_SCALAR_STYLE, + } + if !single { + token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE + } + return true +} + +// Scan a plain scalar. +func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { + + var s, leading_break, trailing_breaks, whitespaces []byte + var leading_blanks bool + var indent = parser.indent + 1 + + start_mark := parser.mark + end_mark := parser.mark + + // Consume the content of the plain scalar. + for { + // Check for a document indicator. + if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { + return false + } + if parser.mark.column == 0 && + ((parser.buffer[parser.buffer_pos+0] == '-' && + parser.buffer[parser.buffer_pos+1] == '-' && + parser.buffer[parser.buffer_pos+2] == '-') || + (parser.buffer[parser.buffer_pos+0] == '.' && + parser.buffer[parser.buffer_pos+1] == '.' && + parser.buffer[parser.buffer_pos+2] == '.')) && + is_blankz(parser.buffer, parser.buffer_pos+3) { + break + } + + // Check for a comment. + if parser.buffer[parser.buffer_pos] == '#' { + break + } + + // Consume non-blank characters. + for !is_blankz(parser.buffer, parser.buffer_pos) { + + // Check for indicators that may end a plain scalar. + if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || + (parser.flow_level > 0 && + (parser.buffer[parser.buffer_pos] == ',' || + parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || + parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || + parser.buffer[parser.buffer_pos] == '}')) { + break + } + + // Check if we need to join whitespaces and breaks. + if leading_blanks || len(whitespaces) > 0 { + if leading_blanks { + // Do we need to fold line breaks? + if leading_break[0] == '\n' { + if len(trailing_breaks) == 0 { + s = append(s, ' ') + } else { + s = append(s, trailing_breaks...) + } + } else { + s = append(s, leading_break...) + s = append(s, trailing_breaks...) + } + trailing_breaks = trailing_breaks[:0] + leading_break = leading_break[:0] + leading_blanks = false + } else { + s = append(s, whitespaces...) + whitespaces = whitespaces[:0] + } + } + + // Copy the character. + s = read(parser, s) + + end_mark = parser.mark + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + } + + // Is it the end? + if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { + break + } + + // Consume blank characters. + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + + for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { + if is_blank(parser.buffer, parser.buffer_pos) { + + // Check for tab characters that abuse indentation. + if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { + yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", + start_mark, "found a tab character that violates indentation") + return false + } + + // Consume a space or a tab character. + if !leading_blanks { + whitespaces = read(parser, whitespaces) + } else { + skip(parser) + } + } else { + if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { + return false + } + + // Check if it is a first line break. + if !leading_blanks { + whitespaces = whitespaces[:0] + leading_break = read_line(parser, leading_break) + leading_blanks = true + } else { + trailing_breaks = read_line(parser, trailing_breaks) + } + } + if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { + return false + } + } + + // Check indentation level. + if parser.flow_level == 0 && parser.mark.column < indent { + break + } + } + + // Create a token. + *token = yaml_token_t{ + typ: yaml_SCALAR_TOKEN, + start_mark: start_mark, + end_mark: end_mark, + value: s, + style: yaml_PLAIN_SCALAR_STYLE, + } + + // Note that we change the 'simple_key_allowed' flag. + if leading_blanks { + parser.simple_key_allowed = true + } + return true +} diff --git a/vendor/gopkg.in/yaml.v2/sorter.go b/vendor/gopkg.in/yaml.v2/sorter.go new file mode 100644 index 0000000000..4c45e660a8 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/sorter.go @@ -0,0 +1,113 @@ +package yaml + +import ( + "reflect" + "unicode" +) + +type keyList []reflect.Value + +func (l keyList) Len() int { return len(l) } +func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l keyList) Less(i, j int) bool { + a := l[i] + b := l[j] + ak := a.Kind() + bk := b.Kind() + for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { + a = a.Elem() + ak = a.Kind() + } + for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { + b = b.Elem() + bk = b.Kind() + } + af, aok := keyFloat(a) + bf, bok := keyFloat(b) + if aok && bok { + if af != bf { + return af < bf + } + if ak != bk { + return ak < bk + } + return numLess(a, b) + } + if ak != reflect.String || bk != reflect.String { + return ak < bk + } + ar, br := []rune(a.String()), []rune(b.String()) + for i := 0; i < len(ar) && i < len(br); i++ { + if ar[i] == br[i] { + continue + } + al := unicode.IsLetter(ar[i]) + bl := unicode.IsLetter(br[i]) + if al && bl { + return ar[i] < br[i] + } + if al || bl { + return bl + } + var ai, bi int + var an, bn int64 + if ar[i] == '0' || br[i] == '0' { + for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { + if ar[j] != '0' { + an = 1 + bn = 1 + break + } + } + } + for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { + an = an*10 + int64(ar[ai]-'0') + } + for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { + bn = bn*10 + int64(br[bi]-'0') + } + if an != bn { + return an < bn + } + if ai != bi { + return ai < bi + } + return ar[i] < br[i] + } + return len(ar) < len(br) +} + +// keyFloat returns a float value for v if it is a number/bool +// and whether it is a number/bool or not. +func keyFloat(v reflect.Value) (f float64, ok bool) { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()), true + case reflect.Float32, reflect.Float64: + return v.Float(), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return float64(v.Uint()), true + case reflect.Bool: + if v.Bool() { + return 1, true + } + return 0, true + } + return 0, false +} + +// numLess returns whether a < b. +// a and b must necessarily have the same kind. +func numLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return a.Int() < b.Int() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Bool: + return !a.Bool() && b.Bool() + } + panic("not a number") +} diff --git a/vendor/gopkg.in/yaml.v2/writerc.go b/vendor/gopkg.in/yaml.v2/writerc.go new file mode 100644 index 0000000000..a2dde608cb --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/writerc.go @@ -0,0 +1,26 @@ +package yaml + +// Set the writer error and return false. +func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { + emitter.error = yaml_WRITER_ERROR + emitter.problem = problem + return false +} + +// Flush the output buffer. +func yaml_emitter_flush(emitter *yaml_emitter_t) bool { + if emitter.write_handler == nil { + panic("write handler not set") + } + + // Check if the buffer is empty. + if emitter.buffer_pos == 0 { + return true + } + + if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { + return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) + } + emitter.buffer_pos = 0 + return true +} diff --git a/vendor/gopkg.in/yaml.v2/yaml.go b/vendor/gopkg.in/yaml.v2/yaml.go new file mode 100644 index 0000000000..de85aa4cdb --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yaml.go @@ -0,0 +1,466 @@ +// Package yaml implements YAML support for the Go language. +// +// Source code and other details for the project are available at GitHub: +// +// https://github.com/go-yaml/yaml +// +package yaml + +import ( + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" +) + +// MapSlice encodes and decodes as a YAML map. +// The order of keys is preserved when encoding and decoding. +type MapSlice []MapItem + +// MapItem is an item in a MapSlice. +type MapItem struct { + Key, Value interface{} +} + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. The UnmarshalYAML +// method receives a function that may be called to unmarshal the original +// YAML value into a field or variable. It is safe to call the unmarshal +// function parameter more than once if necessary. +type Unmarshaler interface { + UnmarshalYAML(unmarshal func(interface{}) error) error +} + +// The Marshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type Marshaler interface { + MarshalYAML() (interface{}, error) +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Maps and pointers (to a struct, string, int, etc) are accepted as out +// values. If an internal pointer within a struct is not initialized, +// the yaml package will initialize it if necessary for unmarshalling +// the provided data. The out parameter must not be nil. +// +// The type of the decoded values should be compatible with the respective +// values in out. If one or more values cannot be decoded due to a type +// mismatches, decoding continues partially until the end of the YAML +// content, and a *yaml.TypeError is returned with details for all +// missed values. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(in []byte, out interface{}) (err error) { + return unmarshal(in, out, false) +} + +// UnmarshalStrict is like Unmarshal except that any fields that are found +// in the data that do not have corresponding struct members, or mapping +// keys that are duplicates, will result in +// an error. +func UnmarshalStrict(in []byte, out interface{}) (err error) { + return unmarshal(in, out, true) +} + +// A Decorder reads and decodes YAML values from an input stream. +type Decoder struct { + strict bool + parser *parser +} + +// NewDecoder returns a new decoder that reads from r. +// +// The decoder introduces its own buffering and may read +// data from r beyond the YAML values requested. +func NewDecoder(r io.Reader) *Decoder { + return &Decoder{ + parser: newParserFromReader(r), + } +} + +// SetStrict sets whether strict decoding behaviour is enabled when +// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict. +func (dec *Decoder) SetStrict(strict bool) { + dec.strict = strict +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + d := newDecoder(dec.strict) + defer handleErr(&err) + node := dec.parser.parse() + if node == nil { + return io.EOF + } + out := reflect.ValueOf(v) + if out.Kind() == reflect.Ptr && !out.IsNil() { + out = out.Elem() + } + d.unmarshal(node, out) + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +func unmarshal(in []byte, out interface{}, strict bool) (err error) { + defer handleErr(&err) + d := newDecoder(strict) + p := newParser(in) + defer p.destroy() + node := p.parse() + if node != nil { + v := reflect.ValueOf(out) + if v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + d.unmarshal(node, v) + } + if len(d.terrors) > 0 { + return &TypeError{d.terrors} + } + return nil +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be included if that method returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" +// +func Marshal(in interface{}) (out []byte, err error) { + defer handleErr(&err) + e := newEncoder() + defer e.destroy() + e.marshalDoc("", reflect.ValueOf(in)) + e.finish() + out = e.out + return +} + +// An Encoder writes YAML values to an output stream. +type Encoder struct { + encoder *encoder +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data +// to w. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + encoder: newEncoderWithWriter(w), + } +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, the +// second and subsequent document will be preceded +// with a "---" document separator, but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go +// values to YAML. +func (e *Encoder) Encode(v interface{}) (err error) { + defer handleErr(&err) + e.encoder.marshalDoc("", reflect.ValueOf(v)) + return nil +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() (err error) { + defer handleErr(&err) + e.encoder.finish() + return nil +} + +func handleErr(err *error) { + if v := recover(); v != nil { + if e, ok := v.(yamlError); ok { + *err = e.err + } else { + panic(v) + } + } +} + +type yamlError struct { + err error +} + +func fail(err error) { + panic(yamlError{err}) +} + +func failf(format string, args ...interface{}) { + panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) +} + +// A TypeError is returned by Unmarshal when one or more fields in +// the YAML document cannot be properly decoded into the requested +// types. When this error is returned, the value is still +// unmarshaled partially. +type TypeError struct { + Errors []string +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +// The code in this section was copied from mgo/bson. + +// structInfo holds details for the serialization of fields of +// a given struct. +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + + // InlineMap is the number of the field in the struct that + // contains an ,inline map, or -1 if there's none. + InlineMap int +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + Flow bool + // Id holds the unique field identifier, so we can cheaply + // check for field duplicates without maintaining an extra map. + Id int + + // Inline holds the field index if the field is part of an inlined struct. + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var fieldMapMutex sync.RWMutex + +func getStructInfo(st reflect.Type) (*structInfo, error) { + fieldMapMutex.RLock() + sinfo, found := structMap[st] + fieldMapMutex.RUnlock() + if found { + return sinfo, nil + } + + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("yaml") + if tag == "" && strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "flow": + info.Flow = true + case "inline": + inline = true + default: + return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("Multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Struct: + sinfo, err := getStructInfo(field.Type) + if err != nil { + return nil, err + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + finfo.Id = len(fieldsList) + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + default: + //return nil, errors.New("Option ,inline needs a struct value or map field") + return nil, errors.New("Option ,inline needs a struct value field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + info.Id = len(fieldsList) + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + + sinfo = &structInfo{ + FieldsMap: fieldsMap, + FieldsList: fieldsList, + InlineMap: inlineMap, + } + + fieldMapMutex.Lock() + structMap[st] = sinfo + fieldMapMutex.Unlock() + return sinfo, nil +} + +// IsZeroer is used to check whether an object is zero to +// determine whether it should be omitted when marshaling +// with the omitempty flag. One notable implementation +// is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func isZero(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go new file mode 100644 index 0000000000..e25cee563b --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yamlh.go @@ -0,0 +1,738 @@ +package yaml + +import ( + "fmt" + "io" +) + +// The version directive data. +type yaml_version_directive_t struct { + major int8 // The major version number. + minor int8 // The minor version number. +} + +// The tag directive data. +type yaml_tag_directive_t struct { + handle []byte // The tag handle. + prefix []byte // The tag prefix. +} + +type yaml_encoding_t int + +// The stream encoding. +const ( + // Let the parser choose the encoding. + yaml_ANY_ENCODING yaml_encoding_t = iota + + yaml_UTF8_ENCODING // The default UTF-8 encoding. + yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. + yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. +) + +type yaml_break_t int + +// Line break types. +const ( + // Let the parser choose the break type. + yaml_ANY_BREAK yaml_break_t = iota + + yaml_CR_BREAK // Use CR for line breaks (Mac style). + yaml_LN_BREAK // Use LN for line breaks (Unix style). + yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). +) + +type yaml_error_type_t int + +// Many bad things could happen with the parser and emitter. +const ( + // No error is produced. + yaml_NO_ERROR yaml_error_type_t = iota + + yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. + yaml_READER_ERROR // Cannot read or decode the input stream. + yaml_SCANNER_ERROR // Cannot scan the input stream. + yaml_PARSER_ERROR // Cannot parse the input stream. + yaml_COMPOSER_ERROR // Cannot compose a YAML document. + yaml_WRITER_ERROR // Cannot write to the output stream. + yaml_EMITTER_ERROR // Cannot emit a YAML stream. +) + +// The pointer position. +type yaml_mark_t struct { + index int // The position index. + line int // The position line. + column int // The position column. +} + +// Node Styles + +type yaml_style_t int8 + +type yaml_scalar_style_t yaml_style_t + +// Scalar styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota + + yaml_PLAIN_SCALAR_STYLE // The plain scalar style. + yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. + yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. + yaml_LITERAL_SCALAR_STYLE // The literal scalar style. + yaml_FOLDED_SCALAR_STYLE // The folded scalar style. +) + +type yaml_sequence_style_t yaml_style_t + +// Sequence styles. +const ( + // Let the emitter choose the style. + yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota + + yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. + yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. +) + +type yaml_mapping_style_t yaml_style_t + +// Mapping styles. +const ( + // Let the emitter choose the style. + yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota + + yaml_BLOCK_MAPPING_STYLE // The block mapping style. + yaml_FLOW_MAPPING_STYLE // The flow mapping style. +) + +// Tokens + +type yaml_token_type_t int + +// Token types. +const ( + // An empty token. + yaml_NO_TOKEN yaml_token_type_t = iota + + yaml_STREAM_START_TOKEN // A STREAM-START token. + yaml_STREAM_END_TOKEN // A STREAM-END token. + + yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. + yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. + yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. + yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. + + yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. + yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. + yaml_BLOCK_END_TOKEN // A BLOCK-END token. + + yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. + yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. + yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. + yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. + + yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. + yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. + yaml_KEY_TOKEN // A KEY token. + yaml_VALUE_TOKEN // A VALUE token. + + yaml_ALIAS_TOKEN // An ALIAS token. + yaml_ANCHOR_TOKEN // An ANCHOR token. + yaml_TAG_TOKEN // A TAG token. + yaml_SCALAR_TOKEN // A SCALAR token. +) + +func (tt yaml_token_type_t) String() string { + switch tt { + case yaml_NO_TOKEN: + return "yaml_NO_TOKEN" + case yaml_STREAM_START_TOKEN: + return "yaml_STREAM_START_TOKEN" + case yaml_STREAM_END_TOKEN: + return "yaml_STREAM_END_TOKEN" + case yaml_VERSION_DIRECTIVE_TOKEN: + return "yaml_VERSION_DIRECTIVE_TOKEN" + case yaml_TAG_DIRECTIVE_TOKEN: + return "yaml_TAG_DIRECTIVE_TOKEN" + case yaml_DOCUMENT_START_TOKEN: + return "yaml_DOCUMENT_START_TOKEN" + case yaml_DOCUMENT_END_TOKEN: + return "yaml_DOCUMENT_END_TOKEN" + case yaml_BLOCK_SEQUENCE_START_TOKEN: + return "yaml_BLOCK_SEQUENCE_START_TOKEN" + case yaml_BLOCK_MAPPING_START_TOKEN: + return "yaml_BLOCK_MAPPING_START_TOKEN" + case yaml_BLOCK_END_TOKEN: + return "yaml_BLOCK_END_TOKEN" + case yaml_FLOW_SEQUENCE_START_TOKEN: + return "yaml_FLOW_SEQUENCE_START_TOKEN" + case yaml_FLOW_SEQUENCE_END_TOKEN: + return "yaml_FLOW_SEQUENCE_END_TOKEN" + case yaml_FLOW_MAPPING_START_TOKEN: + return "yaml_FLOW_MAPPING_START_TOKEN" + case yaml_FLOW_MAPPING_END_TOKEN: + return "yaml_FLOW_MAPPING_END_TOKEN" + case yaml_BLOCK_ENTRY_TOKEN: + return "yaml_BLOCK_ENTRY_TOKEN" + case yaml_FLOW_ENTRY_TOKEN: + return "yaml_FLOW_ENTRY_TOKEN" + case yaml_KEY_TOKEN: + return "yaml_KEY_TOKEN" + case yaml_VALUE_TOKEN: + return "yaml_VALUE_TOKEN" + case yaml_ALIAS_TOKEN: + return "yaml_ALIAS_TOKEN" + case yaml_ANCHOR_TOKEN: + return "yaml_ANCHOR_TOKEN" + case yaml_TAG_TOKEN: + return "yaml_TAG_TOKEN" + case yaml_SCALAR_TOKEN: + return "yaml_SCALAR_TOKEN" + } + return "" +} + +// The token structure. +type yaml_token_t struct { + // The token type. + typ yaml_token_type_t + + // The start/end of the token. + start_mark, end_mark yaml_mark_t + + // The stream encoding (for yaml_STREAM_START_TOKEN). + encoding yaml_encoding_t + + // The alias/anchor/scalar value or tag/tag directive handle + // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). + value []byte + + // The tag suffix (for yaml_TAG_TOKEN). + suffix []byte + + // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). + prefix []byte + + // The scalar style (for yaml_SCALAR_TOKEN). + style yaml_scalar_style_t + + // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). + major, minor int8 +} + +// Events + +type yaml_event_type_t int8 + +// Event types. +const ( + // An empty event. + yaml_NO_EVENT yaml_event_type_t = iota + + yaml_STREAM_START_EVENT // A STREAM-START event. + yaml_STREAM_END_EVENT // A STREAM-END event. + yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. + yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. + yaml_ALIAS_EVENT // An ALIAS event. + yaml_SCALAR_EVENT // A SCALAR event. + yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. + yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. + yaml_MAPPING_START_EVENT // A MAPPING-START event. + yaml_MAPPING_END_EVENT // A MAPPING-END event. +) + +var eventStrings = []string{ + yaml_NO_EVENT: "none", + yaml_STREAM_START_EVENT: "stream start", + yaml_STREAM_END_EVENT: "stream end", + yaml_DOCUMENT_START_EVENT: "document start", + yaml_DOCUMENT_END_EVENT: "document end", + yaml_ALIAS_EVENT: "alias", + yaml_SCALAR_EVENT: "scalar", + yaml_SEQUENCE_START_EVENT: "sequence start", + yaml_SEQUENCE_END_EVENT: "sequence end", + yaml_MAPPING_START_EVENT: "mapping start", + yaml_MAPPING_END_EVENT: "mapping end", +} + +func (e yaml_event_type_t) String() string { + if e < 0 || int(e) >= len(eventStrings) { + return fmt.Sprintf("unknown event %d", e) + } + return eventStrings[e] +} + +// The event structure. +type yaml_event_t struct { + + // The event type. + typ yaml_event_type_t + + // The start and end of the event. + start_mark, end_mark yaml_mark_t + + // The document encoding (for yaml_STREAM_START_EVENT). + encoding yaml_encoding_t + + // The version directive (for yaml_DOCUMENT_START_EVENT). + version_directive *yaml_version_directive_t + + // The list of tag directives (for yaml_DOCUMENT_START_EVENT). + tag_directives []yaml_tag_directive_t + + // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). + anchor []byte + + // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + tag []byte + + // The scalar value (for yaml_SCALAR_EVENT). + value []byte + + // Is the document start/end indicator implicit, or the tag optional? + // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). + implicit bool + + // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). + quoted_implicit bool + + // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). + style yaml_style_t +} + +func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } +func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } +func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } + +// Nodes + +const ( + yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. + yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. + yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. + yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. + yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. + yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. + + yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. + yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. + + // Not in original libyaml. + yaml_BINARY_TAG = "tag:yaml.org,2002:binary" + yaml_MERGE_TAG = "tag:yaml.org,2002:merge" + + yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. + yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. + yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. +) + +type yaml_node_type_t int + +// Node types. +const ( + // An empty node. + yaml_NO_NODE yaml_node_type_t = iota + + yaml_SCALAR_NODE // A scalar node. + yaml_SEQUENCE_NODE // A sequence node. + yaml_MAPPING_NODE // A mapping node. +) + +// An element of a sequence node. +type yaml_node_item_t int + +// An element of a mapping node. +type yaml_node_pair_t struct { + key int // The key of the element. + value int // The value of the element. +} + +// The node structure. +type yaml_node_t struct { + typ yaml_node_type_t // The node type. + tag []byte // The node tag. + + // The node data. + + // The scalar parameters (for yaml_SCALAR_NODE). + scalar struct { + value []byte // The scalar value. + length int // The length of the scalar value. + style yaml_scalar_style_t // The scalar style. + } + + // The sequence parameters (for YAML_SEQUENCE_NODE). + sequence struct { + items_data []yaml_node_item_t // The stack of sequence items. + style yaml_sequence_style_t // The sequence style. + } + + // The mapping parameters (for yaml_MAPPING_NODE). + mapping struct { + pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). + pairs_start *yaml_node_pair_t // The beginning of the stack. + pairs_end *yaml_node_pair_t // The end of the stack. + pairs_top *yaml_node_pair_t // The top of the stack. + style yaml_mapping_style_t // The mapping style. + } + + start_mark yaml_mark_t // The beginning of the node. + end_mark yaml_mark_t // The end of the node. + +} + +// The document structure. +type yaml_document_t struct { + + // The document nodes. + nodes []yaml_node_t + + // The version directive. + version_directive *yaml_version_directive_t + + // The list of tag directives. + tag_directives_data []yaml_tag_directive_t + tag_directives_start int // The beginning of the tag directives list. + tag_directives_end int // The end of the tag directives list. + + start_implicit int // Is the document start indicator implicit? + end_implicit int // Is the document end indicator implicit? + + // The start/end of the document. + start_mark, end_mark yaml_mark_t +} + +// The prototype of a read handler. +// +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. +// +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. +// +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. +type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) + +// This structure holds information about a potential simple key. +type yaml_simple_key_t struct { + possible bool // Is a simple key possible? + required bool // Is a simple key required? + token_number int // The number of the token. + mark yaml_mark_t // The position mark. +} + +// The states of the parser. +type yaml_parser_state_t int + +const ( + yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota + + yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. + yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. + yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. + yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. + yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. + yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. + yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. + yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. + yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. + yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. + yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. + yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. + yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. + yaml_PARSE_END_STATE // Expect nothing. +) + +func (ps yaml_parser_state_t) String() string { + switch ps { + case yaml_PARSE_STREAM_START_STATE: + return "yaml_PARSE_STREAM_START_STATE" + case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: + return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_START_STATE: + return "yaml_PARSE_DOCUMENT_START_STATE" + case yaml_PARSE_DOCUMENT_CONTENT_STATE: + return "yaml_PARSE_DOCUMENT_CONTENT_STATE" + case yaml_PARSE_DOCUMENT_END_STATE: + return "yaml_PARSE_DOCUMENT_END_STATE" + case yaml_PARSE_BLOCK_NODE_STATE: + return "yaml_PARSE_BLOCK_NODE_STATE" + case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: + return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" + case yaml_PARSE_FLOW_NODE_STATE: + return "yaml_PARSE_FLOW_NODE_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: + return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" + case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: + return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: + return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" + case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_KEY_STATE: + return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" + case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" + case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: + return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" + case yaml_PARSE_END_STATE: + return "yaml_PARSE_END_STATE" + } + return "" +} + +// This structure holds aliases data. +type yaml_alias_data_t struct { + anchor []byte // The anchor. + index int // The node id. + mark yaml_mark_t // The anchor mark. +} + +// The parser structure. +// +// All members are internal. Manage the structure using the +// yaml_parser_ family of functions. +type yaml_parser_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + + problem string // Error description. + + // The byte about which the problem occurred. + problem_offset int + problem_value int + problem_mark yaml_mark_t + + // The error context. + context string + context_mark yaml_mark_t + + // Reader stuff + + read_handler yaml_read_handler_t // Read handler. + + input_reader io.Reader // File input data. + input []byte // String input data. + input_pos int + + eof bool // EOF flag + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + unread int // The number of unread characters in the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The input encoding. + + offset int // The offset of the current position (in bytes). + mark yaml_mark_t // The mark of the current position. + + // Scanner stuff + + stream_start_produced bool // Have we started to scan the input stream? + stream_end_produced bool // Have we reached the end of the input stream? + + flow_level int // The number of unclosed '[' and '{' indicators. + + tokens []yaml_token_t // The tokens queue. + tokens_head int // The head of the tokens queue. + tokens_parsed int // The number of tokens fetched from the queue. + token_available bool // Does the tokens queue contain a token ready for dequeueing. + + indent int // The current indentation level. + indents []int // The indentation levels stack. + + simple_key_allowed bool // May a simple key occur at the current position? + simple_keys []yaml_simple_key_t // The stack of simple keys. + + // Parser stuff + + state yaml_parser_state_t // The current parser state. + states []yaml_parser_state_t // The parser states stack. + marks []yaml_mark_t // The stack of marks. + tag_directives []yaml_tag_directive_t // The list of TAG directives. + + // Dumper stuff + + aliases []yaml_alias_data_t // The alias data. + + document *yaml_document_t // The currently parsed document. +} + +// Emitter Definitions + +// The prototype of a write handler. +// +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. +// +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. +// +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. +// +type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error + +type yaml_emitter_state_t int + +// The emitter states. +const ( + // Expect STREAM-START. + yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota + + yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. + yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. + yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. + yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. + yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. + yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. + yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. + yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. + yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. + yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. + yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. + yaml_EMIT_END_STATE // Expect nothing. +) + +// The emitter structure. +// +// All members are internal. Manage the structure using the @c yaml_emitter_ +// family of functions. +type yaml_emitter_t struct { + + // Error handling + + error yaml_error_type_t // Error type. + problem string // Error description. + + // Writer stuff + + write_handler yaml_write_handler_t // Write handler. + + output_buffer *[]byte // String output data. + output_writer io.Writer // File output data. + + buffer []byte // The working buffer. + buffer_pos int // The current position of the buffer. + + raw_buffer []byte // The raw buffer. + raw_buffer_pos int // The current position of the buffer. + + encoding yaml_encoding_t // The stream encoding. + + // Emitter stuff + + canonical bool // If the output is in the canonical style? + best_indent int // The number of indentation spaces. + best_width int // The preferred width of the output lines. + unicode bool // Allow unescaped non-ASCII characters? + line_break yaml_break_t // The preferred line break. + + state yaml_emitter_state_t // The current emitter state. + states []yaml_emitter_state_t // The stack of states. + + events []yaml_event_t // The event queue. + events_head int // The head of the event queue. + + indents []int // The stack of indentation levels. + + tag_directives []yaml_tag_directive_t // The list of tag directives. + + indent int // The current indentation level. + + flow_level int // The current flow level. + + root_context bool // Is it the document root context? + sequence_context bool // Is it a sequence context? + mapping_context bool // Is it a mapping context? + simple_key_context bool // Is it a simple mapping key context? + + line int // The current line. + column int // The current column. + whitespace bool // If the last character was a whitespace? + indention bool // If the last character was an indentation character (' ', '-', '?', ':')? + open_ended bool // If an explicit document end is required? + + // Anchor analysis. + anchor_data struct { + anchor []byte // The anchor value. + alias bool // Is it an alias? + } + + // Tag analysis. + tag_data struct { + handle []byte // The tag handle. + suffix []byte // The tag suffix. + } + + // Scalar analysis. + scalar_data struct { + value []byte // The scalar value. + multiline bool // Does the scalar contain line breaks? + flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? + block_plain_allowed bool // Can the scalar be expressed in the block plain style? + single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? + block_allowed bool // Can the scalar be expressed in the literal or folded styles? + style yaml_scalar_style_t // The output style. + } + + // Dumper stuff + + opened bool // If the stream was already opened? + closed bool // If the stream was already closed? + + // The information associated with the document nodes. + anchors *struct { + references int // The number of references. + anchor int // The anchor id. + serialized bool // If the node has been emitted? + } + + last_anchor_id int // The last assigned anchor id. + + document *yaml_document_t // The currently emitted document. +} diff --git a/vendor/gopkg.in/yaml.v2/yamlprivateh.go b/vendor/gopkg.in/yaml.v2/yamlprivateh.go new file mode 100644 index 0000000000..8110ce3c37 --- /dev/null +++ b/vendor/gopkg.in/yaml.v2/yamlprivateh.go @@ -0,0 +1,173 @@ +package yaml + +const ( + // The size of the input raw buffer. + input_raw_buffer_size = 512 + + // The size of the input buffer. + // It should be possible to decode the whole raw buffer. + input_buffer_size = input_raw_buffer_size * 3 + + // The size of the output buffer. + output_buffer_size = 128 + + // The size of the output raw buffer. + // It should be possible to encode the whole output buffer. + output_raw_buffer_size = (output_buffer_size*2 + 2) + + // The size of other stacks and queues. + initial_stack_size = 16 + initial_queue_size = 16 + initial_string_size = 16 +) + +// Check if the character at the specified position is an alphabetical +// character, a digit, '_', or '-'. +func is_alpha(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' +} + +// Check if the character at the specified position is a digit. +func is_digit(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' +} + +// Get the value of a digit. +func as_digit(b []byte, i int) int { + return int(b[i]) - '0' +} + +// Check if the character at the specified position is a hex-digit. +func is_hex(b []byte, i int) bool { + return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' +} + +// Get the value of a hex-digit. +func as_hex(b []byte, i int) int { + bi := b[i] + if bi >= 'A' && bi <= 'F' { + return int(bi) - 'A' + 10 + } + if bi >= 'a' && bi <= 'f' { + return int(bi) - 'a' + 10 + } + return int(bi) - '0' +} + +// Check if the character is ASCII. +func is_ascii(b []byte, i int) bool { + return b[i] <= 0x7F +} + +// Check if the character at the start of the buffer can be printed unescaped. +func is_printable(b []byte, i int) bool { + return ((b[i] == 0x0A) || // . == #x0A + (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E + (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF + (b[i] > 0xC2 && b[i] < 0xED) || + (b[i] == 0xED && b[i+1] < 0xA0) || + (b[i] == 0xEE) || + (b[i] == 0xEF && // #xE000 <= . <= #xFFFD + !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF + !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) +} + +// Check if the character at the specified position is NUL. +func is_z(b []byte, i int) bool { + return b[i] == 0x00 +} + +// Check if the beginning of the buffer is a BOM. +func is_bom(b []byte, i int) bool { + return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF +} + +// Check if the character at the specified position is space. +func is_space(b []byte, i int) bool { + return b[i] == ' ' +} + +// Check if the character at the specified position is tab. +func is_tab(b []byte, i int) bool { + return b[i] == '\t' +} + +// Check if the character at the specified position is blank (space or tab). +func is_blank(b []byte, i int) bool { + //return is_space(b, i) || is_tab(b, i) + return b[i] == ' ' || b[i] == '\t' +} + +// Check if the character at the specified position is a line break. +func is_break(b []byte, i int) bool { + return (b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) +} + +func is_crlf(b []byte, i int) bool { + return b[i] == '\r' && b[i+1] == '\n' +} + +// Check if the character is a line break or NUL. +func is_breakz(b []byte, i int) bool { + //return is_break(b, i) || is_z(b, i) + return ( // is_break: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + // is_z: + b[i] == 0) +} + +// Check if the character is a line break, space, or NUL. +func is_spacez(b []byte, i int) bool { + //return is_space(b, i) || is_breakz(b, i) + return ( // is_space: + b[i] == ' ' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Check if the character is a line break, space, tab, or NUL. +func is_blankz(b []byte, i int) bool { + //return is_blank(b, i) || is_breakz(b, i) + return ( // is_blank: + b[i] == ' ' || b[i] == '\t' || + // is_breakz: + b[i] == '\r' || // CR (#xD) + b[i] == '\n' || // LF (#xA) + b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) + b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) + b[i] == 0) +} + +// Determine the width of the character. +func width(b byte) int { + // Don't replace these by a switch without first + // confirming that it is being inlined. + if b&0x80 == 0x00 { + return 1 + } + if b&0xE0 == 0xC0 { + return 2 + } + if b&0xF0 == 0xE0 { + return 3 + } + if b&0xF8 == 0xF0 { + return 4 + } + return 0 + +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 416ae83eba..48dd325b3a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -33,6 +33,8 @@ github.com/mdlayher/netlink/nlenc # github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee github.com/mdlayher/wifi github.com/mdlayher/wifi/internal/nl80211 +# github.com/pkg/errors v0.8.1 +github.com/pkg/errors # github.com/prometheus/client_golang v1.0.0 github.com/prometheus/client_golang/prometheus github.com/prometheus/client_golang/prometheus/internal @@ -77,3 +79,5 @@ golang.org/x/sys/windows/registry golang.org/x/sys/windows/svc/eventlog # gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/alecthomas/kingpin.v2 +# gopkg.in/yaml.v2 v2.2.2 +gopkg.in/yaml.v2 From 8a4c9c154b3c03ecc6578ae561eac9a5055b19b1 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Sat, 16 Nov 2019 18:32:15 +0100 Subject: [PATCH 08/35] Bump procfs to v0.0.7 (#1538) Update Prometheus procfs library to the latest release. Signed-off-by: Ben Kochie --- go.mod | 3 +- go.sum | 10 +- .../prometheus/procfs/CONTRIBUTING.md | 109 ++- vendor/github.com/prometheus/procfs/README.md | 16 +- .../github.com/prometheus/procfs/cpuinfo.go | 5 +- .../prometheus/procfs/fixtures.ttar | 749 ++++++++++++++++++ vendor/github.com/prometheus/procfs/go.mod | 6 +- vendor/github.com/prometheus/procfs/go.sum | 8 +- .../prometheus/procfs/internal/fs/fs.go | 2 +- .../procfs/internal/util/readfile.go | 38 + .../procfs/internal/util/sysreadfile.go | 5 +- vendor/github.com/prometheus/procfs/ipvs.go | 12 +- .../github.com/prometheus/procfs/meminfo.go | 453 +++++++++++ .../github.com/prometheus/procfs/mountinfo.go | 114 +-- .../github.com/prometheus/procfs/net_dev.go | 1 - vendor/github.com/prometheus/procfs/proc.go | 23 +- .../prometheus/procfs/proc_environ.go | 12 +- .../prometheus/procfs/proc_fdinfo.go | 17 +- .../github.com/prometheus/procfs/proc_io.go | 12 +- .../github.com/prometheus/procfs/proc_psi.go | 21 +- .../github.com/prometheus/procfs/proc_stat.go | 10 +- .../prometheus/procfs/proc_status.go | 12 +- vendor/github.com/prometheus/procfs/stat.go | 12 +- .../prometheus/procfs/sysfs/class_powercap.go | 112 +++ vendor/modules.txt | 4 +- 25 files changed, 1595 insertions(+), 171 deletions(-) create mode 100644 vendor/github.com/prometheus/procfs/internal/util/readfile.go create mode 100644 vendor/github.com/prometheus/procfs/meminfo.go create mode 100644 vendor/github.com/prometheus/procfs/sysfs/class_powercap.go diff --git a/go.mod b/go.mod index c1a58fb161..3fc762956d 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,11 @@ require ( github.com/prometheus/client_golang v1.0.0 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 github.com/prometheus/common v0.7.0 - github.com/prometheus/procfs v0.0.5 + github.com/prometheus/procfs v0.0.7 github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745 github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect - golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect golang.org/x/sys v0.0.0-20190902133755-9109b7679e13 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index 9d615c6fbe..c111807bf0 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,6 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/hodgesds/perf-utils v0.0.7 h1:V/5aRKeXn/membOpFdzAgd+fFvmtvTYD6moDuZ7K7SM= @@ -87,8 +85,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.7 h1:RS5GAlMbnkWkhs4+bPocMTmGjYkuCY5djjqEDdXOhcQ= +github.com/prometheus/procfs v0.0.7/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745 h1:IuH7WumZNax0D+rEqmy2TyhKCzrtMGqbZO0b8rO00JA= github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -117,8 +115,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTm golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/prometheus/procfs/CONTRIBUTING.md b/vendor/github.com/prometheus/procfs/CONTRIBUTING.md index 40503edbf1..943de7615e 100644 --- a/vendor/github.com/prometheus/procfs/CONTRIBUTING.md +++ b/vendor/github.com/prometheus/procfs/CONTRIBUTING.md @@ -2,17 +2,120 @@ Prometheus uses GitHub to manage reviews of pull requests. +* If you are a new contributor see: [Steps to Contribute](#steps-to-contribute) + * If you have a trivial fix or improvement, go ahead and create a pull request, - addressing (with `@...`) the maintainer of this repository (see + addressing (with `@...`) a suitable maintainer of this repository (see [MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request. * If you plan to do something more involved, first discuss your ideas on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers). This will avoid unnecessary work and surely give you and us a good deal - of inspiration. + of inspiration. Also please see our [non-goals issue](https://github.com/prometheus/docs/issues/149) on areas that the Prometheus community doesn't plan to work on. * Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production - Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). + Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style). + +* Be sure to sign off on the [DCO](https://github.com/probot/dco#how-it-works) + +## Steps to Contribute + +Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue. + +Please check the [`help-wanted`](https://github.com/prometheus/procfs/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) label to find issues that are good for getting started. If you have questions about one of the issues, with or without the tag, please comment on them and one of the maintainers will clarify it. For a quicker response, contact us over [IRC](https://prometheus.io/community). + +For quickly compiling and testing your changes do: +``` +make test # Make sure all the tests pass before you commit and push :) +``` + +We use [`golangci-lint`](https://github.com/golangci/golangci-lint) for linting the code. If it reports an issue and you think that the warning needs to be disregarded or is a false-positive, you can add a special comment `//nolint:linter1[,linter2,...]` before the offending line. Use this sparingly though, fixing the code to comply with the linter's recommendation is in general the preferred course of action. + +## Pull Request Checklist + +* Branch from the master branch and, if needed, rebase to the current master branch before submitting your pull request. If it doesn't merge cleanly with master you may be asked to rebase your changes. + +* Commits should be as small as possible, while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests). + +* If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment, or you can ask for a review on IRC channel [#prometheus](https://webchat.freenode.net/?channels=#prometheus) on irc.freenode.net (for the easiest start, [join via Riot](https://riot.im/app/#/room/#prometheus:matrix.org)). + +* Add tests relevant to the fixed bug or new feature. + +## Dependency management + +The Prometheus project uses [Go modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) to manage dependencies on external packages. This requires a working Go environment with version 1.12 or greater installed. + +All dependencies are vendored in the `vendor/` directory. + +To add or update a new dependency, use the `go get` command: + +```bash +# Pick the latest tagged release. +go get example.com/some/module/pkg + +# Pick a specific version. +go get example.com/some/module/pkg@vX.Y.Z +``` + +Tidy up the `go.mod` and `go.sum` files and copy the new/updated dependency to the `vendor/` directory: + + +```bash +# The GO111MODULE variable can be omitted when the code isn't located in GOPATH. +GO111MODULE=on go mod tidy + +GO111MODULE=on go mod vendor +``` + +You have to commit the changes to `go.mod`, `go.sum` and the `vendor/` directory before submitting the pull request. + + +## API Implementation Guidelines + +### Naming and Documentation + +Public functions and structs should normally be named according to the file(s) being read and parsed. For example, +the `fs.BuddyInfo()` function reads the file `/proc/buddyinfo`. In addition, the godoc for each public function +should contain the path to the file(s) being read and a URL of the linux kernel documentation describing the file(s). + +### Reading vs. Parsing + +Most functionality in this library consists of reading files and then parsing the text into structured data. In most +cases reading and parsing should be separated into different functions/methods with a public `fs.Thing()` method and +a private `parseThing(r Reader)` function. This provides a logical separation and allows parsing to be tested +directly without the need to read from the filesystem. Using a `Reader` argument is preferred over other data types +such as `string` or `*File` because it provides the most flexibility regarding the data source. When a set of files +in a directory needs to be parsed, then a `path` string parameter to the parse function can be used instead. + +### /proc and /sys filesystem I/O + +The `proc` and `sys` filesystems are pseudo file systems and work a bit differently from standard disk I/O. +Many of the files are changing continuously and the data being read can in some cases change between subsequent +reads in the same file. Also, most of the files are relatively small (less than a few KBs), and system calls +to the `stat` function will often return the wrong size. Therefore, for most files it's recommended to read the +full file in a single operation using an internal utility function called `util.ReadFileNoStat`. +This function is similar to `ioutil.ReadFile`, but it avoids the system call to `stat` to get the current size of +the file. + +Note that parsing the file's contents can still be performed one line at a time. This is done by first reading +the full file, and then using a scanner on the `[]byte` or `string` containing the data. + +``` + data, err := util.ReadFileNoStat("/proc/cpuinfo") + if err != nil { + return err + } + reader := bytes.NewReader(data) + scanner := bufio.NewScanner(reader) +``` + +The `/sys` filesystem contains many very small files which contain only a single numeric or text value. These files +can be read using an internal function called `util.SysReadFile` which is similar to `ioutil.ReadFile` but does +not bother to check the size of the file before reading. +``` + data, err := util.SysReadFile("/sys/class/power_supply/BAT0/capacity") +``` + diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md index 6f8850feb6..55d1e3261c 100644 --- a/vendor/github.com/prometheus/procfs/README.md +++ b/vendor/github.com/prometheus/procfs/README.md @@ -1,6 +1,6 @@ # procfs -This procfs package provides functions to retrieve system, kernel and process +This package provides functions to retrieve system, kernel, and process metrics from the pseudo-filesystems /proc and /sys. *WARNING*: This package is a work in progress. Its API may still break in @@ -13,7 +13,8 @@ backwards-incompatible ways without warnings. Use it at your own risk. ## Usage The procfs library is organized by packages based on whether the gathered data is coming from -/proc, /sys, or both. Each package contains an `FS` type which represents the path to either /proc, /sys, or both. For example, current cpu statistics are gathered from +/proc, /sys, or both. Each package contains an `FS` type which represents the path to either /proc, +/sys, or both. For example, cpu statistics are gathered from `/proc/stat` and are available via the root procfs package. First, the proc filesystem mount point is initialized, and then the stat information is read. @@ -29,10 +30,17 @@ Some sub-packages such as `blockdevice`, require access to both the proc and sys stats, err := fs.ProcDiskstats() ``` +## Package Organization + +The packages in this project are organized according to (1) whether the data comes from the `/proc` or +`/sys` filesystem and (2) the type of information being retrieved. For example, most process information +can be gathered from the functions in the root `procfs` package. Information about block devices such as disk drives +is available in the `blockdevices` sub-package. + ## Building and Testing -The procfs library is normally built as part of another application. However, when making -changes to the library, the `make test` command can be used to run the API test suite. +The procfs library is intended to be built as part of another application, so there are no distributable binaries. +However, most of the API includes unit tests which can be run with `make test`. ### Updating Test Fixtures diff --git a/vendor/github.com/prometheus/procfs/cpuinfo.go b/vendor/github.com/prometheus/procfs/cpuinfo.go index 16491d6abb..2e02215528 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo.go @@ -16,9 +16,10 @@ package procfs import ( "bufio" "bytes" - "io/ioutil" "strconv" "strings" + + "github.com/prometheus/procfs/internal/util" ) // CPUInfo contains general information about a system CPU found in /proc/cpuinfo @@ -54,7 +55,7 @@ type CPUInfo struct { // CPUInfo returns information about current system CPUs. // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt func (fs FS) CPUInfo() ([]CPUInfo, error) { - data, err := ioutil.ReadFile(fs.proc.Path("cpuinfo")) + data, err := util.ReadFileNoStat(fs.proc.Path("cpuinfo")) if err != nil { return nil, err } diff --git a/vendor/github.com/prometheus/procfs/fixtures.ttar b/vendor/github.com/prometheus/procfs/fixtures.ttar index 0b29055447..38b71fe324 100644 --- a/vendor/github.com/prometheus/procfs/fixtures.ttar +++ b/vendor/github.com/prometheus/procfs/fixtures.ttar @@ -1674,6 +1674,52 @@ md101 : active (read-only) raid0 sdb[2] sdd[1] sdc[0] unused devices: Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/meminfo +Lines: 42 +MemTotal: 15666184 kB +MemFree: 440324 kB +Buffers: 1020128 kB +Cached: 12007640 kB +SwapCached: 0 kB +Active: 6761276 kB +Inactive: 6532708 kB +Active(anon): 267256 kB +Inactive(anon): 268 kB +Active(file): 6494020 kB +Inactive(file): 6532440 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 0 kB +SwapFree: 0 kB +Dirty: 768 kB +Writeback: 0 kB +AnonPages: 266216 kB +Mapped: 44204 kB +Shmem: 1308 kB +Slab: 1807264 kB +SReclaimable: 1738124 kB +SUnreclaim: 69140 kB +KernelStack: 1616 kB +PageTables: 5288 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 7833092 kB +Committed_AS: 530844 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 36596 kB +VmallocChunk: 34359637840 kB +HardwareCorrupted: 0 kB +AnonHugePages: 12288 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 91136 kB +DirectMap2M: 16039936 kB +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/proc/net Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2776,6 +2822,134 @@ SymlinkTo: ../../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:00/PNP0C09:00 Path: fixtures/sys/class/power_supply/BAT0 SymlinkTo: ../../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:00/PNP0C09:00/PNP0C0A:00/power_supply/BAT0 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/powercap +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/powercap/intel-rapl +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl/enabled +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl/uevent +Lines: 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/powercap/intel-rapl:0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_0_max_power_uw +Lines: 1 +95000000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_0_name +Lines: 1 +long_term +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw +Lines: 1 +4090000000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_0_time_window_us +Lines: 1 +999424 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_1_max_power_uw +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_1_name +Lines: 1 +short_term +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_1_power_limit_uw +Lines: 1 +4090000000 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/constraint_1_time_window_us +Lines: 1 +2440 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/enabled +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/energy_uj +Lines: 1 +240422366267 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/max_energy_range_uj +Lines: 1 +262143328850 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/name +Lines: 1 +package-0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0/uevent +Lines: 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/powercap/intel-rapl:0:0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/constraint_0_max_power_uw +Lines: 0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/constraint_0_name +Lines: 1 +long_term +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/constraint_0_power_limit_uw +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/constraint_0_time_window_us +Lines: 1 +976 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/enabled +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/energy_uj +Lines: 1 +118821284256 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/max_energy_range_uj +Lines: 1 +262143328850 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/name +Lines: 1 +core +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/powercap/intel-rapl:0:0/uevent +Lines: 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/thermal Mode: 775 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4278,6 +4452,581 @@ Lines: 1 0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/bytes_may_use +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/bytes_readonly +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/bytes_reserved +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/bytes_used +Lines: 1 +808189952 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/disk_total +Lines: 1 +2147483648 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/disk_used +Lines: 1 +808189952 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/flags +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/raid0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/raid0/total_bytes +Lines: 1 +2147483648 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/raid0/used_bytes +Lines: 1 +808189952 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/total_bytes +Lines: 1 +2147483648 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/data/total_bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/global_rsv_reserved +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/global_rsv_size +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/bytes_may_use +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/bytes_readonly +Lines: 1 +131072 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/bytes_reserved +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/bytes_used +Lines: 1 +933888 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/disk_total +Lines: 1 +2147483648 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/disk_used +Lines: 1 +1867776 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/flags +Lines: 1 +4 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/raid1 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/raid1/total_bytes +Lines: 1 +1073741824 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/raid1/used_bytes +Lines: 1 +933888 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/total_bytes +Lines: 1 +1073741824 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/metadata/total_bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/bytes_may_use +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/bytes_readonly +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/bytes_reserved +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/bytes_used +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/disk_total +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/disk_used +Lines: 1 +32768 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/flags +Lines: 1 +2 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/raid1 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/raid1/total_bytes +Lines: 1 +8388608 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/raid1/used_bytes +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/total_bytes +Lines: 1 +8388608 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/allocation/system/total_bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/clone_alignment +Lines: 1 +4096 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/devices +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/devices/loop25 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/devices/loop25/size +Lines: 1 +20971520 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/devices/loop26 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/devices/loop26/size +Lines: 1 +20971520 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/features +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/features/big_metadata +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/features/extended_iref +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/features/mixed_backref +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/features/skinny_metadata +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/label +Lines: 1 +fixture +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/metadata_uuid +Lines: 1 +0abb23a9-579b-43e6-ad30-227ef47fcb9d +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/nodesize +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/quota_override +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/0abb23a9-579b-43e6-ad30-227ef47fcb9d/sectorsize +Lines: 1 +4096 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/bytes_may_use +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/bytes_readonly +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/bytes_reserved +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/bytes_used +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/disk_total +Lines: 1 +644087808 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/disk_used +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/flags +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/raid5 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/raid5/total_bytes +Lines: 1 +644087808 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/raid5/used_bytes +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/total_bytes +Lines: 1 +644087808 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/data/total_bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/global_rsv_reserved +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/global_rsv_size +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/bytes_may_use +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/bytes_readonly +Lines: 1 +262144 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/bytes_reserved +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/bytes_used +Lines: 1 +114688 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/disk_total +Lines: 1 +429391872 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/disk_used +Lines: 1 +114688 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/flags +Lines: 1 +4 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/raid6 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/raid6/total_bytes +Lines: 1 +429391872 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/raid6/used_bytes +Lines: 1 +114688 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/total_bytes +Lines: 1 +429391872 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/metadata/total_bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/bytes_may_use +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/bytes_readonly +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/bytes_reserved +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/bytes_used +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/disk_total +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/disk_used +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/flags +Lines: 1 +2 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/raid6 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/raid6/total_bytes +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/raid6/used_bytes +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/total_bytes +Lines: 1 +16777216 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/allocation/system/total_bytes_pinned +Lines: 1 +0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/clone_alignment +Lines: 1 +4096 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/devices +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/devices/loop22 +SymlinkTo: ../../../../devices/virtual/block/loop22 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/devices/loop23 +SymlinkTo: ../../../../devices/virtual/block/loop23 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/devices/loop24 +SymlinkTo: ../../../../devices/virtual/block/loop24 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/devices/loop25 +SymlinkTo: ../../../../devices/virtual/block/loop25 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/features +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/features/big_metadata +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/features/extended_iref +Lines: 1 +1 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/features/mixed_backref +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/features/raid56 +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/features/skinny_metadata +Lines: 1 +1 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/label +Lines: 0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/metadata_uuid +Lines: 1 +7f07c59f-6136-449c-ab87-e1cf2328731b +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/nodesize +Lines: 1 +16384 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/quota_override +Lines: 1 +0 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/fs/btrfs/7f07c59f-6136-449c-ab87-e1cf2328731b/sectorsize +Lines: 1 +4096 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/fs/xfs Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/github.com/prometheus/procfs/go.mod b/vendor/github.com/prometheus/procfs/go.mod index b2f8cca933..0e04e5d1fd 100644 --- a/vendor/github.com/prometheus/procfs/go.mod +++ b/vendor/github.com/prometheus/procfs/go.mod @@ -1,6 +1,8 @@ module github.com/prometheus/procfs +go 1.12 + require ( - github.com/google/go-cmp v0.3.0 - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 + github.com/google/go-cmp v0.3.1 + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e ) diff --git a/vendor/github.com/prometheus/procfs/go.sum b/vendor/github.com/prometheus/procfs/go.sum index db54133d7c..33b824b01b 100644 --- a/vendor/github.com/prometheus/procfs/go.sum +++ b/vendor/github.com/prometheus/procfs/go.sum @@ -1,4 +1,4 @@ -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/vendor/github.com/prometheus/procfs/internal/fs/fs.go b/vendor/github.com/prometheus/procfs/internal/fs/fs.go index 7ddfd6b6ed..565e89e42c 100644 --- a/vendor/github.com/prometheus/procfs/internal/fs/fs.go +++ b/vendor/github.com/prometheus/procfs/internal/fs/fs.go @@ -26,7 +26,7 @@ const ( // DefaultSysMountPoint is the common mount point of the sys filesystem. DefaultSysMountPoint = "/sys" - // DefaultConfigfsMountPoint is the commont mount point of the configfs + // DefaultConfigfsMountPoint is the common mount point of the configfs DefaultConfigfsMountPoint = "/sys/kernel/config" ) diff --git a/vendor/github.com/prometheus/procfs/internal/util/readfile.go b/vendor/github.com/prometheus/procfs/internal/util/readfile.go new file mode 100644 index 0000000000..8051161b2a --- /dev/null +++ b/vendor/github.com/prometheus/procfs/internal/util/readfile.go @@ -0,0 +1,38 @@ +// Copyright 2019 The Prometheus Authors +// 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 util + +import ( + "io" + "io/ioutil" + "os" +) + +// ReadFileNoStat uses ioutil.ReadAll to read contents of entire file. +// This is similar to ioutil.ReadFile but without the call to os.Stat, because +// many files in /proc and /sys report incorrect file sizes (either 0 or 4096). +// Reads a max file size of 512kB. For files larger than this, a scanner +// should be used. +func ReadFileNoStat(filename string) ([]byte, error) { + const maxBufferSize = 1024 * 512 + + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + + reader := io.LimitReader(f, maxBufferSize) + return ioutil.ReadAll(reader) +} diff --git a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go index 68b37c4b3c..c07de0b6c9 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go +++ b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go @@ -23,6 +23,8 @@ import ( // SysReadFile is a simplified ioutil.ReadFile that invokes syscall.Read directly. // https://github.com/prometheus/node_exporter/pull/728/files +// +// Note that this function will not read files larger than 128 bytes. func SysReadFile(file string) (string, error) { f, err := os.Open(file) if err != nil { @@ -35,7 +37,8 @@ func SysReadFile(file string) (string, error) { // // Since we either want to read data or bail immediately, do the simplest // possible read using syscall directly. - b := make([]byte, 128) + const sysFileBufferSize = 128 + b := make([]byte, sysFileBufferSize) n, err := syscall.Read(int(f.Fd()), b) if err != nil { return "", err diff --git a/vendor/github.com/prometheus/procfs/ipvs.go b/vendor/github.com/prometheus/procfs/ipvs.go index 2d6cb8d1c6..89e447746c 100644 --- a/vendor/github.com/prometheus/procfs/ipvs.go +++ b/vendor/github.com/prometheus/procfs/ipvs.go @@ -15,6 +15,7 @@ package procfs import ( "bufio" + "bytes" "encoding/hex" "errors" "fmt" @@ -24,6 +25,8 @@ import ( "os" "strconv" "strings" + + "github.com/prometheus/procfs/internal/util" ) // IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`. @@ -64,17 +67,16 @@ type IPVSBackendStatus struct { // IPVSStats reads the IPVS statistics from the specified `proc` filesystem. func (fs FS) IPVSStats() (IPVSStats, error) { - file, err := os.Open(fs.proc.Path("net/ip_vs_stats")) + data, err := util.ReadFileNoStat(fs.proc.Path("net/ip_vs_stats")) if err != nil { return IPVSStats{}, err } - defer file.Close() - return parseIPVSStats(file) + return parseIPVSStats(bytes.NewReader(data)) } // parseIPVSStats performs the actual parsing of `ip_vs_stats`. -func parseIPVSStats(file io.Reader) (IPVSStats, error) { +func parseIPVSStats(r io.Reader) (IPVSStats, error) { var ( statContent []byte statLines []string @@ -82,7 +84,7 @@ func parseIPVSStats(file io.Reader) (IPVSStats, error) { stats IPVSStats ) - statContent, err := ioutil.ReadAll(file) + statContent, err := ioutil.ReadAll(r) if err != nil { return IPVSStats{}, err } diff --git a/vendor/github.com/prometheus/procfs/meminfo.go b/vendor/github.com/prometheus/procfs/meminfo.go new file mode 100644 index 0000000000..dd630c0480 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/meminfo.go @@ -0,0 +1,453 @@ +// Copyright 2019 The Prometheus Authors +// 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 procfs + +import ( + "bufio" + "bytes" + "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// Meminfo represents memory statistics. +type Meminfo struct { + // Total usable ram (i.e. physical ram minus a few reserved + // bits and the kernel binary code) + MemTotal uint64 + // The sum of LowFree+HighFree + MemFree uint64 + // An estimate of how much memory is available for starting + // new applications, without swapping. Calculated from + // MemFree, SReclaimable, the size of the file LRU lists, and + // the low watermarks in each zone. The estimate takes into + // account that the system needs some page cache to function + // well, and that not all reclaimable slab will be + // reclaimable, due to items being in use. The impact of those + // factors will vary from system to system. + MemAvailable uint64 + // Relatively temporary storage for raw disk blocks shouldn't + // get tremendously large (20MB or so) + Buffers uint64 + Cached uint64 + // Memory that once was swapped out, is swapped back in but + // still also is in the swapfile (if memory is needed it + // doesn't need to be swapped out AGAIN because it is already + // in the swapfile. This saves I/O) + SwapCached uint64 + // Memory that has been used more recently and usually not + // reclaimed unless absolutely necessary. + Active uint64 + // Memory which has been less recently used. It is more + // eligible to be reclaimed for other purposes + Inactive uint64 + ActiveAnon uint64 + InactiveAnon uint64 + ActiveFile uint64 + InactiveFile uint64 + Unevictable uint64 + Mlocked uint64 + // total amount of swap space available + SwapTotal uint64 + // Memory which has been evicted from RAM, and is temporarily + // on the disk + SwapFree uint64 + // Memory which is waiting to get written back to the disk + Dirty uint64 + // Memory which is actively being written back to the disk + Writeback uint64 + // Non-file backed pages mapped into userspace page tables + AnonPages uint64 + // files which have been mapped, such as libraries + Mapped uint64 + Shmem uint64 + // in-kernel data structures cache + Slab uint64 + // Part of Slab, that might be reclaimed, such as caches + SReclaimable uint64 + // Part of Slab, that cannot be reclaimed on memory pressure + SUnreclaim uint64 + KernelStack uint64 + // amount of memory dedicated to the lowest level of page + // tables. + PageTables uint64 + // NFS pages sent to the server, but not yet committed to + // stable storage + NFSUnstable uint64 + // Memory used for block device "bounce buffers" + Bounce uint64 + // Memory used by FUSE for temporary writeback buffers + WritebackTmp uint64 + // Based on the overcommit ratio ('vm.overcommit_ratio'), + // this is the total amount of memory currently available to + // be allocated on the system. This limit is only adhered to + // if strict overcommit accounting is enabled (mode 2 in + // 'vm.overcommit_memory'). + // The CommitLimit is calculated with the following formula: + // CommitLimit = ([total RAM pages] - [total huge TLB pages]) * + // overcommit_ratio / 100 + [total swap pages] + // For example, on a system with 1G of physical RAM and 7G + // of swap with a `vm.overcommit_ratio` of 30 it would + // yield a CommitLimit of 7.3G. + // For more details, see the memory overcommit documentation + // in vm/overcommit-accounting. + CommitLimit uint64 + // The amount of memory presently allocated on the system. + // The committed memory is a sum of all of the memory which + // has been allocated by processes, even if it has not been + // "used" by them as of yet. A process which malloc()'s 1G + // of memory, but only touches 300M of it will show up as + // using 1G. This 1G is memory which has been "committed" to + // by the VM and can be used at any time by the allocating + // application. With strict overcommit enabled on the system + // (mode 2 in 'vm.overcommit_memory'),allocations which would + // exceed the CommitLimit (detailed above) will not be permitted. + // This is useful if one needs to guarantee that processes will + // not fail due to lack of memory once that memory has been + // successfully allocated. + CommittedAS uint64 + // total size of vmalloc memory area + VmallocTotal uint64 + // amount of vmalloc area which is used + VmallocUsed uint64 + // largest contiguous block of vmalloc area which is free + VmallocChunk uint64 + HardwareCorrupted uint64 + AnonHugePages uint64 + ShmemHugePages uint64 + ShmemPmdMapped uint64 + CmaTotal uint64 + CmaFree uint64 + HugePagesTotal uint64 + HugePagesFree uint64 + HugePagesRsvd uint64 + HugePagesSurp uint64 + Hugepagesize uint64 + DirectMap4k uint64 + DirectMap2M uint64 + DirectMap1G uint64 +} + +// Meminfo returns an information about current kernel/system memory statistics. +// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt +func (fs FS) Meminfo() (Meminfo, error) { + data, err := util.ReadFileNoStat(fs.proc.Path("meminfo")) + if err != nil { + return Meminfo{}, err + } + return parseMemInfo(data) +} + +func parseMemInfo(info []byte) (m Meminfo, err error) { + scanner := bufio.NewScanner(bytes.NewReader(info)) + + var line string + for scanner.Scan() { + line = scanner.Text() + + field := strings.Fields(line) + switch field[0] { + case "MemTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.MemTotal = v + case "MemFree:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.MemFree = v + case "MemAvailable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.MemAvailable = v + case "Buffers:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Buffers = v + case "Cached:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Cached = v + case "SwapCached:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SwapCached = v + case "Active:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Active = v + case "Inactive:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Inactive = v + case "Active(anon):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ActiveAnon = v + case "Inactive(anon):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.InactiveAnon = v + case "Active(file):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ActiveFile = v + case "Inactive(file):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.InactiveFile = v + case "Unevictable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Unevictable = v + case "Mlocked:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Mlocked = v + case "SwapTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SwapTotal = v + case "SwapFree:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SwapFree = v + case "Dirty:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Dirty = v + case "Writeback:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Writeback = v + case "AnonPages:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.AnonPages = v + case "Mapped:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Mapped = v + case "Shmem:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Shmem = v + case "Slab:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Slab = v + case "SReclaimable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SReclaimable = v + case "SUnreclaim:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SUnreclaim = v + case "KernelStack:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.KernelStack = v + case "PageTables:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.PageTables = v + case "NFS_Unstable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.NFSUnstable = v + case "Bounce:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Bounce = v + case "WritebackTmp:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.WritebackTmp = v + case "CommitLimit:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CommitLimit = v + case "Committed_AS:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CommittedAS = v + case "VmallocTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.VmallocTotal = v + case "VmallocUsed:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.VmallocUsed = v + case "VmallocChunk:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.VmallocChunk = v + case "HardwareCorrupted:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HardwareCorrupted = v + case "AnonHugePages:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.AnonHugePages = v + case "ShmemHugePages:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ShmemHugePages = v + case "ShmemPmdMapped:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ShmemPmdMapped = v + case "CmaTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CmaTotal = v + case "CmaFree:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CmaFree = v + case "HugePages_Total:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesTotal = v + case "HugePages_Free:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesFree = v + case "HugePages_Rsvd:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesRsvd = v + case "HugePages_Surp:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesSurp = v + case "Hugepagesize:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Hugepagesize = v + case "DirectMap4k:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.DirectMap4k = v + case "DirectMap2M:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.DirectMap2M = v + case "DirectMap1G:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.DirectMap1G = v + } + } + return m, nil +} diff --git a/vendor/github.com/prometheus/procfs/mountinfo.go b/vendor/github.com/prometheus/procfs/mountinfo.go index 61fa618874..bb01bb5a2a 100644 --- a/vendor/github.com/prometheus/procfs/mountinfo.go +++ b/vendor/github.com/prometheus/procfs/mountinfo.go @@ -15,19 +15,13 @@ package procfs import ( "bufio" + "bytes" "fmt" - "io" - "os" "strconv" "strings" -) -var validOptionalFields = map[string]bool{ - "shared": true, - "master": true, - "propagate_from": true, - "unbindable": true, -} + "github.com/prometheus/procfs/internal/util" +) // A MountInfo is a type that describes the details, options // for each mount, parsed from /proc/self/mountinfo. @@ -58,18 +52,10 @@ type MountInfo struct { SuperOptions map[string]string } -// Returns part of the mountinfo line, if it exists, else an empty string. -func getStringSliceElement(parts []string, idx int, defaultValue string) string { - if idx >= len(parts) { - return defaultValue - } - return parts[idx] -} - // Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs. -func parseMountInfo(r io.Reader) ([]*MountInfo, error) { +func parseMountInfo(info []byte) ([]*MountInfo, error) { mounts := []*MountInfo{} - scanner := bufio.NewScanner(r) + scanner := bufio.NewScanner(bytes.NewReader(info)) for scanner.Scan() { mountString := scanner.Text() parsedMounts, err := parseMountInfoString(mountString) @@ -89,57 +75,75 @@ func parseMountInfo(r io.Reader) ([]*MountInfo, error) { func parseMountInfoString(mountString string) (*MountInfo, error) { var err error - // OptionalFields can be zero, hence these checks to ensure we do not populate the wrong values in the wrong spots - separatorIndex := strings.Index(mountString, "-") - if separatorIndex == -1 { - return nil, fmt.Errorf("no separator found in mountinfo string: %s", mountString) + mountInfo := strings.Split(mountString, " ") + mountInfoLength := len(mountInfo) + if mountInfoLength < 11 { + return nil, fmt.Errorf("couldn't find enough fields in mount string: %s", mountString) } - beforeFields := strings.Fields(mountString[:separatorIndex]) - afterFields := strings.Fields(mountString[separatorIndex+1:]) - if (len(beforeFields) + len(afterFields)) < 7 { - return nil, fmt.Errorf("too few fields") + + if mountInfo[mountInfoLength-4] != "-" { + return nil, fmt.Errorf("couldn't find separator in expected field: %s", mountInfo[mountInfoLength-4]) } mount := &MountInfo{ - MajorMinorVer: getStringSliceElement(beforeFields, 2, ""), - Root: getStringSliceElement(beforeFields, 3, ""), - MountPoint: getStringSliceElement(beforeFields, 4, ""), - Options: mountOptionsParser(getStringSliceElement(beforeFields, 5, "")), + MajorMinorVer: mountInfo[2], + Root: mountInfo[3], + MountPoint: mountInfo[4], + Options: mountOptionsParser(mountInfo[5]), OptionalFields: nil, - FSType: getStringSliceElement(afterFields, 0, ""), - Source: getStringSliceElement(afterFields, 1, ""), - SuperOptions: mountOptionsParser(getStringSliceElement(afterFields, 2, "")), + FSType: mountInfo[mountInfoLength-3], + Source: mountInfo[mountInfoLength-2], + SuperOptions: mountOptionsParser(mountInfo[mountInfoLength-1]), } - mount.MountId, err = strconv.Atoi(getStringSliceElement(beforeFields, 0, "")) + mount.MountId, err = strconv.Atoi(mountInfo[0]) if err != nil { return nil, fmt.Errorf("failed to parse mount ID") } - mount.ParentId, err = strconv.Atoi(getStringSliceElement(beforeFields, 1, "")) + mount.ParentId, err = strconv.Atoi(mountInfo[1]) if err != nil { return nil, fmt.Errorf("failed to parse parent ID") } // Has optional fields, which is a space separated list of values. // Example: shared:2 master:7 - if len(beforeFields) > 6 { - mount.OptionalFields = make(map[string]string) - optionalFields := beforeFields[6:] - for _, field := range optionalFields { - optionSplit := strings.Split(field, ":") - target, value := optionSplit[0], "" - if len(optionSplit) == 2 { - value = optionSplit[1] - } - // Checks if the 'keys' in the optional fields in the mountinfo line are acceptable. - // Allowed 'keys' are shared, master, propagate_from, unbindable. - if _, ok := validOptionalFields[target]; ok { - mount.OptionalFields[target] = value - } + if mountInfo[6] != "" { + mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4]) + if err != nil { + return nil, err } } return mount, nil } +// mountOptionsIsValidField checks a string against a valid list of optional fields keys. +func mountOptionsIsValidField(s string) bool { + switch s { + case + "shared", + "master", + "propagate_from", + "unbindable": + return true + } + return false +} + +// mountOptionsParseOptionalFields parses a list of optional fields strings into a double map of strings. +func mountOptionsParseOptionalFields(o []string) (map[string]string, error) { + optionalFields := make(map[string]string) + for _, field := range o { + optionSplit := strings.SplitN(field, ":", 2) + value := "" + if len(optionSplit) == 2 { + value = optionSplit[1] + } + if mountOptionsIsValidField(optionSplit[0]) { + optionalFields[optionSplit[0]] = value + } + } + return optionalFields, nil +} + // Parses the mount options, superblock options. func mountOptionsParser(mountOptions string) map[string]string { opts := make(map[string]string) @@ -159,20 +163,18 @@ func mountOptionsParser(mountOptions string) map[string]string { // Retrieves mountinfo information from `/proc/self/mountinfo`. func GetMounts() ([]*MountInfo, error) { - f, err := os.Open("/proc/self/mountinfo") + data, err := util.ReadFileNoStat("/proc/self/mountinfo") if err != nil { return nil, err } - defer f.Close() - return parseMountInfo(f) + return parseMountInfo(data) } // Retrieves mountinfo information from a processes' `/proc//mountinfo`. func GetProcMounts(pid int) ([]*MountInfo, error) { - f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) + data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid)) if err != nil { return nil, err } - defer f.Close() - return parseMountInfo(f) + return parseMountInfo(data) } diff --git a/vendor/github.com/prometheus/procfs/net_dev.go b/vendor/github.com/prometheus/procfs/net_dev.go index a0b7a01196..47a710befb 100644 --- a/vendor/github.com/prometheus/procfs/net_dev.go +++ b/vendor/github.com/prometheus/procfs/net_dev.go @@ -183,7 +183,6 @@ func (netDev NetDev) Total() NetDevLine { names = append(names, ifc.Name) total.RxBytes += ifc.RxBytes total.RxPackets += ifc.RxPackets - total.RxPackets += ifc.RxPackets total.RxErrors += ifc.RxErrors total.RxDropped += ifc.RxDropped total.RxFIFO += ifc.RxFIFO diff --git a/vendor/github.com/prometheus/procfs/proc.go b/vendor/github.com/prometheus/procfs/proc.go index b7c79cf77b..330e472c70 100644 --- a/vendor/github.com/prometheus/procfs/proc.go +++ b/vendor/github.com/prometheus/procfs/proc.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/prometheus/procfs/internal/fs" + "github.com/prometheus/procfs/internal/util" ) // Proc provides information about a running process. @@ -121,13 +122,7 @@ func (fs FS) AllProcs() (Procs, error) { // CmdLine returns the command line of a process. func (p Proc) CmdLine() ([]string, error) { - f, err := os.Open(p.path("cmdline")) - if err != nil { - return nil, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := util.ReadFileNoStat(p.path("cmdline")) if err != nil { return nil, err } @@ -141,13 +136,7 @@ func (p Proc) CmdLine() ([]string, error) { // Comm returns the command name of a process. func (p Proc) Comm() (string, error) { - f, err := os.Open(p.path("comm")) - if err != nil { - return "", err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := util.ReadFileNoStat(p.path("comm")) if err != nil { return "", err } @@ -252,13 +241,11 @@ func (p Proc) MountStats() ([]*Mount, error) { // It supplies information missing in `/proc/self/mounts` and // fixes various other problems with that file too. func (p Proc) MountInfo() ([]*MountInfo, error) { - f, err := os.Open(p.path("mountinfo")) + data, err := util.ReadFileNoStat(p.path("mountinfo")) if err != nil { return nil, err } - defer f.Close() - - return parseMountInfo(f) + return parseMountInfo(data) } func (p Proc) fileDescriptors() ([]string, error) { diff --git a/vendor/github.com/prometheus/procfs/proc_environ.go b/vendor/github.com/prometheus/procfs/proc_environ.go index 7172bb586e..6134b3580c 100644 --- a/vendor/github.com/prometheus/procfs/proc_environ.go +++ b/vendor/github.com/prometheus/procfs/proc_environ.go @@ -14,22 +14,16 @@ package procfs import ( - "io/ioutil" - "os" "strings" + + "github.com/prometheus/procfs/internal/util" ) // Environ reads process environments from /proc//environ func (p Proc) Environ() ([]string, error) { environments := make([]string, 0) - f, err := os.Open(p.path("environ")) - if err != nil { - return environments, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := util.ReadFileNoStat(p.path("environ")) if err != nil { return environments, err } diff --git a/vendor/github.com/prometheus/procfs/proc_fdinfo.go b/vendor/github.com/prometheus/procfs/proc_fdinfo.go index 83b67d1bde..4e7597f86b 100644 --- a/vendor/github.com/prometheus/procfs/proc_fdinfo.go +++ b/vendor/github.com/prometheus/procfs/proc_fdinfo.go @@ -15,11 +15,10 @@ package procfs import ( "bufio" - "fmt" - "io/ioutil" - "os" + "bytes" "regexp" - "strings" + + "github.com/prometheus/procfs/internal/util" ) // Regexp variables @@ -46,21 +45,15 @@ type ProcFDInfo struct { // FDInfo constructor. On kernels older than 3.8, InotifyInfos will always be empty. func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { - f, err := os.Open(p.path("fdinfo", fd)) + data, err := util.ReadFileNoStat(p.path("fdinfo", fd)) if err != nil { return nil, err } - defer f.Close() - - fdinfo, err := ioutil.ReadAll(f) - if err != nil { - return nil, fmt.Errorf("could not read %s: %s", f.Name(), err) - } var text, pos, flags, mntid string var inotify []InotifyInfo - scanner := bufio.NewScanner(strings.NewReader(string(fdinfo))) + scanner := bufio.NewScanner(bytes.NewReader(data)) for scanner.Scan() { text = scanner.Text() if rPos.MatchString(text) { diff --git a/vendor/github.com/prometheus/procfs/proc_io.go b/vendor/github.com/prometheus/procfs/proc_io.go index 0ff89b1cef..776f349717 100644 --- a/vendor/github.com/prometheus/procfs/proc_io.go +++ b/vendor/github.com/prometheus/procfs/proc_io.go @@ -15,8 +15,8 @@ package procfs import ( "fmt" - "io/ioutil" - "os" + + "github.com/prometheus/procfs/internal/util" ) // ProcIO models the content of /proc//io. @@ -43,13 +43,7 @@ type ProcIO struct { func (p Proc) IO() (ProcIO, error) { pio := ProcIO{} - f, err := os.Open(p.path("io")) - if err != nil { - return pio, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := util.ReadFileNoStat(p.path("io")) if err != nil { return pio, err } diff --git a/vendor/github.com/prometheus/procfs/proc_psi.go b/vendor/github.com/prometheus/procfs/proc_psi.go index 46fe266263..0d7bee54ca 100644 --- a/vendor/github.com/prometheus/procfs/proc_psi.go +++ b/vendor/github.com/prometheus/procfs/proc_psi.go @@ -24,11 +24,13 @@ package procfs // > full avg10=0.00 avg60=0.13 avg300=0.96 total=8183134 import ( + "bufio" + "bytes" "fmt" "io" - "io/ioutil" - "os" "strings" + + "github.com/prometheus/procfs/internal/util" ) const lineFormat = "avg10=%f avg60=%f avg300=%f total=%d" @@ -55,24 +57,21 @@ type PSIStats struct { // resource from /proc/pressure/. At time of writing this can be // either "cpu", "memory" or "io". func (fs FS) PSIStatsForResource(resource string) (PSIStats, error) { - file, err := os.Open(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource))) + data, err := util.ReadFileNoStat(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource))) if err != nil { return PSIStats{}, fmt.Errorf("psi_stats: unavailable for %s", resource) } - defer file.Close() - return parsePSIStats(resource, file) + return parsePSIStats(resource, bytes.NewReader(data)) } // parsePSIStats parses the specified file for pressure stall information -func parsePSIStats(resource string, file io.Reader) (PSIStats, error) { +func parsePSIStats(resource string, r io.Reader) (PSIStats, error) { psiStats := PSIStats{} - stats, err := ioutil.ReadAll(file) - if err != nil { - return psiStats, fmt.Errorf("psi_stats: unable to read data for %s", resource) - } - for _, l := range strings.Split(string(stats), "\n") { + scanner := bufio.NewScanner(r) + for scanner.Scan() { + l := scanner.Text() prefix := strings.Split(l, " ")[0] switch prefix { case "some": diff --git a/vendor/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/prometheus/procfs/proc_stat.go index dbde1fa0d6..4517d2e9dd 100644 --- a/vendor/github.com/prometheus/procfs/proc_stat.go +++ b/vendor/github.com/prometheus/procfs/proc_stat.go @@ -16,10 +16,10 @@ package procfs import ( "bytes" "fmt" - "io/ioutil" "os" "github.com/prometheus/procfs/internal/fs" + "github.com/prometheus/procfs/internal/util" ) // Originally, this USER_HZ value was dynamically retrieved via a sysconf call @@ -113,13 +113,7 @@ func (p Proc) NewStat() (ProcStat, error) { // Stat returns the current status information of the process. func (p Proc) Stat() (ProcStat, error) { - f, err := os.Open(p.path("stat")) - if err != nil { - return ProcStat{}, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := util.ReadFileNoStat(p.path("stat")) if err != nil { return ProcStat{}, err } diff --git a/vendor/github.com/prometheus/procfs/proc_status.go b/vendor/github.com/prometheus/procfs/proc_status.go index ad290fae7d..e30c2b88f4 100644 --- a/vendor/github.com/prometheus/procfs/proc_status.go +++ b/vendor/github.com/prometheus/procfs/proc_status.go @@ -15,10 +15,10 @@ package procfs import ( "bytes" - "io/ioutil" - "os" "strconv" "strings" + + "github.com/prometheus/procfs/internal/util" ) // ProcStatus provides status information about the process, @@ -75,13 +75,7 @@ type ProcStatus struct { // NewStatus returns the current status information of the process. func (p Proc) NewStatus() (ProcStatus, error) { - f, err := os.Open(p.path("status")) - if err != nil { - return ProcStatus{}, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := util.ReadFileNoStat(p.path("status")) if err != nil { return ProcStatus{}, err } diff --git a/vendor/github.com/prometheus/procfs/stat.go b/vendor/github.com/prometheus/procfs/stat.go index 6661ee03a6..b2a6fc994c 100644 --- a/vendor/github.com/prometheus/procfs/stat.go +++ b/vendor/github.com/prometheus/procfs/stat.go @@ -15,13 +15,14 @@ package procfs import ( "bufio" + "bytes" "fmt" "io" - "os" "strconv" "strings" "github.com/prometheus/procfs/internal/fs" + "github.com/prometheus/procfs/internal/util" ) // CPUStat shows how much time the cpu spend in various stages. @@ -164,16 +165,15 @@ func (fs FS) NewStat() (Stat, error) { // Stat returns information about current cpu/process statistics. // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt func (fs FS) Stat() (Stat, error) { - - f, err := os.Open(fs.proc.Path("stat")) + fileName := fs.proc.Path("stat") + data, err := util.ReadFileNoStat(fileName) if err != nil { return Stat{}, err } - defer f.Close() stat := Stat{} - scanner := bufio.NewScanner(f) + scanner := bufio.NewScanner(bytes.NewReader(data)) for scanner.Scan() { line := scanner.Text() parts := strings.Fields(scanner.Text()) @@ -237,7 +237,7 @@ func (fs FS) Stat() (Stat, error) { } if err := scanner.Err(); err != nil { - return Stat{}, fmt.Errorf("couldn't parse %s: %s", f.Name(), err) + return Stat{}, fmt.Errorf("couldn't parse %s: %s", fileName, err) } return stat, nil diff --git a/vendor/github.com/prometheus/procfs/sysfs/class_powercap.go b/vendor/github.com/prometheus/procfs/sysfs/class_powercap.go new file mode 100644 index 0000000000..c8b00557b2 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/sysfs/class_powercap.go @@ -0,0 +1,112 @@ +// Copyright 2019 The Prometheus Authors +// 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. + +// +build !windows + +package sysfs + +import ( + "errors" + "io/ioutil" + "path/filepath" + "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// RaplZone stores the information for one RAPL power zone +type RaplZone struct { + Name string // name of RAPL zone from file "name" + Index int // index (different value for duplicate names) + Path string // filesystem path of RaplZone + MaxMicrojoules uint64 // max RAPL microjoule value +} + +// GetRaplZones returns a slice of RaplZones +// When RAPL files are not present, returns nil with error +// https://www.kernel.org/doc/Documentation/power/powercap/powercap.txt +func GetRaplZones(fs FS) ([]RaplZone, error) { + raplDir := fs.sys.Path("class/powercap") + + files, err := ioutil.ReadDir(raplDir) + if err != nil { + return nil, errors.New( + "no sysfs powercap / RAPL power metrics files found") + } + + var zones []RaplZone + + // count name usages to avoid duplicates (label them with an index) + countNameUsages := make(map[string]int) + + // loop through directory files searching for file "name" from subdirs + for _, f := range files { + nameFile := filepath.Join(raplDir, f.Name(), "/name") + nameBytes, err := ioutil.ReadFile(nameFile) + if err == nil { + // add new rapl zone since name file was found + name := strings.TrimSpace(string(nameBytes)) + + // get a pair of index and final name + index, name := getIndexAndName(countNameUsages, + name) + + maxMicrojouleFilename := filepath.Join(raplDir, f.Name(), + "/max_energy_range_uj") + maxMicrojoules, err := util.ReadUintFromFile(maxMicrojouleFilename) + if err != nil { + return nil, err + } + + zone := RaplZone{ + Name: name, + Index: index, + Path: filepath.Join(raplDir, f.Name()), + MaxMicrojoules: maxMicrojoules, + } + + zones = append(zones, zone) + + // Store into map how many times this name has been used. There can + // be e.g. multiple "dram" instances without any index postfix. The + // count is then used for indexing + countNameUsages[name] = index + 1 + } + } + + return zones, nil +} + +// GetEnergyMicrojoules returns the current microjoule value from the zone energy counter +// https://www.kernel.org/doc/Documentation/power/powercap/powercap.txt +func (rz RaplZone) GetEnergyMicrojoules() (uint64, error) { + return util.ReadUintFromFile(filepath.Join(rz.Path, "/energy_uj")) +} + +// getIndexAndName returns a pair of (index, name) for a given name and name +// counting map. Some RAPL-names have an index at the end, some have duplicates +// without an index at the end. When the index is embedded in the name, it is +// provided back as an integer, and stripped from the returned name. Usage +// count is used when the index value is absent from the name. +func getIndexAndName(countNameUsages map[string]int, name string) (int, string) { + length := len(name) + if length >= 2 { + index, err := strconv.Atoi(name[length-1:]) + if name[length-2:length-1] == "-" && err == nil { + return index, name[:length-2] + } + } + // return count as the index, since name didn't have an index at the end + return countNameUsages[name], name +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 48dd325b3a..0e82aef031 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -47,7 +47,7 @@ github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/log github.com/prometheus/common/model github.com/prometheus/common/version -# github.com/prometheus/procfs v0.0.5 +# github.com/prometheus/procfs v0.0.7 github.com/prometheus/procfs github.com/prometheus/procfs/bcache github.com/prometheus/procfs/internal/fs @@ -70,7 +70,7 @@ golang.org/x/net/bpf golang.org/x/net/internal/iana golang.org/x/net/internal/socket golang.org/x/net/ipv4 -# golang.org/x/sync v0.0.0-20190423024810-112230192c58 +# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sync/errgroup # golang.org/x/sys v0.0.0-20190902133755-9109b7679e13 golang.org/x/sys/unix From c4c5f1f062b141d0111b4068a52eb5917048b3ea Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Sat, 16 Nov 2019 18:32:52 +0100 Subject: [PATCH 09/35] Update github.com/mattn/go-xmlrpc (#1529) Fixes: https://github.com/prometheus/node_exporter/issues/1294 Signed-off-by: Ben Kochie --- go.mod | 2 +- go.sum | 4 ++-- vendor/github.com/mattn/go-xmlrpc/xmlrpc.go | 2 +- vendor/modules.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 3fc762956d..c0ca32283e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/hodgesds/perf-utils v0.0.7 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3 - github.com/mattn/go-xmlrpc v0.0.1 + github.com/mattn/go-xmlrpc v0.0.3 github.com/mdlayher/genetlink v0.0.0-20190828143517-e35f2bf499b9 // indirect github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee github.com/pkg/errors v0.8.1 diff --git a/go.sum b/go.sum index c111807bf0..e8a8f943a5 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3 h1:XGhvld9vIpj929Gri5ybjukYZeyZwKkFkqgATqBQiOs= github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3/go.mod h1:lRgtFVamD7L7GaXOSwBiuXMwU3Aicfn5h66LVs4u2SA= -github.com/mattn/go-xmlrpc v0.0.1 h1:JY8G+sH4jcjzZvxAY5P+wNrWA2WYC+aK+2bsYOl4z0Q= -github.com/mattn/go-xmlrpc v0.0.1/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA= +github.com/mattn/go-xmlrpc v0.0.3 h1:Y6WEMLEsqs3RviBrAa1/7qmbGB7DVD3brZIbqMbQdGY= +github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/genetlink v0.0.0-20190828143517-e35f2bf499b9 h1:o+ckyx58UC6Itoo7sEwmXMpHcnI31lRK6w4M5gQMIMw= diff --git a/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go b/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go index b3083c4413..b4c0fd0396 100644 --- a/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go +++ b/vendor/github.com/mattn/go-xmlrpc/xmlrpc.go @@ -319,7 +319,7 @@ func makeRequest(name string, args ...interface{}) *bytes.Buffer { } func call(client *http.Client, url, name string, args ...interface{}) (v interface{}, e error) { - r, e := httpClient.Post(url, "text/xml", makeRequest(name, args...)) + r, e := client.Post(url, "text/xml", makeRequest(name, args...)) if e != nil { return nil, e } diff --git a/vendor/modules.txt b/vendor/modules.txt index 0e82aef031..484d28b4a7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -21,7 +21,7 @@ github.com/hodgesds/perf-utils github.com/konsorten/go-windows-terminal-sequences # github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3 github.com/lufia/iostat -# github.com/mattn/go-xmlrpc v0.0.1 +# github.com/mattn/go-xmlrpc v0.0.3 github.com/mattn/go-xmlrpc # github.com/matttproud/golang_protobuf_extensions v1.0.1 github.com/matttproud/golang_protobuf_extensions/pbutil From 177ac7f50f869361ef2f0bf4c31ab75d588ba7b3 Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Fri, 22 Nov 2019 13:20:52 -0500 Subject: [PATCH 10/35] collector: refactor textfile collector to avoid looping defer Signed-off-by: Matt Layher --- collector/textfile.go | 129 ++++++++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 56 deletions(-) diff --git a/collector/textfile.go b/collector/textfile.go index 52e88bb780..aee11db815 100644 --- a/collector/textfile.go +++ b/collector/textfile.go @@ -162,98 +162,115 @@ func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Me } func (c *textFileCollector) exportMTimes(mtimes map[string]time.Time, ch chan<- prometheus.Metric) { + if len(mtimes) == 0 { + return + } + // Export the mtimes of the successful files. - if len(mtimes) > 0 { - // Sorting is needed for predictable output comparison in tests. - filenames := make([]string, 0, len(mtimes)) - for filename := range mtimes { - filenames = append(filenames, filename) - } - sort.Strings(filenames) + // Sorting is needed for predictable output comparison in tests. + filenames := make([]string, 0, len(mtimes)) + for filename := range mtimes { + filenames = append(filenames, filename) + } + sort.Strings(filenames) - for _, filename := range filenames { - mtime := float64(mtimes[filename].UnixNano() / 1e9) - if c.mtime != nil { - mtime = *c.mtime - } - ch <- prometheus.MustNewConstMetric(mtimeDesc, prometheus.GaugeValue, mtime, filename) + for _, filename := range filenames { + mtime := float64(mtimes[filename].UnixNano() / 1e9) + if c.mtime != nil { + mtime = *c.mtime } + ch <- prometheus.MustNewConstMetric(mtimeDesc, prometheus.GaugeValue, mtime, filename) } } // Update implements the Collector interface. func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { - error := 0.0 - mtimes := map[string]time.Time{} - - // Iterate over files and accumulate their metrics. + // Iterate over files and accumulate their metrics, but also track any + // parsing errors so an error metric can be reported. + var errored bool files, err := ioutil.ReadDir(c.path) if err != nil && c.path != "" { - log.Errorf("Error reading textfile collector directory %q: %s", c.path, err) - error = 1.0 + errored = true + log.Errorf("failed to read textfile collector directory %q: %v", c.path, err) } + mtimes := make(map[string]time.Time, len(files)) for _, f := range files { if !strings.HasSuffix(f.Name(), ".prom") { continue } - path := filepath.Join(c.path, f.Name()) - file, err := os.Open(path) - if err != nil { - log.Errorf("Error opening %q: %v", path, err) - error = 1.0 - continue - } - defer file.Close() - var parser expfmt.TextParser - parsedFamilies, err := parser.TextToMetricFamilies(file) - if err != nil { - log.Errorf("Error parsing %q: %v", path, err) - error = 1.0 - continue - } - if hasTimestamps(parsedFamilies) { - log.Errorf("Textfile %q contains unsupported client-side timestamps, skipping entire file", path) - error = 1.0 - continue - } - for _, mf := range parsedFamilies { - if mf.Help == nil { - help := fmt.Sprintf("Metric read from %s", path) - mf.Help = &help - } - } - - // Only set this once it has been parsed and validated, so that - // a failure does not appear fresh. - stat, err := file.Stat() + mtime, err := c.processFile(f.Name(), ch) if err != nil { - log.Errorf("Error stat'ing %q: %v", path, err) - error = 1.0 + errored = true + log.Errorf("failed to collect textfile data from %q: %v", f.Name(), err) continue } - mtimes[f.Name()] = stat.ModTime() - for _, mf := range parsedFamilies { - convertMetricFamily(mf, ch) - } + mtimes[f.Name()] = *mtime } c.exportMTimes(mtimes, ch) // Export if there were errors. + var errVal float64 + if errored { + errVal = 1.0 + } + ch <- prometheus.MustNewConstMetric( prometheus.NewDesc( "node_textfile_scrape_error", "1 if there was an error opening or reading a file, 0 otherwise", nil, nil, ), - prometheus.GaugeValue, error, + prometheus.GaugeValue, errVal, ) + return nil } +// processFile processes a single file, returning its modification time on success. +func (c *textFileCollector) processFile(name string, ch chan<- prometheus.Metric) (*time.Time, error) { + path := filepath.Join(c.path, name) + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open textfile data file %q: %v", path, err) + } + defer f.Close() + + var parser expfmt.TextParser + families, err := parser.TextToMetricFamilies(f) + if err != nil { + return nil, fmt.Errorf("failed to parse textfile data from %q: %v", path, err) + } + + if hasTimestamps(families) { + return nil, fmt.Errorf("textfile %q contains unsupported client-side timestamps, skipping entire file", path) + } + + for _, mf := range families { + if mf.Help == nil { + help := fmt.Sprintf("Metric read from %s", path) + mf.Help = &help + } + } + + for _, mf := range families { + convertMetricFamily(mf, ch) + } + + // Only stat the file once it has been parsed and validated, so that + // a failure does not appear fresh. + stat, err := f.Stat() + if err != nil { + return nil, fmt.Errorf("failed to stat %q: %v", path, err) + } + + t := stat.ModTime() + return &t, nil +} + // hasTimestamps returns true when metrics contain unsupported timestamps. func hasTimestamps(parsedFamilies map[string]*dto.MetricFamily) bool { for _, mf := range parsedFamilies { From 04fbcfffa194b5a77c8de60ca38d2530a8dfeae3 Mon Sep 17 00:00:00 2001 From: Benjamin Drung Date: Fri, 22 Nov 2019 22:52:17 +0100 Subject: [PATCH 11/35] Collect InfiniBand port state and physical state (#1357) Collect the InfiniBand port state, the physical state, and the maximum signal transfer rate. Signed-off-by: Benjamin Drung --- collector/fixtures/e2e-64k-page-output.txt | 15 +++++++++++++++ collector/fixtures/e2e-output.txt | 15 +++++++++++++++ collector/infiniband_linux.go | 7 +++++++ 3 files changed, 37 insertions(+) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 729deff9c8..9f75f082a4 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -840,6 +840,11 @@ node_infiniband_multicast_packets_received_total{device="mlx4_0",port="2"} 0 # TYPE node_infiniband_multicast_packets_transmitted_total counter node_infiniband_multicast_packets_transmitted_total{device="mlx4_0",port="1"} 16 node_infiniband_multicast_packets_transmitted_total{device="mlx4_0",port="2"} 0 +# HELP node_infiniband_physical_state_id Physical state of the InfiniBand port (0: no change, 1: sleep, 2: polling, 3: disable, 4: shift, 5: link up, 6: link error recover, 7: phytest) +# TYPE node_infiniband_physical_state_id gauge +node_infiniband_physical_state_id{device="i40iw0",port="1"} 5 +node_infiniband_physical_state_id{device="mlx4_0",port="1"} 5 +node_infiniband_physical_state_id{device="mlx4_0",port="2"} 5 # HELP node_infiniband_port_constraint_errors_received_total Number of packets received on the switch physical port that are discarded # TYPE node_infiniband_port_constraint_errors_received_total counter node_infiniband_port_constraint_errors_received_total{device="mlx4_0",port="1"} 0 @@ -872,6 +877,16 @@ node_infiniband_port_packets_transmitted_total{device="mlx4_0",port="1"} 6.23586 # HELP node_infiniband_port_transmit_wait_total Number of ticks during which the port had data to transmit but no data was sent during the entire tick # TYPE node_infiniband_port_transmit_wait_total counter node_infiniband_port_transmit_wait_total{device="mlx4_0",port="1"} 4.294967295e+09 +# HELP node_infiniband_rate_bytes_per_second Maximum signal transfer rate +# TYPE node_infiniband_rate_bytes_per_second gauge +node_infiniband_rate_bytes_per_second{device="i40iw0",port="1"} 1.25e+09 +node_infiniband_rate_bytes_per_second{device="mlx4_0",port="1"} 5e+09 +node_infiniband_rate_bytes_per_second{device="mlx4_0",port="2"} 5e+09 +# HELP node_infiniband_state_id State of the InfiniBand port (0: no change, 1: down, 2: init, 3: armed, 4: active, 5: act defer) +# TYPE node_infiniband_state_id gauge +node_infiniband_state_id{device="i40iw0",port="1"} 4 +node_infiniband_state_id{device="mlx4_0",port="1"} 4 +node_infiniband_state_id{device="mlx4_0",port="2"} 4 # HELP node_infiniband_unicast_packets_received_total Number of unicast packets received (including errors) # TYPE node_infiniband_unicast_packets_received_total counter node_infiniband_unicast_packets_received_total{device="mlx4_0",port="1"} 61148 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 41129c4a23..c163346963 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -840,6 +840,11 @@ node_infiniband_multicast_packets_received_total{device="mlx4_0",port="2"} 0 # TYPE node_infiniband_multicast_packets_transmitted_total counter node_infiniband_multicast_packets_transmitted_total{device="mlx4_0",port="1"} 16 node_infiniband_multicast_packets_transmitted_total{device="mlx4_0",port="2"} 0 +# HELP node_infiniband_physical_state_id Physical state of the InfiniBand port (0: no change, 1: sleep, 2: polling, 3: disable, 4: shift, 5: link up, 6: link error recover, 7: phytest) +# TYPE node_infiniband_physical_state_id gauge +node_infiniband_physical_state_id{device="i40iw0",port="1"} 5 +node_infiniband_physical_state_id{device="mlx4_0",port="1"} 5 +node_infiniband_physical_state_id{device="mlx4_0",port="2"} 5 # HELP node_infiniband_port_constraint_errors_received_total Number of packets received on the switch physical port that are discarded # TYPE node_infiniband_port_constraint_errors_received_total counter node_infiniband_port_constraint_errors_received_total{device="mlx4_0",port="1"} 0 @@ -872,6 +877,16 @@ node_infiniband_port_packets_transmitted_total{device="mlx4_0",port="1"} 6.23586 # HELP node_infiniband_port_transmit_wait_total Number of ticks during which the port had data to transmit but no data was sent during the entire tick # TYPE node_infiniband_port_transmit_wait_total counter node_infiniband_port_transmit_wait_total{device="mlx4_0",port="1"} 4.294967295e+09 +# HELP node_infiniband_rate_bytes_per_second Maximum signal transfer rate +# TYPE node_infiniband_rate_bytes_per_second gauge +node_infiniband_rate_bytes_per_second{device="i40iw0",port="1"} 1.25e+09 +node_infiniband_rate_bytes_per_second{device="mlx4_0",port="1"} 5e+09 +node_infiniband_rate_bytes_per_second{device="mlx4_0",port="2"} 5e+09 +# HELP node_infiniband_state_id State of the InfiniBand port (0: no change, 1: down, 2: init, 3: armed, 4: active, 5: act defer) +# TYPE node_infiniband_state_id gauge +node_infiniband_state_id{device="i40iw0",port="1"} 4 +node_infiniband_state_id{device="mlx4_0",port="1"} 4 +node_infiniband_state_id{device="mlx4_0",port="2"} 4 # HELP node_infiniband_unicast_packets_received_total Number of unicast packets received (including errors) # TYPE node_infiniband_unicast_packets_received_total counter node_infiniband_unicast_packets_received_total{device="mlx4_0",port="1"} 61148 diff --git a/collector/infiniband_linux.go b/collector/infiniband_linux.go index 237a913394..828e40e5ce 100644 --- a/collector/infiniband_linux.go +++ b/collector/infiniband_linux.go @@ -57,6 +57,7 @@ func NewInfiniBandCollector() (Collector, error) { "link_error_recovery_total": "Number of times the link successfully recovered from an error state", "multicast_packets_received_total": "Number of multicast packets received (including errors)", "multicast_packets_transmitted_total": "Number of multicast packets transmitted (including errors)", + "physical_state_id": "Physical state of the InfiniBand port (0: no change, 1: sleep, 2: polling, 3: disable, 4: shift, 5: link up, 6: link error recover, 7: phytest)", "port_constraint_errors_received_total": "Number of packets received on the switch physical port that are discarded", "port_constraint_errors_transmitted_total": "Number of packets not transmitted from the switch physical port", "port_data_received_bytes_total": "Number of data octets received on all links", @@ -67,6 +68,8 @@ func NewInfiniBandCollector() (Collector, error) { "port_packets_received_total": "Number of packets received on all VLs by this port (including errors)", "port_packets_transmitted_total": "Number of packets transmitted on all VLs from this port (including errors)", "port_transmit_wait_total": "Number of ticks during which the port had data to transmit but no data was sent during the entire tick", + "rate_bytes_per_second": "Maximum signal transfer rate", + "state_id": "State of the InfiniBand port (0: no change, 1: down, 2: init, 3: armed, 4: active, 5: act defer)", "unicast_packets_received_total": "Number of unicast packets received (including errors)", "unicast_packets_transmitted_total": "Number of unicast packets transmitted (including errors)", } @@ -105,6 +108,10 @@ func (c *infinibandCollector) Update(ch chan<- prometheus.Metric) error { for _, port := range device.Ports { portStr := strconv.FormatUint(uint64(port.Port), 10) + c.pushMetric(ch, "state_id", uint64(port.StateID), port.Name, portStr, prometheus.GaugeValue) + c.pushMetric(ch, "physical_state_id", uint64(port.PhysStateID), port.Name, portStr, prometheus.GaugeValue) + c.pushMetric(ch, "rate_bytes_per_second", port.Rate, port.Name, portStr, prometheus.GaugeValue) + c.pushCounter(ch, "legacy_multicast_packets_received_total", port.Counters.LegacyPortMulticastRcvPackets, port.Name, portStr) c.pushCounter(ch, "legacy_multicast_packets_transmitted_total", port.Counters.LegacyPortMulticastXmitPackets, port.Name, portStr) c.pushCounter(ch, "legacy_data_received_bytes_total", port.Counters.LegacyPortRcvData64, port.Name, portStr) From 67d3010a7974b5b255d8a61211a3dcce36ed6410 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Sat, 23 Nov 2019 07:52:47 -0600 Subject: [PATCH 12/35] Add fixture update helper (#1551) * Add makefile target to update sysfs fixtures. * Use similar style for fixtures from procfs. * Re-pack fixtures ttar file. Signed-off-by: Ben Kochie --- Makefile | 12 ++++++++---- collector/fixtures/sys.ttar | 16 ++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 31eae06b6b..2e1f04ff71 100644 --- a/Makefile +++ b/Makefile @@ -91,12 +91,16 @@ test-32bit: collector/fixtures/sys/.unpacked skip-test-32bit: @echo ">> SKIP running tests in 32-bit mode: not supported on $(GOHOSTOS)/$(GOHOSTARCH)" -collector/fixtures/sys/.unpacked: collector/fixtures/sys.ttar - @echo ">> extracting sysfs fixtures" - if [ -d collector/fixtures/sys ] ; then rm -r collector/fixtures/sys ; fi - ./ttar -C collector/fixtures -x -f collector/fixtures/sys.ttar +%/.unpacked: %.ttar + @echo ">> extracting fixtures" + if [ -d $(dir $@) ] ; then rm -r $(dir $@) ; fi + ./ttar -C $(dir $*) -x -f $*.ttar touch $@ +update_fixtures: + rm -vf collector/fixtures/sys/.unpacked + ./ttar -C collector/fixtures -c -f collector/fixtures/sys.ttar sys + .PHONY: test-e2e test-e2e: build collector/fixtures/sys/.unpacked @echo ">> running end-to-end tests" diff --git a/collector/fixtures/sys.ttar b/collector/fixtures/sys.ttar index 27aa0a7954..3704b33212 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -298,11 +298,6 @@ Lines: 1 0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: sys/class/infiniband/mlx4_0/ports/1/counters/port_xmit_discards -Lines: 1 -0 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/class/infiniband/mlx4_0/ports/1/counters/port_rcv_errors Lines: 1 0 @@ -1278,15 +1273,16 @@ Path: sys/class/power_supply/BAT0/voltage_now Lines: 1 11660000 Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/class/thermal Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: sys/class/thermal/thermal_zone0 -SymlinkTo: ../../devices/virtual/thermal/thermal_zone0 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: sys/class/thermal/cooling_device0 SymlinkTo: ../../devices/virtual/thermal/cooling_device0 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/thermal/thermal_zone0 +SymlinkTo: ../../devices/virtual/thermal/thermal_zone0 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/devices Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3200,7 +3196,3 @@ Lines: 1 20 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: sys/.unpacked -Lines: 0 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 3c2c4e7b3c473777b13e79cc84cc04e519fd3a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20Hoffst=C3=A4tte?= Date: Mon, 25 Nov 2019 20:16:15 +0100 Subject: [PATCH 13/35] Add new counters for flush requests in Linux 5.5 (#1548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add diskstat flush request counters for Linux 5.5+ * Update tests for diskstat flush request counters with Linux 5.5+ Signed-off-by: Holger Hoffstätte --- collector/diskstats_linux.go | 17 +++++++++++++++++ collector/diskstats_linux_test.go | 8 ++++++++ collector/fixtures/e2e-output.txt | 21 +++++++++++++++++++++ collector/fixtures/proc/diskstats | 2 ++ 4 files changed, 48 insertions(+) diff --git a/collector/diskstats_linux.go b/collector/diskstats_linux.go index 1d39f2bf88..32bb90cb41 100644 --- a/collector/diskstats_linux.go +++ b/collector/diskstats_linux.go @@ -160,6 +160,23 @@ func NewDiskstatsCollector() (Collector, error) { ), valueType: prometheus.CounterValue, factor: .001, }, + { + desc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "flush_requests_total"), + "The total number of flush requests completed successfully", + diskLabelNames, + nil, + ), valueType: prometheus.CounterValue, + }, + { + desc: prometheus.NewDesc( + prometheus.BuildFQName(namespace, diskSubsystem, "flush_requests_time_seconds_total"), + "This is the total number of seconds spent by all flush requests.", + diskLabelNames, + nil, + ), valueType: prometheus.CounterValue, + factor: .001, + }, }, }, nil } diff --git a/collector/diskstats_linux_test.go b/collector/diskstats_linux_test.go index 84a7de441f..9bfecc58ff 100644 --- a/collector/diskstats_linux_test.go +++ b/collector/diskstats_linux_test.go @@ -41,4 +41,12 @@ func TestDiskStats(t *testing.T) { if want, got := "11130", diskStats["sdb"][14]; want != got { t.Errorf("want diskstats sdb %s, got %s", want, got) } + + if want, got := "1555", diskStats["sdc"][15]; want != got { + t.Errorf("want diskstats sdc %s, got %s", want, got) + } + + if want, got := "1944", diskStats["sdc"][16]; want != got { + t.Errorf("want diskstats sdc %s, got %s", want, got) + } } diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index c163346963..64be188d75 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -309,15 +309,25 @@ node_cpu_seconds_total{cpu="7",mode="user"} 290.98 # HELP node_disk_discard_time_seconds_total This is the total number of seconds spent by all discards. # TYPE node_disk_discard_time_seconds_total counter node_disk_discard_time_seconds_total{device="sdb"} 11.13 +node_disk_discard_time_seconds_total{device="sdc"} 11.13 # HELP node_disk_discarded_sectors_total The total number of sectors discarded successfully. # TYPE node_disk_discarded_sectors_total counter node_disk_discarded_sectors_total{device="sdb"} 1.925173784e+09 +node_disk_discarded_sectors_total{device="sdc"} 1.25173784e+08 # HELP node_disk_discards_completed_total The total number of discards completed successfully. # TYPE node_disk_discards_completed_total counter node_disk_discards_completed_total{device="sdb"} 68851 +node_disk_discards_completed_total{device="sdc"} 18851 # HELP node_disk_discards_merged_total The total number of discards merged. # TYPE node_disk_discards_merged_total counter node_disk_discards_merged_total{device="sdb"} 0 +node_disk_discards_merged_total{device="sdc"} 0 +# HELP node_disk_flush_requests_time_seconds_total This is the total number of seconds spent by all flush requests. +# TYPE node_disk_flush_requests_time_seconds_total counter +node_disk_flush_requests_time_seconds_total{device="sdc"} 1.944 +# HELP node_disk_flush_requests_total The total number of flush requests completed successfully +# TYPE node_disk_flush_requests_total counter +node_disk_flush_requests_total{device="sdc"} 1555 # HELP node_disk_io_now The number of I/Os currently in progress. # TYPE node_disk_io_now gauge node_disk_io_now{device="dm-0"} 0 @@ -332,6 +342,7 @@ node_disk_io_now{device="mmcblk0p2"} 0 node_disk_io_now{device="nvme0n1"} 0 node_disk_io_now{device="sda"} 0 node_disk_io_now{device="sdb"} 0 +node_disk_io_now{device="sdc"} 0 node_disk_io_now{device="sr0"} 0 node_disk_io_now{device="vda"} 0 # HELP node_disk_io_time_seconds_total Total seconds spent doing I/Os. @@ -348,6 +359,7 @@ node_disk_io_time_seconds_total{device="mmcblk0p2"} 0.068 node_disk_io_time_seconds_total{device="nvme0n1"} 222.766 node_disk_io_time_seconds_total{device="sda"} 9653.880000000001 node_disk_io_time_seconds_total{device="sdb"} 60.730000000000004 +node_disk_io_time_seconds_total{device="sdc"} 10.73 node_disk_io_time_seconds_total{device="sr0"} 0 node_disk_io_time_seconds_total{device="vda"} 41614.592000000004 # HELP node_disk_io_time_weighted_seconds_total The weighted # of seconds spent doing I/Os. @@ -364,6 +376,7 @@ node_disk_io_time_weighted_seconds_total{device="mmcblk0p2"} 0.068 node_disk_io_time_weighted_seconds_total{device="nvme0n1"} 1032.546 node_disk_io_time_weighted_seconds_total{device="sda"} 82621.804 node_disk_io_time_weighted_seconds_total{device="sdb"} 67.07000000000001 +node_disk_io_time_weighted_seconds_total{device="sdc"} 17.07 node_disk_io_time_weighted_seconds_total{device="sr0"} 0 node_disk_io_time_weighted_seconds_total{device="vda"} 2.0778722280000001e+06 # HELP node_disk_read_bytes_total The total number of bytes read successfully. @@ -380,6 +393,7 @@ node_disk_read_bytes_total{device="mmcblk0p2"} 389120 node_disk_read_bytes_total{device="nvme0n1"} 2.377714176e+09 node_disk_read_bytes_total{device="sda"} 5.13713216512e+11 node_disk_read_bytes_total{device="sdb"} 4.944782848e+09 +node_disk_read_bytes_total{device="sdc"} 8.48782848e+08 node_disk_read_bytes_total{device="sr0"} 0 node_disk_read_bytes_total{device="vda"} 1.6727491584e+10 # HELP node_disk_read_time_seconds_total The total number of seconds spent by all reads. @@ -396,6 +410,7 @@ node_disk_read_time_seconds_total{device="mmcblk0p2"} 0.068 node_disk_read_time_seconds_total{device="nvme0n1"} 21.650000000000002 node_disk_read_time_seconds_total{device="sda"} 18492.372 node_disk_read_time_seconds_total{device="sdb"} 0.084 +node_disk_read_time_seconds_total{device="sdc"} 0.014 node_disk_read_time_seconds_total{device="sr0"} 0 node_disk_read_time_seconds_total{device="vda"} 8655.768 # HELP node_disk_reads_completed_total The total number of reads completed successfully. @@ -412,6 +427,7 @@ node_disk_reads_completed_total{device="mmcblk0p2"} 95 node_disk_reads_completed_total{device="nvme0n1"} 47114 node_disk_reads_completed_total{device="sda"} 2.5354637e+07 node_disk_reads_completed_total{device="sdb"} 326552 +node_disk_reads_completed_total{device="sdc"} 126552 node_disk_reads_completed_total{device="sr0"} 0 node_disk_reads_completed_total{device="vda"} 1.775784e+06 # HELP node_disk_reads_merged_total The total number of reads merged. @@ -428,6 +444,7 @@ node_disk_reads_merged_total{device="mmcblk0p2"} 0 node_disk_reads_merged_total{device="nvme0n1"} 4 node_disk_reads_merged_total{device="sda"} 3.4367663e+07 node_disk_reads_merged_total{device="sdb"} 841 +node_disk_reads_merged_total{device="sdc"} 141 node_disk_reads_merged_total{device="sr0"} 0 node_disk_reads_merged_total{device="vda"} 15386 # HELP node_disk_write_time_seconds_total This is the total number of seconds spent by all writes. @@ -444,6 +461,7 @@ node_disk_write_time_seconds_total{device="mmcblk0p2"} 0 node_disk_write_time_seconds_total{device="nvme0n1"} 1011.053 node_disk_write_time_seconds_total{device="sda"} 63877.96 node_disk_write_time_seconds_total{device="sdb"} 5.007 +node_disk_write_time_seconds_total{device="sdc"} 1.0070000000000001 node_disk_write_time_seconds_total{device="sr0"} 0 node_disk_write_time_seconds_total{device="vda"} 2.069221364e+06 # HELP node_disk_writes_completed_total The total number of writes completed successfully. @@ -460,6 +478,7 @@ node_disk_writes_completed_total{device="mmcblk0p2"} 0 node_disk_writes_completed_total{device="nvme0n1"} 1.07832e+06 node_disk_writes_completed_total{device="sda"} 2.8444756e+07 node_disk_writes_completed_total{device="sdb"} 41822 +node_disk_writes_completed_total{device="sdc"} 11822 node_disk_writes_completed_total{device="sr0"} 0 node_disk_writes_completed_total{device="vda"} 6.038856e+06 # HELP node_disk_writes_merged_total The number of writes merged. @@ -476,6 +495,7 @@ node_disk_writes_merged_total{device="mmcblk0p2"} 0 node_disk_writes_merged_total{device="nvme0n1"} 43950 node_disk_writes_merged_total{device="sda"} 1.1134226e+07 node_disk_writes_merged_total{device="sdb"} 2895 +node_disk_writes_merged_total{device="sdc"} 1895 node_disk_writes_merged_total{device="sr0"} 0 node_disk_writes_merged_total{device="vda"} 2.0711856e+07 # HELP node_disk_written_bytes_total The total number of bytes written successfully. @@ -492,6 +512,7 @@ node_disk_written_bytes_total{device="mmcblk0p2"} 0 node_disk_written_bytes_total{device="nvme0n1"} 2.0199236096e+10 node_disk_written_bytes_total{device="sda"} 2.58916880384e+11 node_disk_written_bytes_total{device="sdb"} 1.01012736e+09 +node_disk_written_bytes_total{device="sdc"} 8.852736e+07 node_disk_written_bytes_total{device="sr0"} 0 node_disk_written_bytes_total{device="vda"} 1.0938236928e+11 # HELP node_drbd_activitylog_writes_total Number of updates of the activity log area of the meta data. diff --git a/collector/fixtures/proc/diskstats b/collector/fixtures/proc/diskstats index b15bf9a64e..3a75de515b 100644 --- a/collector/fixtures/proc/diskstats +++ b/collector/fixtures/proc/diskstats @@ -47,3 +47,5 @@ 8 0 sdb 326552 841 9657779 84 41822 2895 1972905 5007 0 60730 67070 68851 0 1925173784 11130 8 1 sdb1 231 3 34466 4 24 23 106 0 0 64 64 0 0 0 0 8 2 sdb2 326310 838 9622281 67 40726 2872 1972799 4924 0 58250 64567 68851 0 1925173784 11130 + 8 0 sdc 126552 141 1657779 14 11822 1895 172905 1007 0 10730 17070 18851 0 125173784 11130 1555 1944 + 8 1 sdc1 231 3 34466 4 24 23 106 0 0 64 64 0 0 0 0 0 0 From da6b66371f70822bc75303c79c85b80c46bcfae6 Mon Sep 17 00:00:00 2001 From: Matt Layher Date: Mon, 25 Nov 2019 14:41:38 -0500 Subject: [PATCH 14/35] collector: reimplement sockstat collector with procfs (#1552) * collector: reimplement sockstat collector with procfs * collector: handle sockstat IPv4 disabled, debug logging Signed-off-by: Matt Layher --- CHANGELOG.md | 1 + collector/fixtures/e2e-output.txt | 20 +- collector/fixtures/proc/net/sockstat6 | 5 + collector/fixtures/proc/net/sockstat_rhe4 | 5 - collector/sockstat_linux.go | 184 +++++++++----- collector/sockstat_linux_test.go | 59 ----- go.mod | 2 +- go.sum | 4 +- .../prometheus/procfs/.golangci.yml | 2 - .../prometheus/procfs/fixtures.ttar | 19 ++ .../procfs/internal/util/valueparser.go | 18 +- .../github.com/prometheus/procfs/meminfo.go | 228 ++---------------- .../prometheus/procfs/net_sockstat.go | 163 +++++++++++++ .../github.com/prometheus/procfs/net_unix.go | 4 - vendor/modules.txt | 2 +- 15 files changed, 374 insertions(+), 342 deletions(-) create mode 100644 collector/fixtures/proc/net/sockstat6 delete mode 100644 collector/fixtures/proc/net/sockstat_rhe4 delete mode 100644 collector/sockstat_linux_test.go create mode 100644 vendor/github.com/prometheus/procfs/net_sockstat.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eaf77306a..bfc0c2d08e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * [ENHANCEMENT] Report non-fatal collection errors in the exporter metric. #1439 * [ENHANCEMENT] Expose IPVS firewall mark as a label #1455 * [ENHANCEMENT] Add check for systemd version before attempting to query certain metrics. #1413 +* [ENHANCEMENT] The sockstat collector now exposes IPv6 statistics in addition to the existing IPv4 support. * [BUGFIX] Renamed label `state` to `name` on `node_systemd_service_restart_total`. #1393 * [BUGFIX] Fix netdev nil reference on Darwin #1414 * [BUGFIX] Strip path.rootfs from mountpoint labels #1421 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 64be188d75..e915f923ee 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2575,15 +2575,27 @@ node_scrape_collector_success{collector="vmstat"} 1 node_scrape_collector_success{collector="wifi"} 1 node_scrape_collector_success{collector="xfs"} 1 node_scrape_collector_success{collector="zfs"} 1 +# HELP node_sockstat_FRAG6_inuse Number of FRAG6 sockets in state inuse. +# TYPE node_sockstat_FRAG6_inuse gauge +node_sockstat_FRAG6_inuse 0 +# HELP node_sockstat_FRAG6_memory Number of FRAG6 sockets in state memory. +# TYPE node_sockstat_FRAG6_memory gauge +node_sockstat_FRAG6_memory 0 # HELP node_sockstat_FRAG_inuse Number of FRAG sockets in state inuse. # TYPE node_sockstat_FRAG_inuse gauge node_sockstat_FRAG_inuse 0 # HELP node_sockstat_FRAG_memory Number of FRAG sockets in state memory. # TYPE node_sockstat_FRAG_memory gauge node_sockstat_FRAG_memory 0 +# HELP node_sockstat_RAW6_inuse Number of RAW6 sockets in state inuse. +# TYPE node_sockstat_RAW6_inuse gauge +node_sockstat_RAW6_inuse 1 # HELP node_sockstat_RAW_inuse Number of RAW sockets in state inuse. # TYPE node_sockstat_RAW_inuse gauge node_sockstat_RAW_inuse 0 +# HELP node_sockstat_TCP6_inuse Number of TCP6 sockets in state inuse. +# TYPE node_sockstat_TCP6_inuse gauge +node_sockstat_TCP6_inuse 17 # HELP node_sockstat_TCP_alloc Number of TCP sockets in state alloc. # TYPE node_sockstat_TCP_alloc gauge node_sockstat_TCP_alloc 17 @@ -2602,6 +2614,12 @@ node_sockstat_TCP_orphan 0 # HELP node_sockstat_TCP_tw Number of TCP sockets in state tw. # TYPE node_sockstat_TCP_tw gauge node_sockstat_TCP_tw 4 +# HELP node_sockstat_UDP6_inuse Number of UDP6 sockets in state inuse. +# TYPE node_sockstat_UDP6_inuse gauge +node_sockstat_UDP6_inuse 9 +# HELP node_sockstat_UDPLITE6_inuse Number of UDPLITE6 sockets in state inuse. +# TYPE node_sockstat_UDPLITE6_inuse gauge +node_sockstat_UDPLITE6_inuse 0 # HELP node_sockstat_UDPLITE_inuse Number of UDPLITE sockets in state inuse. # TYPE node_sockstat_UDPLITE_inuse gauge node_sockstat_UDPLITE_inuse 0 @@ -2614,7 +2632,7 @@ node_sockstat_UDP_mem 0 # HELP node_sockstat_UDP_mem_bytes Number of UDP sockets in state mem_bytes. # TYPE node_sockstat_UDP_mem_bytes gauge node_sockstat_UDP_mem_bytes 0 -# HELP node_sockstat_sockets_used Number of sockets sockets in state used. +# HELP node_sockstat_sockets_used Number of IPv4 sockets in use. # TYPE node_sockstat_sockets_used gauge node_sockstat_sockets_used 229 # HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. diff --git a/collector/fixtures/proc/net/sockstat6 b/collector/fixtures/proc/net/sockstat6 new file mode 100644 index 0000000000..de5806d8d1 --- /dev/null +++ b/collector/fixtures/proc/net/sockstat6 @@ -0,0 +1,5 @@ +TCP6: inuse 17 +UDP6: inuse 9 +UDPLITE6: inuse 0 +RAW6: inuse 1 +FRAG6: inuse 0 memory 0 diff --git a/collector/fixtures/proc/net/sockstat_rhe4 b/collector/fixtures/proc/net/sockstat_rhe4 deleted file mode 100644 index 1e178f366c..0000000000 --- a/collector/fixtures/proc/net/sockstat_rhe4 +++ /dev/null @@ -1,5 +0,0 @@ -sockets: used 229 -TCP: inuse 4 orphan 0 tw 4 alloc 17 mem 1 -UDP: inuse 0 -RAW: inuse 0 -FRAG: inuse 0 memory 0 diff --git a/collector/sockstat_linux.go b/collector/sockstat_linux.go index 92847be4ec..d6c4c72a93 100644 --- a/collector/sockstat_linux.go +++ b/collector/sockstat_linux.go @@ -16,14 +16,12 @@ package collector import ( - "bufio" "fmt" - "io" "os" - "strconv" - "strings" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" + "github.com/prometheus/procfs" ) const ( @@ -45,78 +43,138 @@ func NewSockStatCollector() (Collector, error) { } func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { - sockStats, err := getSockStats(procFilePath("net/sockstat")) + fs, err := procfs.NewFS(*procPath) if err != nil { - return fmt.Errorf("couldn't get sockstats: %s", err) + return fmt.Errorf("failed to open procfs: %v", err) } - for protocol, protocolStats := range sockStats { - for name, value := range protocolStats { - v, err := strconv.ParseFloat(value, 64) - if err != nil { - return fmt.Errorf("invalid value %s in sockstats: %s", value, err) - } - ch <- prometheus.MustNewConstMetric( - prometheus.NewDesc( - prometheus.BuildFQName(namespace, sockStatSubsystem, protocol+"_"+name), - fmt.Sprintf("Number of %s sockets in state %s.", protocol, name), - nil, nil, - ), - prometheus.GaugeValue, v, - ) - } + + // If IPv4 and/or IPv6 are disabled on this kernel, handle it gracefully. + stat4, err := fs.NetSockstat() + switch { + case err == nil: + case os.IsNotExist(err): + log.Debug("IPv4 sockstat statistics not found, skipping") + default: + return fmt.Errorf("failed to get IPv4 sockstat data: %v", err) } - return err -} -func getSockStats(fileName string) (map[string]map[string]string, error) { - file, err := os.Open(fileName) - if err != nil { - return nil, err + stat6, err := fs.NetSockstat6() + switch { + case err == nil: + case os.IsNotExist(err): + log.Debug("IPv6 sockstat statistics not found, skipping") + default: + return fmt.Errorf("failed to get IPv6 sockstat data: %v", err) + } + + stats := []struct { + isIPv6 bool + stat *procfs.NetSockstat + }{ + { + stat: stat4, + }, + { + isIPv6: true, + stat: stat6, + }, } - defer file.Close() - return parseSockStats(file, fileName) + for _, s := range stats { + c.update(ch, s.isIPv6, s.stat) + } + + return nil } -func parseSockStats(r io.Reader, fileName string) (map[string]map[string]string, error) { - var ( - sockStat = map[string]map[string]string{} - scanner = bufio.NewScanner(r) - ) - - for scanner.Scan() { - line := strings.Split(scanner.Text(), " ") - // Remove trailing ':'. - protocol := line[0][:len(line[0])-1] - sockStat[protocol] = map[string]string{} - - for i := 1; i < len(line) && i+1 < len(line); i++ { - sockStat[protocol][line[i]] = line[i+1] - i++ - } +func (c *sockStatCollector) update(ch chan<- prometheus.Metric, isIPv6 bool, s *procfs.NetSockstat) { + if s == nil { + // IPv6 disabled or similar; nothing to do. + return } - if err := scanner.Err(); err != nil { - return nil, err + + // If sockstat contains the number of used sockets, export it. + if !isIPv6 && s.Used != nil { + // TODO: this must be updated if sockstat6 ever exports this data. + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, sockStatSubsystem, "sockets_used"), + "Number of IPv4 sockets in use.", + nil, + nil, + ), + prometheus.GaugeValue, + float64(*s.Used), + ) } - // The mem metrics is the count of pages used. Multiply the mem metrics by - // the page size from the kernel to get the number of bytes used. - // - // Update the TCP mem from page count to bytes. - pageCount, err := strconv.Atoi(sockStat["TCP"]["mem"]) - if err != nil { - return nil, fmt.Errorf("invalid value %s in sockstats: %s", sockStat["TCP"]["mem"], err) + // A name and optional value for a sockstat metric. + type ssPair struct { + name string + v *int } - sockStat["TCP"]["mem_bytes"] = strconv.Itoa(pageCount * pageSize) - // Update the UDP mem from page count to bytes. - if udpMem := sockStat["UDP"]["mem"]; udpMem != "" { - pageCount, err = strconv.Atoi(udpMem) - if err != nil { - return nil, fmt.Errorf("invalid value %s in sockstats: %s", sockStat["UDP"]["mem"], err) + // Previously these metric names were generated directly from the file output. + // In order to keep the same level of compatibility, we must map the fields + // to their correct names. + for _, p := range s.Protocols { + pairs := []ssPair{ + { + name: "inuse", + v: &p.InUse, + }, + { + name: "orphan", + v: p.Orphan, + }, + { + name: "tw", + v: p.TW, + }, + { + name: "alloc", + v: p.Alloc, + }, + { + name: "mem", + v: p.Mem, + }, + { + name: "memory", + v: p.Memory, + }, } - sockStat["UDP"]["mem_bytes"] = strconv.Itoa(pageCount * pageSize) - } - return sockStat, nil + // Also export mem_bytes values for sockets which have a mem value + // stored in pages. + if p.Mem != nil { + v := *p.Mem * pageSize + pairs = append(pairs, ssPair{ + name: "mem_bytes", + v: &v, + }) + } + + for _, pair := range pairs { + if pair.v == nil { + // This value is not set for this protocol; nothing to do. + continue + } + + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName( + namespace, + sockStatSubsystem, + fmt.Sprintf("%s_%s", p.Protocol, pair.name), + ), + fmt.Sprintf("Number of %s sockets in state %s.", p.Protocol, pair.name), + nil, + nil, + ), + prometheus.GaugeValue, + float64(*pair.v), + ) + } + } } diff --git a/collector/sockstat_linux_test.go b/collector/sockstat_linux_test.go deleted file mode 100644 index 70bedba900..0000000000 --- a/collector/sockstat_linux_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015 The Prometheus Authors -// 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 collector - -import ( - "os" - "strconv" - "testing" -) - -func TestSockStats(t *testing.T) { - testSockStats(t, "fixtures/proc/net/sockstat") - testSockStats(t, "fixtures/proc/net/sockstat_rhe4") -} - -func testSockStats(t *testing.T, fixture string) { - file, err := os.Open(fixture) - if err != nil { - t.Fatal(err) - } - - defer file.Close() - - sockStats, err := parseSockStats(file, fixture) - if err != nil { - t.Fatal(err) - } - - if want, got := "229", sockStats["sockets"]["used"]; want != got { - t.Errorf("want sockstat sockets used %s, got %s", want, got) - } - - if want, got := "4", sockStats["TCP"]["tw"]; want != got { - t.Errorf("want sockstat TCP tw %s, got %s", want, got) - } - - if want, got := "17", sockStats["TCP"]["alloc"]; want != got { - t.Errorf("want sockstat TCP alloc %s, got %s", want, got) - } - - // The test file has 1 for TCP mem, which is one page. So we should get the - // page size in bytes back from sockstat_linux. We get the page size from - // os here because this value can change from system to system. The value is - // 4096 by default from linux 2.4 onward. - if want, got := strconv.Itoa(os.Getpagesize()), sockStats["TCP"]["mem_bytes"]; want != got { - t.Errorf("want sockstat TCP mem_bytes %s, got %s", want, got) - } -} diff --git a/go.mod b/go.mod index c0ca32283e..078e3bbf06 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/prometheus/client_golang v1.0.0 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 github.com/prometheus/common v0.7.0 - github.com/prometheus/procfs v0.0.7 + github.com/prometheus/procfs v0.0.8 github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745 github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a go.uber.org/atomic v1.3.2 // indirect diff --git a/go.sum b/go.sum index e8a8f943a5..230ca21e15 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.7 h1:RS5GAlMbnkWkhs4+bPocMTmGjYkuCY5djjqEDdXOhcQ= -github.com/prometheus/procfs v0.0.7/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745 h1:IuH7WumZNax0D+rEqmy2TyhKCzrtMGqbZO0b8rO00JA= github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff --git a/vendor/github.com/prometheus/procfs/.golangci.yml b/vendor/github.com/prometheus/procfs/.golangci.yml index 438ca92eca..7c4ce1fa84 100644 --- a/vendor/github.com/prometheus/procfs/.golangci.yml +++ b/vendor/github.com/prometheus/procfs/.golangci.yml @@ -1,6 +1,4 @@ -# Run only staticcheck for now. Additional linters will be enabled one-by-one. linters: enable: - staticcheck - govet - disable-all: true diff --git a/vendor/github.com/prometheus/procfs/fixtures.ttar b/vendor/github.com/prometheus/procfs/fixtures.ttar index 38b71fe324..c50a18ace4 100644 --- a/vendor/github.com/prometheus/procfs/fixtures.ttar +++ b/vendor/github.com/prometheus/procfs/fixtures.ttar @@ -1801,6 +1801,25 @@ proc4 2 2 10853 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/net/sockstat +Lines: 6 +sockets: used 1602 +TCP: inuse 35 orphan 0 tw 4 alloc 59 mem 22 +UDP: inuse 12 mem 62 +UDPLITE: inuse 0 +RAW: inuse 0 +FRAG: inuse 0 memory 0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/net/sockstat6 +Lines: 5 +TCP6: inuse 17 +UDP6: inuse 9 +UDPLITE6: inuse 0 +RAW6: inuse 1 +FRAG6: inuse 0 memory 0 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/proc/net/softnet_stat Lines: 1 00015c73 00020e76 F0000769 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 diff --git a/vendor/github.com/prometheus/procfs/internal/util/valueparser.go b/vendor/github.com/prometheus/procfs/internal/util/valueparser.go index ac93cb42d2..fe2355d3c6 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/valueparser.go +++ b/vendor/github.com/prometheus/procfs/internal/util/valueparser.go @@ -33,6 +33,9 @@ func NewValueParser(v string) *ValueParser { return &ValueParser{v: v} } +// Int interprets the underlying value as an int and returns that value. +func (vp *ValueParser) Int() int { return int(vp.int64()) } + // PInt64 interprets the underlying value as an int64 and returns a pointer to // that value. func (vp *ValueParser) PInt64() *int64 { @@ -40,16 +43,27 @@ func (vp *ValueParser) PInt64() *int64 { return nil } + v := vp.int64() + return &v +} + +// int64 interprets the underlying value as an int64 and returns that value. +// TODO: export if/when necessary. +func (vp *ValueParser) int64() int64 { + if vp.err != nil { + return 0 + } + // A base value of zero makes ParseInt infer the correct base using the // string's prefix, if any. const base = 0 v, err := strconv.ParseInt(vp.v, base, 64) if err != nil { vp.err = err - return nil + return 0 } - return &v + return v } // PUInt64 interprets the underlying value as an uint64 and returns a pointer to diff --git a/vendor/github.com/prometheus/procfs/meminfo.go b/vendor/github.com/prometheus/procfs/meminfo.go index dd630c0480..50dab4bcd5 100644 --- a/vendor/github.com/prometheus/procfs/meminfo.go +++ b/vendor/github.com/prometheus/procfs/meminfo.go @@ -16,6 +16,8 @@ package procfs import ( "bufio" "bytes" + "fmt" + "io" "strconv" "strings" @@ -143,311 +145,133 @@ type Meminfo struct { // Meminfo returns an information about current kernel/system memory statistics. // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt func (fs FS) Meminfo() (Meminfo, error) { - data, err := util.ReadFileNoStat(fs.proc.Path("meminfo")) + b, err := util.ReadFileNoStat(fs.proc.Path("meminfo")) if err != nil { return Meminfo{}, err } - return parseMemInfo(data) + + m, err := parseMemInfo(bytes.NewReader(b)) + if err != nil { + return Meminfo{}, fmt.Errorf("failed to parse meminfo: %v", err) + } + + return *m, nil } -func parseMemInfo(info []byte) (m Meminfo, err error) { - scanner := bufio.NewScanner(bytes.NewReader(info)) +func parseMemInfo(r io.Reader) (*Meminfo, error) { + var m Meminfo + s := bufio.NewScanner(r) + for s.Scan() { + // Each line has at least a name and value; we ignore the unit. + fields := strings.Fields(s.Text()) + if len(fields) < 2 { + return nil, fmt.Errorf("malformed meminfo line: %q", s.Text()) + } - var line string - for scanner.Scan() { - line = scanner.Text() + v, err := strconv.ParseUint(fields[1], 0, 64) + if err != nil { + return nil, err + } - field := strings.Fields(line) - switch field[0] { + switch fields[0] { case "MemTotal:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.MemTotal = v case "MemFree:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.MemFree = v case "MemAvailable:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.MemAvailable = v case "Buffers:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Buffers = v case "Cached:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Cached = v case "SwapCached:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.SwapCached = v case "Active:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Active = v case "Inactive:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Inactive = v case "Active(anon):": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.ActiveAnon = v case "Inactive(anon):": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.InactiveAnon = v case "Active(file):": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.ActiveFile = v case "Inactive(file):": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.InactiveFile = v case "Unevictable:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Unevictable = v case "Mlocked:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Mlocked = v case "SwapTotal:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.SwapTotal = v case "SwapFree:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.SwapFree = v case "Dirty:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Dirty = v case "Writeback:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Writeback = v case "AnonPages:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.AnonPages = v case "Mapped:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Mapped = v case "Shmem:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Shmem = v case "Slab:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Slab = v case "SReclaimable:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.SReclaimable = v case "SUnreclaim:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.SUnreclaim = v case "KernelStack:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.KernelStack = v case "PageTables:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.PageTables = v case "NFS_Unstable:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.NFSUnstable = v case "Bounce:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Bounce = v case "WritebackTmp:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.WritebackTmp = v case "CommitLimit:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.CommitLimit = v case "Committed_AS:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.CommittedAS = v case "VmallocTotal:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.VmallocTotal = v case "VmallocUsed:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.VmallocUsed = v case "VmallocChunk:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.VmallocChunk = v case "HardwareCorrupted:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.HardwareCorrupted = v case "AnonHugePages:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.AnonHugePages = v case "ShmemHugePages:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.ShmemHugePages = v case "ShmemPmdMapped:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.ShmemPmdMapped = v case "CmaTotal:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.CmaTotal = v case "CmaFree:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.CmaFree = v case "HugePages_Total:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.HugePagesTotal = v case "HugePages_Free:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.HugePagesFree = v case "HugePages_Rsvd:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.HugePagesRsvd = v case "HugePages_Surp:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.HugePagesSurp = v case "Hugepagesize:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.Hugepagesize = v case "DirectMap4k:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.DirectMap4k = v case "DirectMap2M:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.DirectMap2M = v case "DirectMap1G:": - v, err := strconv.ParseUint(field[1], 0, 64) - if err != nil { - return Meminfo{}, err - } m.DirectMap1G = v } } - return m, nil + + return &m, nil } diff --git a/vendor/github.com/prometheus/procfs/net_sockstat.go b/vendor/github.com/prometheus/procfs/net_sockstat.go new file mode 100644 index 0000000000..f91ef55237 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/net_sockstat.go @@ -0,0 +1,163 @@ +// Copyright 2019 The Prometheus Authors +// 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 procfs + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// A NetSockstat contains the output of /proc/net/sockstat{,6} for IPv4 or IPv6, +// respectively. +type NetSockstat struct { + // Used is non-nil for IPv4 sockstat results, but nil for IPv6. + Used *int + Protocols []NetSockstatProtocol +} + +// A NetSockstatProtocol contains statistics about a given socket protocol. +// Pointer fields indicate that the value may or may not be present on any +// given protocol. +type NetSockstatProtocol struct { + Protocol string + InUse int + Orphan *int + TW *int + Alloc *int + Mem *int + Memory *int +} + +// NetSockstat retrieves IPv4 socket statistics. +func (fs FS) NetSockstat() (*NetSockstat, error) { + return readSockstat(fs.proc.Path("net", "sockstat")) +} + +// NetSockstat6 retrieves IPv6 socket statistics. +// +// If IPv6 is disabled on this kernel, the returned error can be checked with +// os.IsNotExist. +func (fs FS) NetSockstat6() (*NetSockstat, error) { + return readSockstat(fs.proc.Path("net", "sockstat6")) +} + +// readSockstat opens and parses a NetSockstat from the input file. +func readSockstat(name string) (*NetSockstat, error) { + // This file is small and can be read with one syscall. + b, err := util.ReadFileNoStat(name) + if err != nil { + // Do not wrap this error so the caller can detect os.IsNotExist and + // similar conditions. + return nil, err + } + + stat, err := parseSockstat(bytes.NewReader(b)) + if err != nil { + return nil, fmt.Errorf("failed to read sockstats from %q: %v", name, err) + } + + return stat, nil +} + +// parseSockstat reads the contents of a sockstat file and parses a NetSockstat. +func parseSockstat(r io.Reader) (*NetSockstat, error) { + var stat NetSockstat + s := bufio.NewScanner(r) + for s.Scan() { + // Expect a minimum of a protocol and one key/value pair. + fields := strings.Split(s.Text(), " ") + if len(fields) < 3 { + return nil, fmt.Errorf("malformed sockstat line: %q", s.Text()) + } + + // The remaining fields are key/value pairs. + kvs, err := parseSockstatKVs(fields[1:]) + if err != nil { + return nil, fmt.Errorf("error parsing sockstat key/value pairs from %q: %v", s.Text(), err) + } + + // The first field is the protocol. We must trim its colon suffix. + proto := strings.TrimSuffix(fields[0], ":") + switch proto { + case "sockets": + // Special case: IPv4 has a sockets "used" key/value pair that we + // embed at the top level of the structure. + used := kvs["used"] + stat.Used = &used + default: + // Parse all other lines as individual protocols. + nsp := parseSockstatProtocol(kvs) + nsp.Protocol = proto + stat.Protocols = append(stat.Protocols, nsp) + } + } + + if err := s.Err(); err != nil { + return nil, err + } + + return &stat, nil +} + +// parseSockstatKVs parses a string slice into a map of key/value pairs. +func parseSockstatKVs(kvs []string) (map[string]int, error) { + if len(kvs)%2 != 0 { + return nil, errors.New("odd number of fields in key/value pairs") + } + + // Iterate two values at a time to gather key/value pairs. + out := make(map[string]int, len(kvs)/2) + for i := 0; i < len(kvs); i += 2 { + vp := util.NewValueParser(kvs[i+1]) + out[kvs[i]] = vp.Int() + + if err := vp.Err(); err != nil { + return nil, err + } + } + + return out, nil +} + +// parseSockstatProtocol parses a NetSockstatProtocol from the input kvs map. +func parseSockstatProtocol(kvs map[string]int) NetSockstatProtocol { + var nsp NetSockstatProtocol + for k, v := range kvs { + // Capture the range variable to ensure we get unique pointers for + // each of the optional fields. + v := v + switch k { + case "inuse": + nsp.InUse = v + case "orphan": + nsp.Orphan = &v + case "tw": + nsp.TW = &v + case "alloc": + nsp.Alloc = &v + case "mem": + nsp.Mem = &v + case "memory": + nsp.Memory = &v + } + } + + return nsp +} diff --git a/vendor/github.com/prometheus/procfs/net_unix.go b/vendor/github.com/prometheus/procfs/net_unix.go index 240340a83a..93bd58f809 100644 --- a/vendor/github.com/prometheus/procfs/net_unix.go +++ b/vendor/github.com/prometheus/procfs/net_unix.go @@ -207,10 +207,6 @@ func (u NetUnix) parseUsers(hexStr string) (uint64, error) { return strconv.ParseUint(hexStr, 16, 32) } -func (u NetUnix) parseProtocol(hexStr string) (uint64, error) { - return strconv.ParseUint(hexStr, 16, 32) -} - func (u NetUnix) parseType(hexStr string) (NetUnixType, error) { typ, err := strconv.ParseUint(hexStr, 16, 16) if err != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 484d28b4a7..4d02fdae6b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -47,7 +47,7 @@ github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/log github.com/prometheus/common/model github.com/prometheus/common/version -# github.com/prometheus/procfs v0.0.7 +# github.com/prometheus/procfs v0.0.8 github.com/prometheus/procfs github.com/prometheus/procfs/bcache github.com/prometheus/procfs/internal/fs From 0d9d7e961a9c5784faea6f279d975845a484e927 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Mon, 25 Nov 2019 21:50:00 +0100 Subject: [PATCH 15/35] Update CHANGELOG Add/update entries for recent merged PRs. Signed-off-by: Ben Kochie --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc0c2d08e..9094c4d3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,11 +21,13 @@ * [FEATURE] Add new metric node_cpu_info #1489 * [FEATURE] Add new thermal_zone collector #1425 * [FEATURE] Add new cooling_device metrics to thermal zone collector #1445 +* [ENHANCEMENT] Collect InfiniBand port state and physical state #1357 * [ENHANCEMENT] Include additional XFS runtime statistics. #1423 * [ENHANCEMENT] Report non-fatal collection errors in the exporter metric. #1439 * [ENHANCEMENT] Expose IPVS firewall mark as a label #1455 * [ENHANCEMENT] Add check for systemd version before attempting to query certain metrics. #1413 -* [ENHANCEMENT] The sockstat collector now exposes IPv6 statistics in addition to the existing IPv4 support. +* [ENHANCEMENT] Add new counters for flush requests in Linux 5.5 #1548 +* [ENHANCEMENT] The sockstat collector now exposes IPv6 statistics in addition to the existing IPv4 support. #1552 * [BUGFIX] Renamed label `state` to `name` on `node_systemd_service_restart_total`. #1393 * [BUGFIX] Fix netdev nil reference on Darwin #1414 * [BUGFIX] Strip path.rootfs from mountpoint labels #1421 From 2cae917bb7e0b6379221e8a24da012b16e63d661 Mon Sep 17 00:00:00 2001 From: Matthieu Guegan Date: Mon, 2 Dec 2019 13:41:58 +0100 Subject: [PATCH 16/35] fix OpenBSD cache memory information (#1542) This will now use `bcstats.numbufpages` instead of `uvmexp.vnodepages`. Inspired by OpenBSD's `src/usr.bin/top` Signed-off-by: Matthieu Guegan --- collector/meminfo_openbsd.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/collector/meminfo_openbsd.go b/collector/meminfo_openbsd.go index 4915c01285..75702ef856 100644 --- a/collector/meminfo_openbsd.go +++ b/collector/meminfo_openbsd.go @@ -23,6 +23,7 @@ import ( /* #include #include +#include #include int @@ -37,22 +38,39 @@ sysctl_uvmexp(struct uvmexp *uvmexp) return 0; } +int +sysctl_bcstats(struct bcachestats *bcstats) +{ + static int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT}; + size_t sz = sizeof(struct bcachestats); + + if(sysctl(bcstats_mib, 3, bcstats, &sz, NULL, 0) < 0) + return -1; + + return 0; +} + */ import "C" func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { var uvmexp C.struct_uvmexp + var bcstats C.struct_bcachestats if _, err := C.sysctl_uvmexp(&uvmexp); err != nil { return nil, fmt.Errorf("sysctl CTL_VM VM_UVMEXP failed: %v", err) } + if _, err := C.sysctl_bcstats(&bcstats); err != nil { + return nil, fmt.Errorf("sysctl CTL_VFS VFS_GENERIC VFS_BCACHESTAT failed: %v", err) + } + ps := float64(uvmexp.pagesize) // see uvm(9) return map[string]float64{ "active_bytes": ps * float64(uvmexp.active), - "cache_bytes": ps * float64(uvmexp.vnodepages), + "cache_bytes": ps * float64(bcstats.numbufpages), "free_bytes": ps * float64(uvmexp.free), "inactive_bytes": ps * float64(uvmexp.inactive), "size_bytes": ps * float64(uvmexp.npages), From da0ae76a91dc57bc313ce511633d50d68b429a1a Mon Sep 17 00:00:00 2001 From: Julian Kornberger Date: Thu, 28 Nov 2019 15:48:35 +0100 Subject: [PATCH 17/35] Use Golang 1.13 for all CI jobs Signed-off-by: Julian Kornberger --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9fe2779823..57cdc2e667 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: image: ubuntu-1604:201903-01 environment: - DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.11-base + DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.13-base REPO_PATH: github.com/prometheus/node_exporter steps: From 043fecbfd8f0d5f8547b5a218246c86af783f3d4 Mon Sep 17 00:00:00 2001 From: Julian Kornberger Date: Fri, 29 Nov 2019 14:51:31 +0100 Subject: [PATCH 18/35] Wrap errors in the Go 1.13 way Signed-off-by: Julian Kornberger --- collector/bcache_linux.go | 4 ++-- collector/buddyinfo.go | 2 +- collector/cpu_linux.go | 2 +- collector/cpufreq_linux.go | 2 +- collector/infiniband_linux.go | 2 +- collector/ipvs_linux.go | 2 +- collector/mdadm_linux.go | 2 +- collector/meminfo_openbsd.go | 2 +- collector/mountstats_linux.go | 8 ++++---- collector/netclass_linux.go | 2 +- collector/nfs_linux.go | 4 ++-- collector/nfsd_linux.go | 4 ++-- collector/pressure_linux.go | 2 +- collector/processes_linux.go | 2 +- collector/schedstat_linux.go | 2 +- collector/sockstat_linux.go | 6 +++--- collector/stat_linux.go | 2 +- collector/thermal_zone_linux.go | 2 +- collector/timex.go | 2 +- collector/wifi_linux.go | 4 ++-- collector/xfs_linux.go | 4 ++-- 21 files changed, 31 insertions(+), 31 deletions(-) diff --git a/collector/bcache_linux.go b/collector/bcache_linux.go index b940b44f6d..da4eab5757 100644 --- a/collector/bcache_linux.go +++ b/collector/bcache_linux.go @@ -36,7 +36,7 @@ type bcacheCollector struct { func NewBcacheCollector() (Collector, error) { fs, err := bcache.NewFS(*sysPath) if err != nil { - return nil, fmt.Errorf("failed to open sysfs: %v", err) + return nil, fmt.Errorf("failed to open sysfs: %w", err) } return &bcacheCollector{ @@ -49,7 +49,7 @@ func NewBcacheCollector() (Collector, error) { func (c *bcacheCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.Stats() if err != nil { - return fmt.Errorf("failed to retrieve bcache stats: %v", err) + return fmt.Errorf("failed to retrieve bcache stats: %w", err) } for _, s := range stats { diff --git a/collector/buddyinfo.go b/collector/buddyinfo.go index f6dd5dbf4f..316766ed05 100644 --- a/collector/buddyinfo.go +++ b/collector/buddyinfo.go @@ -47,7 +47,7 @@ func NewBuddyinfoCollector() (Collector, error) { ) fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &buddyinfoCollector{fs, desc}, nil } diff --git a/collector/cpu_linux.go b/collector/cpu_linux.go index c6404c3cac..c946520887 100644 --- a/collector/cpu_linux.go +++ b/collector/cpu_linux.go @@ -47,7 +47,7 @@ func init() { func NewCPUCollector() (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &cpuCollector{ fs: fs, diff --git a/collector/cpufreq_linux.go b/collector/cpufreq_linux.go index 22e0365462..c4de4d5c7f 100644 --- a/collector/cpufreq_linux.go +++ b/collector/cpufreq_linux.go @@ -40,7 +40,7 @@ func init() { func NewCPUFreqCollector() (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { - return nil, fmt.Errorf("failed to open sysfs: %v", err) + return nil, fmt.Errorf("failed to open sysfs: %w", err) } return &cpuFreqCollector{ diff --git a/collector/infiniband_linux.go b/collector/infiniband_linux.go index 828e40e5ce..52bf9a20e0 100644 --- a/collector/infiniband_linux.go +++ b/collector/infiniband_linux.go @@ -40,7 +40,7 @@ func NewInfiniBandCollector() (Collector, error) { i.fs, err = sysfs.NewFS(*sysPath) if err != nil { - return nil, fmt.Errorf("failed to open sysfs: %v", err) + return nil, fmt.Errorf("failed to open sysfs: %w", err) } // Detailed description for all metrics. diff --git a/collector/ipvs_linux.go b/collector/ipvs_linux.go index d2cc480d51..6ac48b1764 100644 --- a/collector/ipvs_linux.go +++ b/collector/ipvs_linux.go @@ -59,7 +59,7 @@ func newIPVSCollector() (*ipvsCollector, error) { c.fs, err = procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } c.connections = typedDesc{prometheus.NewDesc( diff --git a/collector/mdadm_linux.go b/collector/mdadm_linux.go index ce130f5040..aced6cdfca 100644 --- a/collector/mdadm_linux.go +++ b/collector/mdadm_linux.go @@ -94,7 +94,7 @@ func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) error { fs, errFs := procfs.NewFS(*procPath) if errFs != nil { - return fmt.Errorf("failed to open procfs: %v", errFs) + return fmt.Errorf("failed to open procfs: %w", errFs) } mdStats, err := fs.MDStat() diff --git a/collector/meminfo_openbsd.go b/collector/meminfo_openbsd.go index 75702ef856..073fbe7d03 100644 --- a/collector/meminfo_openbsd.go +++ b/collector/meminfo_openbsd.go @@ -58,7 +58,7 @@ func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { var bcstats C.struct_bcachestats if _, err := C.sysctl_uvmexp(&uvmexp); err != nil { - return nil, fmt.Errorf("sysctl CTL_VM VM_UVMEXP failed: %v", err) + return nil, fmt.Errorf("sysctl CTL_VM VM_UVMEXP failed: %w", err) } if _, err := C.sysctl_bcstats(&bcstats); err != nil { diff --git a/collector/mountstats_linux.go b/collector/mountstats_linux.go index 605b3db2e2..eeb01b7ab5 100644 --- a/collector/mountstats_linux.go +++ b/collector/mountstats_linux.go @@ -108,12 +108,12 @@ func init() { func NewMountStatsCollector() (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } proc, err := fs.Self() if err != nil { - return nil, fmt.Errorf("failed to open /proc/self: %v", err) + return nil, fmt.Errorf("failed to open /proc/self: %w", err) } const ( @@ -505,12 +505,12 @@ func NewMountStatsCollector() (Collector, error) { func (c *mountStatsCollector) Update(ch chan<- prometheus.Metric) error { mounts, err := c.proc.MountStats() if err != nil { - return fmt.Errorf("failed to parse mountstats: %v", err) + return fmt.Errorf("failed to parse mountstats: %w", err) } mountsInfo, err := c.proc.MountInfo() if err != nil { - return fmt.Errorf("failed to parse mountinfo: %v", err) + return fmt.Errorf("failed to parse mountinfo: %w", err) } // store all seen nfsDeviceIdentifiers for deduplication diff --git a/collector/netclass_linux.go b/collector/netclass_linux.go index 040ded3ab1..af4b48799b 100644 --- a/collector/netclass_linux.go +++ b/collector/netclass_linux.go @@ -44,7 +44,7 @@ func init() { func NewNetClassCollector() (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { - return nil, fmt.Errorf("failed to open sysfs: %v", err) + return nil, fmt.Errorf("failed to open sysfs: %w", err) } pattern := regexp.MustCompile(*netclassIgnoredDevices) return &netClassCollector{ diff --git a/collector/nfs_linux.go b/collector/nfs_linux.go index b5f6c64e8b..05b241e517 100644 --- a/collector/nfs_linux.go +++ b/collector/nfs_linux.go @@ -45,7 +45,7 @@ func init() { func NewNfsCollector() (Collector, error) { fs, err := nfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &nfsCollector{ @@ -96,7 +96,7 @@ func (c *nfsCollector) Update(ch chan<- prometheus.Metric) error { log.Debugf("Not collecting NFS metrics: %s", err) return nil } - return fmt.Errorf("failed to retrieve nfs stats: %v", err) + return fmt.Errorf("failed to retrieve nfs stats: %w", err) } c.updateNFSNetworkStats(ch, &stats.Network) diff --git a/collector/nfsd_linux.go b/collector/nfsd_linux.go index fe6ff710ce..b5e7cf90a1 100644 --- a/collector/nfsd_linux.go +++ b/collector/nfsd_linux.go @@ -41,7 +41,7 @@ const ( func NewNFSdCollector() (Collector, error) { fs, err := nfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &nfsdCollector{ @@ -62,7 +62,7 @@ func (c *nfsdCollector) Update(ch chan<- prometheus.Metric) error { log.Debugf("Not collecting NFSd metrics: %s", err) return nil } - return fmt.Errorf("failed to retrieve nfsd stats: %v", err) + return fmt.Errorf("failed to retrieve nfsd stats: %w", err) } c.updateNFSdReplyCacheStats(ch, &stats.ReplyCache) diff --git a/collector/pressure_linux.go b/collector/pressure_linux.go index df8c2660c0..2ed7ffb958 100644 --- a/collector/pressure_linux.go +++ b/collector/pressure_linux.go @@ -45,7 +45,7 @@ func init() { func NewPressureStatsCollector() (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &pressureStatsCollector{ diff --git a/collector/processes_linux.go b/collector/processes_linux.go index 52a47df2a1..bb18a69098 100644 --- a/collector/processes_linux.go +++ b/collector/processes_linux.go @@ -41,7 +41,7 @@ func init() { func NewProcessStatCollector() (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } subsystem := "processes" return &processCollector{ diff --git a/collector/schedstat_linux.go b/collector/schedstat_linux.go index ae649ec375..1c91a478b3 100644 --- a/collector/schedstat_linux.go +++ b/collector/schedstat_linux.go @@ -49,7 +49,7 @@ var ( func NewSchedstatCollector() (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &schedstatCollector{fs: fs}, nil diff --git a/collector/sockstat_linux.go b/collector/sockstat_linux.go index d6c4c72a93..fceb0edb2b 100644 --- a/collector/sockstat_linux.go +++ b/collector/sockstat_linux.go @@ -45,7 +45,7 @@ func NewSockStatCollector() (Collector, error) { func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { fs, err := procfs.NewFS(*procPath) if err != nil { - return fmt.Errorf("failed to open procfs: %v", err) + return fmt.Errorf("failed to open procfs: %w", err) } // If IPv4 and/or IPv6 are disabled on this kernel, handle it gracefully. @@ -55,7 +55,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { case os.IsNotExist(err): log.Debug("IPv4 sockstat statistics not found, skipping") default: - return fmt.Errorf("failed to get IPv4 sockstat data: %v", err) + return fmt.Errorf("failed to get IPv4 sockstat data: %w", err) } stat6, err := fs.NetSockstat6() @@ -64,7 +64,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { case os.IsNotExist(err): log.Debug("IPv6 sockstat statistics not found, skipping") default: - return fmt.Errorf("failed to get IPv6 sockstat data: %v", err) + return fmt.Errorf("failed to get IPv6 sockstat data: %w", err) } stats := []struct { diff --git a/collector/stat_linux.go b/collector/stat_linux.go index cd79e38ccc..5e51d9697f 100644 --- a/collector/stat_linux.go +++ b/collector/stat_linux.go @@ -41,7 +41,7 @@ func init() { func NewStatCollector() (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { - return nil, fmt.Errorf("failed to open procfs: %v", err) + return nil, fmt.Errorf("failed to open procfs: %w", err) } return &statCollector{ fs: fs, diff --git a/collector/thermal_zone_linux.go b/collector/thermal_zone_linux.go index 13606daadc..b8a65cf8d0 100644 --- a/collector/thermal_zone_linux.go +++ b/collector/thermal_zone_linux.go @@ -40,7 +40,7 @@ func init() { func NewThermalZoneCollector() (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { - return nil, fmt.Errorf("failed to open sysfs: %v", err) + return nil, fmt.Errorf("failed to open sysfs: %w", err) } return &thermalZoneCollector{ diff --git a/collector/timex.go b/collector/timex.go index 09cc4a6adb..7ef6920f7c 100644 --- a/collector/timex.go +++ b/collector/timex.go @@ -160,7 +160,7 @@ func (c *timexCollector) Update(ch chan<- prometheus.Metric) error { status, err := unix.Adjtimex(timex) if err != nil { - return fmt.Errorf("failed to retrieve adjtimex stats: %v", err) + return fmt.Errorf("failed to retrieve adjtimex stats: %w", err) } if status == timeError { diff --git a/collector/wifi_linux.go b/collector/wifi_linux.go index b9830e4842..8e0d4eef67 100644 --- a/collector/wifi_linux.go +++ b/collector/wifi_linux.go @@ -170,13 +170,13 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { return nil } - return fmt.Errorf("failed to access wifi data: %v", err) + return fmt.Errorf("failed to access wifi data: %w", err) } defer stat.Close() ifis, err := stat.Interfaces() if err != nil { - return fmt.Errorf("failed to retrieve wifi interfaces: %v", err) + return fmt.Errorf("failed to retrieve wifi interfaces: %w", err) } for _, ifi := range ifis { diff --git a/collector/xfs_linux.go b/collector/xfs_linux.go index 9a7ad98609..c92a48ce72 100644 --- a/collector/xfs_linux.go +++ b/collector/xfs_linux.go @@ -33,7 +33,7 @@ func init() { func NewXFSCollector() (Collector, error) { fs, err := xfs.NewFS(*procPath, *sysPath) if err != nil { - return nil, fmt.Errorf("failed to open sysfs: %v", err) + return nil, fmt.Errorf("failed to open sysfs: %w", err) } return &xfsCollector{ @@ -45,7 +45,7 @@ func NewXFSCollector() (Collector, error) { func (c *xfsCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.SysStats() if err != nil { - return fmt.Errorf("failed to retrieve XFS stats: %v", err) + return fmt.Errorf("failed to retrieve XFS stats: %w", err) } for _, s := range stats { From cafb12dc59f5b853bdd1167547056428eaddd353 Mon Sep 17 00:00:00 2001 From: Julian Kornberger Date: Fri, 29 Nov 2019 16:14:18 +0100 Subject: [PATCH 19/35] Add cause to error message Signed-off-by: Julian Kornberger --- collector/netdev_darwin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collector/netdev_darwin.go b/collector/netdev_darwin.go index c45e868588..dd25963057 100644 --- a/collector/netdev_darwin.go +++ b/collector/netdev_darwin.go @@ -18,7 +18,7 @@ package collector import ( "bytes" "encoding/binary" - "errors" + "fmt" "net" "regexp" "strconv" @@ -32,7 +32,7 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]ma ifs, err := net.Interfaces() if err != nil { - return nil, errors.New("net.Interfaces() failed") + return nil, fmt.Errorf("net.Interfaces() failed: %w", err) } for _, iface := range ifs { From a80b7d0bc5ee93e704bab22e7592ed8b7d65899e Mon Sep 17 00:00:00 2001 From: Peter Nicholson Date: Mon, 30 Dec 2019 01:36:10 +0100 Subject: [PATCH 20/35] Add softnet collector (#1576) Signed-off-by: Peter Nicholson --- CHANGELOG.md | 1 + README.md | 1 + collector/fixtures/e2e-64k-page-output.txt | 19 +++++ collector/fixtures/e2e-output.txt | 19 +++++ collector/fixtures/proc/net/softnet_stat | 4 + collector/softnet_linux.go | 99 ++++++++++++++++++++++ 6 files changed, 143 insertions(+) create mode 100644 collector/fixtures/proc/net/softnet_stat create mode 100644 collector/softnet_linux.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9094c4d3c3..b65a3008ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * [FEATURE] Add new metric node_cpu_info #1489 * [FEATURE] Add new thermal_zone collector #1425 * [FEATURE] Add new cooling_device metrics to thermal zone collector #1445 +* [FEATURE] Add new softnet collector #1576 * [ENHANCEMENT] Collect InfiniBand port state and physical state #1357 * [ENHANCEMENT] Include additional XFS runtime statistics. #1423 * [ENHANCEMENT] Report non-fatal collection errors in the exporter metric. #1439 diff --git a/README.md b/README.md index 7a4b9f407f..b8bb283f64 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ nfsd | Exposes NFS kernel server statistics from `/proc/net/rpc/nfsd`. This is t pressure | Exposes pressure stall statistics from `/proc/pressure/`. | Linux (kernel 4.20+ and/or [CONFIG\_PSI](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/accounting/psi.txt)) schedstat | Exposes task scheduler statistics from `/proc/schedstat`. | Linux sockstat | Exposes various statistics from `/proc/net/sockstat`. | Linux +softnet | Exposes statistics from `/proc/net/softnet_stat`. | Linux stat | Exposes various statistics from `/proc/stat`. This includes boot time, forks and interrupts. | Linux textfile | Exposes statistics read from local disk. The `--collector.textfile.directory` flag must be set. | _any_ thermal\_zone | Exposes thermal zone & cooling device statistics from `/sys/class/thermal`. | Linux diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 9f75f082a4..0e8e1e2b71 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2547,6 +2547,7 @@ node_scrape_collector_success{collector="processes"} 1 node_scrape_collector_success{collector="qdisc"} 1 node_scrape_collector_success{collector="schedstat"} 1 node_scrape_collector_success{collector="sockstat"} 1 +node_scrape_collector_success{collector="softnet"} 1 node_scrape_collector_success{collector="stat"} 1 node_scrape_collector_success{collector="textfile"} 1 node_scrape_collector_success{collector="thermal_zone"} 1 @@ -2596,6 +2597,24 @@ node_sockstat_UDP_mem_bytes 0 # HELP node_sockstat_sockets_used Number of sockets sockets in state used. # TYPE node_sockstat_sockets_used gauge node_sockstat_sockets_used 229 +# HELP node_softnet_dropped_total Number of dropped packets +# TYPE node_softnet_dropped_total counter +node_softnet_dropped_total{cpu="0"} 0 +node_softnet_dropped_total{cpu="1"} 41 +node_softnet_dropped_total{cpu="2"} 0 +node_softnet_dropped_total{cpu="3"} 0 +# HELP node_softnet_processed_total Number of processed packets +# TYPE node_softnet_processed_total counter +node_softnet_processed_total{cpu="0"} 299641 +node_softnet_processed_total{cpu="1"} 916354 +node_softnet_processed_total{cpu="2"} 5.577791e+06 +node_softnet_processed_total{cpu="3"} 3.113785e+06 +# HELP node_softnet_times_squeezed_total Number of times processing packets ran out of quota +# TYPE node_softnet_times_squeezed_total counter +node_softnet_times_squeezed_total{cpu="0"} 1 +node_softnet_times_squeezed_total{cpu="1"} 10 +node_softnet_times_squeezed_total{cpu="2"} 85 +node_softnet_times_squeezed_total{cpu="3"} 50 # HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. # TYPE node_textfile_mtime_seconds gauge # HELP node_textfile_scrape_error 1 if there was an error opening or reading a file, 0 otherwise diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index e915f923ee..463de4332c 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2568,6 +2568,7 @@ node_scrape_collector_success{collector="processes"} 1 node_scrape_collector_success{collector="qdisc"} 1 node_scrape_collector_success{collector="schedstat"} 1 node_scrape_collector_success{collector="sockstat"} 1 +node_scrape_collector_success{collector="softnet"} 1 node_scrape_collector_success{collector="stat"} 1 node_scrape_collector_success{collector="textfile"} 1 node_scrape_collector_success{collector="thermal_zone"} 1 @@ -2635,6 +2636,24 @@ node_sockstat_UDP_mem_bytes 0 # HELP node_sockstat_sockets_used Number of IPv4 sockets in use. # TYPE node_sockstat_sockets_used gauge node_sockstat_sockets_used 229 +# HELP node_softnet_dropped_total Number of dropped packets +# TYPE node_softnet_dropped_total counter +node_softnet_dropped_total{cpu="0"} 0 +node_softnet_dropped_total{cpu="1"} 41 +node_softnet_dropped_total{cpu="2"} 0 +node_softnet_dropped_total{cpu="3"} 0 +# HELP node_softnet_processed_total Number of processed packets +# TYPE node_softnet_processed_total counter +node_softnet_processed_total{cpu="0"} 299641 +node_softnet_processed_total{cpu="1"} 916354 +node_softnet_processed_total{cpu="2"} 5.577791e+06 +node_softnet_processed_total{cpu="3"} 3.113785e+06 +# HELP node_softnet_times_squeezed_total Number of times processing packets ran out of quota +# TYPE node_softnet_times_squeezed_total counter +node_softnet_times_squeezed_total{cpu="0"} 1 +node_softnet_times_squeezed_total{cpu="1"} 10 +node_softnet_times_squeezed_total{cpu="2"} 85 +node_softnet_times_squeezed_total{cpu="3"} 50 # HELP node_textfile_mtime_seconds Unixtime mtime of textfiles successfully read. # TYPE node_textfile_mtime_seconds gauge # HELP node_textfile_scrape_error 1 if there was an error opening or reading a file, 0 otherwise diff --git a/collector/fixtures/proc/net/softnet_stat b/collector/fixtures/proc/net/softnet_stat new file mode 100644 index 0000000000..d5a5a20b7b --- /dev/null +++ b/collector/fixtures/proc/net/softnet_stat @@ -0,0 +1,4 @@ +00049279 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +000dfb82 00000029 0000000a 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +00551c3f 00000000 00000055 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +002f8339 00000000 00000032 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 \ No newline at end of file diff --git a/collector/softnet_linux.go b/collector/softnet_linux.go new file mode 100644 index 0000000000..3e767cabd6 --- /dev/null +++ b/collector/softnet_linux.go @@ -0,0 +1,99 @@ +// Copyright 2019 The Prometheus Authors +// 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. + +// +build !nosoftnet + +package collector + +import ( + "fmt" + "strconv" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/procfs" +) + +type softnetCollector struct { + fs procfs.FS + processed *prometheus.Desc + dropped *prometheus.Desc + timeSqueezed *prometheus.Desc +} + +const ( + softnetSubsystem = "softnet" +) + +func init() { + registerCollector("softnet", defaultEnabled, NewSoftnetCollector) +} + +// NewSoftnetCollector returns a new Collector exposing softnet metrics. +func NewSoftnetCollector() (Collector, error) { + fs, err := procfs.NewFS(*procPath) + if err != nil { + return nil, fmt.Errorf("failed to open procfs: %w", err) + } + + return &softnetCollector{ + fs: fs, + processed: prometheus.NewDesc( + prometheus.BuildFQName(namespace, softnetSubsystem, "processed_total"), + "Number of processed packets", + []string{"cpu"}, nil, + ), + dropped: prometheus.NewDesc( + prometheus.BuildFQName(namespace, softnetSubsystem, "dropped_total"), + "Number of dropped packets", + []string{"cpu"}, nil, + ), + timeSqueezed: prometheus.NewDesc( + prometheus.BuildFQName(namespace, softnetSubsystem, "times_squeezed_total"), + "Number of times processing packets ran out of quota", + []string{"cpu"}, nil, + ), + }, nil +} + +// Update gets parsed softnet statistics using procfs. +func (c *softnetCollector) Update(ch chan<- prometheus.Metric) error { + stats, err := c.fs.GatherSoftnetStats() + if err != nil { + return fmt.Errorf("could not get softnet statistics: %s", err) + } + + for cpuNumber, cpuStats := range stats { + cpu := strconv.Itoa(cpuNumber) + + ch <- prometheus.MustNewConstMetric( + c.processed, + prometheus.CounterValue, + float64(cpuStats.Processed), + cpu, + ) + ch <- prometheus.MustNewConstMetric( + c.dropped, + prometheus.CounterValue, + float64(cpuStats.Dropped), + cpu, + ) + ch <- prometheus.MustNewConstMetric( + c.timeSqueezed, + prometheus.CounterValue, + float64(cpuStats.TimeSqueezed), + cpu, + ) + } + + return nil +} From 2477c5c67dff7e7655a9d466450235e9c9eac193 Mon Sep 17 00:00:00 2001 From: Ben Ye Date: Tue, 31 Dec 2019 11:19:37 -0500 Subject: [PATCH 21/35] switch to go-kit/log (#1575) Signed-off-by: yeya24 --- collector/arp_linux.go | 5 +- collector/bcache_linux.go | 9 +- collector/bonding_linux.go | 9 +- collector/boot_time_bsd.go | 9 +- collector/boot_time_solaris.go | 5 +- collector/buddyinfo.go | 14 +- collector/collector.go | 22 +- collector/conntrack_linux.go | 5 +- collector/cpu_darwin.go | 9 +- collector/cpu_dragonfly.go | 9 +- collector/cpu_freebsd.go | 15 +- collector/cpu_linux.go | 17 +- collector/cpu_openbsd.go | 9 +- collector/cpu_solaris.go | 9 +- collector/cpufreq_linux.go | 5 +- collector/cpufreq_solaris.go | 5 +- collector/devstat_dragonfly.go | 5 +- collector/devstat_freebsd.go | 5 +- collector/diskstats_darwin.go | 7 +- collector/diskstats_linux.go | 9 +- collector/diskstats_openbsd.go | 5 +- collector/drbd_linux.go | 13 +- collector/edac_linux.go | 5 +- collector/entropy_linux.go | 5 +- collector/exec_bsd.go | 5 +- collector/filefd_linux.go | 9 +- collector/filesystem_bsd.go | 6 +- collector/filesystem_common.go | 5 +- collector/filesystem_freebsd.go | 6 +- collector/filesystem_linux.go | 28 +- collector/filesystem_linux_test.go | 7 +- collector/hwmon_linux.go | 13 +- collector/infiniband_linux.go | 5 +- collector/interrupts_common.go | 11 +- collector/ipvs_linux.go | 13 +- collector/ipvs_linux_test.go | 5 +- collector/ksmd_linux.go | 6 +- collector/loadavg.go | 9 +- collector/logind_linux.go | 9 +- collector/mdadm_linux.go | 15 +- collector/meminfo.go | 13 +- collector/meminfo_numa_linux.go | 5 +- collector/memory_bsd.go | 5 +- collector/mountstats_linux.go | 12 +- collector/netclass_linux.go | 7 +- collector/netdev_bsd.go | 9 +- collector/netdev_common.go | 7 +- collector/netdev_darwin.go | 11 +- collector/netdev_linux.go | 13 +- collector/netdev_linux_test.go | 5 +- collector/netdev_openbsd.go | 9 +- collector/netstat_linux.go | 5 +- collector/nfs_linux.go | 9 +- collector/nfsd_linux.go | 9 +- collector/ntp.go | 5 +- collector/perf_linux.go | 33 +- collector/perf_linux_test.go | 3 +- collector/powersupplyclass.go | 5 +- collector/pressure_linux.go | 16 +- collector/processes_linux.go | 11 +- collector/processes_linux_test.go | 3 +- collector/qdisc_linux.go | 5 +- collector/runit.go | 16 +- collector/schedstat_linux.go | 8 +- collector/sockstat_linux.go | 15 +- collector/stat_linux.go | 8 +- collector/supervisord.go | 9 +- collector/systemd_linux.go | 65 +-- collector/systemd_linux_test.go | 8 +- collector/tcpstat_linux.go | 7 +- collector/textfile.go | 21 +- collector/textfile_test.go | 20 +- collector/thermal_zone_linux.go | 5 +- collector/time.go | 11 +- collector/timex.go | 5 +- collector/uname.go | 9 +- collector/vmstat_linux.go | 5 +- collector/wifi_linux.go | 18 +- collector/xfs_linux.go | 9 +- collector/zfs.go | 9 +- collector/zfs_freebsd.go | 5 +- collector/zfs_linux.go | 6 +- collector/zfs_solaris.go | 5 +- go.mod | 2 +- go.sum | 6 +- node_exporter.go | 42 +- scripts/errcheck_excludes.txt | 2 + .../{sirupsen/logrus => go-kit/kit}/LICENSE | 11 +- vendor/github.com/go-kit/kit/log/README.md | 151 ++++++ vendor/github.com/go-kit/kit/log/doc.go | 116 ++++ .../github.com/go-kit/kit/log/json_logger.go | 89 ++++ vendor/github.com/go-kit/kit/log/level/doc.go | 22 + .../github.com/go-kit/kit/log/level/level.go | 205 ++++++++ vendor/github.com/go-kit/kit/log/log.go | 135 +++++ .../go-kit/kit/log/logfmt_logger.go | 62 +++ .../github.com/go-kit/kit/log/nop_logger.go | 8 + vendor/github.com/go-kit/kit/log/stdlib.go | 116 ++++ vendor/github.com/go-kit/kit/log/sync.go | 116 ++++ vendor/github.com/go-kit/kit/log/value.go | 110 ++++ vendor/github.com/go-logfmt/logfmt/.gitignore | 4 + .../github.com/go-logfmt/logfmt/.travis.yml | 16 + .../github.com/go-logfmt/logfmt/CHANGELOG.md | 41 ++ vendor/github.com/go-logfmt/logfmt/LICENSE | 22 + vendor/github.com/go-logfmt/logfmt/README.md | 33 ++ vendor/github.com/go-logfmt/logfmt/decode.go | 237 +++++++++ vendor/github.com/go-logfmt/logfmt/doc.go | 6 + vendor/github.com/go-logfmt/logfmt/encode.go | 322 ++++++++++++ vendor/github.com/go-logfmt/logfmt/fuzz.go | 126 +++++ vendor/github.com/go-logfmt/logfmt/go.mod | 3 + vendor/github.com/go-logfmt/logfmt/go.sum | 2 + .../github.com/go-logfmt/logfmt/jsonstring.go | 277 ++++++++++ .../go-windows-terminal-sequences/README.md | 41 -- .../go-windows-terminal-sequences/go.mod | 1 - .../sequences.go | 36 -- .../sequences_dummy.go | 11 - vendor/github.com/kr/logfmt/.gitignore | 3 + .../LICENSE => kr/logfmt/Readme} | 11 +- vendor/github.com/kr/logfmt/decode.go | 184 +++++++ vendor/github.com/kr/logfmt/scanner.go | 149 ++++++ vendor/github.com/kr/logfmt/unquote.go | 149 ++++++ .../common/log/eventlog_formatter.go | 89 ---- .../github.com/prometheus/common/log/log.go | 364 ------------- .../prometheus/common/log/syslog_formatter.go | 126 ----- .../prometheus/common/promlog/flag/flag.go | 45 ++ .../prometheus/common/promlog/log.go | 106 ++++ vendor/github.com/sirupsen/logrus/.gitignore | 2 - vendor/github.com/sirupsen/logrus/.travis.yml | 25 - .../github.com/sirupsen/logrus/CHANGELOG.md | 200 ------- vendor/github.com/sirupsen/logrus/README.md | 495 ------------------ vendor/github.com/sirupsen/logrus/alt_exit.go | 76 --- .../github.com/sirupsen/logrus/appveyor.yml | 14 - vendor/github.com/sirupsen/logrus/doc.go | 26 - vendor/github.com/sirupsen/logrus/entry.go | 407 -------------- vendor/github.com/sirupsen/logrus/exported.go | 225 -------- .../github.com/sirupsen/logrus/formatter.go | 78 --- vendor/github.com/sirupsen/logrus/go.mod | 10 - vendor/github.com/sirupsen/logrus/go.sum | 16 - vendor/github.com/sirupsen/logrus/hooks.go | 34 -- .../sirupsen/logrus/json_formatter.go | 121 ----- vendor/github.com/sirupsen/logrus/logger.go | 351 ------------- vendor/github.com/sirupsen/logrus/logrus.go | 186 ------- .../logrus/terminal_check_appengine.go | 11 - .../sirupsen/logrus/terminal_check_bsd.go | 13 - .../logrus/terminal_check_no_terminal.go | 11 - .../logrus/terminal_check_notappengine.go | 17 - .../sirupsen/logrus/terminal_check_solaris.go | 11 - .../sirupsen/logrus/terminal_check_unix.go | 13 - .../sirupsen/logrus/terminal_check_windows.go | 34 -- .../sirupsen/logrus/text_formatter.go | 295 ----------- vendor/github.com/sirupsen/logrus/writer.go | 64 --- .../golang.org/x/sys/windows/registry/key.go | 198 ------- .../x/sys/windows/registry/mksyscall.go | 9 - .../x/sys/windows/registry/syscall.go | 32 -- .../x/sys/windows/registry/value.go | 387 -------------- .../sys/windows/registry/zsyscall_windows.go | 120 ----- .../x/sys/windows/svc/eventlog/install.go | 80 --- .../x/sys/windows/svc/eventlog/log.go | 70 --- vendor/modules.txt | 16 +- 158 files changed, 3434 insertions(+), 4636 deletions(-) rename vendor/github.com/{sirupsen/logrus => go-kit/kit}/LICENSE (88%) create mode 100644 vendor/github.com/go-kit/kit/log/README.md create mode 100644 vendor/github.com/go-kit/kit/log/doc.go create mode 100644 vendor/github.com/go-kit/kit/log/json_logger.go create mode 100644 vendor/github.com/go-kit/kit/log/level/doc.go create mode 100644 vendor/github.com/go-kit/kit/log/level/level.go create mode 100644 vendor/github.com/go-kit/kit/log/log.go create mode 100644 vendor/github.com/go-kit/kit/log/logfmt_logger.go create mode 100644 vendor/github.com/go-kit/kit/log/nop_logger.go create mode 100644 vendor/github.com/go-kit/kit/log/stdlib.go create mode 100644 vendor/github.com/go-kit/kit/log/sync.go create mode 100644 vendor/github.com/go-kit/kit/log/value.go create mode 100644 vendor/github.com/go-logfmt/logfmt/.gitignore create mode 100644 vendor/github.com/go-logfmt/logfmt/.travis.yml create mode 100644 vendor/github.com/go-logfmt/logfmt/CHANGELOG.md create mode 100644 vendor/github.com/go-logfmt/logfmt/LICENSE create mode 100644 vendor/github.com/go-logfmt/logfmt/README.md create mode 100644 vendor/github.com/go-logfmt/logfmt/decode.go create mode 100644 vendor/github.com/go-logfmt/logfmt/doc.go create mode 100644 vendor/github.com/go-logfmt/logfmt/encode.go create mode 100644 vendor/github.com/go-logfmt/logfmt/fuzz.go create mode 100644 vendor/github.com/go-logfmt/logfmt/go.mod create mode 100644 vendor/github.com/go-logfmt/logfmt/go.sum create mode 100644 vendor/github.com/go-logfmt/logfmt/jsonstring.go delete mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/README.md delete mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/go.mod delete mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go delete mode 100644 vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go create mode 100644 vendor/github.com/kr/logfmt/.gitignore rename vendor/github.com/{konsorten/go-windows-terminal-sequences/LICENSE => kr/logfmt/Readme} (71%) create mode 100644 vendor/github.com/kr/logfmt/decode.go create mode 100644 vendor/github.com/kr/logfmt/scanner.go create mode 100644 vendor/github.com/kr/logfmt/unquote.go delete mode 100644 vendor/github.com/prometheus/common/log/eventlog_formatter.go delete mode 100644 vendor/github.com/prometheus/common/log/log.go delete mode 100644 vendor/github.com/prometheus/common/log/syslog_formatter.go create mode 100644 vendor/github.com/prometheus/common/promlog/flag/flag.go create mode 100644 vendor/github.com/prometheus/common/promlog/log.go delete mode 100644 vendor/github.com/sirupsen/logrus/.gitignore delete mode 100644 vendor/github.com/sirupsen/logrus/.travis.yml delete mode 100644 vendor/github.com/sirupsen/logrus/CHANGELOG.md delete mode 100644 vendor/github.com/sirupsen/logrus/README.md delete mode 100644 vendor/github.com/sirupsen/logrus/alt_exit.go delete mode 100644 vendor/github.com/sirupsen/logrus/appveyor.yml delete mode 100644 vendor/github.com/sirupsen/logrus/doc.go delete mode 100644 vendor/github.com/sirupsen/logrus/entry.go delete mode 100644 vendor/github.com/sirupsen/logrus/exported.go delete mode 100644 vendor/github.com/sirupsen/logrus/formatter.go delete mode 100644 vendor/github.com/sirupsen/logrus/go.mod delete mode 100644 vendor/github.com/sirupsen/logrus/go.sum delete mode 100644 vendor/github.com/sirupsen/logrus/hooks.go delete mode 100644 vendor/github.com/sirupsen/logrus/json_formatter.go delete mode 100644 vendor/github.com/sirupsen/logrus/logger.go delete mode 100644 vendor/github.com/sirupsen/logrus/logrus.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_appengine.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_bsd.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_solaris.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_unix.go delete mode 100644 vendor/github.com/sirupsen/logrus/terminal_check_windows.go delete mode 100644 vendor/github.com/sirupsen/logrus/text_formatter.go delete mode 100644 vendor/github.com/sirupsen/logrus/writer.go delete mode 100644 vendor/golang.org/x/sys/windows/registry/key.go delete mode 100644 vendor/golang.org/x/sys/windows/registry/mksyscall.go delete mode 100644 vendor/golang.org/x/sys/windows/registry/syscall.go delete mode 100644 vendor/golang.org/x/sys/windows/registry/value.go delete mode 100644 vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go delete mode 100644 vendor/golang.org/x/sys/windows/svc/eventlog/install.go delete mode 100644 vendor/golang.org/x/sys/windows/svc/eventlog/log.go diff --git a/collector/arp_linux.go b/collector/arp_linux.go index 49c437e80a..6a188795a8 100644 --- a/collector/arp_linux.go +++ b/collector/arp_linux.go @@ -22,11 +22,13 @@ import ( "os" "strings" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) type arpCollector struct { entries *prometheus.Desc + logger log.Logger } func init() { @@ -34,13 +36,14 @@ func init() { } // NewARPCollector returns a new Collector exposing ARP stats. -func NewARPCollector() (Collector, error) { +func NewARPCollector(logger log.Logger) (Collector, error) { return &arpCollector{ entries: prometheus.NewDesc( prometheus.BuildFQName(namespace, "arp", "entries"), "ARP entries by device", []string{"device"}, nil, ), + logger: logger, }, nil } diff --git a/collector/bcache_linux.go b/collector/bcache_linux.go index da4eab5757..20995c7608 100644 --- a/collector/bcache_linux.go +++ b/collector/bcache_linux.go @@ -18,6 +18,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/bcache" ) @@ -28,19 +29,21 @@ func init() { // A bcacheCollector is a Collector which gathers metrics from Linux bcache. type bcacheCollector struct { - fs bcache.FS + fs bcache.FS + logger log.Logger } // NewBcacheCollector returns a newly allocated bcacheCollector. // It exposes a number of Linux bcache statistics. -func NewBcacheCollector() (Collector, error) { +func NewBcacheCollector(logger log.Logger) (Collector, error) { fs, err := bcache.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) } return &bcacheCollector{ - fs: fs, + fs: fs, + logger: logger, }, nil } diff --git a/collector/bonding_linux.go b/collector/bonding_linux.go index f67e2a366b..771786bf82 100644 --- a/collector/bonding_linux.go +++ b/collector/bonding_linux.go @@ -22,12 +22,14 @@ import ( "path/filepath" "strings" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) type bondingCollector struct { slaves, active typedDesc + logger log.Logger } func init() { @@ -36,7 +38,7 @@ func init() { // NewBondingCollector returns a newly allocated bondingCollector. // It exposes the number of configured and active slave of linux bonding interfaces. -func NewBondingCollector() (Collector, error) { +func NewBondingCollector(logger log.Logger) (Collector, error) { return &bondingCollector{ slaves: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "bonding", "slaves"), @@ -48,6 +50,7 @@ func NewBondingCollector() (Collector, error) { "Number of active slaves per bonding interface.", []string{"master"}, nil, ), prometheus.GaugeValue}, + logger: logger, }, nil } @@ -57,7 +60,7 @@ func (c *bondingCollector) Update(ch chan<- prometheus.Metric) error { bondingStats, err := readBondingStats(statusfile) if err != nil { if os.IsNotExist(err) { - log.Debugf("Not collecting bonding, file does not exist: %s", statusfile) + level.Debug(c.logger).Log("msg", "Not collecting bonding, file does not exist", "file", statusfile) return nil } return err diff --git a/collector/boot_time_bsd.go b/collector/boot_time_bsd.go index cc2e895390..74d4bcee36 100644 --- a/collector/boot_time_bsd.go +++ b/collector/boot_time_bsd.go @@ -17,17 +17,21 @@ package collector import ( + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) -type bootTimeCollector struct{ boottime bsdSysctl } +type bootTimeCollector struct { + boottime bsdSysctl + logger log.Logger +} func init() { registerCollector("boottime", defaultEnabled, newBootTimeCollector) } // newBootTimeCollector returns a new Collector exposing system boot time on BSD systems. -func newBootTimeCollector() (Collector, error) { +func newBootTimeCollector(logger log.Logger) (Collector, error) { return &bootTimeCollector{ boottime: bsdSysctl{ name: "boot_time_seconds", @@ -35,6 +39,7 @@ func newBootTimeCollector() (Collector, error) { mib: "kern.boottime", dataType: bsdSysctlTypeStructTimeval, }, + logger: logger, }, nil } diff --git a/collector/boot_time_solaris.go b/collector/boot_time_solaris.go index ea0c50a47b..eb270347ff 100644 --- a/collector/boot_time_solaris.go +++ b/collector/boot_time_solaris.go @@ -17,19 +17,21 @@ package collector import ( + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/siebenmann/go-kstat" ) type bootTimeCollector struct { boottime typedDesc + logger log.Logger } func init() { registerCollector("boottime", defaultEnabled, newBootTimeCollector) } -func newBootTimeCollector() (Collector, error) { +func newBootTimeCollector(logger log.Logger) (Collector, error) { return &bootTimeCollector{ boottime: typedDesc{ prometheus.NewDesc( @@ -37,6 +39,7 @@ func newBootTimeCollector() (Collector, error) { "Unix time of last boot, including microseconds.", nil, nil, ), prometheus.GaugeValue}, + logger: logger, }, nil } diff --git a/collector/buddyinfo.go b/collector/buddyinfo.go index 316766ed05..e94e283bc6 100644 --- a/collector/buddyinfo.go +++ b/collector/buddyinfo.go @@ -20,8 +20,9 @@ import ( "fmt" "strconv" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) @@ -30,8 +31,9 @@ const ( ) type buddyinfoCollector struct { - fs procfs.FS - desc *prometheus.Desc + fs procfs.FS + desc *prometheus.Desc + logger log.Logger } func init() { @@ -39,7 +41,7 @@ func init() { } // NewBuddyinfoCollector returns a new Collector exposing buddyinfo stats. -func NewBuddyinfoCollector() (Collector, error) { +func NewBuddyinfoCollector(logger log.Logger) (Collector, error) { desc := prometheus.NewDesc( prometheus.BuildFQName(namespace, buddyInfoSubsystem, "blocks"), "Count of free blocks according to size.", @@ -49,7 +51,7 @@ func NewBuddyinfoCollector() (Collector, error) { if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) } - return &buddyinfoCollector{fs, desc}, nil + return &buddyinfoCollector{fs, desc, logger}, nil } // Update calls (*buddyinfoCollector).getBuddyInfo to get the platform specific @@ -60,7 +62,7 @@ func (c *buddyinfoCollector) Update(ch chan<- prometheus.Metric) error { return fmt.Errorf("couldn't get buddyinfo: %s", err) } - log.Debugf("Set node_buddy: %#v", buddyInfo) + level.Debug(c.logger).Log("msg", "Set node_buddy", "buddyInfo", buddyInfo) for _, entry := range buddyInfo { for size, value := range entry.Sizes { ch <- prometheus.MustNewConstMetric( diff --git a/collector/collector.go b/collector/collector.go index 4b74b89bf8..af8712c00e 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -19,8 +19,9 @@ import ( "sync" "time" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "gopkg.in/alecthomas/kingpin.v2" ) @@ -48,11 +49,11 @@ const ( ) var ( - factories = make(map[string]func() (Collector, error)) + factories = make(map[string]func(logger log.Logger) (Collector, error)) collectorState = make(map[string]*bool) ) -func registerCollector(collector string, isDefaultEnabled bool, factory func() (Collector, error)) { +func registerCollector(collector string, isDefaultEnabled bool, factory func(logger log.Logger) (Collector, error)) { var helpDefaultState string if isDefaultEnabled { helpDefaultState = "enabled" @@ -73,10 +74,11 @@ func registerCollector(collector string, isDefaultEnabled bool, factory func() ( // NodeCollector implements the prometheus.Collector interface. type NodeCollector struct { Collectors map[string]Collector + logger log.Logger } // NewNodeCollector creates a new NodeCollector. -func NewNodeCollector(filters ...string) (*NodeCollector, error) { +func NewNodeCollector(logger log.Logger, filters ...string) (*NodeCollector, error) { f := make(map[string]bool) for _, filter := range filters { enabled, exist := collectorState[filter] @@ -91,7 +93,7 @@ func NewNodeCollector(filters ...string) (*NodeCollector, error) { collectors := make(map[string]Collector) for key, enabled := range collectorState { if *enabled { - collector, err := factories[key]() + collector, err := factories[key](log.With(logger, "collector", key)) if err != nil { return nil, err } @@ -100,7 +102,7 @@ func NewNodeCollector(filters ...string) (*NodeCollector, error) { } } } - return &NodeCollector{Collectors: collectors}, nil + return &NodeCollector{Collectors: collectors, logger: logger}, nil } // Describe implements the prometheus.Collector interface. @@ -115,24 +117,24 @@ func (n NodeCollector) Collect(ch chan<- prometheus.Metric) { wg.Add(len(n.Collectors)) for name, c := range n.Collectors { go func(name string, c Collector) { - execute(name, c, ch) + execute(name, c, ch, n.logger) wg.Done() }(name, c) } wg.Wait() } -func execute(name string, c Collector, ch chan<- prometheus.Metric) { +func execute(name string, c Collector, ch chan<- prometheus.Metric, logger log.Logger) { begin := time.Now() err := c.Update(ch) duration := time.Since(begin) var success float64 if err != nil { - log.Errorf("ERROR: %s collector failed after %fs: %s", name, duration.Seconds(), err) + level.Error(logger).Log("msg", "collector failed", "name", name, "duration_seconds", duration.Seconds(), "err", err) success = 0 } else { - log.Debugf("OK: %s collector succeeded after %fs.", name, duration.Seconds()) + level.Debug(logger).Log("msg", "collector succeeded", "name", name, "duration_seconds", duration.Seconds()) success = 1 } ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds(), name) diff --git a/collector/conntrack_linux.go b/collector/conntrack_linux.go index 4a0e3750c2..9dc8114f1a 100644 --- a/collector/conntrack_linux.go +++ b/collector/conntrack_linux.go @@ -16,12 +16,14 @@ package collector import ( + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) type conntrackCollector struct { current *prometheus.Desc limit *prometheus.Desc + logger log.Logger } func init() { @@ -29,7 +31,7 @@ func init() { } // NewConntrackCollector returns a new Collector exposing conntrack stats. -func NewConntrackCollector() (Collector, error) { +func NewConntrackCollector(logger log.Logger) (Collector, error) { return &conntrackCollector{ current: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "nf_conntrack_entries"), @@ -41,6 +43,7 @@ func NewConntrackCollector() (Collector, error) { "Maximum size of connection tracking table.", nil, nil, ), + logger: logger, }, nil } diff --git a/collector/cpu_darwin.go b/collector/cpu_darwin.go index 74afafdc71..ba4ba809e6 100644 --- a/collector/cpu_darwin.go +++ b/collector/cpu_darwin.go @@ -25,6 +25,7 @@ import ( "strconv" "unsafe" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -49,7 +50,8 @@ import "C" const ClocksPerSec = float64(C.CLK_TCK) type statCollector struct { - cpu *prometheus.Desc + cpu *prometheus.Desc + logger log.Logger } func init() { @@ -57,9 +59,10 @@ func init() { } // NewCPUCollector returns a new Collector exposing CPU stats. -func NewCPUCollector() (Collector, error) { +func NewCPUCollector(logger log.Logger) (Collector, error) { return &statCollector{ - cpu: nodeCPUSecondsDesc, + cpu: nodeCPUSecondsDesc, + logger: logger, }, nil } diff --git a/collector/cpu_dragonfly.go b/collector/cpu_dragonfly.go index b8c4c06d59..beef390f02 100644 --- a/collector/cpu_dragonfly.go +++ b/collector/cpu_dragonfly.go @@ -20,6 +20,7 @@ import ( "fmt" "unsafe" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -75,7 +76,8 @@ import "C" const maxCPUTimesLen = C.MAXCPU * C.CPUSTATES type statCollector struct { - cpu *prometheus.Desc + cpu *prometheus.Desc + logger log.Logger } func init() { @@ -83,9 +85,10 @@ func init() { } // NewStatCollector returns a new Collector exposing CPU stats. -func NewStatCollector() (Collector, error) { +func NewStatCollector(logger log.Logger) (Collector, error) { return &statCollector{ - cpu: nodeCPUSecondsDesc, + cpu: nodeCPUSecondsDesc, + logger: logger, }, nil } diff --git a/collector/cpu_freebsd.go b/collector/cpu_freebsd.go index dc3d6a1164..41573e40e3 100644 --- a/collector/cpu_freebsd.go +++ b/collector/cpu_freebsd.go @@ -21,8 +21,9 @@ import ( "strconv" "unsafe" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "golang.org/x/sys/unix" ) @@ -81,8 +82,9 @@ func getCPUTimes() ([]cputime, error) { } type statCollector struct { - cpu typedDesc - temp typedDesc + cpu typedDesc + temp typedDesc + logger log.Logger } func init() { @@ -90,7 +92,7 @@ func init() { } // NewStatCollector returns a new Collector exposing CPU stats. -func NewStatCollector() (Collector, error) { +func NewStatCollector(logger log.Logger) (Collector, error) { return &statCollector{ cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, temp: typedDesc{prometheus.NewDesc( @@ -98,6 +100,7 @@ func NewStatCollector() (Collector, error) { "CPU temperature", []string{"cpu"}, nil, ), prometheus.GaugeValue}, + logger: logger, }, nil } @@ -130,11 +133,11 @@ func (c *statCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { if err == unix.ENOENT { // No temperature information for this CPU - log.Debugf("no temperature information for CPU %d", cpu) + level.Debug(c.logger).Log("msg", "no temperature information for CPU", "cpu", cpu) } else { // Unexpected error ch <- c.temp.mustNewConstMetric(math.NaN(), lcpu) - log.Errorf("failed to query CPU temperature for CPU %d: %s", cpu, err) + level.Error(c.logger).Log("msg", "failed to query CPU temperature for CPU", "cpu", cpu, "err", err) } continue } diff --git a/collector/cpu_linux.go b/collector/cpu_linux.go index c946520887..1cb6f2c1ec 100644 --- a/collector/cpu_linux.go +++ b/collector/cpu_linux.go @@ -20,10 +20,11 @@ import ( "path/filepath" "strconv" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" - kingpin "gopkg.in/alecthomas/kingpin.v2" + "gopkg.in/alecthomas/kingpin.v2" ) type cpuCollector struct { @@ -33,6 +34,7 @@ type cpuCollector struct { cpuGuest *prometheus.Desc cpuCoreThrottle *prometheus.Desc cpuPackageThrottle *prometheus.Desc + logger log.Logger } var ( @@ -44,7 +46,7 @@ func init() { } // NewCPUCollector returns a new Collector exposing kernel/system statistics. -func NewCPUCollector() (Collector, error) { +func NewCPUCollector(logger log.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -72,6 +74,7 @@ func NewCPUCollector() (Collector, error) { "Number of times this cpu package has been throttled.", []string{"package"}, nil, ), + logger: logger, }, nil } @@ -134,12 +137,12 @@ func (c *cpuCollector) updateThermalThrottle(ch chan<- prometheus.Metric) error // topology/physical_package_id if physicalPackageID, err = readUintFromFile(filepath.Join(cpu, "topology", "physical_package_id")); err != nil { - log.Debugf("CPU %v is missing physical_package_id", cpu) + level.Debug(c.logger).Log("msg", "CPU is missing physical_package_id", "cpu", cpu) continue } // topology/core_id if coreID, err = readUintFromFile(filepath.Join(cpu, "topology", "core_id")); err != nil { - log.Debugf("CPU %v is missing core_id", cpu) + level.Debug(c.logger).Log("msg", "CPU is missing core_id", "cpu", cpu) continue } @@ -157,7 +160,7 @@ func (c *cpuCollector) updateThermalThrottle(ch chan<- prometheus.Metric) error if coreThrottleCount, err := readUintFromFile(filepath.Join(cpu, "thermal_throttle", "core_throttle_count")); err == nil { packageCoreThrottles[physicalPackageID][coreID] = coreThrottleCount } else { - log.Debugf("CPU %v is missing core_throttle_count", cpu) + level.Debug(c.logger).Log("msg", "CPU is missing core_throttle_count", "cpu", cpu) } } @@ -167,7 +170,7 @@ func (c *cpuCollector) updateThermalThrottle(ch chan<- prometheus.Metric) error if packageThrottleCount, err := readUintFromFile(filepath.Join(cpu, "thermal_throttle", "package_throttle_count")); err == nil { packageThrottles[physicalPackageID] = packageThrottleCount } else { - log.Debugf("CPU %v is missing package_throttle_count", cpu) + level.Debug(c.logger).Log("msg", "CPU is missing package_throttle_count", "cpu", cpu) } } } diff --git a/collector/cpu_openbsd.go b/collector/cpu_openbsd.go index cddc204fea..b7d7688722 100644 --- a/collector/cpu_openbsd.go +++ b/collector/cpu_openbsd.go @@ -19,6 +19,7 @@ import ( "strconv" "unsafe" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -30,16 +31,18 @@ import ( import "C" type cpuCollector struct { - cpu typedDesc + cpu typedDesc + logger log.Logger } func init() { registerCollector("cpu", defaultEnabled, NewCPUCollector) } -func NewCPUCollector() (Collector, error) { +func NewCPUCollector(logger log.Logger) (Collector, error) { return &cpuCollector{ - cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + logger: logger, }, nil } diff --git a/collector/cpu_solaris.go b/collector/cpu_solaris.go index f772765e4d..0624454f49 100644 --- a/collector/cpu_solaris.go +++ b/collector/cpu_solaris.go @@ -19,6 +19,7 @@ package collector import ( "strconv" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" kstat "github.com/siebenmann/go-kstat" ) @@ -27,16 +28,18 @@ import ( import "C" type cpuCollector struct { - cpu typedDesc + cpu typedDesc + logger log.Logger } func init() { registerCollector("cpu", defaultEnabled, NewCpuCollector) } -func NewCpuCollector() (Collector, error) { +func NewCpuCollector(logger log.Logger) (Collector, error) { return &cpuCollector{ - cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + cpu: typedDesc{nodeCPUSecondsDesc, prometheus.CounterValue}, + logger: logger, }, nil } diff --git a/collector/cpufreq_linux.go b/collector/cpufreq_linux.go index c4de4d5c7f..aa32ad2d67 100644 --- a/collector/cpufreq_linux.go +++ b/collector/cpufreq_linux.go @@ -18,6 +18,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -30,6 +31,7 @@ type cpuFreqCollector struct { scalingFreq *prometheus.Desc scalingFreqMin *prometheus.Desc scalingFreqMax *prometheus.Desc + logger log.Logger } func init() { @@ -37,7 +39,7 @@ func init() { } // NewCPUFreqCollector returns a new Collector exposing kernel/system statistics. -func NewCPUFreqCollector() (Collector, error) { +func NewCPUFreqCollector(logger log.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -75,6 +77,7 @@ func NewCPUFreqCollector() (Collector, error) { "Maximum scaled cpu thread frequency in hertz.", []string{"cpu"}, nil, ), + logger: logger, }, nil } diff --git a/collector/cpufreq_solaris.go b/collector/cpufreq_solaris.go index 77ac3ab8d8..7c614ffcb5 100644 --- a/collector/cpufreq_solaris.go +++ b/collector/cpufreq_solaris.go @@ -20,6 +20,7 @@ import ( "fmt" "strconv" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" kstat "github.com/siebenmann/go-kstat" ) @@ -30,13 +31,14 @@ import "C" type cpuFreqCollector struct { cpuFreq *prometheus.Desc cpuFreqMax *prometheus.Desc + logger log.Logger } func init() { registerCollector("cpufreq", defaultEnabled, NewCpuFreqCollector) } -func NewFreqCpuCollector() (Collector, error) { +func NewFreqCpuCollector(logger log.Logger) (Collector, error) { return &cpuFreqCollector{ cpuFreq: prometheus.NewDesc( prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "frequency_hertz"), @@ -48,6 +50,7 @@ func NewFreqCpuCollector() (Collector, error) { "Maximum cpu thread frequency in hertz.", []string{"cpu"}, nil, ), + logger: logger, }, nil } diff --git a/collector/devstat_dragonfly.go b/collector/devstat_dragonfly.go index d2cc6cd222..69e24ba8e0 100644 --- a/collector/devstat_dragonfly.go +++ b/collector/devstat_dragonfly.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -96,6 +97,7 @@ type devstatCollector struct { bytesDesc *prometheus.Desc transfersDesc *prometheus.Desc blocksDesc *prometheus.Desc + logger log.Logger } func init() { @@ -103,7 +105,7 @@ func init() { } // NewDevstatCollector returns a new Collector exposing Device stats. -func NewDevstatCollector() (Collector, error) { +func NewDevstatCollector(logger log.Logger) (Collector, error) { return &devstatCollector{ bytesDesc: prometheus.NewDesc( prometheus.BuildFQName(namespace, devstatSubsystem, "bytes_total"), @@ -120,6 +122,7 @@ func NewDevstatCollector() (Collector, error) { "The total number of bytes given in terms of the devices blocksize.", []string{"device"}, nil, ), + logger: logger, }, nil } diff --git a/collector/devstat_freebsd.go b/collector/devstat_freebsd.go index d394f341e4..dfa18d1e88 100644 --- a/collector/devstat_freebsd.go +++ b/collector/devstat_freebsd.go @@ -21,6 +21,7 @@ import ( "sync" "unsafe" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -41,6 +42,7 @@ type devstatCollector struct { duration typedDesc busyTime typedDesc blocks typedDesc + logger log.Logger } func init() { @@ -48,7 +50,7 @@ func init() { } // NewDevstatCollector returns a new Collector exposing Device stats. -func NewDevstatCollector() (Collector, error) { +func NewDevstatCollector(logger log.Logger) (Collector, error) { return &devstatCollector{ devinfo: &C.struct_devinfo{}, bytes: typedDesc{prometheus.NewDesc( @@ -76,6 +78,7 @@ func NewDevstatCollector() (Collector, error) { "The total number of blocks transferred.", []string{"device"}, nil, ), prometheus.CounterValue}, + logger: logger, }, nil } diff --git a/collector/diskstats_darwin.go b/collector/diskstats_darwin.go index 2c765827d8..8b11812827 100644 --- a/collector/diskstats_darwin.go +++ b/collector/diskstats_darwin.go @@ -18,6 +18,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/lufia/iostat" "github.com/prometheus/client_golang/prometheus" ) @@ -28,7 +29,8 @@ type typedDescFunc struct { } type diskstatsCollector struct { - descs []typedDescFunc + descs []typedDescFunc + logger log.Logger } func init() { @@ -36,7 +38,7 @@ func init() { } // NewDiskstatsCollector returns a new Collector exposing disk device stats. -func NewDiskstatsCollector() (Collector, error) { +func NewDiskstatsCollector(logger log.Logger) (Collector, error) { var diskLabelNames = []string{"device"} return &diskstatsCollector{ @@ -124,6 +126,7 @@ func NewDiskstatsCollector() (Collector, error) { }, }, }, + logger: logger, }, nil } diff --git a/collector/diskstats_linux.go b/collector/diskstats_linux.go index 32bb90cb41..5d714227bd 100644 --- a/collector/diskstats_linux.go +++ b/collector/diskstats_linux.go @@ -24,8 +24,9 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "gopkg.in/alecthomas/kingpin.v2" ) @@ -54,6 +55,7 @@ func (d *typedFactorDesc) mustNewConstMetric(value float64, labels ...string) pr type diskstatsCollector struct { ignoredDevicesPattern *regexp.Regexp descs []typedFactorDesc + logger log.Logger } func init() { @@ -62,7 +64,7 @@ func init() { // NewDiskstatsCollector returns a new Collector exposing disk device stats. // Docs from https://www.kernel.org/doc/Documentation/iostats.txt -func NewDiskstatsCollector() (Collector, error) { +func NewDiskstatsCollector(logger log.Logger) (Collector, error) { var diskLabelNames = []string{"device"} return &diskstatsCollector{ @@ -178,6 +180,7 @@ func NewDiskstatsCollector() (Collector, error) { factor: .001, }, }, + logger: logger, }, nil } @@ -189,7 +192,7 @@ func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { for dev, stats := range diskStats { if c.ignoredDevicesPattern.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(c.logger).Log("msg", "Ignoring device", "device", dev) continue } diff --git a/collector/diskstats_openbsd.go b/collector/diskstats_openbsd.go index f17e2a8b45..3385f3777c 100644 --- a/collector/diskstats_openbsd.go +++ b/collector/diskstats_openbsd.go @@ -18,6 +18,7 @@ package collector import ( "unsafe" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -34,6 +35,7 @@ type diskstatsCollector struct { wxfer typedDesc wbytes typedDesc time typedDesc + logger log.Logger } func init() { @@ -41,13 +43,14 @@ func init() { } // NewDiskstatsCollector returns a new Collector exposing disk device stats. -func NewDiskstatsCollector() (Collector, error) { +func NewDiskstatsCollector(logger log.Logger) (Collector, error) { return &diskstatsCollector{ rxfer: typedDesc{readsCompletedDesc, prometheus.CounterValue}, rbytes: typedDesc{readBytesDesc, prometheus.CounterValue}, wxfer: typedDesc{writesCompletedDesc, prometheus.CounterValue}, wbytes: typedDesc{writtenBytesDesc, prometheus.CounterValue}, time: typedDesc{ioTimeSecondsDesc, prometheus.CounterValue}, + logger: logger, }, nil } diff --git a/collector/drbd_linux.go b/collector/drbd_linux.go index 521f2e2526..5a340b9f02 100644 --- a/collector/drbd_linux.go +++ b/collector/drbd_linux.go @@ -20,8 +20,9 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) // Numerical metric provided by /proc/drbd. @@ -74,13 +75,14 @@ type drbdCollector struct { numerical map[string]drbdNumericalMetric stringPair map[string]drbdStringPairMetric connected *prometheus.Desc + logger log.Logger } func init() { registerCollector("drbd", defaultDisabled, newDRBDCollector) } -func newDRBDCollector() (Collector, error) { +func newDRBDCollector(logger log.Logger) (Collector, error) { return &drbdCollector{ numerical: map[string]drbdNumericalMetric{ "ns": newDRBDNumericalMetric( @@ -176,6 +178,7 @@ func newDRBDCollector() (Collector, error) { []string{"device"}, nil, ), + logger: logger, }, nil } @@ -184,7 +187,7 @@ func (c *drbdCollector) Update(ch chan<- prometheus.Metric) error { file, err := os.Open(statsFile) if err != nil { if os.IsNotExist(err) { - log.Debugf("drbd: %s does not exist, skipping: %s", statsFile, err) + level.Debug(c.logger).Log("msg", "stats file does not exist, skipping", "file", statsFile, "err", err) return nil } @@ -201,7 +204,7 @@ func (c *drbdCollector) Update(ch chan<- prometheus.Metric) error { kv := strings.Split(field, ":") if len(kv) != 2 { - log.Debugf("drbd: skipping invalid key:value pair %q", field) + level.Debug(c.logger).Log("msg", "skipping invalid key:value pair", "field", field) continue } @@ -267,7 +270,7 @@ func (c *drbdCollector) Update(ch chan<- prometheus.Metric) error { continue } - log.Debugf("drbd: unhandled key-value pair: [%s: %q]", kv[0], kv[1]) + level.Debug(c.logger).Log("msg", "unhandled key-value pair", "key", kv[0], "value", kv[1]) } return scanner.Err() diff --git a/collector/edac_linux.go b/collector/edac_linux.go index de338731d6..91b95100cd 100644 --- a/collector/edac_linux.go +++ b/collector/edac_linux.go @@ -20,6 +20,7 @@ import ( "path/filepath" "regexp" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -37,6 +38,7 @@ type edacCollector struct { ueCount *prometheus.Desc csRowCECount *prometheus.Desc csRowUECount *prometheus.Desc + logger log.Logger } func init() { @@ -44,7 +46,7 @@ func init() { } // NewEdacCollector returns a new Collector exposing edac stats. -func NewEdacCollector() (Collector, error) { +func NewEdacCollector(logger log.Logger) (Collector, error) { return &edacCollector{ ceCount: prometheus.NewDesc( prometheus.BuildFQName(namespace, edacSubsystem, "correctable_errors_total"), @@ -66,6 +68,7 @@ func NewEdacCollector() (Collector, error) { "Total uncorrectable memory errors for this csrow.", []string{"controller", "csrow"}, nil, ), + logger: logger, }, nil } diff --git a/collector/entropy_linux.go b/collector/entropy_linux.go index 455615bc0d..e68eb9588f 100644 --- a/collector/entropy_linux.go +++ b/collector/entropy_linux.go @@ -18,11 +18,13 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) type entropyCollector struct { entropyAvail *prometheus.Desc + logger log.Logger } func init() { @@ -30,13 +32,14 @@ func init() { } // NewEntropyCollector returns a new Collector exposing entropy stats. -func NewEntropyCollector() (Collector, error) { +func NewEntropyCollector(logger log.Logger) (Collector, error) { return &entropyCollector{ entropyAvail: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "entropy_available_bits"), "Bits of available entropy.", nil, nil, ), + logger: logger, }, nil } diff --git a/collector/exec_bsd.go b/collector/exec_bsd.go index e4b780bd10..c7bcb74ebf 100644 --- a/collector/exec_bsd.go +++ b/collector/exec_bsd.go @@ -17,11 +17,13 @@ package collector import ( + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) type execCollector struct { sysctls []bsdSysctl + logger log.Logger } func init() { @@ -29,7 +31,7 @@ func init() { } // NewExecCollector returns a new Collector exposing system execution statistics. -func NewExecCollector() (Collector, error) { +func NewExecCollector(logger log.Logger) (Collector, error) { // From sys/vm/vm_meter.c: // All are of type CTLTYPE_UINT. // @@ -73,6 +75,7 @@ func NewExecCollector() (Collector, error) { mib: "vm.stats.vm.v_forks", }, }, + logger: logger, }, nil } diff --git a/collector/filefd_linux.go b/collector/filefd_linux.go index 1d34889a1d..f8d3fcec34 100644 --- a/collector/filefd_linux.go +++ b/collector/filefd_linux.go @@ -22,6 +22,7 @@ import ( "os" "strconv" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -29,15 +30,17 @@ const ( fileFDStatSubsystem = "filefd" ) -type fileFDStatCollector struct{} +type fileFDStatCollector struct { + logger log.Logger +} func init() { registerCollector(fileFDStatSubsystem, defaultEnabled, NewFileFDStatCollector) } // NewFileFDStatCollector returns a new Collector exposing file-nr stats. -func NewFileFDStatCollector() (Collector, error) { - return &fileFDStatCollector{}, nil +func NewFileFDStatCollector(logger log.Logger) (Collector, error) { + return &fileFDStatCollector{logger}, nil } func (c *fileFDStatCollector) Update(ch chan<- prometheus.Metric) error { diff --git a/collector/filesystem_bsd.go b/collector/filesystem_bsd.go index b9aa1cc62d..7b30904021 100644 --- a/collector/filesystem_bsd.go +++ b/collector/filesystem_bsd.go @@ -20,7 +20,7 @@ import ( "errors" "unsafe" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log/level" ) /* @@ -50,14 +50,14 @@ func (c *filesystemCollector) GetStats() (stats []filesystemStats, err error) { for i := 0; i < int(count); i++ { mountpoint := C.GoString(&mnt[i].f_mntonname[0]) if c.ignoredMountPointsPattern.MatchString(mountpoint) { - log.Debugf("Ignoring mount point: %s", mountpoint) + level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", mountpoint) continue } device := C.GoString(&mnt[i].f_mntfromname[0]) fstype := C.GoString(&mnt[i].f_fstypename[0]) if c.ignoredFSTypesPattern.MatchString(fstype) { - log.Debugf("Ignoring fs type: %s", fstype) + level.Debug(c.logger).Log("msg", "Ignoring fs type", "type", fstype) continue } diff --git a/collector/filesystem_common.go b/collector/filesystem_common.go index da4cecd3f0..6971f448e1 100644 --- a/collector/filesystem_common.go +++ b/collector/filesystem_common.go @@ -19,6 +19,7 @@ package collector import ( "regexp" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "gopkg.in/alecthomas/kingpin.v2" ) @@ -48,6 +49,7 @@ type filesystemCollector struct { sizeDesc, freeDesc, availDesc *prometheus.Desc filesDesc, filesFreeDesc *prometheus.Desc roDesc, deviceErrorDesc *prometheus.Desc + logger log.Logger } type filesystemLabels struct { @@ -66,7 +68,7 @@ func init() { } // NewFilesystemCollector returns a new Collector exposing filesystems stats. -func NewFilesystemCollector() (Collector, error) { +func NewFilesystemCollector(logger log.Logger) (Collector, error) { subsystem := "filesystem" mountPointPattern := regexp.MustCompile(*ignoredMountPoints) filesystemsTypesPattern := regexp.MustCompile(*ignoredFSTypes) @@ -123,6 +125,7 @@ func NewFilesystemCollector() (Collector, error) { filesFreeDesc: filesFreeDesc, roDesc: roDesc, deviceErrorDesc: deviceErrorDesc, + logger: logger, }, nil } diff --git a/collector/filesystem_freebsd.go b/collector/filesystem_freebsd.go index 47c48335ec..8fceba7032 100644 --- a/collector/filesystem_freebsd.go +++ b/collector/filesystem_freebsd.go @@ -19,7 +19,7 @@ import ( "bytes" "unsafe" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log/level" "golang.org/x/sys/unix" ) @@ -54,14 +54,14 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { for _, fs := range buf { mountpoint := gostring(fs.Mntonname[:]) if c.ignoredMountPointsPattern.MatchString(mountpoint) { - log.Debugf("Ignoring mount point: %s", mountpoint) + level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", mountpoint) continue } device := gostring(fs.Mntfromname[:]) fstype := gostring(fs.Fstypename[:]) if c.ignoredFSTypesPattern.MatchString(fstype) { - log.Debugf("Ignoring fs type: %s", fstype) + level.Debug(c.logger).Log("msg", "Ignoring fs type", "type", fstype) continue } diff --git a/collector/filesystem_linux.go b/collector/filesystem_linux.go index 98e51b8758..e83b27aa4e 100644 --- a/collector/filesystem_linux.go +++ b/collector/filesystem_linux.go @@ -24,9 +24,10 @@ import ( "sync" "time" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "golang.org/x/sys/unix" - kingpin "gopkg.in/alecthomas/kingpin.v2" + "gopkg.in/alecthomas/kingpin.v2" ) const ( @@ -42,18 +43,18 @@ var stuckMountsMtx = &sync.Mutex{} // GetStats returns filesystem stats. func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { - mps, err := mountPointDetails() + mps, err := mountPointDetails(c.logger) if err != nil { return nil, err } stats := []filesystemStats{} for _, labels := range mps { if c.ignoredMountPointsPattern.MatchString(labels.mountPoint) { - log.Debugf("Ignoring mount point: %s", labels.mountPoint) + level.Debug(c.logger).Log("msg", "Ignoring mount point", "mountpoint", labels.mountPoint) continue } if c.ignoredFSTypesPattern.MatchString(labels.fsType) { - log.Debugf("Ignoring fs type: %s", labels.fsType) + level.Debug(c.logger).Log("msg", "Ignoring fs", "type", labels.fsType) continue } stuckMountsMtx.Lock() @@ -62,7 +63,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { labels: labels, deviceError: 1, }) - log.Debugf("Mount point %q is in an unresponsive state", labels.mountPoint) + level.Debug(c.logger).Log("msg", "Mount point is in an unresponsive state", "mountpoint", labels.mountPoint) stuckMountsMtx.Unlock() continue } @@ -71,7 +72,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { // The success channel is used do tell the "watcher" that the stat // finished successfully. The channel is closed on success. success := make(chan struct{}) - go stuckMountWatcher(labels.mountPoint, success) + go stuckMountWatcher(labels.mountPoint, success, c.logger) buf := new(unix.Statfs_t) err = unix.Statfs(rootfsFilePath(labels.mountPoint), buf) @@ -79,7 +80,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { close(success) // If the mount has been marked as stuck, unmark it and log it's recovery. if _, ok := stuckMounts[labels.mountPoint]; ok { - log.Debugf("Mount point %q has recovered, monitoring will resume", labels.mountPoint) + level.Debug(c.logger).Log("msg", "Mount point has recovered, monitoring will resume", "mountpoint", labels.mountPoint) delete(stuckMounts, labels.mountPoint) } stuckMountsMtx.Unlock() @@ -89,7 +90,8 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { labels: labels, deviceError: 1, }) - log.Debugf("Error on statfs() system call for %q: %s", rootfsFilePath(labels.mountPoint), err) + + level.Debug(c.logger).Log("msg", "Error on statfs() system call", "rootfs", rootfsFilePath(labels.mountPoint), "err", err) continue } @@ -117,7 +119,7 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) { // stuckMountWatcher listens on the given success channel and if the channel closes // then the watcher does nothing. If instead the timeout is reached, the // mount point that is being watched is marked as stuck. -func stuckMountWatcher(mountPoint string, success chan struct{}) { +func stuckMountWatcher(mountPoint string, success chan struct{}, logger log.Logger) { select { case <-success: // Success @@ -128,18 +130,18 @@ func stuckMountWatcher(mountPoint string, success chan struct{}) { case <-success: // Success came in just after the timeout was reached, don't label the mount as stuck default: - log.Debugf("Mount point %q timed out, it is being labeled as stuck and will not be monitored", mountPoint) + level.Debug(logger).Log("msg", "Mount point timed out, it is being labeled as stuck and will not be monitored", "mountpoint", mountPoint) stuckMounts[mountPoint] = struct{}{} } stuckMountsMtx.Unlock() } } -func mountPointDetails() ([]filesystemLabels, error) { +func mountPointDetails(logger log.Logger) ([]filesystemLabels, error) { file, err := os.Open(procFilePath("1/mounts")) if os.IsNotExist(err) { // Fallback to `/proc/mounts` if `/proc/1/mounts` is missing due hidepid. - log.Debugf("Got %q reading root mounts, falling back to system mounts", err) + level.Debug(logger).Log("msg", "Reading root mounts failed, falling back to system mounts", "err", err) file, err = os.Open(procFilePath("mounts")) } if err != nil { diff --git a/collector/filesystem_linux_test.go b/collector/filesystem_linux_test.go index 6271c19130..973cd14463 100644 --- a/collector/filesystem_linux_test.go +++ b/collector/filesystem_linux_test.go @@ -16,6 +16,7 @@ package collector import ( + "github.com/go-kit/kit/log" "strings" "testing" @@ -80,7 +81,7 @@ func TestMountPointDetails(t *testing.T) { "/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[vsanDatastore] bafb9e5a-8856-7e6c-699c-801844e77a4a/kubernetes-dynamic-pvc-3eba5bba-48a3-11e8-89ab-005056b92113.vmdk": "", } - filesystems, err := mountPointDetails() + filesystems, err := mountPointDetails(log.NewNopLogger()) if err != nil { t.Log(err) } @@ -101,7 +102,7 @@ func TestMountsFallback(t *testing.T) { "/": "", } - filesystems, err := mountPointDetails() + filesystems, err := mountPointDetails(log.NewNopLogger()) if err != nil { t.Log(err) } @@ -129,7 +130,7 @@ func TestPathRootfs(t *testing.T) { "/sys/fs/cgroup": "", } - filesystems, err := mountPointDetails() + filesystems, err := mountPointDetails(log.NewNopLogger()) if err != nil { t.Log(err) } diff --git a/collector/hwmon_linux.go b/collector/hwmon_linux.go index a6eb3b9342..31dbd22177 100644 --- a/collector/hwmon_linux.go +++ b/collector/hwmon_linux.go @@ -24,8 +24,9 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "golang.org/x/sys/unix" ) @@ -45,12 +46,14 @@ func init() { registerCollector("hwmon", defaultEnabled, NewHwMonCollector) } -type hwMonCollector struct{} +type hwMonCollector struct { + logger log.Logger +} // NewHwMonCollector returns a new Collector exposing /sys/class/hwmon stats // (similar to lm-sensors). -func NewHwMonCollector() (Collector, error) { - return &hwMonCollector{}, nil +func NewHwMonCollector(logger log.Logger) (Collector, error) { + return &hwMonCollector{logger}, nil } func cleanMetricName(name string) string { @@ -422,7 +425,7 @@ func (c *hwMonCollector) Update(ch chan<- prometheus.Metric) error { hwmonFiles, err := ioutil.ReadDir(hwmonPathName) if err != nil { if os.IsNotExist(err) { - log.Debug("hwmon collector metrics are not available for this system") + level.Debug(c.logger).Log("msg", "hwmon collector metrics are not available for this system") return nil } diff --git a/collector/infiniband_linux.go b/collector/infiniband_linux.go index 52bf9a20e0..9d3ff609b3 100644 --- a/collector/infiniband_linux.go +++ b/collector/infiniband_linux.go @@ -20,6 +20,7 @@ import ( "fmt" "strconv" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -27,6 +28,7 @@ import ( type infinibandCollector struct { fs sysfs.FS metricDescs map[string]*prometheus.Desc + logger log.Logger } func init() { @@ -34,7 +36,7 @@ func init() { } // NewInfiniBandCollector returns a new Collector exposing InfiniBand stats. -func NewInfiniBandCollector() (Collector, error) { +func NewInfiniBandCollector(logger log.Logger) (Collector, error) { var i infinibandCollector var err error @@ -42,6 +44,7 @@ func NewInfiniBandCollector() (Collector, error) { if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) } + i.logger = logger // Detailed description for all metrics. descriptions := map[string]string{ diff --git a/collector/interrupts_common.go b/collector/interrupts_common.go index 9e488299ba..e681cfc5c9 100644 --- a/collector/interrupts_common.go +++ b/collector/interrupts_common.go @@ -16,10 +16,14 @@ package collector -import "github.com/prometheus/client_golang/prometheus" +import ( + "github.com/go-kit/kit/log" + "github.com/prometheus/client_golang/prometheus" +) type interruptsCollector struct { - desc typedDesc + desc typedDesc + logger log.Logger } func init() { @@ -27,12 +31,13 @@ func init() { } // NewInterruptsCollector returns a new Collector exposing interrupts stats. -func NewInterruptsCollector() (Collector, error) { +func NewInterruptsCollector(logger log.Logger) (Collector, error) { return &interruptsCollector{ desc: typedDesc{prometheus.NewDesc( namespace+"_interrupts_total", "Interrupt details.", interruptLabelNames, nil, ), prometheus.CounterValue}, + logger: logger, }, nil } diff --git a/collector/ipvs_linux.go b/collector/ipvs_linux.go index 6ac48b1764..89469e52ea 100644 --- a/collector/ipvs_linux.go +++ b/collector/ipvs_linux.go @@ -20,8 +20,9 @@ import ( "os" "strconv" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) @@ -30,6 +31,7 @@ type ipvsCollector struct { fs procfs.FS backendConnectionsActive, backendConnectionsInact, backendWeight typedDesc connections, incomingPackets, outgoingPackets, incomingBytes, outgoingBytes typedDesc + logger log.Logger } func init() { @@ -38,11 +40,11 @@ func init() { // NewIPVSCollector sets up a new collector for IPVS metrics. It accepts the // "procfs" config parameter to override the default proc location (/proc). -func NewIPVSCollector() (Collector, error) { - return newIPVSCollector() +func NewIPVSCollector(logger log.Logger) (Collector, error) { + return newIPVSCollector(logger) } -func newIPVSCollector() (*ipvsCollector, error) { +func newIPVSCollector(logger log.Logger) (*ipvsCollector, error) { var ( ipvsBackendLabelNames = []string{ "local_address", @@ -57,6 +59,7 @@ func newIPVSCollector() (*ipvsCollector, error) { subsystem = "ipvs" ) + c.logger = logger c.fs, err = procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -111,7 +114,7 @@ func (c *ipvsCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { // Cannot access ipvs metrics, report no error. if os.IsNotExist(err) { - log.Debug("ipvs collector metrics are not available for this system") + level.Debug(c.logger).Log("msg", "ipvs collector metrics are not available for this system") return nil } return fmt.Errorf("could not get IPVS stats: %s", err) diff --git a/collector/ipvs_linux_test.go b/collector/ipvs_linux_test.go index 5cb934af0d..a1c0cba837 100644 --- a/collector/ipvs_linux_test.go +++ b/collector/ipvs_linux_test.go @@ -15,6 +15,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "io/ioutil" "net/http" "net/http/httptest" @@ -30,7 +31,7 @@ func TestIPVSCollector(t *testing.T) { if _, err := kingpin.CommandLine.Parse([]string{"--path.procfs", "fixtures/proc"}); err != nil { t.Fatal(err) } - collector, err := newIPVSCollector() + collector, err := newIPVSCollector(log.NewNopLogger()) if err != nil { t.Fatal(err) } @@ -79,7 +80,7 @@ func TestIPVSCollectorResponse(t *testing.T) { if _, err := kingpin.CommandLine.Parse([]string{"--path.procfs", "fixtures/proc"}); err != nil { t.Fatal(err) } - collector, err := NewIPVSCollector() + collector, err := NewIPVSCollector(log.NewNopLogger()) if err != nil { t.Fatal(err) } diff --git a/collector/ksmd_linux.go b/collector/ksmd_linux.go index 857664f2ea..44d6f8d3eb 100644 --- a/collector/ksmd_linux.go +++ b/collector/ksmd_linux.go @@ -19,6 +19,7 @@ import ( "fmt" "path/filepath" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -29,6 +30,7 @@ var ( type ksmdCollector struct { metricDescs map[string]*prometheus.Desc + logger log.Logger } func init() { @@ -47,7 +49,7 @@ func getCanonicalMetricName(filename string) string { } // NewKsmdCollector returns a new Collector exposing kernel/system statistics. -func NewKsmdCollector() (Collector, error) { +func NewKsmdCollector(logger log.Logger) (Collector, error) { subsystem := "ksmd" descs := make(map[string]*prometheus.Desc) @@ -56,7 +58,7 @@ func NewKsmdCollector() (Collector, error) { prometheus.BuildFQName(namespace, subsystem, getCanonicalMetricName(n)), fmt.Sprintf("ksmd '%s' file.", n), nil, nil) } - return &ksmdCollector{descs}, nil + return &ksmdCollector{descs, logger}, nil } // Update implements Collector and exposes kernel and system statistics. diff --git a/collector/loadavg.go b/collector/loadavg.go index 5568faa4b2..7c8bcff632 100644 --- a/collector/loadavg.go +++ b/collector/loadavg.go @@ -19,12 +19,14 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) type loadavgCollector struct { metric []typedDesc + logger log.Logger } func init() { @@ -32,13 +34,14 @@ func init() { } // NewLoadavgCollector returns a new Collector exposing load average stats. -func NewLoadavgCollector() (Collector, error) { +func NewLoadavgCollector(logger log.Logger) (Collector, error) { return &loadavgCollector{ metric: []typedDesc{ {prometheus.NewDesc(namespace+"_load1", "1m load average.", nil, nil), prometheus.GaugeValue}, {prometheus.NewDesc(namespace+"_load5", "5m load average.", nil, nil), prometheus.GaugeValue}, {prometheus.NewDesc(namespace+"_load15", "15m load average.", nil, nil), prometheus.GaugeValue}, }, + logger: logger, }, nil } @@ -48,7 +51,7 @@ func (c *loadavgCollector) Update(ch chan<- prometheus.Metric) error { return fmt.Errorf("couldn't get load: %s", err) } for i, load := range loads { - log.Debugf("return load %d: %f", i, load) + level.Debug(c.logger).Log("msg", "return load", "index", i, "load", load) ch <- c.metric[i].mustNewConstMetric(load) } return err diff --git a/collector/logind_linux.go b/collector/logind_linux.go index ba23f118aa..fb3cb5703c 100644 --- a/collector/logind_linux.go +++ b/collector/logind_linux.go @@ -20,6 +20,7 @@ import ( "os" "strconv" + "github.com/go-kit/kit/log" "github.com/godbus/dbus" "github.com/prometheus/client_golang/prometheus" ) @@ -43,7 +44,9 @@ var ( ) ) -type logindCollector struct{} +type logindCollector struct { + logger log.Logger +} type logindDbus struct { conn *dbus.Conn @@ -82,8 +85,8 @@ func init() { } // NewLogindCollector returns a new Collector exposing logind statistics. -func NewLogindCollector() (Collector, error) { - return &logindCollector{}, nil +func NewLogindCollector(logger log.Logger) (Collector, error) { + return &logindCollector{logger}, nil } func (lc *logindCollector) Update(ch chan<- prometheus.Metric) error { diff --git a/collector/mdadm_linux.go b/collector/mdadm_linux.go index aced6cdfca..faf1cee242 100644 --- a/collector/mdadm_linux.go +++ b/collector/mdadm_linux.go @@ -19,20 +19,23 @@ import ( "fmt" "os" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) -type mdadmCollector struct{} +type mdadmCollector struct { + logger log.Logger +} func init() { registerCollector("mdadm", defaultEnabled, NewMdadmCollector) } // NewMdadmCollector returns a new Collector exposing raid statistics. -func NewMdadmCollector() (Collector, error) { - return &mdadmCollector{}, nil +func NewMdadmCollector(logger log.Logger) (Collector, error) { + return &mdadmCollector{logger}, nil } var ( @@ -101,7 +104,7 @@ func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { if os.IsNotExist(err) { - log.Debugf("Not collecting mdstat, file does not exist: %s", *procPath) + level.Debug(c.logger).Log("msg", "Not collecting mdstat, file does not exist", "file", *procPath) return nil } @@ -109,7 +112,7 @@ func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) error { } for _, mdStat := range mdStats { - log.Debugf("collecting metrics for device %s", mdStat.Name) + level.Debug(c.logger).Log("msg", "collecting metrics for device", "device", mdStat.Name) stateVals := make(map[string]float64) stateVals[mdStat.ActivityState] = 1 diff --git a/collector/meminfo.go b/collector/meminfo.go index 1652b8bff4..d3d3b8dea9 100644 --- a/collector/meminfo.go +++ b/collector/meminfo.go @@ -20,23 +20,26 @@ import ( "fmt" "strings" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) const ( memInfoSubsystem = "memory" ) -type meminfoCollector struct{} +type meminfoCollector struct { + logger log.Logger +} func init() { registerCollector("meminfo", defaultEnabled, NewMeminfoCollector) } // NewMeminfoCollector returns a new Collector exposing memory stats. -func NewMeminfoCollector() (Collector, error) { - return &meminfoCollector{}, nil +func NewMeminfoCollector(logger log.Logger) (Collector, error) { + return &meminfoCollector{logger}, nil } // Update calls (*meminfoCollector).getMemInfo to get the platform specific @@ -47,7 +50,7 @@ func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { return fmt.Errorf("couldn't get meminfo: %s", err) } - log.Debugf("Set node_mem: %#v", memInfo) + level.Debug(c.logger).Log("msg", "Set node_mem", "memInfo", memInfo) for k, v := range memInfo { if strings.HasSuffix(k, "_total") { metricType = prometheus.CounterValue diff --git a/collector/meminfo_numa_linux.go b/collector/meminfo_numa_linux.go index 3d121bd659..d36f6a0bfd 100644 --- a/collector/meminfo_numa_linux.go +++ b/collector/meminfo_numa_linux.go @@ -25,6 +25,7 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -43,6 +44,7 @@ type meminfoMetric struct { type meminfoNumaCollector struct { metricDescs map[string]*prometheus.Desc + logger log.Logger } func init() { @@ -50,9 +52,10 @@ func init() { } // NewMeminfoNumaCollector returns a new Collector exposing memory stats. -func NewMeminfoNumaCollector() (Collector, error) { +func NewMeminfoNumaCollector(logger log.Logger) (Collector, error) { return &meminfoNumaCollector{ metricDescs: map[string]*prometheus.Desc{}, + logger: logger, }, nil } diff --git a/collector/memory_bsd.go b/collector/memory_bsd.go index cc6bc085cf..4be5ddddc7 100644 --- a/collector/memory_bsd.go +++ b/collector/memory_bsd.go @@ -19,6 +19,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -31,6 +32,7 @@ type memoryCollector struct { pageSize uint64 sysctls []bsdSysctl kvm kvm + logger log.Logger } func init() { @@ -38,7 +40,7 @@ func init() { } // NewMemoryCollector returns a new Collector exposing memory stats. -func NewMemoryCollector() (Collector, error) { +func NewMemoryCollector(logger log.Logger) (Collector, error) { tmp32, err := unix.SysctlUint32("vm.stats.vm.v_page_size") if err != nil { return nil, fmt.Errorf("sysctl(vm.stats.vm.v_page_size) failed: %s", err) @@ -57,6 +59,7 @@ func NewMemoryCollector() (Collector, error) { } return &memoryCollector{ + logger: logger, pageSize: uint64(tmp32), sysctls: []bsdSysctl{ // Descriptions via: https://wiki.freebsd.org/Memory diff --git a/collector/mountstats_linux.go b/collector/mountstats_linux.go index eeb01b7ab5..3fa1597e39 100644 --- a/collector/mountstats_linux.go +++ b/collector/mountstats_linux.go @@ -16,8 +16,9 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) @@ -91,6 +92,8 @@ type mountStatsCollector struct { NFSEventPNFSWriteTotal *prometheus.Desc proc procfs.Proc + + logger log.Logger } // used to uniquely identify an NFS mount to prevent duplicates @@ -105,7 +108,7 @@ func init() { } // NewMountStatsCollector returns a new Collector exposing NFS statistics. -func NewMountStatsCollector() (Collector, error) { +func NewMountStatsCollector(logger log.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -498,7 +501,8 @@ func NewMountStatsCollector() (Collector, error) { nil, ), - proc: proc, + proc: proc, + logger: logger, }, nil } @@ -534,7 +538,7 @@ func (c *mountStatsCollector) Update(ch chan<- prometheus.Metric) error { deviceIdentifier := nfsDeviceIdentifier{m.Device, stats.Transport.Protocol, mountAddress} i := deviceList[deviceIdentifier] if i { - log.Debugf("Skipping duplicate device entry %q", deviceIdentifier) + level.Debug(c.logger).Log("msg", "Skipping duplicate device entry", "device", deviceIdentifier) continue } diff --git a/collector/netclass_linux.go b/collector/netclass_linux.go index af4b48799b..8d8a984e11 100644 --- a/collector/netclass_linux.go +++ b/collector/netclass_linux.go @@ -20,9 +20,10 @@ import ( "fmt" "regexp" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" - kingpin "gopkg.in/alecthomas/kingpin.v2" + "gopkg.in/alecthomas/kingpin.v2" ) var ( @@ -34,6 +35,7 @@ type netClassCollector struct { subsystem string ignoredDevicesPattern *regexp.Regexp metricDescs map[string]*prometheus.Desc + logger log.Logger } func init() { @@ -41,7 +43,7 @@ func init() { } // NewNetClassCollector returns a new Collector exposing network class stats. -func NewNetClassCollector() (Collector, error) { +func NewNetClassCollector(logger log.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -52,6 +54,7 @@ func NewNetClassCollector() (Collector, error) { subsystem: "network", ignoredDevicesPattern: pattern, metricDescs: map[string]*prometheus.Desc{}, + logger: logger, }, nil } diff --git a/collector/netdev_bsd.go b/collector/netdev_bsd.go index 18eca22316..8bed2e1424 100644 --- a/collector/netdev_bsd.go +++ b/collector/netdev_bsd.go @@ -21,7 +21,8 @@ import ( "regexp" "strconv" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" ) /* @@ -34,7 +35,7 @@ import ( */ import "C" -func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) { +func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) { netDev := map[string]map[string]string{} var ifap, ifa *C.struct_ifaddrs @@ -47,11 +48,11 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]ma if ifa.ifa_addr.sa_family == C.AF_LINK { dev := C.GoString(ifa.ifa_name) if ignore != nil && ignore.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(logger).Log("msg", "Ignoring device", "device", dev) continue } if accept != nil && !accept.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(logger).Log("msg", "Ignoring device", "device", dev) continue } diff --git a/collector/netdev_common.go b/collector/netdev_common.go index 0c11023b86..e634c90c85 100644 --- a/collector/netdev_common.go +++ b/collector/netdev_common.go @@ -22,6 +22,7 @@ import ( "regexp" "strconv" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "gopkg.in/alecthomas/kingpin.v2" ) @@ -36,6 +37,7 @@ type netDevCollector struct { ignoredDevicesPattern *regexp.Regexp acceptDevicesPattern *regexp.Regexp metricDescs map[string]*prometheus.Desc + logger log.Logger } func init() { @@ -43,7 +45,7 @@ func init() { } // NewNetDevCollector returns a new Collector exposing network device stats. -func NewNetDevCollector() (Collector, error) { +func NewNetDevCollector(logger log.Logger) (Collector, error) { if *netdevIgnoredDevices != "" && *netdevAcceptDevices != "" { return nil, errors.New("device-blacklist & accept-devices are mutually exclusive") } @@ -63,11 +65,12 @@ func NewNetDevCollector() (Collector, error) { ignoredDevicesPattern: ignorePattern, acceptDevicesPattern: acceptPattern, metricDescs: map[string]*prometheus.Desc{}, + logger: logger, }, nil } func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error { - netDev, err := getNetDevStats(c.ignoredDevicesPattern, c.acceptDevicesPattern) + netDev, err := getNetDevStats(c.ignoredDevicesPattern, c.acceptDevicesPattern, c.logger) if err != nil { return fmt.Errorf("couldn't get netstats: %s", err) } diff --git a/collector/netdev_darwin.go b/collector/netdev_darwin.go index dd25963057..4970853f81 100644 --- a/collector/netdev_darwin.go +++ b/collector/netdev_darwin.go @@ -23,11 +23,12 @@ import ( "regexp" "strconv" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "golang.org/x/sys/unix" ) -func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) { +func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) { netDev := map[string]map[string]string{} ifs, err := net.Interfaces() @@ -38,16 +39,16 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]ma for _, iface := range ifs { ifaceData, err := getIfaceData(iface.Index) if err != nil { - log.Debugf("failed to load data for interface %q: %v", iface.Name, err) + level.Debug(logger).Log("msg", "failed to load data for interface", "device", iface.Name, "err", err) continue } if ignore != nil && ignore.MatchString(iface.Name) { - log.Debugf("Ignoring device: %s", iface.Name) + level.Debug(logger).Log("msg", "Ignoring device", "device", iface.Name) continue } if accept != nil && !accept.MatchString(iface.Name) { - log.Debugf("Ignoring device: %s", iface.Name) + level.Debug(logger).Log("msg", "Ignoring device", "device", iface.Name) continue } diff --git a/collector/netdev_linux.go b/collector/netdev_linux.go index a5c6163514..1a4b5dfcfa 100644 --- a/collector/netdev_linux.go +++ b/collector/netdev_linux.go @@ -23,7 +23,8 @@ import ( "regexp" "strings" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" ) var ( @@ -31,17 +32,17 @@ var ( procNetDevFieldSep = regexp.MustCompile(` +`) ) -func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) { +func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) { file, err := os.Open(procFilePath("net/dev")) if err != nil { return nil, err } defer file.Close() - return parseNetDevStats(file, ignore, accept) + return parseNetDevStats(file, ignore, accept, logger) } -func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) { +func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) { scanner := bufio.NewScanner(r) scanner.Scan() // skip first header scanner.Scan() @@ -65,11 +66,11 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp) dev := parts[1] if ignore != nil && ignore.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(logger).Log("msg", "Ignoring device", "device", dev) continue } if accept != nil && !accept.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(logger).Log("msg", "Ignoring device", "device", dev) continue } diff --git a/collector/netdev_linux_test.go b/collector/netdev_linux_test.go index 163c87ffe4..1c88d83cce 100644 --- a/collector/netdev_linux_test.go +++ b/collector/netdev_linux_test.go @@ -14,6 +14,7 @@ package collector import ( + "github.com/go-kit/kit/log" "os" "regexp" "testing" @@ -26,7 +27,7 @@ func TestNetDevStatsIgnore(t *testing.T) { } defer file.Close() - netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth"), nil) + netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth"), nil, log.NewNopLogger()) if err != nil { t.Fatal(err) } @@ -67,7 +68,7 @@ func TestNetDevStatsAccept(t *testing.T) { } defer file.Close() - netStats, err := parseNetDevStats(file, nil, regexp.MustCompile("^💩0$")) + netStats, err := parseNetDevStats(file, nil, regexp.MustCompile("^💩0$"), log.NewNopLogger()) if err != nil { t.Fatal(err) } diff --git a/collector/netdev_openbsd.go b/collector/netdev_openbsd.go index b080cbcd27..f7c90309d9 100644 --- a/collector/netdev_openbsd.go +++ b/collector/netdev_openbsd.go @@ -20,7 +20,8 @@ import ( "regexp" "strconv" - "github.com/prometheus/common/log" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" ) /* @@ -31,7 +32,7 @@ import ( */ import "C" -func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) { +func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp, logger log.Logger) (map[string]map[string]string, error) { netDev := map[string]map[string]string{} var ifap, ifa *C.struct_ifaddrs @@ -44,11 +45,11 @@ func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]ma if ifa.ifa_addr.sa_family == C.AF_LINK { dev := C.GoString(ifa.ifa_name) if ignore != nil && ignore.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(logger).Log("msg", "Ignoring device", "device", dev) continue } if accept != nil && !accept.MatchString(dev) { - log.Debugf("Ignoring device: %s", dev) + level.Debug(logger).Log("msg", "Ignoring device", "device", dev) continue } diff --git a/collector/netstat_linux.go b/collector/netstat_linux.go index d5eed69603..f0aca26a7b 100644 --- a/collector/netstat_linux.go +++ b/collector/netstat_linux.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "gopkg.in/alecthomas/kingpin.v2" ) @@ -38,6 +39,7 @@ var ( type netStatCollector struct { fieldPattern *regexp.Regexp + logger log.Logger } func init() { @@ -46,10 +48,11 @@ func init() { // NewNetStatCollector takes and returns // a new Collector exposing network stats. -func NewNetStatCollector() (Collector, error) { +func NewNetStatCollector(logger log.Logger) (Collector, error) { pattern := regexp.MustCompile(*netStatFields) return &netStatCollector{ fieldPattern: pattern, + logger: logger, }, nil } diff --git a/collector/nfs_linux.go b/collector/nfs_linux.go index 05b241e517..7ed493083b 100644 --- a/collector/nfs_linux.go +++ b/collector/nfs_linux.go @@ -18,8 +18,9 @@ import ( "os" "reflect" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs/nfs" ) @@ -35,6 +36,7 @@ type nfsCollector struct { nfsRPCRetransmissionsDesc *prometheus.Desc nfsRPCAuthenticationRefreshesDesc *prometheus.Desc nfsProceduresDesc *prometheus.Desc + logger log.Logger } func init() { @@ -42,7 +44,7 @@ func init() { } // NewNfsCollector returns a new Collector exposing NFS statistics. -func NewNfsCollector() (Collector, error) { +func NewNfsCollector(logger log.Logger) (Collector, error) { fs, err := nfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -86,6 +88,7 @@ func NewNfsCollector() (Collector, error) { []string{"proto", "method"}, nil, ), + logger: logger, }, nil } @@ -93,7 +96,7 @@ func (c *nfsCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.ClientRPCStats() if err != nil { if os.IsNotExist(err) { - log.Debugf("Not collecting NFS metrics: %s", err) + level.Debug(c.logger).Log("msg", "Not collecting NFS metrics", "err", err) return nil } return fmt.Errorf("failed to retrieve nfs stats: %w", err) diff --git a/collector/nfsd_linux.go b/collector/nfsd_linux.go index b5e7cf90a1..234e1da202 100644 --- a/collector/nfsd_linux.go +++ b/collector/nfsd_linux.go @@ -17,8 +17,9 @@ import ( "fmt" "os" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs/nfs" ) @@ -27,6 +28,7 @@ import ( type nfsdCollector struct { fs nfs.FS requestsDesc *prometheus.Desc + logger log.Logger } func init() { @@ -38,7 +40,7 @@ const ( ) // NewNFSdCollector returns a new Collector exposing /proc/net/rpc/nfsd statistics. -func NewNFSdCollector() (Collector, error) { +func NewNFSdCollector(logger log.Logger) (Collector, error) { fs, err := nfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -51,6 +53,7 @@ func NewNFSdCollector() (Collector, error) { "Total number NFSd Requests by method and protocol.", []string{"proto", "method"}, nil, ), + logger: logger, }, nil } @@ -59,7 +62,7 @@ func (c *nfsdCollector) Update(ch chan<- prometheus.Metric) error { stats, err := c.fs.ServerRPCStats() if err != nil { if os.IsNotExist(err) { - log.Debugf("Not collecting NFSd metrics: %s", err) + level.Debug(c.logger).Log("msg", "Not collecting NFSd metrics", "err", err) return nil } return fmt.Errorf("failed to retrieve nfsd stats: %w", err) diff --git a/collector/ntp.go b/collector/ntp.go index 9b3c5a27c4..ea3a69e07c 100644 --- a/collector/ntp.go +++ b/collector/ntp.go @@ -22,6 +22,7 @@ import ( "time" "github.com/beevik/ntp" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "gopkg.in/alecthomas/kingpin.v2" ) @@ -47,6 +48,7 @@ var ( type ntpCollector struct { stratum, leap, rtt, offset, reftime, rootDelay, rootDispersion, sanity typedDesc + logger log.Logger } func init() { @@ -57,7 +59,7 @@ func init() { // Default definition of "local" is: // - collector.ntp.server address is a loopback address (or collector.ntp.server-is-mine flag is turned on) // - the server is reachable with outgoin IP_TTL = 1 -func NewNtpCollector() (Collector, error) { +func NewNtpCollector(logger log.Logger) (Collector, error) { ipaddr := net.ParseIP(*ntpServer) if !*ntpServerIsLocal && (ipaddr == nil || !ipaddr.IsLoopback()) { return nil, fmt.Errorf("only IP address of local NTP server is valid for --collector.ntp.server") @@ -112,6 +114,7 @@ func NewNtpCollector() (Collector, error) { "NTPD sanity according to RFC5905 heuristics and configured limits.", nil, nil, ), prometheus.GaugeValue}, + logger: logger, }, nil } diff --git a/collector/perf_linux.go b/collector/perf_linux.go index 0ab7b84f98..0e8a3cca87 100644 --- a/collector/perf_linux.go +++ b/collector/perf_linux.go @@ -17,7 +17,8 @@ import ( "fmt" "runtime" - perf "github.com/hodgesds/perf-utils" + "github.com/go-kit/kit/log" + "github.com/hodgesds/perf-utils" "github.com/prometheus/client_golang/prometheus" ) @@ -29,7 +30,7 @@ func init() { registerCollector(perfSubsystem, defaultDisabled, NewPerfCollector) } -// perfCollector is a Collecter that uses the perf subsystem to collect +// perfCollector is a Collector that uses the perf subsystem to collect // metrics. It uses perf_event_open an ioctls for profiling. Due to the fact // that the perf subsystem is highly dependent on kernel configuration and // settings not all profiler values may be exposed on the target system at any @@ -39,34 +40,36 @@ type perfCollector struct { perfSwProfilers map[int]perf.SoftwareProfiler perfCacheProfilers map[int]perf.CacheProfiler desc map[string]*prometheus.Desc + logger log.Logger } // NewPerfCollector returns a new perf based collector, it creates a profiler // per CPU. -func NewPerfCollector() (Collector, error) { - collector := &perfCollector{ +func NewPerfCollector(logger log.Logger) (Collector, error) { + c := &perfCollector{ perfHwProfilers: map[int]perf.HardwareProfiler{}, perfSwProfilers: map[int]perf.SoftwareProfiler{}, perfCacheProfilers: map[int]perf.CacheProfiler{}, + logger: logger, } ncpus := runtime.NumCPU() for i := 0; i < ncpus; i++ { // Use -1 to profile all processes on the CPU, see: // man perf_event_open - collector.perfHwProfilers[i] = perf.NewHardwareProfiler(-1, i) - if err := collector.perfHwProfilers[i].Start(); err != nil { - return collector, err + c.perfHwProfilers[i] = perf.NewHardwareProfiler(-1, i) + if err := c.perfHwProfilers[i].Start(); err != nil { + return c, err } - collector.perfSwProfilers[i] = perf.NewSoftwareProfiler(-1, i) - if err := collector.perfSwProfilers[i].Start(); err != nil { - return collector, err + c.perfSwProfilers[i] = perf.NewSoftwareProfiler(-1, i) + if err := c.perfSwProfilers[i].Start(); err != nil { + return c, err } - collector.perfCacheProfilers[i] = perf.NewCacheProfiler(-1, i) - if err := collector.perfCacheProfilers[i].Start(); err != nil { - return collector, err + c.perfCacheProfilers[i] = perf.NewCacheProfiler(-1, i) + if err := c.perfCacheProfilers[i].Start(); err != nil { + return c, err } } - collector.desc = map[string]*prometheus.Desc{ + c.desc = map[string]*prometheus.Desc{ "cpucycles_total": prometheus.NewDesc( prometheus.BuildFQName( namespace, @@ -309,7 +312,7 @@ func NewPerfCollector() (Collector, error) { ), } - return collector, nil + return c, nil } // Update implements the Collector interface and will collect metrics per CPU. diff --git a/collector/perf_linux_test.go b/collector/perf_linux_test.go index 0b57d101dd..68c580b2d6 100644 --- a/collector/perf_linux_test.go +++ b/collector/perf_linux_test.go @@ -16,6 +16,7 @@ package collector import ( + "github.com/go-kit/kit/log" "io/ioutil" "strconv" "strings" @@ -37,7 +38,7 @@ func TestPerfCollector(t *testing.T) { if paranoid >= 1 { t.Skip("Skipping perf tests, set perf_event_paranoid to 0") } - collector, err := NewPerfCollector() + collector, err := NewPerfCollector(log.NewNopLogger()) if err != nil { t.Fatal(err) } diff --git a/collector/powersupplyclass.go b/collector/powersupplyclass.go index 3b5c8d7fc7..988b0eed2c 100644 --- a/collector/powersupplyclass.go +++ b/collector/powersupplyclass.go @@ -20,6 +20,7 @@ import ( "fmt" "regexp" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" "gopkg.in/alecthomas/kingpin.v2" @@ -33,18 +34,20 @@ type powerSupplyClassCollector struct { subsystem string ignoredPattern *regexp.Regexp metricDescs map[string]*prometheus.Desc + logger log.Logger } func init() { registerCollector("powersupplyclass", defaultEnabled, NewPowerSupplyClassCollector) } -func NewPowerSupplyClassCollector() (Collector, error) { +func NewPowerSupplyClassCollector(logger log.Logger) (Collector, error) { pattern := regexp.MustCompile(*powerSupplyClassIgnoredPowerSupplies) return &powerSupplyClassCollector{ subsystem: "power_supply", ignoredPattern: pattern, metricDescs: map[string]*prometheus.Desc{}, + logger: logger, }, nil } diff --git a/collector/pressure_linux.go b/collector/pressure_linux.go index 2ed7ffb958..8f0b8db6f2 100644 --- a/collector/pressure_linux.go +++ b/collector/pressure_linux.go @@ -18,8 +18,9 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) @@ -35,6 +36,8 @@ type pressureStatsCollector struct { memFull *prometheus.Desc fs procfs.FS + + logger log.Logger } func init() { @@ -42,7 +45,7 @@ func init() { } // NewPressureStatsCollector returns a Collector exposing pressure stall information -func NewPressureStatsCollector() (Collector, error) { +func NewPressureStatsCollector(logger log.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -74,17 +77,18 @@ func NewPressureStatsCollector() (Collector, error) { "Total time in seconds no process could make progress due to memory congestion", nil, nil, ), - fs: fs, + fs: fs, + logger: logger, }, nil } // Update calls procfs.NewPSIStatsForResource for the different resources and updates the values func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { for _, res := range psiResources { - log.Debugf("collecting statistics for resource: %s", res) + level.Debug(c.logger).Log("msg", "collecting statistics for resource", "resource", res) vals, err := c.fs.PSIStatsForResource(res) if err != nil { - log.Debug("pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel") + level.Debug(c.logger).Log("msg", "pressure information is unavailable, you need a Linux kernel >= 4.20 and/or CONFIG_PSI enabled for your kernel") return nil } switch res { @@ -97,7 +101,7 @@ func (c *pressureStatsCollector) Update(ch chan<- prometheus.Metric) error { ch <- prometheus.MustNewConstMetric(c.mem, prometheus.CounterValue, float64(vals.Some.Total)/1000.0/1000.0) ch <- prometheus.MustNewConstMetric(c.memFull, prometheus.CounterValue, float64(vals.Full.Total)/1000.0/1000.0) default: - log.Debugf("did not account for resource: %s", res) + level.Debug(c.logger).Log("msg", "did not account for resource", "resource", res) } } diff --git a/collector/processes_linux.go b/collector/processes_linux.go index bb18a69098..3d64cbd66d 100644 --- a/collector/processes_linux.go +++ b/collector/processes_linux.go @@ -19,8 +19,9 @@ import ( "fmt" "os" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) @@ -31,6 +32,7 @@ type processCollector struct { procsState *prometheus.Desc pidUsed *prometheus.Desc pidMax *prometheus.Desc + logger log.Logger } func init() { @@ -38,7 +40,7 @@ func init() { } // NewProcessStatCollector returns a new Collector exposing process data read from the proc filesystem. -func NewProcessStatCollector() (Collector, error) { +func NewProcessStatCollector(logger log.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -67,6 +69,7 @@ func NewProcessStatCollector() (Collector, error) { pidMax: prometheus.NewDesc(prometheus.BuildFQName(namespace, subsystem, "max_processes"), "Number of max PIDs limit", nil, nil, ), + logger: logger, }, nil } func (c *processCollector) Update(ch chan<- prometheus.Metric) error { @@ -108,11 +111,11 @@ func (c *processCollector) getAllocatedThreads() (int, map[string]int32, int, er stat, err := pid.Stat() // PIDs can vanish between getting the list and getting stats. if os.IsNotExist(err) { - log.Debugf("file not found when retrieving stats for pid %v: %q", pid, err) + level.Debug(c.logger).Log("msg", "file not found when retrieving stats for pid", "pid", pid, "err", err) continue } if err != nil { - log.Debugf("error reading stat for pid %v: %q", pid, err) + level.Debug(c.logger).Log("msg", "error reading stat for pid", "pid", pid, "err", err) return 0, nil, 0, err } pids++ diff --git a/collector/processes_linux_test.go b/collector/processes_linux_test.go index dc71cedfd1..cb01fbb7ee 100644 --- a/collector/processes_linux_test.go +++ b/collector/processes_linux_test.go @@ -16,6 +16,7 @@ package collector import ( + "github.com/go-kit/kit/log" "testing" "github.com/prometheus/procfs" @@ -31,7 +32,7 @@ func TestReadProcessStatus(t *testing.T) { if err != nil { t.Errorf("failed to open procfs: %v", err) } - c := processCollector{fs: fs} + c := processCollector{fs: fs, logger: log.NewNopLogger()} pids, states, threads, err := c.getAllocatedThreads() if err != nil { t.Fatalf("Cannot retrieve data from procfs getAllocatedThreads function: %v ", err) diff --git a/collector/qdisc_linux.go b/collector/qdisc_linux.go index 74757b3a4d..daba1999c6 100644 --- a/collector/qdisc_linux.go +++ b/collector/qdisc_linux.go @@ -21,6 +21,7 @@ import ( "path/filepath" "github.com/ema/qdisc" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "gopkg.in/alecthomas/kingpin.v2" ) @@ -31,6 +32,7 @@ type qdiscStatCollector struct { drops typedDesc requeues typedDesc overlimits typedDesc + logger log.Logger } var ( @@ -42,7 +44,7 @@ func init() { } // NewQdiscStatCollector returns a new Collector exposing queuing discipline statistics. -func NewQdiscStatCollector() (Collector, error) { +func NewQdiscStatCollector(logger log.Logger) (Collector, error) { return &qdiscStatCollector{ bytes: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "bytes_total"), @@ -69,6 +71,7 @@ func NewQdiscStatCollector() (Collector, error) { "Number of overlimit packets.", []string{"device", "kind"}, nil, ), prometheus.CounterValue}, + logger: logger, }, nil } diff --git a/collector/runit.go b/collector/runit.go index 2608e1bbc6..9d889046a3 100644 --- a/collector/runit.go +++ b/collector/runit.go @@ -16,8 +16,9 @@ package collector import ( + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/soundcloud/go-runit/runit" "gopkg.in/alecthomas/kingpin.v2" ) @@ -25,7 +26,11 @@ import ( var runitServiceDir = kingpin.Flag("collector.runit.servicedir", "Path to runit service directory.").Default("/etc/service").String() type runitCollector struct { - state, stateDesired, stateNormal, stateTimestamp typedDesc + state typedDesc + stateDesired typedDesc + stateNormal typedDesc + stateTimestamp typedDesc + logger log.Logger } func init() { @@ -33,7 +38,7 @@ func init() { } // NewRunitCollector returns a new Collector exposing runit statistics. -func NewRunitCollector() (Collector, error) { +func NewRunitCollector(logger log.Logger) (Collector, error) { var ( subsystem = "service" constLabels = prometheus.Labels{"supervisor": "runit"} @@ -61,6 +66,7 @@ func NewRunitCollector() (Collector, error) { "Unix timestamp of the last runit service state change.", labelNames, constLabels, ), prometheus.GaugeValue}, + logger: logger, }, nil } @@ -73,11 +79,11 @@ func (c *runitCollector) Update(ch chan<- prometheus.Metric) error { for _, service := range services { status, err := service.Status() if err != nil { - log.Debugf("Couldn't get status for %s: %s, skipping...", service.Name, err) + level.Debug(c.logger).Log("msg", "Couldn't get status", "service", service.Name, "err", err) continue } - log.Debugf("%s is %d on pid %d for %d seconds", service.Name, status.State, status.Pid, status.Duration) + level.Debug(c.logger).Log("msg", "duration", "service", service.Name, "status", status.State, "pid", status.Pid, "duration_seconds", status.Duration) ch <- c.state.mustNewConstMetric(float64(status.State), service.Name) ch <- c.stateDesired.mustNewConstMetric(float64(status.Want), service.Name) ch <- c.stateTimestamp.mustNewConstMetric(float64(status.Timestamp.Unix()), service.Name) diff --git a/collector/schedstat_linux.go b/collector/schedstat_linux.go index 1c91a478b3..6810b931ff 100644 --- a/collector/schedstat_linux.go +++ b/collector/schedstat_linux.go @@ -16,6 +16,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" ) @@ -46,17 +47,18 @@ var ( ) // NewSchedstatCollector returns a new Collector exposing task scheduler statistics -func NewSchedstatCollector() (Collector, error) { +func NewSchedstatCollector(logger log.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) } - return &schedstatCollector{fs: fs}, nil + return &schedstatCollector{fs, logger}, nil } type schedstatCollector struct { - fs procfs.FS + fs procfs.FS + logger log.Logger } func init() { diff --git a/collector/sockstat_linux.go b/collector/sockstat_linux.go index fceb0edb2b..c7596c9c96 100644 --- a/collector/sockstat_linux.go +++ b/collector/sockstat_linux.go @@ -19,8 +19,9 @@ import ( "fmt" "os" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "github.com/prometheus/procfs" ) @@ -31,15 +32,17 @@ const ( // Used for calculating the total memory bytes on TCP and UDP. var pageSize = os.Getpagesize() -type sockStatCollector struct{} +type sockStatCollector struct { + logger log.Logger +} func init() { registerCollector(sockStatSubsystem, defaultEnabled, NewSockStatCollector) } // NewSockStatCollector returns a new Collector exposing socket stats. -func NewSockStatCollector() (Collector, error) { - return &sockStatCollector{}, nil +func NewSockStatCollector(logger log.Logger) (Collector, error) { + return &sockStatCollector{logger}, nil } func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { @@ -53,7 +56,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { switch { case err == nil: case os.IsNotExist(err): - log.Debug("IPv4 sockstat statistics not found, skipping") + level.Debug(c.logger).Log("msg", "IPv4 sockstat statistics not found, skipping") default: return fmt.Errorf("failed to get IPv4 sockstat data: %w", err) } @@ -62,7 +65,7 @@ func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { switch { case err == nil: case os.IsNotExist(err): - log.Debug("IPv6 sockstat statistics not found, skipping") + level.Debug(c.logger).Log("msg", "IPv6 sockstat statistics not found, skipping") default: return fmt.Errorf("failed to get IPv6 sockstat data: %w", err) } diff --git a/collector/stat_linux.go b/collector/stat_linux.go index 5e51d9697f..667d9621c8 100644 --- a/collector/stat_linux.go +++ b/collector/stat_linux.go @@ -18,9 +18,9 @@ package collector import ( "fmt" - "github.com/prometheus/procfs" - + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/procfs" ) type statCollector struct { @@ -31,6 +31,7 @@ type statCollector struct { btime *prometheus.Desc procsRunning *prometheus.Desc procsBlocked *prometheus.Desc + logger log.Logger } func init() { @@ -38,7 +39,7 @@ func init() { } // NewStatCollector returns a new Collector exposing kernel/system statistics. -func NewStatCollector() (Collector, error) { +func NewStatCollector(logger log.Logger) (Collector, error) { fs, err := procfs.NewFS(*procPath) if err != nil { return nil, fmt.Errorf("failed to open procfs: %w", err) @@ -75,6 +76,7 @@ func NewStatCollector() (Collector, error) { "Number of processes blocked waiting for I/O to complete.", nil, nil, ), + logger: logger, }, nil } diff --git a/collector/supervisord.go b/collector/supervisord.go index 0e32540739..a18206216e 100644 --- a/collector/supervisord.go +++ b/collector/supervisord.go @@ -18,9 +18,10 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/mattn/go-xmlrpc" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "gopkg.in/alecthomas/kingpin.v2" ) @@ -33,6 +34,7 @@ type supervisordCollector struct { stateDesc *prometheus.Desc exitStatusDesc *prometheus.Desc startTimeDesc *prometheus.Desc + logger log.Logger } func init() { @@ -40,7 +42,7 @@ func init() { } // NewSupervisordCollector returns a new Collector exposing supervisord statistics. -func NewSupervisordCollector() (Collector, error) { +func NewSupervisordCollector(logger log.Logger) (Collector, error) { var ( subsystem = "supervisord" labelNames = []string{"name", "group"} @@ -70,6 +72,7 @@ func NewSupervisordCollector() (Collector, error) { labelNames, nil, ), + logger: logger, }, nil } @@ -147,7 +150,7 @@ func (c *supervisordCollector) Update(ch chan<- prometheus.Metric) error { } else { ch <- prometheus.MustNewConstMetric(c.upDesc, prometheus.GaugeValue, 0, labels...) } - log.Debugf("%s:%s is %s on pid %d", info.Group, info.Name, info.StateName, info.PID) + level.Debug(c.logger).Log("msg", "process info", "group", info.Group, "name", info.Name, "state", info.StateName, "pid", info.PID) } return nil diff --git a/collector/systemd_linux.go b/collector/systemd_linux.go index eb1fa788a2..3c55f4dbc7 100644 --- a/collector/systemd_linux.go +++ b/collector/systemd_linux.go @@ -25,8 +25,9 @@ import ( "time" "github.com/coreos/go-systemd/dbus" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" kingpin "gopkg.in/alecthomas/kingpin.v2" ) @@ -62,6 +63,7 @@ type systemdCollector struct { systemdVersion int unitWhitelistPattern *regexp.Regexp unitBlacklistPattern *regexp.Regexp + logger log.Logger } var unitStatesName = []string{"active", "activating", "deactivating", "inactive", "failed"} @@ -71,7 +73,7 @@ func init() { } // NewSystemdCollector returns a new Collector exposing systemd statistics. -func NewSystemdCollector() (Collector, error) { +func NewSystemdCollector(logger log.Logger) (Collector, error) { const subsystem = "systemd" unitDesc := prometheus.NewDesc( @@ -119,10 +121,10 @@ func NewSystemdCollector() (Collector, error) { unitWhitelistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitWhitelist)) unitBlacklistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitBlacklist)) - systemdVersion := getSystemdVersion() + systemdVersion := getSystemdVersion(logger) if systemdVersion < minSystemdVersionSystemState { - log.Warnf("Detected systemd version %v is lower than minimum %v", systemdVersion, minSystemdVersionSystemState) - log.Warn("Some systemd state and timer metrics will not be available") + level.Warn(logger).Log("msg", "Detected systemd version is lower than minimum", "current", systemdVersion, "minimum", minSystemdVersionSystemState) + level.Warn(logger).Log("msg", "Some systemd state and timer metrics will not be available") } return &systemdCollector{ @@ -141,6 +143,7 @@ func NewSystemdCollector() (Collector, error) { systemdVersion: systemdVersion, unitWhitelistPattern: unitWhitelistPattern, unitBlacklistPattern: unitBlacklistPattern, + logger: logger, }, nil } @@ -158,16 +161,16 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { return fmt.Errorf("couldn't get units: %s", err) } - log.Debugf("systemd getAllUnits took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "getAllUnits took", "duration_seconds", time.Since(begin).Seconds()) begin = time.Now() summary := summarizeUnits(allUnits) c.collectSummaryMetrics(ch, summary) - log.Debugf("systemd collectSummaryMetrics took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectSummaryMetrics took", "duration_seconds", time.Since(begin).Seconds()) begin = time.Now() - units := filterUnits(allUnits, c.unitWhitelistPattern, c.unitBlacklistPattern) - log.Debugf("systemd filterUnits took %f", time.Since(begin).Seconds()) + units := filterUnits(allUnits, c.unitWhitelistPattern, c.unitBlacklistPattern, c.logger) + level.Debug(c.logger).Log("msg", "filterUnits took", "duration_seconds", time.Since(begin).Seconds()) var wg sync.WaitGroup defer wg.Wait() @@ -177,7 +180,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectUnitStatusMetrics(conn, ch, units) - log.Debugf("systemd collectUnitStatusMetrics took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectUnitStatusMetrics took", "duration_seconds", time.Since(begin).Seconds()) }() if *enableStartTimeMetrics { @@ -186,7 +189,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectUnitStartTimeMetrics(conn, ch, units) - log.Debugf("systemd collectUnitStartTimeMetrics took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectUnitStartTimeMetrics took", "duration_seconds", time.Since(begin).Seconds()) }() } @@ -196,7 +199,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectUnitTasksMetrics(conn, ch, units) - log.Debugf("systemd collectUnitTasksMetrics took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectUnitTasksMetrics took", "duration_seconds", time.Since(begin).Seconds()) }() } @@ -206,7 +209,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectTimers(conn, ch, units) - log.Debugf("systemd collectTimers took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectTimers took", "duration_seconds", time.Since(begin).Seconds()) }() } @@ -215,13 +218,13 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { defer wg.Done() begin = time.Now() c.collectSockets(conn, ch, units) - log.Debugf("systemd collectSockets took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectSockets took", "duration_seconds", time.Since(begin).Seconds()) }() if c.systemdVersion >= minSystemdVersionSystemState { begin = time.Now() err = c.collectSystemState(conn, ch) - log.Debugf("systemd collectSystemState took %f", time.Since(begin).Seconds()) + level.Debug(c.logger).Log("msg", "collectSystemState took", "duration_seconds", time.Since(begin).Seconds()) } ch <- prometheus.MustNewConstMetric( @@ -236,14 +239,14 @@ func (c *systemdCollector) collectUnitStatusMetrics(conn *dbus.Conn, ch chan<- p if strings.HasSuffix(unit.Name, ".service") { serviceTypeProperty, err := conn.GetUnitTypeProperty(unit.Name, "Service", "Type") if err != nil { - log.Debugf("couldn't get unit '%s' Type: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit type", "unit", unit.Name, "err", err) } else { serviceType = serviceTypeProperty.Value.Value().(string) } } else if strings.HasSuffix(unit.Name, ".mount") { serviceTypeProperty, err := conn.GetUnitTypeProperty(unit.Name, "Mount", "Type") if err != nil { - log.Debugf("couldn't get unit '%s' Type: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit type", "unit", unit.Name, "err", err) } else { serviceType = serviceTypeProperty.Value.Value().(string) } @@ -261,7 +264,7 @@ func (c *systemdCollector) collectUnitStatusMetrics(conn *dbus.Conn, ch chan<- p // NRestarts wasn't added until systemd 235. restartsCount, err := conn.GetUnitTypeProperty(unit.Name, "Service", "NRestarts") if err != nil { - log.Debugf("couldn't get unit '%s' NRestarts: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit NRestarts", "unit", unit.Name, "err", err) } else { ch <- prometheus.MustNewConstMetric( c.nRestartsDesc, prometheus.CounterValue, @@ -279,7 +282,7 @@ func (c *systemdCollector) collectSockets(conn *dbus.Conn, ch chan<- prometheus. acceptedConnectionCount, err := conn.GetUnitTypeProperty(unit.Name, "Socket", "NAccepted") if err != nil { - log.Debugf("couldn't get unit '%s' NAccepted: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit NAccepted", "unit", unit.Name, "err", err) continue } ch <- prometheus.MustNewConstMetric( @@ -288,7 +291,7 @@ func (c *systemdCollector) collectSockets(conn *dbus.Conn, ch chan<- prometheus. currentConnectionCount, err := conn.GetUnitTypeProperty(unit.Name, "Socket", "NConnections") if err != nil { - log.Debugf("couldn't get unit '%s' NConnections: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit NConnections", "unit", unit.Name, "err", err) continue } ch <- prometheus.MustNewConstMetric( @@ -316,7 +319,7 @@ func (c *systemdCollector) collectUnitStartTimeMetrics(conn *dbus.Conn, ch chan< } else { timestampValue, err := conn.GetUnitProperty(unit.Name, "ActiveEnterTimestamp") if err != nil { - log.Debugf("couldn't get unit '%s' StartTimeUsec: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit StartTimeUsec", "unit", unit.Name, "err", err) continue } startTimeUsec = timestampValue.Value.Value().(uint64) @@ -334,7 +337,7 @@ func (c *systemdCollector) collectUnitTasksMetrics(conn *dbus.Conn, ch chan<- pr if strings.HasSuffix(unit.Name, ".service") { tasksCurrentCount, err := conn.GetUnitTypeProperty(unit.Name, "Service", "TasksCurrent") if err != nil { - log.Debugf("couldn't get unit '%s' TasksCurrent: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit TasksCurrent", "unit", unit.Name, "err", err) } else { val = tasksCurrentCount.Value.Value().(uint64) // Don't set if tasksCurrent if dbus reports MaxUint64. @@ -346,7 +349,7 @@ func (c *systemdCollector) collectUnitTasksMetrics(conn *dbus.Conn, ch chan<- pr } tasksMaxCount, err := conn.GetUnitTypeProperty(unit.Name, "Service", "TasksMax") if err != nil { - log.Debugf("couldn't get unit '%s' TasksMax: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit TasksMax", "unit", unit.Name, "err", err) } else { val = tasksMaxCount.Value.Value().(uint64) // Don't set if tasksMax if dbus reports MaxUint64. @@ -368,7 +371,7 @@ func (c *systemdCollector) collectTimers(conn *dbus.Conn, ch chan<- prometheus.M lastTriggerValue, err := conn.GetUnitTypeProperty(unit.Name, "Timer", "LastTriggerUSec") if err != nil { - log.Debugf("couldn't get unit '%s' LastTriggerUSec: %s", unit.Name, err) + level.Debug(c.logger).Log("msg", "couldn't get unit LastTriggerUSec", "unit", unit.Name, "err", err) continue } @@ -440,36 +443,36 @@ func summarizeUnits(units []unit) map[string]float64 { return summarized } -func filterUnits(units []unit, whitelistPattern, blacklistPattern *regexp.Regexp) []unit { +func filterUnits(units []unit, whitelistPattern, blacklistPattern *regexp.Regexp, logger log.Logger) []unit { filtered := make([]unit, 0, len(units)) for _, unit := range units { if whitelistPattern.MatchString(unit.Name) && !blacklistPattern.MatchString(unit.Name) && unit.LoadState == "loaded" { - log.Debugf("Adding unit: %s", unit.Name) + level.Debug(logger).Log("msg", "Adding unit", "unit", unit.Name) filtered = append(filtered, unit) } else { - log.Debugf("Ignoring unit: %s", unit.Name) + level.Debug(logger).Log("msg", "Ignoring unit", "unit", unit.Name) } } return filtered } -func getSystemdVersion() int { +func getSystemdVersion(logger log.Logger) int { conn, err := newSystemdDbusConn() if err != nil { - log.Warnf("Unable to get systemd dbus connection, defaulting systemd version to 0: %s", err) + level.Warn(logger).Log("msg", "Unable to get systemd dbus connection, defaulting systemd version to 0", "err", err) return 0 } defer conn.Close() version, err := conn.GetManagerProperty("Version") if err != nil { - log.Warn("Unable to get systemd version property, defaulting to 0") + level.Warn(logger).Log("msg", "Unable to get systemd version property, defaulting to 0") return 0 } version = strings.Replace(version, "\"", "", 2) v, err := strconv.Atoi(version) if err != nil { - log.Warnf("Got invalid systemd version: %v", version) + level.Warn(logger).Log("msg", "Got invalid systemd version", "version", version) return 0 } return v diff --git a/collector/systemd_linux_test.go b/collector/systemd_linux_test.go index b2298d6407..613a1ab6d6 100644 --- a/collector/systemd_linux_test.go +++ b/collector/systemd_linux_test.go @@ -14,6 +14,7 @@ package collector import ( + "github.com/go-kit/kit/log" "regexp" "testing" @@ -90,7 +91,7 @@ func TestSystemdIgnoreFilter(t *testing.T) { fixtures := getUnitListFixtures() whitelistPattern := regexp.MustCompile("^foo$") blacklistPattern := regexp.MustCompile("^bar$") - filtered := filterUnits(fixtures[0], whitelistPattern, blacklistPattern) + filtered := filterUnits(fixtures[0], whitelistPattern, blacklistPattern, log.NewNopLogger()) for _, unit := range filtered { if blacklistPattern.MatchString(unit.Name) || !whitelistPattern.MatchString(unit.Name) { t.Error(unit.Name, "should not be in the filtered list") @@ -98,13 +99,14 @@ func TestSystemdIgnoreFilter(t *testing.T) { } } func TestSystemdIgnoreFilterDefaultKeepsAll(t *testing.T) { - c, err := NewSystemdCollector() + logger := log.NewNopLogger() + c, err := NewSystemdCollector(logger) if err != nil { t.Fatal(err) } fixtures := getUnitListFixtures() collector := c.(*systemdCollector) - filtered := filterUnits(fixtures[0], collector.unitWhitelistPattern, collector.unitBlacklistPattern) + filtered := filterUnits(fixtures[0], collector.unitWhitelistPattern, collector.unitBlacklistPattern, logger) // Adjust fixtures by 3 "not-found" units. if len(filtered) != len(fixtures[0])-3 { t.Error("Default filters removed units") diff --git a/collector/tcpstat_linux.go b/collector/tcpstat_linux.go index cc4e960b0f..9b09e9ab4f 100644 --- a/collector/tcpstat_linux.go +++ b/collector/tcpstat_linux.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -54,7 +55,8 @@ const ( ) type tcpStatCollector struct { - desc typedDesc + desc typedDesc + logger log.Logger } func init() { @@ -62,13 +64,14 @@ func init() { } // NewTCPStatCollector returns a new Collector exposing network stats. -func NewTCPStatCollector() (Collector, error) { +func NewTCPStatCollector(logger log.Logger) (Collector, error) { return &tcpStatCollector{ desc: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "tcp", "connection_states"), "Number of connection states.", []string{"state"}, nil, ), prometheus.GaugeValue}, + logger: logger, }, nil } diff --git a/collector/textfile.go b/collector/textfile.go index aee11db815..64165e622d 100644 --- a/collector/textfile.go +++ b/collector/textfile.go @@ -24,10 +24,11 @@ import ( "strings" "time" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" - "github.com/prometheus/common/log" kingpin "gopkg.in/alecthomas/kingpin.v2" ) @@ -44,7 +45,8 @@ var ( type textFileCollector struct { path string // Only set for testing to get predictable output. - mtime *float64 + mtime *float64 + logger log.Logger } func init() { @@ -53,14 +55,15 @@ func init() { // NewTextFileCollector returns a new Collector exposing metrics read from files // in the given textfile directory. -func NewTextFileCollector() (Collector, error) { +func NewTextFileCollector(logger log.Logger) (Collector, error) { c := &textFileCollector{ - path: *textFileDirectory, + path: *textFileDirectory, + logger: logger, } return c, nil } -func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric) { +func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric, logger log.Logger) { var valType prometheus.ValueType var val float64 @@ -76,7 +79,7 @@ func convertMetricFamily(metricFamily *dto.MetricFamily, ch chan<- prometheus.Me for _, metric := range metricFamily.Metric { if metric.TimestampMs != nil { - log.Warnf("Ignoring unsupported custom timestamp on textfile collector metric %v", metric) + level.Warn(logger).Log("msg", "Ignoring unsupported custom timestamp on textfile collector metric", "metric", metric) } labels := metric.GetLabel() @@ -191,7 +194,7 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { files, err := ioutil.ReadDir(c.path) if err != nil && c.path != "" { errored = true - log.Errorf("failed to read textfile collector directory %q: %v", c.path, err) + level.Error(c.logger).Log("msg", "failed to read textfile collector directory", "path", c.path, "err", err) } mtimes := make(map[string]time.Time, len(files)) @@ -203,7 +206,7 @@ func (c *textFileCollector) Update(ch chan<- prometheus.Metric) error { mtime, err := c.processFile(f.Name(), ch) if err != nil { errored = true - log.Errorf("failed to collect textfile data from %q: %v", f.Name(), err) + level.Error(c.logger).Log("msg", "failed to collect textfile data", "file", f.Name(), "err", err) continue } @@ -257,7 +260,7 @@ func (c *textFileCollector) processFile(name string, ch chan<- prometheus.Metric } for _, mf := range families { - convertMetricFamily(mf, ch) + convertMetricFamily(mf, ch, c.logger) } // Only stat the file once it has been parsed and validated, so that diff --git a/collector/textfile_test.go b/collector/textfile_test.go index fb1a700738..f0e93a8bd4 100644 --- a/collector/textfile_test.go +++ b/collector/textfile_test.go @@ -20,10 +20,12 @@ import ( "net/http/httptest" "testing" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prometheus/common/log" - kingpin "gopkg.in/alecthomas/kingpin.v2" + "github.com/prometheus/common/promlog" + "github.com/prometheus/common/promlog/flag" + "gopkg.in/alecthomas/kingpin.v2" ) type collectorAdapter struct { @@ -39,8 +41,7 @@ func (a collectorAdapter) Describe(ch chan<- *prometheus.Desc) { // Collect implements the prometheus.Collector interface. func (a collectorAdapter) Collect(ch chan<- prometheus.Metric) { - err := a.Update(ch) - if err != nil { + if err := a.Update(ch); err != nil { panic(fmt.Sprintf("failed to update collector: %v", err)) } } @@ -95,15 +96,16 @@ func TestTextfileCollector(t *testing.T) { for i, test := range tests { mtime := 1.0 c := &textFileCollector{ - path: test.path, - mtime: &mtime, + path: test.path, + mtime: &mtime, + logger: log.NewNopLogger(), } // Suppress a log message about `nonexistent_path` not existing, this is // expected and clutters the test output. - log.AddFlags(kingpin.CommandLine) - _, err := kingpin.CommandLine.Parse([]string{"--log.level", "fatal"}) - if err != nil { + promlogConfig := &promlog.Config{} + flag.AddFlags(kingpin.CommandLine, promlogConfig) + if _, err := kingpin.CommandLine.Parse([]string{"--log.level", "debug"}); err != nil { t.Fatal(err) } diff --git a/collector/thermal_zone_linux.go b/collector/thermal_zone_linux.go index b8a65cf8d0..5d223bcd4f 100644 --- a/collector/thermal_zone_linux.go +++ b/collector/thermal_zone_linux.go @@ -18,6 +18,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) @@ -30,6 +31,7 @@ type thermalZoneCollector struct { coolingDeviceCurState *prometheus.Desc coolingDeviceMaxState *prometheus.Desc zoneTemp *prometheus.Desc + logger log.Logger } func init() { @@ -37,7 +39,7 @@ func init() { } // NewThermalZoneCollector returns a new Collector exposing kernel/system statistics. -func NewThermalZoneCollector() (Collector, error) { +func NewThermalZoneCollector(logger log.Logger) (Collector, error) { fs, err := sysfs.NewFS(*sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) @@ -60,6 +62,7 @@ func NewThermalZoneCollector() (Collector, error) { "Maximum throttle state of the cooling device", []string{"name", "type"}, nil, ), + logger: logger, }, nil } diff --git a/collector/time.go b/collector/time.go index eb57bd85e6..76a2d1e968 100644 --- a/collector/time.go +++ b/collector/time.go @@ -18,12 +18,14 @@ package collector import ( "time" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) type timeCollector struct { - desc *prometheus.Desc + desc *prometheus.Desc + logger log.Logger } func init() { @@ -32,19 +34,20 @@ func init() { // NewTimeCollector returns a new Collector exposing the current system time in // seconds since epoch. -func NewTimeCollector() (Collector, error) { +func NewTimeCollector(logger log.Logger) (Collector, error) { return &timeCollector{ desc: prometheus.NewDesc( namespace+"_time_seconds", "System time in seconds since epoch (1970).", nil, nil, ), + logger: logger, }, nil } func (c *timeCollector) Update(ch chan<- prometheus.Metric) error { now := float64(time.Now().UnixNano()) / 1e9 - log.Debugf("Return time: %f", now) + level.Debug(c.logger).Log("msg", "Return time", "now", now) ch <- prometheus.MustNewConstMetric(c.desc, prometheus.GaugeValue, now) return nil } diff --git a/collector/timex.go b/collector/timex.go index 7ef6920f7c..4dc4d6c35c 100644 --- a/collector/timex.go +++ b/collector/timex.go @@ -19,6 +19,7 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sys/unix" ) @@ -54,6 +55,7 @@ type timexCollector struct { stbcnt, tai, syncStatus typedDesc + logger log.Logger } func init() { @@ -61,7 +63,7 @@ func init() { } // NewTimexCollector returns a new Collector exposing adjtime(3) stats. -func NewTimexCollector() (Collector, error) { +func NewTimexCollector(logger log.Logger) (Collector, error) { const subsystem = "timex" return &timexCollector{ @@ -150,6 +152,7 @@ func NewTimexCollector() (Collector, error) { "Is clock synchronized to a reliable server (1 = yes, 0 = no).", nil, nil, ), prometheus.GaugeValue}, + logger: logger, }, nil } diff --git a/collector/uname.go b/collector/uname.go index 381c0ae9f1..2c0c3068a1 100644 --- a/collector/uname.go +++ b/collector/uname.go @@ -17,6 +17,7 @@ package collector import ( + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) @@ -34,7 +35,9 @@ var unameDesc = prometheus.NewDesc( nil, ) -type unameCollector struct{} +type unameCollector struct { + logger log.Logger +} type uname struct { SysName string Release string @@ -49,8 +52,8 @@ func init() { } // NewUnameCollector returns new unameCollector. -func newUnameCollector() (Collector, error) { - return &unameCollector{}, nil +func newUnameCollector(logger log.Logger) (Collector, error) { + return &unameCollector{logger}, nil } func (c *unameCollector) Update(ch chan<- prometheus.Metric) error { diff --git a/collector/vmstat_linux.go b/collector/vmstat_linux.go index 50268dd18b..605c38adb6 100644 --- a/collector/vmstat_linux.go +++ b/collector/vmstat_linux.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "gopkg.in/alecthomas/kingpin.v2" ) @@ -37,6 +38,7 @@ var ( type vmStatCollector struct { fieldPattern *regexp.Regexp + logger log.Logger } func init() { @@ -44,10 +46,11 @@ func init() { } // NewvmStatCollector returns a new Collector exposing vmstat stats. -func NewvmStatCollector() (Collector, error) { +func NewvmStatCollector(logger log.Logger) (Collector, error) { pattern := regexp.MustCompile(*vmStatFields) return &vmStatCollector{ fieldPattern: pattern, + logger: logger, }, nil } diff --git a/collector/wifi_linux.go b/collector/wifi_linux.go index 8e0d4eef67..5a5a86ce39 100644 --- a/collector/wifi_linux.go +++ b/collector/wifi_linux.go @@ -20,9 +20,10 @@ import ( "os" "path/filepath" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/mdlayher/wifi" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" "gopkg.in/alecthomas/kingpin.v2" ) @@ -40,6 +41,8 @@ type wifiCollector struct { stationTransmitRetriesTotal *prometheus.Desc stationTransmitFailedTotal *prometheus.Desc stationBeaconLossTotal *prometheus.Desc + + logger log.Logger } var ( @@ -61,7 +64,7 @@ type wifiStater interface { } // NewWifiCollector returns a new Collector exposing Wifi statistics. -func NewWifiCollector() (Collector, error) { +func NewWifiCollector(logger log.Logger) (Collector, error) { const ( subsystem = "wifi" ) @@ -154,6 +157,7 @@ func NewWifiCollector() (Collector, error) { labels, nil, ), + logger: logger, }, nil } @@ -162,11 +166,11 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { if err != nil { // Cannot access wifi metrics, report no error. if os.IsNotExist(err) { - log.Debug("wifi collector metrics are not available for this system") + level.Debug(c.logger).Log("msg", "wifi collector metrics are not available for this system") return nil } if os.IsPermission(err) { - log.Debug("wifi collector got permission denied when accessing metrics") + level.Debug(c.logger).Log("msg", "wifi collector got permission denied when accessing metrics") return nil } @@ -185,7 +189,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { continue } - log.Debugf("probing wifi device %q with type %q", ifi.Name, ifi.Type) + level.Debug(c.logger).Log("msg", "probing wifi device with type", "wifi", ifi.Name, "type", ifi.Type) ch <- prometheus.MustNewConstMetric( c.interfaceFrequencyHertz, @@ -203,7 +207,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { case err == nil: c.updateBSSStats(ch, ifi.Name, bss) case os.IsNotExist(err): - log.Debugf("BSS information not found for wifi device %q", ifi.Name) + level.Debug(c.logger).Log("msg", "BSS information not found for wifi device", "name", ifi.Name) default: return fmt.Errorf("failed to retrieve BSS for device %s: %v", ifi.Name, err) @@ -216,7 +220,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error { c.updateStationStats(ch, ifi.Name, station) } case os.IsNotExist(err): - log.Debugf("station information not found for wifi device %q", ifi.Name) + level.Debug(c.logger).Log("msg", "station information not found for wifi device", "name", ifi.Name) default: return fmt.Errorf("failed to retrieve station info for device %q: %v", ifi.Name, err) diff --git a/collector/xfs_linux.go b/collector/xfs_linux.go index c92a48ce72..77824c53ec 100644 --- a/collector/xfs_linux.go +++ b/collector/xfs_linux.go @@ -16,13 +16,15 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/xfs" ) // An xfsCollector is a Collector which gathers metrics from XFS filesystems. type xfsCollector struct { - fs xfs.FS + fs xfs.FS + logger log.Logger } func init() { @@ -30,14 +32,15 @@ func init() { } // NewXFSCollector returns a new Collector exposing XFS statistics. -func NewXFSCollector() (Collector, error) { +func NewXFSCollector(logger log.Logger) (Collector, error) { fs, err := xfs.NewFS(*procPath, *sysPath) if err != nil { return nil, fmt.Errorf("failed to open sysfs: %w", err) } return &xfsCollector{ - fs: fs, + fs: fs, + logger: logger, }, nil } diff --git a/collector/zfs.go b/collector/zfs.go index aa33688095..093ded5c11 100644 --- a/collector/zfs.go +++ b/collector/zfs.go @@ -20,8 +20,9 @@ import ( "errors" "strings" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) var errZFSNotAvailable = errors.New("ZFS / ZFS statistics are not available") @@ -36,10 +37,11 @@ type zfsCollector struct { linuxProcpathBase string linuxZpoolIoPath string linuxPathMap map[string]string + logger log.Logger } // NewZFSCollector returns a new Collector exposing ZFS statistics. -func NewZFSCollector() (Collector, error) { +func NewZFSCollector(logger log.Logger) (Collector, error) { return &zfsCollector{ linuxProcpathBase: "spl/kstat/zfs", linuxZpoolIoPath: "/*/io", @@ -56,6 +58,7 @@ func NewZFSCollector() (Collector, error) { "zfs_zfetch": "zfetchstats", "zfs_zil": "zil", }, + logger: logger, }, nil } @@ -63,7 +66,7 @@ func (c *zfsCollector) Update(ch chan<- prometheus.Metric) error { for subsystem := range c.linuxPathMap { if err := c.updateZfsStats(subsystem, ch); err != nil { if err == errZFSNotAvailable { - log.Debug(err) + level.Debug(c.logger).Log("err", err) // ZFS /proc files are added as new features to ZFS arrive, it is ok to continue continue } diff --git a/collector/zfs_freebsd.go b/collector/zfs_freebsd.go index 18fcd247c1..a625bbc97e 100644 --- a/collector/zfs_freebsd.go +++ b/collector/zfs_freebsd.go @@ -16,11 +16,13 @@ package collector import ( "fmt" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" ) type zfsCollector struct { sysctls []bsdSysctl + logger log.Logger } const ( @@ -31,7 +33,7 @@ func init() { registerCollector("zfs", defaultEnabled, NewZfsCollector) } -func NewZfsCollector() (Collector, error) { +func NewZfsCollector(logger log.Logger) (Collector, error) { return &zfsCollector{ sysctls: []bsdSysctl{ { @@ -238,6 +240,7 @@ func NewZfsCollector() (Collector, error) { valueType: prometheus.CounterValue, }, }, + logger: logger, }, nil } diff --git a/collector/zfs_linux.go b/collector/zfs_linux.go index a964d502a7..aa27db8402 100644 --- a/collector/zfs_linux.go +++ b/collector/zfs_linux.go @@ -22,8 +22,8 @@ import ( "strconv" "strings" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/common/log" ) // constants from https://github.com/zfsonlinux/zfs/blob/master/lib/libspl/include/sys/kstat.h @@ -45,7 +45,7 @@ func (c *zfsCollector) openProcFile(path string) (*os.File, error) { // file not found error can occur if: // 1. zfs module is not loaded // 2. zfs version does not have the feature with metrics -- ok to ignore - log.Debugf("Cannot open %q for reading", procFilePath(path)) + level.Debug(c.logger).Log("msg", "Cannot open file for reading", "path", procFilePath(path)) return nil, errZFSNotAvailable } return file, nil @@ -77,7 +77,7 @@ func (c *zfsCollector) updatePoolStats(ch chan<- prometheus.Metric) error { file, err := os.Open(zpoolPath) if err != nil { // this file should exist, but there is a race where an exporting pool can remove the files -- ok to ignore - log.Debugf("Cannot open %q for reading", zpoolPath) + level.Debug(c.logger).Log("msg", "Cannot open file for reading", "path", zpoolPath) return errZFSNotAvailable } diff --git a/collector/zfs_solaris.go b/collector/zfs_solaris.go index 5bf2235f37..bfda64f316 100644 --- a/collector/zfs_solaris.go +++ b/collector/zfs_solaris.go @@ -18,6 +18,7 @@ package collector import ( "strings" + "github.com/go-kit/kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/siebenmann/go-kstat" ) @@ -52,6 +53,7 @@ type zfsCollector struct { arcstatsSize *prometheus.Desc zfetchstatsHits *prometheus.Desc zfetchstatsMisses *prometheus.Desc + logger log.Logger } const ( @@ -62,7 +64,7 @@ func init() { registerCollector("zfs", defaultEnabled, NewZfsCollector) } -func NewZfsCollector() (Collector, error) { +func NewZfsCollector(logger log.Logger) (Collector, error) { return &zfsCollector{ abdstatsLinearCount: prometheus.NewDesc( prometheus.BuildFQName(namespace, zfsCollectorSubsystem, "abdstats_linear_count_total"), @@ -180,6 +182,7 @@ func NewZfsCollector() (Collector, error) { prometheus.BuildFQName(namespace, zfsCollectorSubsystem, "zfetchstats_misses_total"), "ZFS cache fetch misses", nil, nil, ), + logger: logger, }, nil } diff --git a/go.mod b/go.mod index 078e3bbf06..a811337435 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ require ( github.com/beevik/ntp v0.2.0 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/ema/qdisc v0.0.0-20190904071900-b82c76788043 + github.com/go-kit/kit v0.9.0 github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968 github.com/hodgesds/perf-utils v0.0.7 - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3 github.com/mattn/go-xmlrpc v0.0.3 github.com/mdlayher/genetlink v0.0.0-20190828143517-e35f2bf499b9 // indirect diff --git a/go.sum b/go.sum index 230ca21e15..810a85492e 100644 --- a/go.sum +++ b/go.sum @@ -20,9 +20,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/ema/qdisc v0.0.0-20190904071900-b82c76788043 h1:I3hLsM87FSASssIrIOGwJCio31dvLkvpYDKn2+r31ec= github.com/ema/qdisc v0.0.0-20190904071900-b82c76788043/go.mod h1:ix4kG2zvdUd8kEKSW0ZTr1XLks0epFpI4j745DXxlNE= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968 h1:s+PDl6lozQ+dEUtUtQnO7+A2iPG3sK1pI4liU+jxn90= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -46,8 +49,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3 h1:XGhvld9vIpj929Gri5ybjukYZeyZwKkFkqgATqBQiOs= github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3/go.mod h1:lRgtFVamD7L7GaXOSwBiuXMwU3Aicfn5h66LVs4u2SA= diff --git a/node_exporter.go b/node_exporter.go index b48170c16c..6e92cc396f 100644 --- a/node_exporter.go +++ b/node_exporter.go @@ -15,13 +15,17 @@ package main import ( "fmt" + "github.com/prometheus/common/promlog" + "github.com/prometheus/common/promlog/flag" "net/http" _ "net/http/pprof" + "os" "sort" + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prometheus/common/log" "github.com/prometheus/common/version" "github.com/prometheus/node_exporter/collector" "github.com/prometheus/node_exporter/https" @@ -38,13 +42,15 @@ type handler struct { exporterMetricsRegistry *prometheus.Registry includeExporterMetrics bool maxRequests int + logger log.Logger } -func newHandler(includeExporterMetrics bool, maxRequests int) *handler { +func newHandler(includeExporterMetrics bool, maxRequests int, logger log.Logger) *handler { h := &handler{ exporterMetricsRegistry: prometheus.NewRegistry(), includeExporterMetrics: includeExporterMetrics, maxRequests: maxRequests, + logger: logger, } if h.includeExporterMetrics { h.exporterMetricsRegistry.MustRegister( @@ -53,7 +59,7 @@ func newHandler(includeExporterMetrics bool, maxRequests int) *handler { ) } if innerHandler, err := h.innerHandler(); err != nil { - log.Fatalf("Couldn't create metrics handler: %s", err) + panic(fmt.Sprintf("Couldn't create metrics handler: %s", err)) } else { h.unfilteredHandler = innerHandler } @@ -63,7 +69,7 @@ func newHandler(includeExporterMetrics bool, maxRequests int) *handler { // ServeHTTP implements http.Handler. func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { filters := r.URL.Query()["collect[]"] - log.Debugln("collect query:", filters) + level.Debug(h.logger).Log("msg", "collect query:", "filters", filters) if len(filters) == 0 { // No filters, use the prepared unfiltered handler. @@ -73,7 +79,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // To serve filtered metrics, we create a filtering handler on the fly. filteredHandler, err := h.innerHandler(filters...) if err != nil { - log.Warnln("Couldn't create filtered metrics handler:", err) + level.Warn(h.logger).Log("msg", "Couldn't create filtered metrics handler:", "err", err) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err))) return @@ -81,13 +87,13 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { filteredHandler.ServeHTTP(w, r) } -// innerHandler is used to create buth the one unfiltered http.Handler to be +// innerHandler is used to create both the one unfiltered http.Handler to be // wrapped by the outer handler and also the filtered handlers created on the // fly. The former is accomplished by calling innerHandler without any arguments // (in which case it will log all the collectors enabled via command-line // flags). func (h *handler) innerHandler(filters ...string) (http.Handler, error) { - nc, err := collector.NewNodeCollector(filters...) + nc, err := collector.NewNodeCollector(h.logger, filters...) if err != nil { return nil, fmt.Errorf("couldn't create collector: %s", err) } @@ -95,14 +101,14 @@ func (h *handler) innerHandler(filters ...string) (http.Handler, error) { // Only log the creation of an unfiltered handler, which should happen // only once upon startup. if len(filters) == 0 { - log.Infof("Enabled collectors:") + level.Info(h.logger).Log("msg", "Enabled collectors") collectors := []string{} for n := range nc.Collectors { collectors = append(collectors, n) } sort.Strings(collectors) - for _, n := range collectors { - log.Infof(" - %s", n) + for _, c := range collectors { + level.Info(h.logger).Log("collector", c) } } @@ -114,7 +120,6 @@ func (h *handler) innerHandler(filters ...string) (http.Handler, error) { handler := promhttp.HandlerFor( prometheus.Gatherers{h.exporterMetricsRegistry, r}, promhttp.HandlerOpts{ - ErrorLog: log.NewErrorLogger(), ErrorHandling: promhttp.ContinueOnError, MaxRequestsInFlight: h.maxRequests, Registry: h.exporterMetricsRegistry, @@ -154,15 +159,17 @@ func main() { ).Default("").String() ) - log.AddFlags(kingpin.CommandLine) + promlogConfig := &promlog.Config{} + flag.AddFlags(kingpin.CommandLine, promlogConfig) kingpin.Version(version.Print("node_exporter")) kingpin.HelpFlag.Short('h') kingpin.Parse() + logger := promlog.New(promlogConfig) - log.Infoln("Starting node_exporter", version.Info()) - log.Infoln("Build context", version.BuildContext()) + level.Info(logger).Log("msg", "Starting node_exporter", "version", version.Info()) + level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) - http.Handle(*metricsPath, newHandler(!*disableExporterMetrics, *maxRequests)) + http.Handle(*metricsPath, newHandler(!*disableExporterMetrics, *maxRequests, logger)) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` Node Exporter @@ -173,9 +180,10 @@ func main() { `)) }) - log.Infoln("Listening on", *listenAddress) + level.Info(logger).Log("msg", "Listening on", "address", *listenAddress) server := &http.Server{Addr: *listenAddress} if err := https.Listen(server, *configFile); err != nil { - log.Fatal(err) + level.Error(logger).Log("err", err) + os.Exit(1) } } diff --git a/scripts/errcheck_excludes.txt b/scripts/errcheck_excludes.txt index 2cadaa5382..ed0736fbab 100644 --- a/scripts/errcheck_excludes.txt +++ b/scripts/errcheck_excludes.txt @@ -1,2 +1,4 @@ // Used in HTTP handlers, any error is handled by the server itself. (net/http.ResponseWriter).Write +// Never check for logger errors. +(github.com/go-kit/kit/log.Logger).Log \ No newline at end of file diff --git a/vendor/github.com/sirupsen/logrus/LICENSE b/vendor/github.com/go-kit/kit/LICENSE similarity index 88% rename from vendor/github.com/sirupsen/logrus/LICENSE rename to vendor/github.com/go-kit/kit/LICENSE index f090cb42f3..9d83342acd 100644 --- a/vendor/github.com/sirupsen/logrus/LICENSE +++ b/vendor/github.com/go-kit/kit/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Simon Eskildsen +Copyright (c) 2015 Peter Bourgon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,14 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/go-kit/kit/log/README.md b/vendor/github.com/go-kit/kit/log/README.md new file mode 100644 index 0000000000..a201a3d922 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/README.md @@ -0,0 +1,151 @@ +# package log + +`package log` provides a minimal interface for structured logging in services. +It may be wrapped to encode conventions, enforce type-safety, provide leveled +logging, and so on. It can be used for both typical application log events, +and log-structured data streams. + +## Structured logging + +Structured logging is, basically, conceding to the reality that logs are +_data_, and warrant some level of schematic rigor. Using a stricter, +key/value-oriented message format for our logs, containing contextual and +semantic information, makes it much easier to get insight into the +operational activity of the systems we build. Consequently, `package log` is +of the strong belief that "[the benefits of structured logging outweigh the +minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)". + +Migrating from unstructured to structured logging is probably a lot easier +than you'd expect. + +```go +// Unstructured +log.Printf("HTTP server listening on %s", addr) + +// Structured +logger.Log("transport", "HTTP", "addr", addr, "msg", "listening") +``` + +## Usage + +### Typical application logging + +```go +w := log.NewSyncWriter(os.Stderr) +logger := log.NewLogfmtLogger(w) +logger.Log("question", "what is the meaning of life?", "answer", 42) + +// Output: +// question="what is the meaning of life?" answer=42 +``` + +### Contextual Loggers + +```go +func main() { + var logger log.Logger + logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) + logger = log.With(logger, "instance_id", 123) + + logger.Log("msg", "starting") + NewWorker(log.With(logger, "component", "worker")).Run() + NewSlacker(log.With(logger, "component", "slacker")).Run() +} + +// Output: +// instance_id=123 msg=starting +// instance_id=123 component=worker msg=running +// instance_id=123 component=slacker msg=running +``` + +### Interact with stdlib logger + +Redirect stdlib logger to Go kit logger. + +```go +import ( + "os" + stdlog "log" + kitlog "github.com/go-kit/kit/log" +) + +func main() { + logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout)) + stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) + stdlog.Print("I sure like pie") +} + +// Output: +// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"} +``` + +Or, if, for legacy reasons, you need to pipe all of your logging through the +stdlib log package, you can redirect Go kit logger to the stdlib logger. + +```go +logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{}) +logger.Log("legacy", true, "msg", "at least it's something") + +// Output: +// 2016/01/01 12:34:56 legacy=true msg="at least it's something" +``` + +### Timestamps and callers + +```go +var logger log.Logger +logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) +logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) + +logger.Log("msg", "hello") + +// Output: +// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello +``` + +## Levels + +Log levels are supported via the [level package](https://godoc.org/github.com/go-kit/kit/log/level). + +## Supported output formats + +- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write)) +- JSON + +## Enhancements + +`package log` is centered on the one-method Logger interface. + +```go +type Logger interface { + Log(keyvals ...interface{}) error +} +``` + +This interface, and its supporting code like is the product of much iteration +and evaluation. For more details on the evolution of the Logger interface, +see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1), +a talk by [Chris Hines](https://github.com/ChrisHines). +Also, please see +[#63](https://github.com/go-kit/kit/issues/63), +[#76](https://github.com/go-kit/kit/pull/76), +[#131](https://github.com/go-kit/kit/issues/131), +[#157](https://github.com/go-kit/kit/pull/157), +[#164](https://github.com/go-kit/kit/issues/164), and +[#252](https://github.com/go-kit/kit/pull/252) +to review historical conversations about package log and the Logger interface. + +Value-add packages and suggestions, +like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level), +are of course welcome. Good proposals should + +- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With), +- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and +- Be friendly to packages that accept only an unadorned log.Logger. + +## Benchmarks & comparisons + +There are a few Go logging benchmarks and comparisons that include Go kit's package log. + +- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log +- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log diff --git a/vendor/github.com/go-kit/kit/log/doc.go b/vendor/github.com/go-kit/kit/log/doc.go new file mode 100644 index 0000000000..918c0af46f --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/doc.go @@ -0,0 +1,116 @@ +// Package log provides a structured logger. +// +// Structured logging produces logs easily consumed later by humans or +// machines. Humans might be interested in debugging errors, or tracing +// specific requests. Machines might be interested in counting interesting +// events, or aggregating information for off-line processing. In both cases, +// it is important that the log messages are structured and actionable. +// Package log is designed to encourage both of these best practices. +// +// Basic Usage +// +// The fundamental interface is Logger. Loggers create log events from +// key/value data. The Logger interface has a single method, Log, which +// accepts a sequence of alternating key/value pairs, which this package names +// keyvals. +// +// type Logger interface { +// Log(keyvals ...interface{}) error +// } +// +// Here is an example of a function using a Logger to create log events. +// +// func RunTask(task Task, logger log.Logger) string { +// logger.Log("taskID", task.ID, "event", "starting task") +// ... +// logger.Log("taskID", task.ID, "event", "task complete") +// } +// +// The keys in the above example are "taskID" and "event". The values are +// task.ID, "starting task", and "task complete". Every key is followed +// immediately by its value. +// +// Keys are usually plain strings. Values may be any type that has a sensible +// encoding in the chosen log format. With structured logging it is a good +// idea to log simple values without formatting them. This practice allows +// the chosen logger to encode values in the most appropriate way. +// +// Contextual Loggers +// +// A contextual logger stores keyvals that it includes in all log events. +// Building appropriate contextual loggers reduces repetition and aids +// consistency in the resulting log output. With and WithPrefix add context to +// a logger. We can use With to improve the RunTask example. +// +// func RunTask(task Task, logger log.Logger) string { +// logger = log.With(logger, "taskID", task.ID) +// logger.Log("event", "starting task") +// ... +// taskHelper(task.Cmd, logger) +// ... +// logger.Log("event", "task complete") +// } +// +// The improved version emits the same log events as the original for the +// first and last calls to Log. Passing the contextual logger to taskHelper +// enables each log event created by taskHelper to include the task.ID even +// though taskHelper does not have access to that value. Using contextual +// loggers this way simplifies producing log output that enables tracing the +// life cycle of individual tasks. (See the Contextual example for the full +// code of the above snippet.) +// +// Dynamic Contextual Values +// +// A Valuer function stored in a contextual logger generates a new value each +// time an event is logged. The Valuer example demonstrates how this feature +// works. +// +// Valuers provide the basis for consistently logging timestamps and source +// code location. The log package defines several valuers for that purpose. +// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and +// DefaultCaller. A common logger initialization sequence that ensures all log +// entries contain a timestamp and source location looks like this: +// +// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) +// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) +// +// Concurrent Safety +// +// Applications with multiple goroutines want each log event written to the +// same logger to remain separate from other log events. Package log provides +// two simple solutions for concurrent safe logging. +// +// NewSyncWriter wraps an io.Writer and serializes each call to its Write +// method. Using a SyncWriter has the benefit that the smallest practical +// portion of the logging logic is performed within a mutex, but it requires +// the formatting Logger to make only one call to Write per log event. +// +// NewSyncLogger wraps any Logger and serializes each call to its Log method. +// Using a SyncLogger has the benefit that it guarantees each log event is +// handled atomically within the wrapped logger, but it typically serializes +// both the formatting and output logic. Use a SyncLogger if the formatting +// logger may perform multiple writes per log event. +// +// Error Handling +// +// This package relies on the practice of wrapping or decorating loggers with +// other loggers to provide composable pieces of functionality. It also means +// that Logger.Log must return an error because some +// implementations—especially those that output log data to an io.Writer—may +// encounter errors that cannot be handled locally. This in turn means that +// Loggers that wrap other loggers should return errors from the wrapped +// logger up the stack. +// +// Fortunately, the decorator pattern also provides a way to avoid the +// necessity to check for errors every time an application calls Logger.Log. +// An application required to panic whenever its Logger encounters +// an error could initialize its logger as follows. +// +// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) +// logger := log.LoggerFunc(func(keyvals ...interface{}) error { +// if err := fmtlogger.Log(keyvals...); err != nil { +// panic(err) +// } +// return nil +// }) +package log diff --git a/vendor/github.com/go-kit/kit/log/json_logger.go b/vendor/github.com/go-kit/kit/log/json_logger.go new file mode 100644 index 0000000000..66094b4dd3 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/json_logger.go @@ -0,0 +1,89 @@ +package log + +import ( + "encoding" + "encoding/json" + "fmt" + "io" + "reflect" +) + +type jsonLogger struct { + io.Writer +} + +// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a +// single JSON object. Each log event produces no more than one call to +// w.Write. The passed Writer must be safe for concurrent use by multiple +// goroutines if the returned Logger will be used concurrently. +func NewJSONLogger(w io.Writer) Logger { + return &jsonLogger{w} +} + +func (l *jsonLogger) Log(keyvals ...interface{}) error { + n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd + m := make(map[string]interface{}, n) + for i := 0; i < len(keyvals); i += 2 { + k := keyvals[i] + var v interface{} = ErrMissingValue + if i+1 < len(keyvals) { + v = keyvals[i+1] + } + merge(m, k, v) + } + return json.NewEncoder(l.Writer).Encode(m) +} + +func merge(dst map[string]interface{}, k, v interface{}) { + var key string + switch x := k.(type) { + case string: + key = x + case fmt.Stringer: + key = safeString(x) + default: + key = fmt.Sprint(x) + } + + // We want json.Marshaler and encoding.TextMarshaller to take priority over + // err.Error() and v.String(). But json.Marshall (called later) does that by + // default so we force a no-op if it's one of those 2 case. + switch x := v.(type) { + case json.Marshaler: + case encoding.TextMarshaler: + case error: + v = safeError(x) + case fmt.Stringer: + v = safeString(x) + } + + dst[key] = v +} + +func safeString(str fmt.Stringer) (s string) { + defer func() { + if panicVal := recover(); panicVal != nil { + if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { + s = "NULL" + } else { + panic(panicVal) + } + } + }() + s = str.String() + return +} + +func safeError(err error) (s interface{}) { + defer func() { + if panicVal := recover(); panicVal != nil { + if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { + s = nil + } else { + panic(panicVal) + } + } + }() + s = err.Error() + return +} diff --git a/vendor/github.com/go-kit/kit/log/level/doc.go b/vendor/github.com/go-kit/kit/log/level/doc.go new file mode 100644 index 0000000000..505d307b11 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/level/doc.go @@ -0,0 +1,22 @@ +// Package level implements leveled logging on top of Go kit's log package. To +// use the level package, create a logger as per normal in your func main, and +// wrap it with level.NewFilter. +// +// var logger log.Logger +// logger = log.NewLogfmtLogger(os.Stderr) +// logger = level.NewFilter(logger, level.AllowInfo()) // <-- +// logger = log.With(logger, "ts", log.DefaultTimestampUTC) +// +// Then, at the callsites, use one of the level.Debug, Info, Warn, or Error +// helper methods to emit leveled log events. +// +// logger.Log("foo", "bar") // as normal, no level +// level.Debug(logger).Log("request_id", reqID, "trace_data", trace.Get()) +// if value > 100 { +// level.Error(logger).Log("value", value) +// } +// +// NewFilter allows precise control over what happens when a log event is +// emitted without a level key, or if a squelched level is used. Check the +// Option functions for details. +package level diff --git a/vendor/github.com/go-kit/kit/log/level/level.go b/vendor/github.com/go-kit/kit/log/level/level.go new file mode 100644 index 0000000000..fceafc454a --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/level/level.go @@ -0,0 +1,205 @@ +package level + +import "github.com/go-kit/kit/log" + +// Error returns a logger that includes a Key/ErrorValue pair. +func Error(logger log.Logger) log.Logger { + return log.WithPrefix(logger, Key(), ErrorValue()) +} + +// Warn returns a logger that includes a Key/WarnValue pair. +func Warn(logger log.Logger) log.Logger { + return log.WithPrefix(logger, Key(), WarnValue()) +} + +// Info returns a logger that includes a Key/InfoValue pair. +func Info(logger log.Logger) log.Logger { + return log.WithPrefix(logger, Key(), InfoValue()) +} + +// Debug returns a logger that includes a Key/DebugValue pair. +func Debug(logger log.Logger) log.Logger { + return log.WithPrefix(logger, Key(), DebugValue()) +} + +// NewFilter wraps next and implements level filtering. See the commentary on +// the Option functions for a detailed description of how to configure levels. +// If no options are provided, all leveled log events created with Debug, +// Info, Warn or Error helper methods are squelched and non-leveled log +// events are passed to next unmodified. +func NewFilter(next log.Logger, options ...Option) log.Logger { + l := &logger{ + next: next, + } + for _, option := range options { + option(l) + } + return l +} + +type logger struct { + next log.Logger + allowed level + squelchNoLevel bool + errNotAllowed error + errNoLevel error +} + +func (l *logger) Log(keyvals ...interface{}) error { + var hasLevel, levelAllowed bool + for i := 1; i < len(keyvals); i += 2 { + if v, ok := keyvals[i].(*levelValue); ok { + hasLevel = true + levelAllowed = l.allowed&v.level != 0 + break + } + } + if !hasLevel && l.squelchNoLevel { + return l.errNoLevel + } + if hasLevel && !levelAllowed { + return l.errNotAllowed + } + return l.next.Log(keyvals...) +} + +// Option sets a parameter for the leveled logger. +type Option func(*logger) + +// AllowAll is an alias for AllowDebug. +func AllowAll() Option { + return AllowDebug() +} + +// AllowDebug allows error, warn, info and debug level log events to pass. +func AllowDebug() Option { + return allowed(levelError | levelWarn | levelInfo | levelDebug) +} + +// AllowInfo allows error, warn and info level log events to pass. +func AllowInfo() Option { + return allowed(levelError | levelWarn | levelInfo) +} + +// AllowWarn allows error and warn level log events to pass. +func AllowWarn() Option { + return allowed(levelError | levelWarn) +} + +// AllowError allows only error level log events to pass. +func AllowError() Option { + return allowed(levelError) +} + +// AllowNone allows no leveled log events to pass. +func AllowNone() Option { + return allowed(0) +} + +func allowed(allowed level) Option { + return func(l *logger) { l.allowed = allowed } +} + +// ErrNotAllowed sets the error to return from Log when it squelches a log +// event disallowed by the configured Allow[Level] option. By default, +// ErrNotAllowed is nil; in this case the log event is squelched with no +// error. +func ErrNotAllowed(err error) Option { + return func(l *logger) { l.errNotAllowed = err } +} + +// SquelchNoLevel instructs Log to squelch log events with no level, so that +// they don't proceed through to the wrapped logger. If SquelchNoLevel is set +// to true and a log event is squelched in this way, the error value +// configured with ErrNoLevel is returned to the caller. +func SquelchNoLevel(squelch bool) Option { + return func(l *logger) { l.squelchNoLevel = squelch } +} + +// ErrNoLevel sets the error to return from Log when it squelches a log event +// with no level. By default, ErrNoLevel is nil; in this case the log event is +// squelched with no error. +func ErrNoLevel(err error) Option { + return func(l *logger) { l.errNoLevel = err } +} + +// NewInjector wraps next and returns a logger that adds a Key/level pair to +// the beginning of log events that don't already contain a level. In effect, +// this gives a default level to logs without a level. +func NewInjector(next log.Logger, level Value) log.Logger { + return &injector{ + next: next, + level: level, + } +} + +type injector struct { + next log.Logger + level interface{} +} + +func (l *injector) Log(keyvals ...interface{}) error { + for i := 1; i < len(keyvals); i += 2 { + if _, ok := keyvals[i].(*levelValue); ok { + return l.next.Log(keyvals...) + } + } + kvs := make([]interface{}, len(keyvals)+2) + kvs[0], kvs[1] = key, l.level + copy(kvs[2:], keyvals) + return l.next.Log(kvs...) +} + +// Value is the interface that each of the canonical level values implement. +// It contains unexported methods that prevent types from other packages from +// implementing it and guaranteeing that NewFilter can distinguish the levels +// defined in this package from all other values. +type Value interface { + String() string + levelVal() +} + +// Key returns the unique key added to log events by the loggers in this +// package. +func Key() interface{} { return key } + +// ErrorValue returns the unique value added to log events by Error. +func ErrorValue() Value { return errorValue } + +// WarnValue returns the unique value added to log events by Warn. +func WarnValue() Value { return warnValue } + +// InfoValue returns the unique value added to log events by Info. +func InfoValue() Value { return infoValue } + +// DebugValue returns the unique value added to log events by Warn. +func DebugValue() Value { return debugValue } + +var ( + // key is of type interface{} so that it allocates once during package + // initialization and avoids allocating every time the value is added to a + // []interface{} later. + key interface{} = "level" + + errorValue = &levelValue{level: levelError, name: "error"} + warnValue = &levelValue{level: levelWarn, name: "warn"} + infoValue = &levelValue{level: levelInfo, name: "info"} + debugValue = &levelValue{level: levelDebug, name: "debug"} +) + +type level byte + +const ( + levelDebug level = 1 << iota + levelInfo + levelWarn + levelError +) + +type levelValue struct { + name string + level +} + +func (v *levelValue) String() string { return v.name } +func (v *levelValue) levelVal() {} diff --git a/vendor/github.com/go-kit/kit/log/log.go b/vendor/github.com/go-kit/kit/log/log.go new file mode 100644 index 0000000000..66a9e2fde7 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/log.go @@ -0,0 +1,135 @@ +package log + +import "errors" + +// Logger is the fundamental interface for all log operations. Log creates a +// log event from keyvals, a variadic sequence of alternating keys and values. +// Implementations must be safe for concurrent use by multiple goroutines. In +// particular, any implementation of Logger that appends to keyvals or +// modifies or retains any of its elements must make a copy first. +type Logger interface { + Log(keyvals ...interface{}) error +} + +// ErrMissingValue is appended to keyvals slices with odd length to substitute +// the missing value. +var ErrMissingValue = errors.New("(MISSING)") + +// With returns a new contextual logger with keyvals prepended to those passed +// to calls to Log. If logger is also a contextual logger created by With or +// WithPrefix, keyvals is appended to the existing context. +// +// The returned Logger replaces all value elements (odd indexes) containing a +// Valuer with their generated value for each call to its Log method. +func With(logger Logger, keyvals ...interface{}) Logger { + if len(keyvals) == 0 { + return logger + } + l := newContext(logger) + kvs := append(l.keyvals, keyvals...) + if len(kvs)%2 != 0 { + kvs = append(kvs, ErrMissingValue) + } + return &context{ + logger: l.logger, + // Limiting the capacity of the stored keyvals ensures that a new + // backing array is created if the slice must grow in Log or With. + // Using the extra capacity without copying risks a data race that + // would violate the Logger interface contract. + keyvals: kvs[:len(kvs):len(kvs)], + hasValuer: l.hasValuer || containsValuer(keyvals), + } +} + +// WithPrefix returns a new contextual logger with keyvals prepended to those +// passed to calls to Log. If logger is also a contextual logger created by +// With or WithPrefix, keyvals is prepended to the existing context. +// +// The returned Logger replaces all value elements (odd indexes) containing a +// Valuer with their generated value for each call to its Log method. +func WithPrefix(logger Logger, keyvals ...interface{}) Logger { + if len(keyvals) == 0 { + return logger + } + l := newContext(logger) + // Limiting the capacity of the stored keyvals ensures that a new + // backing array is created if the slice must grow in Log or With. + // Using the extra capacity without copying risks a data race that + // would violate the Logger interface contract. + n := len(l.keyvals) + len(keyvals) + if len(keyvals)%2 != 0 { + n++ + } + kvs := make([]interface{}, 0, n) + kvs = append(kvs, keyvals...) + if len(kvs)%2 != 0 { + kvs = append(kvs, ErrMissingValue) + } + kvs = append(kvs, l.keyvals...) + return &context{ + logger: l.logger, + keyvals: kvs, + hasValuer: l.hasValuer || containsValuer(keyvals), + } +} + +// context is the Logger implementation returned by With and WithPrefix. It +// wraps a Logger and holds keyvals that it includes in all log events. Its +// Log method calls bindValues to generate values for each Valuer in the +// context keyvals. +// +// A context must always have the same number of stack frames between calls to +// its Log method and the eventual binding of Valuers to their value. This +// requirement comes from the functional requirement to allow a context to +// resolve application call site information for a Caller stored in the +// context. To do this we must be able to predict the number of logging +// functions on the stack when bindValues is called. +// +// Two implementation details provide the needed stack depth consistency. +// +// 1. newContext avoids introducing an additional layer when asked to +// wrap another context. +// 2. With and WithPrefix avoid introducing an additional layer by +// returning a newly constructed context with a merged keyvals rather +// than simply wrapping the existing context. +type context struct { + logger Logger + keyvals []interface{} + hasValuer bool +} + +func newContext(logger Logger) *context { + if c, ok := logger.(*context); ok { + return c + } + return &context{logger: logger} +} + +// Log replaces all value elements (odd indexes) containing a Valuer in the +// stored context with their generated value, appends keyvals, and passes the +// result to the wrapped Logger. +func (l *context) Log(keyvals ...interface{}) error { + kvs := append(l.keyvals, keyvals...) + if len(kvs)%2 != 0 { + kvs = append(kvs, ErrMissingValue) + } + if l.hasValuer { + // If no keyvals were appended above then we must copy l.keyvals so + // that future log events will reevaluate the stored Valuers. + if len(keyvals) == 0 { + kvs = append([]interface{}{}, l.keyvals...) + } + bindValues(kvs[:len(l.keyvals)]) + } + return l.logger.Log(kvs...) +} + +// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If +// f is a function with the appropriate signature, LoggerFunc(f) is a Logger +// object that calls f. +type LoggerFunc func(...interface{}) error + +// Log implements Logger by calling f(keyvals...). +func (f LoggerFunc) Log(keyvals ...interface{}) error { + return f(keyvals...) +} diff --git a/vendor/github.com/go-kit/kit/log/logfmt_logger.go b/vendor/github.com/go-kit/kit/log/logfmt_logger.go new file mode 100644 index 0000000000..a00305298b --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/logfmt_logger.go @@ -0,0 +1,62 @@ +package log + +import ( + "bytes" + "io" + "sync" + + "github.com/go-logfmt/logfmt" +) + +type logfmtEncoder struct { + *logfmt.Encoder + buf bytes.Buffer +} + +func (l *logfmtEncoder) Reset() { + l.Encoder.Reset() + l.buf.Reset() +} + +var logfmtEncoderPool = sync.Pool{ + New: func() interface{} { + var enc logfmtEncoder + enc.Encoder = logfmt.NewEncoder(&enc.buf) + return &enc + }, +} + +type logfmtLogger struct { + w io.Writer +} + +// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in +// logfmt format. Each log event produces no more than one call to w.Write. +// The passed Writer must be safe for concurrent use by multiple goroutines if +// the returned Logger will be used concurrently. +func NewLogfmtLogger(w io.Writer) Logger { + return &logfmtLogger{w} +} + +func (l logfmtLogger) Log(keyvals ...interface{}) error { + enc := logfmtEncoderPool.Get().(*logfmtEncoder) + enc.Reset() + defer logfmtEncoderPool.Put(enc) + + if err := enc.EncodeKeyvals(keyvals...); err != nil { + return err + } + + // Add newline to the end of the buffer + if err := enc.EndRecord(); err != nil { + return err + } + + // The Logger interface requires implementations to be safe for concurrent + // use by multiple goroutines. For this implementation that means making + // only one call to l.w.Write() for each call to Log. + if _, err := l.w.Write(enc.buf.Bytes()); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/go-kit/kit/log/nop_logger.go b/vendor/github.com/go-kit/kit/log/nop_logger.go new file mode 100644 index 0000000000..1047d626c4 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/nop_logger.go @@ -0,0 +1,8 @@ +package log + +type nopLogger struct{} + +// NewNopLogger returns a logger that doesn't do anything. +func NewNopLogger() Logger { return nopLogger{} } + +func (nopLogger) Log(...interface{}) error { return nil } diff --git a/vendor/github.com/go-kit/kit/log/stdlib.go b/vendor/github.com/go-kit/kit/log/stdlib.go new file mode 100644 index 0000000000..ff96b5dee5 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/stdlib.go @@ -0,0 +1,116 @@ +package log + +import ( + "io" + "log" + "regexp" + "strings" +) + +// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's +// designed to be passed to a Go kit logger as the writer, for cases where +// it's necessary to redirect all Go kit log output to the stdlib logger. +// +// If you have any choice in the matter, you shouldn't use this. Prefer to +// redirect the stdlib log to the Go kit logger via NewStdlibAdapter. +type StdlibWriter struct{} + +// Write implements io.Writer. +func (w StdlibWriter) Write(p []byte) (int, error) { + log.Print(strings.TrimSpace(string(p))) + return len(p), nil +} + +// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib +// logger's SetOutput. It will extract date/timestamps, filenames, and +// messages, and place them under relevant keys. +type StdlibAdapter struct { + Logger + timestampKey string + fileKey string + messageKey string +} + +// StdlibAdapterOption sets a parameter for the StdlibAdapter. +type StdlibAdapterOption func(*StdlibAdapter) + +// TimestampKey sets the key for the timestamp field. By default, it's "ts". +func TimestampKey(key string) StdlibAdapterOption { + return func(a *StdlibAdapter) { a.timestampKey = key } +} + +// FileKey sets the key for the file and line field. By default, it's "caller". +func FileKey(key string) StdlibAdapterOption { + return func(a *StdlibAdapter) { a.fileKey = key } +} + +// MessageKey sets the key for the actual log message. By default, it's "msg". +func MessageKey(key string) StdlibAdapterOption { + return func(a *StdlibAdapter) { a.messageKey = key } +} + +// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed +// logger. It's designed to be passed to log.SetOutput. +func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { + a := StdlibAdapter{ + Logger: logger, + timestampKey: "ts", + fileKey: "caller", + messageKey: "msg", + } + for _, option := range options { + option(&a) + } + return a +} + +func (a StdlibAdapter) Write(p []byte) (int, error) { + result := subexps(p) + keyvals := []interface{}{} + var timestamp string + if date, ok := result["date"]; ok && date != "" { + timestamp = date + } + if time, ok := result["time"]; ok && time != "" { + if timestamp != "" { + timestamp += " " + } + timestamp += time + } + if timestamp != "" { + keyvals = append(keyvals, a.timestampKey, timestamp) + } + if file, ok := result["file"]; ok && file != "" { + keyvals = append(keyvals, a.fileKey, file) + } + if msg, ok := result["msg"]; ok { + keyvals = append(keyvals, a.messageKey, msg) + } + if err := a.Logger.Log(keyvals...); err != nil { + return 0, err + } + return len(p), nil +} + +const ( + logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` + logRegexpTime = `(?P