├── .gitignore ├── .travis.yml ├── COPYRIGHT ├── DCO ├── LICENSE ├── README.md ├── bors.toml ├── dist ├── Dockerfile └── examples │ └── config.toml ├── docs └── design.md ├── go.mod ├── go.sum ├── internal ├── backend │ ├── common.go │ ├── dbus.go │ ├── file.go │ └── uds.go ├── cli │ ├── common.go │ └── serve.go ├── config │ ├── backends.go │ ├── settings.go │ └── toml.go └── server │ ├── bridge.go │ ├── metrics.go │ └── server.go ├── local_exporter.go └── vendor ├── github.com ├── BurntSushi │ └── toml │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── COMPATIBLE │ │ ├── COPYING │ │ ├── Makefile │ │ ├── README.md │ │ ├── decode.go │ │ ├── decode_meta.go │ │ ├── doc.go │ │ ├── encode.go │ │ ├── encoding_types.go │ │ ├── encoding_types_1.1.go │ │ ├── lex.go │ │ ├── parse.go │ │ ├── session.vim │ │ ├── type_check.go │ │ └── type_fields.go ├── godbus │ └── dbus │ │ ├── .travis.yml │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── MAINTAINERS │ │ ├── README.markdown │ │ ├── auth.go │ │ ├── auth_anonymous.go │ │ ├── auth_external.go │ │ ├── auth_sha1.go │ │ ├── call.go │ │ ├── conn.go │ │ ├── conn_darwin.go │ │ ├── conn_other.go │ │ ├── conn_unix.go │ │ ├── conn_windows.go │ │ ├── dbus.go │ │ ├── decoder.go │ │ ├── default_handler.go │ │ ├── doc.go │ │ ├── encoder.go │ │ ├── export.go │ │ ├── go.mod │ │ ├── homedir.go │ │ ├── homedir_dynamic.go │ │ ├── homedir_static.go │ │ ├── message.go │ │ ├── object.go │ │ ├── server_interfaces.go │ │ ├── sig.go │ │ ├── transport_darwin.go │ │ ├── transport_generic.go │ │ ├── transport_nonce_tcp.go │ │ ├── transport_tcp.go │ │ ├── transport_unix.go │ │ ├── transport_unixcred_dragonfly.go │ │ ├── transport_unixcred_freebsd.go │ │ ├── transport_unixcred_linux.go │ │ ├── transport_unixcred_openbsd.go │ │ ├── variant.go │ │ ├── variant_lexer.go │ │ └── variant_parser.go ├── inconshreveable │ └── mousetrap │ │ ├── LICENSE │ │ ├── README.md │ │ ├── trap_others.go │ │ ├── trap_windows.go │ │ └── trap_windows_1.4.go ├── konsorten │ └── go-windows-terminal-sequences │ │ ├── LICENSE │ │ ├── README.md │ │ ├── go.mod │ │ └── sequences.go ├── sirupsen │ └── logrus │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── alt_exit.go │ │ ├── appveyor.yml │ │ ├── doc.go │ │ ├── entry.go │ │ ├── exported.go │ │ ├── formatter.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── hooks.go │ │ ├── json_formatter.go │ │ ├── logger.go │ │ ├── logrus.go │ │ ├── terminal_check_appengine.go │ │ ├── terminal_check_bsd.go │ │ ├── terminal_check_no_terminal.go │ │ ├── terminal_check_notappengine.go │ │ ├── terminal_check_solaris.go │ │ ├── terminal_check_unix.go │ │ ├── terminal_check_windows.go │ │ ├── text_formatter.go │ │ └── writer.go └── spf13 │ ├── cobra │ ├── .gitignore │ ├── .mailmap │ ├── .travis.yml │ ├── LICENSE.txt │ ├── README.md │ ├── args.go │ ├── bash_completions.go │ ├── bash_completions.md │ ├── cobra.go │ ├── command.go │ ├── command_notwin.go │ ├── command_win.go │ ├── go.mod │ ├── go.sum │ └── zsh_completions.go │ └── pflag │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── bool.go │ ├── bool_slice.go │ ├── bytes.go │ ├── count.go │ ├── duration.go │ ├── duration_slice.go │ ├── flag.go │ ├── float32.go │ ├── float64.go │ ├── golangflag.go │ ├── int.go │ ├── int16.go │ ├── int32.go │ ├── int64.go │ ├── int8.go │ ├── int_slice.go │ ├── ip.go │ ├── ip_slice.go │ ├── ipmask.go │ ├── ipnet.go │ ├── string.go │ ├── string_array.go │ ├── string_slice.go │ ├── string_to_int.go │ ├── string_to_string.go │ ├── uint.go │ ├── uint16.go │ ├── uint32.go │ ├── uint64.go │ ├── uint8.go │ └── uint_slice.go ├── golang.org └── x │ └── sys │ ├── AUTHORS │ ├── CONTRIBUTORS │ ├── LICENSE │ ├── PATENTS │ └── unix │ ├── .gitignore │ ├── README.md │ ├── affinity_linux.go │ ├── aliases.go │ ├── asm_aix_ppc64.s │ ├── asm_darwin_386.s │ ├── asm_darwin_amd64.s │ ├── asm_darwin_arm.s │ ├── asm_darwin_arm64.s │ ├── asm_dragonfly_amd64.s │ ├── asm_freebsd_386.s │ ├── asm_freebsd_amd64.s │ ├── asm_freebsd_arm.s │ ├── asm_freebsd_arm64.s │ ├── asm_linux_386.s │ ├── asm_linux_amd64.s │ ├── asm_linux_arm.s │ ├── asm_linux_arm64.s │ ├── asm_linux_mips64x.s │ ├── asm_linux_mipsx.s │ ├── asm_linux_ppc64x.s │ ├── asm_linux_s390x.s │ ├── asm_netbsd_386.s │ ├── asm_netbsd_amd64.s │ ├── asm_netbsd_arm.s │ ├── asm_netbsd_arm64.s │ ├── asm_openbsd_386.s │ ├── asm_openbsd_amd64.s │ ├── asm_openbsd_arm.s │ ├── asm_solaris_amd64.s │ ├── bluetooth_linux.go │ ├── cap_freebsd.go │ ├── constants.go │ ├── dev_aix_ppc.go │ ├── dev_aix_ppc64.go │ ├── dev_darwin.go │ ├── dev_dragonfly.go │ ├── dev_freebsd.go │ ├── dev_linux.go │ ├── dev_netbsd.go │ ├── dev_openbsd.go │ ├── dirent.go │ ├── endian_big.go │ ├── endian_little.go │ ├── env_unix.go │ ├── errors_freebsd_386.go │ ├── errors_freebsd_amd64.go │ ├── errors_freebsd_arm.go │ ├── fcntl.go │ ├── fcntl_darwin.go │ ├── fcntl_linux_32bit.go │ ├── gccgo.go │ ├── gccgo_c.c │ ├── gccgo_linux_amd64.go │ ├── ioctl.go │ ├── mkall.sh │ ├── mkasm_darwin.go │ ├── mkerrors.sh │ ├── mkpost.go │ ├── mksyscall.go │ ├── mksyscall_aix_ppc.go │ ├── mksyscall_aix_ppc64.go │ ├── mksyscall_solaris.go │ ├── mksysctl_openbsd.pl │ ├── mksysnum.go │ ├── openbsd_pledge.go │ ├── openbsd_unveil.go │ ├── pagesize_unix.go │ ├── race.go │ ├── race0.go │ ├── sockcmsg_linux.go │ ├── sockcmsg_unix.go │ ├── str.go │ ├── syscall.go │ ├── syscall_aix.go │ ├── syscall_aix_ppc.go │ ├── syscall_aix_ppc64.go │ ├── syscall_bsd.go │ ├── syscall_darwin.go │ ├── syscall_darwin_386.go │ ├── syscall_darwin_amd64.go │ ├── syscall_darwin_arm.go │ ├── syscall_darwin_arm64.go │ ├── syscall_darwin_libSystem.go │ ├── syscall_dragonfly.go │ ├── syscall_dragonfly_amd64.go │ ├── syscall_freebsd.go │ ├── syscall_freebsd_386.go │ ├── syscall_freebsd_amd64.go │ ├── syscall_freebsd_arm.go │ ├── syscall_freebsd_arm64.go │ ├── syscall_linux.go │ ├── syscall_linux_386.go │ ├── syscall_linux_amd64.go │ ├── syscall_linux_amd64_gc.go │ ├── syscall_linux_arm.go │ ├── syscall_linux_arm64.go │ ├── syscall_linux_gc.go │ ├── syscall_linux_gc_386.go │ ├── syscall_linux_gccgo_386.go │ ├── syscall_linux_gccgo_arm.go │ ├── syscall_linux_mips64x.go │ ├── syscall_linux_mipsx.go │ ├── syscall_linux_ppc64x.go │ ├── syscall_linux_riscv64.go │ ├── syscall_linux_s390x.go │ ├── syscall_linux_sparc64.go │ ├── syscall_netbsd.go │ ├── syscall_netbsd_386.go │ ├── syscall_netbsd_amd64.go │ ├── syscall_netbsd_arm.go │ ├── syscall_netbsd_arm64.go │ ├── syscall_openbsd.go │ ├── syscall_openbsd_386.go │ ├── syscall_openbsd_amd64.go │ ├── syscall_openbsd_arm.go │ ├── syscall_solaris.go │ ├── syscall_solaris_amd64.go │ ├── syscall_unix.go │ ├── syscall_unix_gc.go │ ├── syscall_unix_gc_ppc64x.go │ ├── timestruct.go │ ├── types_aix.go │ ├── types_darwin.go │ ├── types_dragonfly.go │ ├── types_freebsd.go │ ├── types_netbsd.go │ ├── types_openbsd.go │ ├── types_solaris.go │ ├── xattr_bsd.go │ ├── zerrors_aix_ppc.go │ ├── zerrors_aix_ppc64.go │ ├── zerrors_darwin_386.go │ ├── zerrors_darwin_amd64.go │ ├── zerrors_darwin_arm.go │ ├── zerrors_darwin_arm64.go │ ├── zerrors_dragonfly_amd64.go │ ├── zerrors_freebsd_386.go │ ├── zerrors_freebsd_amd64.go │ ├── zerrors_freebsd_arm.go │ ├── zerrors_freebsd_arm64.go │ ├── zerrors_linux_386.go │ ├── zerrors_linux_amd64.go │ ├── zerrors_linux_arm.go │ ├── zerrors_linux_arm64.go │ ├── zerrors_linux_mips.go │ ├── zerrors_linux_mips64.go │ ├── zerrors_linux_mips64le.go │ ├── zerrors_linux_mipsle.go │ ├── zerrors_linux_ppc64.go │ ├── zerrors_linux_ppc64le.go │ ├── zerrors_linux_riscv64.go │ ├── zerrors_linux_s390x.go │ ├── zerrors_linux_sparc64.go │ ├── zerrors_netbsd_386.go │ ├── zerrors_netbsd_amd64.go │ ├── zerrors_netbsd_arm.go │ ├── zerrors_netbsd_arm64.go │ ├── zerrors_openbsd_386.go │ ├── zerrors_openbsd_amd64.go │ ├── zerrors_openbsd_arm.go │ ├── zerrors_solaris_amd64.go │ ├── zptrace386_linux.go │ ├── zptracearm_linux.go │ ├── zptracemips_linux.go │ ├── zptracemipsle_linux.go │ ├── zsyscall_aix_ppc.go │ ├── zsyscall_aix_ppc64.go │ ├── zsyscall_aix_ppc64_gc.go │ ├── zsyscall_aix_ppc64_gccgo.go │ ├── zsyscall_darwin_386.1_11.go │ ├── zsyscall_darwin_386.go │ ├── zsyscall_darwin_386.s │ ├── zsyscall_darwin_amd64.1_11.go │ ├── zsyscall_darwin_amd64.go │ ├── zsyscall_darwin_amd64.s │ ├── zsyscall_darwin_arm.1_11.go │ ├── zsyscall_darwin_arm.go │ ├── zsyscall_darwin_arm.s │ ├── zsyscall_darwin_arm64.1_11.go │ ├── zsyscall_darwin_arm64.go │ ├── zsyscall_darwin_arm64.s │ ├── zsyscall_dragonfly_amd64.go │ ├── zsyscall_freebsd_386.go │ ├── zsyscall_freebsd_amd64.go │ ├── zsyscall_freebsd_arm.go │ ├── zsyscall_freebsd_arm64.go │ ├── zsyscall_linux_386.go │ ├── zsyscall_linux_amd64.go │ ├── zsyscall_linux_arm.go │ ├── zsyscall_linux_arm64.go │ ├── zsyscall_linux_mips.go │ ├── zsyscall_linux_mips64.go │ ├── zsyscall_linux_mips64le.go │ ├── zsyscall_linux_mipsle.go │ ├── zsyscall_linux_ppc64.go │ ├── zsyscall_linux_ppc64le.go │ ├── zsyscall_linux_riscv64.go │ ├── zsyscall_linux_s390x.go │ ├── zsyscall_linux_sparc64.go │ ├── zsyscall_netbsd_386.go │ ├── zsyscall_netbsd_amd64.go │ ├── zsyscall_netbsd_arm.go │ ├── zsyscall_netbsd_arm64.go │ ├── zsyscall_openbsd_386.go │ ├── zsyscall_openbsd_amd64.go │ ├── zsyscall_openbsd_arm.go │ ├── zsyscall_solaris_amd64.go │ ├── zsysctl_openbsd_386.go │ ├── zsysctl_openbsd_amd64.go │ ├── zsysctl_openbsd_arm.go │ ├── zsysnum_darwin_386.go │ ├── zsysnum_darwin_amd64.go │ ├── zsysnum_darwin_arm.go │ ├── zsysnum_darwin_arm64.go │ ├── zsysnum_dragonfly_amd64.go │ ├── zsysnum_freebsd_386.go │ ├── zsysnum_freebsd_amd64.go │ ├── zsysnum_freebsd_arm.go │ ├── zsysnum_freebsd_arm64.go │ ├── zsysnum_linux_386.go │ ├── zsysnum_linux_amd64.go │ ├── zsysnum_linux_arm.go │ ├── zsysnum_linux_arm64.go │ ├── zsysnum_linux_mips.go │ ├── zsysnum_linux_mips64.go │ ├── zsysnum_linux_mips64le.go │ ├── zsysnum_linux_mipsle.go │ ├── zsysnum_linux_ppc64.go │ ├── zsysnum_linux_ppc64le.go │ ├── zsysnum_linux_riscv64.go │ ├── zsysnum_linux_s390x.go │ ├── zsysnum_linux_sparc64.go │ ├── zsysnum_netbsd_386.go │ ├── zsysnum_netbsd_amd64.go │ ├── zsysnum_netbsd_arm.go │ ├── zsysnum_netbsd_arm64.go │ ├── zsysnum_openbsd_386.go │ ├── zsysnum_openbsd_amd64.go │ ├── zsysnum_openbsd_arm.go │ ├── ztypes_aix_ppc.go │ ├── ztypes_aix_ppc64.go │ ├── ztypes_darwin_386.go │ ├── ztypes_darwin_amd64.go │ ├── ztypes_darwin_arm.go │ ├── ztypes_darwin_arm64.go │ ├── ztypes_dragonfly_amd64.go │ ├── ztypes_freebsd_386.go │ ├── ztypes_freebsd_amd64.go │ ├── ztypes_freebsd_arm.go │ ├── ztypes_freebsd_arm64.go │ ├── ztypes_linux_386.go │ ├── ztypes_linux_amd64.go │ ├── ztypes_linux_arm.go │ ├── ztypes_linux_arm64.go │ ├── ztypes_linux_mips.go │ ├── ztypes_linux_mips64.go │ ├── ztypes_linux_mips64le.go │ ├── ztypes_linux_mipsle.go │ ├── ztypes_linux_ppc64.go │ ├── ztypes_linux_ppc64le.go │ ├── ztypes_linux_riscv64.go │ ├── ztypes_linux_s390x.go │ ├── ztypes_linux_sparc64.go │ ├── ztypes_netbsd_386.go │ ├── ztypes_netbsd_amd64.go │ ├── ztypes_netbsd_arm.go │ ├── ztypes_netbsd_arm64.go │ ├── ztypes_openbsd_386.go │ ├── ztypes_openbsd_amd64.go │ ├── ztypes_openbsd_arm.go │ └── ztypes_solaris_amd64.go └── modules.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /local_exporter 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/lucab/local_exporter 3 | 4 | go: 5 | - "1.12" 6 | 7 | branches: 8 | only: 9 | - staging 10 | - trying 11 | - master 12 | 13 | env: 14 | global: 15 | - GO111MODULE="on" 16 | - GOFLAGS="-mod=vendor" 17 | 18 | install: 19 | - go mod verify 20 | 21 | script: 22 | - go build 23 | - go test -v ./... 24 | - docker build --rm -f dist/Dockerfile -t quay.io/lucab/local_exporter:travis . 25 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: local_exporter 3 | Source: https://www.github.com/lucab/local_exporter 4 | 5 | Files: * 6 | Copyright: 2019, Luca BRUNO 7 | License: Apache-2.0 8 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 660 York Street, Suite 102, 6 | San Francisco, CA 94110 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # local\_exporter 2 | 3 | [![Build status](https://travis-ci.com/lucab/local_exporter.svg?branch=master)](https://travis-ci.com/lucab/local_exporter) 4 | [![Container image](https://quay.io/repository/lucab/local_exporter/status)](https://quay.io/repository/lucab/local_exporter) 5 | 6 | 7 | `local-exporter` bridges between Prometheus and instrumented on-host daemons that do not expose a web-server on their own. 8 | 9 | It is meant to run as an unpriviliged container with few bind-mounts, and can bridge to multiple local endpoints: 10 | * plain metrics textfile 11 | * Unix-domain socket 12 | * DBus method 13 | 14 | For more information and background details, see the [design doc](docs/design.md). 15 | 16 | ## Quickstart 17 | 18 | ``` 19 | go get -u -v github.com/lucab/local_exporter && local_exporter serve --help 20 | ``` 21 | 22 | A TOML configuration sample (with comments) is available under [examples](dist/examples/). 23 | 24 | An automatically built `x86_64` container image is available on [quay.io](https://quay.io/repository/lucab/local_exporter) and can be run as: 25 | 26 | ``` 27 | docker run -p 9598:9598/tcp -v "$PWD/dist/examples/config.toml:/etc/local_exporter/config.toml" -v /run:/host/run quay.io/lucab/local_exporter:master local_exporter serve -vv 28 | ``` 29 | 30 | ## Demo 31 | 32 | [![asciicast](https://asciinema.org/a/256453.svg)](https://asciinema.org/a/256453) 33 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ "continuous-integration/travis-ci/push" ] 2 | -------------------------------------------------------------------------------- /dist/Dockerfile: -------------------------------------------------------------------------------- 1 | # build environment 2 | FROM golang:1.12-alpine3.9 AS build-env 3 | ADD . /src 4 | RUN cd /src && go build -mod=vendor 5 | 6 | # run environment 7 | FROM alpine:3.9 8 | WORKDIR / 9 | COPY --from=build-env /src/local_exporter /usr/local/bin/local_exporter 10 | CMD [ "/usr/local/bin/local_exporter", "serve" ] 11 | -------------------------------------------------------------------------------- /dist/examples/config.toml: -------------------------------------------------------------------------------- 1 | # Main service 2 | [service] 3 | # Listening address 4 | address = "127.0.0.1" 5 | # Listening port 6 | port = 9598 7 | # Whether to serve HTTP endpoints over TLS 8 | tls = false 9 | 10 | # Bridge configuration 11 | [bridge] 12 | dbus_system_bus_address = "unix:path=/host/run/dbus/system_bus_socket" 13 | 14 | # Backends multiplexing 15 | [bridge.selectors] 16 | # Metrics source over normal file (by path) 17 | "daemon1" = { kind = "file", path = "/run/daemon1/public/metrics.promfile" } 18 | # Metrics source over unix-domain socket (streaming socket, by path) 19 | "daemon2" = { kind = "uds", path = "/run/daemon2/public/metrics.promsock" } 20 | 21 | # Metrics source over DBus protocol (by remote-call endpoint) 22 | [bridge.selectors.daemon3] 23 | kind = "dbus" 24 | bus = "system" 25 | destination = "com.github.lucab.Prombus" 26 | method = "com.github.lucab.Prombus.Observable.PromMetrics" 27 | path = "/com/github/lucab/Prombus" 28 | -------------------------------------------------------------------------------- /docs/design.md: -------------------------------------------------------------------------------- 1 | # Local exporter 2 | 3 | ## Summary 4 | 5 | "local-exporter" bridges between Prometheus and instrumented on-host daemons that do not expose their own web-servers. It builds on the experience of node-exporter textfile collector. It is designed to run in a containerized and unprivileged environment. Additionally, it provides a uniform approach for future instrumentation and exposition of host-local services. 6 | 7 | ## Proposal 8 | 9 | This proposes building a new exporter, called "local-exporter". It runs (via container orchestration) on each node with host-local instrumented services. It exposes multiple metrics endpoint, one for each service. 10 | 11 | ## Rationale 12 | 13 | Linux hosts are usually running multiple host-local services, most of which not exposing a web server. Such services could be [incrementally instrumented][zincati], but it is unclear how to expose/gather their metrics without adding a web service to each one. 14 | 15 | Node-exporter provides a textfile collector, which is however not suited for fanning-in multiple services. 16 | 17 | Ideas and notes for this exporter came from an [IRC discussion][irc-logs]. 18 | 19 | [zincati]: https://github.com/coreos/zincati/issues/27 20 | [irc-logs]: https://matrix.to/#/!HaYTjhTxVqshXFkNfu:matrix.org/$1559744867345463AWGdk:matrix.org?via=matrix.org&via=half-shot.uk&via=mx.geekbundle.org 21 | 22 | ## Node-exporter shortcomings 23 | 24 | 1. It was designed to run privileged and non-containerized 25 | 1. Textfile collector takes a single directory (problematic with different daemons running under their own uid/gid) 26 | 1. Multiple sources are coalesced under the same `/metrics` endpoint 27 | 1. Requires non-timestamped metrics 28 | 1. Relies on daemon-side caches and timers for refreshes 29 | 30 | ## Design sketch 31 | 32 | 1. Written in golang and running from a container (daemonset) 33 | 2. Can be unprivileged, only requires bind-mounts for specific `/run` directories 34 | 3. Can run in overlay/pod network, following cluster network policies 35 | 4. Human configuration file (e.g. TOML) 36 | 5. Exposes a single, fixed port with a web service 37 | 6. Main configuration is a map of "selector" -> "directory" 38 | 7. Exposes its own metrics over `/metrics` 39 | 8. Expose bridged metrics (for other host-local services) as `/bridge?selector=foo` 40 | 9. Prometheus can configure/scrape each service separately 41 | 10. Bridged modes: 42 | 1. dbus (for daemons already speaking dbus): like unix-socket mode, but uses a DBus endpoint 43 | 1. unix-socket (recommended): bridges a Prometheus GET to a unix-socket read. Daemon spits out metric to each new unix-socket connection. 44 | 1. file (legacy): similar to node-exporter, with caching and timers. However, it doesn't coalesce sources and doesn't complain on timestamps. 45 | 11. Daemons can be directly instrumented (the usual Prometheus way) and write their metrics to via one of the methods above 46 | 47 | ## Implementation sketch 48 | 49 | * Local exporter: https://github.com/lucab/local_exporter 50 | * Dummy instrumented local service: https://github.com/lucab/instrumented-daemon 51 | * Metrics over DBus: https://github.com/lucab/prombus 52 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lucab/local_exporter 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/BurntSushi/toml v0.3.1 7 | github.com/godbus/dbus v5.0.1+incompatible 8 | github.com/sirupsen/logrus v1.4.2 9 | github.com/spf13/cobra v0.0.4 10 | ) 11 | -------------------------------------------------------------------------------- /internal/backend/common.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "context" 5 | "io" 6 | ) 7 | 8 | // MetricsSource is the general interface implemented by all backends 9 | type MetricsSource interface { 10 | OpenMetrics(context.Context) (io.ReadCloser, error) 11 | } 12 | -------------------------------------------------------------------------------- /internal/backend/dbus.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "errors" 7 | "io" 8 | "io/ioutil" 9 | 10 | "github.com/godbus/dbus" 11 | ) 12 | 13 | // DbusBackendKind is the configuration kind for the dbus backend 14 | const DbusBackendKind = "dbus" 15 | 16 | // DbusBackend is the dbus backend 17 | type DbusBackend struct { 18 | Bus string 19 | Destination string 20 | Method string 21 | Path string 22 | } 23 | 24 | // OpenMetrics returns the endpoint for this backend 25 | func (db DbusBackend) OpenMetrics(ctx context.Context) (io.ReadCloser, error) { 26 | conn, err := db.connect(ctx) 27 | if err != nil { 28 | return nil, err 29 | } 30 | obj := conn.Object(db.Destination, dbus.ObjectPath(db.Path)) 31 | 32 | var result []byte 33 | call := obj.CallWithContext(ctx, db.Method, 0) 34 | if call.Err != nil { 35 | return nil, call.Err 36 | } 37 | if err := call.Store(&result); err != nil { 38 | return nil, err 39 | } 40 | 41 | out := ioutil.NopCloser(bytes.NewReader(result)) 42 | return out, nil 43 | } 44 | 45 | // Connect to the bus 46 | func (db DbusBackend) connect(ctx context.Context) (*dbus.Conn, error) { 47 | switch db.Bus { 48 | case "system": 49 | return dbus.SystemBus() 50 | case "session": 51 | return dbus.SessionBus() 52 | default: 53 | return nil, errors.New("invalid bus type") 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /internal/backend/file.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "os" 7 | ) 8 | 9 | // FileBackendKind is the configuration kind for the file backend 10 | const FileBackendKind = "file" 11 | 12 | // FileBackend is the file backend 13 | type FileBackend struct { 14 | Path string 15 | } 16 | 17 | // OpenMetrics returns the endpoint for this backend 18 | func (fb FileBackend) OpenMetrics(ctx context.Context) (io.ReadCloser, error) { 19 | fp, err := os.Open(fb.Path) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | return fp, nil 25 | } 26 | -------------------------------------------------------------------------------- /internal/backend/uds.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "net" 7 | ) 8 | 9 | // UnixBackendKind is the configuration kind for the unix-socket backend 10 | const UnixBackendKind = "uds" 11 | 12 | // UnixBackend is the unix-socket backend 13 | type UnixBackend struct { 14 | Path string 15 | } 16 | 17 | // OpenMetrics returns the endpoint for this backend 18 | func (ub UnixBackend) OpenMetrics(ctx context.Context) (io.ReadCloser, error) { 19 | dialer := net.Dialer{} 20 | conn, err := dialer.Dial("unix", ub.Path) 21 | if err != nil { 22 | return nil, err 23 | } 24 | return conn, nil 25 | } 26 | -------------------------------------------------------------------------------- /internal/cli/common.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/sirupsen/logrus" 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/lucab/local_exporter/internal/config" 11 | ) 12 | 13 | var ( 14 | // localExporterCmd is the top-level cobra command for `localExporter` 15 | localExporterCmd = &cobra.Command{ 16 | Use: "local_exporter", 17 | PersistentPreRunE: commonSetup, 18 | } 19 | 20 | verbosity int 21 | configPath string 22 | runSettings *config.Settings 23 | ) 24 | 25 | // Init initializes the CLI environment for localExporter 26 | func Init() (*cobra.Command, error) { 27 | localExporterCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "/etc/local_exporter/config.toml", "path to configuration file") 28 | localExporterCmd.PersistentFlags().CountVarP(&verbosity, "verbose", "v", "increase verbosity level") 29 | 30 | localExporterCmd.AddCommand(cmdServe) 31 | 32 | return localExporterCmd, nil 33 | } 34 | 35 | // commonSetup perform actions commons to all CLI subcommands 36 | func commonSetup(cmd *cobra.Command, cmdArgs []string) error { 37 | if configPath == "" { 38 | return errors.New("empty path to configuration file") 39 | } 40 | logrus.SetLevel(verbosityLevel(verbosity)) 41 | 42 | cfg, err := config.Parse(configPath) 43 | if err != nil { 44 | return err 45 | } 46 | runSettings = &cfg 47 | 48 | if runSettings.DbusSessionBusAddress != "" { 49 | os.Setenv("DBUS_SESSION_BUS_ADDRESS", runSettings.DbusSessionBusAddress) 50 | } 51 | if runSettings.DbusSystemBusAddress != "" { 52 | os.Setenv("DBUS_SYSTEM_BUS_ADDRESS", runSettings.DbusSystemBusAddress) 53 | } 54 | 55 | return nil 56 | } 57 | 58 | // verbosityLevel parses `-v` count into logrus log-level 59 | func verbosityLevel(verbCount int) logrus.Level { 60 | switch verbCount { 61 | case 0: 62 | return logrus.WarnLevel 63 | case 1: 64 | return logrus.InfoLevel 65 | case 2: 66 | return logrus.DebugLevel 67 | default: 68 | return logrus.TraceLevel 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /internal/cli/serve.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/sirupsen/logrus" 9 | "github.com/spf13/cobra" 10 | 11 | "github.com/lucab/local_exporter/internal/server" 12 | ) 13 | 14 | var ( 15 | cmdServe = &cobra.Command{ 16 | Use: "serve", 17 | RunE: runServe, 18 | } 19 | ) 20 | 21 | // runServe runs the main HTTP service 22 | func runServe(cmd *cobra.Command, cmdArgs []string) error { 23 | logrus.WithFields(logrus.Fields{ 24 | "selectors": runSettings.SelectorsNames(), 25 | }).Debug("configured metrics") 26 | 27 | logrus.WithFields(logrus.Fields{ 28 | "address": runSettings.ServiceAddress, 29 | "port": runSettings.ServicePort, 30 | }).Info("starting service") 31 | 32 | if runSettings == nil { 33 | return errors.New("nil runSettings") 34 | } 35 | 36 | exporter := server.LocalExporter{*runSettings} 37 | 38 | http.Handle(server.BridgeEndpoint, exporter.BridgeHandler()) 39 | http.Handle(server.MetricsEndpoint, exporter.MetricsHandler()) 40 | 41 | listenAddr := fmt.Sprintf("%s:%d", runSettings.ServiceAddress, runSettings.ServicePort) 42 | if err := http.ListenAndServe(listenAddr, nil); err != nil { 43 | return err 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /internal/config/backends.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/lucab/local_exporter/internal/backend" 7 | ) 8 | 9 | // ParseBackend parses a backend configuration 10 | func ParseBackend(cfg selectorSection) (backend.MetricsSource, error) { 11 | switch cfg.Kind { 12 | case backend.FileBackendKind: 13 | return parseFileBackend(cfg) 14 | case backend.UnixBackendKind: 15 | return parseUnixBackend(cfg) 16 | case backend.DbusBackendKind: 17 | return parseDbusBackend(cfg) 18 | default: 19 | return nil, fmt.Errorf("unknown backend: %q", cfg.Kind) 20 | } 21 | } 22 | 23 | func parseFileBackend(cfg selectorSection) (*backend.FileBackend, error) { 24 | fb := backend.FileBackend{ 25 | Path: cfg.Path, 26 | } 27 | return &fb, nil 28 | } 29 | 30 | func parseUnixBackend(cfg selectorSection) (*backend.UnixBackend, error) { 31 | ub := backend.UnixBackend{ 32 | Path: cfg.Path, 33 | } 34 | return &ub, nil 35 | } 36 | 37 | func parseDbusBackend(cfg selectorSection) (*backend.DbusBackend, error) { 38 | db := backend.DbusBackend{ 39 | Bus: cfg.Bus, 40 | Destination: cfg.Destination, 41 | Method: cfg.Method, 42 | Path: cfg.Path, 43 | } 44 | return &db, nil 45 | } 46 | -------------------------------------------------------------------------------- /internal/config/settings.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/lucab/local_exporter/internal/backend" 7 | ) 8 | 9 | // Settings stores runtime application configuration 10 | type Settings struct { 11 | ServiceAddress string 12 | ServicePort uint64 13 | ServiceTLS bool 14 | 15 | DbusSessionBusAddress string 16 | DbusSystemBusAddress string 17 | 18 | Selectors map[string]backend.MetricsSource 19 | } 20 | 21 | // SelectorsNames returns the set of all configured selectors names. 22 | func (s Settings) SelectorsNames() []string { 23 | keys := make([]string, 0) 24 | for key := range s.Selectors { 25 | keys = append(keys, key) 26 | } 27 | return keys 28 | } 29 | 30 | // Parse parses a TOML configuration file and default values 31 | // into runtime settings 32 | func Parse(fpath string) (Settings, error) { 33 | base := defaultSettings() 34 | 35 | settings, err := parseConfig(fpath, base) 36 | if err != nil { 37 | return Settings{}, err 38 | } 39 | 40 | if err := validate(settings); err != nil { 41 | return Settings{}, err 42 | } 43 | 44 | return settings, nil 45 | } 46 | 47 | // defaultSettings returns default settings for all commands 48 | func defaultSettings() Settings { 49 | return Settings{ 50 | ServiceAddress: "0.0.0.0", 51 | ServicePort: 9598, 52 | ServiceTLS: true, 53 | 54 | DbusSessionBusAddress: "", 55 | DbusSystemBusAddress: "", 56 | 57 | Selectors: make(map[string]backend.MetricsSource), 58 | } 59 | } 60 | 61 | // validate sanity-checks all settings 62 | func validate(cfg Settings) error { 63 | if len(cfg.Selectors) == 0 { 64 | return errors.New("no selectors configured") 65 | } 66 | if cfg.ServiceTLS { 67 | return errors.New("TLS mode not yet implemented") 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /internal/config/toml.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/BurntSushi/toml" 7 | ) 8 | 9 | // tomlConfig is the top-level TOML configuration fragment 10 | type tomlConfig struct { 11 | Service *serviceSection `toml:"service"` 12 | Bridge *bridgeSection `toml:"bridge"` 13 | } 14 | 15 | // serviceSection holds the optional `service` fragment 16 | type serviceSection struct { 17 | Address *string `toml:"address"` 18 | Port *uint64 `toml:"port"` 19 | TLS *bool `toml:"tls"` 20 | } 21 | 22 | // bridgeSection holds the optional `bridge` fragment 23 | type bridgeSection struct { 24 | Selectors map[string]selectorSection `toml:"selectors"` 25 | DbusSessionBusAddress *string `toml:"dbus_session_bus_address"` 26 | DbusSystemBusAddress *string `toml:"dbus_system_bus_address"` 27 | } 28 | 29 | // selectorSection holds the optional `selector` fragment 30 | type selectorSection struct { 31 | Kind string `toml:"kind"` 32 | Path string `toml:"path"` 33 | 34 | // DBus specific 35 | Bus string `toml:"bus"` 36 | Destination string `toml:"destination"` 37 | Method string `toml:"method"` 38 | } 39 | 40 | // parseConfig tries to parse and merge TOML config and default settings 41 | func parseConfig(fpath string, defaults Settings) (Settings, error) { 42 | cfg := tomlConfig{} 43 | if _, err := toml.DecodeFile(fpath, &cfg); err != nil { 44 | return Settings{}, err 45 | } 46 | 47 | runSettings := defaults 48 | 49 | if err := mergeToml(&runSettings, cfg); err != nil { 50 | return Settings{}, err 51 | } 52 | 53 | return runSettings, nil 54 | } 55 | 56 | // mergeToml applies a TOML configuration fragment on top of existing settings 57 | func mergeToml(settings *Settings, cfg tomlConfig) error { 58 | if settings == nil { 59 | return errors.New("nil settings") 60 | } 61 | 62 | if cfg.Service != nil { 63 | if err := mergeService(settings, *cfg.Service); err != nil { 64 | return err 65 | } 66 | } 67 | if cfg.Bridge != nil { 68 | if err := mergeBridge(settings, *cfg.Bridge); err != nil { 69 | return err 70 | } 71 | } 72 | 73 | return nil 74 | } 75 | 76 | func mergeService(settings *Settings, cfg serviceSection) error { 77 | if settings == nil { 78 | return errors.New("nil settings") 79 | } 80 | 81 | if cfg.Address != nil { 82 | settings.ServiceAddress = *cfg.Address 83 | } 84 | if cfg.Port != nil { 85 | settings.ServicePort = *cfg.Port 86 | } 87 | if cfg.TLS != nil { 88 | settings.ServiceTLS = *cfg.TLS 89 | } 90 | 91 | return nil 92 | } 93 | 94 | func mergeBridge(settings *Settings, cfg bridgeSection) error { 95 | if settings == nil { 96 | return errors.New("nil settings") 97 | } 98 | 99 | if cfg.DbusSessionBusAddress != nil { 100 | settings.DbusSessionBusAddress = *cfg.DbusSessionBusAddress 101 | } 102 | if cfg.DbusSystemBusAddress != nil { 103 | settings.DbusSystemBusAddress = *cfg.DbusSystemBusAddress 104 | } 105 | 106 | for selector, entry := range cfg.Selectors { 107 | backend, err := ParseBackend(entry) 108 | if err != nil { 109 | return err 110 | } 111 | 112 | settings.Selectors[selector] = backend 113 | } 114 | 115 | return nil 116 | } 117 | -------------------------------------------------------------------------------- /internal/server/bridge.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "io" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | const ( 12 | // BridgeEndpoint is the endpoint for exposing bridged metrics. 13 | BridgeEndpoint = "/bridge" 14 | ) 15 | 16 | // BridgeHandler is the handler for the `/bridge` endpoint. 17 | func (le *LocalExporter) BridgeHandler() http.Handler { 18 | handler := func(w http.ResponseWriter, req *http.Request) { 19 | if le == nil { 20 | ErrorResponse(w, errNilServer, "", 500) 21 | return 22 | } 23 | 24 | queryParams, ok := req.URL.Query()["selector"] 25 | if !ok || len(queryParams) == 0 || len(queryParams[0]) == 0 { 26 | err := errors.New("missing selector") 27 | ErrorResponse(w, err, "", 500) 28 | return 29 | } 30 | selector := queryParams[0] 31 | 32 | backend, ok := le.Selectors[selector] 33 | if !ok || backend == nil { 34 | err := errors.New("backend not found") 35 | ErrorResponse(w, err, selector, 404) 36 | return 37 | } 38 | 39 | timeout := 3 * time.Second 40 | ctx, cancel := context.WithTimeout(context.Background(), timeout) 41 | defer cancel() 42 | 43 | source, err := backend.OpenMetrics(ctx) 44 | if err != nil { 45 | ErrorResponse(w, err, selector, 500) 46 | return 47 | } 48 | defer source.Close() 49 | 50 | io.Copy(w, source) 51 | 52 | } 53 | 54 | return http.HandlerFunc(handler) 55 | } 56 | -------------------------------------------------------------------------------- /internal/server/metrics.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | ) 7 | 8 | const ( 9 | // MetricsEndpoint is the endpoint for exposing own internal metrics. 10 | MetricsEndpoint = "/metrics" 11 | ) 12 | 13 | // MetricsHandler is the handler for the `/metrics` endpoint. 14 | func (le *LocalExporter) MetricsHandler() http.Handler { 15 | handler := func(w http.ResponseWriter, req *http.Request) { 16 | if le == nil { 17 | ErrorResponse(w, errNilServer, "", 500) 18 | return 19 | } 20 | 21 | err := errors.New("metrics: unimplemented") 22 | ErrorResponse(w, err, "", 500) 23 | return 24 | 25 | } 26 | 27 | return http.HandlerFunc(handler) 28 | } 29 | -------------------------------------------------------------------------------- /internal/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | 7 | "github.com/sirupsen/logrus" 8 | 9 | "github.com/lucab/local_exporter/internal/config" 10 | ) 11 | 12 | var ( 13 | // errNilServer is returned on nil server 14 | errNilServer = errors.New("nil server") 15 | ) 16 | 17 | // LocalExporter is the main service 18 | type LocalExporter struct { 19 | config.Settings 20 | } 21 | 22 | // ErrorResponse handles errors 23 | func ErrorResponse(w http.ResponseWriter, err error, selector string, code int) { 24 | logrus.WithFields(logrus.Fields{ 25 | "selector": selector, 26 | "code": code, 27 | "value": err.Error(), 28 | }).Error("endpoint error") 29 | 30 | w.WriteHeader(code) 31 | 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /local_exporter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sirupsen/logrus" 7 | 8 | "github.com/lucab/local_exporter/internal/cli" 9 | ) 10 | 11 | func main() { 12 | exitCode := 0 13 | 14 | err := run() 15 | if err != nil { 16 | exitCode = 1 17 | logrus.Errorln(err) 18 | } 19 | os.Exit(exitCode) 20 | } 21 | 22 | func run() error { 23 | cmd, err := cli.Init() 24 | if err != nil { 25 | return err 26 | } 27 | 28 | return cmd.Execute() 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/.gitignore: -------------------------------------------------------------------------------- 1 | TAGS 2 | tags 3 | .*.swp 4 | tomlcheck/tomlcheck 5 | toml.test 6 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.1 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | - 1.6 9 | - tip 10 | install: 11 | - go install ./... 12 | - go get github.com/BurntSushi/toml-test 13 | script: 14 | - export PATH="$PATH:$HOME/gopath/bin" 15 | - make test 16 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/COMPATIBLE: -------------------------------------------------------------------------------- 1 | Compatible with TOML version 2 | [v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md) 3 | 4 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/COPYING: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 TOML authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | go install ./... 3 | 4 | test: install 5 | go test -v 6 | toml-test toml-test-decoder 7 | toml-test -encoder toml-test-encoder 8 | 9 | fmt: 10 | gofmt -w *.go */*.go 11 | colcheck *.go */*.go 12 | 13 | tags: 14 | find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS 15 | 16 | push: 17 | git push origin master 18 | git push github master 19 | 20 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package toml provides facilities for decoding and encoding TOML configuration 3 | files via reflection. There is also support for delaying decoding with 4 | the Primitive type, and querying the set of keys in a TOML document with the 5 | MetaData type. 6 | 7 | The specification implemented: https://github.com/toml-lang/toml 8 | 9 | The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify 10 | whether a file is a valid TOML document. It can also be used to print the 11 | type of each key in a TOML document. 12 | 13 | Testing 14 | 15 | There are two important types of tests used for this package. The first is 16 | contained inside '*_test.go' files and uses the standard Go unit testing 17 | framework. These tests are primarily devoted to holistically testing the 18 | decoder and encoder. 19 | 20 | The second type of testing is used to verify the implementation's adherence 21 | to the TOML specification. These tests have been factored into their own 22 | project: https://github.com/BurntSushi/toml-test 23 | 24 | The reason the tests are in a separate project is so that they can be used by 25 | any implementation of TOML. Namely, it is language agnostic. 26 | */ 27 | package toml 28 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/encoding_types.go: -------------------------------------------------------------------------------- 1 | // +build go1.2 2 | 3 | package toml 4 | 5 | // In order to support Go 1.1, we define our own TextMarshaler and 6 | // TextUnmarshaler types. For Go 1.2+, we just alias them with the 7 | // standard library interfaces. 8 | 9 | import ( 10 | "encoding" 11 | ) 12 | 13 | // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here 14 | // so that Go 1.1 can be supported. 15 | type TextMarshaler encoding.TextMarshaler 16 | 17 | // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined 18 | // here so that Go 1.1 can be supported. 19 | type TextUnmarshaler encoding.TextUnmarshaler 20 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/encoding_types_1.1.go: -------------------------------------------------------------------------------- 1 | // +build !go1.2 2 | 3 | package toml 4 | 5 | // These interfaces were introduced in Go 1.2, so we add them manually when 6 | // compiling for Go 1.1. 7 | 8 | // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here 9 | // so that Go 1.1 can be supported. 10 | type TextMarshaler interface { 11 | MarshalText() (text []byte, err error) 12 | } 13 | 14 | // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined 15 | // here so that Go 1.1 can be supported. 16 | type TextUnmarshaler interface { 17 | UnmarshalText(text []byte) error 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/session.vim: -------------------------------------------------------------------------------- 1 | au BufWritePost *.go silent!make tags > /dev/null 2>&1 2 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/type_check.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | // tomlType represents any Go type that corresponds to a TOML type. 4 | // While the first draft of the TOML spec has a simplistic type system that 5 | // probably doesn't need this level of sophistication, we seem to be militating 6 | // toward adding real composite types. 7 | type tomlType interface { 8 | typeString() string 9 | } 10 | 11 | // typeEqual accepts any two types and returns true if they are equal. 12 | func typeEqual(t1, t2 tomlType) bool { 13 | if t1 == nil || t2 == nil { 14 | return false 15 | } 16 | return t1.typeString() == t2.typeString() 17 | } 18 | 19 | func typeIsHash(t tomlType) bool { 20 | return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) 21 | } 22 | 23 | type tomlBaseType string 24 | 25 | func (btype tomlBaseType) typeString() string { 26 | return string(btype) 27 | } 28 | 29 | func (btype tomlBaseType) String() string { 30 | return btype.typeString() 31 | } 32 | 33 | var ( 34 | tomlInteger tomlBaseType = "Integer" 35 | tomlFloat tomlBaseType = "Float" 36 | tomlDatetime tomlBaseType = "Datetime" 37 | tomlString tomlBaseType = "String" 38 | tomlBool tomlBaseType = "Bool" 39 | tomlArray tomlBaseType = "Array" 40 | tomlHash tomlBaseType = "Hash" 41 | tomlArrayHash tomlBaseType = "ArrayHash" 42 | ) 43 | 44 | // typeOfPrimitive returns a tomlType of any primitive value in TOML. 45 | // Primitive values are: Integer, Float, Datetime, String and Bool. 46 | // 47 | // Passing a lexer item other than the following will cause a BUG message 48 | // to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. 49 | func (p *parser) typeOfPrimitive(lexItem item) tomlType { 50 | switch lexItem.typ { 51 | case itemInteger: 52 | return tomlInteger 53 | case itemFloat: 54 | return tomlFloat 55 | case itemDatetime: 56 | return tomlDatetime 57 | case itemString: 58 | return tomlString 59 | case itemMultilineString: 60 | return tomlString 61 | case itemRawString: 62 | return tomlString 63 | case itemRawMultilineString: 64 | return tomlString 65 | case itemBool: 66 | return tomlBool 67 | } 68 | p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) 69 | panic("unreachable") 70 | } 71 | 72 | // typeOfArray returns a tomlType for an array given a list of types of its 73 | // values. 74 | // 75 | // In the current spec, if an array is homogeneous, then its type is always 76 | // "Array". If the array is not homogeneous, an error is generated. 77 | func (p *parser) typeOfArray(types []tomlType) tomlType { 78 | // Empty arrays are cool. 79 | if len(types) == 0 { 80 | return tomlArray 81 | } 82 | 83 | theType := types[0] 84 | for _, t := range types[1:] { 85 | if !typeEqual(theType, t) { 86 | p.panicf("Array contains values of type '%s' and '%s', but "+ 87 | "arrays must be homogeneous.", theType, t) 88 | } 89 | } 90 | return tomlArray 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/godbus/dbus/.travis.yml: -------------------------------------------------------------------------------- 1 | dist: precise 2 | language: go 3 | go_import_path: github.com/godbus/dbus 4 | sudo: true 5 | 6 | go: 7 | - 1.7.3 8 | - 1.8.7 9 | - 1.9.5 10 | - 1.10.1 11 | - tip 12 | 13 | env: 14 | global: 15 | matrix: 16 | - TARGET=amd64 17 | - TARGET=arm64 18 | - TARGET=arm 19 | - TARGET=386 20 | - TARGET=ppc64le 21 | 22 | matrix: 23 | fast_finish: true 24 | allow_failures: 25 | - go: tip 26 | exclude: 27 | - go: tip 28 | env: TARGET=arm 29 | - go: tip 30 | env: TARGET=arm64 31 | - go: tip 32 | env: TARGET=386 33 | - go: tip 34 | env: TARGET=ppc64le 35 | 36 | addons: 37 | apt: 38 | packages: 39 | - dbus 40 | - dbus-x11 41 | 42 | before_install: 43 | 44 | script: 45 | - go test -v -race ./... # Run all the tests with the race detector enabled 46 | - go vet ./... # go vet is the official Go static analyzer 47 | -------------------------------------------------------------------------------- /vendor/github.com/godbus/dbus/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Getting Started 4 | 5 | - Fork the repository on GitHub 6 | - Read the [README](README.markdown) for build and test instructions 7 | - Play with the project, submit bugs, submit patches! 8 | 9 | ## Contribution Flow 10 | 11 | This is a rough outline of what a contributor's workflow looks like: 12 | 13 | - Create a topic branch from where you want to base your work (usually master). 14 | - Make commits of logical units. 15 | - Make sure your commit messages are in the proper format (see below). 16 | - Push your changes to a topic branch in your fork of the repository. 17 | - Make sure the tests pass, and add any new tests as appropriate. 18 | - Submit a pull request to the original repository. 19 | 20 | Thanks for your contributions! 21 | 22 | ### Format of the Commit Message 23 | 24 | We follow a rough convention for commit messages that is designed to answer two 25 | questions: what changed and why. The subject line should feature the what and 26 | the body of the commit should describe the why. 27 | 28 | ``` 29 | scripts: add the test-cluster command 30 | 31 | this uses tmux to setup a test cluster that you can easily kill and 32 | start for debugging. 33 | 34 | Fixes #38 35 | ``` 36 | 37 | The format can be described more formally as follows: 38 | 39 | ``` 40 | : 41 | 42 | 43 | 44 |